|
#include <foo.h> |
|
#include <ruby.h> |
|
|
|
static struct Foo* get_Foo(VALUE self) { |
|
// Use this macro to get the data structure (cast) pointer value and return |
|
// it as it comes from the Ruby object. There may be a case when this is actually |
|
// not adequately setup (initialized) yet. |
|
struct Foo* object; |
|
Data_Get_Struct(self, struct Foo, object); |
|
return object; |
|
} |
|
|
|
static VALUE rb_foo_allocate(VALUE klass) { |
|
// Create a structure of type Foo inside of the virtual machine. |
|
// Set the default value to zero. |
|
struct Foo* object; |
|
return Data_Make_Struct(klass, struct Foo, rb_gc_mark, free, object); |
|
} |
|
|
|
static VALUE rb_foo_set_bar(VALUE self, VALUE value) { |
|
struct Foo* object = get_Foo(self); |
|
object->bar = NUM2INT(value); |
|
return self; |
|
} |
|
|
|
static VALUE rb_foo_get_bozo(VALUE self) { |
|
return rb_cv_get(self, "@@bozo"); |
|
} |
|
|
|
static VALUE rb_foo_get_bar(VALUE self) { |
|
struct Foo* object = get_Foo(self); |
|
return INT2NUM(object->bar); |
|
} |
|
|
|
static VALUE rb_foo_initialize(VALUE self, VALUE opts) { |
|
struct Foo* object = get_Foo(self); |
|
int value = 42; |
|
|
|
// In order to check the instance of the object we need the ID value |
|
// in the symbol take for Hash. We do this by calling the below function |
|
// and passing this value onward. |
|
VALUE hklass = rb_const_get(rb_cObject, rb_intern("Hash")); |
|
|
|
// Only execute the following bit if the opts parameter is not |
|
// nil and is a Hash. |
|
if (!NIL_P(opts) && rb_obj_is_instance_of(opts, hklass) == Qtrue) { |
|
// No type checking - we're going to assume that the caller is |
|
// being a nice citizen and not trying to mess with us. |
|
VALUE v = rb_hash_aref(opts, rb_str_new2("bar")); |
|
if (!NIL_P(v)) { |
|
value = NUM2INT(v); |
|
} |
|
|
|
// Set the @baz instance variable if we define it in the options |
|
// hash. Since this can be nil we're going to have to take care |
|
// when accessing the variable. |
|
v = rb_hash_aref(opts, rb_str_new2("baz")); |
|
if (!NIL_P(v)) { |
|
rb_iv_set(self, "@baz", v); |
|
} |
|
} |
|
|
|
object->bar = value; |
|
return self; |
|
} |
|
|
|
static VALUE rb_foo_new(VALUE klass, VALUE opts) { |
|
// Manually call the method that we've overriden to allocate |
|
// an instance of the Foo class. Then we'll call Foo#initialize. |
|
VALUE self = rb_foo_allocate(klass); |
|
rb_obj_call_init(self, 1, &opts); |
|
return self; |
|
} |
|
|
|
static VALUE rb_foo_sum(VALUE self) { |
|
struct Foo* object = get_Foo(self); |
|
|
|
// Initialize the value from the C POD object instance. |
|
int sum = object->bar; |
|
|
|
// We need an instance of the Class to call a class method. |
|
VALUE klass = rb_const_get(rb_cObject, rb_intern("Foo")); |
|
|
|
// Go into Ruby for the other values from class and instance |
|
// variables. |
|
sum += NUM2INT(rb_funcall(klass, rb_intern("bozo"), 0, 0)); |
|
|
|
// If the instance variable @baz is set then we'll sum this up |
|
// as well. This can only be set through the options hash passed |
|
// in through initialization. |
|
if (rb_ivar_defined(self, rb_intern("baz")) == Qtrue) { |
|
sum += NUM2INT(rb_iv_get(self, "@baz")); |
|
} |
|
|
|
// Yield to a block if one was supplied, otherwise return |
|
// with the value. |
|
if (rb_block_given_p()) { |
|
return rb_yield(INT2NUM(sum)); |
|
} |
|
|
|
// If we do not have a block just return the sum. |
|
return INT2NUM(sum); |
|
} |
|
|
|
VALUE rb_define_class_Foo(void) { |
|
// See the header file for this definition. |
|
VALUE klass = rb_define_class("Foo", rb_cObject); |
|
rb_define_alloc_func(klass, rb_foo_allocate); |
|
rb_define_singleton_method(klass, "new", rb_foo_new, 1); |
|
rb_define_private_method(klass, "initialize", rb_foo_initialize, 1); |
|
rb_define_method(klass, "bar=", rb_foo_set_bar, 1); |
|
rb_define_method(klass, "bar", rb_foo_get_bar, 0); |
|
rb_define_singleton_method(klass, "bozo", rb_foo_get_bozo, 0); |
|
rb_define_method(klass, "sum", rb_foo_sum, 0); |
|
rb_cv_set(klass, "@@bozo", INT2NUM(1)); |
|
return klass; |
|
} |
|
|