Last active
February 28, 2024 21:20
-
-
Save daniel-j/3689d53d9c04dd5f5ac404be8c6d3d53 to your computer and use it in GitHub Desktop.
Nim closure wrapping C with generics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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