LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

C++ dynamic library problem with Numerical Recipes (ADVANCED!!)

Super-Advanced Users;

I need to do 2D bicubic spline interpolation since I'll be curve fitting and need good, reliable function derivatives.  For this, I'm calling the C++ Numerical Recipes.  I've written a dynamic library which generates a spline fit and returns a handle to that "object".  When I call the interpolation routine, the "object" appears to have been partially clobbered, and I get a crash.

When I say "partially", it appears the pointer is correct, and many of the things it points to are still correct, but it still crashes.  The smallish code looks like this:

EXPORT long build_spline(unsigned long *handle, TD1Hdl mag_array, double sigmas[],
        double dielectrics[], LStrHandle error)
{
int i, j, m, n; double g;
VecDoub a, b; MatDoub c;

m = (*mag_array)->dimSizes[0];n = (*mag_array)->dimSizes[1];
a = NRvector<double>(m, sigmas); b = NRvector<double>(n, dielectrics);
c = NRmatrix<double>(m, n, (*mag_array)->elt);

Spline2D_interp *Spline2D = new Spline2D_interp(a, b, c);
g = Spline2D->interp(0.01, 6.0);  // sanity check
*handle = (unsigned long) Spline2D;
return 0;
}

EXPORT long eval_spline(unsigned long *handle, double sigma, double dielectric,
        double *result, LStrHandle error)
{
Spline2D_interp *Spline2D;
if (*handle == 0) {LV_str_cp(error, (char*) "NULL handle passed to eval_spline\n");return -2;}

Spline2D = (Spline2D_interp*) *handle;

*result = Spline2D->interp(0.01, 6.0);
// *result = Spline2D->interp(sigma, dielectric);

return 0;
}

and gdb shows:

50      *handle = (unsigned long) Spline2D;
(gdb) print g
$1 = -6.3409099999999965
(gdb) print Spline2D
$2 = (Spline2D_interp *) 0x9509690
(gdb) print *Spline2D
$3 = {m = 7, n = 10, y = @0xff89ad18, x1 = @0xff89ad2c, yv = {nn = 7,
    v = 0x9501de0}, srp = {nn = 7, v = 0x95096c8}}
(gdb) del 1
(gdb) cont
Continuing.

Breakpoint 2, eval_spline (handle=0x942b14c, sigma=0, dielectric=5.5,
    result=0x942b134, error=0xf4156814) at lookup.cc:62
62      *result = Spline2D->interp(0.01, 6.0);
(gdb) print Spline2D
$4 = (Spline2D_interp *) 0x9509690
(gdb) print *Spline2D
$5 = {m = 7, n = 10, y = @0xff89ad18, x1 = @0xff89ad2c, yv = {nn = 7,
    v = 0x9501de0}, srp = {nn = 7, v = 0x95096c8}}
(gdb) cont
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0xf73382a6 in Spline_interp::sety2 (this=0xff89acec, xv=0x3f847ae1,
    yv=0x9501de0, yp1=9.9999999999999997e+98, ypn=9.9999999999999997e+98)
    at /home/danny/src/NR_C301/code/interp_1d.h:192
192                     sig=(xv[i]-xv[i-1])/(xv[i+1]-xv[i-1]);
(gdb) quit

Any suggestions are welcome.

Regards,
   ...Dan

0 Kudos
Message 1 of 19
(5,077 Views)
Is this a LabVIEW question?  Try positing this to the Measurement Studio for VC++ board
SteveA
CLD

-------------------------------------
FPGA/RT/PDA/TP/DSC
-------------------------------------
0 Kudos
Message 2 of 19
(5,069 Views)


@StevenA wrote:
Is this a LabVIEW question?  Try positing this to the Measurement Studio for VC++ board


It is a LabVIEW question, more precisely how to write an external code shared library that LabVIEW can interface to. But my C++ knowledge is not good enough to be of much help here.

