如何使用ccall调用fortran动态数值结果以及调试方法


#1

准备windows下使用ccall调用fortran写dll,遇到了些问题,请求大家提供帮助。
fortran使用vs+intel ifortran编译。

第一个问题:如何使用ccall调用fortran中的动态数组
在fortran代码中大量使用了ALLOCATE分配的动态数组。
示例代码如下:

SUBROUTINE Init2( InitM ) bind(C,name="Init2")
!DEC$ ATTRIBUTES DLLEXPORT :: Init2
REAL(SELECTED_REAL_KIND( 6, 30 )) , DIMENSION(:,:), ALLOCATABLE, INTENT(INOUT) :: InitM
 ALLOCATE( InitM(3,3) )
    InitM(1:3,1) = (/ 1.1, 2.1, 3.1 /)
    InitM(1:3,2) = (/ 1.2, 2.2, 3.2 /)
   InitM(1:3,3) = (/ 1.3, 2.3, 3.3 /)
 END SUBROUTINE Init2

如果使用下面这种形式进行调用

ccall(init2,Cvoid,(Ptr(T),),InitM)

InitM需要Julia分配好内存空间,调用动态数组的时候就会和fortran中的分配产生冲突,产生如下错误:
forrtl: severe (151): allocatable array is already allocated

请问各位,有没有什么方法可以获取到fortran中分配的指针,并读取数组?

第二个问题是,如何使用VS attach到Julia进程进行dll调试
使用vs尝试对dll进行调试,但是附加julia进程后,dll调试无法进入断点,没有加载符号,请问如何解决这个问题。

非常感谢!


#2

试了一下也是 无效值、Fortran runtime error: Attempting to allocate already allocated variable 'initm' 和各种段错误。

我总觉得既然都动态内存分配了,看上去也没用 InitM 传进去的值。
就不能没输入直接返回么? (没学过 fortran 可能理解有误)

要不你再看看:调用 C 和 Fortran 代码 · Julia中文文档

我想的是像

julia> t = ccall((:clock, "libc"), Int32, ())
2292761

这样直接返回的。

跟你比较像的是传数组进去的

julia> A = [1.3, -2.7, 4.4, 3.1]
4-element Array{Float64,1}:
  1.3
 -2.7
  4.4
  3.1

julia> ccall(:qsort, Cvoid, (Ptr{Cdouble}, Csize_t, Csize_t, Ptr{Cvoid}),
             A, length(A), sizeof(eltype(A)), mycompare_c)

julia> A
4-element Array{Float64,1}:
 -2.7
  1.3
  3.1
  4.4

他这个是在原数组上进行修改。
也没动态分配。

再就没找到比较相似的例子了。


#3

读取fortran数组的可能解决方法:
思路: fortran中传递的都是指针。在Julia中新建一个空指针,该空指针指向Float32类型的指针。该值被传递给fortran函数,并被修改为函数内数组的指针
实现如下:
···Julia
u = Ref{Ptr{Float32}}
ccall(init2,Cvoid,(Ref{Ptr{Float32}},),u)
a = unsafe_wrap(Array{Float32,2}, u[], (3,3))
···
但是奇怪的是,该程序在atom可以顺利运行,但是在32位1.1 repl下报错,直接退出,在64位1.0.2 repl下可以正常运行,可能存在bug。
还可以采用
···Julia
unsafe_load(Ptr{Float32}(u[])
···
一个一个读,这个函数比较稳定,不会报错


#4

fortran只能传指针,而且在函数中会有很多个动态数组,无法只返回一个


#5

fortran 编 DLL 的时候也调成 32位的,得对应才能正常调,跟和用 C 调一样。


#6

这个已经注意到了,测试的是对应的版本,如果不对应,dll载入就不成功


#7

这里似乎没有必要用 Ref:

u = Ptr{Float32}(C_NULL)
GC.@preserve u begin
    ccall(init2,Cvoid,(Ref{Float32},),u)
    a = unsafe_wrap(Array{Float32,2}, u, (3,3))
end

#8

1.1测试会报错


#9

注意ccall是使用的C的API调用Fortran,函数名称后面要加下划线,例如:gemm -> gemm_


#10

他好像用了 bind(C,name="Init2") 这样mangling就和C一样了。


#11

是的,使用了bind,保证接口是C