http://www.linux-nantes.org/~fmonnier/OCaml/ocaml-wrapping-c.php
How to wrap C functions to OCaml
This document is a small tutorial that introduces how to call C functions from OCaml. Which is usefull if you want to make a mixed C and OCaml application, if you want to call some functions you need from a C library, or to write a complete binding to a C library.
Contents
- Quick start
- Compiling C and OCaml together
- Returning Basic Types
- Accessing Tuples
- Accessing Arrays
- Accessing Lists
- Creating Tuples and Arrays
- Creating Lists
- Creating Floats arrays
- Enums / Variants
- Convert C bit fields
- Exchanging Raw Data
- More than 5 parameters
- Raising Exception
- Pointers to C structures
- Finalisation of C objects
- Custom Operations Structure
- Custom Finalisation
- Variants with arguments
- The type 'a option
- optional labeled parameter
- Polymorphic Variants
- Linking against a library
- Call Caml functions from C
- Start-up from C
- Mixing with C++
- Conclusion, going further
- Licence of this document
Quick start
Let's start with the simpler case one can find, calling a C function with an integer parameter. This is achieved in an .ml file by this line:
external send_an_int: int -> unit = "get_an_int"
The external keyword tells the compiler that the function comes from outside the OCaml world. The compiler needs the function name which will be seen from the OCaml area, its type, and the name of the C function which will be called.
In the C source file, define a C function whose name is that given in the OCaml side between quotes. This function must have return type
CAMLprim value
and its parameter type has to be
value
. All parameters given to C functions from OCaml are of type
value
. The include header
<caml/mlvalues.h>
provides conversion macros to convert the type
value
to C native types. In this case to convert to a C integer, that macro is
Int_val()
. Then as the return value of the OCaml function is
unit
, the C function have to return a unit, which is done with the macro
Val_unit
.
#include <stdio.h> #include <caml/mlvalues.h> CAMLprim value get_an_int( value v ) { int i; i = Int_val(v); printf("%d/n", i); return Val_unit; }
Of course, think to include the C headers you need, the standard library or other libraries.
Compiling C and OCaml together
Then to compile these two files together, here are the command lines you will need:
ocamlc -i funs.ml > funs.mli ocamlc -c funs.mli ocamlc -c funs.ml ocamlc -c wrap.c ocamlmklib -o _wrap_stubs wrap.o ocamlc -a -custom -o funs.cma funs.cmo -dllib dll_wrap_stubs.so
But it will be more handy with a basic
Makefile
:
wrap.o: wrap.c ocamlc -c $< dll_wrap_stubs.so: wrap.o ocamlmklib -o _wrap_stubs $< funs.mli: funs.ml ocamlc -i $< > $@ funs.cmi: funs.mli ocamlc -c $< funs.cmo: funs.ml funs.cmi ocamlc -c $< funs.cma: funs.cmo dll_wrap_stubs.so ocamlc -a -o $@ $< -dllib -l_wrap_stubs funs.cmx: funs.ml funs.cmi ocamlopt -c $< funs.cmxa: funs.cmx dll_wrap_stubs.so ocamlopt -a -o $@ $< -cclib -l_wrap_stubs clean: rm -f *.[oa] *.so *.cm[ixoa] *.cmxa
And if you use a library in the file wrap.c, you need to add some extra compilation instructions to link against the library, which is described in
another paragraph below.
Just a detail about the command
ocamlc -c wrap.c
, you can replace this line by one of the line below for cases when you wish to give extra arguments to gcc:
ocamlc -c -cc "gcc -o wrap.o" wrap.c gcc -c -I"`ocamlc -where`" wrap.c
Also I would recommand trying to replace the command
ocamlc -c wrap.c
by
ocamlc -verbose -c wrap.c
so that the ocamlc compiler will print out all the internal commands that it will proceed.
Then you can test the result opening the .cma file in the top-level:
% ocaml funs.cma # open Funs;; # send_an_int 9 ;; 9 - : unit = ()
Or at your option from a test script:
#load "funs.cma" ;; open Funs ;; let () = send_an_int 5; ;;
You also have the option, if you just want to build an C-OCaml mixed application without to build an intermediate .cma / .cmxa binding, to directly compile the objects together:
make wrap.o make funs.cmx ocamlopt wrap.o funs.cmx -o myapp ./myapp
OK, Now if everything works, let's see how to convert the other types. floating point numbers, and strings:
external send_a_float: float -> unit = "get_a_float" external send_a_string: string -> unit = "get_a_string"
CAMLprim value get_a_float( value v ) { float f; f = Double_val(v); printf("%f/n", f); return Val_unit; } CAMLprim value get_a_string( value v ) { char *s; s = String_val(v); printf("%s/n", s); return Val_unit; }
The
Double_val()
conversion macro can be used both for C floats and C doubles.
Returning Basic Types
Now let's see how to return these basic types from C to OCaml.
external get_an_int: unit -> int = "send_an_int"
CAMLprim value send_an_int( value unit ) { int i; i = 6; return Val_int(i); }
The conversion macro
Val_int()
is similar to the macros seen before, it converts a C integer into an OCaml integer value.
For cases where C code uses long integers, instead of
Int_val()
just use
Long_val()
.
In the same way you can convert an OCaml boolean to a C integer with
Bool_val()
, and a C integer to an OCaml boolean with
Val_bool()
. For convenience there are also macros
Val_true
and
Val_false
.
Allocated Types
It goes a bit different for floats and strings than ints, since these types have to be allocated for OCaml, and one thing important to know in OCaml is that when an allocation is done (a new value created), a garbage collection may be done. If the allocation is given at the end point in the C
return
statement, all is fine. But notice that in other cases which will be seen below, additional statements have to be used.
external get_a_float: unit -> float = "send_a_float" external get_a_string: unit -> string = "send_a_string"
#include <caml/alloc.h> CAMLprim value send_a_float( value unit ) { double d; d = 2.6; return caml_copy_double(d); } CAMLprim value send_a_string( value unit ) { char *s = "the walking camel"; return caml_copy_string(s); }
In more complex functions where input values are still used after a new OCaml value is allocated, there is a risk that the input value cannot be used because it has been garbage collected when the new OCaml value is allocated. In such cases you have to use the macros
CAMLparamN()
(where
N is the number of parameters) at the beginning of the function and at the end instead of
return
use
CAMLreturn()
. And for local allocated values, use the declaration
CAMLlocalN()
. For example you could write the previous functions "send_a_float" and "send_a_string" in this way:
CAMLprim value send_a_float( value unit ) { CAMLparam1(unit); CAMLlocal1(ml_f); double d; d = 2.6; ml_f = caml_copy_double(d); CAMLreturn(ml_f); } CAMLprim value send_a_string( value unit ) { CAMLparam1(unit); CAMLlocal1(ml_s); char *s = "the walking camel"; ml_s = caml_copy_string(s); CAMLreturn(ml_s); }
When you are not sure if you need to use these macro or not, it's better to use these to stay in peace with the garbage collector, since using these macros won't cause problems, even if they are not needed. That's why you will see below cases where they are used while not strictly needed. Moreover, a lot of OCaml users recommend always using these to avoid the risk of forgetting to use them when they are needed.
Also when using this set of macros, add this header where they are defined:
#include <caml/memory.h>
Constructed Types
Now let's see how to inspect and construct more complex structures like tuples, arrays, lists and records.
Accessing Tuples
To access the content of an OCaml tuple, the
Field()
macro is provided to access each piece of the tuple. Then each field is as usual of type value which needs to be converted to native C types.
external inspect_tuple: int * float * string -> unit = "inspect_tuple"
CAMLprim value inspect_tuple( value ml_tuple ) { CAMLparam1( ml_tuple ); CAMLlocal3( vi, vf, vs ); vi = Field(ml_tuple, 0); vf = Field(ml_tuple, 1); vs = Field(ml_tuple, 2); printf("%d/n", Int_val(vi)); printf("%f/n", Double_val(vf)); printf("%s/n", String_val(vs)); CAMLreturn( Val_unit ); }
You can check the result is as expected from the top-level:
# inspect_tuple (3, 2.4, "functional") ;; 3 2.400000 functional - : unit = ()
Or the C part may have been written in a more concise way to get the same result:
CAMLprim value inspect_tuple( value ml_tuple ) { printf("%d/n", Int_val(Field(ml_tuple,0))); printf("%f/n", Double_val(Field(ml_tuple,1))); printf("%s/n", String_val(Field(ml_tuple,2))); return Val_unit; }
(Since there are no allocation here.)
Accessing fields of a record is exactly the same than for a tuple:
type rec_t = { a:int; b:float; c:string } external inspect_record: rec_t -> unit = "inspect_record"
CAMLprim value inspect_record( value ml_record ) { printf("%d/n", Int_val(Field(ml_record, 0))); printf("%g/n", Double_val(Field(ml_record, 1))); printf("%s/n", String_val(Field(ml_record, 2))); return Val_unit; }
The index number of the fields are ordered as the fields appear in the OCaml declaration of the record type.
# inspect_record {a=26; b=4.3; c="camelids folks" } ;; 26 4.3 camelids folks - : unit = ()
There is though a particular case when all the fields of the record are of type float, in this case the record is seen from C as a float array, see below for this particular case.
Accessing Arrays
Accessing fields of arrays is very similar:
external inspect_int_array: int array -> unit = "inspect_int_array"
CAMLprim value inspect_int_array( value ml_array ) { CAMLparam1( ml_array ); int i, len; len = Wosize_val(ml_array); for (i=0; i < len; i++) { printf("%d/n", Int_val(Field(ml_array, i))); } CAMLreturn( Val_unit ); }
The only trick here is to get the number of elements in the array. And also if the array is a
float array
the macros to get the number of elements and to access the content are a bit different:
external inspect_float_array: float array -> unit = "inspect_float_array"
CAMLprim value inspect_float_array( value ml_float_array ) { CAMLparam1( ml_float_array ); int i, len; len = Wosize_val(ml_float_array) / Double_wosize; for (i=0; i < len; i++) { printf("%g/n", Double_field(ml_float_array, i)); } CAMLreturn( Val_unit ); }
If you test these two functions:
# inspect_int_array [| 2; 4; 3; 1; 9 |] ;; 2 4 3 1 9 - : unit = () # inspect_float_array [| 2.4; 5.8; 6.9; 12.2; 32.8 |] ;; 2.4 5.8 6.9 12.2 32.8 - : unit = ()
Accessing Lists
Now here is how you can access the content of an OCaml list:
external inspect_list: string list -> unit = "inspect_list"
CAMLprim value inspect_list( value ml_list ) { CAMLparam1( ml_list ); CAMLlocal1( head ); while ( ml_list != Val_emptylist ) { head = Field(ml_list, 0); /* accessing the head */ printf("%s/n", String_val(head)); ml_list = Field(ml_list, 1); /* point to the tail for next loop */ } CAMLreturn( Val_unit ); }
Test it from the top-level:
# inspect_list ["hello"; "you"; "world"; "camelids"] ;; hello you world camelids - : unit = ()
As you can see lists are constructed as pair of items, the first item in the pair is the head, and the second is the tail, which can be another list or the empty list which is written as
[]
on the OCaml side, and
Val_emptylist
on the C side.
As an illustration of this you can test from the top-level typing these commands:
# external trans: (int * (int * (int * (int * int)))) -> int list = "%identity" ;; external trans : int * (int * (int * (int * int))) -> int list = "%identity" # trans (1, (2, (3, (4, 0)))) ;; - : int list = [1; 2; 3; 4]
Here "%identity" is a built-in OCaml function which just allows to convert a type to another, keeping its internal representation identical.
And you can also notice here that the last integer given is 0, since as you can see reading the
<caml/mlvalues.h>
header that
Val_emptylist
is defined as
Val_int(0)
.
Creating Tuples and Arrays
Now let's see how to construct a tuple on the C side and returning it to OCaml:
external create_tuple: 'a -> 'b -> 'c -> 'a * 'b * 'c = "create_tuple"
CAMLprim value create_tuple( value a, value b, value c ) { CAMLparam3( a, b, c ); CAMLlocal1( abc ); abc = caml_alloc(3, 0); Store_field( abc, 0, a ); Store_field( abc, 1, b ); Store_field( abc, 2, c ); CAMLreturn( abc ); }
# create_tuple 38 'G' "www.ifrc.org" ;; - : int * char * string = (38, 'G', "www.ifrc.org")
The first parameter of
caml_alloc()
is the number of fields, and the second one indicates the tag of the value, here it is 0 because for ordinary ocaml values like tuples there is no tags needed.
And it works the same for records, as we have seen they have the same internal representation as tuples, except when the record contains only floats.
This also works for arrays except that you have to make sure that all the elements of the array have the same type. All arrays can be created this way except
float array
s that have a different internal representation.
In the same manner than the list, you can verify that records and tuples have the same internal representation with:
# type rec_b = { i:int; c:char; s:string } ;; type rec_b = { i : int; c : char; s : string; } # external trans: rec_b -> int * char * string = "%identity" ;; external trans : rec_b -> int * char * string = "%identity" # trans { i=38; c='G'; s="www.ifrc.org" } ;; - : int * char * string = (38, 'G', "www.ifrc.org")
# external create_rec_b: int -> char -> string -> rec_b = "create_tuple" ;; external create_rec_b : int -> char -> string -> rec_b = "create_tuple" # create_rec_b 38 'G' "www.ifrc.org" ;; - : rec_b = {i = 38; c = 'G'; s = "www.ifrc.org"}
Creating Lists
At this point you should be able to find how to build an OCaml list form C. Here is an exemple:
external string_explode: string -> char list = "create_list"
CAMLprim value create_list( value ml_str ) { CAMLparam1( ml_str ); CAMLlocal2( cli, cons ); char *str = String_val(ml_str); int len = caml_string_length(ml_str); int i; cli = Val_emptylist; for (i = len - 1; i >= 0; i--) { cons = caml_alloc(2, 0); Store_field( cons, 0, Val_int(str[i]) ); // head Store_field( cons, 1, cli ); // tail cli = cons; } CAMLreturn( cli ); }
# string_explode "OCaml" ;; - : char list = ['O'; 'C'; 'a'; 'm'; 'l']
Creating Floats arrays
And as the floats arrays are particular, they are a bit different to create from C, notice both the length trick and the tag when allocating:
And to store elements use
float_array = caml_alloc(length * Double_wosize, Double_array_tag);
And to store elements use
Store_double_field()
instead of
Store_field()
, and there's no need to use the
caml_copy_double
in it.
Enums / Variants
For wrapping C enums, or for wrapping params defined as constant with
#define
the method is the same. Both can be wrapped on OCaml enumerated variants with constants constructors without parameters. From the C site the constructors is represented as integers ordered from 0 to (N - 1) (where N is the number of constructors in the variant type). So basically you can just use a
switch
or an array containing all these values make match the C value. For instance:
type moving = | WALKING | RUNNING | SWIMMING | FLYING external send_enum: moving -> unit = "wrapping_enum_ml2c"
typedef enum _moving { WALKING, RUNNING, SWIMMING, FLYING } moving; CAMLprim value wrapping_enum_ml2c( value v ) { moving param; switch (Int_val(v)) { case 0: param = WALKING; break; case 1: param = RUNNING; break; case 2: param = SWIMMING; break; case 3: param = FLYING; break; } switch (param) { case WALKING: puts("Walking"); break; case RUNNING: puts("Running"); break; case SWIMMING: puts("Swimming"); break; case FLYING: puts("Flying"); break; } return Val_unit; }
Here it is very important to make match the good number in the switch according to the place in the enumeration in the OCaml variant.
To send back an constant variant from C to OCaml, just return
Val_int()
with its corresponding index.
If there are a lots of possible values in the enumeration (or possible constants), you can also use an array in stead of a switch:
static const moving table_moving[] = { WALKING, RUNNING, SWIMMING, FLYING }; CAMLprim value wrapping_enum_ml2c( value v ) { moving param; param = table_moving[Long_val(v)]; switch (param) { case WALKING: puts("Walking"); break; case RUNNING: puts("Running"); break; case SWIMMING: puts("Swimming"); break; case FLYING: puts("Flying"); break; } return Val_unit; }
Here you need to be sure not to try to access to an index in the association table after the last one. Which could occur if you add an item on the OCaml side, and forget to add it also in the array on the C side.
Something you could do is checking if the index requested is not greater than the last one, and if it is raising an exception.
Something you could do is checking if the index requested is not greater than the last one, and if it is raising an exception.
Convert C bit fields
Imagine you have a C bit field defined as below in your header file:
#define ShiftMask (1<<0) #define LockMask (1<<1) #define ControlMask (1<<2) #define Mod1Mask (1<<3)
to get it wrapped, the most common solution is to convert it to a variant list:
type state = | ShiftMask | LockMask | ControlMask | Mod1Mask type state_mask = state list
As previously build a table with the C values in the same
exact order than in the OCaml variant, and iter all items of the caml list to set the C bit field accordingly:
static const int state_mask_table[] = { ShiftMask, LockMask, ControlMask, Mod1Mask }; static inline int state_mask_val( value mask_list ) { int c_mask = 0; while ( mask_list != Val_emptylist ) { value head = Field(mask_list, 0); c_mask |= state_mask_table[Long_val(head)]; mask_list = Field(mask_list, 1); } return c_mask; }
And to convert the C bit field to the OCaml variant list, you need to test the bit of every field, and if it matches push the associated OCaml value to the list:
#define Val_ShiftMask Val_int(0) #define Val_LockMask Val_int(1) #define Val_ControlMask Val_int(2) #define Val_Mod1Mask Val_int(3) static value Val_state_mask( int c_mask ) { CAMLparam0(); CAMLlocal2(li, cons); li = Val_emptylist; if (c_mask & ShiftMask) { cons = caml_alloc(2, 0); Store_field( cons, 0, Val_ShiftMask ); Store_field( cons, 1, li ); li = cons; } if (c_mask & LockMask) { cons = caml_alloc(2, 0); Store_field( cons, 0, Val_LockMask ); Store_field( cons, 1, li ); li = cons; } if (c_mask & ControlMask) { cons = caml_alloc(2, 0); Store_field( cons, 0, Val_ControlMask ); Store_field( cons, 1, li ); li = cons; } if (c_mask & Mod1Mask) { cons = caml_alloc(2, 0); Store_field( cons, 0, Val_Mod1Mask ); Store_field( cons, 1, li ); li = cons; } CAMLreturn(li); }
Exchanging Raw Data
If you have to handle raw data, you can do so using OCaml strings wherever there are array of bytes or void* in C.
On the OCaml side, strings are able to handle any character, included the possible '/000', but if you use
So you should use
On the OCaml side, strings are able to handle any character, included the possible '/000', but if you use
caml_copy_string()
to copy the raw data it will end at the first Null character, because it considers it as the string terminator.
So you should use
caml_alloc_string()
and
memcpy()
:
#include <caml/mlvalues.h> #include <caml/alloc.h> #include <string.h> CAMLprim value get_raw_data( value unit ) { CAMLlocal1( ml_data ); char * raw_data; int data_len; /* get raw_data and data_len here (if raw_data is c-mallocated, you can use sizeof() for its length) */ ml_data = caml_alloc_string (data_len); memcpy( String_val(ml_data), raw_data, data_len ); return ml_data; }
Notice that here it is possible not to use CAMLparam/CAMLreturn even if there is an ocaml alloc, because there aren't any ocaml values used *after* the ocaml alloc. In your code if there are some, do put the CAMLparam/CAMLreturn !
If the data chunk represents something like a matrix (an image for example) you will probably prefer to use an OCaml bigarray which is a very handy data structure for this task because it shares the same memory layout than in C.
In your code, if the buffer
raw_data
is C mallocated and then filled by some process, it is even possible to only allocate an ocaml string and then get a pointer to the first byte of this ocaml string. So in such cases you will save one C malloc(), one C free(), and the memcpy() call.
CAMLprim value get_raw_data( value some_param ) { CAMLparam( some_param ); CAMLlocal1( ml_data ); char * raw_data; int data_len; /* do initialise data_len */ ml_data = caml_alloc_string( data_len ); raw_data = String_val(ml_data); /* Now you can use raw_data and fill it as if it was a buffer of type (char *) of length data_len. (once given to ocaml it will be garbage-collected as every ocaml value) */ CAMLreturn( ml_data ); }
If you need to access to one byte of the buffer like this
(And if raw_data was of type
raw_data[n]
, it is also possible to make the same access directly on the ocaml value like this
Byte(ml_data, n)
.
(And if raw_data was of type
(unsigned char *)
replace
Byte()
by
Byte_u()
.)
More than 5 parameters
There is a special case for OCaml functions that have more than 5 parameters, in such cases you have to provide 2 C functions, as you can see below:
external lots_of_params: p1:int -> p2:int -> p3:int -> p4:int -> p5:int -> p6:int -> p7:int -> unit = "lotprm_bytecode" "lotprm_native"
CAMLprim value lotprm_native( value p1, value p2, value p3, value p4, value p5, value p6, value p7 ) { printf("1(%d) 2(%d) 3(%d) 4(%d) 5(%d) 6(%d) 7(%d)/n", Int_val(p1), Int_val(p2), Int_val(p3), Int_val(p4), Int_val(p5), Int_val(p6), Int_val(p7) ); return Val_unit; } CAMLprim value lotprm_bytecode( value * argv, int argn ) { return lotprm_native( argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6] ); }
And as you can see the function that will be called in bytecode compiled programs have a different prototype. Then in the bytecode function, the common and easiest solution is to call the native one.
And another detail for those functions when you need to protect the values from the garbage collector, the macros CAMLparam
N() exist only for
Nfrom 1 to 5. Then put the 5 first parameters in CAMLparam5(), and use additional CAML
xparam
N() macros which exist too with
N from 1 to 5. So for example if you have 13 parameters, use one CAMLparam5(), one CAMLxparam5() and one CAMLxparam3(). You have to use only one macro without the "x" in the middle, and you can use as many of the one with the "x".
Here is the same function with these macros:
Here is the same function with these macros:
CAMLprim value lotprm_native( value p1, value p2, value p3, value p4, value p5, value p6, value p7 ) { CAMLparam5(p1, p2, p3, p4, p5); CAMLxparam2(p6, p7); printf("1(%d) 2(%d) 3(%d) 4(%d) 5(%d) 6(%d) 7(%d)/n", Int_val(p1), Int_val(p2), Int_val(p3), Int_val(p4), Int_val(p5), Int_val(p6), Int_val(p7) ); CAMLreturn(Val_unit); } CAMLprim value lotprm_bytecode( value * argv, int argn ) { return lotprm_native( argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6] ); }
Raising Exception
Raising the predefined exceptions of OCaml is very easy. For example for raising the
Invalid_argument
exception, the C function is:
caml_invalid_argument("Error message");
And to raise an exception
Failure
:
caml_failwith("Error message");
The header file to include when using these functions is:
#include <caml/fail.h>
And if your module defines custom exceptions, it is possible to raise these from C as explained
here in the official manual .
Pointers to C structures
Often C libraries use types defined from structures or pointers to structures. OCaml can safely handle pointers to memory allocated areas. To achieve this task, the simpler method is to cast your pointer to and from the type
The only disadvantage of this simple method is that you have to free your allocated value from the C side of your program. Usually this means providing a function to free the allocated memory with interface to that on the OCaml side. In case of a library this is equivalent to using the destroy function associated with a particular type.
(value)
which can handle pointers.
The only disadvantage of this simple method is that you have to free your allocated value from the C side of your program. Usually this means providing a function to free the allocated memory with interface to that on the OCaml side. In case of a library this is equivalent to using the destroy function associated with a particular type.
typedef struct _obj_st { double d; int i; char c; } obj_st; typedef obj_st *obj_p; CAMLprim value wrapping_ptr_ml2c( value d, value i, value c ) { obj_p my_obj; my_obj = malloc(sizeof(obj_st)); my_obj->d = Double_val(d); my_obj->i = Int_val(i); my_obj->c = Int_val(c); return (value) my_obj; } CAMLprim value dump_ptr( value ml_ptr ) { obj_p my_obj; my_obj = (obj_p) ml_ptr; printf(" d: %g/n i: %d/n c: %c/n", my_obj->d, my_obj->i, my_obj->c ); return Val_unit; } CAMLprim value free_ptr( value ml_ptr ) { obj_p my_obj; my_obj = (obj_p) ml_ptr; free(my_obj); return Val_unit; }
Then the pointer can be hidden in an abstract type on the OCaml side:
type t external abs_get: float -> int -> char -> t = "wrapping_ptr_ml2c" external print_t: t -> unit = "dump_ptr" external free_t: t -> unit = "free_ptr"
# let ty = abs_get 255.9 107 'K' in print_t ty; free_t ty; ;; d: 255.9 i: 107 c: K - : unit = ()
Finalisation of C objects
To finalise those abstract type that represent C object, don't try to use the
Gc.finalise
like this:
let abs_get f i c = let t = abs_get f i c in Gc.finalise free_t t; (* doesn't work *) (t) ;;
because this function does only work with heap-allocated values.
But you can bypass this by creating a new type that mixes the abstract type with heap-allocated value:
But you can bypass this by creating a new type that mixes the abstract type with heap-allocated value:
type u = {t:t; s:string} let free_t v = free_t v.t ;; let print_t v = print_t v.t ;; let abs_get f i c = let t = abs_get f i c in let u = {t=t; s=" "} in Gc.finalise free_t u; (u) ;;
This is a simple way, but there is an other "more official" way to achieve the same effect from the C side, which is described in the next paragraph.
Custom Operations Structure
You can use the function
caml_alloc_custom()
to store datas of a given size. The size of this data is given as the second parameter of this function. The first parameter is a
custom_operations
structure, which can be used to provide functions associated with this ocaml value. In the example below no functions are associated, so the
custom_operations
structure is filled with the default functions (which are in fact defined as
NULL
in
<caml/custom.h>
)
#include <caml/custom.h> #include <string.h> typedef struct _obj_st { double d; int i; char c; } obj_st; static struct custom_operations objst_custom_ops = { identifier: "obj_st handling", finalize: custom_finalize_default, compare: custom_compare_default, hash: custom_hash_default, serialize: custom_serialize_default, deserialize: custom_deserialize_default }; static inline value copy_obj( obj_st *some_obj ) { CAMLparam0(); CAMLlocal1(v); v = caml_alloc_custom( &objst_custom_ops, sizeof(obj_st), 0, 1); memcpy( Data_custom_val(v), some_obj, sizeof(obj_st) ); CAMLreturn(v); } CAMLprim value get_finalized_obj( value d, value i, value c ) { CAMLparam3( d, i, c ); CAMLlocal1(ml_obj); obj_st my_obj; my_obj.d = Double_val(d); my_obj.i = Int_val(i); my_obj.c = Int_val(c); ml_obj = copy_obj( &my_obj ); CAMLreturn(ml_obj); } CAMLprim value access_obj( value v ) { obj_st * my_obj; my_obj = (obj_st *) Data_custom_val(v); printf(" d: %g/n i: %d/n c: %c/n", my_obj->d, my_obj->i, my_obj->c ); return Val_unit; }
type obj external new_obj: float -> int -> char -> obj = "get_finalized_obj" external dump_obj: obj -> unit = "access_obj"
The values optained by the function
new_abs
won't need an explicite free as in the previous paragraph
Pointers to C structures, OCaml will finalise these values when the garbage collection will occur.
# let a = Array.init 20 (fun i -> new_obj (float i) i 'A') in dump_obj a.(9); ;; d: 9 i: 9 c: A - : unit = ()
Custom Finalisation
Very often C structures contain allocated members, in such case you have to use a custom finalisation function to free these members.
You can see this in the exemple below which is very close to the previous example. Here the member
You can see this in the exemple below which is very close to the previous example. Here the member
str
of the C structure is allocated, and needs to be freed in the custom finalisation function:
typedef struct _fobj { double d; int i; char * str; } fobj; void finalize_fobj( value v ) { fobj * my_obj; my_obj = (fobj *) Data_custom_val(v); free( my_obj->str ); puts("fobj freed done"); } static struct custom_operations fobj_custom_ops = { identifier: "fobj handling", finalize: finalize_fobj, compare: custom_compare_default, hash: custom_hash_default, serialize: custom_serialize_default, deserialize: custom_deserialize_default }; static inline value copy_fobj( fobj *some_obj ) { CAMLparam0(); CAMLlocal1(v); v = caml_alloc_custom( &fobj_custom_ops, sizeof(fobj), 0, 1); memcpy( Data_custom_val(v), some_obj, sizeof(fobj) ); CAMLreturn(v); } CAMLprim value get_finalized_fobj( value d, value i, value str ) { CAMLparam3( d, i, str ); CAMLlocal1(ml_obj); int len; fobj my_obj; my_obj.d = Double_val(d); my_obj.i = Int_val(i); len = caml_string_length(str) + 1; my_obj.str = malloc( len * sizeof(char) ); memcpy( my_obj.str, String_val(str), len ); ml_obj = copy_fobj( &my_obj ); CAMLreturn(ml_obj); } CAMLprim value access_fobj( value v ) { fobj * my_obj; my_obj = (fobj *) Data_custom_val(v); printf(" d: %g/n i: %d/n str: %s/n", my_obj->d, my_obj->i, my_obj->str ); return Val_unit; }
type fobj external new_fobj: float -> int -> string -> fobj = "get_finalized_fobj" external dump_fobj: fobj -> unit = "access_fobj"
You can see when values are finalised with the
printf()
message. In the script below calling
Gc.full_major
will produce the finalisation:
let () = begin let f = function i -> new_fobj (float i) i (String.make i '.') in let a = Array.init 20 f in dump_fobj a.(9); end; Gc.full_major(); print_endline "end test"; ;;
You can find more informations about the
custom_operations structure in the offical manual.
This structure can be used too for other custom operations like serialisation and so on.
This structure can be used too for other custom operations like serialisation and so on.
Variants with arguments
Now the case of variants with parameters, as in this example:
type pvar = | P0_a | P1_a of int | P2_a of int * int | P0_b | P1_b of int | P2_b of int * int external handle_pvar: pvar -> unit = "param_variant"
OCaml values seen from on the C side, can be divised in two cathegories, longs and blocks. Booleans, characters, integers, and in the present case scalar variants are represented as a C long. Other types are blocks. It is possible to test from which cathegory belongs a value with these macros:
Is_long(v) Is_block(v)
So you can use these to test if a variant is a scalar, or if it has parameters.
CAMLprim value param_variant( value v ) { if (Is_long(v)) { switch (Int_val(v)) { case 0: printf("P0_a/n"); break; case 1: printf("P0_b/n"); break; default: caml_failwith("variant handling bug"); } } else // (Is_block(v)) { switch (Tag_val(v)) { case 0: printf("P1_a(%d)/n", Int_val(Field(v,0)) ); break; case 1: printf("P2_a(%d, %d)/n", Int_val(Field(v,0)), Int_val(Field(v,1)) ); break; case 2: printf("P1_b(%d)/n", Int_val(Field(v,0)) ); break; case 3: printf("P2_b(%d, %d)/n", Int_val(Field(v,0)), Int_val(Field(v,1)) ); break; default: caml_failwith("variant handling bug"); } } return Val_unit; }
Here you can notice that the constructor of variants with arguments can be handled with the macro
Tag_val(v)
. Also be careful with the numbers to switch on, scalars and blocks are numbered separately as you can see in this example.
#load "funs.cma" ;; open Funs ;; let () = handle_pvar(P0_a); handle_pvar(P0_b); handle_pvar(P1_a(21)); handle_pvar(P1_b(27)); handle_pvar(P2_a(30, 34)); handle_pvar(P2_b(70, 74)); ;;
The type 'a option
type 'a option = None | Some of 'a
The type
'a option
is nothing more than a variant with one argument (as seen in the previous section), but here is the code as quick reference:
#define Val_none Val_int(0) static inline value Val_some( value v ) { CAMLparam1( v ); CAMLlocal1( some ); some = caml_alloc(1, 0); Store_field( some, 0, v ); CAMLreturn( some ); }
CAMLprim value rand_int_option( value unit ) { int d = random() % 4; if (d) return Val_some( Val_int(d) ); else return Val_none; }
external rand_int: unit -> int option = "rand_int_option"
# Array.init 10 (fun _ -> rand_int()) ;; [|Some 3; None; Some 1; None; Some 3; Some 3; Some 2; None; None; Some 3|]
and in the opposite way:
external say: string option -> unit = "maybe_say"
#define Some_val(v) Field(v,0) CAMLprim value maybe_say( value speech ) { if (speech == Val_none) printf("Nothing/n"); else printf("Something: %s/n", String_val(Some_val(speech)) ); return Val_unit; }
# say None ;; Nothing - : unit = () # say (Some "Camelus bactrianus") ;; Something: Camelus bactrianus - : unit = ()
optional labeled parameter
In a very close way, you can put optional parameters using labels:
external say_lbl: ?speech:string -> unit -> unit = "maybe_say_label"
Note that here the type is
string
and not
string option
, and that from the C side it is seen as a
string option
.
CAMLprim value maybe_say_label( value speech, value unit ) { if (speech == Val_none) printf("Nothing/n"); else printf("Something: %s/n", String_val(Some_val(speech)) ); return Val_unit; }
You will then often need to add a unit parameter at the end of the parameters list, which also need to be added in the C function even if it is not used.
# say_lbl () ;; Nothing - : unit = () # say_lbl ~speech:"le chameau songeur" () ;; Something: le chameau songeur - : unit = ()
Polymorphic Variants
type plm_var = [ `plm_A | `plm_B | `plm_C ] external plm_variant: plm_var -> unit = "polymorphic_variant"
The C representation of a polymorphic variant can by retrieved with a function that computes its hash value:
CAMLprim value polymorphic_variant( value v ) { if (v == caml_hash_variant("plm_A")) puts("polymorphic variant A"); if (v == caml_hash_variant("plm_B")) puts("polymorphic variant B"); if (v == caml_hash_variant("plm_C")) puts("polymorphic variant C"); return Val_unit; }
let () = plm_variant `plm_A; plm_variant `plm_B; plm_variant `plm_C; ;;
If performance is an issue, instead of make a call to
caml_hash_variant()
each time, what you can do is to get its result for each variant, and create constants which you use in a switch:
#include <caml/mlvalues.h> #include <stdio.h> int main() { printf("#define MLVAR_plm_A (%d)/n", caml_hash_variant("plm_A") ); printf("#define MLVAR_plm_B (%d)/n", caml_hash_variant("plm_B") ); printf("#define MLVAR_plm_C (%d)/n", caml_hash_variant("plm_C") ); return 0; }
Which you can compile with:
> empty.ml ocamlc -o empty.o -output-obj empty.ml ocamlc -c variant.c gcc -o variant.exe variant.o empty.o -L"`ocamlc -where`" -lcamlrun -lm -lcurses
./variant.exe
will output this result:
#define MLVAR_plm_A (-1993467801) #define MLVAR_plm_B (-1993467799) #define MLVAR_plm_C (-1993467797)
Which you can then include at the beginning of your C code, then you can replace the previous function
polymorphic_variant()
by:
CAMLprim value polymorphic_variant( value v ) { switch (v) { case MLVAR_plm_A: puts("polymorphic variant A"); break; case MLVAR_plm_C: puts("polymorphic variant B"); break; case MLVAR_plm_D: puts("polymorphic variant C"); break; default: caml_failwith("unrecognised polymorphic variant"); } return Val_unit; }
And another way to get the same defines:
CAMLprim value print_polymorphic_variant_val( value v ) { printf("%d", (long) v ); fflush(stdout); return Val_unit; }
external pmvar_print_i: pmvar -> unit = "print_polymorphic_variant_val" let () = let p = Printf.printf in p "#define MLVAR_plm_A /t %!"; (pmvar_print_i `plm_A); p "/n%!"; p "#define MLVAR_plm_B /t %!"; (pmvar_print_i `plm_B); p "/n%!"; p "#define MLVAR_plm_C /t %!"; (pmvar_print_i `plm_C); p "/n%!"; ;;
Notice the flush of the stdout channels on the two sides after each strings (in OCaml flush is achieved by
"%!"
), this is because the stdout of OCaml and C are two different channels that are not synchronised.
Linking against a library
Now imagine that you want to use a C library in the C part, let's say a fictitious library called "MyLib", which is compiled in C programs this way:
In this case the argument
cc -o my_prog -L/lib/path -lMyLib my_prog.c
In this case the argument
-lMyLib
will have to be inserted while compiling the module, as shown below:
dll_wrap_stubs.so: wrap.o ocamlmklib -o _wrap_stubs $< / -L/lib/path -lMyLib funs.cmxa: funs.cmx dll_wrap_stubs.so ocamlopt -a -o $@ $< -cclib -l_wrap_stubs / -ccopt -L/lib/path / -cclib -lMyLib funs.cma: funs.cmo dll_wrap_stubs.so ocamlc -a -o $@ $< -dllib -l_wrap_stubs / -ccopt -L/lib/path / -cclib -lMyLib
For the native code module, the directives have to be preceded by
Note the use of
-cclib
for the linker, and
-ccopt
for the compiler and linker.
Note the use of
-dllib
for ocamlc.
Call Caml functions from C
To call caml functions from C, you have to give a string that identifies each caml functions with
Callback.register
. You can then retrieve this caml function from C with
caml_named_value("ID")
. And you can cache the call to
caml_named_value()
, as you can see in the C code below, with a
static
variable. But you still have to test if it is equal to
NULL
in case the caml garbage collector has freed it.
let print_hello () = print_endline "Hello World"; ;; let () = Callback.register "Hello callback" print_hello; ;;
#include <caml/callback.h> void hello_closure() { static value * closure_f = NULL; if (closure_f == NULL) { closure_f = caml_named_value("Hello callback"); } caml_callback(*closure_f, Val_unit); }
Start-up from C
If you want to make your main program from C, which will call caml parts, you can do it as below,
file
"ml_part.ml"
:
let print_hello () = print_endline "Hello World"; ;; let () = Callback.register "Hello callback" print_hello; ;;
file
"main.c"
:
#include <caml/mlvalues.h> #include <caml/callback.h> void hello_closure() { static value * closure_f = NULL; if (closure_f == NULL) { closure_f = caml_named_value("Hello callback"); } caml_callback(*closure_f, Val_unit); } int main(int argc, char **argv) { caml_main(argv); hello_closure(); return 0; }
In the
main
function the first instruction have to be
caml_main(argv)
in order to init the caml part of the program. All the caml instructions at the root level (for exemple
print_endline "something" ;;
, and everything defined under
let () = (* code *) ;;
) will be evaluated and executed at this moment.
Compilation:
ocamlopt -output-obj ml_part.ml -o ml_part_obj.o gcc -c main.c -I"`ocamlc -where`" gcc -o prog.opt / main.o ml_part_obj.o / -L"`ocamlc -where`" / -lm -ldl -lasmrun
The last line mentions the
-lm
and
-ldl
libraries, as can be found by:
grep NATIVECCLIBS `ocamlc -where`/Makefile.config
So for portability issue with your Makefile, it is possible to replace the two arguments
-lm -ldl
by the variable
$(NATIVECCLIBS)
. To achieve this you just need to include the file
`ocamlc -where`/Makefile.config
as below:
prog.opt: main.o ml_part_obj.o gcc -o $@ $^ / -L"`ocamlc -where`" / $(NATIVECCLIBS) -lasmrun -include $(shell ocamlc -where)/Makefile.config
The related chapter in the OCaml manual is here:
18.7.5 Embedding the Caml code in the C code.
Mixing with C++
Contents of file
mymod_stubs.cc:
#include <iostream> #include <string> extern "C" { #include <caml/memory.h> #include <caml/mlvalues.h> } extern "C" value my_hello_cc (value v_str) { CAMLparam1 (v_str); std::cout << "Hello " << String_val(v_str) << "!/n"; CAMLreturn (Val_unit); }
Contents of file
mymod.ml:
external my_hello : string -> unit = "my_hello_cc"
Contents of file
caller.ml:
let _ = Mymod.my_hello "Blue Camel"; ;;
compile to native code with:
g++ -o mymod_stubs.o -I`ocamlc -where` -c mymod_stubs.cc ocamlopt -c mymod.ml ocamlmklib -o mymod mymod_stubs.o ocamlmklib -o mymod mymod.cmx ocamlopt -I . -cclib -lstdc++ mymod.cmxa caller.ml -o caller.opt
then call it:
% ./caller.opt Hello Blue Camel!
now the compilation for bytecode:
g++ -o mymod_stubs.o -I`ocamlc -where` -c mymod_stubs.cc ocamlc -c mymod.ml ocamlmklib -o mymod -lstdc++ mymod_stubs.o ocamlmklib -o mymod mymod.cmo ocamlc -I . mymod.cma caller.ml -o caller.byte
then call it:
% ocaml mymod.cma caller.ml Hello Blue Camel! % ocamlrun -I . caller.byte Hello Blue Camel!
This section was greatly inspired by the tutorial by
Anne Pacalet (in French) which you can find
here or
there.
This was an introduction tutorial, for more complete details, refer to the
page on this subject in the official OCaml manual.
If there are questions that have not found an answer on this page nor in the official manual, you can join the
ocaml_beginners mailing list to find help.
You may also find examples to follow from the
other ocaml bindings registered in the OCaml-Hump.
If you have saved this document, you can check for up-dates or corrections from
its original location on the web.
© 2007-2008 Florent Monnier
You can use this document according to the terms of the GNU/FDL license.
Which means basically that you can redistribute this document with or without modifications, as long as you keep the copyright and the license.
Thanks to Eric Norige, for corrections of the English.
You can use this document according to the terms of the GNU/FDL license.
Which means basically that you can redistribute this document with or without modifications, as long as you keep the copyright and the license.
Thanks to Eric Norige, for corrections of the English.