我正在尝试在Linux / Unix系统上动态调用外部库的函数。
我在dl
库中取得了一些成功,但只有在使用原始C类型且参数按值传递时才会成功:
require 'dl/import'
module LibM
extend DL::Importer
dlload 'libm.so'
extern 'double sin(double)'
end
puts LibM.sin(3.14159265358979323846 / 2) # 1.0
然而,如何使用更复杂的类型(如C结构)或者如果参数是存储调用结果的指针的接口函数?
module LibX11
extend DL::Importer
dlload 'libX11.so.6'
extern 'Display *XkbOpenDisplay (char *display_name, int *event_rtrn, int *error_rtrn, int *major_in_out, int *minor_in_out, int *reason_rtrn)'
end
Display
是一个大结构,event_rtrn
存储了一些结果等。
我已经看了DL::CStructBuilder它看起来可以完成这项工作,但由于文档非常简短,没有找到工作示例,我在这里丢失了如何正确使用它。
我必须添加标准的Ruby 1.9模块(如果可能),因为禁止在目标机器上安装其他宝石。
答案 0 :(得分:2)
我正面临着在此期间与C库接口(我正在撰写wrapper for libgtop),我选择使用ffi,quite well documented(尽管文档有时是有点过时了)最重要的是它的邮件列表里到处都是那些在遇到麻烦时帮助你的人。
所以我建议你使用ffi的解决方案:
require 'ffi'
module LibX11
extend FFI::Library
ffi_lib 'libX11.so.6'
# Display *XkbOpenDisplay (char *display_name, int *event_rtrn, int *error_rtrn, int *major_in_out, int *minor_in_out, int *reason_rtrn)
attach_function :XkbOpenDisplay, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :pointer
end
然后你必须描述Display
结构布局,比如写here:
class Display < FFI::Struct
layout :value, :double,
:other_value, :char,
...
end
然后你做这样的事情:
p1 = FFI::MemoryPointer.new(:char)
p2 = FFI::MemoryPointer.new(:int)
p3 = FFI::MemoryPointer.new(:int)
p4 = FFI::MemoryPointer.new(:int)
p5 = FFI::MemoryPointer.new(:int)
p6 = FFI::MemoryPointer.new(:int)
# write to pointer if needed, otherwise it is a null pointer
# p1.write_char('a')
# p2.write_int(1)
# ...
struct_pointer = LibX11.XkbOpenDisplay(p1, p2, p3, p4, p5, p6)
# read the struct
struct = Display.new(display_struct_pointer)
p Hash[ s.members.map { |m| [ m, s[m] ] } ]
我没有测试代码,但它应该大致正确。如果不是,请现在让我。
在对ruby 2.0上的DL进行了一些研究之后,我发现它已被弃用并替换为小提琴,如果你不能使用FFI,你会考虑使用它而不是DL。小提琴似乎也适用于ruby 1.9.3和1.9.2