(以下操作均以Mac OS为例)。
例一:Fortran function(有返回值)
module foo
implicit none
contains
function bar(x)
integer :: bar, x
bar = x * 2
end function bar
end module foo
编译命令:
gfortran -shared -dynamiclib -fPIC -O2 f.f90 -o f1.dylib
用objdump -t f1.dylib
命令看一看符号表:
f1.dylib: file format mach-o-x86-64
SYMBOL TABLE:
0000000000003fc0 g 0f SECT 01 0000 [.text] ___foo_MOD_bar
0000000000000000 g 01 UND 00 0100 dyld_stub_binder
可以看到我们定义的函数对应的符号为___foo_MOD_bar
,但需要注意的是,在Julia中调用时,应当使用__foo_MOD_bar
,也即最前面是两个下划线而非三个。
对应的Julia代码:
num = 4
result = ccall((:__foo_MOD_bar, "f1"), Int32, (Ref{Int32}, ), num)
println(result)
输出为8
。注意这里ccall()
函数的调用形式。第一个参数为需要调用的Fortran函数,这里通过(symbol, dylib)
结构体的形式指定,第二个参数为返回参数的类型,第三个参数是一个由输入参数的类型组成的Tuple
,最后是提供需要的输入参数。
注意这里输入参数应当全部使用
Ref
或Ptr
(如果为数组)类型。
例二:Fortran subroutine(无返回值)
module foo
implicit none
contains
subroutine bar(x)
integer :: x
x = x * 2
end
end module foo
我们把这段代码编译为f2.dylib
。
对应的Julia代码:
num = 4
num_ref = Ref{Int32}(num)
ccall((:__foo_MOD_bar, "f2"), Cvoid, (Ref{Int32}, ), num_ref)
println(num_ref[])
输出为8
。
注意这里函数的返回类型为Cvoid
,因为没有返回值。运行函数之后,结果保存在我们提供的Base.RefValue{Int32}
中。因此如果我们直接打印num_ref
,得到的输出将会是Base.RefValue{Int32}(8)
。
例三:输入和输出包含数组的情形
Fortran代码:
module foo
implicit none
contains
subroutine bar(n, arr)
integer :: i, n, arr(n)
do i = 1, n
arr(i) = (-1)**i
end do
end
end module foo
数组类型的参数,在Julia中需要以Ptr{}
的指针形式传入。
Julia代码:
len = 10
nums = Array{Int32}(undef, len)
ccall((:__foo_MOD_bar, "f3"), Cvoid, (Ref{Int32}, Ptr{Int32}, ), len, nums)
println(nums)
输出为Int32[-1, 1, -1, 1, -1, 1, -1, 1, -1, 1]
。
例四:Fortran函数中包含全局变量的情形
Fortran代码:
module foo
implicit none
integer :: a = 0
contains
subroutine bar(n)
integer :: n
a = a + n
end
end module foo
我们还是用objdump
来看看符号表:
f4.dylib: file format mach-o-x86-64
SYMBOL TABLE:
0000000000004000 g 0f SECT 03 0000 [__DATA.__pu_bss2] ___foo_MOD_a
0000000000003fb0 g 0f SECT 01 0000 [.text] ___foo_MOD_bar
0000000000000000 g 01 UND 00 0200 dyld_stub_binder
可以看到这里多出来一个___foo_MOD_a
的符号,显然它就对应于我们在foo
这个模块中定义的全局变量a
。同样,在Julia中调用时,前缀需要改为两个下划线。
在Julia中,我们通过cglobal()
函数获取到全局变量的指针,使用unsafe_load(ptr, [index])
函数来读取,使用unsafe_store!(ptr, value, [index])
函数来写入。注意unsafe_load
和unsafe_store!
中的index
参数都是可选的,默认值为1
。
Julia代码:
num = 10
a = cglobal((:__foo_MOD_a, "f4"), Int32)
println(unsafe_load(a))
ccall((:__foo_MOD_bar, "f4"), Cvoid, (Ref{Int32}, ), num)
println(unsafe_load(a))
例五:综合
Fortran代码:
module foo
implicit none
integer :: arr(10) = 0
contains
subroutine bar(n)
integer :: i, n
do i = 1,10
arr(i) = arr(i) * n
end do
end
end module foo
Julia代码:
num = 10
arr = cglobal((:__foo_MOD_arr, "f5"), Int32)
for i in 1:10
unsafe_store!(arr, i, i)
end
ccall((:__foo_MOD_bar, "f5"), Cvoid, (Ref{Int32}, ), num)
for i in 1:10
print(unsafe_load(arr, i), " ")
end
最后的运行结果为(%表示行末):
10 20 30 40 50 60 70 80 90 100 %
我是吴子寒,欢迎关注我的GitHub账号@lucifer1004,以及我的个人站点CP-Wiki。