potass' blog

ポタシウムのことが書いてないブログ。

gcc(gfortran) で C から Fortran77 のサブルーチンを呼ぶ

私は fortran は全く使えないんだが、有名なパッケージが fortran77 で書かれてて書き直すのもだるいんで C でラッパーを作ることにした。その練習です。

環境

OS : Win 8.1 (64 bit)
コンパイラgcc 4.8.3 (including gfortran) on cygwin

手法

ソースコード

呼び出す側の 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

参考にしたもの

*1:と大学の計算機演習で習ったが別に気にせんでも大丈夫だった。

*2:C++ から読み込みたいときは extern "C" を使う。