From: Thomas Geymayer Date: Thu, 15 Nov 2012 20:17:33 +0000 (+0100) Subject: Canvas/C++ bindings: automatically detect dynamic type of polymorphic exposed classes X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=392ba18ff7295f2622ae3a052049c76e9d760e26;p=simgear.git Canvas/C++ bindings: automatically detect dynamic type of polymorphic exposed classes --- diff --git a/simgear/nasal/cppbind/Ghost.hxx b/simgear/nasal/cppbind/Ghost.hxx index 2b037496..c679f644 100644 --- a/simgear/nasal/cppbind/Ghost.hxx +++ b/simgear/nasal/cppbind/Ghost.hxx @@ -84,6 +84,7 @@ namespace nasal struct SharedPointerPolicy { typedef typename GhostTypeTraits::raw_type raw_type; + typedef boost::false_type returns_dynamic_type; /** * Create a shared pointer on the heap to handle the reference counting for @@ -107,6 +108,7 @@ namespace nasal struct RawPointerPolicy { typedef typename GhostTypeTraits::raw_type raw_type; + typedef boost::true_type returns_dynamic_type; /** * Create a new object instance on the heap @@ -243,10 +245,14 @@ namespace nasal /** * Register a new ghost type. * + * @note Only intialize each ghost type once! + * * @param name Descriptive name of the ghost type. */ static Ghost& init(const std::string& name) { + assert( !getSingletonPtr() ); + getSingletonHolder().reset( new Ghost(name) ); return *getSingletonPtr(); } @@ -273,7 +279,7 @@ namespace nasal bases() { BaseGhost* base = BaseGhost::getSingletonPtr(); - //addBaseClass( base ); + base->addDerived( &getTypeFor ); // Replace any getter that is not available in the current class. // TODO check if this is the correct behavior of function overriding @@ -490,6 +496,62 @@ namespace nasal template friend class Ghost; + typedef naGhostType* (*type_checker_t)(const raw_type*); + typedef std::vector DerivedList; + DerivedList _derived_classes; + + void addDerived(const type_checker_t& derived_info) + { + _derived_classes.push_back(derived_info); + } + + template + static + typename boost::enable_if + < boost::is_polymorphic, + naGhostType* + >::type + getTypeFor(const typename BaseGhost::raw_type* base) + { + // Check first if passed pointer can by converted to instance of class + // this ghost wraps. + if( !boost::is_same + < typename BaseGhost::raw_type, + Ghost::raw_type + >::value + && dynamic_cast(base) != base ) + return 0; + + // Now check if we can further downcast to one of our derived classes. + naGhostType* ghost_type = 0; + for( typename DerivedList::reverse_iterator + derived = getSingletonPtr()->_derived_classes.rbegin(); + derived != getSingletonPtr()->_derived_classes.rend(); + ++derived ) + { + ghost_type = (*derived)( static_cast(base) ); + if( ghost_type ) + return ghost_type; + } + + // If base is not an instance of any derived class, this class has to + // be the dynamic type. + return &getSingletonPtr()->_ghost_type; + } + + template + static + typename boost::disable_if + < boost::is_polymorphic, + naGhostType* + >::type + getTypeFor(const typename BaseGhost::raw_type* base) + { + // For non polymorphic classes there is no possibility to get the actual + // dynamic type, therefore we can only use its static type. + return &BaseGhost::getSingletonPtr()->_ghost_type; + } + static Ghost* getSingletonPtr() { return getSingletonHolder().get(); @@ -580,7 +642,23 @@ namespace nasal static naRef makeGhost(naContext c, void *ptr) { - return naNewGhost2(c, &getSingletonPtr()->_ghost_type, ptr); + naGhostType* ghost_type = 0; + if( Ghost::returns_dynamic_type::value ) + // For pointer policies already returning instances of an object with + // the dynamic type of this Ghost's raw_type the type is always the + // same. + ghost_type = &getSingletonPtr()->_ghost_type; + else + // If wrapping eg. shared pointers the users passes an already + // existing instance of an object which will then be hold be a new + // shared pointer. We therefore have to check for the dynamic type + // of the object as it might differ from the passed static type. + ghost_type = getTypeFor( Ghost::getRawPtr(ptr) ); + + if( !ghost_type ) + return naNil(); + + return naNewGhost2(c, ghost_type, ptr); } static void destroyGhost(void *ptr) diff --git a/simgear/nasal/cppbind/cppbind_test.cxx b/simgear/nasal/cppbind/cppbind_test.cxx index 1a68f1b5..30d4a338 100644 --- a/simgear/nasal/cppbind/cppbind_test.cxx +++ b/simgear/nasal/cppbind/cppbind_test.cxx @@ -1,19 +1,22 @@ #include "Ghost.hxx" #include "NasalHash.hxx" +#include + #include #include #define VERIFY(a) \ if( !(a) ) \ { \ - std::cerr << "failed:" << #a << std::endl; \ + std::cerr << "failed: line " << __LINE__ << ": " << #a << std::endl; \ return 1; \ } struct Base { naRef member(naContext, int, naRef*) { return naNil(); } + virtual ~Base(){}; }; struct Derived: public Base @@ -22,6 +25,16 @@ struct Derived: int getX() const { return _x; } void setX(int x) { _x = x; } }; +struct DoubleDerived: + public Derived +{ + +}; +struct DoubleDerived2: + public Derived +{ + +}; naRef member(Derived&, naContext, int, naRef*) { return naNil(); } @@ -83,6 +96,36 @@ int main(int argc, char* argv[]) naRef derived = Ghost::create(c); VERIFY( naIsGhost(derived) ); + VERIFY( std::string("Derived") == naGhost_type(derived)->name ); + + typedef boost::shared_ptr BasePtr; + typedef boost::shared_ptr DerivedPtr; + typedef boost::shared_ptr DoubleDerivedPtr; + typedef boost::shared_ptr DoubleDerived2Ptr; + + Ghost::init("BasePtr"); + Ghost::init("DerivedPtr") + .bases(); + Ghost::init("DoubleDerivedPtr") + .bases(); + Ghost::init("DoubleDerived2Ptr") + .bases(); + + BasePtr d( new Derived ); + derived = Ghost::create(c, d); + VERIFY( naIsGhost(derived) ); + VERIFY( std::string("DerivedPtr") == naGhost_type(derived)->name ); + + BasePtr d2( new DoubleDerived ); + derived = Ghost::create(c, d2); + VERIFY( naIsGhost(derived) ); + VERIFY( std::string("DoubleDerivedPtr") == naGhost_type(derived)->name ); + + BasePtr d3( new DoubleDerived2 ); + derived = Ghost::create(c, d3); + VERIFY( naIsGhost(derived) ); + VERIFY( std::string("DoubleDerived2Ptr") == naGhost_type(derived)->name ); + // TODO actuall do something with the ghosts... naFreeContext(c);