Question

From my Ruby C Extension I wanted to create a new Geom::Vector3d instance from the Google SketchUp Ruby API: https://developers.google.com/sketchup/docs/ourdoc/vector3d

My initial code was this:

static inline VALUE
vector_to_sketchup( Point3d vector )
{
  VALUE skp_vector, args[3];

  args[0] = rb_float_new( vector.x );
  args[1] = rb_float_new( vector.y );
  args[2] = rb_float_new( vector.z );

  skp_vector = rb_class_new_instance( 3, args, cVector3d );
}

This however raised an error:

Error: #<ArgumentError: wrong type - expected Sketchup::Vector3d>

Instead I had to call the ruby new method - like so:

static inline VALUE
vector_to_sketchup( Point3d vector )
{
  VALUE skp_vector;

  skp_vector = rb_funcall( cVector3d, sNew, 3,
    rb_float_new( vector.x ),
    rb_float_new( vector.y ),
    rb_float_new( vector.z )
  );

  return skp_vector;
}

I ran into the same problem with Geom::Point3d and Sketchup::Color.

rb_class_new_instance is the preferred way to create a new instance in Ruby C, right? Anyone got any idea why I needed to call new? Some oddity with how the class was defined in SketchUp?

Was it helpful?

Solution

After communicating with the developers of Google SketchUp I have found the cause of this.

SketchUp makes use of Data_Wrap_Struct to link their C classes with the Ruby classes. But they use an old method of allocating the data - which is within the #new method.

In Ruby 1.8 you use rb_define_alloc_func() to do the allocation and you never mess around with #new. Ruby (1.6 and 1.8) defines #new to call rb_class_new_instance().

Since I used rb_class_new_instance() on SketchUp's old style classes the objects where not created properly, the allocation function was bypassed and never triggered. The error I got came from SketchUp, not Ruby.

So the answer is, you can use rb_class_new_instance() to create new instances of classes provided they haven't overloaded the #new method to do any initializations. Before Ruby 1.6 this was common for Ruby C classes if they needed to allocate data, but as of 1.8 it should be done with rb_define_alloc_func(). (Matz says so here: http://www.justskins.com/forums/the-new-allocation-scheme-132572.html#post437362 )

You can see the differences between Ruby 1.6 style and 1.8 style in this post: http://www.justskins.com/forums/the-new-allocation-scheme-132572.html#post444948

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top