TRNSYSの関数をC/C++から呼び出すと動かない
TRNSYS-USERSにC/C++からの関数呼び出しの話題が流れていました。
[TRNSYS-users] C++ API not working
C/C++のコンポーネントから整数値を返す関数を呼び出すとNaNが返ってくるとか、文字列を返す関数を使うとAccess Violationでクラッシュするとか。なかなか興味深い質問なのでしばらく注目してました。でも誰も返事がない。
C/C++でコンポーネントを書いている人ってあんまりいないんですね、たぶん。 試しに書いてみたら、前者のNaNが返ってくるのは再現できませんでした。自分で書いてみたらちゃんと動く。
質問者と何が違うんだろうと考えてみたんですが、これ原因はおそらく関数の呼び出しスタイルがTRNSYS16かTRNSYS17の違いじゃないかなー、と思います。どちらのスタイルで書くかによってTRNSYS側の挙動が違っていた気がします。
問題は後者の方ですが、もともとTRNSYSはFORTRANを前提に作られているので、C/C++から呼び出すにはひと手間必要になります。文字列を返す関数は今まで使う機会がなかったので、実は試していませんでした。(単純に呼び出してみたら質問者と同じようにAccess Violationでクラッシュした) で、いろいろ調べ始めたらFORTRANとC/C++って文字列の扱いが違うので、そのままじゃやり取りできないんですね。(FORTRANは固定長で、C/C++はNULL終端で云々。。。)
詳しくはこちら↓
[日本語]
インテル® Fortran コンパイラー 14.0 ユーザー・リファレンス・ガイド
文字データ型の戻り
[英語版]
User and Reference Guide for the Intel® Fortran Compiler 15.0
Returning Character Data Types
この記事の例を参考に、もともとgetDeckFileName()の関数をベースに新しいC/C++用に新しく関数getDeckFileNameEx() を追加してみました。
・TrnsysFunciton.f90
Function getDeckFileNameEx() bind(c,name=”getDeckFileNameEx”)
!dec$ attributes dllexport :: getDeckFileNameEx
Use TrnsysData
Use, intrinsic :: iso_c_binding
type(C_ptr) :: getDeckFileNameEx
Character(len=maxPathLength) ret
ret = trim(deckn1)//CHAR(0)
getDeckFileNameEx = C_LOC(ret)
End Function getDeckFileNameEx C/C++の呼び出し側はこんな感じで記述。。。
・TRNSYS.h
extern “C” __declspec(dllimport) char* _cdecl getDeckFileNameEx(void);
・Type201.cpp
char *dckfilename;
dckfilename = getDeckFileNameEx(); こうするとちゃんと文字列でファイル名が取得できました。
一応対策はできたんですが、毎回毎回これと同じように関数を追加するのもなんかなー。それにFORTRANのソースコードの変更とリビルドが必要になるのでコンパイラも必要になる。ちょっと一般向けにはお勧めしにくい。他の方法ないかなー、というのが今日のひとまずの結論。
2017/4/15追記
TrnsysFunciton.f90に手を加えずに直接C/C++から関数を呼び出す方法をまとめました。
1件のピンバック
動いたけど半信半疑 – 建築環境工学系日記