将数组从 C/C++ 传递给 Fortran 和 return 一个计算数组
Pass arrays from C/C++ to Fortran and return a calculated array
我正在尝试将数组从 C/C++ 传递到 Fortran 2003 模块并将计算值返回到 C/C++。我已经能够很好地传递 return 单个值(标量),但事实证明来回获取 array 很困难。我发现了很多关于标量值的线索,并且我已经成功地完成了这些工作。
我已经根据我的工作标量函数为基于数组的函数建模。
我正在使用 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 <stdio.h>
#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 确认之前,我要说的是,据我所知,这有点陈旧,互操作性不允许您尝试对数组执行的操作。
此外,一些好习惯在编码时总是至关重要的。例如,在 fortran 中使用 implicit none
强制声明所有变量,然后再使用它们。在语言允许的情况下使用命名常量,例如您在 fortran 中用作数组大小的 2
。
下面是您的代码的修改版本,应该可以实现您想要实现的目标。
//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 <stdio.h>
#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<N; i++){
printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i] );
}
return 0;
}
根据当前 Fortran 的规则(Fortran 2008,但这与在 Fortran 2003 中引入 C 互操作性时相同),如果 Fortran 过程具有假定形状的伪参数(其他限制),则它不能与 C 互操作也适用)。在您的代码 degC
中,函数 DegCtoF
中的伪参数声明为
real(c_double), intent(in), dimension(:) :: degC
就是这么回事
所以,在F2003下你不能有这样的互操作功能。这就是事情变得棘手的地方。
在 F2015 的拟议草案中(基于 ISO TS29113 Further Interoperability of Fortran with C),这样的事情 是 可互操作的。而且这种语法(我认为)得到了最新版本的 gcc 的支持,这就是为什么代码没有被 gfortran 拒绝的原因。
(TS) 然而,与具有假定形状参数的此类过程的标准化互操作需要在 C 端使用 ISO_Fortran_binding.h
中描述的 C 描述符,即 not 在 gcc 中实现。要进行此类交互,需要直接了解 gcc 数组描述符。
但你很幸运。在您的情况下,您实际上不需要使用假定的形状虚拟参数:您可以使用显式形状虚拟参数,这种互操作是 F2003 的一部分。您需要做的就是传递数组的大小。
无论哪种方式,可互操作的函数必须 return 标量结果,因此您还需要移至子例程,如 .
中给出的
最后,我要提一下你对
的使用
real(c_double), bind(c) :: degF, degC
在模块中。
这些是可互操作的全局变量(通过链接关联)。您不会在 Fortran 代码中引用这些变量:虚拟变量和函数结果不是这些东西。
在上面这个简单的例子和另一个答案中,人们会很高兴有一个像
这样的子程序
subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF')
...
end subroutine
但这也许是描述 ISO_Fortran_binding.h
中 C 描述符的使用的好机会。但请注意,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"
#include <stdio.h>
void 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 子例程中,评估传入数组的大小完全没有问题。
我正在尝试将数组从 C/C++ 传递到 Fortran 2003 模块并将计算值返回到 C/C++。我已经能够很好地传递 return 单个值(标量),但事实证明来回获取 array 很困难。我发现了很多关于标量值的线索,并且我已经成功地完成了这些工作。
我已经根据我的工作标量函数为基于数组的函数建模。
我正在使用 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 <stdio.h>
#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 确认之前,我要说的是,据我所知,这有点陈旧,互操作性不允许您尝试对数组执行的操作。
此外,一些好习惯在编码时总是至关重要的。例如,在 fortran 中使用 implicit none
强制声明所有变量,然后再使用它们。在语言允许的情况下使用命名常量,例如您在 fortran 中用作数组大小的 2
。
下面是您的代码的修改版本,应该可以实现您想要实现的目标。
//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 <stdio.h>
#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<N; i++){
printf("%d : %3.1f [C] = %3.1f [F]\n", i, DegreesC[i], DegreesF[i] );
}
return 0;
}
根据当前 Fortran 的规则(Fortran 2008,但这与在 Fortran 2003 中引入 C 互操作性时相同),如果 Fortran 过程具有假定形状的伪参数(其他限制),则它不能与 C 互操作也适用)。在您的代码 degC
中,函数 DegCtoF
中的伪参数声明为
real(c_double), intent(in), dimension(:) :: degC
就是这么回事
所以,在F2003下你不能有这样的互操作功能。这就是事情变得棘手的地方。
在 F2015 的拟议草案中(基于 ISO TS29113 Further Interoperability of Fortran with C),这样的事情 是 可互操作的。而且这种语法(我认为)得到了最新版本的 gcc 的支持,这就是为什么代码没有被 gfortran 拒绝的原因。
(TS) 然而,与具有假定形状参数的此类过程的标准化互操作需要在 C 端使用 ISO_Fortran_binding.h
中描述的 C 描述符,即 not 在 gcc 中实现。要进行此类交互,需要直接了解 gcc 数组描述符。
但你很幸运。在您的情况下,您实际上不需要使用假定的形状虚拟参数:您可以使用显式形状虚拟参数,这种互操作是 F2003 的一部分。您需要做的就是传递数组的大小。
无论哪种方式,可互操作的函数必须 return 标量结果,因此您还需要移至子例程,如
最后,我要提一下你对
的使用real(c_double), bind(c) :: degF, degC
在模块中。
这些是可互操作的全局变量(通过链接关联)。您不会在 Fortran 代码中引用这些变量:虚拟变量和函数结果不是这些东西。
在上面这个简单的例子和另一个答案中,人们会很高兴有一个像
这样的子程序subroutine DegCtoF(n, degC, degF) bind(c,name='DegCtoF')
...
end subroutine
但这也许是描述 ISO_Fortran_binding.h
中 C 描述符的使用的好机会。但请注意,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"
#include <stdio.h>
void 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 子例程中,评估传入数组的大小完全没有问题。