从Julia调用Fortran函数,返回一个数组:未知函数,segfault?

时间:2015-02-06 19:54:50

标签: fortran interop shared-libraries julia

我想从Julia调用我的Fortran库中的函数。在这种情况下,我有一个函数eye,它接受​​一个I​​nteger,并返回一个二维的整数数组。

使用

将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

1 个答案:

答案 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

你的段错误的特殊原因可能是许多事情 - 数据类型不匹配,返回类型问题,可选参数问题或实际调用和传递的变量不匹配。