]> git.mxchange.org Git - simgear.git/commitdiff
Canvas/C++ bindings: automatically detect dynamic type of polymorphic exposed classes
authorThomas Geymayer <tomgey@gmail.com>
Thu, 15 Nov 2012 20:17:33 +0000 (21:17 +0100)
committerThomas Geymayer <tomgey@gmail.com>
Thu, 15 Nov 2012 20:17:33 +0000 (21:17 +0100)
simgear/nasal/cppbind/Ghost.hxx
simgear/nasal/cppbind/cppbind_test.cxx

index 2b0374962c8e9e72ef3e24e2563fa169e4f9df7c..c679f6440c69a244f532dbce68e4450c3675ff64 100644 (file)
@@ -84,6 +84,7 @@ namespace nasal
   struct SharedPointerPolicy
   {
     typedef typename GhostTypeTraits<T>::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<T>::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<BaseGhost> );
 
         // 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<class>
       friend class Ghost;
 
+      typedef naGhostType* (*type_checker_t)(const raw_type*);
+      typedef std::vector<type_checker_t> DerivedList;
+      DerivedList _derived_classes;
+
+      void addDerived(const type_checker_t& derived_info)
+      {
+        _derived_classes.push_back(derived_info);
+      }
+
+      template<class BaseGhost>
+      static
+      typename boost::enable_if
+        < boost::is_polymorphic<typename BaseGhost::raw_type>,
+          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<const Ghost::raw_type*>(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<const Ghost::raw_type*>(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<class BaseGhost>
+      static
+      typename boost::disable_if
+        < boost::is_polymorphic<typename BaseGhost::raw_type>,
+          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>( Ghost::getRawPtr(ptr) );
+
+        if( !ghost_type )
+          return naNil();
+
+        return naNewGhost2(c, ghost_type, ptr);
       }
 
       static void destroyGhost(void *ptr)
index 1a68f1b5359877ad736cf7a4c8eb14013f8865d1..30d4a338611199752428940e55a65089055aedfb 100644 (file)
@@ -1,19 +1,22 @@
 #include "Ghost.hxx"
 #include "NasalHash.hxx"
 
+#include <boost/shared_ptr.hpp>
+
 #include <cstring>
 #include <iostream>
 
 #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<Derived>::create(c);
   VERIFY( naIsGhost(derived) );
+  VERIFY( std::string("Derived") ==  naGhost_type(derived)->name );
+
+  typedef boost::shared_ptr<Base> BasePtr;
+  typedef boost::shared_ptr<Derived> DerivedPtr;
+  typedef boost::shared_ptr<DoubleDerived> DoubleDerivedPtr;
+  typedef boost::shared_ptr<DoubleDerived2> DoubleDerived2Ptr;
+
+  Ghost<BasePtr>::init("BasePtr");
+  Ghost<DerivedPtr>::init("DerivedPtr")
+    .bases<BasePtr>();
+  Ghost<DoubleDerivedPtr>::init("DoubleDerivedPtr")
+    .bases<DerivedPtr>();
+  Ghost<DoubleDerived2Ptr>::init("DoubleDerived2Ptr")
+    .bases<DerivedPtr>();
+
+  BasePtr d( new Derived );
+  derived = Ghost<BasePtr>::create(c, d);
+  VERIFY( naIsGhost(derived) );
+  VERIFY( std::string("DerivedPtr") == naGhost_type(derived)->name );
+
+  BasePtr d2( new DoubleDerived );
+  derived = Ghost<BasePtr>::create(c, d2);
+  VERIFY( naIsGhost(derived) );
+  VERIFY( std::string("DoubleDerivedPtr") ==  naGhost_type(derived)->name );
+
+  BasePtr d3( new DoubleDerived2 );
+  derived = Ghost<BasePtr>::create(c, d3);
+  VERIFY( naIsGhost(derived) );
+  VERIFY( std::string("DoubleDerived2Ptr") ==  naGhost_type(derived)->name );
+
   // TODO actuall do something with the ghosts...
 
   naFreeContext(c);