はじめに
本記事では、Fortranにおける手続きの`EXTERNAL`属性について、その重要性と具体的な活用方法を解説します。特に、外部ライブラリの呼び出しや、異なるソースファイルで定義された手続きを参照する際に役立つ知識です。現代的なFortranでは`INTERFACE`ブロックの使用が推奨されていますが、`EXTERNAL`属性の理解は、既存コードの読解や、より柔軟なプログラム設計のために依然として重要です。
基礎知識: `EXTERNAL`属性とは?
`EXTERNAL`属性は、Fortranにおいて、現在コンパイルしているソースファイル内ではなく、別の場所(別のソースファイルや外部ライブラリ)で定義されている手続き(サブルーチンや関数)を参照することをコンパイラに指示するための宣言です。
例えば、科学技術計算で広く使われているBLAS(Basic Linear Algebra Subprograms)やLAPACK(Linear Algebra PACKage)といった外部ライブラリの手続きを呼び出す際に、この`EXTERNAL`属性が使用されます。
`EXTERNAL`属性を付けずに外部手続きを呼び出そうとすると、コンパイラはその手続きが現在のファイル内で定義されていると誤解し、未定義エラーなどを引き起こす可能性があります。
実装/解決策: `EXTERNAL`属性の使い方
`EXTERNAL`属性は、手続き名に対して宣言します。最も基本的な使い方は以下の通りです。
PROGRAM main
IMPLICIT NONE
EXTERNAL dgemm ! dgemm という手続きを外部で定義されているものとして宣言
REAL, DIMENSION(3, 3) :: A, B, C
REAL :: alpha, beta
! ここで dgemm を呼び出す
! alpha A B + beta C を計算し、結果を C に格納する例 (BLASのdgemmに相当)
alpha = 1.0
beta = 0.0
A = RESHAPE([1.0, 4.0, 2.0, 5.0, 3.0, 6.0], [3, 2]) ! 例として A を初期化
B = RESHAPE([7.0, 1.0, 8.0, 2.0, 9.0, 3.0], [2, 3]) ! 例として B を初期化
C = 0.0
! dgemm(TRANS_A, TRANS_B, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC)
! Fortranでは通常、引数リストで呼び出す
CALL dgemm(‘N’, ‘N’, 3, 3, 2, alpha, A, 3, B, 2, beta, C, 3)
PRINT , “Result C:”
PRINT “(3F8.2)”, C
CONTAINS
! 実際には dgemm は外部ライブラリ等で定義されているため、
! ここには記述しない。
! もしテストのためにローカルで定義する場合は、EXTERNAL宣言は不要になる。
! SUBROUTINE dgemm(…)
! …
! END SUBROUTINE dgemm
END PROGRAM main
上記の例では、`dgemm`というサブルーチンが外部で定義されていることを`EXTERNAL`属性で宣言しています。`main`プログラムからこの`dgemm`を呼び出すことが可能になります。
サンプルプログラム
ここでは、`EXTERNAL`属性を使って、別のファイルで定義された簡単なサブルーチンを呼び出す例を示します。
ファイル1: `external_proc.f90`
SUBROUTINE greet(name)
IMPLICIT NONE
CHARACTER(LEN=), INTENT(IN) :: name
PRINT , “Hello, “, TRIM(name), ” from external procedure!”
END SUBROUTINE greet
ファイル2: `main_program.f90`
PROGRAM main_with_external
IMPLICIT NONE
CHARACTER(LEN=20) :: my_name
EXTERNAL greet ! greet サブルーチンを外部で定義されているものとして宣言
my_name = “Fortran User”
! greet サブルーチンを呼び出す
CALL greet(my_name)
END PROGRAM main_with_external
コンパイルと実行 (gfortranの場合):
gfortran external_proc.f90 main_program.f90 -o main_app
./main_app
実行結果:
Hello, Fortran User from external procedure!
この例では、`external_proc.f90`で定義された`greet`サブルーチンを、`main_program.f90`の`EXTERNAL greet`宣言によって参照し、呼び出しています。
応用・注意点
1. `INTERFACE`ブロックとの比較
現代のFortranでは、`EXTERNAL`属性よりも`INTERFACE`ブロック内で手続きの仕様(引数の型、数、順序など)を宣言することが強く推奨されています。`INTERFACE`ブロックを使用すると、型安全性が向上し、コンパイラによる引数の型チェックが強化されるため、バグの早期発見に繋がります。
INTERFACE
SUBROUTINE dgemm(transa, transb, m, n, k, alpha, a, lda, b, ldb, beta, c, ldc)
IMPLICIT NONE
CHARACTER(LEN=), INTENT(IN) :: transa, transb
INTEGER, INTENT(IN) :: m, n, k, lda, ldb, ldc
REAL, INTENT(IN) :: alpha, beta
REAL, INTENT(INOUT) :: c(ldc,)
REAL, INTENT(IN) :: a(lda,)
REAL, INTENT(IN) :: b(ldb,)
END SUBROUTINE dgemm
END INTERFACE
`INTERFACE`ブロックで宣言された手続きは、`EXTERNAL`属性を明示的に付ける必要はありません。
2. 外部ライブラリのリンク
BLASやLAPACKのような外部ライブラリを利用する場合、`EXTERNAL`宣言だけでなく、コンパイル時にライブラリファイルをリンクする必要があります。例えば、OpenBLASを使用する場合は、コンパイラオプションに`-lopenblas`などを追加します。
gfortran main_program.f90 -o main_app -lblas -llapack # 例
3. 手続きポインタとの関係
`EXTERNAL`属性は、手続きポインタ(Procedure Pointers)とは異なります。手続きポインタは、実行時に手続きを動的に割り当てるための機能です。`EXTERNAL`属性は、コンパイル時またはリンク時に、コンパイラ(またはリンカ)に手続きの参照先を指示するものです。
4. `INTRINSIC`属性との区別
Fortranには組み込み関数(例: `SQRT`, `ABS`)がありますが、これらは`INTRINSIC`属性を持ちます。`EXTERNAL`属性は、これら組み込み手続きではなく、ユーザー定義または外部ライブラリで定義された手続きに対して使用します。もし、組み込み手続き名をユーザー定義手続きとして扱いたい場合は、`EXTERNAL`属性を付けることで、組み込み手続きよりもユーザー定義手続きが優先されることがあります(ただし、これは意図しない挙動を引き起こす可能性があるので避けるべきです)。
`EXTERNAL`属性は、Fortranプログラムが外部の世界と連携するための基本的な仕組みの一つです。現代的なコードでは`INTERFACE`ブロックが主流ですが、この属性の概念を理解しておくことで、より堅牢で保守性の高いコードを書くための助けとなるでしょう。

コメント