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

Julia 调用 C 和 Fortran 代码

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

ntmax_tUint64floatCfloatFloat32doubleCdoubleFloat64ptrdiff_tCptrdiff_tIntssize_tCssize_tIntsize_tCsize_tUintvoidVoidvoid*Ptr{Void}char* (or char[], e.g. a string)Ptr{Uint8}char* (or char[])Ptr{Ptr{Uint8}}struct T* (T 正确表示一个定义好的 bit 类型)Ptr{T} (在参数列表中使用 &variable_name 调用)struct T (T 正确表示一个定义好的 bit 类型)T (在参数列表中使用 &variable_name 调用)jl_value_t* (任何 Julia 类型)Ptr{Any}对应于字符串参数( char* )的 Julia 类型为 Ptr{Uint8} ,而不是 ASCIIString 。参数中有 char** 类型的 C 函数,在 Julia 中调用时应使用 Ptr{Ptr{Uint8}} 类型。例如,C 函数: int main(int argc, char **argv);在 Julia 中应该这样调用: argv = [ "a.out", "arg1", "arg2" ] ccall(:main, Int32, (Int32, Ptr{Ptr{Uint8}}), length(argv), argv)对于 wchar_t* 参数,Julia 类型为 Ptr{Wchar_t},并且数据可以通过 wstring(s) 方法转换为原始的 Julia 字符串(等同于 utf16(s)或utf32(s) ,这取决于 Cwchar_t 的宽度)。还要注意 ASCII, UTF-8, UTF-16, 和UTF-32 字符串数据在 Julia 内部是以 NUL 结尾的,所以它能够传递到 C 函数中以 NUL 为结尾的数据,而不用再做一个拷贝。通过指针读取数据下列方法是“不安全”的,因为坏指针或类型声明可能会导致意外终止或损坏任意进程内存。指定 Ptr{T} ,常使用 unsafe_ref(ptr, [index]) 方法,将类型为 T 的内容从所引用的内存复制到 Julia 对象中。 index 参数是可选的(默认为 1 ),它是从 1 开始的索引值。此函数类似于 getindex() 和 setindex!() 的行为(如 [] 语法)。返回值是一个被初始化的新对象,它包含被引用内存内容的浅拷贝。被引用的内存可安全释放。如果 T 是 Any 类型,被引用的内存会被认为包含对 Julia 对象 jl_value_t* 的引用,结果为这个对象的引用,且此对象不会被拷贝。需要谨慎确保对象始终对垃圾回收机制可见(指针不重要,重要的是新的引用),来确保内存不会过早释放。注意,如果内存原本不是由 Julia 申请的,新对象将永远不会被 Julia 的垃圾回收机制释放。如果 Ptr 本身就是 jl_value_t* ,可使用 unsafe_pointer_to_objref(ptr) 将其转换回 Julia 对象引用。(可通过调用 pointer_from_objref(v) 将Julia 值 v 转换为 jl_value_t* 指针 Ptr{Void} 。)逆操作(向 Ptr{T} 写数据)可通过 unsafe_store!(ptr, value, [index]) 来实现。目前,仅支持位类型和其它无指针( isbits )不可变类型。现在任何抛出异常的操作,估摸着都是还没实现完呢。来写个帖子上报 bug 吧,就会有人来解决啦。如果所关注的指针是(位类型或不可变)的目标数据数组, pointer_to_array(ptr,dims,[own]) 函数就非常有用啦。如果想要 Julia “控制”底层缓冲区并在返回的 Array 被释放时调用 free(ptr) ,最后一个参数应该为真。如果省略 own 参数或它为假,则调用者需确保缓冲区一直存在,直至所有的读取都结束。Ptr 的算术(比如 +) 和 C 的指针算术不同, 对 Ptr 加一个整数会将指针移动一段距离的 字节 , 而不是元素。这样从指针运算上得到的地址不会依赖指针类型。用指针传递修改值因为 C 不支持多返回值, 所以通常 C 函数会用指针来修改值。 在 ccall 里完成这些需要把值放在适当类型的数组里。当你用 Ptr 传递整个数组时,Julia 会自动传递一个 C 指针到被这个值: width = Cint[0] range = Cfloat[0] ccall(:foo, Void, (Ptr{Cint}, Ptr{Cfloat}), width, range)这被广泛用在了 Julia 的 LAPACK 接口上, 其中整数类型的 info 被以引用的方式传到 LAPACK, 再返回是否成功。垃圾回收机制的安全给 ccall 传递数据时,最好避免使用 pointer() 函数。应当定义一个转换方法,将变量直接传递给 ccall 。ccall 会自动安排,使得在调用返回前,它的所有参数都不会被垃圾回收机制处理。如果 C API 要存储一个由 Julia 分配好的内存的引用,当 ccall 返回后,需要自己设置,使对象对垃圾回收机制保持可见。推荐的方法为,在一个类型为 Array{Any,1} 的全局变量中保存这些值,直到 C 接口通知它已经处理完了。只要构造了指向 Julia 数据的指针,就必须保证原始数据直至指针使用完之前一直存在。Julia 中的许多方法,如 unsafe_ref() 和 bytestring() ,都复制数据而不是控制缓冲区,因此可以安全释放(或修改)原始数据,不会影响到 Julia 。有一个例外需要注意,由于性能的原因, pointer_to_array() 会共享(或控制)底层缓冲区。垃圾回收并不能保证回收的顺序。例如,当 a 包含对 b 的引用,且两者都要被垃圾回收时,不能保证 b 在 a 之后被回收。这需要用其它方式来处理。非常量函数说明(name, library) 函数说明应为常量表达式。可以通过 eval ,将计算结果作为函数名: @eval ccall(($(string("a","b")),"lib"), ...表达式用 string 构造名字,然后将名字代入 ccall 表达式进行计算。注意 eval 仅在顶层运行,因此在表达式之内,不能使用本地变量(除非本地变量的值使用 $ 进行过内插)。 eval 通常用来作为顶层定义,例如,将包含多个相似函数的库封装在一起。间接调用ccall 的第一个参数可以是运行时求值的表达式。此时,表达式的值应为 Ptr 类型,指向要调用的原生函数的地址。这个特性用于 ccall的第一参数包含对非常量(本地变量或函数参数)的引用时。调用方式ccall 的第二个(可选)参数指定调用方式(在返回值之前)。如果没指定,将会使用操作系统的默认 C 调用方式。其它支持的调用方式为: stdcall , cdecl , fastcall 和 thiscall 。例如 (来自 base/libc.jl): hn = Array(Uint8, 256) err=ccall(:gethostname, stdcall, Int32, (Ptr{Uint8}, Uint32), hn, length(hn))更多信息请参考 LLVM Language Reference访问全局变量当全局变量导出到本地库时可以使用 cglobal 方法,通过名称进行访问。cglobal的参数和 ccall 的指定参数是相同的符号,并且其表述了存储在变量中的值类型: julia> cglobal((:errno,:libc), Int32) Ptr{Int32} @0x00007f418d0816b8该结果是一个该值的地址的指针。可以通过这个指针对这个值进行操作,但

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


Julia 调用 C 和 Fortran 代码