您的位置:首页 > 编程语言 > C语言/C++

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

2012-10-16 11:44 190 查看
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
plaincopy

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++代码:

[cpp] view
plaincopy

#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\n",outstring);

}

像Fortran传递数据时,可以是值传递也可以是地址传递,如果是值传递则应该在Fortran从参数声明中加上Value关键字,如:

[plain] view
plaincopy

<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
plaincopy

<span style="font-size:12px;">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</span>

C/C++代码:

callbackFunctioncallbackFunctioncallbackFunction

[cpp] view
plaincopy

<span style="font-size:12px;">bool HelloNumber3(int i) //回调函数

{

if(i>97)

{

printf("Hello %d\n",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);

} </span>

在Fortran中使用:

[plain] view
plaincopy

<span style="font-size:12px;">BIND(c,NAME='callbackFunction')</span>

声明,可以将导出函数的名称命名为”callbackFunction“,而不像第一种方法要将函数名都写成大写的。

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

但是这样就不能声明回调函数的参数类型了。
C/C++中回调函数的调用约定默认是__cdecl,如果不是请将它改为”__cdecl“这样才不会留下隐患。
如果要向Fortran中传递二维数组,特别要注意的是Fortran中是列先数组,而C/C++中是行先数组。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: