全部博文(230)
分类:
2008-08-01 22:18:38
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
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
#include
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
% ocaml funs.cma
# open Funs;;
# send_an_int 9 ;;
9
- : unit = ()
#load "funs.cma" ;;
open Funs ;;
let () =
send_an_int 5;
;;
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 = 2.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"
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
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]
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' "" ;;
- : int * char * string = (38, 'G', "")
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="" } ;;
- : int * char * string = (38, 'G', "")
# 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' "" ;;
- : rec_b = {i = 38; c = 'G'; s = ""}
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()
in stead 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);
}
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
(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
)
#include
#include
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
,
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 = ()
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
#include
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
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
#include
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