Question

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.

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top