I'm trying to wrap this C function:
PmReturn_t heap_init(uint8_t *base, uint32_t size);
here is the wrap I made:
proc heapInit*(
base: openArray[char], size: cuint
): PmReturn {.importc: "heap_init", header: "heap.h".}
where I created a nim enum, PmReturn, to mirror the c enum PmReturn_t.
I am calling this code from a test:
test "heap init SHOULD return OK after a successful init":
var base4 = ['a', 'b', 'c', 'd']
check heapInit(base4, 4) == PmRetOk
When I compile, I get the error:
CC: test_heap.nim
C:\Users\dwhall\nimcache\test_heap_d\@mtest_heap.nim.c: In function 'NimMainModule':
C:\Users\dwhall\nimcache\test_heap_d\@mtest_heap.nim.c:960:164: error: too many arguments to function 'heap_init'
960 | nimlf_(688, "C:\\Users\\dwhall\\.choosenim\\toolchains\\nim-2.2.0\\lib\\pure\\unittest.nim"); colonc1___test95heap_u99 = heap_init(base4__test95heap_u98, 4, ((unsigned int)4));
|
^~~~~~~~~
In file included from C:\Users\dwhall\nimcache\test_heap_d\@mtest_heap.nim.c:10:
src/vm/heap.h:52:12: note: declared here
52 | PmReturn_t heap_init(uint8_t *base, uint32_t size);
| ^~~~~~~~~
Error: execution of an external compiler program 'gcc.exe -c -w -fmax-errors=3 -mno-ms-bitfields -DWIN32_LEAN_AND_MEAN -Isrc/vm -Isrc/platform/posix64 -Dplat=posix64 -IC:\Users\dwhall\.choosenim\tool64 -Dplat=posix64 -IC:\Users\dwhall\.choosenim\toolchains\nim-2.2.0\lib -IC:\Users\dwhall\GoogleDrive\code\p14p\tests\tdd -o C:\Users\dwhall\nimcacmcache\test_heap_d\@mtest_heap.nim.c' failed with exihe\test_heap_d\@mtest_heap.nim.c.o C:\Users\dwhall\nimcache\test_heap_d\@mtest_heap.nim.c' failed with exit code: 1
Please ignore, for now, that I'm using an array of chars as a static memory pool. I'm trying to learn how to properly use FFI.
Doofenstein is 100% right, the openArray is turned into a pointer and a size. Same as happens under the hood in Nim. This means that you end up with an extra argument. Nim code will generate forward declarations when you wrap things, which gives a weak guarantee that you wrap things correctly and allows more flexible linking. However in this case it causes a type mismatch error which is actually fine. So you can do (slightly modified to make it self contained):
{.emit:"""
int heap_init(uint8_t *base, uint32_t size) {
return size;
}
""".}
proc heapInit*(
base: openArray[char]): cint {.importc: "heap_init", nodecl.}
var base4 = ['a', 'b', 'c', 'd']
echo heapInit(base4)
However the more idiomatic wrap would be to use ptr UncheckedArray and a convenience template (or if you wrap a lot of these then a macro which generates said templates through a small pattern language).
Thank you all. I made the helper function and I feel it's headed in the right direction
proc heapInitWrap(
# base: ptr UncheckedArray[uint8], size: cuint
base: ptr uint8, size: cuint
): PmReturn {.importc: "heap_init", header: "heap.h".}
proc heapInit*(
base: openArray[uint8], size: Positive
): PmReturn =
heapInitWrap(
# cast[ptr UncheckedArray[uint8]](base.addr),
cast[ptr uint8](base.addr),
cast[cuint](size))
with the caller:
test "heap init SHOULD return OK after a successful init":
var base4: array[4, uint8]
check heapInit(base4, 4) == PmRetOk
but a different kind of error pops up:
Undefined symbols for architecture arm64:
"_heap_init", referenced from:
_heapInit__OOZOOZsrcZvmZheap_u5 in @m..@s..@ssrc@[email protected]
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I will try the auto-wrapping tools eventually. But I wanted to learn enough about the process to get one example function wrapped.
That is a linker error. The wrapper just tells Nim how it can call this function, but you still need to link whatever code required to run the function into your binary. This is typically done with --passL:"-l<some library>".
Second the openArray already has a length, so there's no need to pass it separately in your wrapper:
proc heapInit*(
base: openArray[uint8]): cint =
heapInitWrap(
cast[ptr uint8](base.addr),
cast[cuint](base.len))