Fortran-C 互操作性和浮点数组
Fortran-C interoperability and float arrays
我有大量现有的 Fortran95 代码。它使用
real(dp), dimension(num) :: array
声明数组。
我想加入一些 C 代码,发现我可以通过编写 C 函数的接口并将数组声明为
来做到这一点
use iso_c_binding
real(c_double), allocatable, target :: array(:)
我有工作的 fortran 函数调用 C 函数
call myfunction(c_loc(array));
将 real(dp)
数组传递给 myfunction 需要什么?显然,我需要从中创建一个 C 指针(怎么做?)。除了复制数组还有其他方法吗?是否可以确保两种类型确实引用兼容的双精度数据块?最重要的是,该解决方案必须与 GNU 编译器一起工作。请注意,将现有 Fortran 代码中的 real(dp)
替换为 real(c_double)
现在对我来说不是一个选项。
如果除了复制整个数组别无选择,我该如何在界面中正确执行此操作?
首先,我假设您将 dp 定义为某处模块中的参数。你可以简单地使用
integer, parameter :: dp = c_double
在该模块中(并且在某处有 if (dp /= c_double) stop "Bletchful sytem"
。
在 C 和 Fortran 之间传递一个数组是这样的:
module foo
use iso_c_binding
private
public :: bar
interface
subroutine bar(a,n) bind(C)
import
real(kind=c_double), dimension(*), intent(inout) :: a
integer(c_size_t), value, intent(in) :: n
end subroutine bar
end interface
end module foo
你的 C 函数将是
void bar(double *a, size_t n)
编辑:
从 Fortran 调用 C 函数的方法是
program main
use iso_c_binding
use foo
real(c_double), dimension(10) :: a
call bar(a,size(a,kind=c_size_t))
print *,a
end program main
编辑 2:
如果你真的想每次都拷贝入/拷贝出,你可以这样做
subroutine bar2(array)
real(kind=c_double), intent(inout), dimension(:) :: array
real(kind=c_double), dimension(size(array)) :: a
a = array ! Copy in
call bar(a,size(a,kind=c_size_t))
array = a ! Copy out
end subroutine bar2
end module foo
但我不明白为什么这是必要的。
编辑 3:
如果您担心 C 和 Fortran 数据类型不匹配,您可以编写一个通用包装器来解决这个问题。这就是它的样子:
module foo
use iso_c_binding
implicit none
private
public :: bar
interface
subroutine bar_double(a,n) bind(C)
import
real(kind=c_double), dimension(*), intent(inout) :: a
integer(c_size_t), value, intent(in) :: n
end subroutine bar_double
end interface
interface
subroutine bar_float(a,n) bind(C)
import
real(kind=c_float), dimension(*), intent(inout) :: a
integer(c_size_t), value, intent(in) :: n
end subroutine bar_float
end interface
interface bar
module procedure bar_aux_double, bar_aux_float
end interface bar
contains
subroutine bar_aux_double (a)
real(kind=c_double), dimension(:), intent(inout) :: a
call bar_double (a, size(a,kind=c_size_t))
end subroutine bar_aux_double
subroutine bar_aux_float (a)
real(kind=c_float), dimension(:), intent(inout) :: a
call bar_float (a, size(a,kind=c_size_t))
end subroutine bar_aux_float
end module foo
你的主程序看起来像
program main
use foo
integer, parameter :: dp = selected_real_kind(15)
integer, parameter :: sp = selected_real_kind(6)
real(dp), dimension(10) :: a_dp
real(sp), dimension(10) :: a_sp
call bar(a_dp)
call bar(a_sp)
print *,a_dp,a_sp
end program main
您根本没有提及 iso_c_binding。如果没有dp或sp的包装函数,编译会因为缺少通用过程而失败。
如果您使用模块,在 Fortran 中混合 dp
和 c_double
时不要太担心。在极不可能的情况下,selected_real_kind(15, 307) /= c_double
编译器会在检查过程接口时报错。否则,它会发现类型数字是一致的,并且它不关心您如何调用类型常量(声明可互操作过程时除外)。
我有大量现有的 Fortran95 代码。它使用
real(dp), dimension(num) :: array
声明数组。
我想加入一些 C 代码,发现我可以通过编写 C 函数的接口并将数组声明为
来做到这一点use iso_c_binding
real(c_double), allocatable, target :: array(:)
我有工作的 fortran 函数调用 C 函数
call myfunction(c_loc(array));
将 real(dp)
数组传递给 myfunction 需要什么?显然,我需要从中创建一个 C 指针(怎么做?)。除了复制数组还有其他方法吗?是否可以确保两种类型确实引用兼容的双精度数据块?最重要的是,该解决方案必须与 GNU 编译器一起工作。请注意,将现有 Fortran 代码中的 real(dp)
替换为 real(c_double)
现在对我来说不是一个选项。
如果除了复制整个数组别无选择,我该如何在界面中正确执行此操作?
首先,我假设您将 dp 定义为某处模块中的参数。你可以简单地使用
integer, parameter :: dp = c_double
在该模块中(并且在某处有 if (dp /= c_double) stop "Bletchful sytem"
。
在 C 和 Fortran 之间传递一个数组是这样的:
module foo
use iso_c_binding
private
public :: bar
interface
subroutine bar(a,n) bind(C)
import
real(kind=c_double), dimension(*), intent(inout) :: a
integer(c_size_t), value, intent(in) :: n
end subroutine bar
end interface
end module foo
你的 C 函数将是
void bar(double *a, size_t n)
编辑:
从 Fortran 调用 C 函数的方法是
program main
use iso_c_binding
use foo
real(c_double), dimension(10) :: a
call bar(a,size(a,kind=c_size_t))
print *,a
end program main
编辑 2:
如果你真的想每次都拷贝入/拷贝出,你可以这样做
subroutine bar2(array)
real(kind=c_double), intent(inout), dimension(:) :: array
real(kind=c_double), dimension(size(array)) :: a
a = array ! Copy in
call bar(a,size(a,kind=c_size_t))
array = a ! Copy out
end subroutine bar2
end module foo
但我不明白为什么这是必要的。
编辑 3:
如果您担心 C 和 Fortran 数据类型不匹配,您可以编写一个通用包装器来解决这个问题。这就是它的样子:
module foo
use iso_c_binding
implicit none
private
public :: bar
interface
subroutine bar_double(a,n) bind(C)
import
real(kind=c_double), dimension(*), intent(inout) :: a
integer(c_size_t), value, intent(in) :: n
end subroutine bar_double
end interface
interface
subroutine bar_float(a,n) bind(C)
import
real(kind=c_float), dimension(*), intent(inout) :: a
integer(c_size_t), value, intent(in) :: n
end subroutine bar_float
end interface
interface bar
module procedure bar_aux_double, bar_aux_float
end interface bar
contains
subroutine bar_aux_double (a)
real(kind=c_double), dimension(:), intent(inout) :: a
call bar_double (a, size(a,kind=c_size_t))
end subroutine bar_aux_double
subroutine bar_aux_float (a)
real(kind=c_float), dimension(:), intent(inout) :: a
call bar_float (a, size(a,kind=c_size_t))
end subroutine bar_aux_float
end module foo
你的主程序看起来像
program main
use foo
integer, parameter :: dp = selected_real_kind(15)
integer, parameter :: sp = selected_real_kind(6)
real(dp), dimension(10) :: a_dp
real(sp), dimension(10) :: a_sp
call bar(a_dp)
call bar(a_sp)
print *,a_dp,a_sp
end program main
您根本没有提及 iso_c_binding。如果没有dp或sp的包装函数,编译会因为缺少通用过程而失败。
如果您使用模块,在 Fortran 中混合 dp
和 c_double
时不要太担心。在极不可能的情况下,selected_real_kind(15, 307) /= c_double
编译器会在检查过程接口时报错。否则,它会发现类型数字是一致的,并且它不关心您如何调用类型常量(声明可互操作过程时除外)。