プログラミング言語SML#解説 4.1.0版
19

19.21 静的インポート式: _import string : cfunty

C関数を直接SML#関数として使用するための式である. 構文の構成要素の意味は以下のとおりである.

  • string:C関数名. この名前が,リンク時にリンカによって外部名として参照される名前で ある.

  • cfunty:C関数の型の指定. 以下の文法に従い,C関数の型を指定する.

C関数型:cfunty
cfunty ::= (cfunattr)?  argTyList -> retTyOpt
argTyList ::= ( argTy,,argTy (,varArgs)? ) (複数引数)
 | argTy (引数がただ1つ)
 | () (引数なし)
retTyOpt ::= retTy
 | () (Cのvoid型に対応)
コールバック関数型:argfunty
argfunty ::= (cfunattr)?  retTylist -> argTyOpt
retTyList ::= ( retTy,,retTy (,varRets)? ) (複数引数)
 | retTy (引数がただ1つ)
 | () (引数なし)
argTyOpt ::= argTy
 | () (Cのvoid型に対応)
相互運用型:interoperableTy
interoperableTy ::= (tySeq)? longTycon (Cと受け渡し可能な型に限る.以下に詳述)
SML#からCに渡す引数の型:argTy
argTy ::= argTy * * argTy (Cの構造体へのポインタ型に相当)
 | { argTyRow } (Cの構造体へのポインタ型に相当)
 | tyvar (boxedカインドを持つものに限る)
 | interoperableTy
 | argfunty (コールバック関数引数型)
argTyRow ::= lab : argTy (, argTyRow)? (labdecimalで始まるものに限る)
CからSML#に渡される返り値の型:retTy
retTy ::= interoperableTy
 | tyvar (boxedカインドを持つものに限る)
可変長引数型指定:varArgsおよびvarRets
varArgs ::= ... ( argTy,,argTy )
varRets ::= ... ( retTy,,retTy )
C関数属性指定:cfunattr
cfunattr ::= __attribute__((attr,,attr))
att ::= cdecl  | stdcall  | fastcc  | pure  | fast

C関数の型に現れる型名interoperableTyは, 以下の2つの条件を全て満たさなければならない.

  1. 1.

    interoperableTyは以下のいずれかでなければならない.

    • 相互運用可能な原子型: int, int8, int16, int64, word, word8, word16, word64, real, real32, char, または string.

    • Cポインタの型: codeptr, interoperableTy ptr, または unit ptr.

    • 多相Cポインタ型: tyvar ptr (ただしtyvarboxedカインドまたはunboxedカインドを 持つものに限る).

    • サイズ型: ty size.

    • 配列型: interoperableTy array, interoperableTy vector, または interoperableTy ref.

    • 多相配列型: tyvar array, tyvar vector, または tyvar ref (ただしtyvarboxedカインドまたはunboxedカインドを 持つものに限る).

    • type宣言で付けた,上記の型のいずれかの別名. (tySeq)? longTyconを展開すると 上記のいずれかにならなければならない. ただし,argTyとしてのinteroperableTytype宣言で付けた型の別名が来た場合に限り, (tySeq)? longTyconを展開した結果が argTy相当の組型あるいはレコード型であっても良い.

  2. 2.

    CからSML#に渡されたり,Cが書き換えたりする可能性のある 値の型に,string型,array型,vector型,および ref型が現れてはならない. すなわち,これらの型は以下の箇所に現れてはならない.

    • retTyとしてのinteroperableTy

    • arrayref,およびptr型の型パラメタ

以上の条件を満たすinteroperableTyは, その型名から自然に類推されるCの型に相当する. 対応を以下に示す.

interoperableTy 対応するCの型
intおよびその仲間 同じサイズの符号付き整数型
wordおよびその仲間 同じサイズの符号付き整数型
real double
real32 float
char char
string const char *
codeptr Cの関数へのポインタ型
τ ptr τへのポインタの型
unit ptr void *または不完全型へのポインタ型
τ size size_t
τ array τ型の配列の先頭を指すポインタ型
τ vector τ型のconst配列の先頭を指すポインタ型
τ ref τ型の要素数1の配列を指すポインタ型

SML#のintおよびwordは常に 32ビットサイズの整数であることに注意されたい. 今日のほとんどのシステムでは SML#のintとCのintは対応するが, intが32ビットでないシステムではこれらは対応しない.

argTyargTy1 * * argTynおよび {lab1:argTy1, , labn:argTyn}型は, argTy1, , argTynを ラベルのdecimalの順番でメンバとして持つ const付き構造体型へのポインタに対応する. もし,全てのargTyiが同じならば, 要素数nargTyiconst付き配列の先頭への ポインタにも対応する.

C関数がSML#から見てパラメタ多相的に働くとき,かつ その場合に限り,C関数の引数および返り値の型として型変数を書いても良い. 例えば,Cの恒等関数

    void *id(void *x) { return x; }

を以下のようにインポートしてもよい.

    val ’a#boxed id = _import "id" : ’a -> ’a

また,任意のポインタの値を表示するprintf関数を 以下のようにインポートしてもよい.

    val ’a#boxed printPtr = _import "printf" : (string,...(’a)) -> int

C関数の型の前に記述する属性の意味は以下の通りである.

cdecl

ターゲットプラットフォーム標準のC関数呼び出し規約に従うC関数である ことを表す. 呼び出し規約に関する属性が指定されていない場合のデフォルトである.

stdcall

Windowsプラットフォームにおけるstdcall呼び出し規約に従うC関数である ことを表す.

fastcc

LLVMが提供するfastcc呼び出し規約に従うC関数であることを表す.

pure

C関数がSML#から見て純粋な関数であることを表す. すなわち,この属性を持つC関数は, メモリの書き換えを行わず,かつ引数リストのみから返り値がただ1つに定まる. SML#コンパイラの最適化に影響を与える.

fast

C関数が非常に短い時間で終了することを表す. この属性を持つC関数は, コールバック関数を通じてSML#のコードを呼び出したり, スレッドの実行を中断してはならない. SML#コンパイラは,この属性を持つC関数に対して,より 効率の良い呼び出しコードを生成する. ただし,この属性を持つC関数が長い時間を消費すると, GCに影響を与え,このC関数が終了するまでの間,全スレッドの実行が 止まる可能性がある.

この式の型は,cfuntyで指定したC関数の型に対応する SML#の関数型である. 対応の定義は以下の通りである.

C関数型 SML#関数型
( argTy1, , argTyn (varArgs)? ) -> retTy argTy1 * * argTyn (* varArgs)? -> retTy

argTyListまたはretTyOpt()の場合は, unit型としてSML#の型に現れる. interoperableTytyvar,および*は そのままSML#の型に現れる. この定義は,コールバック関数型argfuntyについても 同様である.

この式の値は,stringで指定されたC関数を呼び出す SML#の関数である. C関数型の指定が正しい限り,通常のSML#関数として使用できる.