4

我真的可以在正确的方向上推动这一点。

鉴于此 C 代码:

typedef void cbfunc(void *data);
void set_callback(cbfunc* cb);
//do_stuff calls the callback multiple times with data as argument
void do_stuff(void *data);

这个红宝石代码:

module Lib
    extend FFI::Library
    # ...
    callback :cbfunc, [:pointer], :void
    attach_function :set_callback, [:cbfunc], :void
    attach_function :do_stuff, [:pointer], :void
end

有什么方法可以将 ruby​​ 数组作为回调数据传递,例如:

proc = Proc.new do |data|
    # somehow cast FFI::Pointer to be a ruby array here?
    data.push(5)
end
Lib::set_callback(proc)
Lib::do_stuff(ptr_to_a_ruby_obj_here)

问题是回调将被多次调用,我需要一种轻松构造各种 ruby​​ 对象数组的方法。

也许我只是有点累,但感觉有一种简单的方法可以完成这项工作,而我只是没有看到。

4

2 回答 2

1

在发布这个之后我意识到我可以对 Proc 进行 curry 并将其用作回调。

所以像:

proc = Proc.new do |results, data|
    results.push(5)
end
results = []
callback = proc[results]
Lib::set_callback(callback)
Lib::do_stuff(nil) # Not concerned with this pointer anymore

我只是切换到忽略 void* 数据参数(这是 C 端的要求)。必须有其他一些方法,如果有人想分享,我很想听听它们。

于 2017-01-07T01:30:20.663 回答
1

您可以FFI::Pointer使用该对象的 Ruby 对象创建一个object_id

# @param obj [any] Any value
# @return [::FFI::Pointer] a pointer to the given value
def ruby_to_pointer(obj)
  require 'ffi'
  address = obj.object_id << 1
  ffi_pointer = ::FFI::Pointer.new(:pointer, address)
end

问题是当它在回调中返回到 Ruby 时,从这种指针中获取可用的 Ruby 值。FFI 没有为此提供自然接口,但 Ruby 标准库模块fiddle提供了它的指针类型:

# @param ffi_pointer [FFI::Pointer, #to_i]
# @return [any] Ruby object
def pointer_to_ruby(ffi_pointer)
  require 'fiddle'
  address = ffi_pointer.to_i
  fiddle = ::Fiddle::Pointer.new(address)
  obj = fiddle.to_value
end

这是不安全的操作!在将原始 Ruby 对象的 FFI::Pointer 表示转换回 Ruby 之前,您应该格外小心,不要对原始 Ruby 对象进行垃圾回收。

于 2017-09-25T20:59:27.077 回答