Converting class_for to a C extension

Hi all,

I found this snippet posted to handle doing const_get for nested
classes/modules:

def class_for(class_name)
   names = class_name.split("::")
   result = Object
   names.each { |n|
      result = result.const_get(n)
   }
   result
rescue NameError
   nil
end

I'm trying to convert this to a C extension, but I'm having some
trouble:

static VALUE class_for(VALUE klass_name){
   VALUE v_names, v_result;
   ID id_split = rb_intern("split");
   int i;

   v_names = rb_funcall(klass_name, id_split, 1, rb_str_new2("::"));
   v_result = rb_cObject;

   for(i = 0; i < RARRAY(v_names)->len; i++)
      v_result = rb_const_get(RBASIC(v_result)->klass, RARRAY(v_names)-

ptr[i]);

   return v_result;
}

With this code I get "uninitialized constant Class::(null)
(NameError)".

It appears to be a problem with the first argument to rb_const_get().
I'm not sure what that value should be, but I also tried
TYPE(v_result) - which caused a segfault - and just v_result - which
resulted in a similar "uninitialized constant (null) (NameError)"
error.

What am I doing wrong here?

Thanks,

Dan

PS - Yes, I realize I'm not doing error handling in the C extension -
I'm not worried about that for now.

Naturally I saw my mistake 2 minutes later:

It should be:

for(i = 0; i < RARRAY(v_names)->len; i++){
   v_result = rb_const_get(
      v_result,
      rb_intern(StringValuePtr(RARRAY(v_names)->ptr[i]))
   );
}

Regards,

Dan

···

On Feb 13, 3:26 pm, "Daniel Berger" <djber...@gmail.com> wrote:

Hi all,

I found this snippet posted to handle doing const_get for nested
classes/modules:

def class_for(class_name)
   names = class_name.split("::")
   result = Object
   names.each { |n|
      result = result.const_get(n)
   }
   result
rescue NameError
   nil
end

I'm trying to convert this to a C extension, but I'm having some
trouble:

static VALUE class_for(VALUE klass_name){
   VALUE v_names, v_result;
   ID id_split = rb_intern("split");
   int i;

   v_names = rb_funcall(klass_name, id_split, 1, rb_str_new2("::"));
   v_result = rb_cObject;

   for(i = 0; i < RARRAY(v_names)->len; i++)
      v_result = rb_const_get(RBASIC(v_result)->klass, RARRAY(v_names)-

>ptr[i]);

   return v_result;

}

With this code I get "uninitialized constant Class::(null)
(NameError)".

It appears to be a problem with the first argument to rb_const_get().
I'm not sure what that value should be, but I also tried
TYPE(v_result) - which caused a segfault - and just v_result - which
resulted in a similar "uninitialized constant (null) (NameError)"
error.

What am I doing wrong here?

Daniel Berger wrote:

Hi all,

I found this snippet posted to handle doing const_get for nested
classes/modules:

def class_for(class_name)
   names = class_name.split("::")
   result = Object
   names.each { |n|
      result = result.const_get(n)
   }
   result
rescue NameError
   nil
end

I'm trying to convert this to a C extension, but I'm having some
trouble:

static VALUE class_for(VALUE klass_name){
   VALUE v_names, v_result;
   ID id_split = rb_intern("split");
   int i;

   v_names = rb_funcall(klass_name, id_split, 1, rb_str_new2("::"));
   v_result = rb_cObject;

   for(i = 0; i < RARRAY(v_names)->len; i++)
      v_result = rb_const_get(RBASIC(v_result)->klass, RARRAY(v_names)-

ptr[i]);

   return v_result;
}

With this code I get "uninitialized constant Class::(null)
(NameError)".

It appears to be a problem with the first argument to rb_const_get().
I'm not sure what that value should be, but I also tried
TYPE(v_result) - which caused a segfault - and just v_result - which
resulted in a similar "uninitialized constant (null) (NameError)"
error.

What am I doing wrong here?

Thanks,

Dan

PS - Yes, I realize I'm not doing error handling in the C extension -
I'm not worried about that for now.

  You should :wink: the (null) stuff is a sign that you're using a NULL
pointer somewhere, though I can't get where. The segfault is another
indication. So one thing here is returning false or NULL...

  The problem, in my opinion, is here:

RBASIC(v_result)->klass

  Why do you need to use ->klass ?? All objects you're getting *are*
classes or modules already. You end up looking for constants in the
Class class, and that is not where they are defined.

  Cheers,

  Vince

···

--
Vincent Fourmond, PhD student (not for long anymore)
http://vincent.fourmond.neuf.fr/

<snip>

I know this doesn't directly answer the question you asked, but why
not just use rb_path2class()?

···

On 2/13/07, Daniel Berger <djberg96@gmail.com> wrote:

I found this snippet posted to handle doing const_get for nested
classes/modules:

*SLAPS FOREHEAD*

Thanks Lyle.

Dan

···

On Feb 13, 3:40 pm, "Lyle Johnson" <lyle.john...@gmail.com> wrote:

On 2/13/07, Daniel Berger <djber...@gmail.com> wrote:

> I found this snippet posted to handle doing const_get for nested
> classes/modules:

<snip>

I know this doesn't directly answer the question you asked, but why
not just use rb_path2class()?

One of these days I'm going to write a patch to expose this to Ruby :stuck_out_tongue:

···

