2012-11-30

FortranからCを呼ぶときの諸問題 @uwabami さんへ

https://twitter.com/uwabami/statuses/274207773250691072 というtwを受信したので、Fortran から C を呼ぼうとした場合の諸問題について知るところをまとめておく。

1.問題の解剖

・データ型の対応
・名前の対応
・引数順序の対応
・ランタイムがらみの話

2.データ型の対応

ちょっと考えでは無保証の荒野に放り出されるようであるが、意外とパズルの解は少ない。

Fortran には「INTEGER型とREAL型の幅が同じでなければならず、DOUBLE PRECISIONはその二倍でなければならない」という制約がある。そして、Fortranが実装されるような機械ではCコンパイラであってもIEEE 754のbinary32および
binary64型はどうせ実装されざるを得ないので、結局次の関係ができる。

REAL = binary32 = float
DOUBLE PRECISION = binary64 = double
符号付き32ビット整数 = INTEGER = int32_t ≒ int

反例は知らない。

int32_t は C99 でしか使えないので、実際には int を使うことになる。一見おっかないようだが、昔懐かし 16 ビットコンピュータか、ILP64 モデルでない限り int が 32 ビッ
トとみなして外れることはない。怖かったら typedef でもしよう。

3.名前の対応

実はこれが一番いやらしい。Fortranの名前が Cでどう見えるかには次の4通りが知られている。

: gfortran
全て小文字にして "_" を後置する。ただし、元の名前に "_" が含まれる場合は
"__" を後置する。
: Intel Fortran
全て小文字にして "_" を後置する。
: IBM XL Fortran
全て小文字にする。
: 旧Visual Fortran
全て大文字にする。

これを configure で判定するのがわりと常道だが、判定が外れるとリンクできないというリスクがあり、しかもどういうわけだかうまくいかないことが多い。個人的な趣味は、4種類のラッパーを作って .lib を作ってしまうこ
とである。

ところで、上記知見の重要な系:

Cで書かれたライブラリで、API関数名が全て小文字であるばあい、まったく同じ名前をFortranバインディングのAPIとすることは禁忌である。あなたはAIXの前で泣くか、かわいそうな犠牲者に石を投げられることになる。NetCDF がバージョン3で関
数名を変えたのも、NuSDaS の C API がすべて NuSDaS_ で始まるのもこのせいである。


4.引数順序の対応

Fortran の文字型引数は、データとともに長さも渡される。さてどうやって?
これは今日的には問題ではないかもしれないが、cfortran.h などのツールが第一に取り組もうとした課題である。

現在知られている Fortran 処理系では、文字型の引数を渡すにあたって、文字列の先頭へのポインタの次に文字列長を与える int の2つの引数を渡す。

SUBROUTINE SUB(I1, C, I2)
INTEGER:: I1, I2
CHARACTER(*):: C

というのがあったら

void sub_(int *i1, char *c, int c_len, int *i2);

になるというわけだ。しかし、文字列長引数を全ての引数の最後にまとめて置くという流儀があり、MS Fortran PowerStation ではこれがデフォルトだった。

void SUB(int *i1, char *c, int *i2, int c_len);

になるというわけだ。

5.ランタイムまわり

Fortran コードを書くならば、どうせメインは Fortran にする羽目になるものだ。

つまり、Fortran ライブラリ(文字列コピーや数学関数などの実体)だけでなく
Fortran ランタイム、すなわち main() としてコマンドラインや環境変数を受け取り、ファイル番号 5, 6 を開き、主プログラム(PROGRAM ... END)を呼び出すルーチンに依
存している機能が多いので、ランタイムをリンクしたくなるものである。

それはそれでいいが、メインを取られているということは、コマンドラインへのアクセスは Fortran の方法によらざるを得ないということだ。C から Fortran を呼ばねばならなくなるとリンケージ問題(上記3.)を必ず解決せねばならないし、文字列長が不明になるのは誠に腹立たしい。が、ま、しかたがない。

環境変数については getenv(3) が使えなくなる Fortran 処理系があるかどうか知見がない。たぶんないと思う。

もうちょっと嫌らしいのは、たいていの Fortran ランタイムはシグナルハンドラをフックするとか、OpenMPするとSIGFPEがマスクされる処理系があるとか、浮動小数点の丸めモードがある設定でないと動作保証しないFortranライブラリがあるとか、等々といった怪しいリソースだ。まあ、これらも知っていさえすればいいのだが。

0 件のコメント:

コメントを投稿