我想从Julia调用我的Fortran库中的函数。在这种情况下,我有一个函数eye
,它接受一个Integer,并返回一个二维的整数数组。
使用
将Fortran模块编译为共享库$ gfortran -shared -fPIC -o matrix_routines.so matrix_routines.f90
此后我试图从交互式Julia解释器中调用它(从nm
获得名称):
julia> n=5
5
julia> ccall( (:__matrix_routines_MOD_eye, "/path/to/library/matrix_routines.so"), Array{Int64,2} , (Ptr{Int64},), &n )
然而,这立刻导致朱莉娅对我投了一个段错误:
signal (11): Segmentation fault
__matrix_routines_MOD_eye at /path/to/library/matrix_routines.so (unknown line)
anonymous at no file:0
unknown function (ip: -1137818532)
jl_f_top_eval at /usr/bin/../lib/julia/libjulia.so (unknown line)
eval_user_input at REPL.jl:53
jlcall_eval_user_input_19998 at (unknown line)
jl_apply_generic at /usr/bin/../lib/julia/libjulia.so (unknown line)
anonymous at task.jl:95
jl_handle_stack_switch at /usr/bin/../lib/julia/libjulia.so (unknown line)
julia_trampoline at /usr/bin/../lib/julia/libjulia.so (unknown line)
unknown function (ip: 4199613)
__libc_start_main at /usr/bin/../lib/libc.so.6 (unknown line)
unknown function (ip: 4199667)
unknown function (ip: 0)
zsh: segmentation fault (core dumped) julia
我是否以错误的方式调用该函数?函数的正确名称是什么? (它似乎不只是eye
,因为它也不起作用。)
作为一个额外的问题:Julia对结果数组的内存导向做了什么? Fortran和Julia都是专栏专家,但我想知道如果由于ccall()而Julia可能会认为它应该转换它们吗?
module matrix_routines
implicit none
private
public :: eye
contains
pure function eye(n,offset) result(um) !{{{
integer, intent(in) :: n
integer, intent(in), optional :: offset
integer, dimension(n,n) :: um
integer :: i, l, u, os
um = 0
l = 1
u = n
os = 0
if (present(offset)) then
os = offset
end if
if (abs(os) < n) then
if (os > 0) then
u = n - os
else if (os < 0) then
l = 1 - os
end if
do i=l, u
um(i, i+os) = 1
end do
end if
end function eye !}}}
end module matrix_routines
答案 0 :(得分:1)
您的方法存在一些问题。将数组直接返回到julia是有问题的,因为除非满足特定条件,否则Fortran数组不能与C互操作。当您使数组可互操作时(将bind(C)
添加到您的过程并为数组提供C类型),编译器(gfortran
)会抱怨:
Error: Return type of BIND(C) function 'um' at (1) cannot be an array
为了解决这个问题,我们可以通过伪参数返回数组。您希望将此变为intent(inout)
参数并在julia中构造数组,以避免在Fortran中创建数组时出现任何内存/范围问题。
其次,可选参数存在问题,并且略读Julia文档我不确定它是否支持。请注意,即使Fortran可以使用可选参数调用Fortran而没有明确的接口,并且由于Julia不与.mod
文件进行交互,并且似乎期望采用C方式做事,它可能会赢得&#t; t工作(Fortran 2008 15.3.7 p2.6似乎表示它不受支持)。但是有一些解决方法 - 您可以使用不同数量的参数创建多个Fortran过程,然后使用可选参数调用该过程。
首先,考虑一下这个Fortran模块,该模块以您的示例开头,但是正好用于演示互操作的必要条件:
module matrix_routines
implicit none
private
public :: eye
contains
pure subroutine eye(n,um) bind(C,name="eye") !{{{
use, intrinsic :: iso_c_binding, only: c_int
implicit none
integer(c_int), intent(in) :: n
integer(c_int), intent(inout), dimension(n,n) :: um
integer :: i, j
do j=1,n
do i=1,n
um(i,j) = i+j
end do
end do
end subroutine eye !}}}
end module matrix_routines
请注意,我已将um
移动为inout
伪参数,因为我们未返回值,所以将过程更改为子例程。我还删除了可选参数。我还使用了C interop类型并将C名称绑定到过程。你可以在你的问题中编译它。
在Julia,您现在可以执行以下操作:
julia> n = 2
2
julia> um = zeros(Int32, n, n)
2x2 Array{Int32,2}:
0 0
0 0
julia> ccall((:eye, "matrix_routines.so"), Void, (Ptr{Int32}, Ptr{Array{Int32,2}}), &n, um)
julia> um
2x2 Array{Int32,2}:
2 3
3 4
julia> n = 4
4
julia> um = zeros(Int32, n, n)
4x4 Array{Int32,2}:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
julia> ccall((:eye, "matrix_routines.so"), Void, (Ptr{Int32}, Ptr{Array{Int32,2}}), &n, um)
julia> um
4x4 Array{Int32,2}:
2 3 4 5
3 4 5 6
4 5 6 7
5 6 7 8
请注意,我们可以将函数调用为:eye
,因为我们在Fortran中使用了bind(C,name="eye")
C interop。
最后,如果我们将Fortran示例中的do循环更改为um(i,j) = i*10+j
,我们可以看到数组中没有发生转置:
julia> um
3x3 Array{Int32,2}:
11 12 13
21 22 23
31 32 33
你的段错误的特殊原因可能是许多事情 - 数据类型不匹配,返回类型问题,可选参数问题或实际调用和传递的变量不匹配。