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 <stdexcept> include <typeinfo>

template<typename T> VALUE Rice::Data_Type<T>::klass_ = Qnil;

template<typename T> std_unique_ptr<Rice::detail::Abstract_Caster> Rice::Data_Type<T>::caster_;

template<typename T> template<typename Base_T>

inline Rice::Data_Type<T> Rice::Data_Type<T>

bind(Module const & klass) {

if(klass.value() == klass_)
{
  return Data_Type<T>();
}

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<Base_T>().caster();
caster_.reset(new detail::Caster<T, Base_T>(base_caster, klass));
Data_Type_Base::casters().insert(std::make_pair(klass, caster_.get()));
return Data_Type<T>();

}

template<typename T>

inline Rice::Data_Type<T>

Data_Type()

: Module_impl<Data_Type_Base, Data_Type<T> >(
    klass_ == Qnil ? rb_cObject : klass_)

{

if(!is_bound())
{
  unbound_instances().insert(this);
}

}

template<typename T>

inline Rice::Data_Type<T>

Data_Type(Module const & klass)

: Module_impl<Data_Type_Base, Data_Type<T> >(
    klass)

{

this->bind<void>(klass);

}

template<typename T>

inline Rice::Data_Type<T>

~Data_Type() {

unbound_instances().erase(this);

}

template<typename T> Rice::Module

Rice::Data_Type<T>

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<typename T>

Rice::Data_Type<T> & Rice::Data_Type<T>

operator=(Module const & klass) {

this->bind<void>(klass);
return *this;

}

template<typename T> template<typename Constructor_T>

inline Rice::Data_Type<T> & Rice::Data_Type<T>

define_constructor(

Constructor_T    constructor   ,
Arguments* arguments)

{

check_is_bound();

// Normal constructor pattern with new/initialize
rb_define_alloc_func(
    static_cast<VALUE>(*this),
    detail::default_allocation_func<T>);
this->define_method(
    "initialize",
    &Constructor_T::construct,
    arguments
    );

return *this;

}

template<typename T> template<typename Constructor_T>

inline Rice::Data_Type<T> & Rice::Data_Type<T>

define_constructor(

Constructor_T constructor,
Arg const& arg)

{

Arguments* args = new Arguments();
args->add(arg);
return define_constructor(constructor, args);

}

template<typename T> template<typename Director_T>

inline Rice::Data_Type<T>& Rice::Data_Type<T>

define_director() {

Rice::Data_Type<Director_T>::template bind<T>(*this);
return *this;

}

template<typename T>

inline T * Rice::Data_Type<T>

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<T> data_klass;
  Data_Object<T> 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<T *>(caster->cast_to_base(v, klass_));
  return result;
}
else
{
  return static_cast<T *>(v);
}

}

template<typename T>

inline bool Rice::Data_Type<T>

is_bound() {

return klass_ != Qnil;

}

template<typename T>

inline Rice::detail::Abstract_Caster * Rice::Data_Type<T>

caster() const {

check_is_bound();
return caster_.get();

}

namespace Rice {

template<>

inline detail::Abstract_Caster * Data_Type<void>

caster() const {

return 0;

}

template<typename T>

void Data_Type<T>

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<typename T>

inline Rice::Data_Type<T> 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<T>::template bind<void>(c);

}

template<typename T, typename Base_T>

inline Rice::Data_Type<T> Rice

define_class_under(

Object module,
char const * name)

{

Data_Type<Base_T> base_dt;
Class c(define_class_under(module, name, base_dt));
c.undef_creation_funcs();
return Data_Type<T>::template bind<Base_T>(c);

}

template<typename T>

inline Rice::Data_Type<T> Rice

define_class(

char const * name)

{

Class c(define_class(name, rb_cObject));
c.undef_creation_funcs();
return Data_Type<T>::template bind<void>(c);

}

template<typename T, typename Base_T>

inline Rice::Data_Type<T> Rice

define_class(

char const * name)

{

Data_Type<Base_T> base_dt;
Class c(define_class(name, base_dt));
c.undef_creation_funcs();
return Data_Type<T>::template bind<Base_T>(c);

}

template<typename From_T, typename To_T> 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<From_T>::klass().value();
Class to_class = Data_Type<To_T>::klass().value();

detail::Abstract_Caster* from_caster = 
  Data_Type<From_T>::caster_.release();

detail::Abstract_Caster* new_caster = 
  new detail::Implicit_Caster<To_T, From_T>(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<From_T>::caster_.reset(new_caster);

}

endif // Rice__Data_Type__ipp_