LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Call Library Node Error 1097 with struct-by-value

Hi everyone,

I have the following struct definition and function prototype:

typedef struct TF_Output {
  TF_Operation* oper;
  int index;  // The index of the output within oper.
} TF_Output;

TF_DataType TF_OperationOutputType(TF_Output oper_out);

TF_DataType is an enum, I assume its size is four bytes but that might not be true. I need to pass the TF_Output struct by value, so I set up the call library node with this interface:

uint32_t TF_OperationOutputType_lv(uintptr_t TF_Operation, int32_t index);

Apparently, that is not correct and I get error 1097. The oper parameter is retrieved from another library function and should be valid. I tried different sizes for the enum and index but that did not help.

Am I missing something obvious? I would like to avoid building a wrapper dll, because there is no lib file and the tensorflow build process on windows is a rather involved endeavour..

Cord

0 Kudos
Message 1 of 7
(2,445 Views)

Hi Cord,

 

have you tried the following:

- Create a cluster according to the struct definition
- Add one parameter for this cluster to your call library node

- Set type to "adapt to type"

- Set data format to "Handles by Value"

 

This does work for me.

Regards

 

ʍolɟɐʇɐp ʞuıɥʇ
0 Kudos
Message 2 of 7
(2,411 Views)

As far as I know, you cannot pass a struct by value using a CLFN. The help documentation states that when you have configured the parameter as adapt to type, clusters will be passed as references. The data format option is only relevant for arrays/strings/paths being wired to a parameter configured as adapt to type.

Matt J | National Instruments | CLA
0 Kudos
Message 3 of 7
(2,405 Views)

Actually while passing a struct as value is not directly supported by LabVIEW, what the OP did try in his tests should have been working. A struct by value is simply passed as each data element in the struct was passed as individual parameter, but you have to account for alignment. The enum return value should not cause any crashes even if the size is not correctly configured, worst case the returned value does not look like what the function returned. The struct in question should also not cause any alignement issues so the shown configuration should actually work both for 32-bit and 64–bit LabVIEW.

That more or less only leaves two or three possibilities:

- the calling convention

- the function pointer despite claiming that it should be valid

- the reality of either the struct or function declaration is different than shown here by the OP

 

An interesting question would be how the oper value is retrieved and if the referencing is done right. Would be helpful to see the declaration for that and both VIs preferably in 2016 or earlier format!

Rolf Kalbermatter
My Blog
Message 4 of 7
(2,400 Views)

@rolfk wrote:

Actually while passing a struct as value is not directly supported by LabVIEW, what the OP did try in his tests should have been working. A struct by value is simply passed as each data element in the struct was passed as individual parameter, but you have to account for alignment.


Interesting, didn't know that

Matt J | National Instruments | CLA
0 Kudos
Message 5 of 7
(2,394 Views)

Sorry for responding so late, I was off last week. Thank you for your responses!

 

Here is an example VI that creates an operation and reads back its properties. You will have to download the dll (https://www.tensorflow.org/install/lang_c) and adjust the path in the vi. The dll is 64 bit.

The operation pointer looks valid, as I can read back the inputs. Calling TF_OperationOutputType produces the error.

 

Someone else managed to build from source and was kind enough to provide a lib file (https://github.com/Neargye/hello_tf_c_api). Using that, I created a very basic wrapper dll with this function:

uint32_t TF_OperationOutputType_lv(uintptr_t oper, int32_t index)
{
TF_Output out;
out.oper = reinterpret_cast<TF_Operation*>(oper);
out.index = index;
return TF_OperationOutputType(out);
}

Calling this function instead of the native one works. sizeof(TF_Operation) is 16, so I suppose this could be a struct packing issue? The calling convention is cdecl, by the way. The api is defined here: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/c/c_api.h.

I'd like to get rid of the wrapper but for now it's ok.

Cord

0 Kudos
Message 6 of 7
(2,366 Views)

Well TF_Operation as defined in the header is an opaque struct, so technically it doesn't really have any size. Why your compiler thinks it is 16 byte is not clear, 0 would be more logical but probably cause other problems elsewhere so they may just return a default value there.

But the size of TF_Operation itself is not important here. You only work with pointers to that opaque struct in the API and the size of the pointer to that struct (sizeof(TF_Operation*)) should be 8 byte for 64 bit operation.

 

More interesting might be sizeof(TF_Output). I would guess it is 12 for 64 bit compilation and 8 for 32 bit compilation, but 16 might be also possible for 64 bit compilation. Thinking about this more you might be having an endianess issue when trying to pass that struct as value by passing the structure elements as individual parameters. The oper* pointer goes into the first 64 bit register,/stack location, and the  index into the second. But index is a 32 bit value while the register/stack is a 64 bit location. Since passing a struct by value is really more like copying the struct byte for byte from the struct memory into the stack location, this index may end up in the wrong half of the 64 bit register when passed as individual parameter. I'm not really sure and at the moment to lazy to think this all the way through. Memory alignment in itself is already tricky enough, trying to map that in my mind to a top-down oriented stack frame is just one step to much for my coffeine depraved brain. Smiley Happy

The easyiest way would be to look in the debugger at the memory content of your out variable in your wrapper and create a second function in the wrapper DLL where you pass those two variables as individual parameters and then break inside that function and look if the layout of the stack variables is different than in the first case.

 

 

Rolf Kalbermatter
My Blog
0 Kudos
Message 7 of 7
(2,359 Views)