ccall调用Fortran变量

有关于调用Fortran函数和变量的问题需要请教。官方文档的例子不太全,很多地方需要摸索。

现在已有一个Fortran module simplemodule.f95如下:

module simpleModule

implicit none

integer, parameter:: hparam = 10
integer :: h1 = 1
real :: r(2) = [1.0,2.0]
real, allocatable :: s(:)

contains

function foo(x)
  integer :: foo, x
  foo = x * 2
end function foo

end module simplemodule

编译成动态库:

gfortran simplemodule.f95 -o simplemodule.so -shared -fPIC

使用ccall 调用:

# import integer
a = cglobal((:__simplemodule_MOD_h1, "./simplemodule.so"), Int32)
b = unsafe_load(a)

# import integer array
a = cglobal((:__simplemodule_MOD_r, "./simplemodule.so"), Float32)
# method 1
b = [unsafe_load(a,i) for i in 1:2]
# method 2
b = unsafe_wrap(Array{Float32,1}, a, 2)

都没有问题,但是

# import constant integer error?
a = cglobal((:__simplemodule_MOD_hparam, "./simplemodule.so"), Int32)

则显示找不到该变量。请问Fortran中constant的命名规则是什么?

另外第二个问题,关于allocatable array的调用。如果在Fortran module 中有如下定义的 module variable:

real, allocatable :: s(:)

并且在contains 下添加如下函数:

subroutine init_var
  integer :: i

  if(.not.allocated(s)) then
    allocate(s(10))
  else
    write(*,*) 's has already been allocated!'
  endif

  do i=1,10
    s(i) = i
  enddo

  write(*,*) s

end subroutine init_var

subroutine double_var(x)
  real, intent(inout):: x(:)

  x = x * 2
  write(*,*) x

end subroutine double_var

通过如下Julia调用:

ccall((:__simplemodule_MOD_init_var, "./simplemodule.so"), Cvoid, ())
s = cglobal((:__simplemodule_MOD_s, "./simplemodule.so"), Float32)
ccall((:__simplemodule_MOD_double_var, "./simplemodule.so"), Cvoid,
      (Ptr{Float64},), s)

可以看到s被正确地分配大小并初始化。而且可以由另外的函数对s进行修改。但是,通过Julia进行调用:

b = unsafe_wrap(Array{Float32,1}, s, 10) # This is not working!

给出的值却并不正确。

不吝赐教!

1 个赞

我的理解是cglobal命令是获取库中的全局变量(global variables),而fortran中利用parameter定义的是全局常量。通过nm命令可以查看到simplemodule.so中并未有全局常量hparam的信息,因此无法利用cglobal命令来调用。

1 个赞

有道理。那有没有方法能够获得常量的值呢?即使在有关C的文档里我似乎也没有找到。

另外,关于allocated variable的问题也很困惑,在另外一个相关帖子如何使用ccall调用fortran动态数值结果以及调试方法也有提到,但我并没有完全看懂操作方法。

我在英文区问了同样的问题,得到了一种至少行得通的解决方法:Calling Fortran from Julia

总结来说,底层语言里毕竟还是C的接口支持最好,而像Fortran目前最好的方法都是使用iso_c_binding module来完成和C的对接。而对于allocatable array的支持,不能直接看到值的原因是在纯粹的数值外还有Fortran的array descriptor。

如果各位还有更好的解决方法,望告知!

请问具体如何实现的?我向您在那边回复的那样,将real, allocatable :: s(:)改为real, allocatable, target :: s(:),并根据答案里的步骤调用,但是结果出现signal (11): Segmentation fault

请问你是否在module中添加了 use iso_c_binding?
大概完整的 code:

module simpleModule

use iso_c_binding

implicit none

real, target, allocatable :: s(:)

contains

subroutine init_var
  integer :: i

  if(.not.allocated(s)) then
    allocate(s(10))
  else
    write(*,*) 's has already been allocated!'
  endif

  do i=1,10
    s(i) = i
  enddo

  write(*,*) s

end subroutine init_var

function get_s() bind(c)
  type(c_ptr) :: get_s

  get_s = c_loc(s)
end function get_s

end module simplemodule

编译方法:

gfortran simplemodule.f95 -o simplemodule.so -shared -fPIC

Julia 调用方法:

ccall((:__simplemodule_MOD_init_var, "./simplemodule.so"), Cvoid, ())
ps = ccall((:get_s, "simplemodule.so"), Ptr{Cfloat}, ())
s = unsafe_wrap(Vector{Cfloat}, ps, 10)
1 个赞