fortran でサブルーチンの引数にサブルーチンを渡す
fortran のプログラムにおいてサブルーチンの引数にサブルーチン名を書いて処理の細かい部分を変更したいと思った。C言語なら
定義:void refer ( void (*proc) ( void ) ) { proc() ; }のように書くところ。
void hello(void){ printf("hello"); }
呼出:refer(hello);
実引数が手続である場合,その対応する仮引数は仮手続となります。仮手続は関数またはサブルーチン副プログラム中に置くことができます。(強調あらき)というわけでfortranでも同じように関数やサブルーチンを引数として渡すことが出来る。
http://www.xlsoft.com/jp/products/intel/cvf/docs/vf-html/lr/lr08_07_07.htm
実引数としては,変数,式,または手続名を使用することができます。実引数の型,種別パラメタ,および次元数は,それに結合された仮引数と一致していなくてはなりません。C言語の場合と同様に関数の戻り値と引数の型を明示的に書かないといけない。Cの場合、この明示的に書く作業は、関数の定義で関数の引数の中で書くが、fortranの場合は INTERFACE 文で定義する。例えばCのソースで
…
副プログラムとして引用されている仮引数は,呼び出し側ルーチンで EXTERNAL または INTRINSIC として宣言された実引数と結合されなくてはなりません。(強調あらき)
http://www.xlsoft.com/jp/products/intel/cvf/docs/vf-html/lr/lr08_07.htm
int refer ( double (*func)( int* ) ) { [def] }とある部分は(引数はわざとアドレスで渡すように書いた)、fortranでは次のようになる:
INTEGER FUNCTION refer (func) INTERFACE REAL(8) FUNCTION func (a) INTEGER,INTENT(IN):: a END FUNCTION END INTERFACE [def] END FUNCTION
fortranでは手続(関数、サブルーチンなど)の引数の並びと引数の型宣言をバラバラに書く仕様なのでこのような書き方になるのだろう。*1
注意すべき点は手続を引数にとる手続を呼び出すときに、手続が引数に代入できるように EXTERNAL 宣言をしなくてはいけないことである。つまり実際に refer に func を代入して動作させる部分のソースは次のようになる
EXTERNAL func ! 宣言部でこの行が必須Cではこのような宣言は要らない(だから意図しないアドレスを渡してしまうこともできる)。fortranではこのEXTERNAL宣言のおかげで、関数が存在しないときにコンパイルの段階で文法エラーを返すことが出来るようになっているようだ。
I= refer(func)
サンプル・プログラム(デモ用のミニマル・コンプリート)
PROGRAM main ! main が「呼び出し側ルーチン」[2010.2.15] 手許のg95でコンパイルしてみたら、EXTERNAL文が無くてもCALLしてあれば通るみたい
EXTERNAL A,B ! 「EXTERNALとして宣言」
CALL REFER(A) ! この A, B が「手続きである実引数」
CALL REFER(B)
END PROGRAMSUBROUTINE REFER(PROC) ! この PROC が「仮手続」
INTERFACE
SUBROUTINE PROC
END
END INTERFACE
CALL PROC
RETURN
ENDSUBROUTINE A
WRITE(*,*) 'hello A()'
RETURN
ENDSUBROUTINE B
WRITE(*,*) 'hello B()'
RETURN
END
PROGRAM main ! main が「呼び出し側ルーチン」
EXTERNAL B ! 「EXTERNALとして宣言」
CALL REFER(A) ! この A, B が「手続きである実引数」
CALL REFER(B)
CALL A ! A を呼び出していたら EXTERNAL は要らないらしい
END PROGRAM