Ciml is a Camlp4 extension which allows the inlining of C code and provides helpers for type conversion inside OCaml code. For example, one could use the following :
letext hello (s:string) : unit = <<
printf("Hello %s !\n", s);
Return();
>>
It is important to understand how Ciml works in order to use it correctly. Ciml does not compile C code, nor does it use Camlp4 to do so. What Ciml does is just strip out the C code from the OCaml code, and write it to a temporary C file. Using an intelligent Makefile you then compile that file and then link it to the OCaml compiled module.
The letext
statement is very similar to OCaml's let
statement, and they share the same syntax. However, please note that letext
statement's are only allowed at a top level (not nested).
Moreover, as the functions are declared as external, their type cannot be infered. This is why one needs to manually declare all types (which should nevertheless be done by using the external
statement).
C code is surrounded by <<
and >>
which represents quotations for Camlp4. If some C code is found at an expression level (inside a letext
) it will automatically be wrapped into a C function declaration. C code found at the top level (statement level) will just be copied to the temporary C file.
Here is an example :
<<
// this is some C code at the statement level
// it is directly copied
#include <stdio.h>
>>
letext hello (s:string) : unit = <<
printf("Hello %s !\n", s);
Return();
>>
When preprocessed, the following file is created :
#include <caml/mlvalues.h>
#include <caml/alloc.h>
#include <caml/memory.h>
#include <caml/fail.h>
#include <caml/callback.h>
#include <caml/custom.h>
#include <caml/intext.h>
// this is some C code at the statement level
// it is directly copied
#include <stdio.h>
value hello (value s__0) {
CAMLparam1(s__0);
char* s = String_val(s__0);
#define Return(foo) CAMLreturn(Val_unit)
printf("Hello %s !\n", s);
Return();
#undef Return
}
You might notice that several other instructions were added to the source code :
CAMLParam1
and CAMLreturn
, those are used to deal with the garbarge collector#define Return(foo)
, this allows you to simply write Return()
at the end of your function, instead of some complex stuff.Here again, a little history: when a C function is called from OCaml as external
, OCaml passes its arguments with the value
C type. One then uses one of the Int_val
, String_val
, etc. macro to convert the value
to whatever type is needed.
As Ciml is aware of the type of the arguments, automatic type converters are implemented. For example, when one writes :
letext hello (s:string) : unit = <<
printf("Hello %s !\n", s);
Return();
>>
The s
argument is passed as a value
to the C function, but there is an automatic conversion using String_val
(because we know that s
is a string). Conversely, returned values are automatically converted:
// use:
Return(42);
// don't use:
Return(Val_int(42));
Currently, type converters are implemented for int
, bool
, float
, string
, unit
.
You can add and remove custom converters using the following syntax:
(* add *)
register_fromval "Int_val" "int" : int
register_toval "Val_int" : int
(* remove *)
unregister_fromval : int
unregister_toval : int
The sources are available in a darcs repository:
darcs get http://chadok.info/darcs/ciml
and can be browsed in a the Redmine project:
http://redmine.chadok.info/projects/ciml