[MLton] Callback to function pointer?
Matthew Fluet
fluet@cs.cornell.edu
Tue, 12 Jul 2005 11:15:39 -0400 (EDT)
On Tue, 12 Jul 2005, Wesley W. Terpstra wrote:
> On Tue, Jul 12, 2005 at 08:16:54AM -0400, Matthew Fluet wrote:
> > > MLton can already call methods by function pointer, but it cannot export
> > > methods _to_ function pointer. Would this be difficult? Many C libraries
> > > (especially GUIs and event libraries) use callbacks to C function pointers.
> >
> > Why do you think that MLton cannot export functions that may be called
> > indirectly? An "_export"-ed function is a legitimate C function in the
> > object file. If you want a function pointer to that function, take its
> > address with "&".
>
> I meant something like this:
Ah, now I see your situation.
So, things to clear up. First, the "_export" expression has both a static
component and a dynamic component. To implement exported SML functions,
the compiler creates an array of entry points (essentially, unit -> unit
functions that take care of wrapping the real SML function with fetches
and stores of the C arguments and results). Each (static) "_export" in
the source maps to one element of the array. When C calls an "_export"ed
function, it really calls a stub that sets up the C arguments and then
dispatches in to the MLton runtime with a unique integer indicating which
entry in the array to call.
The dynamic component of an "_export" expression updates the array of
entry points with the SML function passed to "_export".
Hence, it is inadvisable to evaluate the same static "_export" with
different SML functions, since, as you observed, the last SML function
wins. Also, it is inadvisable to call an "_export"ed function from C
before the "_export" expression is evaluated in SML, since no SML function
will have been registered.
Taken together, it is really best to only have "_export" in top-level
declarations -- hence, they will be evaluated exactly once and before
useful SML code evaluates.
Now, if you don't want to leave SML to get the address of an "_export"ed
function, then that is currently supported, using an (as of yet
undocumented) feature of MLton FFI. Essentially,
_import # "name": MLton.Pointer.t;
imports the address of the symbol "name", rather than fetching the
contents of the symbol. See doc/examples/ffi/import2.sml for an example.
Hence, we can write your example as follows:
fun clicker1 () = print "clicked callback 1!\n"
val () = (_export "clicker1" : unit -> unit;) clicker1
val clicker1Ptr = _import # "clicker1": MLton.Pointer.t
fun clicker2 () = print "clicked callback 2!\n"
val () = (_export "clicker2" : unit -> unit;) clicker2
val clicker2Ptr = _import # "clicker2": MLton.Pointer.t
val gui_clicked = _import "gui_clicked": MLton.Pointer.t -> unit;
fun setupclick fPtr = gui_clicked fPtr
val () = setupclick clicker1Ptr
val () = setupclick clicker2Ptr
val () = enterTheGuiMainLoop ()
Admittedly, a little more verbose that what you originally wrote, though
not too bad. I could imagine allowing:
_export # "name" : (tyArg -> tyRes) -> tyPtr;
to mean:
(fn f => ((_export "name" : tyArg -> tyRes;) f; (import # "name" : tyPtr;))
though that is a little complicated to implement (because we need to
elaborate before taking apart the type annotation, so it isn't a simple
syntactic transformation).