从Julia调用具有数组参数的Fortran子程序

6
我可以编译这个Fortran代码“test.f90”。
subroutine test(g,o)
double precision, intent(in):: g
double precision, intent(out):: o
o=g*g
end subroutine

使用

gfortran -shared -fPIC test.f90 -o test.so

并创建这个用于Julia的包装函数test.jl:

function test(s)
  res=Float64[1]
  ccall((:test_, "./test.so"), Ptr{Float64}, (Ptr{Float64}, Ptr{Float64}), &s,res);
  return res[1]
end

并运行这些命令以获取所需的输出:

julia> include("./test.jl")    
julia> test(3.4)
11.559999999999999

但我想返回一个数组而不是一个标量。我觉得我已经尝试了所有的方法,包括在这个答案中使用iso_c_binding。但是我尝试的所有方法都会向我抛出错误,看起来像这样:

ERROR: MethodError: `convert` has no method matching convert(::Type{Ptr{Array{Int32,2}}}, ::Array{Int32,2})
This may have arisen from a call to the constructor Ptr{Array{Int32,2}}(...),
since type constructors fall back to convert methods.
Closest candidates are:
  call{T}(::Type{T}, ::Any)
  convert{T}(::Type{Ptr{T}}, ::UInt64)
  convert{T}(::Type{Ptr{T}}, ::Int64)
  ...
 [inlined code] from ./deprecated.jl:417
 in unsafe_convert at ./no file:429496729

作为一个例子,我想从julia中调用以下代码:
subroutine arr(array) ! or  arr(n,array)
implicit none
integer*8, intent(inout) :: array(:,:)
!integer*8, intent(in) :: n
!integer*8, intent(out) :: array(n,n)
integer :: i, j

do i=1,size(array,2) !n
    do j=1,size(array,1) !n
        array(i,j)= j+i
    enddo
enddo
end subroutine

直接更改参数似乎在从julia调用时没有什么用处,因此使用注释的变体也是一种选择。

那么我如何从Julia调用包含数组的Fortran子程序呢?


1
你说你尝试过 iso_c_binding,那么你是指实际上使用了 bind(C) 还是只是使用了 iso_c_binding 模块?具体是哪种方式?哪段代码与问题中的错误信息相对应? - Vladimir F Героям слава
integer*8 不符合标准,也从未成为任何 ISO FORTRAN/Fortran 标准的一部分。您可以使用内置模块 ISO_Fortran_env 中的命名常量 INT64ISO_C_binding 中的 C_INT64_T,或者使用 selected_int_kind 以安全可移植的方式使用大整数。 - jlokimlin
在ifort中,一个BIND C函数不能返回一个数组。 - Holmz
@VladimirF,关于您关于互操作性和假定形状数组的说法,我想发表一下评论:当使用F2PY从Fortran代码构建Python封装库时,实际上需要使用假定形状arrays(:,:)才能将数组从Python传递到被调用的子程序中。 - Jonatan Öström
F2py是一个特殊情况,我在谈论从Fortran直接与其他语言进行接口。F2py使用了我所说的内部机制,并将其隐藏起来。虽然可以手动完成,但非常困难。但不,你不对,你可以使用F2PY的显式形状和假定大小数组。遗憾的是,它们中的大多数都是这样的,只需打开NumPY附带的库,它们中的大多数都是FORTRAN 77,并且FORTRAN 77中没有假定形状数组。 - Vladimir F Героям слава
显示剩余8条评论
2个回答

6

在使用 ccall 时,Julia 数组应该作为元素类型的指针一并传入,并且需要额外提供一个描述大小的参数。

你的示例代码 test.f90 应该如下:

subroutine arr(n,array)
implicit none
integer*8, intent(in) :: n
integer*8, intent(out) :: array(n,n)
integer :: i, j

do i=1,size(array,2) !n
    do j=1,size(array,1) !n
        array(i,j)= j+i
    enddo
enddo
end subroutine

和以前一样编译

gfortran -shared -fPIC test.f90 -o test.so

然后在Julia中:

n = 10
X = zeros(Int64,n,n) # 8-byte integers
ccall((:arr_, "./test.so"), Void, (Ptr{Int64}, Ptr{Int64}), &n, X)

这个完美地工作了,谢谢!我认为我最一贯的错误是,我一直试图使用 Ptr{Array{Int64,2}} 作为输出变量说明符和/或在 ccall 中的返回类型,这让我无法运行几乎完全相同的代码。 - Jonatan Öström

2
建议: 在较新的版本中,'&n' 会导致无效的语法错误。使用Ref比Ptr更安全。 你可以尝试: 最初的回答:

建议: 在较新的版本中,'&n'会导致无效的语法错误。使用Ref比Ptr更安全。 您可以尝试:

ccall((:arr_, "./test.so"), Cvoid, (Ref{Int64}, Ref{Int64}), Ref(n), X)

它以哪种方式更安全?那么它是兼容的吗? - Vladimir F Героям слава
看起来在 Julia 1.1.0 中调用 fortran 函数的最佳(唯一?)方法。 - Charlie Chu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接