【发布时间】:2018-01-30 03:56:38
【问题描述】:
我从较大的遗留代码库中找到了一个小的辅助子例程,它计算向量的一些统计数据并单独编译它,并试图从 Julia (0.6.2) 调用它以进行练习。
首先,我找到了一些论坛帖子和讨论帖,但我想仔细检查一下,因为该语言发展迅速,有些可能使用过时的语法like this one。此外,我使用的是 Windows,而且我遇到的许多论坛帖子都参考了 .so 文件,据我了解,这些文件适用于 UNIX 系统,而不是 Windows?
我引用的整个 Fortran 代码(称为 meand.f)是:
SUBROUTINE MEANSD (A,N,ABAR,VAR,STD)
IMPLICIT NONE
C COMPUTE THE MEAN AND STANDARD DEVIATION OF A REAL VECTOR.
C PARAMETERS:
C A: REAL VECTOR TO COMPUTE MEAN AND STANDARD DEVIATION OVER.
C N: LENGTH OF A.
C ABAR: COMPUTED MEAN OF A.
C VAR: VARIANCE OF A.
C STD: COMPUTED STANDARD DEVIATION OF A.
C
REAL A(N)
INTEGER I,N
DOUBLE PRECISION APX, ABAR, VAR, STD, SUM, SUMSQ, DN, Z
C
DN=N
APX=0D0
DO 30 I=1,N
APX=APX+A(I)
30 CONTINUE
APX=APX/DN
SUM=0D0
SUMSQ=0D0
DO 40 I=1,N
Z=A(I)-APX
SUM=SUM+Z
SUMSQ=SUMSQ+Z*Z
40 CONTINUE
ABAR=SUM/DN
VAR=(SUMSQ-(SUM*ABAR))/(DN-1)
STD=DSQRT(VAR)
ABAR=ABAR+APX
RETURN
END
我用 gcc 编译了这个
gcc (x86_64-posix-seh-rev1, Built by MinGW-W64 project) 7.2.0
主要关注this question,完成且没有错误:
gfortran -c meansd.f
gfortran -shared -fPIC meansd.o -o meansd.dll
当我使用 Libdl 进行测试时,一切似乎都恢复正常,并且我得到了一个指针
path=joinpath(pwd(),"meansd.dll")
dl=Libdl.dlopen(path)
然后我尝试将我的ccall 函数构建为:
a_ref=Ref{Array{Float64,1}}([5.0,5.0,5.0,5.0,5.0])
i_ref=Ref{Int64}(1)
n_ref=Ref{Int64}(length([5.0,5.0,5.0,5.0,5.0]))
apx_ref=Ref{Float64}(0)
abar_ref=Ref{Float64}(0)
var_ref=Ref{Float64}(0)
std_ref=Ref{Float64}(0)
sum_ref=Ref{Float64}(0)
sumsq_ref=Ref{Float64}(0)
dn_ref=Ref{Float64}(0)
z_ref=Ref{Float64}(0)
ccall((:meansd_,"meansd.dll"),Void,(
Ref{Array{Float64,1}},#A
Ref{Int64}, #I
Ref{Int64}, #N
Ref{Float64}, #APX #I think I have to pass these too?
Ref{Float64}, #ABAR
Ref{Float64}, #VAR
Ref{Float64}, #STD
Ref{Float64}, #SUM
Ref{Float64}, #SUMSQ
Ref{Float64}, #DN
Ref{Float64}, #Z
),a_ref,i_ref,n_ref,apx_ref,
abar_ref,var_ref,std_ref,sum_ref,sumsq_ref,dn_ref,z_ref)
据我所知,这已完成,但 abar_ref 返回为 NaN。然后我也制作了一个 Float/Int 32 版本,它完成但abar_ref 返回为0.0。我认为我应该使用 64 位,因为我使用 64 位 gfortran 编译。
我想我搞砸了如何传递数组,但我尝试了许多我能想到的不同方法,但都没有成功。
对于何时使用 Julia 类型与 ccall 类型(Float32 与 Cint?),我也感到困惑。我已经看到了这两个分散在网络上的例子,但如果我没看错的话,现在幕后有底层的转换语句可以处理这个问题吗?
【问题讨论】:
-
嗨,第一个要检查的问题:在 fortran 中,默认的“REAL”和“INTEGER”类型取决于编译器。在典型的 64 位系统上,整数和实数的常见类型是 32 位(但同样,这是“处理器相关的”)。在您的情况下,可能的类型是 32 位实数、32 位整数,然后是三个 64 位实数。由于调用依赖于 c 接口,您可能需要查找 Fortran 的“iso c 绑定”功能。
-
谢谢,知道这很有趣。关于你提出的观点,我有几个问题。 1. 调用外部库时需要声明中间变量(例如
APX)还是只声明列出的输入/输出? 2. C ISO 标准在 F77 代码方面的工作情况如何? some quick searching makes it seem like this is represented more in the later formats -
我不会太担心 F77 代码的限制:您的示例代码并不完全是 F77。 gcc 7.2 对 C 互操作性有很好的支持。 (因为它是固定形式的,也许你可以考虑缩进它?)
-
这可能是一个有趣的错误,但我承认我不知道代码应该缩进。我从source repo 复制并粘贴了代码,整个项目中的许多文件看起来都是这样,所以我认为这就是 fortran 文件的外观。我是编译语言的新手,我不太了解完整的编译器与语言本身的能力。