FortranプログラムからC言語で実装された関数を呼ぶ

分散・並列計算機上でのアプリケーション実行環境の研究をしているため、Fortranで書かれたプログラムを実行して性能評価を行うことがたびたびあった。今まではアプリケーションは無修正で評価のためのデータを取得することが出来ていたんだけど、今回Fortranで書かれたプログラムを少し修正して、データ取得用のコードを埋め込む必要が出来た。そのデータ取得のためのプログラム・関数はC言語で実装したもの。FortranプログラムからC言語で実装されたライブラリ内の関数を、関数なりサブルーチンなりで呼び出すことが出来ると言うことは知っていたけど、実際に自分で試してみたのは初めて。その簡単なメモ。

Fortranのmainプログラム

     1	      program main
     2	      integer cnt, a, b, ab
     3	
     4	      cnt = 5
     5	      print *, 'before call "print_cnt"'
     6	      call print_cnt(cnt)
     7	      print *, 'after  call "print_cnt"'
     8	
     9	      a   = 2
    10	      b   = 3
    11	      ab = multiply(a, b)
    12	      print '(I1, " x ", I1, " = ", I1)', a, b, ab
    13	      end

6行目で呼び出しているサブルーチン(C言語の戻り値のない関数に相当)print_cntと、11行目で実行している関数(C言語の戻り値のある関数に相当)multiplyがCで実装された関数。無事実行されれば以下のように出力される。

 before call "print_cnt"
print_cnt: 0000
print_cnt: 0001
print_cnt: 0002
print_cnt: 0003
print_cnt: 0004
 after  call "print_cnt"
2 x 3 = 6

次のようにコンパイルしてオブジェクトファイルを生成。

$ gfortran -g -Wall -c fmain.f

C言語ライブラリ

     1	#include <stdio.h>
     2	
     3	void print_cnt(int cnt)
     4	{
     5	  int i;
     6	  for (i = 0; i < cnt; i++) {
     7	    printf("print_cnt: %04d\n", i);
     8	  }
     9	}
    10	
    11	int multiply(int a, int b)
    12	{
    13	  return a * b;
    14	}
    15	
    16	// function for Fortran subroutine
    17	void print_cnt_(int *cnt)
    18	{
    19	  print_cnt(*cnt);
    20	}
    21	
    22	// function for Fortran function
    23	int multiply_(int *a, int *b)
    24	{
    25	  return multiply(*a, *b);
    26	}

14行目までは上記2つの関数の普通の実装。Cプログラムから使われる。17〜20行目がprint_cntをFortranプログラムから呼び出すためのラッパー関数、23〜26行目がmultiplyを呼び出すためのもの。print_cntはFortranでいうところのサブルーチン、multiplyは関数になるけど、C言語でラッパーを実行する点においては違いはない。
注意点として以下の2つ。

  • C関数名の最後にアンダーバーをつける
  • Fortranでは引数は参照渡しとなるので、C言語側のインターフェース関数の引数はポインタ型にする

ファイル名をclib.c、生成する静的ライブラリ名libclib.aとすると、次のようにして作成できる。

$ gcc -g -Wall -c clib.c
$ ar cr libclib.a clib.o
$ ranlib libclib.a

実行ファイルの作成

以上で出来たfmain.oとlibclib.aをリンクして、実行ファイルを生成する。

$ gfortran -L. -o fmain fmain.o libclib.a -lclib

実行すれば上のように出力される。

その他

今回の僕の目的は単純にFortranプログラムからCで実装された関数が呼べればよかったので、上記のサンプルのちょっとした発展程度のものだったけど、実際にはCとFortranではデータ型の違いや配列データの取り扱いに違いがあるために、もっとややこしいことになる。その辺は参考文献の1が詳しい。