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