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

Reply via email to