So, let me explain what I'm trying to do:
I've created a Nim wrapper for a C/C++ library (basically, https://github.com/webview/webview).
I need to pass a proc as a callback to a C function expecting a void*. Initially I declared it as pointer, then I decided to typedef it as:
type
WebView* = pointer
Callback* = proc (s: cstring, r: cstring, a: pointer) {. cdecl .}
The problem is... it generally works, but whenever I pass a Callback to the function in question I cannot capture any outer variable from inside the callback body and keep getting the following error:
Error: illegal capture 'callback' because ':anonymous' has the calling convention: <cdecl>
So... instead of randomly changing pragmas (because I honestly do not know 100% what is going on), I decided to ask you and see if there's any viable solution to this situation.
I'm all ears :)
proc closurething(x: int) =
proc c =
echo x
proc d {.cdecl.} = discard
proc e = discard
echo pointer.sizeof # 8
echo c.sizeof # 16
echo d.sizeof # 8
echo e.sizeof # 8
closurething(1)
C functions that sets callback function usually takes a pointer to callback function and a pointer (typically void* type) that is passed to the callback function when it is called. That pointer is usually called user data and points to heap memory that contains variables used in the callback function. That user data pointer is held by a C object and that means the object that user data pointer points to is not managed by Nim's GC and you must free it manually. So I might forget to free it or read the pointer after freed.
I uses id and table instead of pointer to avoid such a problem. Create an unique id and an object that I want to use in the callback function and add them to the global table. Then, pass the unique id instead of a pointer to C function that takes pointer to callback function. When the callback is called, cast user data pointer to a int and get the object from global table. I can check whether the object the callback function is about to use is exists.
For example:
# Example C function binding code
type
CObject = object
callback: proc (arg: int; userData: pointer) {.cdecl.}
userData: pointer
proc cfuncSetCallback(cobj: ptr CObject; callback: proc (arg: int; userData: pointer) {.cdecl.}; userData: pointer) {.cdecl.} =
cobj.callback = callback
cobj.userData = userData
proc cfuncDoSomething(cobj: ptr CObject) {.cdecl.} =
echo "cfuncDoSomething"
cobj.callback(123, cobj.userData)
# Example user code
import tables
type
Mydata = ref object
mydata: string
var
idToMydata: Table[int, Mydata]
idCounter: int
proc main =
var cobj: CObject
proc mycallback(arg: int; userData: pointer) {.cdecl.} =
let id = cast[int](userData)
assert id in idToMydata
var mydata = idToMydata[id]
echo arg
echo mydata.mydata
inc idCounter
let id = idCounter
idToMydata[id] = Mydata(mydata: "My data")
cfuncSetCallback(cobj.addr, mycallback, cast[pointer](id))
cfuncDoSomething(cobj.addr)
del idToMydata, id
assert idToMydata.len == 0
main()