Skip to content

Instantly share code, notes, and snippets.

@daniel-j
Last active February 28, 2024 21:20
Show Gist options
  • Save daniel-j/3689d53d9c04dd5f5ac404be8c6d3d53 to your computer and use it in GitHub Desktop.
Save daniel-j/3689d53d9c04dd5f5ac404be8c6d3d53 to your computer and use it in GitHub Desktop.
Nim closure wrapping C with generics
# does not leak with --mm:arc :)
# nim c -d:release -d:useMalloc --debugger:native --mm:arc --threads:off closure && valgrind --leak-check=full ./closure
{.emit: """
typedef void(* c_callback) (int param, void *arg);
struct cState {
int param;
c_callback callback;
void *arg;
};
static struct cState storedState = { 0 };
void cFunction(int param, c_callback cb, void *arg) {
if (!cb) return;
storedState.param = param;
storedState.callback = cb;
storedState.arg = arg;
}
void cPoll() {
void *arg = storedState.arg;
if (!arg) return;
storedState.arg = NULL;
storedState.callback(storedState.param + 1, arg);
storedState.callback = NULL;
}
""".}
type
Callback = proc (param: int)
CbState = ref object
cb: Callback
Types = enum
TypeA
TypeB
MyType[T: static[Types]] = ref object
when T == TypeA:
flag: int
elif T == TypeB:
flag: string
cb: Callback
TypeAny = MyType[TypeA] | MyType[TypeB]
proc `=destroy`(cbstate: typeof(CbState()[])) =
echo "Destroyed CbState ", repr cbstate
if cbstate.cb != nil:
# ElegantmagicTM
{.cast(raises: []).}:
`=destroy`(cbstate.addr.cb)
proc `=destroy`(mt: typeof(MyType[TypeB]()[])) =
echo "Destroyed MyType[TypeB] ", repr mt
if mt.cb != nil:
# ElegantmagicTM
{.cast(raises: []).}:
`=destroy`(mt.addr.cb)
`=destroy`(mt.addr.flag)
proc cFunction(param: cint; cb: proc (param: cint; arg: pointer) {.cdecl.}; arg: pointer) {.importc, cdecl.}
proc cPoll() {.importc, cdecl.}
proc cCallback(param: cint; arg: pointer) {.cdecl.} =
assert(arg != nil)
var state = cast[CbState](arg)
let cb = state.cb
state.cb = nil
GC_unref(state)
state = nil
echo "callback removed"
cb(param)
proc myProc(paramIn: int; cb: Callback) =
let state = CbState(cb: cb)
GC_ref(state)
echo "callback registered"
cFunction(paramIn.cint, cCallback, cast[pointer](state))
var complete = false
proc otherFunc(self: MyType[TypeA]; param: int) =
echo "param int: ", repr self
if self.cb != nil:
self.cb(param)
complete = true
proc otherFunc(self: MyType[TypeB]; param: int) =
echo "param str: ", repr self
if self.cb != nil:
self.cb(param)
complete = true
proc main(self: TypeAny) =
var v = 5
echo "main ", repr self
myProc(2, proc (param: int) =
echo "calling other func"
self.otherFunc(param + v)
)
(proc () =
var mt = MyType[TypeB]()
mt.flag = "abc"
mt.cb = proc (param: int) =
echo param, " ", repr mt
mt = nil # release the closure
main(mt)
)()
(proc () =
while not complete:
echo "polling"
cPoll()
)()
echo "complete!"
# to make valgrind happy on orc
GC_fullCollect()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment