It is my pleasure to announce the first release of ppx_cstubs!
ppx_cstubs is ppx-based preprocessor for quick and dirty stub generation with ctypes. It creates two files from a single OCaml file: a file with c stub code and a ml file with all additional boilerplate code.
The preprocessor abuses external
declarations that are not used in regular code. Instead of OCaml types, values of type Ctypes.typ are used inside the declarations:
/* function prototypes */
int puts(const char *s);
char *getenv(const char *name);
To access the functions above from OCaml, you simply repeat the prototype in OCaml syntax and with appropriate Ctypes.typ
values:
external puts: string -> int = "puts"
external getenv: string -> string_opt = "getenv"
let () = (* regular code *)
match getenv "HOME" with
| None -> ()
| Some s ->
let i = puts s in
...
As a slight extension of the scheme above, you can also label your parameters, annotate external (%c
) and write a few lines of C code:
external%c flush_printf : str:string -> i64:int64_t -> bool = {|
int r = printf("first param:%s; second param:%" PRId64 "\n", $str, $i64);
if ( r < 0 ) {
return false;
}
r = fflush(stdout);
return (r == 0); /* `return` is mandatory, unless your function is void */
|} [@@ release_runtime_lock]
/* other possible attributes are [@@ noalloc] and [@@ return_errno] */
let () =
let r : bool = flush_printf ~str:"Hello World" ~i64:4L in
...
This way several switches between the OCaml runtime and C are avoided. This has various advantages:
-
Intermediate results can be stored on the C stack. They don’t need to be allocated on the heap and wrapped in a way to appease the OCaml runtime.
-
the c compiler can better optimise your code.
-
constant parameters don’t need to exposed to OCaml, just to pass them to the C function.
-
you often have to write (and generate) less code, if you don’t create wrappers for every c function and type, but just wrap snippets of C code.
Further possibilities, examples, and limitations are described in the project’s README.