我试图将数组从C/C++传递到Fortran 2003模块,并将计算值返回到C/C++.我已经能够传递并返回单个值(标量),但是来回获取数组很难.我在标量值上发现了很多线程,并且我已经成功地完成了这些工作.
在我的工作标量函数之后,我已经模拟了基于数组的函数.
我正在使用gcc/gfortran.
这是Fortran模块(ConvertUnitsLib.f03).
module ConvertUnitsLib use :: iso_c_binding ! for C/C++ interop real(c_double), bind(c) :: degF, degC public DegCtoF contains ! ! Convert temperature degrees Celsius Fahrenheit ! real(kind = c_double) function DegCtoF(degC) result(degF) & & bind(c, name = "DegCtoF") real(c_double), intent(in), dimension(:) :: degC real(c_double), dimension(size(degC)) :: degF do i = 1, size(degC) degF(i) = ( degC(i) * 1.8 ) + 32 end do end function DegCtoF ! End of module end module ConvertUnitsLib
和C/C++,(CFort.cpp)
#include#ifdef __cplusplus extern"C" { #endif double DegCtoF(double *[]); #ifdef __cplusplus } #endif /**********************************************************************/ int main(int argc, char *argv[]) { printf("C/C++ and Fortran together!\n"); double DegreesC[2] = {32, 64}; double DegreesF[2]; DegreesF = DegCtoF(&DegreesC); printf("%3.1f [C] = %3.1f [F]\n", DegreesC, DegreesF ); return 0; }
最后但并非最不重要的,Makefile
# C++ directives CC=g++ CFLAGS=-std=c++11 # Fortran directives FC=gfortran FFLAGS=-std=f2003 all: clean $(FC) $(FFLAGS) -c -fcheck=all ConvertUnitsLib.f03 $(CC) $(CFLAGS) -c CFort.cpp $(FC) $(FFLAGS) ConvertUnitsLib.o CFort.o -o convert clean: rm -f *.o rm -f *.mod
francescalus.. 7
根据当前Fortran的规则(Fortran 2008,但在Fortran 2003中引入C互操作性时这是相同的),如果Fortran过程具有假定的形状伪参数(其他限制也适用),则它不能与C互操作.在您的代码中degC
,函数中的伪参数DegCtoF
声明为
real(c_double), intent(in), dimension(:) :: degC
是这样的事情.
所以,在F2003下你不可能有这样一个可互操作的功能.事情变得棘手.
在F2015的拟议草案中(基于ISO TS29113 Fortran与C的进一步互操作性),这样的东西是可互操作的.这个语法(我认为)支持最新版本的gcc,这就是gfortran不拒绝代码的原因.
(TS)然而,使用假定形状参数的这种过程的标准化互操作需要使用ISO_Fortran_binding.h
C侧描述的C描述符,该描述符未在gcc中实现.要进行此类交互,需要直接了解gcc数组描述符.
但你很幸运.在您的情况下,您实际上不需要使用假定的形状伪参数:您可以使用显式形状伪参数,这种互操作是F2003的一部分.您需要做的就是传递数组的大小.
无论哪种方式,可互操作的函数都必须返回标量结果,因此您还需要移动到子程序,如innoSPG的答案中所给出的.
最后,我会提到你的使用
real(c_double), bind(c) :: degF, degC
在模块中.
这些是可互操作的全局变量(通过链接关联).您没有在Fortran代码中引用这些变量:虚拟和函数结果不是这些.
在上面这个简单的例子和另一个答案中,人们很乐意拥有一个子程序
subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF') ... end subroutine
但这可能是描述C描述符使用的好机会ISO_Fortran_binding.h
.但请注意,在短期内gfortran不支持这种方法.
考虑Fortran源代码
subroutine DegCtoF(degC, degF) bind(c,name='DegCtoF') use, intrinsic :: iso_c_binding, only : c_double implicit none real(c_double), intent(in), dimension(:) :: degC real(c_double), intent(out), dimension(*) :: degF degF(1:SIZE(degC)) = degC*1.8+32 end subroutine DegCtoF
(为简单起见,我假设内存管理degF
是在C端完成的 - 自然可以超出假设的大小数组).为了使该子例程具有可互操作性,对应的参数degC
必须是指针CFI_cdesc_t
.
拿C代码(大小魔术数字)
#include "ISO_Fortran_binding.h" #includevoid DegCtoF(CFI_cdesc_t*, double*); int main(int argc, char *argv[]) { printf("C and Fortran together!\n"); CFI_CDESC_T(1) DegreesC_Fdesc; CFI_index_t extent[1] = {2}; CFI_rank_t rank = 1; double DegreesC[2] = {32, 64}; double DegreesF[2]; CFI_establish((CFI_cdesc_t*)&DegreesC_Fdesc, &DegreesC, CFI_attribute_other, CFI_type_double, 2*sizeof(double), rank, extent); DegCtoF((CFI_cdesc_t*)&DegreesC_Fdesc, DegreesF); printf("%3.1f [C] = %3.1f [F]\n", DegreesC[0], DegreesF[0] ); printf("%3.1f [C] = %3.1f [F]\n", DegreesC[1], DegreesF[1] ); return 0; }
这里CFI_establish
建立一个合适的C描述符DegreesC_Fdesc
,它可以对应假设的形状Fortran伪参数.在Fortran子程序中,根本没有问题来评估传入数组的大小.
根据当前Fortran的规则(Fortran 2008,但在Fortran 2003中引入C互操作性时这是相同的),如果Fortran过程具有假定的形状伪参数(其他限制也适用),则它不能与C互操作.在您的代码中degC
,函数中的伪参数DegCtoF
声明为
real(c_double), intent(in), dimension(:) :: degC
是这样的事情.
所以,在F2003下你不可能有这样一个可互操作的功能.事情变得棘手.
在F2015的拟议草案中(基于ISO TS29113 Fortran与C的进一步互操作性),这样的东西是可互操作的.这个语法(我认为)支持最新版本的gcc,这就是gfortran不拒绝代码的原因.
(TS)然而,使用假定形状参数的这种过程的标准化互操作需要使用ISO_Fortran_binding.h
C侧描述的C描述符,该描述符未在gcc中实现.要进行此类交互,需要直接了解gcc数组描述符.
但你很幸运.在您的情况下,您实际上不需要使用假定的形状伪参数:您可以使用显式形状伪参数,这种互操作是F2003的一部分.您需要做的就是传递数组的大小.
无论哪种方式,可互操作的函数都必须返回标量结果,因此您还需要移动到子程序,如innoSPG的答案中所给出的.
最后,我会提到你的使用
real(c_double), bind(c) :: degF, degC
在模块中.
这些是可互操作的全局变量(通过链接关联).您没有在Fortran代码中引用这些变量:虚拟和函数结果不是这些.
在上面这个简单的例子和另一个答案中,人们很乐意拥有一个子程序
subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF') ... end subroutine
但这可能是描述C描述符使用的好机会ISO_Fortran_binding.h
.但请注意,在短期内gfortran不支持这种方法.
考虑Fortran源代码
subroutine DegCtoF(degC, degF) bind(c,name='DegCtoF') use, intrinsic :: iso_c_binding, only : c_double implicit none real(c_double), intent(in), dimension(:) :: degC real(c_double), intent(out), dimension(*) :: degF degF(1:SIZE(degC)) = degC*1.8+32 end subroutine DegCtoF
(为简单起见,我假设内存管理degF
是在C端完成的 - 自然可以超出假设的大小数组).为了使该子例程具有可互操作性,对应的参数degC
必须是指针CFI_cdesc_t
.
拿C代码(大小魔术数字)
#include "ISO_Fortran_binding.h" #includevoid DegCtoF(CFI_cdesc_t*, double*); int main(int argc, char *argv[]) { printf("C and Fortran together!\n"); CFI_CDESC_T(1) DegreesC_Fdesc; CFI_index_t extent[1] = {2}; CFI_rank_t rank = 1; double DegreesC[2] = {32, 64}; double DegreesF[2]; CFI_establish((CFI_cdesc_t*)&DegreesC_Fdesc, &DegreesC, CFI_attribute_other, CFI_type_double, 2*sizeof(double), rank, extent); DegCtoF((CFI_cdesc_t*)&DegreesC_Fdesc, DegreesF); printf("%3.1f [C] = %3.1f [F]\n", DegreesC[0], DegreesF[0] ); printf("%3.1f [C] = %3.1f [F]\n", DegreesC[1], DegreesF[1] ); return 0; }
这里CFI_establish
建立一个合适的C描述符DegreesC_Fdesc
,它可以对应假设的形状Fortran伪参数.在Fortran子程序中,根本没有问题来评估传入数组的大小.
在francescalus确认之前,我打算说,据我所知,这有点旧,互操作性不允许您尝试使用数组.此外,编码时一些好习惯总是至关重要的.例如,implicit none
在fortran中使用强制在使用之前声明所有变量.语言允许时使用命名常量,例如2
你在fortran中使用的数组大小.
下面是您的代码的修改版本,应该执行您想要实现的目标.
// Fortran语言
module ConvertUnitsLib use :: iso_c_binding ! for C/C++ interop !real(c_double), bind(c) :: degF, degC implicit none public DegCtoF contains ! ! Convert temperature degrees Celsius Fahrenheit ! subroutine DegCtoF(degC, degF, n)& bind(c, name = "DegCtoF") integer, intent(in) :: n real(c_double), intent(in), dimension(n) :: degC real(c_double), intent(out), dimension(n) :: degF integer :: i do i = 1, n degF(i) = ( degC(i) * 1.8 ) + 32 end do end subroutine DegCtoF
// C++
#include#ifdef __cplusplus extern"C" { #endif double DegCtoF(double [], double [], const int *); #ifdef __cplusplus } #endif /**********************************************************************/ int main(int argc, char *argv[]) { const int N = 2; printf("C/C++ and Fortran together!\n"); double DegreesC[N] = {32, 64}; double DegreesF[N]; DegCtoF(DegreesC, DegreesF, &N); for(int i = 0; i