3

I'm trying to convert an if condition of:

unless defined? SomeConstant
  # do some stuff
end

Into part of a native C extension. Does anybody know how to do the defined? predicate check in the C API?

EDIT | I guess I could invoke:

rb_funcall(rb_cObject, rb_intern("const_defined?"), 1, rb_intern("SomeConstant"))

Though this is obviously slightly different, semantically.

Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107
d11wtq
  • 34,788
  • 19
  • 120
  • 195
  • 3
    `Object.const_defined?` might be as good as you're going to get. The actual `defined?` keyword is implemented in `insns.def` (look for `DEFINE_INSN`) and that calls `vm_get_ev_const` which is static inside `vm_insnhelper.c` and thus inaccessible. – mu is too short May 04 '12 at 05:18
  • I think you're right, thanks! Would you like to make that an answer? :) – d11wtq May 04 '12 at 06:34
  • I put down some notes but it isn't (of course) always as simple as `Object.const_defined?`. Of course. – mu is too short May 04 '12 at 07:52

1 Answers1

3

If you trace through the 1.9.3 source, you'll find that defined? is implemented in insns.def:

DEFINE_INSN
defined
(rb_num_t op_type, VALUE obj, VALUE needstr)
/* ... */
    switch (type) {
    /* ... */
      case DEFINED_CONST:
        klass = v;
        if (vm_get_ev_const(th, GET_ISEQ(), klass, SYM2ID(obj), 1)) {
            expr_type = "constant";
        }
        break;

So when you defined? SomeConstant, you trickle through that big switch and end up calling vm_get_ev_const. The function is defined in vm_insnhelper.c:

static inline VALUE
vm_get_ev_const(rb_thread_t *th, const rb_iseq_t *iseq,
                VALUE orig_klass, ID id, int is_defined)

That function happens to be static so you can't get at it. Looks like vm_get_ev_const is defined in terms of rb_const_defined and rb_const_defined_from and both of those should be available in your C so you could try those; but you'd have to find the right klass for those.

Or you could go with your idea and just use Object.const_defined?. One problem with that is that it won't do The Right Thing with things like A::B, you'd have to say Object.const_defined? :A && A.const_defined? :B for that as Object.const_defined? :'A::B' will just throw an exception in your face. A general solution here would require iteration and class lookups. However, if the classes that you're looking at are all in the top level namespace, then a simple Object.const_defined? should do the trick.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • `rb_const_defined()` is the method to use. `if ( rb_const_defined( rb_cObject, rb_intern("MY_CONST") ) )` – thomthom May 22 '12 at 11:08
  • @thomthom coudln't find `rb_const_defined` declared in `ruby.h` on 1.9.3 or 2.0.0. – mezis Mar 31 '13 at 22:46
  • 1
    @mezis If you follow the link in "mu is too short"'s answer (`rb_const_defined`) - then it takes you to where it is located in `variable.c`. – thomthom Mar 31 '13 at 23:02
  • 1
    And it is declared in `intern.h`: https://github.com/ruby/ruby/blob/ruby_1_9_3/include/ruby/intern.h#L872 – thomthom Mar 31 '13 at 23:16
  • This is a nice tool to search the Ruby source: http://rxr.whitequark.org/mri/ident?i=rb_const_defined – thomthom Mar 31 '13 at 23:17
  • @thomthom ah, thanks. And for the curious, it's declared in `intern.h`, not `ruby.h`. I suppose that makes it part of the internal API, although not the "public" one. Use with caution :) – mezis Apr 03 '13 at 07:57