Hi David, I'm about to start generating code that makes calls to C via the low-level FFI. I was planning to use a form where parameters that provide return values are temporary vols created by alloc, as shown in the example SML code below (as suggested in the C interface documentation). However, it appears that the high-level FFI implementation doesn't do things this way - it uses call_sym_and_convert. Is that more efficient and should I be looking at that?
Using the alloc form works when a parameter is both and input and an output: in the example C code below, the second parameter `pos` is an in-out. I notice that call_sym_and_convert doesn't have an InOut constructor for Union.directedArg. This appears to be a problem for ML types that are not a vol (i.e. not passed by reference in the C FFI) such as int. Is there a way around this?
Thanks, Phil
/* -- C -- */
#include <string.h>
#define TRUE (0 == 0) #define FALSE (0 != 0)
/* get_next (s, pos, c) copies the character at offset pos in string s * to c, increments pos and returns TRUE, if pos is a valid index into s. * If pos is not a valid index, the function returns FALSE without * incrementing pos or writing to c. */ int get_next (const char *s, /* in */ int *pos, /* in out */ char *c) /* out */ { int len = strlen (s); if (0 <= *pos && *pos < len) { *c = s[*pos]; (*pos)++; return TRUE; } else { return FALSE; } }
(* -- SML -- *)
open CInterface
val lib = load_lib "testlib.so.1"
val (fromCbool, toCbool, Cbool) = breakConversion BOOL val Cstring = Cpointer Cchar
val get_next = fn (in1, in2) => let val sym = load_sym lib "get_next" val tmp2 = alloc 1 Cint val tmp3 = alloc 1 Cchar val () = assign Cint tmp2 (toCint in2) val args = [ (Cstring, toCstring in1), (Cpointer Cint, address tmp2), (Cpointer Cchar, address tmp3) ] val res = fromCbool (call_sym sym args Cbool) val out2 = fromCint tmp2 val out3 = fromCchar tmp3 in (res, out2, out3) end;
get_next ("hello", 0); get_next ("hello", 1); get_next ("hello", 2); get_next ("hello", 3); get_next ("hello", 4); get_next ("hello", 5);
(* Poly/ML output *)
... val it = (true, 1, #"h"): bool * int * char val it = (true, 2, #"e"): bool * int * char val it = (true, 3, #"l"): bool * int * char val it = (true, 4, #"l"): bool * int * char val it = (true, 5, #"o"): bool * int * char val it = (false, 5, #"^@"): bool * int * char
Phil Clayton wrote:
Using the alloc form works when a parameter is both and input and an output: in the example C code below, the second parameter `pos` is an in-out. I notice that call_sym_and_convert doesn't have an InOut constructor for Union.directedArg. This appears to be a problem for ML types that are not a vol (i.e. not passed by reference in the C FFI) such as int. Is there a way around this?
I think I've answered the second question for myself: convert the value to a vol using a suitable toCxxx function and then just us the In constructor. (Therefore, in my previous example, the alloc could have been avoided for the in-out parameter.)
On 19/09/2010 16:09, Phil Clayton wrote:
I'm about to start generating code that makes calls to C via the low-level FFI. I was planning to use a form where parameters that provide return values are temporary vols created by alloc, as shown in the example SML code below (as suggested in the C interface documentation). However, it appears that the high-level FFI implementation doesn't do things this way - it uses call_sym_and_convert. Is that more efficient and should I be looking at that?
Hi Phil, It's some time since I looked at this so it would take me a while to re-familiarise with it. I seem to recall, though, that call_sym_and_convert is more efficient since it does all the argument and result conversion in with the foreign function call as a single RTS call. I think with call_sym the arguments need to be converted to vols as separate calls. I wasn't involved in writing this code; it was done by AHL but my impression is that call_sym is only there for backwards compatibility. Regards, David