Reputation: 406
How to initialize C Struct and wrap it as Ruby class as parameter of another Ruby object? I am rewriting the memory, but don't know, how to fix it.
Ruby code, I want to create instances of Person class and add Address variable into them, which is another class:
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 code, extending 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);
}
Output produces unexpected result:
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
Upvotes: 4
Views: 1778
Reputation: 588
The FFI gem may provide a better way to accomplish what you want to do:
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"
Or whatever. Check out the FFI documentation at https://github.com/ffi/ffi/wiki
Upvotes: 1
Reputation: 1695
All the C routines should be defined as static returning a VALUE. This should be Qnil, if nothing relevant should be returned. There is also another problem, with the add_address_t
routine - you are wrapping a locally defined struct - it should be allocated with ALLOC(struct Address);
Upvotes: 0