2012-04-19 3 views
4

C Structを初期化して別のRubyオブジェクトのパラメータとしてRubyクラスとしてラップするには?私は記憶を書き直していますが、それを修正する方法はわかりません。RubyにC構造体をラップする

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コード、ルビーを拡張:

#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); 

} 

出力が予期しない生産

Rubyのコード、私は別のクラスである、それらにアドレス変数をPersonクラスのインスタンスを作成し、追加したいです結果:

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 

答えて

0

すべてのCルーチンは静的ALUE。関連するものが返されない場合、これはQnilでなければなりません。あなたがローカルに定義された構造体をラップしている - - もう一つの問題は、add_address_tルーチンでもあり、あなたがやりたいことを達成するためのより良い方法を提供することができるALLOC(struct Address);

+0

でFFIのドキュメントをチェックしてください。それは問題でした。ありがとう。構造体がローカルに定義されていれば、そのメモリが自動的に解放されることはわかりませんでした。それとも、私はそれを割り当てなければならないと思います。 mallocとALLOCの使用に違いはありますか? staticとして定義され、VALUEを返すのは良い習慣であるか、そうでなければなりませんか?それはそれなしで働いています... – matejuh

+1

ALLOCは十分な空きメモリがあるかどうかチェックし、空きメモリがない場合は例外を発生させます。また、十分なメモリがない場合、GCを実行します。したがって、ALLOCを使用しない場合は、自分で行う必要があります。 関数が静的として定義されていない場合は、名前の衝突が発生する可能性があります。 –

1

FFIの宝石を割り当てる必要があります:

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

関連する問題