Most likely you are doing something bad with the LabVIEW array handle that gets entered as mag_array. Not sure if your Spline2D object copies the entire data from the array into itself or if it tries to simply hold a reference to it. In the second case you are doing something very bad. That LabVIEW handle is only guaranteed to be valid for the duration of your call. After that LabVIEW simply assumes that it has gotten full control of it and will deallocate it, resize it, change it, clobber it or simply eat it, whatever it's mind is up to at that point.

So unless your
c = NRmatrix<double>(m, n, (*mag_array)->elt); or at least the  new Spline2D_interp(a, b, c); call does create a full copy of the original handle (or does not need to reference that data later on) your array is only by great exception going to survive between your two Call Library calls and if you should happen to try to write into it your going to overwrite sooner or later vital LabVIEW internals since LabVIEw has deallocated that handle long ago and reused that memory for something else since.

Rolf Kalbermatter


Message Edited by rolfk on 06-13-2008 10:59 AM
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 3 of 19
(5,061 Views)
Rolfk;

Your answer is perceptive, thanks.  But -- you seem to indicate LabView might clobber CLFN-malloced memory between calls to the DLL.  The pointer, "handle", seems to be correct and appears to point to the same structure in the evaluate call that it did in the generate call.

I do have quite a bit of experience creating a handle to database objects (from LabView), where opening a database connection must (though I'm not intimately familiar with the internals of odbc.dll) malloc memory for the structure.  You're implying I must have been lucky that chain of DLLs aren't being clobbered by LabView between calls, that could be, but my odbc libraries have worked well.

BTW:  "handle" is a pass-by-pointer unsigned integer, I typecast it to a LabView handle to make the wires look correct in the block diagrams.

To summarize, then:  Does LabView clobber CLFN-malloced memory between calls to the DLL?  If it does, have I just been lucky before?  If it does, how do I protect my memory?  If it doesn't, what I'm I doing wrong in the C++ code?

   ...Dan
0 Kudos
Message 4 of 19
(5,047 Views)
Rolfk;  I just re-read your post, you indicate that it isn't the data allocated in the DLL that is suspect, that it may be C++ code is storing pointers to the LabView data, which is then clobbered between calls.  I'll try building/allocating a new structure and copy all the necessary data into that, then build the splines and work from that.  ...I'll let you know

0 Kudos
Message 5 of 19
(5,041 Views)


@dgholstein wrote:
Rolfk;  I just re-read your post, you indicate that it isn't the data allocated in the DLL that is suspect, that it may be C++ code is storing pointers to the LabView data, which is then clobbered between calls.  I'll try building/allocating a new structure and copy all the necessary data into that, then build the splines and work from that.  ...I'll let you know



Yes you misunderstood at first what I was trying to say. This here is more precisely what I had in mind but which I might have not been able to express as clearly as I would have wanted to do. LabVIEW does not interfere with memory you allocate yourself at all but it considers handles it passes to the DLL to be generally owned by itself and will accordingly do with them as it pleases as soon as your DLL function returns control to LabVIEw. So storing a reference to a LabVIEW handle passed in as function parameter into a structure to use later will be doomed to fail.

If you want to do that you would really have to pass that handle by reference (Pointer to LabVIEW Handle) to your DLL, store that handle somewhere and replace the value in the parameter with a handle that you allocated with LabVIEW memory manager calls such as for instance NumericArrayResize(). Basically you take in that way ownership of the handle passed in from LabVIEW and give LabVIEW a different handle back. There is also an optimizing shortcut for empty arrays (which might be not very well documented) where a LabVIEW NULL handle is treated as empty handle too. So instead of allocating an empty array you can simply NULL out the referenced value of that parameter and LabVIEW will consider it an empty handle. This is especially handy if the right side of that parameter is not connected since that will cause LabVIEW to deallocate that handle immediately so that it makes no sense to allocate a memory location to hold an empty array to have it deallocated immediately by LabVIEW later on.

However don't forget that you now own that handle and need to deallocate it explicitedly using DSDisposeHandle() when you destroy your handle (object).

Rolf Kalbermatter




Message Edited by rolfk on 06-13-2008 08:51 PM
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 6 of 19
(5,021 Views)
Rolfk;

Again, thanks for the input -- these are not easy things to program and I don't work with anyone who programs at the same level.  An acquaintence at a national lab, who is not familiar with LabView but has programmed similar problems with Python, was able to point out the same things as you did.

One interesting difference between what you describe and how I program.  I've been allocating to a pointer, then return that pointer to LabView as an unsigned int, I then typecast that to a reference (for the correct looking wires) and do the opposite when I need to pass it back to my DLL (which I C/C++ typecast back to a C/C++ pointer).  I was told that I shouldn't use LabView handles since those are very well-defined for file handles and TCP connections etcetera -- and that I might hurt myself.

BTW:  I bundled all the information into one C++ structure and use that pointer (typecast to reference) within LabView, it seems to work now.

   ...Dan
0 Kudos
Message 7 of 19
(5,014 Views)


@dgholstein wrote:
Rolfk;

Again, thanks for the input -- these are not easy things to program and I don't work with anyone who programs at the same level.  An acquaintence at a national lab, who is not familiar with LabView but has programmed similar problems with Python, was able to point out the same things as you did.

One interesting difference between what you describe and how I program.  I've been allocating to a pointer, then return that pointer to LabView as an unsigned int, I then typecast that to a reference (for the correct looking wires) and do the opposite when I need to pass it back to my DLL (which I C/C++ typecast back to a C/C++ pointer).  I was told that I shouldn't use LabView handles since those are very well-defined for file handles and TCP connections etcetera -- and that I might hurt myself.

BTW:  I bundled all the information into one C++ structure and use that pointer (typecast to reference) within LabView, it seems to work now.

   ...Dan


When I said LabVIEW handles I meant parameters like your TD1Hdl one. This is a pointer to a pointer to a memory area where LabVIEW stores the array data (and its dimension sizes). Such handles need to be allocated, resized and finally deallocated using LabVIEW memory manager functions.

What you refer to as handles are generally handles in C programming but LabVIEW calls them refnums.

I do sometimes use datalog refnums to pass around my private data handles. Dropping in an enum with a single enum value specific to your handle type will basically refuse to allow anyone to wire something else to that connection pane parameter than my own private handle type.

You can wire a refnum wire directly to a Call Library Node parameter configured to Adapt to Type. LabVIEW will pass that "handle" then always by reference.

Rolf Kalbermatter


Message Edited by rolfk on 06-13-2008 10:46 PM
Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 8 of 19
(5,006 Views)
I'm familiar with implementing multi-dimensional and parameterized splines. A common mistake is in support for evaluation points outside the grid for which the spline is defined. Could that be the case here? I'm not familiar w/ the NRC++ code, but it's a possibility. If the evaluation point you're using is outside (note: you are using a hard-coded value currently), errors in indexing the spline structure internally during evaluation can generate this type of segmentation fault.
0 Kudos
Message 9 of 19
(4,967 Views)
MathGuy;

In this case, no, I'm not using out-of-bounds variables.  BTW:  The NR routines return NaN  when out of bounds (very well thought out).

The problem was this, the C++ routines store pointers to the knots and data, which, in this case, had been passed in as LabView data.  When I then called the evaluation routines, those knots and data had been clobbered (de-allocated) by LabView -- so, hillarity ensues!

The solution was to construct a structure and copy the LabView data to it.  All I need to return to LabView is a pointer to that structure.  The structure looks like:
typedef struct LV_spline_ptr {
    int m, n;
    double *sigmas;      // x1
    double *dielectrics;
  // x2
    double *vals;     // values
    VecDoub a, b;  // NR Vectors
    MatDoub c;
     // NR Matix
    Spline2D_interp *Spline2D;   // NR Spline object
    } ;
allocation is:
LV_spline_ptr *ptr = new LV_spline_ptr;
and data is returned to LabView with:
*handle = (unsigned long) ptr;
Unless the program is a one shot program, destructors need to be written and used to avoid a memory leak.

Thanks for the suggestion though.  In the end, it was caused by the routine as I described, not from out-of-bounds.

   ...Dan
0 Kudos
Message 10 of 19
(4,955 Views)