Search code examples
crubyruby-c-extension

Ruby C API `defined? SomeConstant` equivalent?


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.


Solution

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