On Feb 13, 2007, at 14:40, Lyle Johnson wrote:

On 2/13/07, Daniel Berger <djberg96@gmail.com> wrote:

I found this snippet posted to handle doing const_get for nested
classes/modules:

<snip>

I know this doesn't directly answer the question you asked, but why
not just use rb_path2class()?

Expose what to Ruby?

···

On 2/14/07, Eric Hodel <drbrain@segment7.net> wrote:

One of these days I'm going to write a patch to expose this to Ruby :stuck_out_tongue:

I think he just means that there's no direct equivalent in plain Ruby
(right?).

I think just patching const_get to handle "::" would suffice.

Dan

···

On Feb 14, 12:50 pm, "Lyle Johnson" <lyle.john...@gmail.com> wrote:

On 2/14/07, Eric Hodel <drbr...@segment7.net> wrote:

> One of these days I'm going to write a patch to expose this to Ruby :stuck_out_tongue:

Expose what to Ruby?

*SLAPS FOREHEAD*

Oh, yeah. :wink:

···

On 2/14/07, Daniel Berger <djberg96@gmail.com> wrote:

I think he just means that there's no direct equivalent in plain Ruby
(right?).

One of these days I'm going to write a patch to expose this to Ruby :stuck_out_tongue:

Expose what to Ruby?

I think he just means that there's no direct equivalent in plain Ruby
(right?).

Yup. I've reimplemented rb_path2class() numerous times in ruby, and there's a perfectly good C method lying around that needs only a one-line patch to be accessible.

I think just patching const_get to handle "::" would suffice.

Possibly.

···

On Feb 14, 2007, at 12:05, Daniel Berger wrote:

On Feb 14, 12:50 pm, "Lyle Johnson" <lyle.john...@gmail.com> wrote:

On 2/14/07, Eric Hodel <drbr...@segment7.net> wrote:

I tried messing with object.c yesterday, but I couldn't make it work.
Any ideas for a patch? It would be nice to get one in for 1.8.6 if
possible.

Dan

···

On Feb 14, 1:44 pm, Eric Hodel <drbr...@segment7.net> wrote:

On Feb 14, 2007, at 12:05, Daniel Berger wrote:

> On Feb 14, 12:50 pm, "Lyle Johnson" <lyle.john...@gmail.com> wrote:
>> On 2/14/07, Eric Hodel <drbr...@segment7.net> wrote:

>>> One of these days I'm going to write a patch to expose this to
>>> Ruby :stuck_out_tongue:

>> Expose what to Ruby?

> I think he just means that there's no direct equivalent in plain Ruby
> (right?).

Yup. I've reimplemented rb_path2class() numerous times in ruby, and
there's a perfectly good C method lying around that needs only a one-
line patch to be accessible.

> I think just patching const_get to handle "::" would suffice.

Possibly.

Oh, I was mistaken, its VALUE rb_path2class(const char *) not VALUE rb_path2class(VALUE), so a wrapper function needs to be written (so more than one line). I don't know if Object::path2class is a good name, since the C implementation can only have Object as its root.

I got this far:

Index: intern.h

···

On Feb 15, 2007, at 07:50, Daniel Berger wrote:

On Feb 14, 1:44 pm, Eric Hodel <drbr...@segment7.net> wrote:

On Feb 14, 2007, at 12:05, Daniel Berger wrote:

I think he just means that there's no direct equivalent in plain Ruby
(right?).

Yup. I've reimplemented rb_path2class() numerous times in ruby, and
there's a perfectly good C method lying around that needs only a one-
line patch to be accessible.

I tried messing with object.c yesterday, but I couldn't make it work.
Any ideas for a patch? It would be nice to get one in for 1.8.6 if
possible.

===================================================================
--- intern.h (revision 11758)
+++ intern.h (working copy)
@@ -453,6 +453,7 @@ VALUE rb_mod_name _((VALUE));
VALUE rb_class_path _((VALUE));
void rb_set_class_path _((VALUE, VALUE, const char*));
VALUE rb_path2class _((const char*));
+VALUE rb_obj_path2class _((VALUE));
void rb_name_class _((VALUE, ID));
VALUE rb_class_name _((VALUE));
void rb_autoload _((VALUE, ID, const char*));
Index: variable.c

--- variable.c (revision 11758)
+++ variable.c (working copy)
@@ -274,6 +274,13 @@ rb_path2class(path)
      return c;
}
+VALUE
+rb_obj_path2class(path)
+ VALUE path;
+{
+ return rb_path2class(StringValueCStr(path));
+}
+
void
rb_name_class(klass, id)
      VALUE klass;
Index: object.c

--- object.c (revision 11758)
+++ object.c (working copy)
@@ -2646,7 +2646,7 @@ Init_Object()
      rb_define_private_method(rb_cModule, "method_added", rb_obj_dummy, 1);
      rb_define_private_method(rb_cModule, "method_removed", rb_obj_dummy, 1);
      rb_define_private_method(rb_cModule, "method_undefined", rb_obj_dummy, 1);
-
+ rb_define_singleton_method(rb_cObject, "path2class", rb_obj_path2class, 1);
      rb_define_method(rb_mKernel, "nil?", rb_false, 0);
      rb_define_method(rb_mKernel, "==", rb_obj_equal, 1);

$ ./ruby186 -e 'p Object.path2class("Struct")'
-e:1:in `path2class': can't convert Class into String (TypeError)
         from -e:1