Subclassing in a C extension

Discussion in 'Ruby' started by Eric Christopherson, Jan 5, 2010.

  1. 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.
     
    Eric Christopherson, Jan 5, 2010
    #1
    1. Advertising

  2. Eric Christopherson 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.

    > 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.
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Jan 5, 2010
    #2
    1. Advertising

  3. Eric Christopherson 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.

    --
    Posted via http://www.ruby-forum.com/.
     
    Albert Schlef, Jan 6, 2010
    #3
  4. On Tue, Jan 5, 2010 at 3:36 AM, Brian Candler <> wrote:
    > Eric Christopherson 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.


    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.

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


    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?

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


    OK.

    Thanks!
     
    Eric Christopherson, Jan 6, 2010
    #4
  5. Eric Christopherson 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.
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Jan 6, 2010
    #5
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Anand
    Replies:
    3
    Views:
    898
    Tim Daneliuk
    Nov 8, 2003
  2. Christian Seberino
    Replies:
    3
    Views:
    1,188
    Christian Seberino
    Feb 5, 2004
  3. gregory lielens
    Replies:
    6
    Views:
    603
    Gregory Lielens
    Dec 2, 2004
  4. Steve
    Replies:
    3
    Views:
    26,997
    Steve
    Aug 25, 2006
  5. Nitin
    Replies:
    1
    Views:
    296
Loading...

Share This Page