VS与IVF C/C++与Fortran混合编程的解决方案


IVF与VS结合的Fortran IDE,由于VS中的项目只能使用同一种语言来编程,所以如果要实现C/C++调用Fortran的功能,目前只有将Fortran代码编译成动态库或静态库的方式来实现:


在介绍代码之前我觉得应该先介绍一下我们在实现C/C++调用Fortran功能时经常会遇到的一些问题和解决方法,因为很多人已经知道了怎样编写调用代码,但是遇到了一些问题,这样就可以直接看下面的解决方法,而不必看后面的一大堆代码了:

问题一:

在编译调用的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/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)

问题二:

在调用Fortran静态库的时候,库的路径和名称都设置对了但是一直出现下面这样的连接错误:

error LNK2001: unresolved external symbol __imp__DataProcess

如果你的函数的定义像下面这样:

extern "C"  int _declspec(dllimport)  DataProcess()

那很好解决,只要将”_declspec(dllimport)“删除就可以了,因为调用静态库不需要这个声明,但是调用动态库时必须要有这个声明,如果去掉了还是有问题,那请再检查一遍静态库是否包含到项目中。

问题三:

在Fortran中调用C/C++传进来的回调函数时(运行时),出现下面的运行时错误:

Unhandled exception at 0x00000005 in FortranDllTest.exe: 0xC0000005: Access violation reading location 0x00000005.

那我们首先调试一下看是运行到哪一步出现这样的错误,在看看这一步的参数是否正确,起始一般是程序的堆栈被破坏导致的,很有可能是我们的回调函数的调用约定不正确造成的,这个调用约定只能是__cdecl,不能是__stdcall,否则就会出现上面的错误。


下面开始介绍程序了:

下面的例程都是将Fortran编译成动态库的方式:

1)简单的调用:

Fortran代码:

[plain]  view plain copy
  1. SUBROUTINE FSUB (INT_ARG, STR_IN, STR_OUT)  
  2. IMPLICIT NONE  
  3. !DEC$ IF DEFINED (_DLL)  
  4. !DEC$ ATTRIBUTES DLLEXPORT :: FSUB  
  5. !DEC$ END IF  
  6. INTEGER, INTENT(IN) :: INT_ARG  
  7. CHARACTER(*), INTENT(IN) :: STR_IN  
  8. CHARACTER(*), INTENT(OUT) :: STR_OUT  
  9. CHARACTER*5 INT_STR  
  10. WRITE (INT_STR,'(I5.5)')INT_ARG  
  11. STR_OUT = STR_IN // INT_STR // CHAR(0)  
  12. RETURN  
  13. END   

C/C++代码:

[cpp]  view plain copy
  1. #ifdef __cplusplus  
  2. extern "C"   
  3. #endif  
  4. #ifdef USEDLL  
  5. __declspec(dllimportvoid FSUB (int *INT_ARG,char *STR_IN,char *STR_OUT,size_t STR_IN_LEN,size_t STR_OUT_LEN);  //字符串操作  
  6.   
  7. void main (int argc, char *argv[])  
  8. {  
  9.     char instring[40];  
  10.     char outstring[40];  
  11.     int intarg;  
  12.   
  13.     strcpy_s(instring,"Testing...");  
  14.     intarg = 123;  
  15.     FSUB(&intarg,instring,outstring,strlen(instring),sizeof(outstring));  
  16.     printf("%s\n",outstring);  
  17. }  

像Fortran传递数据时,可以是值传递也可以是地址传递,如果是值传递则应该在Fortran从参数声明中加上Value关键字,如:
[plain]  view plain copy
  1. <span style="font-family:Microsoft YaHei;font-size:18px;">INTEGER, value,INTENT(IN) :: INT_ARG</span>  
如果是地址传递则不需要加这个关键字,因为Fortran默认是地址传递,还有要注意的是向Fortran中传递字符串时应该要加上字符串的长度,从fortran中传出字符串时已改在字符串最后加上CHAR(0)

2)复杂调用:

Fortran代码:

[plain]  view plain copy
  1. <span style="font-size:12px;">function callbackFunction(Array,count,Fun) BIND(c,NAME='callbackFunction')    
  2. use,intrinsic :: ISO_C_BINDING  
  3. implicit none  
  4. !DEC$ IF DEFINED (_DLL)  
  5. !DEC$ ATTRIBUTES DLLEXPORT :: callbackFunction  
  6. !DEC$ END IF  
  7. interface  
  8.     function Fun(num)  
  9.     use,intrinsic :: ISO_C_BINDING  
  10.     implicit none  
  11.     integer(C_INT),value :: num  
  12.     logical :: Fun  
  13.     end function  
  14. end interface  
  15.   integer (C_INT),value :: count  ! 加value表示值传递  
  16.   integer (C_INT),INTENT(IN)::Array(count)  
  17.   integer ::callbackFunction  
  18.   integer :: i  
  19.   logical ::b  
  20.   
  21.   !开始循环计算  
  22.   do i=1,count  
  23.     b=Fun(Array(i))    
  24.     if(b) exit;  !如果为true则跳出循环  
  25.   end do     
  26.     
  27.   callbackFunction=0;      
  28. return    
  29. END</span>  
C/C++代码:

callbackFunctioncallbackFunctioncallbackFunction

[cpp]  view plain copy
  1. <span style="font-size:12px;">bool HelloNumber3(int i)   //回调函数  
  2. {    
  3.     if(i>97)  
  4.     {  
  5.         printf("Hello %d\n",i);    
  6.         return false;  
  7.     }  
  8.     else  
  9.         return true;      
  10. }   
  11. #ifdef __cplusplus  
  12. extern "C"   
  13. #endif  
  14. #ifdef USEDLL  
  15. __declspec(dllimportint callbackFunction(int* Array,int rows,bool(*f)(int));   
  16.   
  17. void main (int argc, char *argv[])  
  18. {  
  19.   
  20.     int ary[4];  
  21.     ary[0]=10;ary[1]=11;ary[2]=12;ary[3]=13;  
  22.     int rtb=callbackFunction(ary,4,HelloNumber);  
  23. } </span>  
在Fortran中使用:
[plain]  view plain copy
  1. <span style="font-size:12px;">BIND(c,NAME='callbackFunction')</span>  
声明,可以将导出函数的名称命名为 ”callbackFunction“,而不像第一种方法要将函数名都写成大写的。

在Fortran中回调函数的声明可以像上面用Interface来声明也可以用下面这一句来代替:

logical,external :: Fun 

但是这样就不能声明回调函数的参数类型了。

C/C++中回调函数的调用约定默认是__cdecl,如果不是请将它改为”__cdecl“这样才不会留下隐患。

如果要向Fortran中传递二维数组,特别要注意的是Fortran中是列先数组,而C/C++中是行先数组


你可能感兴趣的:(编程,exception,Integer,dll,library,fortran)