当前位置:K88软件开发文章中心编程语言非主流编程语言Julia → 文章内容

Julia 调用 C 和 Fortran 代码

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 16:28:27

由 陈 创建, 最后一次修改 2016-08-12 调用 C 和 Fortran 代码Julia 调用 C 和 Fortran 的函数,既简单又高效。被调用的代码应该是共享库的格式。大多数 C 和 Fortran 库都已经被编译为共享库。如果自己使用 GCC (或 Clang )编译代码,需要添加 -shared 和 -fPIC 选项。Julia 调用这些库的开销与本地 C 语言相同。调用共享库和函数时使用多元组形式: (:function, "library") 或 ("function", "library") ,其中 function 是 C 的导出函数名, library 是共享库名。共享库依据名字来解析,路径由环境变量来确定,有时需要直接指明。多元组内有时仅有函数名(仅 :function 或 "function" )。此时,函数名由当前进程解析。这种形式可以用来调用 C 库函数, Julia 运行时函数,及链接到 Julia 的应用中的函数。使用 ccall 来生成库函数调用。 ccall 的参数如下:(:function, "library") 多元组对儿(必须为常量,详见下面)返回类型,可以为任意的位类型,包括 Int32 , Int64 , Float64 ,或者指向任意类型参数 T 的指针 Ptr{T} ,或者仅仅是指向无类型指针 void* 的 Ptr输入的类型的多元组,与上述的返回类型的要求类似。输入必须是多元组,而不是值为多元组的变量或表达式后面的参数,如果有的话,都是被调用函数的实参下例调用标准 C 库中的 clock : julia> t = ccall( (:clock, "libc"), Int32, ()) 2292761 julia> t 2292761 julia> typeof(ans) Int32clock 函数没有参数,返回 Int32 类型。输入的类型如果只有一个,常写成一元多元组,在后面跟一逗号。例如要调用 getenv 函数取得指向一个环境变量的指针,应这样调用: julia> path = ccall( (:getenv, "libc"), Ptr{Uint8}, (Ptr{Uint8},), "SHELL") Ptr{Uint8} @0x00007fff5fbffc45 julia> bytestring(path) "/bin/bash"注意,类型多元组的参数必须写成 (Ptr{Uint8},) ,而不是 (Ptr{Uint8}) 。这是因为 (Ptr{Uint8}) 等价于 Ptr{Uint8} ,它并不是一个包含 Ptr{Uint8} 的一元多元组: julia> (Ptr{Uint8}) Ptr{Uint8} julia> (Ptr{Uint8},) (Ptr{Uint8},)实际中要提供可复用代码时,通常要使用 Julia 的函数来封装 ccall ,设置参数,然后检查 C 或 Fortran 函数中可能出现的任何错误,将其作为异常传递给 Julia 的函数调用者。下例中, getenv C 库函数被封装在 env.jl 里的 Julia 函数中: function getenv(var::String) val = ccall( (:getenv, "libc"), Ptr{Uint8}, (Ptr{Uint8},), var) if val == C_NULL error("getenv: undefined variable: ", var) end bytestring(val) end上例中,如果函数调用者试图读取一个不存在的环境变量,封装将抛出异常: julia> getenv("SHELL") "/bin/bash" julia> getenv("FOOBAR") getenv: undefined variable: FOOBAR下例稍复杂些,显示本地机器的主机名: function gethostname() hostname = Array(Uint8, 128) ccall( (:gethostname, "libc"), Int32, (Ptr{Uint8}, Uint), hostname, length(hostname)) return bytestring(convert(Ptr{Uint8}, hostname)) end此例先分配出一个字节数组,然后调用 C 库函数 gethostname 向数组中填充主机名,取得指向主机名缓冲区的指针,在默认其为空结尾 C 字符串的前提下,将其转换为 Julia 字符串。 C 库函数一般都用这种方式从函数调用者那儿,将申请的内存传递给被调用者,然后填充。在 Julia 中分配内存,通常都需要通过构建非初始化数组,然后将指向数据的指针传递给 C 函数。调用 Fortran 函数时,所有的输入都必须通过引用来传递。& 前缀说明传递的是指向标量参数的指针,而不是标量值本身。下例使用 BLAS 函数计算点积: function compute_dot(DX::Vector{Float64}, DY::Vector{Float64}) assert(length(DX) == length(DY)) n = length(DX) incx = incy = 1 product = ccall( (:ddot_, "libLAPACK"), Float64, (Ptr{Int32}, Ptr{Float64}, Ptr{Int32}, Ptr{Float64}, Ptr{Int32}), &n, DX, &incx, DY, &incy) return product end前缀 & 的意思与 C 中的不同。对引用的变量的任何更改,都是对 Julia 不可见的。 & 并不是真正的地址运算符,可以在任何语法中使用它,例如 &0 和 &f(x) 。注意在处理过程中,C 的头文件可以放在任何地方。目前还不能将 Julia 的结构和其他非基础类型传递给 C 库。通过传递指针来生成、使用非透明结构类型的 C 函数,可以向 Julia 返回 Ptr{Void} 类型的值,这个值以 Ptr{Void} 的形式被其它 C 函数调用。可以像任何 C 程序一样,通过调用库中对应的程序,对对象进行内存分配和释放。把 C 类型映射到 JuliaJulia 自动调用 convert 函数,将参数转换为指定类型。例如: ccall( (:foo, "libfoo"), Void, (Int32, Float64), x, y)会按如下操作: ccall( (:foo, "libfoo"), Void, (Int32, Float64), convert(Int32, x), convert(Float64, y))如果标量值与 & 一起被传递作为 Ptr{T} 类型的参数时,值首先会被转换为 T 类型。数组转换把数组作为一个 Ptr{T} 参数传递给 C 时,它不进行转换。Julia 仅检查元素类型是否为 T ,然后传递首元素的地址。这样做可以避免不必要的复制整个数组。因此,如果 Array 中的数据格式不对时,要使用显式转换,如 int32(a) 。如果想把数组 不经转换 而作为一个不同类型的指针传递时,要么声明参数为 Ptr{Void} 类型,要么显式调用 convert(Ptr{T}, pointer(A)) 。类型相关基础的 C/C++ 类型和 Julia 类型对照如下。每个 C 类型也有一个对应名称的 Julia 类型,不过冠以了前缀 C 。这有助于编写简便的代码(但 C 中的 int 与 Julia 中的 Int 不同)。与系统无关:unsigned charCucharUint8shortCshortInt16unsigned shortCushortUint16intCintInt32unsigned intCuintUint32long longClonglongInt64unsigned long longCulonglongUint64intmax_tCintmax_tInt64uintmax_tCui

[1] [2] [3]  下一页


Julia 调用 C 和 Fortran 代码