[MLton-devel] Callbacks
Stephen Weeks
MLton@mlton.org
Mon, 19 May 2003 17:59:32 -0700
> Here is a proposal for a MLton.Callback structure to replace
> MLton.FFI.
...
> Thoughts? Comments?
...
> The vararg solution was surprisingly simple. Now the client code looks
> like:
...
It all makes sense. My one minor complaint is that the functions are
curried (I understand that they have to be due to the printf-style
stuff). My major complaint is that there is too much work and room
for error on the C side. I would prefer if the exported function
appeared to be a normal C function.
How about the following approach, which admittedly requires compiler
support, but I think not too much.
Similar to _ffi, add a new core-level expression
_export "<name>": <ty>;
The type of the _export expression is (<ty> -> unit), and it exports a
C function named <name>. The _export declaration evaluates to a
function that when called with a function f, sets the meaning of the C
call to be f. For example
_export "A": int * int -> int; (fn (x1, x2) => x1 + x2)
>From this _export, MLton will construct the necessary C routine and C
prototype so that C code "printf ("%d\n", A(17, 18));" will type
check and will print "35".
Here's how it's implemented. Early in the front end, MLton counts the
number of _export declarations and makes that number available via
_build_const "MLton_numExports": int;
MLton also assigns a unique integer in [0, numExports) to each _export
declaration. That integer will be used to access into the array of
exported functions. I will continue with
_export "A": int * int -> int;
as a running example, assuming it has been assigned slot 13.
As part of elaboration, MLton builds a file z-export.h that contains
the prototypes of all the exported functions. In our example, it
would contain
int A (int x1, int x2);
MLton also builds the appropriate C routine for each export, which is
easy to do from the type. Our example would generate
int A (int x1, int x1) {
MLton_FFI_setI (0, 13);
MLton_FFI_setI (1, x1);
MLton_FFI_setI (2, x2);
MLton_callFromC ();
return MLton_FFI_getI (0);
}
Finally, during elaboration, MLton replaces the export declaration
with the function that will set the appropriate entry in the exports
array.
(fn f: int * int -> int =>
MLton.FFI.register
(13, fn () =>
let
val x1 = MLton.FFI.getI 1
val x2 = MLton.FFI.getI 2
val _ = MLton.Thread.atomicEnd ()
val res = f (x1, x2)
val _ = MLton.Thread.atomicBegin ()
val _ = MLton.FFI.setI (0, res)
in
()
end))
MLton.FFI implements the register function, which simply updates the
array and sets the callFromC handler to dispatch to the appropriate
function.
signature MLTON_FFI =
sig
val getI: int -> int
val register: int * (unit -> unit) -> unit
val setI: int * int -> unit
end
structure MLtonFFI =
struct
val getI = "MLton_FFI_getI": int -> int;
val numExported = _build_const "MLton_numExports": int;
val setI = "MLton_FFI_setI": int * int -> unit
val register =
let
val exports = Array.array (numExported, fn () =>
raise Fail "undefined export\n")
val _ = MLtonThread.handleCallFromC
(fn () => Array.sub (exports, getI 0) ())
in
fn (i, f) => Array.update (exports, i, f)
end
end
That's it. I know it's more compiler support, but the gain in
simplicity and error checking on the C side seems worth it.
-------------------------------------------------------
This SF.net email is sponsored by: ObjectStore.
If flattening out C++ or Java code to make your application fit in a
relational database is painful, don't do it! Check out ObjectStore.
Now part of Progress Software. http://www.objectstore.net/sourceforge
_______________________________________________
MLton-devel mailing list
MLton-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mlton-devel