LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

correct way to pass pointers around labview

Solved!
Go to solution

Hello,

 

what is the correct way to pass pointers, pointing to DLL allocated memory around labview ?

I have been for quite some time using the standard C type cast working as:

 

typedef struct
{
	void *mem;
} myVars;

int InitMyVars(int64_t *myVarsLVPtr)
{
	void *myVarsPtr = NULL;
	myVarsPtr = malloc(sizeof(myVars));
	if(myVarsPtr) 
	{
		*myVarsLVPtr = (int64_t)myVarsPtr;
		return 0;
	}
	return -1;
}

int UseMyVars(int64_t *myVarsLVPtr)
{
	void *myVarsPtr = NULL;
	myVarsPtr =(void*)*myVarsLVPtr;
	//return funcWithVars(myVarsPtr);
}

Afterwards configure the library call nodes as follows:

 

LVImg.png

And pass the pointer around in labview as an 64 bit number (since it is a pointer value, I think it does not make difference if its signed or unsigned, please facepalm me if wrong)

 

This way is very ugly. I do not like it. Found several discussions stating that "Every time someone writes a code like int tmp = (int)(&something); a little fluffy kitten chokes to death. Unfortunately, programming so far had been quite a felinicide." This makes me a serial kittenchoker.

It is also dangerous, for different platforms eg. i686 and x86_64.

 

I was self learning all this stuff, now looking back at it, it feels like I missed something big time.

There must be a better way.

 

I am thankful for suggestions.

0 Kudos
Message 1 of 23
(6,202 Views)

This example may be of help:

 

<LabVIEW>\examples\dll\data passing


CLA CTAChampionI'm attending the GLA Summit!
Subscribe to the Test Automation user group: UK Test Automation Group
0 Kudos
Message 2 of 23
(6,195 Views)

@.aCe. wrote:

This example may be of help:

 

<LabVIEW>\examples\dll\data passing


Thanks for suggestion, does not help though.

0 Kudos
Message 3 of 23
(6,184 Views)

There's no need for the "UseMyVars" function. You could call funcWithVars directly from LabVIEW. The only change is that in the Call Library Function Node configuration, you pass the pointer BY VALUE instead of by pointer. As you've already noted, as far as LabVIEW is concerned a pointer is just a 64-bit value (on a 64-bit platform; on a 32-bit platform it's a 32-bit value, and if you're writing code to work on both then you need to take this into account). You still need to call InitMyVars with a pointer parameter, so that the function can return the address of the allocated memory, but then any functions that use that memory should be called with the address as a by-value parameter.

 

If that doesn't make sense, or doesn't solve your issue, could you show how you're actually using these functions? I'm making some guesses about your configuration, since your CLFN setup doesn't show how you're calling either of the functions that you provided above.

0 Kudos
Message 4 of 23
(6,149 Views)

@nathand wrote:

There's no need for the "UseMyVars" function. You could call funcWithVars directly from LabVIEW. The only change is that in the Call Library Function Node configuration, you pass the pointer BY VALUE instead of by pointer. As you've already noted, as far as LabVIEW is concerned a pointer is just a 64-bit value (on a 64-bit platform; on a 32-bit platform it's a 32-bit value, and if you're writing code to work on both then you need to take this into account). You still need to call InitMyVars with a pointer parameter, so that the function can return the address of the allocated memory, but then any functions that use that memory should be called with the address as a by-value parameter.

 

If that doesn't make sense, or doesn't solve your issue, could you show how you're actually using these functions? I'm making some guesses about your configuration, since your CLFN setup doesn't show how you're calling either of the functions that you provided above.


Thanks for answer, I am confused with your answer. I will try to clarify, what is that example i provided supposed to do. It is a demonstartion, its not supposed to provide any functionality.

Basicly, the way I use DLLs is as follows. I create/wrap a class, as long as I need to use such object around in LabView code, I need to keep track of it. I can either keep track of that class using static variables inside DLL and let the compiler allocate it, then I just need to keep track of the object in some counter manner (onject 1, 2, 3, 4...) - this has often many limits, especially when working with memory consuming code where you need better control of memory in differents parts of labview code. The other option is to allocate everything dynamicaly, once this happens, I need to "wire" pointer to such memory around in LabView. On one place, I need to initialize such object (open HW, file etc.) and on another I need to do other stuff with it (read, write, kill etc.).

 

