首页 > 解决方案 > 将标量和数组(任何维度)从 Fortran 传递到 C

问题描述

我有以下名为的 Fortran 子例程show_value,它调用名为的 C 函数show_value

INTERFACE
    SUBROUTINE show_value(variable) BIND(C, name = "show_value")
        USE, INTRINSIC :: iso_c_binding
        TYPE(*) :: variable
    END SUBROUTINE
END INTERFACE

C函数show_value

void show_value(const void *variable)
{
    printf("%d\n", *(int *) variable);
}

Fortran 子例程在将标量传递给它时运行良好。例子:

INTEGER :: x
x = 12
call show_value(x)

这将调用 C 函数show_value和 print 12,这是正确的。

现在,根据 Fortran 文档,如果要启用子程序show_value来接收数组(任何维度的)而不仅仅是标量,则该行TYPE(*) :: variable应更改为TYPE(*), DIMENSION(..) :: variable.

进行此更改后,在执行以下 Fortran 代码时:

INTEGER, DIMENSION(3) :: y
y(1) = 15
y(2) = 17
y(3) = 19
call show_value(y)

C 函数show_value不再打印正确的消息(即打印随机数)。此外,我发现 C 函数接收到的地址比原始地址低 528(在 Fortran 中)。要确认这一点:

void show_value(const void *variable)
{
    printf("%d\n", *(int *) (variable + 528));
}

...打印15(正确的数字)。

知道这里发生了什么吗?

环境:Ubuntu 14.04 64 位,gfortran 4.9

标签: cfortrangfortranfortran-iso-c-binding

解决方案


尽管您的第一种情况(带有标量参数)可以与参数正确匹配void*,但当调用的参数是假定等级(type(*), dimension(..) :: variable表示)时,Fortran 过程不能与具有相应形式参数的 C 过程互操作const void *variable

相反,有必要使用该CFI_cdesc_t机制:

#include <stdio.h>
#include <ISO_Fortran_binding.h>

void show_value(const CFI_cdesc_t* variable)
{
  printf("%d\n", *(int*) variable->base_addr);
}

您可以在 Fortran 2018 18.5.3 中找到详细信息。

然而,本质上,这是一个包含 Fortran 实体的大部分细节的描述符。这里base_addr是数据的开始,但您还会发现可分配/指针/数据状态、等级、范围、类型。

唉,gfortran 4.9 不支持这个。如果它完全受支持,它只会出现在最近的版本中。


或者,您可以避免使用假定等级的假定类型实际参数,而是使用c_loc. 不太优雅,但得到更广泛的支持:

use, intrinsic :: iso_c_binding, only : c_loc, c_ptr, c_int
interface
  subroutine show_value(variable) bind(c)
    import c_ptr
    type(c_ptr), value :: variable
  end subroutine
end interface

integer(c_int), target :: x, y(3)

x = 12
y = [15, 17, 19]

call show_value(c_loc(x))
call show_value(c_loc(y))

end

然而,这留下了 C 函数如何知道如何处理参数的问题。


推荐阅读