こんにちはFORTRAN使いです。fortranに多くの財産があるのでVC++で使おうと考えてい
ます。mingw環境でdllhelper( http://www.nanotech.wisc.edu/%7Ekhan/software/gnu-
win32/dllhelpers.html)でfortran DLLを作成しVC++から呼び出そうとしています。
HMODULE hVBVCDLL = AfxLoadLibrary(f77dll.dll);
if (hVBVCDLL != NULL)
{
typedef float (*Dllfunc)();
Dllfunc dllf2;
dllf2 = (Dllfunc)GetProcAddress(hVBVCDLL,dllf2_);
float result = (*dllf2)();
}
引数なしの関数の呼び出しは上記のように成功はしていますがsubroutineで引数つきの
ものを呼び出そうとするとどうやっていいのかわかりません。引数は参照渡しというの
はわかっているのですが。
具体的にサンプルを示していただけるとありがたいのですが、よろしくお願いします。
「fortran使い」さん、はじめまして。
実は、「FORTRAN」は、16年前の情報処理試験で勉強したきり
コンパイルもしたことがありません。頭の中で動かしただけです。
結局、当時は仕事が忙しく試験も受けていません(追想)。
「引数が参照渡し」ということは、「C言語」では
「コールバイリファレンス」ともいって、
float result = (*dllf2)(¶); ← 引数の前に「&」を付加
になると思います。
引数「para」は使う前に定義して置いて下さい。
「DLL」の方の関数に値を与えて使うなら、内容も事前に
設定して下さい。逆に「DLL」の方の関数で設定したときは
コールした後で値を取得できると思います。
引数が2個以上になったときは、スタックへの積み方が
「パスカル型」か「C型」か、どちらになるか実験してください。
有識者の補足・訂正、お願いします。
> 「引数が参照渡し」ということは、「C言語」では
> 「コールバイリファレンス」ともいって、
> float result = (*dllf2)(¶); ← 引数の前に「&」を付加
> になると思います。
この場合、宣言も
// * がつきます。anytype は任意の型名に読み替えてください。
typedef float (*Dllfunc)( anytype * para );
に変ります。
以上、C 言語の話。
C++ ではポインタの他に「参照」というモノが登場するので、関数の引数渡しは
「値渡し」「ポインタ渡し」「参照渡し」の3つになります。
C++ で「参照渡し」と言うと、3つめのものを指すことになり、この場合は
// ここに & がつきます。anytype は任意の型名に読み替えてください。
typedef float (*Dllfunc)( anytype & para );
Dllfunc dllf2;
dllf2 = (Dllfunc)GetProcAddress(hVBVCDLL,dllf2_);
// こっちには & がつきません。
float result = (*dllf2)( para );
になります。
なお、先の C 言語の例は「ポインタ渡し」と呼ばれます。
ただし、参照は C++ に独自のものであり、他の言語とやり取りする場合は、ポインタ渡しのほ
うが一般的なようです。
また、他の言語ではポインタ渡しと参照渡しの区別が無いため、C++ の「ポインタ渡し」を他
の言語では「参照渡し」と呼ぶ場合があります。
まぎらわしいですが、ご注意ください。
エートリーぶ さんお返事ありがとうございす。
質問時の文はfunctionのテストでsubroutineのテスト文は以下のようになります。
HMODULE hm2 = AfxLoadLibrary(f77dllsub.dll);
if (hm2 != NULL)
{
typedef void (*Dllsub)(double test[]);
Dllsub dllsub1;
dllsub1 = (Dllsub)GetProcAddress(hm2,dllf3_);
double testd[6],sampled;
(*dllsub1)(testd);
}
これだと問題なくコンパイルができますが配列の中身を見ると値が帰ってきてません。
エートリーぶさんのご指摘どおり参照渡しのために以下のように変更してみました。
subroutineの引数をポインタとして宣言
typedef void (*Dllsub)(double *test[]);
subroutineの引数に参照渡しで代入
(*dllsub1)(&testd);これだと
error C2664: 'void (double *[])' : 1 番目の引数を 'double (*__w64 )[6]' か
ら 'double *[]' に変換できません。
となります。
シャノンさん 鋭いご指摘ありがとうございます。
ネックになっていたのは「参照渡し」の理解のようです。
Cもfortranもそこそこの年数使ってきたのですが、自分の知っている文法だけ
で汚い文を書いてきたもので、とろこで今の最終的な状況は
HMODULE hm2 = AfxLoadLibrary(f77dllsub.dll);
if (hm2 != NULL)
{
typedef void (*Dllsub)(double &test[6]);
Dllsub dllsub1;
dllsub1 = (Dllsub)GetProcAddress(hm2,dllf3_);
double *testd[6],sampled;
(*dllsub1)(testd);
sampled = &testd[2];
CString sampleds;
sampleds.Format(%.2lf,sampled);
pDC->TextOut(0,200,sampleds);
}
こんな感じです。
: error C2234: 'test' : 参照の配列は宣言できません。(typedefのとこ)
: 'double **__w64 ' から 'double' に変換できません。(実際に関数を呼ぶとこ)
参照渡しの理解不足ですね。基礎から勉強しなおしてみます。
こんなのは参考になるでしょうか
http://www.microsoft.com/japan/developer/library/vccore/_core_mixed.2d.language_
programming_topics.htm
URL が切れてしまいました。
ブラウザのアドレスバーにコピペして繋げてください。
で。
FORTRAN 側の関数は、double 型(8バイト浮動小数点)の参照を引数に取り、そこに値を返す
ものでよろしいですね?
であれば、こうです。
typedef void (*Dllsub)(double * test);
Dllsub dllsub1 dllsub1 = (Dllsub)GetProcAddress(hm2,dllf3_);
double ret = 0.0;
(*dllsub1)(&testd);
間違えました。
typedef void (*Dllsub)(double * test);
Dllsub dllsub1 = (Dllsub)GetProcAddress(hm2,dllf3_);
double ret = 0.0;
(*dllsub1)(&ret);
これが正解です。
シャノンさんご親切にありがとうございます。
typedef void (*Dllsub)(double *test[6]);
Dllsub dllsub1;
dllsub1 = (Dllsub)GetProcAddress(hm2,dllf3_);
double testd[6],sampled;
(*dllsub1)(&testd);
これだと
C2664: 'void (double *[])' : 1 番目の引数を 'double (*__w64 )[6]' から 'double
*[]' に変換できません。
となります。
配列でやっているので問題が複雑になっているのかもしれません。
問題を単純化してつぶしていこうと考えています。
基本的に帰ってきてほしい引数を*で定義して&で代入すれば問題ないと考えていま
す。もしかしたらDLL環境であることが悪さしているのかもしれませんが。
とりあえず配列ではない引数のバージョンです。
typedef void (*Dllsub)(double *param);
Dllsub dllsub1;
dllsub1 = (Dllsub)GetProcAddress(hm2,dllf4_);
double mparam=0.0;
(*dllsub1)(&mparam);
CString sampleds;
sampleds.Format(%.2lf,mparam);
pDC->TextOut(0,200,sampleds);
コンパイル自体は問題ありません。(なぜ配列のものはだめなのでしょうか?)
ただ実行してみるとハンドルされていない例外が発生してとまってしまいます。
すみません。まちがいです。上記の
「ただ実行してみるとハンドルされていない例外が発生してとまってしまいます。」
はfortranDLLの作成上のミスでした。
fortranのsubroutineの引数の配列ではないバージョンは問題なく使えました。
あと少しでできそうです。
配列引数バージョンです。
typedef void (*Dllsub)(double *param[6]);
Dllsub dllsub1;
dllsub1 = (Dllsub)GetProcAddress(hm2,dllf3_);
double mparam[6];
(*dllsub1)(&mparam);
CString sampleds;
sampleds.Format(%.2lf,mparam[2]);
pDC->TextOut(0,200,sampleds);
error C2664: 'void (double *[])' : 1 番目の引数を 'double (*__w64 )[6]' か
ら 'double *[]' に変換できません。(subroutineの呼び出しのところで)
ここからは完全にVC++の問題だとは思うのですが、配列ではないものでよくて
配列でだめですね。よくわかりません。
double * test[ 6 ]
これはポインタの配列(double * 型の要素6個からなる配列)ですが
double ( * test )[ 6 ]
これは配列へのポインタ(double[ 6 ] 型の変数へのポインタ)です。
> typedef void (*Dllsub)(double *param[6]);
この関数は「ポインタの配列」を要求しますが
> double mparam[6];
> (*dllsub1)(&mparam);
渡しているのは「配列のポインタ」であることがわかるでしょうか。
(mparam は配列、&mparam は配列へのポインタです)
ただ、普通、こんなことは気にしません。
VC++ 側と Fortran 側で、配列の要素数が6個という合意があるのであれば、その先頭のポイ
ンタだけ渡せば事足ります。
つまり、
typedef void (*Dllsub)(double *param);
Dllsub dllsub1 = (Dllsub)GetProcAddress(hm2,dllf4_);
double param[ 6 ];
(*dllsub1)(param);
こんな感じで行けます(配列名は、その先頭要素のポインタ、つまり ¶m[ 0 ] と同義で
す)。
> ただ実行してみるとハンドルされていない例外が発生してとまってしまいます。
呼び出し規約の不一致かも。
typedef void ( __stdcall * Dllsub )( double * param );
としてみてください。
シャノン さん
例外発生の件は早とちりして記入してしまいました。
すみません。
経緯は3つ上の書き込みのとおりです。
fortran DLL の作成ミスでDLLにない関数を呼び出そうとしたものでした。
簡略化した配列でない引数のモデルはうまくいきました。
いまはfortranのsubroutineの引数が配列のときのみだけコンパイルエラーが発生する
という問題で悩んでいます。
一応__stdcallもつけてみましたが効果はありませんでした。