http://www.linux-nantes.org/~fmonnier/OCaml/ocaml-wrapping-c.php
external send_an_int: int -> unit = "get_an_int"
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; }
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
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
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
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.
% ocaml funs.cma # open Funs;; # send_an_int 9 ;; 9 - : unit = ()
#load "funs.cma" ;; open Funs ;; let () = send_an_int 5; ;;
make wrap.o make funs.cmx ocamlopt wrap.o funs.cmx -o myapp ./myapp
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; }
Double_val()
conversion macro can be used both for C floats and C doubles.
external get_an_int: unit -> int = "send_an_int"
CAMLprim value send_an_int( value unit ) { int i; i = 6; return Val_int(i); }
Val_int()
is similar to the macros seen before, it converts a C integer into an OCaml integer value.
Int_val()
just use
Long_val()
.
Bool_val()
, and a C integer to an OCaml boolean with
Val_bool()
. For convenience there are also macros
Val_true
and
Val_false
.
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); }
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); }
#include <caml/memory.h>
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 ); }
# inspect_tuple (3, 2.4, "functional") ;; 3 2.400000 functional - : unit = ()
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; }
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; }
# inspect_record {a=26; b=4.3; c="camelids folks" } ;; 26 4.3 camelids folks - : unit = ()
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 ); }
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 ); }
# 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 = ()
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 ); }
# inspect_list ["hello"; "you"; "world"; "camelids"] ;; hello you world camelids - : unit = ()
[]
on the OCaml side, and
Val_emptylist
on the C side.
# 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]
<caml/mlvalues.h>
header that
Val_emptylist
is defined as
Val_int(0)
.
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")
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.
float array
s that have a different internal representation.
# 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"}
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']
float_array = caml_alloc(length * Double_wosize, Double_array_tag);
Store_double_field()
instead of
Store_field()
, and there's no need to use the
caml_copy_double
in it.
#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; }
Val_int()
with its corresponding index.
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; }
#define ShiftMask (1<<0) #define LockMask (1<<1) #define ControlMask (1<<2) #define Mod1Mask (1<<3)
type state = | ShiftMask | LockMask | ControlMask | Mod1Mask type state_mask = state list
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; }
#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); }
caml_copy_string()
to copy the raw data it will end at the first Null character, because it considers it as the string terminator.
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; }
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 ); }
raw_data[n]
, it is also possible to make the same access directly on the ocaml value like this
Byte(ml_data, n)
.
(unsigned char *)
replace
Byte()
by
Byte_u()
.)
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] ); }
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] ); }
Invalid_argument
exception, the C function is:
caml_invalid_argument("Error message");
Failure
:
caml_failwith("Error message");
#include <caml/fail.h>
(value)
which can handle pointers.
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; }
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 = ()
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) ;;
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) ;;
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"
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 = ()
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"
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"; ;;
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"
Is_long(v) Is_block(v)
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; }
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)); ;;
type 'a option = None | Some of 'a
'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|]
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 = ()
external say_lbl: ?speech:string -> unit -> unit = "maybe_say_label"
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; }
# say_lbl () ;; Nothing - : unit = () # say_lbl ~speech:"le chameau songeur" () ;; Something: le chameau songeur - : unit = ()
type plm_var = [ `plm_A | `plm_B | `plm_C ] external plm_variant: plm_var -> unit = "polymorphic_variant"
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; ;;
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; }
> 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)
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; }
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%!"; ;;
"%!"
), this is because the stdout of OCaml and C are two different channels that are not synchronised.
cc -o my_prog -L/lib/path -lMyLib my_prog.c
-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
-cclib
for the linker, and
-ccopt
for the compiler and linker.
-dllib
for ocamlc.
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); }
"ml_part.ml"
:
let print_hello () = print_endline "Hello World"; ;; let () = Callback.register "Hello callback" print_hello; ;;
"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; }
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.
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
-lm
and
-ldl
libraries, as can be found by:
grep NATIVECCLIBS `ocamlc -where`/Makefile.config
-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
#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); }
external my_hello : string -> unit = "my_hello_cc"
let _ = Mymod.my_hello "Blue Camel"; ;;
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
% ./caller.opt Hello Blue Camel!
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
% ocaml mymod.cma caller.ml Hello Blue Camel! % ocamlrun -I . caller.byte Hello Blue Camel!