プログラミング言語SML#解説 4.1.0版
10 SML#の拡張機能:Cとの直接連携

10.3 基本的なC関数のインポート例

いくつかのCの標準ライブラリ関数をインポートしてみましょう. まず,インポートするC関数のプロトタイプ宣言を探します. ここでは以下のC関数をインポートすることにします.

double pow(double, double);
void srand(unsigned);
int rand(void);

次に,引数および返り値の型に対応するSML#の相互運用型を 用いて,SML#プログラム中に_import宣言を書きます.

val pow = _import "pow" : (real, real) -> real
val srand = _import "srand" : word -> ()
val rand = _import "rand" : () -> int

これらのC関数は以下の型の関数としてSML#にインポートされます.

val pow : real * real -> real
val srand : word -> unit
val rand : unit -> int

可変長引数リストの記法を用いれば,printf(3)関数をインポート することも可能です. printf関数のプロトタイプ宣言は以下の通りです.

int printf(const char *, ...);

このプロトタイプ宣言に対応付けて,以下の_import宣言で printf関数をインポートします.

val printfIntReal = _import "printf" : (string, ...(int, real)) -> int

このprintfIntReal関数の型は以下の通りです.

val printfIntReal : string * int * real -> int

ただし,このようにしてインポートしたprintfIntReal関数を呼び出す 際は,その第一引数は,追加の引数として整数と浮動小数点数をこの順番で要求する ような出力フォーマットである必要があります.

ポインタ型と対応する相互運用型の取り扱いには注意が必要です. C言語ではポインタ引数を,巨大なデータ構造を参照渡しする場合と, 関数の結果をストアするためのメモリ領域を指す場合の両方に用います. ポインタ引数の使い方の違いによって,対応する相互運用型は異なります. 従って,ポインタ型の引数を持つC関数をインポートする場合は, そのプロトタイプ宣言を見るだけでなく,そのポインタ引数の意味をマニュアル 等で調べ,その使われ方に正しく対応する相互運用型を選択する必要があります.

ポインタを引数に取るC関数をいくつかインポートしてみましょう. 例えば,C標準ライブラリ関数modfをインポートすることを 考えます. この関数のプロトタイプ宣言は以下の通りです.

double modf(double, double *);

この関数の場合,第二引数のポインタは,計算結果の出力先を表します. 従って,この関数をインポートするときは, 書き換え可能な値を持つ相互運用型を第二引数の型として指定します.

val modf = _import "modf" : (real, real ref) -> real

charへのポインタ型には特に注意が必要です. C言語ではこの型を,ヌル終端文字列を表す型として使ったり, 最も汎用的なバッファの型として使ったりします. 例えば,C標準ライブラリ関数sprintfを考えます. そのプロトタイプ宣言は以下の通りです.

int sprintf(char *, const char *, ...);

2つ現れるcharへのポインタは,第一引数は出力先バッファ, 第二引数はヌル終端文字列を表します. このポインタの使われ方の違いに従って,これら2つのポインタ型に 異なる相互運用型を当てはめ,以下のようにインポートします.

val sprintfInt = _import "sprintf" : (char array, string, ...(int)) -> int

ここまでは,C標準ライブラリ関数を例に,C関数をインポートする 方法を説明してきました. しかし,SML#は,ユーザーが書いたC関数を含む,任意のC関数を インポートすることができます. SML#からユーザー定義のC関数に構造体の受け渡す簡単な例を 図10.1に示します.

sample.cファイル: #include <math.h>
double f(const struct {double x; double y;} *s) {
  return sqrt(s->x * s->x + s->y * s->y);
}
sample.smlファイル: val f = _import "f" : real * real -> real
val x = (1.1, 2.2);
val y = f x;
print ("result : " ^ Real.toString y ^ "\n");
実行例: # gcc -c sample.c
# smlsharp sample.sml sample.o
# a.out
result : 2.459675
Figure 10.1: 構造体引数を持つユーザー定義C関数の呼び出し例