前一篇文章讨论了C++程序对Fortran动态链接库中的函数调用,其数据交换主要在Fortran程序之间,而没有出现在两种语言之间。本文进一步探讨C++与Fortran动态链接库之间的数据交换。
数据交换有多种方式,例如通过函数的参数传递、指针、全局变量等等。C++与Fortran这两种语言之间一般变量(变量或数组等)的数据传递比较容易,但自定义数据类型的数据交换则略显困难,主要是因为两种语言对于自定义数据类型的内存管理方式存在差异。示例如下:
一、Fortran90 程序
MODULE EXAMP_MOD
USE, INTRINSIC :: ISO_C_BINDING
IMPLICIT NONE
!两个自定义类型
TYPE,BIND(C) :: EXAMP1_TYPE
REAL(C_FLOAT) :: A
INTEGER(C_INT) :: I1
INTEGER(C_INT) :: I2
END TYPE EXAMP1_TYPE
TYPE,BIND(C) :: EXAMP2_TYPE
REAL(C_FLOAT) :: AA
INTEGER(C_INT) :: II1, II2
END TYPE EXAMP2_TYPE
!函数接口可以不要
!interface
!
!subroutine FSUB(EXAMP,COMMON_EXAMP,INT_ARG, STR_IN, STR_OUT) bind(C,name='FSUB')
!use examp_mod
!implicit none
!type(EXAMP1_TYPE) :: EXAMP
!type(EXAMP2_TYPE) :: COMMON_EXAMP
!INTEGER :: INT_ARG
!CHARACTER,dimension(*):: STR_IN
!CHARACTER,dimension(*) :: STR_OUT
!end subroutine FSUB
!
!end interface
END MODULE EXAMP_MOD
在Fortran中,通过module定义了两个自定义数据类型,将这两个类型定义的变量通过函数参数,实现了C++与Fortran之间的传递。
!通过参数传递这两个自定义类型的变量
SUBROUTINE FSUB(EXAMP,COMMON_EXAMP,INT_ARG, STR_IN, STR_OUT) !BIND(C,NAME='FSUB')
!如果绑定,反而对字符参数传递有影响,因为fortran当中是字符串,而C++是字符数组
!DEC$ ATTRIBUTES DLLEXPORT::FSUB
USE EXAMP_MOD
IMPLICIT NONE
TYPE(EXAMP1_TYPE) :: EXAMP !
!DEC$ ATTRIBUTES DLLEXPORT :: vEXAMP!
TYPE(EXAMP2_TYPE) :: COMMON_EXAMP !
!DEC$ ATTRIBUTES DLLEXPORT :: vCOMMON_EXAMP!
INTEGER, INTENT(IN) :: INT_ARG !如果绑定应该使用INTEGER(C_INT)
CHARACTER(*), INTENT(IN) :: STR_IN !如果绑定应该使用CHARACTER(C_CHAR)
CHARACTER(*), INTENT(OUT) :: STR_OUT !如果绑定应该使用CHARACTER(C_CHAR)
CHARACTER*5 INT_STR
WRITE (INT_STR,'(I5.5)')INT_ARG !将整型数转换成字符串
EXAMP%A = 12.
EXAMP%I1 = 22
EXAMP%I2 = 33
COMMON_EXAMP%AA = 13.
COMMON_EXAMP%II1 = 24
COMMON_EXAMP%II2 = 34
STR_OUT = STR_IN // INT_STR // CHAR(0) !合并字符串,如果绑定此处STR_OUT = STR_IN
RETURN
END subroutine FSUB
二、C++程序
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <Windows.h>
// 与Fortran对应的两个自定义类型
typedef struct EXAMP1_TYPE{
float A;
int I1;
int I2;
}EXAMP;
typedef struct EXAMP2_TYPE{
float AA;
int II1;
int II2;
}COMMON_EXAMP;
extern "C" void _cdecl FSUB (EXAMP *vEXAMP1, //也可使用&vEXAMP1
COMMON_EXAMP *vCOMMON_EXAMP1, //也可使用&vCOMMON_EXAMP1
int *INT_ARG, //也可使用&INT_ARG
char *STR_IN,
char *STR_OUT,
size_t STR_IN_LEN,
size_t STR_OUT_LEN);
在C++中,首先也要定义两个与Fortran对应的数据类型,同时申明外部函数,需要注意函数声明中变量的写法。
void main (int argc, char *argv[])
{
// declaration
char instring[40];
char outstring[40];
int intarg;
EXAMP vEXAMP;
COMMON_EXAMP vCOMMON_EXAMP;
// initializing
strcpy_s(instring,"Testing...");
intarg = 123;
vEXAMP.A = 0.;
vEXAMP.I1 = 0;
vEXAMP.I2 = 0;
vCOMMON_EXAMP.AA = 0.;
vCOMMON_EXAMP.II1 = 0;
vCOMMON_EXAMP.II2 = 0;
/* Call Fortran routine - pass intarg by reference,
pass length of outstring explicitly */
FSUB(&vEXAMP,&vCOMMON_EXAMP,&intarg,instring,outstring,strlen(instring),sizeof(outstring));
//若外部函数声明中使用了取地址符&,此处则应使用EXAMP,COMMON_EXAMP,intarg
printf(" %f %i %i %f %i %i\n", vEXAMP.A, vEXAMP.I1, vEXAMP.I2, vCOMMON_EXAMP.AA, vCOMMON_EXAMP.II1, vCOMMON_EXAMP.II2);
vEXAMP.A = 0.;
vEXAMP.I1 = 0;
vEXAMP.I2 = 0;
vCOMMON_EXAMP.AA = 0.;
vCOMMON_EXAMP.II1 = 0;
vCOMMON_EXAMP.II2 = 0;
printf(" %f %i %i %f %i %i\n", vEXAMP.A, vEXAMP.I1, vEXAMP.I2, vCOMMON_EXAMP.AA, vCOMMON_EXAMP.II1, vCOMMON_EXAMP.II2);
printf("%s\n",outstring);
system("pause");
}
通过地址或值传递都可以实现两种语言之间的数据交换。程序运行结果如下: