Subclassing in a C extension

  • Thread starter Eric Christopherson
  • Start date
E

Eric Christopherson

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).

3. 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.
 
B

Brian Candler

Eric said:
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.
2. 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.
3. 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.
 
A

Albert Schlef

Eric said:
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.
 
E

Eric Christopherson

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.

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.
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.

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?
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!
 
B

Brian Candler

Eric said:
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top