Subclassing in a C extension

I’ve been trying to make a subclass written in C of the MySQL/Ruby
class (mysql gem), which is also a C extension. I’m running into some
problems and questions and I can’t seem to find anything explaining
the right things to do.

Here is the source of the beginning of my init function:

void Init_mysqlemb(void)
{
// Debugging class path
// print_ruby_string_array is a function I wrote to print an array
printf(“Class path: “);
print_ruby_string_array(rb_gv_get(”$:”));

// This doesn’t load the mysql extension as it should
//rb_require(“mysql”);

// This file is at
/Library/Ruby/Site/1.8/universal-darwin10.0/require_test.rb;
// it loads just fine.
rb_require(“require_test”);

// This doesn’t work either (I saw somewhere in the docs or one of
the Ruby source files that you can specify the full filename of the
extension binary. I guess the whole path is required.
//rb_require(“mysql_api.bundle”);

//rb_require("/Users/eric/.gem/ruby/1.8/gems/mysql-2.8.1/ext/mysql_api/mysql_api.bundle");
//
Works
//rb_require("/Users/eric/.gem/ruby/1.8/gems/mysql-2.8.1/lib/mysql_api.bundle");
//
Works
//rb_require("/Users/eric/.gem/ruby/1.8/gems/mysql-2.8.1/lib/mysql.rb");
//
Works

// Simplest and seemingly most platform-agnostic way that actually
works
rb_eval_string(“require ‘mysql’”);

//VALUE cMysql = rb_const_get(rb_cObject, rb_intern(“Mysql”));
VALUE cMysql= rb_path2class(“Mysql”);

// Debugging superclass
printf(“Superclass: %s\n”, rb_class2name(cMysql));

cMysqlemb = rb_define_class(“Mysqlemb”, cMysql);


}

My questions:

  1. The original Mysql class is installed as a gem. Should my extension
    use rb_require(“rubygems”)? Or is requiring rubygems the
    responsibility of the scripts that use it?

  2. Why doesn’t rb_require(“mysql”) work? In a script or irb, I am able
    to load it just fine via require; rb_eval_string(“require ‘mysql’”)
    also works in C, but that seems like a hack. I’ve compared my $: path
    in irb and inside the extension, and they are identical.

  • Someone on IRC said maybe rb_require works only with .rb files; but
    then I noticed that the Mysql extension does include a file mysql.rb.
    That file, in turn, requires mysql_api.bundle, which is the actual
    binary (on Mac OS X).
  1. Is there one best way to get the class object for a class specified
    by a C string? I’ve seen both rb_const_get(rb_cObject,
    rb_intern(“Classname”)) and rb_path2class(“Classname”) and wonder if
    they have any practical differences.

Thanks.

Eric C. wrote:

  1. The original Mysql class is installed as a gem. Should my extension
    use rb_require(“rubygems”)? Or is requiring rubygems the
    responsibility of the scripts that use it?

In my opinion, you should not use rb_require(“rubygems”). If your
extension is packaged as a gem, then clearly rubygems will be enabled
already; if your extension is being loaded outside rubygems, then I
think it’s the user’s responsibility to require rubygems.

It annoys me when I build a system where all the libraries are
‘vendorised’ locally, and one of them insists on doing a require
‘rubygems’ which I neither need nor want. But to be fair, this is a
mistake I’ve made myself in the past.

  1. Why doesn’t rb_require(“mysql”) work?

You haven’t shown the exception, but my guess is that rb_require hooks
in at a lower level which bypasses all the fudging that rubygems does
with load_paths.

You could prove this using something like (untested):

rb_require(‘rubygems’);
rb_eval_string(‘gem “mysql”’);
rb_require(‘mysql’);

However that defeats (1).

It might be cleaner to use one of the rb_funcall variants to to
send(:require,“mysql”) instead of rb_eval_string.

  1. Is there one best way to get the class object for a class specified
    by a C string? I’ve seen both rb_const_get(rb_cObject,
    rb_intern(“Classname”)) and rb_path2class(“Classname”) and wonder if
    they have any practical differences.

Looking at the source of rb_path2class (in variable.c), it has built-in
ability to follow namespaces, i.e. rb_path2class(“Foo::Bar”) should
work. Otherwise, you can see it just does rb_const_get_at itself,
starting at rb_cObject.

Eric C. wrote:

My questions:

  1. The original Mysql class is installed as a gem. Should my extension
    use rb_require(“rubygems”)? Or is requiring rubygems the
    responsibility of the scripts that use it?

  2. Why doesn’t rb_require(“mysql”) work? In a script or irb, I am able

Here’s a simple solution: ship a mysqlemb.rb file with your extension,
to be required by programmers. This file will “require ‘mysql’”, then
will “require ‘mysqlemv_ext’” to load the C extension.

On Tue, Jan 5, 2010 at 3:36 AM, Brian C. [email protected]
wrote:

It annoys me when I build a system where all the libraries are
‘vendorised’ locally, and one of them insists on doing a require
‘rubygems’ which I neither need nor want. But to be fair, this is a
mistake I’ve made myself in the past.

That makes sense. It is possible to install mysql (and I would imagine
all or most other gems) directly in a site directory, rather than as
gems, so it doesn’t make sense to require rubygems outright.

rb_eval_string(‘gem “mysql”’);
rb_require(‘mysql’);

However that defeats (1).

It might be cleaner to use one of the rb_funcall variants to to
send(:require,“mysql”) instead of rb_eval_string.

Oh, thanks for that. I would send that to the main object. After
some digging, it appears that that is assigned to the variable
ruby_top_self (which isn’t declared in a header), so I did this:

EXTERN VALUE ruby_top_self;
...
rb_funcall(ruby_top_self, rb_intern("send"), 2,

ID2SYM(rb_intern(“require”)), rb_str_new2(“mysql”));

This also works:

rb_funcall(ruby_top_self, rb_intern("require"), 1, 

rb_str_new2(“mysql”));

Why would one want to use send rather that calling require directly?

  1. Is there one best way to get the class object for a class specified
    by a C string? I’ve seen both rb_const_get(rb_cObject,
    rb_intern(“Classname”)) and rb_path2class(“Classname”) and wonder if
    they have any practical differences.

Looking at the source of rb_path2class (in variable.c), it has built-in
ability to follow namespaces, i.e. rb_path2class(“Foo::Bar”) should
work. Otherwise, you can see it just does rb_const_get_at itself,
starting at rb_cObject.

OK.

Thanks!

Eric C. wrote:

Oh, thanks for that. I would send that to the main object. After
some digging, it appears that that is assigned to the variable
ruby_top_self (which isn’t declared in a header), so I did this:

EXTERN VALUE ruby_top_self;
...
rb_funcall(ruby_top_self, rb_intern("send"), 2,

ID2SYM(rb_intern(“require”)), rb_str_new2(“mysql”));

This also works:

rb_funcall(ruby_top_self, rb_intern("require"), 1, 

rb_str_new2(“mysql”));

Why would one want to use send rather that calling require directly?

Sorry, I meant the latter - using rb_funcall to invoke ‘require’, a bit
like a ‘send’ in ruby, but not actually invoking the ‘send’ method.

I don’t know if there’s a cleaner way to avoid the hidden ruby_top_self.