お仕事用のメモです。
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」になります。
という訳で、このあたり初手でハマるので、ひとまず公開しておきます。
あとは、
– Common による FortranとC++との共有
– Fortran と構造体の共有
– 文字列の受け渡し
– Fortran で Read/Write した結果を C++と共有
まで調べればok。
京都の大学院生です。
一つ質問させていただきたいことがあります。
今、大学院の研究でFortranとC++を使い、シミュレーションを行っています。その際、f90ファイル内のサブルーチンをC++のコード内に呼び出したいのですが、可能でしょうか。
ちなみにC++はMicrosoft Visual Studio Express 2013 for Windows Desktopを使ってコンパイルしています。
お忙しいとは存じますが、ご意見を伺えたら幸いでございます。
結論から言うと「可能」です。手元の仕事でばりばり使っています。
この記事のように、Fortran の関数を extern “C” で再定義しないといけないので少々手間が掛かりますが、適当なヘッダファイルを作って(手元のものでは自動生成しています)include すれば普通の C言語ライブラリと同様に使えます。
配列の値が1始まりなのと、文字列渡しがちょっと面倒なのを除けば、既存の科学計算の Fortran ライブラリが使えますので、頑張る価値がありますよ、と。
ヘッダファイルを作成するときは、FORTRANで書いたサブルーチンをそのままコピペで貼り付けるだけでいいのでしょうか。
NO. コピペではできませんね。
記事にある通り、CALLMODULE_mp_CSUB1 のようにモジュール名と関数名を組み合わせたヘッダファイルを作ります。このあたりは、Intel Fortran と他のもので異なるし、Windows と Linux の環境でも異なります。
試した感じでは、Linux 環境で gcc + fortran library が一番素直にできそうです。
私の場合、Intel Fortran + Windows + C++ だったので茨の道なんですが :)
fortranのある一つのプログラムを実行した際に,modファイルとdllファイルが出力されます.dllファイル内の関数はC言語から読み込むことは可能なですが,modファイル内に書かれてある関数を読み込むことができません.何かよい方法はありますでしょうか
*.mod ファイルということは、Fortran の module の中間ファイルですよね。
おそらく http://www.moonmile.net/blog/archives/3359 のように module に contains されたサブルーチンは C++ から呼び出せると思います。
関数名が違っているのでちょっと使いづらいかもしれませんが、別途 define し直せば良いけど、どうでしょう?