Chapter 8. C言語との相互呼び出し

Table of Contents
外部のグローバル名
ATS における外部の型と値
ATS に外部コードを含める
ATS における外部関数呼び出し
安全ではないC言語スタイルのATSプログラミング
ATS の型をエクスポートしてC言語から使う
例: 静的に確保されたリストをコンストラクトする

ATS とC言語は全く同じネイティブ/フラット/アンボックスなデータ表現を共有しています。 結果的に、ATS で実装された関数をC言語から呼び出す際にラップ/アンラップやボックス化/アンボックス化したりする必要はありません。 逆もまた同様です。 さらにその際の実行時オーバーヘッドもありません。 おおざっぱに ATS はC言語のフロントエンドと考えることができます。 このフロントエンドは (プログラムの不変条件を記述するための) 表現力豊かな型システムと (コードの再利用を楽にしてくれる) 柔軟なテンプレートシステムを備えているのです。 とりわけ ATS は大きな一つのタスクを一貫したインターフェイスのサブタスク群に分解するのにしばしば有効です。 それらのサブタスクは ATS で実装することもできますし、C言語や他の言語で実装することもできます。 そうしてそれらを一緒に汲み上げることで元のタスクを構築できるのです。

期待される通りですが、ATS コード中に直接記述されたC言語コードは ATS コードに対して行なわれるような厳格な型検査を通りません。 そのため慎重に ATS コードの中にC言語コードを埋め込むことがプログラマに要求されます。 私個人の経験からは実際のところ ATS コード中におけるC言語コードがバグの犯人である可能性が非常に高いです。

この章に出てくるコードとテストのための追加コードは オンライン から得られます。

外部のグローバル名

ATS で宣言された関数にはC言語スタイルのグローバル名が与えれらます。 その関数は ATS コードからもC言語コードからも呼び出すことができます。 つまり、関数は ATS で実装してC言語から呼び出すことができ、その逆もまた可能なのです。

次のコードでは2つの関数を宣言しています:

extern fun fact (n: int): int extern fun fact2 (n: int, res: int): int = "ext#fact2_in_c"

最初の関数 fact はグローバル名を持たないのに対し、 2番目の関数 fact2 にはグローバル名 fact2_in_c が割り当てられます。 ext# シンボルは fact2_in_c をC言語のグローバル関数のように扱います。 そして extern キーワードによるプロトタイプ宣言が呼び出される前に必要です。 もし先の宣言 ext#fact2_in_c の代わりに ext# が使われた場合、 ATS の fact2 関数へのグローバル名は ATS の関数名と同じになります。 言いかえれば、先の例で ext# という宣言を書くことは、ext#fact2 と書くことと同じです。

次のように fact の実装が与えられたとしましょう:

implement fact (n) = fact2 (n, 1)

この実装をコンパイルするには factfact2 を参照する関数名を、 ATS コンパイラは生成するC言語コード中に作らなければなりません。 前者のC言語での関数名はあるルール (名前空間の問題を考慮します) によって決定されます。 後者の関数名は単純に fact2_in_c グローバル名が割り当てられます。 fact2_in_c という名前の通り、この関数は次のように直接C言語で実装されていることでしょう:


int
fact2_in_c (int n, int res)
{
  while (n > 0) { res *= n ; n -= 1 ; } ; return res ;
}

そして次のような fact2 をATSで直接実装することもできます:

implement fact2 (n, res) = if n > 0 then fact2 (n-1, n*res) else res

この fact2 の実装は fact2_in_c という名前を通してC言語から呼び出すことができます。

もし fact2fact2_in_c が両方とも実装されていたら (前者は ATS で後者はC言語で実装しました)、 fact2_in_c が二重に実装されていることを示すリンク時エラーが発生するでしょう。

また fact2 を次のように宣言することもできます:

extern fun fact2 (n: int, res: int): int = "mac#fact2_in_c"

mac# シンボルは fact2_in_c をC言語のマクロのように扱うことを示しています。 特に fact2_in_c はそのプロトタイプ宣言を最初に宣言しなくても呼び出すことができます。 実際のところ、プロトタイプ宣言がなくても問題ありません。 このスタイルの宣言は当然 fact2_in_c がC言語で直接実装されることを期待しています。

また mac# のかわりに sta# を使うこともできます:

extern fun fact2 (n: int, res: int): int = "sta#fact2_in_c"

このスタイルの宣言すると fact2_in_c がC言語の static 関数のように扱われます。 しかし実際にはあまり使うことはないでしょう。

完全を期すために、static 関数を宣言する他の方法を紹介します:

static fun fact2 (n: int, res: int): int

このスタイルの宣言は自動的に次のように変換されます:

extern fun fact2 (n: int, res: int): int = "sta#"

sta# のように使うと、C言語における fact2 という名前が単純に fact2 を意味するようになります。