My code works just fine, it is the ugly type casts I do not like and should be avoided.

 

You wrote about passing the pointer value as Value, that would work, I would have to typecast it still + anytime the pointer value would change (I move the memory inside the DLL) I would have to engage some extra new acrobatics so I do not leak the memory and loose the object.

 

Here is how I call those functions

LVSnippy.png

 

The thing I do not like about this approach is that I have to use code like this:

int tmp = (int)(&something);

 I was hoping for some more elegant way to keep the pointer information in LabView. Something, that would tell the compiler it is a pointer, not a number.

 

I was exploring this problem a bit more now, I think that LabView has a "Call library node" terminal datatype called "Pointer sized integer".

As I understand to it, it should platform specifically determine the datatype for it, eg. 32bit on 32bit system 64bit on 64bit system.

 

Thanks for answer.

 

0 Kudos
Message 5 of 23
(6,135 Views)

It would help for you to post real code, or at least consistent examples. Where in your code are you actually using the cast from pointer to integer that you are trying to avoid?

 

There is no need for LabVIEW to know whether a wire contains a pointer or a number - either way, it's just a number. There are many situations in which the Call Library Function Node configuration does NOT match the function prototype, but you get the correct result. One such situation is when the wire contains an address. In that case, even though the function prototype specificies that the parameter is a pointer, in LabVIEW you should configure that parameter to be passed BY VALUE because the value on the wire already is a pointer. Does that make sense? In your example code, you have essentially created a handle, because the myVars data type contains a pointer. Your C code can move around the memory pointed to by myVars.mem as much as it wants, so long as the pointer to myVars stays constant. I'm assuming that's what you're doing; is that correct? If so, then again, in LabVIEW you should be passing the pointer by value. I think this will avoid the dereference cast that's bothering you. As you have it now, you are adding an unnecessary extra layer of reference (LabVIEW is passing a pointer to an address, rather than the address itself), which you then need to cancel out with the cast that you don't like.

0 Kudos
Message 6 of 23
(6,112 Views)

@nathand wrote:

It would help for you to post real code, or at least consistent examples. Where in your code are you actually using the cast from pointer to integer that you are trying to avoid?


Lines 12 and 21 in the 1st post

*myVarsLVPtr = (int64_t)myVarsPtr;

myVarsPtr =(void*)*myVarsLVPtr;

 Sorry, but I cannot see how are my examples inconsistent, I only supplied one. I know it consists of 2 images and some code, but still describes the same process.


@nathand wrote:

 

There is no need for LabVIEW to know whether a wire contains a pointer or a number - either way, it's just a number. There are many situations in which the Call Library Function Node configuration does NOT match the function prototype, but you get the correct result.


You allways have to match the function prototype, if you do not, you corrupt memory. I guess you mean, that the function prototype may be written different, as long as the datatypes and memory layout is the same.

 


@nathand wrote:

One such situation is when the wire contains an address. In that case, even though the function prototype specificies that the parameter is a pointer, in LabVIEW you should configure that parameter to be passed BY VALUE because the value on the wire already is a pointer. Does that make sense? In your example code, you have essentially created a handle, because the myVars data type contains a pointer. Your C code can move around the memory pointed to by myVars.mem as much as it wants, so long as the pointer to myVars stays constant. I'm assuming that's what you're doing; is that correct?


If you pass a variable into DLL "by value", it does exactly what it says, it passes the value. Means, if you want to change the value going through the wire, you cant, you just get the value of it to the function stack. You can assign new value to such value inside DLL, but LabView will still keep the same value in its wire, since it only passed value. Its confusing, I know.

 

Imagine 1 guy thinking a number, he tells it to his neighbourgh, so they both know what the number is. But if the second guy wants to change it, he cant, ho only knows the value.

If the guy 1 passes a position of such information in his brain, guy 2 can read the number and if he modifies it, guy 1 will think of the new number. However guy 2 cannot move the position.


