ashinn / chibi-scheme

Official chibi-scheme repository
Other
1.22k stars 141 forks source link

Add mechanism to pass user data to foreign function #935

Open w0utert opened 1 year ago

w0utert commented 1 year ago

Foreign functions bound using sexp_define_foreign are required to have a signature that only includes the context, a reference to the function itself (it's not completely clear to me what the typical use case for this is), the number of arguments, and of course the arguments itself. There is no way to add any kind of user data to the binding (e.g. a void * as is quite typical for embedded scripting languages).

This is pretty annoying if you want to emulate calling the bound function as a closure, e.g. to receive a 'this' pointer to some native class that implements the foreign function logic. It seems the only option is to a) use some global variable directly visible to the bound function (which I don't like because I intend to have different concurrent chibi contexts in the same process), or b) define the user data pointer inside the Scheme environment as a fixnum variable with a magic name, which the bound function can retrieve from the chibi context it receives.

I now use b) which works but it feels nasty to poke around in the Scheme environment to communicate between the VM and native code.

Would it be possible to have another variant of sexp_define_foreign that can take an extra void * argument (or if this goes agains the philosophy of the C API design asexp would also work) which is passed to the bound function whenever it is called from Scheme code?

ashinn commented 1 year ago

There's sexp_opcode_data (and data2) which is accessible from the self binding from the stub:

sexp sexp_foo (sexp ctx, sexp self, sexp_sint_t n) {
  ...
  res = foo(self == NULL ? NULL : (MyStruct*) sexp_opcode_data(self));
  ...
}

You can probably convince the FFI to output that code automatically. I'll try to provide some examples.

w0utert commented 1 year ago

This works and does exactly what I need, thanks!

You can probably convince the FFI to output that code automatically.

For my use case this is fine, I only have a single foreign function and I don't expect I will need many others, so I can just bind it explicitly using sexp_define_foreign and set the opcode data manually.

I'll try to provide some examples.

A mention of sexp_opcode_data in the manual would also be useful, in the section about foreign functions, e.g. to explain the self parameter passed to the foreign function is useful for this.