将标量和数组(任意维度)从 Fortran 传递到 C
Passing both scalars and arrays (of any dimensions) from Fortran to C
我有以下名为 show_value
的 Fortran 子例程,它调用名为 show_value
的 C 函数:
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
并打印 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
虽然你的第一个案例,带有标量参数,可以与 void*
参数正确匹配,但是当调用的参数是假定等级(type(*), dimension(..) :: variable
代表)时,Fortran 过程不是可与具有相应正式参数 const void *variable
.
的 C 过程互操作
而是需要使用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
是数据的开始,但您还会发现 allocatable/pointer/data 状态、排名、范围、类型。
唉,gfortran 4.9 不支持这个。如果它完全受支持,它将只在最新版本中。
或者,您可以避免使用假定秩假定类型实际参数,而是使用 c_loc
传递参数的 C 地址。不太优雅,但得到更广泛的支持:
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 函数如何知道如何处理参数的问题。
我有以下名为 show_value
的 Fortran 子例程,它调用名为 show_value
的 C 函数:
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
并打印 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
虽然你的第一个案例,带有标量参数,可以与 void*
参数正确匹配,但是当调用的参数是假定等级(type(*), dimension(..) :: variable
代表)时,Fortran 过程不是可与具有相应正式参数 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
是数据的开始,但您还会发现 allocatable/pointer/data 状态、排名、范围、类型。
唉,gfortran 4.9 不支持这个。如果它完全受支持,它将只在最新版本中。
或者,您可以避免使用假定秩假定类型实际参数,而是使用 c_loc
传递参数的 C 地址。不太优雅,但得到更广泛的支持:
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 函数如何知道如何处理参数的问题。