首页 > 解决方案 > 从 C 函数返回 Ruby 的 Fiddle::Pointer

问题描述

我目前正在开发一个高性能的 Vector/Matrix Ruby gem C 扩展,因为我发现内置实现很麻烦,并且对于我个人遇到的大多数情况并不理想,并且在其他领域也缺乏。

我的第一种方法是在 Ruby 中作为Fiddle::CStructEntity. 在 C 中实现为数学提供了很大的好处,但是在尝试实现一个次要函数时遇到了障碍。

我希望有一个方法将 a 返回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 中做到这一点吗?Fiddle 不是核心库的一部分,它是标准库的一部分,因此,它实际上只是一个扩展本身。

这个问题是微不足道的,可以通过上面演示的几行 Ruby 代码轻松解决,但如果Fiddle没有 hack 甚至可以从 C 扩展返回一个对象,那就更好奇了?我找不到任何这样做的例子,并且一如既往地涉及涉及 Fiddle 的文档,它非常基本,并没有太多解释。

标签: rubyrubygemsffifiddle

解决方案


这个解决方案实际上相当简单,尽管我承认它没有我希望发现的优雅或干净的解决方案。

可能有更复杂的方法来解决这个问题,包括头文件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,然后可用于Fiddle::Pointer从 C 创建和返回对象。

    // 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 方法后,您可以调用它来获取指向内存中内部结构的大小指针。


推荐阅读