如何界面更复杂的共享库?

时间:2013-03-29 09:36:34

标签: ruby ctypes ffi

我正在尝试在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模块(如果可能),因为禁止在目标机器上安装其他宝石。

1 个答案:

答案 0 :(得分:2)

我正面临着在此期间与C库接口(我正在撰写wrapper for libgtop),我选择使用ffiquite 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