Hi, I'm quite new to Nim, although I've been following the language for awhile now. This is probably a simple misunderstanding of the FFI on my part, but I was wondering if anyone could advise on why I'm getting the errors I am. I'm trying to wrap the C client to the Neo4j graph database. Consider this subset of the header file:
// neo4j-client.h
union _neo4j_value_data
{
uint64_t _int;
uintptr_t _ptr;
double _dbl;
};
struct neo4j_value
{
uint8_t _vt_off;
uint8_t _type; /*TODO: combine with _vt_off? (both always have same value)*/
uint16_t _pad1;
uint32_t _pad2;
union _neo4j_value_data _vdata;
};
typedef struct neo4j_value neo4j_value_t;
__neo4j_pure
neo4j_value_t neo4j_int(long long value);
ssize_t neo4j_fprint(neo4j_value_t value, FILE *stream);
I wrote a small test program in C to check that I understand the interface. I simply create a variable v which stores a value that can be used with the database client and then print it.
#include <neo4j-client.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
neo4j_value_t v = neo4j_int(10);
printf("%s\n", "Printing with neo4j-client function:");
neo4j_fprint(v, stdout);
printf("\n%s\n", "Was anything printed?");
/* Output:
Printing with neo4j-client function:
10
Was anything printed?
*/
return EXIT_SUCCESS;
}
I then wrote a wrapper and a small test as follows:
import posix
const
libneo4j* = "libneo4j-client.so"
type
value_data* {.union, final, pure.} = object
`int`*: uint64
`ptr`*: cuint #uintptr_t
`dbl`*: cdouble
value_t* {.final, pure.} = object
vt_off*: uint8
`type`*: uint8
pad1*: uint16
pad2*: uint32
vdata*: value_data
proc newInt*(value: clonglong): value_t {.cdecl, importc: "neo4j_int", dynlib: libneo4j.}
proc fprint*(value: value_t; stream: File): clonglong {.cdecl,
importc: "neo4j_fprint", dynlib: libneo4j.}
var v = newInt(10)
echo repr v
echo "Printing with neo4j-client function:"
discard fprint(v, stdout)
echo "\nWas anything printed?"
#[ Output:
[vt_off = 2,
type = 2,
pad1 = 0,
pad2 = 0,
vdata = [int = 10,
ptr = 10,
dbl = 9.881312916824931e-324]]
Printing with neo4j-client function:
Was anything printed?
]#
As you can see, although a value_t object seems to have been returned with the correct integer encoded, it isn't being printed properly. In fact, I find that none of the library functions which I try to wrap work properly with object arguments. Is there something obvious I'm missing here? Thanks in advance for any help.ptr*: cuint #uintptr_t
Are you sure that that is correct?
For a 64 bit OS I would be not sure. cuint size is generally 4 byte, maybe uintptr_t is 8 byte? See https://stackoverflow.com/questions/1845482/what-is-uintptr-t-data-type
For low level glib of GTK3 3.20 bindings I used:
type
Gpointer* = pointer
But that was tested by me only for 64 bit Linux yet.
Have you built your wrapper with c2nim?
tried it, but it did not fix the problem.
Yes, it was only a guess. What is special in your code is, that you are working with value types, while most C libs I am aware of are using pointers. If the C lib really want to use value types, it may still be a problem how Nim compiler pass value types to the C library. Sorry, can not help. Maybe you should mention which OS and which C compiler you are using, that may make it easier for the experts...
Thanks for that suggestion; I've made some progress. You were right to wonder whether it was related to passing parameters by value. It turns out that
proc fprint*(value: value_t; stream: File): clonglong {.cdecl,
importc: "neo4j_fprint", dynlib: libneo4j.}
gets converted to
typedef N_CDECL_PTR(long long, TY_cCLPRp1DVAZDRduZNEg73g) (value_t_0EYYJ3aKtwLRJQYI9cfqKlA* value, FILE* stream);
For some reason, what should be passed by value instead gets passed by reference, which of course confuses the actual C implementation. When I manually edited the C output to fix this, everything worked as expected. Any idea how I can make Nim compile my code correctly? Or is this a compiler bug?When I manually edited the C output to fix this, everything worked as expected.
Great. You should try to contact Araq or file a bug report to Nim issue tracker.
I can remember complains about such bugs long time ago -- I assumed they were fixed already.
One temporary solution may be use of the byCopy pragma -- I think that is applied to the object itself, not to the procs. I will do a google or forum search to give you an example, stay tuned:
OK, this seems to be a related bug report
https://github.com/nim-lang/c2nim/issues/84
And here is a wrapper where the byCopy pragma is applied to objects: