windows 環境で fortran と C++ を連携させる(intel fortranの場合)

お仕事用のメモです。

FORTRANとCが混在したプログラム – Nobuhito Mori
http://www.oceanwave.jp/index.php?FORTRAN%A4%C8C%A4%AC%BA%AE%BA%DF%A4%B7%A4%BF%A5%D7%A5%ED%A5%B0%A5%E9%A5%E0#ubf9712b
プログラミングの話:Fortran,C,C++ の連携
http://www.mlab.ice.uec.ac.jp/~ej-sib/prog/prog_mixed.html#fortran_from_other_C
Fortran プログラマーズガイド: 11 – C と Fortran のインタフェース
http://www.hiroshima-cu.ac.jp/japanese/IPC/hunet99/sun/WorkShop/ja/html_docs/fortran/prog_guide/11_cfort.doc.html
FORTRANとCが混在したプログラム – Nobuhito Mori
http://www.oceanwave.jp/index.php?FORTRAN%A4%C8C%A4%AC%BA%AE%BA%DF%A4%B7%A4%BF%A5%D7%A5%ED%A5%B0%A5%E9%A5%E0#v4d3436c
DLLのメイク
http://rakasaka.fc2web.com/propath/prodll_make.html#Command

と、Intel Fortran と Free Fortran 95 を使って windows 7 上で調べています。
要は、

  • Fortran で計算ロジックを lib/dll で作る
  • C/C++ からライブラリを呼び出す

ということをするので、その下調べ。
どうやら、巷で溢れてい情報は、linux 上の C/Fortran連携のようで、C言語の関数の最後にアンダーバーが付くというのは、windows 版だとちょっと違う…ってな具合です。Fortran側を「Func1」で定義しておいて、C言語側で「Func1_」で呼び出すというのは、gcc の仕様らしい。逆に言えば、Intel Fortran や Free Fortran
95 だと違っているんですよ、ってなことです。

■DLLの定義

subroutine DllSub1(x,y)

  ! この DLL のユーザーへのサブルーチン Dll1 の説明
  !
  !DEC$ ATTRIBUTES DLLEXPORT::DllSub1

  ! 変数
  integer, intent(in) :: x, y

  ! DllSub1 の本文

end subroutine DllSub1

integer function DllFunc1( x, y )

  ! この DLL のユーザーへのサブルーチン Dll1 の説明
  !
  !DEC$ ATTRIBUTES DLLEXPORT::DllFunc1

  ! 変数
  integer, intent(in) :: x, y

  ! DllFunc1 の本文
  DllFunc1 = x + y

end function DllFunc1

■Libraryの定義

! C++からの呼び出し
module callmodule
    implicit none
  contains
    ! 関数定義1
    integer function csub1( x, y )
        integer, intent(in):: x, y
        csub1 = x + y
        return
    end function csub1
end module callmodule

! 関数定義1
integer function csub2( x, y )
    integer, intent(in):: x, y
    csub2 = x + y
    return
end function csub2

■C++から呼出し

#include "stdafx.h"
#include <iostream>
using namespace std;

extern "C" {
	extern int CALLMODULE_mp_CSUB1( const int *, const int * );
	extern int CSUB2( const int *, const int * );
}

#define DllImport __declspec( dllimport )

extern "C" {
	DllImport void DLLSUB1( const int *, const int * );
	DllImport int  DLLFUNC1( const int *, const int * );
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << "c and fortran programing" << endl;

	int x = 10;
	int y = 10;
	int ans = CALLMODULE_mp_CSUB1(&x,&y);
	cout << "ans:" << ans  << endl;

	ans = CSUB2(&x,&y);
	cout << "ans:" << ans  << endl;

	DLLSUB1( &x, &y );
	ans = DLLFUNC1( &x, &y );
	cout << "ans:" << ans  << endl;

	return 0;
}

Intel Fortran の場合は上記の呼出ができます。gcc とは違って末尾に「_」が付きません。関数名称は、nm コマンドを使うと調べることができます。

>nm CallModule.obj

