I couldn't make the `rawEnv` and `rawProc` work mostly because I don't have control over the order of parameters passed to the proc to make sure the userdata is passed around correctly.
Instead I wrapped the `proc` type inside a tuple that I allocate before making my C call, and I store this tuple alongside the C wrapper object so I have the guarantee that its lifetime will match the C object lifetime. Code is here: <https://github.com/mildred/easy_sqlite3/blob/ae9cde848debf023196ff9a53d82d00533b71c4c/src/easy_sqlite3/bindings.nim#L506> For the future here is the relevant piece of code: type SqliteRawAuthorizer* = proc( userdata: pointer, action_code: AuthorizerActionCode, arg3, arg4, arg5, arg6: cstring): AuthorizerResult {.cdecl.} Authorizer* = proc( action_code: AuthorizerActionCode, arg3, arg4, arg5, arg6: Option[string]): AuthorizerResult RawAuthorizer = object authorizer: Authorizer type Database* = object raw*: ptr RawDatabase stmtcache: Table[CachedHash[string], ref Statement] authorizer: ref RawAuthorizer proc setAuthorizer*(db: var Database, callback: Authorizer = nil) = let userdata: ref RawAuthorizer = new(RawAuthorizer) userdata.authorizer = callback proc raw_callback( userdata: pointer, action_code: AuthorizerActionCode, arg3, arg4, arg5, arg6: cstring): AuthorizerResult {.cdecl.} = let callback = cast[ref RawAuthorizer](userdata).authorizer callback(action_code, arg3.toS(), arg4.toS(), arg5.toS(), arg6.toS()) var res: ResultCode if callback == nil: res = db.raw.sqlite3_set_authorizer(nil, nil) else: res = db.raw.sqlite3_set_authorizer(raw_callback, cast[pointer](userdata)) db.authorizer = userdata if res != ResultCode.sr_ok: raise newSQLiteError res Run
