02-06-2014 03:44 PM - edited 02-06-2014 03:46 PM
Is it clear that when you configure LabVIEW to pass by pointer, it's the equivalent of putting & (address-of) in front of the parameter? Whereas pass by value is exactly as you'd expect?
void square (int *s){
*s *= *s;
}
int i = 4;
int *iptr;
iptr = &i;
square(iptr);
Since iptr is already a pointer type, you wouldn't call square(&iptr) - but that's effectively what your LabVIEW code is doing, because you're ignoring that the Ptr wire is already a pointer.
EDIT: sorry I was a bit sloppy about the C assignments in the initial post, edited to be clearer.
02-07-2014 12:17 PM
You may have already given up on listening to me about this, or less likely you decided to run some tests and see if I'm right, but in any case after trying to come up with a better way to explain this I'm going to give it one more try by modifying your picture to account for the case that describes what is really happening and isn't shown in your picture:
The LabVIEW wire is the POINTER, not the object being pointed at. So even though you pass the wire to the function by value, the function still receives a pointer and can update the memory.
02-07-2014 02:56 PM - edited 02-07-2014 03:00 PM
Your example is unnccessary complicated:
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); }
You are right that all the typecasts are not only ugly but in fact dangerous as you quickly start to loose the whole overview about what should be what. It's not the poor chickens that get hurt but your brain cells! Therefore ALWAYS avoid typecasts as much as possible. In your example you use typecasts repeatedly and very inconsistently. The only place where a typecast is really appropriate is at the malloc function where you would typecast the returned void * pointer into your application specific pointer type, which in this case happens to be a pointer to a structure containing a pointer or in other words a void **.
Basically you seem to have a function which has following prototype:
int funcWithVars(void *somevalue);
This prototype says nothing in terms of what the somevalue pointer would point to. As such it is not enough to call this function. From the context of your other C code it would seem that the prototype would be in this case actually really:
int funcWhichReturnsAInternalAllocatedPointer(void **ppValue);
and then it starts to make sense and you do not need to create any C code to call this function from LabVIEW.
Simply configure the parameter in the Call Library Node to be a pointer sized integer passed by reference, et voila.
And most likely you have other functions in that library that look more like this:
int someOtherFuncTakingAPointer(void *pValue);
Here you would need to configure the parameter to be a pointer sized integer passed by value.
In the LabVIEW diagram this pointer is always represented as 64 bit integer. But the Call Library Node will take care to either use the full 64 bit on a 64 bit platform or store the 32 bit pointer always in the same consistent location inside those 64 bits when executing on a 32 bit platform.
02-07-2014 03:24 PM
Hi nathand 🙂
I did not run any tests. I was just busy.
In your image you posted, you are accessing the wire value this way.
*value = *value + 1;
If the value is passed "as value", the function prototype would look like this.
void func(int value);
I will go ahead and create probably a first code of its kind that simulates what would happen.
int value = 4; *value = *value + 1;
As you can see, you just made an integer datatype become pointer. Maybe you have a mistake in your code, maybe I do not follow. Plase be adviced, that I know what I am doing with my code and it is working, I am not looking for solution to a broken code. I am was just trying to avoid something, that is not too much OK in C world.
Anyway,
(solution)
Since LabView has no equivalent of pointer type, and uintptr_t (you can choose this datatype in call node, it is the unsigned pointer sized integer) is defined in extcode as unsigned long,
you can carry a pointer around in labview only as a number. Since the extcode.h types become compiled and built once you get your DLL built, it is not platform specific or whatever, it is the same as using uint64_t (I do not really know what is that datatype good for unless NI decides in the future to add a pointer datatype to LabView and modify extcode.h definitions)
Thanks for discussion.
02-07-2014 03:32 PM
@Bublina wrote:
Since LabView has no equivalent of pointer type, and uintptr_t (you can choose this datatype in call node, it is the unsigned pointer sized integer) is defined in extcode as unsigned long,
you can carry a pointer around in labview only as a number. Since the extcode.h types become compiled and built once you get your DLL built, it is not platform specific or whatever, it is the same as using uint64_t (I do not really know what is that datatype good for unless NI decides in the future to add a pointer datatype to LabView and modify extcode.h definitions)
You misread the extcode.h file. There is a precompiler condition for the uintptr_t declaration. Most modern compilers (and definitely every compiler supporting 64 bit compilation) defines the uintptr_t type in one of its standard headers. The extcode.h declaration only comes into play when this definition could not be found in any system headers and that is only true for older compilers, which definitely won't be 64 bit compilers, so there the declaration as long is true, but on newer 64 bit compilers it will be something different and most likely not just an int64_t alias as that has some bad implications for pointer safe operations. Some compilers have special keyword support to allow for pointer to integer conversion without loosing all the type safety checks and their system headers would use that to define the uintptr_t in a way that allows passing it as integer value without loosing the inherent pointer attribute of such a variable.
02-07-2014 03:42 PM
@Bublina wrote:
If the value is passed "as value", the function prototype would look like this.
void func(int value);
NO! This is what I was trying to say about the prototype does not need to match the CLFN configuration. In the example in my previous post, the prototype would be void func (int *value). The CLFN should be configured to pass the parameter BY VALUE - because the value itself is already a pointer (an address). Note this exactly the same thing that rolfk wrote. If you want to continue to ignore both of us you can do so, but we're trying to help you solve your problem.
The C function has no idea about how the CLFN is configured. All parameters - even pointers - are passed by value on the stack. The only difference is that a function that expects a pointer as an argument treats the value on the stack as a memory address, and dereferences it to get the actual value. So, if the LabVIEW wire contains an address, it should be passed by value.
02-07-2014 04:13 PM
@nathand wrote:
@Bublina wrote:
If the value is passed "as value", the function prototype would look like this.
void func(int value);NO! This is what I was trying to say about the prototype does not need to match the CLFN configuration. In the example in my previous post, the prototype would be void func (int *value). The CLFN should be configured to pass the parameter BY VALUE - because the value itself is already a pointer (an address). Note this exactly the same thing that rolfk wrote. If you want to continue to ignore both of us you can do so, but we're trying to help you solve your problem.
The C function has no idea about how the CLFN is configured. All parameters - even pointers - are passed by value on the stack. The only difference is that a function that expects a pointer as an argument treats the value on the stack as a memory address, and dereferences it to get the actual value. So, if the LabVIEW wire contains an address, it should be passed by value.
OOOH, now I seeeeee 😄
So you want me to have such prototype in Call library node, and have a different prototype in the called function !
Man, I will never do that, that is even more mayhem then those pointer to number to pointer casts.
02-07-2014 04:22 PM
@rolfk wrote:
Your example is unnccessary complicated:
I know, I was just trying to provide some generic pattern of bringing some existing code functionality to LabView. I made some simple artificial code so it makes more sense.
I make, and others make code to be used as is, not with the intention to be more labview compatible.
You can see, that those mechanical casts can be macroed.
The text formatting is different (dunno why).
class myRectangle { int mywidth, myheight; public: myRectangle (int width, int height) { mywidth = width; myheight = height; } myRectangle (){} int area() { return mywidth*myheight; } void *copyRectangle(myRectangle *newPos) { if(newPos) { return memcpy(newPos, this, sizeof(myRectangle)); } return NULL; } }; void initRectangle(uint64_t *LVPtr) { myRectangle *ptr = NULL; ptr = new myRectangle(4, 2); *LVPtr = (uint64_t)ptr; } void getArea(uint64_t *LVPtr, int32_t *Area) { myRectangle *ptr = NULL; ptr = (myRectangle*)*LVPtr; *Area = ptr->area(); } int moveRectangle(uint64_t *LVPtr) { myRectangle *ptr = NULL; ptr = (myRectangle*)*LVPtr; myRectangle *newptr = NULL; newptr = new myRectangle; if(ptr->copyRectangle(newptr)) { delete ptr; *LVPtr = (uint64_t)newptr; return 0; } return -1; } void cleanRectangle(uint64_t *LVPtr) { myRectangle *ptr = NULL; ptr = (myRectangle*)*LVPtr; delete ptr; }
So here you have some class you want to use in LabView, so you use the generic pointer typecasting.
02-07-2014 04:26 PM
@rolfk wrote:
@Bublina wrote:
Since LabView has no equivalent of pointer type, and uintptr_t (you can choose this datatype in call node, it is the unsigned pointer sized integer) is defined in extcode as unsigned long,
you can carry a pointer around in labview only as a number. Since the extcode.h types become compiled and built once you get your DLL built, it is not platform specific or whatever, it is the same as using uint64_t (I do not really know what is that datatype good for unless NI decides in the future to add a pointer datatype to LabView and modify extcode.h definitions)
You misread the extcode.h file. There is a precompiler condition for the uintptr_t declaration. Most modern compilers (and definitely every compiler supporting 64 bit compilation) defines the uintptr_t type in one of its standard headers. The extcode.h declaration only comes into play when this definition could not be found in any system headers and that is only true for older compilers, which definitely won't be 64 bit compilers, so there the declaration as long is true, but on newer 64 bit compilers it will be something different and most likely not just an int64_t alias as that has some bad implications for pointer safe operations. Some compilers have special keyword support to allow for pointer to integer conversion without loosing all the type safety checks and their system headers would use that to define the uintptr_t in a way that allows passing it as integer value without loosing the inherent pointer attribute of such a variable.
Dang, I had (it is defined in "fountypes.h") that document opened just as a file, so MSVS did not highlight the code properly.
If only I just googled it 🙂
Thanks for answer.
02-07-2014 04:44 PM
Here is a sample DLL and VI that demonstrate what I have been trying to explain about passing a pointer by value. You can see that in this example, even though the prototype for the AddOne function is void AddOne(int *ptr), the ptr parameter is passed by value, and the code works exactly as expected. I am attaching the VI (LabVIEW 2012), the DLL, and the C file used to build it. This was built in Visual Studio 2010, 32-bit. Note: no type casts!
#include "stdafx.h" #include <malloc.h> extern "C" __declspec(dllexport) void __cdecl InitVar(int **ptr); extern "C" __declspec(dllexport) void __cdecl AddOne(int *ptr); extern "C" __declspec(dllexport) void __cdecl ClearVar(int *ptr); extern "C" __declspec(dllexport) int __cdecl GetVal(int *ptr); __declspec(dllexport) void InitVar(int **ptr){ *ptr = (int *)malloc(sizeof(int)); **ptr = 1; } __declspec(dllexport) void AddOne(int *ptr){ *ptr += 1; } __declspec(dllexport) void ClearVar(int *ptr){ free(ptr); } __declspec(dllexport) int GetVal(int *ptr){ return *ptr; }