【问题标题】:Wrapping C struct to Ruby将 C 结构包装到 Ruby
【发布时间】:2012-04-19 20:26:26
【问题描述】:

如何初始化 C 结构并将其包装为 Ruby 类作为另一个 Ruby 对象的参数?我正在重写内存,但不知道如何修复它。

Ruby 代码,我想创建 Person 类的实例并将 Address 变量添加到其中,这是另一个类:

require_relative 'my_extension'

class Address
  def inspect
    "Replaced #inspect: <Address: town:#{town}>"
  end
end

class Person
    def initialize
        puts "init"
    end

    def print()
        puts "Addr class #{@addr.inspect}"
    end
end

foo1=Person.new
foo1.add_address("London")
foo1.print
foo2=Person.new
foo2.add_address("Paris")
foo1.print
foo2.print
foo1.print

C 代码,扩展 Ruby:

#include <stdio.h>
#include "ruby.h"

struct Address {
    char * town;
};

static VALUE get_addr(VALUE self) {
    return rb_iv_get(self,"@addr");
}

static VALUE wrap_address_get_town(VALUE self) {

    struct Address * address;
    Data_Get_Struct(self, struct Address, address);
    return rb_str_new2(address->town);
}


VALUE foo_class;
VALUE address_wrapper_class;


void free_m(){
    printf("free\n");//just for test
}

void add_address_t(VALUE self,VALUE new_town){
    printf("add_address\n");
    /*init new struct and add value to it*/
    struct Address addr;
    addr.town=StringValuePtr(new_town);

    /*wrap struct*/
    VALUE wrapped_address=Data_Wrap_Struct(address_wrapper_class, 0, free_m,&addr);

    /*set it as instance variable*/
    rb_iv_set(self,"@addr",wrapped_address);
}

static VALUE foo_class_alloc(VALUE self){
    return self;
}


void Init_my_extension(){
    foo_class = rb_define_class("Person", rb_cObject);

    address_wrapper_class = rb_define_class("Address", rb_cObject);

    rb_define_method(address_wrapper_class, "town", wrap_address_get_town, 0);

    rb_define_method(foo_class, "add_address", add_address_t, 1);

}

输出产生意想不到的结果:

init
Addr class Replaced #inspect: <Address: town:London>
init
Addr class Replaced #inspect: <Address: town:Paris> //London expected
Addr class Replaced #inspect: <Address: town:�)> //another problem
Addr class Replaced #inspect: <Address: town:�)>
run
run
free
free

【问题讨论】:

    标签: c ruby


    【解决方案1】:

    FFI gem 可能会提供更好的方式来完成您想做的事情:

    require 'ffi'  
    module AddressModule
       extend FFI::Library
       ffi_lib '<path to your c library>'
    
       class Address < FFI::Struct
          layout :address, :string,
       end
    end
    
    person = AddressModule::Address.new
    person[:address] = "an address"
    

    或其他。在https://github.com/ffi/ffi/wiki查看 FFI 文档

    【讨论】:

      【解决方案2】:

      所有的 C 例程都应该定义为静态返回值。这应该是 Qnil,如果不返回任何相关内容。还有另一个问题,add_address_t 例程 - 您正在包装一个本地定义的结构 - 它应该分配给 ALLOC(struct Address);

      【讨论】:

      • 这就是问题所在。谢谢。我没有意识到如果我有本地定义的结构,它的内存会自动释放。或者我想这就是我必须分配它的原因。使用 malloc 和 ALLOC 有什么区别吗?定义为静态并返回 VALUE 只是一种好的做法,还是必须如此?没有它也能正常工作...
      • ALLOC 检查是否有足够的可用内存,如果没有则引发异常。如果内存不足,它也会运行 GC。所以你应该自己做,如果你不使用ALLOC。如果函数未定义为静态,则可能存在名称冲突。
      猜你喜欢
      • 2011-02-10
      • 1970-01-01
      • 1970-01-01
      • 2019-11-04
      • 1970-01-01
      • 1970-01-01
      • 2012-09-12
      • 2020-07-06
      • 2014-06-26
      相关资源
      最近更新 更多