2012-04-19 33 views
4

如何初始化C結構並將它作爲另一個Ruby對象的參數包裝爲Ruby類?我正在重寫內存,但不知道如何解決它。將C結構包裝爲Ruby

Ruby代碼,我想創建一個Person類的實例,並添加地址變量放進去,這是另一個類:

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

} 

輸出產生意外結果:

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例程都應該定義爲靜態返回一個V ALUE。這應該是Qnil,如果沒有相關的應該返回。還有另外一個問題,與add_address_t常規 - 你是包裝本地定義的結構 - 它應該ALLOC(struct Address);

+0

這就是問題所在。謝謝。我沒有意識到,如果我有本地定義的結構,它的內存會自動釋放。或者我想這就是爲什麼我必須分配它。使用malloc和ALLOC有什麼區別嗎?定義爲靜態並返回VALUE只是一種好的做法,或者它必須如此?它在沒有它的情況下工作... – matejuh 2012-04-20 07:43:55

+1

ALLOC檢查是否有足夠的可用內存,如果沒有,則引發異常。如果內存不足,它也會運行GC。所以你應該自己做,如果你不使用ALLOC。 如果函數未定義爲靜態,則可能存在名稱衝突。 – 2012-04-20 08:38:27

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的FFI文檔