【问题标题】:Pass arrays from C/C++ to Fortran and return a calculated array将数组从 C/C++ 传递到 Fortran 并返回计算的数组
【发布时间】:2016-04-06 08:02:46
【问题描述】:

我正在尝试将数组从 C/C++ 传递到 Fortran 2003 模块,并将计算值返回到 C/C++。我已经能够很好地传递和返回单个值(标量),但是来回获取 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

【问题讨论】:

  • 好吧,我不确定 Fortran,但在 C 和 C++ 中,传递数组(实际上是传递第一个元素的地址)会丢失所有大小信息,因此您的 Fortran 例程对数组包含的实际元素数。我希望有两个参数,数组(或指向缓冲区的指针)和元素的数量。
  • 从您的标签中,我假设您使用的是 gfortran/gcc,但您能确认一下吗?传递假定的形状参数,例如 degC 不是 Fortran 2003 C 互操作性的一部分,需要付出更多的努力。目前预计将成为 F2015(ISO TS)一部分的方面不受 gcc 的支持。在没有此类支持的情况下,请使用@PaulMcKenzie 的评论,并使用一个明确的形状数组,其中也传递了大小。
  • @francescalus,是的,我正在使用 gfortran/gcc。当您的意思是更多的努力时,我是否还需要传递数组的大小?我知道当 C 调用 Fortran 函数时,它通过指针传递标量值。我是否也只需要传递大小,然后它(Fortran)可以解决剩下的问题吗?
  • @francescalus,好吧,这是有道理的。我现在正在处理该代码,但如何将其从 Fortran 传递回 C?
  • @francescalus,让标量变量在语言之间移动的唯一方法是将它们声明为 public。

标签: c++ c arrays fortran fortran-iso-c-binding


【解决方案1】:

在 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;
}

【讨论】:

  • 这就是我要找的。刚刚测试了代码,效果很好。我不确定的是使用子程序的安全性。来自 IDL 的子程序允许代码块修改块内的任何内部变量。本质上,子程序是内联执行的。 Fortran 中也一样吗?我很好奇的是,如果我在子例程中说了一个名为 NUM 的变量,那么子例程是否能够在全局意义上修改该变量?
  • 把它变成一个子程序是我忘记提到的一件事。几年前我就是这样做的。感谢@francescalus 在他的回答中提到它。我对 IDL 一无所知。对于内联执行,我不明白你的问题。
【解决方案2】:

根据当前 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 描述符,该描述符 在 gcc 中实现。要进行这样的交互,需要直接了解 gcc 数组描述符。

但你很幸运。在您的情况下,您实际上并不需要使用假定的形状虚拟参数:您可以使用显式形状虚拟参数,并且这种互操作是 F2003 的一部分。您需要做的就是传递数组的大小。

无论哪种方式,一个可互操作的函数都必须返回一个标量结果,因此您还需要移动到一个子例程,如 answer by innoSPG 中给出的那样。

最后,我会提到你的使用

real(c_double), bind(c) :: degF, degC

在模块中。

这些是可互操作的全局变量(通过链接关联)。您不会在 Fortran 代码中引用这些变量:dummy 和函数结果不是这些东西。


在上面这个简单的例子和​​另一个答案中,人们会很高兴有一个像

这样的子例程
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 子例程中,评估传入数组的大小完全没有问题。

【讨论】:

    猜你喜欢
    • 2015-04-18
    • 1970-01-01
    • 2013-04-26
    • 2015-05-30
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 2014-02-07
    • 2021-09-05
    相关资源
    最近更新 更多