Skip to content

Instantly share code, notes, and snippets.

@matejuh
Forked from mattwildig/embed.c
Created January 20, 2012 09:33
Show Gist options
  • Save matejuh/1646430 to your computer and use it in GitHub Desktop.
Save matejuh/1646430 to your computer and use it in GitHub Desktop.
Wrapping struct
/*
N.B. this program doesn't include any error checking. In particular you'll have
to do all the rb_protect stuff
*/
#include <stdio.h>
#include "ruby.h"
struct Address {
char * town;
};
/*
Implementation of getter method for town
*/
static VALUE wrap_address_get_town(VALUE self) {
/*
Data_Get_Struct is the reverse of Data_Wrap_Struct
It unpacks the struct associated with self, casts it to struct Address
and assigns it to address.
Note it's a macro, not a function.
*/
struct Address * address;
Data_Get_Struct(self, struct Address, address);
/*
rb_str_new2 converts a C char* into a Ruby String
*/
return rb_str_new2(address->town);
}
/*
Implementation of setter method for town
*/
static VALUE wrap_address_set_town(VALUE self, VALUE new_town) {
struct Address * address;
Data_Get_Struct(self, struct Address, address);
/*
StringValuePtr gets the char* associated with a Ruby String
*/
address->town = StringValuePtr(new_town);
}
int main(int argc, char **argv) {
//setup
ruby_init();
ruby_init_loadpath();
ruby_script("embed");
/*
Define wrapper class for the struct
As an alternative to creating an anonymous class with rb_class_new, we could create
a named class with rb_define_class:
VALUE address_wrapper_class = rb_define_class("Address", rb_cObject);
if we did this, the class would be available in Ruby as the class "Address"
*/
VALUE address_wrapper_class = rb_class_new(rb_cObject);
/*
Add #town and #town= methods to the new class
Here we associate the two implementation functions defined above with the
wrapper class, so that in ruby it will respond to the methods #town and #town=
*/
rb_define_method(address_wrapper_class, "town", wrap_address_get_town, 0); //getter method: #town
rb_define_method(address_wrapper_class, "town=", wrap_address_set_town, 1);//setter method: #town=
/*
Create instance of the C struct
*/
struct Address adr;
adr.town = "London";
/*
Wrap the C struct to create Ruby object
Here is where we use Data_Wrap_Struct. The result is a Ruby object which is an instance
of the class we have just defined, so it will respond to the methods #town and #town=
Note that in Ruby the class understands the #town and #town= methods because of the
rb_define_method calls on the class, _not_ because the C struct has similarly named
members.
*/
VALUE wrapped_address = Data_Wrap_Struct(address_wrapper_class, NULL, NULL, &adr);
/*
Create instance of our receiving Ruby class
*/
rb_require("./ruby_code");
VALUE test_object = rb_class_new_instance(0, NULL, rb_const_get(rb_cObject, rb_intern("TestKlass")));
/*
Pass wrapped struct to print_info method on the instance of TestKlass
*/
rb_funcall(test_object, rb_intern("print_info"), 1, wrapped_address);
/*
Demonstrate #town= method works
*/
printf("Back in C, \"address.town\" is now %s\n", adr.town);
}
# If you use rb_define_class("Address", rb_cObject)
# instead of rb_class_new then this would reopen that
# class and redefine the inspect method.
class Address
def inspect
"Replaced #inspect: <Address: town:#{town}>"
end
end
class TestKlass
def print_info(struct)
puts "In Ruby"
puts struct.inspect
puts struct.town
struct.town = "Tokyo"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment