3

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

4

1 回答 1

2

在此期间,我面临着与 C 库的交互(我正在为 libgtop 编写一个包装器),并且我选择使用ffi,它有很好的文档记录(尽管文档有时有点过时),最重要的是它的邮件列表是当你遇到困难时,到处都是帮助你的人。

所以我建议你一个使用 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结构布局,就像写在这里

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 进行了一些研究之后,我发现它已被弃用并被 fiddle 取代,如果你不能使用 FFI,你会考虑使用它而不是 DL。Fiddle 似乎也可用于 ruby​​ 1.9.3 和 1.9.2

于 2013-03-29T10:03:13.527 回答