Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Ruby (http://www.velocityreviews.com/forums/f66-ruby.html)
-   -   Subclassing in a C extension (http://www.velocityreviews.com/forums/t861154-subclassing-in-a-c-extension.html)

Eric Christopherson 01-05-2010 05:06 AM

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

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.


Brian Candler 01-05-2010 09:36 AM

Re: Subclassing in a C extension
 
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/.


Albert Schlef 01-06-2010 04:57 AM

Re: Subclassing in a C extension
 
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/.


Eric Christopherson 01-06-2010 05:07 AM

Re: Subclassing in a C extension
 
On Tue, Jan 5, 2010 at 3:36 AM, Brian Candler <b.candler@pobox.com> 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!


Brian Candler 01-06-2010 08:24 AM

Re: Subclassing in a C extension
 
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/.



All times are GMT. The time now is 08:25 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.