从C函数返回Ruby的Fiddle :: Pointer

时间:2018-07-27 23:02:51

标签: ruby rubygems ffi fiddle

我目前正在开发高性能的Vector / Matrix Ruby gem C扩展,因为我发现内置的实现很麻烦,对于我个人遇到的大多数情况以及其他领域所欠缺的情况来说,并不是理想的选择。< / p>

我的第一种方法是在Ruby中作为Fiddle::CStructEntity的子类实现,目的是使它们针对互操作进行优化,而无需进行转换(例如传递给本机OpenGL函数)。用C语言实现为数学带来很大的好处,但是在尝试实现次要功能时遇到了障碍。

我希望有一个方法将一个Fiddle::Pointer返回到该结构(基本上是指向Rdata->data的指针。我希望返回一个实际的Fiddle::Pointer对象。返回一个整数地址,打包字符串等等是微不足道的,使用它可以很容易地在Ruby方法中扩展为转换为Fiddle::Pointer,如下所示:

def ptr
  # Assume address is an integer address returned from C
  Fiddle::Pointer.new(self.address, self.size)
end

这种问题向我提出了一个问题,是否有可能从C做到这一点?小提琴不是核心库的一部分,它是标准库的一部分,因此实际上只是扩展本身。

这个问题很简单,可以通过上面展示的几行Ruby代码轻松解决,但是更好奇是否可以通过C扩展名返回Fiddle对象而没有黑客?我无法找到完成此操作的任何示例,而且与涉及Fiddle的文档一样,它总是很基础的,没有太多解释。

1 个答案:

答案 0 :(得分:0)

该解决方案实际上很简单,尽管公认它不像我希望发现的那样优雅或干净。

通过包括Fiddle的标头并针对它进行构建,可能还有更多精心设计的方法,但这并不是真正可行的解决方案,因为我不想将C扩展名限制为仅适用于Ruby 2.0+,并且在Ruby版本低于2.0的情况下,只要省略该方法,完全可以接受。

首先,我包含version.h,它提供了访问权限,定义了宏RUBY_API_VERSION_MAJOR,关于Fiddle是否存在,这是我真正需要知道的全部。

这是一个缩写版本,仅显示如何将Fiddle::Pointer类作为VALUE并创建实例。

#if RUBY_API_VERSION_MAJOR >= 2

    rb_require("fiddle");
    VALUE fiddle = rb_const_get(rb_cObject, rb_intern("Fiddle"));
    rb_cFiddlePointer = rb_const_get(fiddle, rb_intern("Pointer"));

#endif

在此示例中,该类存储在rb_cFiddlePointer中,然后可以使用该类从C创建并返回Fiddle::Pointer对象。

    // Get basic data about the struct
    struct RData *rdata = RDATA(self);
    VALUE *args = xmalloc(sizeof(VALUE) * 2);
    // Set the platform pointer-size address (could just use size_t here...)
    #if SIZEOF_INTPTR_T == 4
        args[0] = LONG2NUM((long) rdata->data);
    #elif SIZEOF_INTPTR_T == 8
        args[0] = LL2NUM((long long) rdata->data);
    #else 
        args[0] = INT2NUM(0);
    #endif
    // Get size of structure
    args[1] = INT2NUM(SIZE_OF_YOUR_STRUCTURE);
    VALUE ptr = rb_class_new_instance(2, args, rb_cFiddlePointer);
    xfree(args);
    return ptr;

将函数链接到实际的Ruby方法之后,可以调用它以获取指向内存中内部结构的大小指针。