Fortranの「インターフェース」を攻略せよ:ABSTRACT INTERFACEで実現する型安全なコールバック
こんにちは。宇宙航空の現場で何十年も数値計算と格闘してきた者です。
C言語で`void (callback)(double, double)`といった関数ポインタを駆使したり、Pythonで高階関数をサラッと書いたりしている皆さん、Fortranを触り始めて「あれ、Fortranって関数をどうやって渡すの?」と戸惑ったことはありませんか?
Fortranは古い言語だと思われがちですが、実は手続き(関数やサブルーチン)の型を厳密に定義し、コンパイル時に徹底的にチェックする「型安全」の鬼のような言語です。今回は、その核心部分である`ABSTRACT INTERFACE`を使った、モダンな手続きの抽象化について紐解いていきましょう。
—
なぜ「インターフェース」が必要なのか?
C言語の関数ポインタは強力ですが、コンパイラが「この関数、本当にその引数で合ってる?」とチェックしてくれる範囲には限界がありますよね。Fortranでは、「この形式(引数の型や数)を持つ手続きなら何でもOK」という設計図をあらかじめ作っておくのが作法です。
この設計図にあたるのが `ABSTRACT INTERFACE` です。
ステップ1:まずは「設計図」を書く
まず、あなたが実装したいコールバック関数の「型」を定義します。例えば、「2つの実数を受け取って、結果を返す関数」を定義してみましょう。
! この型定義が「設計図」になります。
! 名前は「callback_func」としましょう。
ABSTRACT INTERFACE
FUNCTION callback_func(x, y) RESULT(res)
REAL, INTENT(IN) :: x, y
REAL :: res
END FUNCTION callback_func
END INTERFACE
これだけで、`callback_func`という「名前のついた型」が誕生しました。
ステップ2:手続きポインタを宣言する
次に、この設計図を使って、実際に手続きを格納する変数(手続きポインタ)を作ります。
! プロシージャポインタの宣言
PROCEDURE(callback_func), POINTER :: my_op => NULL()
ここで`PROCEDURE(callback_func)`と書くことで、「このポインタに入る関数は、必ず先ほど定義した`callback_func`の形式を満たさなければならない」とコンパイラに誓約させます。もし違う引数の関数を代入しようとすれば、コンパイル時に容赦なくエラーを出してくれます。これがFortranの強さです。
ステップ3:実際に使ってみる
では、具体的な関数を代入して実行してみましょう。
MODULE math_ops
CONTAINS
FUNCTION add(a, b) RESULT(res)
REAL, INTENT(IN) :: a, b
res = a + b
END FUNCTION add
FUNCTION multiply(a, b) RESULT(res)
REAL, INTENT(IN) :: a, b
res = a b
END FUNCTION multiply
END MODULE math_ops
PROGRAM main
USE math_ops
! … (ここに先ほどのINTERFACE定義を置くか、MODULEに入れておきます)
PROCEDURE(callback_func), POINTER :: my_op => NULL()
! 足し算を代入
my_op => add
PRINT , “結果: “, my_op(10.0, 5.0) ! 15.0
! 掛け算を代入
my_op => multiply
PRINT , “結果: “, my_op(10.0, 5.0) ! 50.0
END PROGRAM
—
現場の視点:なぜこれが「最強」なのか
数値計算の現場では、シミュレーションのアルゴリズム(例えば数値積分や微分方程式のソルバー)をライブラリとして提供し、ユーザーにその中身(物理モデル)を差し替えてもらうことがよくあります。
このとき、`ABSTRACT INTERFACE` を使っておくと、ユーザーが誤った引数の関数を渡そうとした瞬間にコンパイルエラーで弾くことができます。実行時にセグメンテーションフォールトで落ちる悲劇を、コンパイル段階で防げるのです。
最適化のワンポイントアドバイス
手続きポインタを多用するコードは、CPUの分岐予測やインライン展開を阻害し、わずかにパフォーマンスを低下させることがあります。
- ホットスポットでは避ける: ループの最内側で頻繁に呼び出されるなら、手続きポインタではなく`MODULE`内の`GENERIC`インターフェースで静的に解決することをお勧めします。
- コンパイルフラグ: `gfortran -O3` 等でビルドする際、最適化レポートを確認する癖をつけましょう。
—
最後に:一歩踏み出してみましょう
「インターフェース」という言葉は難しく聞こえるかもしれませんが、結局のところ「この型の関数以外は受け付けないよ」という堅い約束事に過ぎません。
最初はコード量が増えるように感じるかもしれませんが、大規模なシミュレーションコードにおいて、この「約束事」がバグを未然に防ぎ、あなたのデバッグ時間を劇的に短縮してくれることを保証します。
まずは、小さな関数ポインタを扱うところから試してみてください。Fortranの型システムの美しさに、きっと気づいていただけるはずです。応援しています!

コメント