gcc(gfortran) で C から Fortran77 のサブルーチンを呼ぶ
私は fortran は全く使えないんだが、有名なパッケージが fortran77 で書かれてて書き直すのもだるいんで C でラッパーを作ることにした。その練習です。
手法
ソースコード
呼び出す側の C プログラム test.c
#include <stdio.h> #define NUM 3 int main(int argc, char *argv[]){ int a[NUM][NUM]; int aa[NUM*NUM]; int i, j, k, n; n = NUM; k = 0; printf("C: a[i][j]\n"); for(i = 0; i < n; i++){ for(j = 0; j < n; j++){ a[i][j] = 10*i+j; printf("%2d ", a[i][j]); aa[k++] = a[i][j]; } printf("\n"); } printf("fortran77 : a(i, j) - output_(a:'2D array', &n)\n"); output_(a, &n); printf("fortran77 : a(i, j) - output2_(a(0:n-1, 1:n):'2D array', &n)\n"); output2_(a, &n); printf("fortran77 : a(i, j) - output_(aa:'1D array', &n)\n"); output_(aa, &n); printf("fortran77 : a(i, j) - output2_(aa(0:n-1):'2D array', &n)\n"); output2_(aa, &n); return 0; }
fortran77 のサブルーチン(または関数)を呼ぶときには注意点がいくつかある。
fortran77 での integer は C では long int にすると無難。*1- fortran77 のサブルーチン名の末尾にアンダーバーをつけること。つまり、サブルーチンが hoge(...) なら C では hoge_(...) とする*2。これは gcc の仕様らしいので他のコンパイラではまた別の方法を使う必要あり。
- fortran77 のサブルーチンの引数は参照渡し。
- 文字変数(後日追記)
- 2次元配列を fortran に渡すときは1次元配列を渡す時と同じで OK。ただし転置がかかり、行と列が逆に成ります。C で関数に2次元配列を渡す時みたいな面倒なことしなくていい。
呼び出される側の fortran77 プログラム ftest.f
c output(a) subroutine output(a, n) implicit integer(a-h,o-z) dimension a(n, n) do 10 i=1, n write(*,*) (a(i,j),j=1,n) 10 continue end c output2(a) subroutine output2(a, n) implicit integer(a-h,o-z) dimension a(0:n-1, n) do 20 i=0, n-1 write(*,*) (a(i,j),j=1,n) 20 continue end
DLL を用いない方法
$ gcc -c test.c $ gfortran ftest.f test.o -o test $ ./test
で OK。
後述する DLL を使う方法とは異なり、gcc に食わせた後に gfortran でリンクする。これを逆にして
hellow.o:hellow.f:(.text+0x3b): undefined reference to `_gfortran_st_write' hellow.o:hellow.f:(.text+0x59): undefined reference to `_gfortran_transfer_chara cter_write' hellow.o:hellow.f:(.text+0x76): undefined reference to `_gfortran_transfer_real_ write' hellow.o:hellow.f:(.text+0x84): undefined reference to `_gfortran_st_write_done' collect2: error: ld returned 1 exit status
と gcc にエラー吐かれて(この時使ってた hellow.f は double を受け取って 'hellow (その値)' を出力するだけのものです。)悩んでた。
DLL を経由する方法
$ gfortran --shared -o ftest.dll ftest.f $ gcc -I ./ -L ./ test.c -lftest -o test $ ./test
gfortran で dll を作成した後にそれをライブラリとして gcc に食わせる方法。こっちはすぐに出来た。
出力結果
C: a[i][j] 0 1 2 10 11 12 20 21 22 fortran77 : a(i, j) - output_(a:'2D array', &n) 0 10 20 1 11 21 2 12 22 fortran77 : a(i, j) - output2_(a(0:n-1, 1:n):'2D array', &n) 0 10 20 1 11 21 2 12 22 fortran77 : a(i, j) - output_(aa:'1D array', &n) 0 10 20 1 11 21 2 12 22 fortran77 : a(i, j) - output2_(aa(0:n-1):'2D array', &n) 0 10 20 1 11 21 2 12 22
参考にしたもの
- 「マルチスケール計算材料科学」講義方針・スケジュール:このサイトの「Fortranの基礎」という PDF が要点がまとまっていて Fortran ド素人の私には非常に助かった。
- C から Fortran を呼び出してみる - 雑食性雑感雑記:一番わかり易い。大規模行列だとメモリエラーが起こりうるだと…。
- FORTRANとCが混在したプログラム - Nobuhito Mori
- Fortran: uyota 匠の一手:今回の逆のケース(fortran から C の関数を呼び出す)も解説してある。