I am not creating a Handle by making a pointer to a pointer. First, handle can be quite anything, that is used to identify an object of some use. I know LabView calls Handles the pointers to memory pointers pointing at some LabView datatype. The value in the wire, as you correctly stated, is a place in a memory, the fact, that I pass it to DLL in a "pointer style" manner, does not mean I intend to create pointer to it, I just want to know the place in memory, where LabView keeps such value, so I am able to modify it in cases it changes (inside DLL, my DLL is guy 2 and labview is guy 1).

 

Pointer to a memory in heap tells the computer where the memory is. You cannot move a memory and still have the same pointer.

 


@nathand wrote:

If so, then again, in LabVIEW you should be passing the pointer by value. I think this will avoid the dereference cast that's bothering you. As you have it now, you are adding an unnecessary extra layer of reference (LabVIEW is passing a pointer to an address, rather than the address itself), which you then need to cancel out with the cast that you don't like.


I cannot avoid type cast in LabView, since you are LabView limited. I am trying to avoid casting a pointer to integer.
There is a reason why pointer is of datatype pointer. It is platform specific, it can be flat number or something more complex, so anytime I make a number from a pointer, it is risky, I am mixing apples and bananas + the kitten chokes.

 

Thank you for answer.

0 Kudos
Message 7 of 23
(6,093 Views)

@Bublina wrote:

If you pass a variable into DLL "by value", it does exactly what it says, it passes the value. Means, if you want to change the value going through the wire, you cant, you just get the value of it to the function stack. You can assign new value to such value inside DLL, but LabView will still keep the same value in its wire, since it only passed value. Its confusing, I know.


I think this is where your misunderstanding is. A pointer argument is just a 32- (or 64-) bit address passed by value. It's not a special data type. So, if the value that is on the wire is an address, then it should be passed by value, not by pointer. When you pass the address by pointer, what you're doing is passing a pointer to a pointer, forcing you to dereference it.

 

See, for example, this thread: http://forums.ni.com/t5/LabVIEW/array-pointer-from-dll/m-p/1217453#M519958

In that example, DSNewPtr is like your InitMyVars - it puts an address on the wire. Then, MoveBlock is configured with the source parameter by value, because the value on the wire is already an address - even though the prototype for MoveBlock is void MoveBlock(*source, *destination, size). Passing the pointer parameter by value is the correct thing to do and works.

0 Kudos
Message 8 of 23
(6,076 Views)

Thanks for answer, I understand the problematics. Thanks for example. The difference I am trying to point out, is that if you pass a wire value into DLL by value, you CANNOT change the wire value, you only change the value in the DLL copy. Yes, copy is the right word. LabView still has its wire and the DLL function recieves the copy of that value in its own stack. So if you pass like this an address, you cannot change the address (in case it changes).  

 

Try to take a look at the "Call DLL.vi" example. You will see, that all outputs are configured in call library node to be passed by pointers. Inside such DLL, the output is then assigned as

 

*output = func(input);

I do not know, how familiar are you with C code, so pardon me if stating obvious.

The code says: Assign a value of func output with input argument to a place where output lies.

 

Here is a special picture, that describes the difference. Please note, it is just for demonstration, in reality, the copies might occur in different manner.

 

LVimg2.png 

We are a bit off the topic I wanted to discuss, still thank you for input.

 

This pointer/value problem is common to many programming languages. Try following code (ouput is 4, the guy 2 only knew the value).

 

void funct(int val)
{
	val = 5;
}

int _tmain(int argc, _TCHAR* argv[])
{
	int val = 4;
	funct(val);
	printf("%d", val);
	return 0;
}

 

0 Kudos
Message 9 of 23
(6,067 Views)

I understand pointers. I've been programming in C for years. I added some additional comments to your original LabVIEW image to show the equivalent C operations. I hope this will help explain where the problem is. If you were to configure the second CLFN to pass Ptr by value, then you would correctly be calling funcName(Ptr) whereas right now you're calling funcName(&Ptr), and then you have to undo that extra address operation, leading to the cast that you don't like.

original.png

EDIT: after funcName completes, while Ptr is still the same (same address), the memory pointed to by Ptr may have changed. LabVIEW doesn't care or need to know what the function did, or even if that value is a pointer or a number. As far as LabVIEW is concerned, that value is just some reference or index that the DLL is using to locate a particular object.

0 Kudos
Message 10 of 23
(6,049 Views)