VS IVF C/C++とFortranのハイブリッドプログラミングの究極の解決策
IVFとVSが結合したFortran IDEは、VSの項目は同じ言語でしかプログラミングできないため、C/C++がFortranを呼び出す機能を実現するには、現在、Fortranコードを動的ライブラリまたは静的ライブラリにコンパイルする方法しか実現されていない.
コードを紹介する前に、C/C++呼び出しFortran機能を実現する際によく遭遇するいくつかの問題と解決方法を紹介すべきだと思います.多くの人が呼び出しコードの作成方法を知っているからです.しかし、いくつかの問題に遭遇しました.そうすれば、後ろのコードの山を見る必要はありません.
問題1:
呼び出したC/C++プログラムのコンパイル中に、次のような接続エラーメッセージが大量に表示されました.
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __CrtSetCheckCount already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(dbghook.obj) : error LNK2005: __crt_debugger_hook already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(setlocal.obj) : error LNK2005: __configthreadlocale already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(tidtable.obj) : error LNK2005: __encode_pointer already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(tidtable.obj) : error LNK2005: __decode_pointer already defined in MSVCRTD.lib(MSVCR90D.dll)
以上のエラーは、呼び出し元のランタイムライブラリタイプと呼び出されたランタイムライブラリタイプが一致しないため、関数再定義エラーです.解決策は次のとおりです.
まず、呼び出されたFortranダイナミックまたはスタティックライブラリプログラムの形式ライブラリのタイプを見て、「プロジェクト->プロパティ->Fortran->Libraris->Runtime Library」で、呼び出されたC/C++プログラムの形式ライブラリのタイプを見て、「プロジェクト->プロパティ->Configuration Properties->C+->Code Generation->Runtime Library」;
Fortranが静的ライブラリであり、Debugが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Debug Multithreaded (/libs:static/threads/dbglibs)
では、対応するC/C++プログラムDebug構成のRuntime Libraryのタイプは、
Multi-threaded Debug (/MTd)
Fortranが静的ライブラリであり、Releaseが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Multithreaded
では、対応するC/C++プログラムRelease構成のRuntime Libraryのタイプは、
Multi-threaded (/MT)
Fortranがダイナミックライブラリであり、Debugが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Debug Multithread DLL (/libs:dll/threads/dbglibs)
では、対応するC/C++プログラムDebug構成のRuntime Libraryのタイプは、
Multi-threaded Debug DLL (/MDd)
Fortranがダイナミックライブラリであり、Releaseが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Multithread DLL (/libs:dll/threads)
では、対応するC/C++プログラムRelease構成のRuntime Libraryのタイプは、
Multi-threaded DLL (/MD)
問題2:
Fortranスタティックライブラリを呼び出すと、ライブラリのパスと名前が一致しますが、次のような接続エラーが発生します.
error LNK2001: unresolved external symbol __imp__DataProcess
関数の定義が次のようになっている場合:
extern "C" int _declspec(dllimport) DataProcess()
それはよく解決します.declspec(dllimport)「削除すればいいです.静的ライブラリを呼び出すにはこの宣言は必要ありませんが、動的ライブラリを呼び出すにはこの宣言が必要です.削除しても問題がある場合は、静的ライブラリがプロジェクトに含まれているかどうかをもう一度確認してください.
問題3:
FortranでC/C++のコールバック関数を呼び出すと(実行時)、次の実行時エラーが発生します.
Unhandled exception at 0x00000005 in FortranDllTest.exe: 0xC0000005: Access violation reading location 0x00000005.
では、まずデバッグして、どのステップでこのようなエラーが発生したのかを見てみましょう.このステップのパラメータが正しいかどうかを見てみましょう.最初はプログラムのスタックが破壊されたため、私たちのコールバック関数の呼び出し規則が正しくない可能性が高いです.この呼び出しは__しかありません.cdecl、ではありません_stdcall、そうしないと上のエラーが発生します.
プログラムの説明を始めます.
次のルーチンは、Fortranをダイナミックライブラリにコンパイルする方法です.
1)簡単な呼び出し:
Fortranコード:
C/C++コード:
Fortranのようにデータを転送する場合は、値転送でもアドレス転送でも構いません.値転送の場合は、パラメータ宣言にValueキーを付ける必要があります.たとえば、次のようにします.
アドレス転送の場合はこのキーワードを付ける必要はありません.Fortranはデフォルトでアドレス転送です.また、Fortranに文字列を転送するときは文字列の長さを加えるべきで、fortranから文字列を転送するときは文字列にCHAR(0)を付けるように変更されています.
2)複雑な呼び出し:
Fortranコード:
callbackFunctioncallbackFunctioncallbackFunction
Fortranでの使用:
エクスポート関数の名前を
「callbackFunction」は、最初の方法で関数名を大文字に書くのではなく、大文字に書きます.
Fortranでのコールバック関数の宣言は、Interfaceで宣言したり、次の文で置き換えたりできます.
logical,external::Fun
しかし、これではコールバック関数のパラメータタイプを宣言することはできません.
C/C++におけるコールバック関数の呼び出し規則のデフォルトは_cdecl、そうでない場合は「」に変更してください.cdecl」は危険を残さない.
2 D配列をFortranに渡す場合は、特にFortranでは列先配列であり、C/C++では行先配列であることに注意してください.
間違いがあれば指摘してください.みんなで進歩します.
コードを紹介する前に、C/C++呼び出しFortran機能を実現する際によく遭遇するいくつかの問題と解決方法を紹介すべきだと思います.多くの人が呼び出しコードの作成方法を知っているからです.しかし、いくつかの問題に遭遇しました.そうすれば、後ろのコードの山を見る必要はありません.
問題1:
呼び出したC/C++プログラムのコンパイル中に、次のような接続エラーメッセージが大量に表示されました.
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __CrtSetCheckCount already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(dbghook.obj) : error LNK2005: __crt_debugger_hook already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(setlocal.obj) : error LNK2005: __configthreadlocale already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(tidtable.obj) : error LNK2005: __encode_pointer already defined in MSVCRTD.lib(MSVCR90D.dll) 1>LIBCMTD.lib(tidtable.obj) : error LNK2005: __decode_pointer already defined in MSVCRTD.lib(MSVCR90D.dll)
以上のエラーは、呼び出し元のランタイムライブラリタイプと呼び出されたランタイムライブラリタイプが一致しないため、関数再定義エラーです.解決策は次のとおりです.
まず、呼び出されたFortranダイナミックまたはスタティックライブラリプログラムの形式ライブラリのタイプを見て、「プロジェクト->プロパティ->Fortran->Libraris->Runtime Library」で、呼び出されたC/C++プログラムの形式ライブラリのタイプを見て、「プロジェクト->プロパティ->Configuration Properties->C+->Code Generation->Runtime Library」;
Fortranが静的ライブラリであり、Debugが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Debug Multithreaded (/libs:static/threads/dbglibs)
では、対応するC/C++プログラムDebug構成のRuntime Libraryのタイプは、
Multi-threaded Debug (/MTd)
Fortranが静的ライブラリであり、Releaseが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Multithreaded
では、対応するC/C++プログラムRelease構成のRuntime Libraryのタイプは、
Multi-threaded (/MT)
Fortranがダイナミックライブラリであり、Debugが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Debug Multithread DLL (/libs:dll/threads/dbglibs)
では、対応するC/C++プログラムDebug構成のRuntime Libraryのタイプは、
Multi-threaded Debug DLL (/MDd)
Fortranがダイナミックライブラリであり、Releaseが構成されている場合、Runtime Libraryのタイプは一般的に次のとおりです.
Multithread DLL (/libs:dll/threads)
では、対応するC/C++プログラムRelease構成のRuntime Libraryのタイプは、
Multi-threaded DLL (/MD)
問題2:
Fortranスタティックライブラリを呼び出すと、ライブラリのパスと名前が一致しますが、次のような接続エラーが発生します.
error LNK2001: unresolved external symbol __imp__DataProcess
関数の定義が次のようになっている場合:
extern "C" int _declspec(dllimport) DataProcess()
それはよく解決します.declspec(dllimport)「削除すればいいです.静的ライブラリを呼び出すにはこの宣言は必要ありませんが、動的ライブラリを呼び出すにはこの宣言が必要です.削除しても問題がある場合は、静的ライブラリがプロジェクトに含まれているかどうかをもう一度確認してください.
問題3:
FortranでC/C++のコールバック関数を呼び出すと(実行時)、次の実行時エラーが発生します.
Unhandled exception at 0x00000005 in FortranDllTest.exe: 0xC0000005: Access violation reading location 0x00000005.
では、まずデバッグして、どのステップでこのようなエラーが発生したのかを見てみましょう.このステップのパラメータが正しいかどうかを見てみましょう.最初はプログラムのスタックが破壊されたため、私たちのコールバック関数の呼び出し規則が正しくない可能性が高いです.この呼び出しは__しかありません.cdecl、ではありません_stdcall、そうしないと上のエラーが発生します.
プログラムの説明を始めます.
次のルーチンは、Fortranをダイナミックライブラリにコンパイルする方法です.
1)簡単な呼び出し:
Fortranコード:
SUBROUTINE FSUB (INT_ARG, STR_IN, STR_OUT)
IMPLICIT NONE
!DEC$ IF DEFINED (_DLL)
!DEC$ ATTRIBUTES DLLEXPORT :: FSUB
!DEC$ END IF
INTEGER, INTENT(IN) :: INT_ARG
CHARACTER(*), INTENT(IN) :: STR_IN
CHARACTER(*), INTENT(OUT) :: STR_OUT
CHARACTER*5 INT_STR
WRITE (INT_STR,'(I5.5)')INT_ARG
STR_OUT = STR_IN // INT_STR // CHAR(0)
RETURN
END
C/C++コード:
#ifdef __cplusplus
extern "C"
#endif
#ifdef USEDLL
__declspec(dllimport) void FSUB (int *INT_ARG,char *STR_IN,char *STR_OUT,size_t STR_IN_LEN,size_t STR_OUT_LEN); //
void main (int argc, char *argv[])
{
char instring[40];
char outstring[40];
int intarg;
strcpy_s(instring,"Testing...");
intarg = 123;
FSUB(&intarg,instring,outstring,strlen(instring),sizeof(outstring));
printf("%s
",outstring);
}
Fortranのようにデータを転送する場合は、値転送でもアドレス転送でも構いません.値転送の場合は、パラメータ宣言にValueキーを付ける必要があります.たとえば、次のようにします.
INTEGER, value,INTENT(IN) :: INT_ARG
アドレス転送の場合はこのキーワードを付ける必要はありません.Fortranはデフォルトでアドレス転送です.また、Fortranに文字列を転送するときは文字列の長さを加えるべきで、fortranから文字列を転送するときは文字列にCHAR(0)を付けるように変更されています.
2)複雑な呼び出し:
Fortranコード:
function callbackFunction(Array,count,Fun) BIND(c,NAME='callbackFunction')
use,intrinsic :: ISO_C_BINDING
implicit none
!DEC$ IF DEFINED (_DLL)
!DEC$ ATTRIBUTES DLLEXPORT :: callbackFunction
!DEC$ END IF
interface
function Fun(num)
use,intrinsic :: ISO_C_BINDING
implicit none
integer(C_INT),value :: num
logical :: Fun
end function
end interface
integer (C_INT),value :: count ! value
integer (C_INT),INTENT(IN)::Array(count)
integer ::callbackFunction
integer :: i
logical ::b
!
do i=1,count
b=Fun(Array(i))
if(b) exit; ! true
end do
callbackFunction=0;
return
END
C/C++コード:callbackFunctioncallbackFunctioncallbackFunction
bool HelloNumber3(int i) //
{
if(i>97)
{
printf("Hello %d
",i);
return false;
}
else
return true;
}
#ifdef __cplusplus
extern "C"
#endif
#ifdef USEDLL
__declspec(dllimport) int callbackFunction(int* Array,int rows,bool(*f)(int));
void main (int argc, char *argv[])
{
int ary[4];
ary[0]=10;ary[1]=11;ary[2]=12;ary[3]=13;
int rtb=callbackFunction(ary,4,HelloNumber);
}
Fortranでの使用:
BIND(c,NAME='callbackFunction')
エクスポート関数の名前を
「callbackFunction」は、最初の方法で関数名を大文字に書くのではなく、大文字に書きます.
Fortranでのコールバック関数の宣言は、Interfaceで宣言したり、次の文で置き換えたりできます.
logical,external::Fun
しかし、これではコールバック関数のパラメータタイプを宣言することはできません.
C/C++におけるコールバック関数の呼び出し規則のデフォルトは_cdecl、そうでない場合は「」に変更してください.cdecl」は危険を残さない.
2 D配列をFortranに渡す場合は、特にFortranでは列先配列であり、C/C++では行先配列であることに注意してください.
間違いがあれば指摘してください.みんなで進歩します.