[MLton] Callback functions: how?

Wesley W. Terpstra wesley at terpstra.ca
Wed Feb 14 10:59:57 PST 2007

What's the most efficient way to implement callback functions in MLton?

As I understand it, the code
> val fns : (int -> unit) list ref = ref [ fn _ => () ]
> val register f = fns := f :: !fns
> val runAll x = List.app (fn f => f x) (!fns)
will prevent flow-analysis and the runAll method will have a loop  
over a giant switch statement that could call all possible functions  
of type 'int -> unit'. Is this correct? Or can MLton recognize that  
only methods passed to 'register' need to be in the switch statement?

I'm trying to wrap the standard C idiom of 'void registercb(const  
char* name, void (*cb)(void* uarg, ...), void* uarg);'. My best idea  
so far is to use the 'uarg' as a word that is the index into some SML- 
side vector of callback functions. eg:
> local
>   val fns = GrowingVector.empty
>   fun runOne (id, x) = GrowingVector.sub (fns, Word.toInt id) x
>   val () = _export "mlton_lib_ufnhook": (word * ... -> unit) ->  
> unit; runOne
>   val runOne_addr = _address "mlton_lib_ufnhook" : MLton.Pointer.t;
>   val Cregistercb = _import "registercb" : string * MLton.Pointer.t  
> * word -> unit;
> in
>   fun registercb (name, f) = registercb (name, runOne_addr,  
> Word.fromInt (GrowingVector.insert f))
> end
Is there a better way? Can MLton (still) recognize that only  
registercb'd methods need be in the switch?

This idiom appears in enough C libraries that we should really have a  
good solution for this in the FFI section of the wiki. If a good  
solution is relatively complex, perhaps we should offer a small  
library (I'l volunteer to write it). For my scenario, it's quite  
important that this be as fast as possible---callbacks are invoked  
inside a tight loop, and the callbacks themselves are very simple.

Also, what's the best known way to implement GrowingVector? I've been  
> datatype 'a used = FREE of int | USED of 'a
> type 'a t = { free: int, buf: 'a used array }
where buf doubles in size when free = ~1. This isn't such a big deal,  
since 'registercb' is rarely invoked compared to 'runOne'. However,  
I'd like to know a better solution.

|'m assuming Henry has a good solution to this in his secret mGtk/ 
MLton port. Of course, it's not in svn, so I can't look and see...

More information about the MLton mailing list