00000000 N .debug$S
00000000 N .debug$T
00000000 i .drectve
00000000 t .text
00000000 r .trace
00000001 a @feat.00
00000000 T _CALLMODULE.
00000006 T _CALLMODULE_mp_CSUB1

Fortran でライブラリを作って、C言語にリンクさせる場合には「CALLMODULE_mp_CSUB1」という定義か「CSUB2」という定義になります。「CALLMODULE」の部分は fortran の module 名ですね。関数名がぶつからないように module を使うと、それが名前に入ってくるという罠なんですが、まぁ、コンパイラ依存で書いて良いのならば、こんな風に直接名前を付けられます。module に入っていない場合は、実はC言語の定義「_CSUB2」のように先頭にアンダーバーが付いた名前になります。

DLLの関数として公開する場合は「!DEC$ ATTRIBUTES DLLEXPORT::DllSub1」のような属性をつけます。Fortranの場合は「!」以降がコメントになるので、コメントを使ったプラグラマです。これは独自文法になるので intel fortran のみかと。
C言語の呼び出しのほうは、通常のDLL呼出と同じで「__declspec( dllimport )」を使います。このあたりは定番です。Fortran の方からは、大文字で公開されるので fortran 内で「DllFunc1」のように小文字を使っていても、公開されている関数名は「DLLFUNC1」になります。

という訳で、このあたり初手でハマるので、ひとまず公開しておきます。

カテゴリー: 開発, Fortran パーマリンク

windows 環境で fortran と C++ を連携させる(intel fortranの場合) への7件のフィードバック

  1. masuda のコメント:

    あとは、
    – Common による FortranとC++との共有
    – Fortran と構造体の共有
    – 文字列の受け渡し
    – Fortran で Read/Write した結果を C++と共有
    まで調べればok。

  2. 増成一樹 のコメント:

    京都の大学院生です。
    一つ質問させていただきたいことがあります。

    今、大学院の研究でFortranとC++を使い、シミュレーションを行っています。その際、f90ファイル内のサブルーチンをC++のコード内に呼び出したいのですが、可能でしょうか。
    ちなみにC++はMicrosoft Visual Studio Express 2013 for Windows Desktopを使ってコンパイルしています。

    お忙しいとは存じますが、ご意見を伺えたら幸いでございます。

    • masuda のコメント:

      結論から言うと「可能」です。手元の仕事でばりばり使っています。

      この記事のように、Fortran の関数を extern “C” で再定義しないといけないので少々手間が掛かりますが、適当なヘッダファイルを作って(手元のものでは自動生成しています)include すれば普通の C言語ライブラリと同様に使えます。
      配列の値が1始まりなのと、文字列渡しがちょっと面倒なのを除けば、既存の科学計算の Fortran ライブラリが使えますので、頑張る価値がありますよ、と。

  3. KM のコメント:

    ヘッダファイルを作成するときは、FORTRANで書いたサブルーチンをそのままコピペで貼り付けるだけでいいのでしょうか。

    • masuda のコメント:

      NO. コピペではできませんね。
      記事にある通り、CALLMODULE_mp_CSUB1 のようにモジュール名と関数名を組み合わせたヘッダファイルを作ります。このあたりは、Intel Fortran と他のもので異なるし、Windows と Linux の環境でも異なります。
      試した感じでは、Linux 環境で gcc + fortran library が一番素直にできそうです。
      私の場合、Intel Fortran + Windows + C++ だったので茨の道なんですが :)

  4. k のコメント:

    fortranのある一つのプログラムを実行した際に,modファイルとdllファイルが出力されます.dllファイル内の関数はC言語から読み込むことは可能なですが,modファイル内に書かれてある関数を読み込むことができません.何かよい方法はありますでしょうか

    • masuda のコメント:

      *.mod ファイルということは、Fortran の module の中間ファイルですよね。
      おそらく http://www.moonmile.net/blog/archives/3359 のように module に contains されたサブルーチンは C++ から呼び出せると思います。
      関数名が違っているのでちょっと使いづらいかもしれませんが、別途 define し直せば良いけど、どうでしょう?

コメントは停止中です。