OCamlでCの関数を呼んでみよう

30分プログラム、その548。OCamlでCの関数を呼んでみよう。
OCamlバインディングの無いライブラリを使いたいときに必要になるよ。便利かどうかは分からないけど。

Interfacing C with Objective Camlを参考に、階乗計算モジュールを作ってみよう。

OCaml側のインタフェース

externalでCの関数名を指定してやる。

external fact : int -> int = "ml_fact"

Cの関数

OCamlから呼べる関数は、以下の形になる。

CAMLprim value f(value arg1, value arg2, ..., value argN){
 ...
}

value型とint型の間の変換は、Val_intやInt_valを使う。

int n = Int_val(some_value);

value other_value = Val_int(42);

あと、引数CAMLparamNとかを使うとGC的に嬉しいらしい。よく分からないけど。

value bar (value v1, value v2, value v3)
{
  CAMLparam3 (v1, v2, v3);
  CAMLlocal1 (result);
  result = alloc (3, 0);
  ...
  CAMLreturn (result);
}

これらのことを踏まえて、階乗を計算する関数を書くと次のようになる。

#include <caml/mlvalues.h>
#include <caml/memory.h>

int fact(int n) {
  int i,r = 1;
  for(i = 1; i < n ; i++){
    r *= i;
  }
  return r;
}

CAMLprim value ml_fact(value n){
  CAMLparam1(n);
  CAMLlocal1(result);
  result = Val_int(fact(Int_val(n)));
  CAMLreturn(result);
}

コンパイル

$ ocamlc fact.c
$ ocamlc -c fact.ml
$ ocamlmklib -o fact -oc fact fact.o fact.cmo

これでfact.cmaなどが生成される。ocamlcのかわりにocamloptを使えば、fact.cmxaが生成できる。

さあ、使ってみよう

let _ =
  print_endline (string_of_int (Fact.fact 10))
$ ocamlc fact.cma main.ml
$ ./a.out
362880