FortranとCの邂逅:グローバル変数を安全に共有する「モジュール」という特等席
皆さん、こんにちは。かつて宇宙の深淵を覗く数値シミュレーションの基盤を叩いていたエンジニアです。
C言語やPythonを使いこなす皆さんがFortranの世界に足を踏み入れるとき、最初にぶつかるのが「これ、Cとどうやって値をやり取りするの?」という壁ではないでしょうか。かつてのFortranには、悪名高き「COMMONブロック」というグローバル変数共有の仕組みがありましたが、現代の我々はもうそんな危うい橋は渡りません。
今日は、モダンFortranにおける正攻法、`BIND(C)` 属性を活用したモジュール変数によるデータ共有について、現場の知見を交えてお話しします。
—
なぜ「COMMONブロック」ではいけないのか?
C言語のエンジニアから見れば、COMMONブロックは「メモリ上の特定の並びを強引に共有する」という手法に見えるでしょう。しかし、これはコンパイラの最適化を阻害し、配列の境界チェックすら満足に働かない「魔界」への入り口です。
モダンFortranでは、「モジュール(Module)」というカプセルの中に変数を置き、それを `BIND(C)` でC言語から見えるようにエクスポートします。これが、型安全かつコンパイラに最適化の余地を残す、現代のベストプラクティスです。
—
ステップ1:Fortran側で「共有の玄関口」を作る
まずは、C言語から見える変数を定義するモジュールを書きましょう。ここで重要なのが `BIND(C, name=”…”)` です。
module global_data_mod
use, intrinsic :: iso_c_binding, only: c_double, c_int
implicit none
! C言語側からは “my_global_var” という名前でアクセスできるようにする
! bind(c) をつけることで、Fortran特有のメモリレイアウトの最適化を
! C言語と互換性のあるものに固定します
real(c_double), bind(c, name=”my_global_var”) :: my_val
integer(c_int), bind(c, name=”my_count”) :: my_count
end module global_data_mod
ここで `iso_c_binding` を使うのがポイントです。`real` や `integer` をそのまま使うと、環境によってバイト数が変わるリスクがありますが、`c_double` や `c_int` を使うことで、C側の `double` や `int` と完全に一致させることができます。
—
ステップ2:C言語から「呼び出す」
C言語側では、これらを単なる `extern` 変数として扱うだけです。
include
// Fortranでbind(c, name=”…”)した名前と一致させる
extern double my_global_var;
extern int my_count;
void update_data_from_c() {
my_global_var = 3.14159;
my_count = 42;
}
たったこれだけです。リンカはこれらを同じメモリ空間上のシンボルとして解決してくれます。
—
現場で死なないための「メモリ配置」の鉄則
ここからは教科書には載っていない「現場の泥臭い話」です。
1. 列優先(Column-Major)の罠
Fortranは「列優先」、Cは「行優先」です。配列を共有する場合、この違いを忘れるとシミュレーションの結果がゴミになります。例えば `A(10, 20)` をFortranで宣言した場合、メモリ上では `A(1,1), A(2,1)…` の順で並びます。C言語側でこれにアクセスする際は、インデックスを逆順にするか、多次元配列として扱わずポインタとして受け取るなどの工夫が必要です。
2. コンパイラの最適化フラグとの付き合い方
`BIND(C)` をつけていない変数は、コンパイラが「このスコープ以外からは触られない」と判断し、レジスタにキャッシュしてしまいます。しかし、`BIND(C)` を付与した変数は、コンパイラに対して「外部(C言語)からいつ書き換えられるかわからないぞ」というサイン(揮発性)を送ることになります。
そのため、過剰なグローバル変数は性能劣化の原因になります。共有が必要なデータだけを厳選してモジュールに詰め込むのが、プロの流儀です。
3. ビルド時の注意:シンボル名の衝突
Fortranコンパイラ(gfortran, ifort等)によっては、シンボル名の末尾にアンダースコア `_` を勝手に付与する文化があります。`BIND(C)` を使えばこの挙動を抑制できますが、リンクエラーが出た際はまず `nm` コマンドでオブジェクトファイル内のシンボル名を確認してください。
生成されたオブジェクトファイルのシンボルを確認する基本技
nm my_fortran_code.o | grep my_global_var
—
最後に:皆さんのコードを強くするために
最初は「なんでこんなに記述に縛りがあるんだ?」と感じるかもしれません。ですが、Fortranという言語は、「数値計算という極限の負荷がかかる環境で、いかに計算結果の再現性と速度を両立させるか」を40年以上突き詰めてきた言語です。
`BIND(C)` を使ったデータ共有は、単なる機能ではなく「安全な境界線」です。この境界線を正しく引けるようになれば、皆さんの書くコードはC言語の柔軟性とFortranの数値計算性能を併せ持つ、非常に強力な武器になります。
まずは小さな配列一つから、ぜひ試してみてください。詰まったらまた聞きに来てくださいね。現場の知見を総動員してサポートします!

コメント