#ifndef Rice__Data_Type__ipp_ #define Rice__Data_Type__ipp_ #include "Class.hpp" #include "String.hpp" #include "Data_Object.hpp" #include "detail/default_allocation_func.hpp" #include "detail/creation_funcs.hpp" #include "detail/method_data.hpp" #include "detail/Caster.hpp" #include "detail/demangle.hpp" #include #include template VALUE Rice::Data_Type::klass_ = Qnil; template std::auto_ptr Rice::Data_Type::caster_; template template inline Rice::Data_Type Rice::Data_Type:: bind(Module const & klass) { if(klass.value() == klass_) { return Data_Type(); } if(is_bound()) { std::string s; s = "Data type "; s += detail::demangle(typeid(T).name()); s += " is already bound to a different type"; throw std::runtime_error(s.c_str()); } // TODO: Make sure base type is bound; throw an exception otherwise. // We can't do this just yet, because we don't have a specialization // for binding to void. klass_ = klass; // TODO: do we need to unregister when the program exits? we have to // be careful if we do, because the ruby interpreter might have // already shut down. The correct behavior is probably to register an // exit proc with the interpreter, so the proc gets called before the // GC shuts down. rb_gc_register_address(&klass_); for(typename Instances::iterator it = unbound_instances().begin(), end = unbound_instances().end(); it != end; unbound_instances().erase(it++)) { (*it)->set_value(klass); } detail::Abstract_Caster * base_caster = Data_Type().caster(); caster_.reset(new detail::Caster(base_caster, klass)); Data_Type_Base::casters().insert(std::make_pair(klass, caster_.get())); return Data_Type(); } template inline Rice::Data_Type:: Data_Type() : Module_impl >( klass_ == Qnil ? rb_cObject : klass_) { if(!is_bound()) { unbound_instances().insert(this); } } template inline Rice::Data_Type:: Data_Type(Module const & klass) : Module_impl >( klass) { this->bind(klass); } template inline Rice::Data_Type:: ~Data_Type() { unbound_instances().erase(this); } template Rice::Module Rice::Data_Type:: klass() { if(is_bound()) { return klass_; } else { std::string s; s += detail::demangle(typeid(T *).name()); s += " is unbound"; throw std::runtime_error(s.c_str()); } } template Rice::Data_Type & Rice::Data_Type:: operator=(Module const & klass) { this->bind(klass); return *this; } template template inline Rice::Data_Type & Rice::Data_Type:: define_constructor( Constructor_T /* constructor */, Arguments* arguments) { check_is_bound(); // Normal constructor pattern with new/initialize rb_define_alloc_func( static_cast(*this), detail::default_allocation_func); define_method( "initialize", &Constructor_T::construct, arguments ); return *this; } template template inline Rice::Data_Type & Rice::Data_Type:: define_constructor( Constructor_T constructor, Arg const& arg) { Arguments* args = new Arguments(); args->add(arg); return define_constructor(constructor, args); } template template inline Rice::Data_Type& Rice::Data_Type:: define_director() { Rice::Data_Type::template bind(*this); return *this; } template inline T * Rice::Data_Type:: from_ruby(Object x) { check_is_bound(); void * v = DATA_PTR(x.value()); Class klass = x.class_of(); if(klass.value() == klass_) { // Great, not converting to a base/derived type Data_Type data_klass; Data_Object obj(x, data_klass); return obj.get(); } Data_Type_Base::Casters::const_iterator it = Data_Type_Base::casters().begin(); Data_Type_Base::Casters::const_iterator end = Data_Type_Base::casters().end(); // Finding the bound type that relates to the given klass is // a two step process. We iterate over the list of known type casters, // looking for: // // 1) casters that handle this direct type // 2) casters that handle types that are ancestors of klass // // Step 2 allows us to handle the case where a Rice-wrapped class // is subclassed in Ruby but then an instance of that class is passed // back into C++ (say, in a Listener / callback construction) // VALUE ancestors = rb_mod_ancestors(klass.value()); long earliest = RARRAY_LEN(ancestors) + 1; int index; VALUE indexFound; Data_Type_Base::Casters::const_iterator toUse = end; for(; it != end; it++) { // Do we match directly? if(klass.value() == it->first) { toUse = it; break; } // Check for ancestors. Trick is, we need to find the lowest // ancestor that does have a Caster to make sure that we're casting // to the closest C++ type that the Ruby class is subclassing. // There might be multiple ancestors that are also wrapped in // the extension, so find the earliest in the list and use that one. indexFound = rb_funcall(ancestors, rb_intern("index"), 1, it->first); if(indexFound != Qnil) { index = NUM2INT(indexFound); if(index < earliest) { earliest = index; toUse = it; } } } if(toUse == end) { std::string s = "Class "; s += klass.name().str(); s += " is not registered/bound in Rice"; throw std::runtime_error(s); } detail::Abstract_Caster * caster = toUse->second; if(caster) { T * result = static_cast(caster->cast_to_base(v, klass_)); return result; } else { return static_cast(v); } } template inline bool Rice::Data_Type:: is_bound() { return klass_ != Qnil; } template inline Rice::detail::Abstract_Caster * Rice::Data_Type:: caster() const { check_is_bound(); return caster_.get(); } namespace Rice { template<> inline detail::Abstract_Caster * Data_Type:: caster() const { return 0; } template void Data_Type:: check_is_bound() { if(!is_bound()) { std::string s; s = "Data type "; s += detail::demangle(typeid(T).name()); s += " is not bound"; throw std::runtime_error(s.c_str()); } } } // Rice template inline Rice::Data_Type Rice:: define_class_under( Object module, char const * name) { Class c(define_class_under(module, name, rb_cObject)); c.undef_creation_funcs(); return Data_Type::template bind(c); } template inline Rice::Data_Type Rice:: define_class_under( Object module, char const * name) { Data_Type base_dt; Class c(define_class_under(module, name, base_dt)); c.undef_creation_funcs(); return Data_Type::template bind(c); } template inline Rice::Data_Type Rice:: define_class( char const * name) { Class c(define_class(name, rb_cObject)); c.undef_creation_funcs(); return Data_Type::template bind(c); } template inline Rice::Data_Type Rice:: define_class( char const * name) { Data_Type base_dt; Class c(define_class(name, base_dt)); c.undef_creation_funcs(); return Data_Type::template bind(c); } template inline void Rice::define_implicit_cast() { // As Rice currently expects only one entry into // this list for a given klass VALUE, we need to get // the current caster for From_T and insert in our // new caster as the head of the caster list Class from_class = Data_Type::klass().value(); Class to_class = Data_Type::klass().value(); detail::Abstract_Caster* from_caster = Data_Type::caster_.release(); detail::Abstract_Caster* new_caster = new detail::Implicit_Caster(from_caster, to_class); // Insert our new caster into the list for the from class Data_Type_Base::casters().erase(from_class); Data_Type_Base::casters().insert( std::make_pair( from_class, new_caster ) ); // And make sure the from_class has direct access to the // updated caster list Data_Type::caster_.reset(new_caster); } #endif // Rice__Data_Type__ipp_