]> git.mxchange.org Git - simgear.git/commitdiff
nasal::Ghost: improve intrusive pointer storage and weak references.
authorThomas Geymayer <tomgey@gmail.com>
Sun, 22 Jun 2014 22:27:41 +0000 (00:27 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Mon, 23 Jun 2014 11:14:07 +0000 (13:14 +0200)
 - Just increment/decrement reference count for intrusive
   smart pointers. No need to create an additional object
   on the heap.
 - Keep strong reference for weak pointer based ghosts
   to prevent destroying objects while beeing used.

simgear/nasal/cppbind/Ghost.hxx
simgear/nasal/cppbind/cppbind_test_ghost.cxx
simgear/nasal/cppbind/detail/nasal_traits.hxx

index b7d1d84993b8a0e25f61f2b013a0bf26afaf1287..6dcb5e15f6660e8cc411a43adbd7da1e3688fc8f 100644 (file)
@@ -50,12 +50,25 @@ inline T* get_pointer(SGWeakPtr<T> const& p)
   return p.lock().get();
 }
 
+namespace osg
+{
+  template<class T>
+  inline T* get_pointer(observer_ptr<T> const& p)
+  {
+    ref_ptr<T> ref;
+    p.lock(ref);
+    return ref.get();
+  }
+}
+
 template<class T>
-inline T* get_pointer(osg::observer_ptr<T> const& p)
+inline typename boost::enable_if<
+  boost::is_pointer<T>,
+  T
+>::type
+get_pointer(T ptr)
 {
-  osg::ref_ptr<T> ref;
-  p.lock(ref);
-  return ref.get();
+  return ptr;
 }
 
 // Both ways of retrieving the address of a static member function
@@ -292,9 +305,26 @@ namespace nasal
               if( !holder )
                 throw std::runtime_error("holder has expired");
 
+              // Keep reference for duration of call to prevent expiring
+              // TODO not needed for strong referenced ghost
+              strong_ref ref = fromNasal<strong_ref>(c, me);
+              if( !ref )
+              {
+                naGhostType* ghost_type = naGhost_type(me);
+                naRuntimeError
+                (
+                  c,
+                  "method called on object of wrong type: "
+                  "is '%s' expected '%s'",
+                  naIsNil(me) ? "nil"
+                              : (ghost_type ? ghost_type->name : "unknown"),
+                  _ghost_type_strong.name
+                );
+              }
+
               return holder->_method
               (
-                requireObject(c, me),
+                *get_pointer(ref),
                 CallContext(c, argc, args)
               );
             }
@@ -793,7 +823,9 @@ namespace nasal
         if( !ghost_type )
           return naNil();
 
-        return naNewGhost2(c, ghost_type, new RefType(ref_ptr));
+        return naNewGhost2( c,
+                            ghost_type,
+                            shared_ptr_storage<RefType>::ref(ref_ptr) );
       }
 
       /**
@@ -801,14 +833,8 @@ namespace nasal
        * Nasal objects has to be derived class of the target class (Either
        * derived in C++ or in Nasal using a 'parents' vector)
        */
-      template<class RefType>
-      static
-      typename boost::enable_if_c<
-           boost::is_same<RefType, strong_ref>::value
-        || boost::is_same<RefType, weak_ref>::value,
-        RefType
-      >::type
-      fromNasal(naContext c, naRef me)
+      template<class Type>
+      static Type fromNasal(naContext c, naRef me)
       {
         bool is_weak = false;
 
@@ -816,8 +842,8 @@ namespace nasal
         if( isBaseOf(naGhost_type(me), is_weak) )
         {
           void* ghost = naGhost_ptr(me);
-          return is_weak ? getPtr<RefType, true>(ghost)
-                         : getPtr<RefType, false>(ghost);
+          return is_weak ? getPtr<Type, true>(ghost)
+                         : getPtr<Type, false>(ghost);
         }
 
         // Now if it is derived from a ghost (hash with ghost in parent vector)
@@ -825,7 +851,7 @@ namespace nasal
         {
           naRef na_parents = naHash_cget(me, const_cast<char*>("parents"));
           if( !naIsVector(na_parents) )
-            return RefType();
+            return Type();
 
           typedef std::vector<naRef> naRefs;
           naRefs parents = from_nasal<naRefs>(c, na_parents);
@@ -833,13 +859,13 @@ namespace nasal
                                       parent != parents.end();
                                     ++parent )
           {
-            RefType ptr = fromNasal<RefType>(c, *parent);
+            Type ptr = fromNasal<Type>(c, *parent);
             if( get_pointer(ptr) )
               return ptr;
           }
         }
 
-        return RefType();
+        return Type();
       }
 
       static bool isBaseOf(naRef obj)
@@ -881,8 +907,11 @@ namespace nasal
       >::type
       getPtr(void* ptr)
       {
+        typedef shared_ptr_storage<strong_ref> storage_type;
         if( ptr )
-          return RefPtr(*static_cast<strong_ref*>(ptr));
+          return storage_type::template get<RefPtr>(
+            static_cast<typename storage_type::storage_type*>(ptr)
+          );
         else
           return RefPtr();
       }
@@ -895,8 +924,11 @@ namespace nasal
       >::type
       getPtr(void* ptr)
       {
+        typedef shared_ptr_storage<weak_ref> storage_type;
         if( ptr )
-          return RefPtr(*static_cast<weak_ref*>(ptr));
+          return storage_type::template get<RefPtr>(
+            static_cast<typename storage_type::storage_type*>(ptr)
+          );
         else
           return RefPtr();
       }
@@ -990,25 +1022,6 @@ namespace nasal
         return getSingletonHolder().get();
       }
 
-      static raw_type& requireObject(naContext c, naRef me)
-      {
-        raw_type* obj = get_pointer( fromNasal<strong_ref>(c, me) );
-
-        if( !obj )
-        {
-          naGhostType* ghost_type = naGhost_type(me);
-          naRuntimeError
-          (
-            c,
-            "method called on object of wrong type: is '%s' expected '%s'",
-            naIsNil(me) ? "nil" : (ghost_type ? ghost_type->name : "unknown"),
-            _ghost_type_strong.name
-          );
-        }
-
-        return *obj;
-      }
-
       template<class Ret>
       getter_t to_getter(Ret (raw_type::*getter)() const)
       {
@@ -1138,10 +1151,8 @@ namespace nasal
         _ghost_type_strong.destroy =
           SG_GET_TEMPLATE_MEMBER(Ghost, queueDestroy<strong_ref>);
         _ghost_type_strong.name = _name_strong.c_str();
-        _ghost_type_strong.get_member =
-          SG_GET_TEMPLATE_MEMBER(Ghost, getMember<false>);
-        _ghost_type_strong.set_member =
-          SG_GET_TEMPLATE_MEMBER(Ghost, setMember<false>);
+        _ghost_type_strong.get_member = &Ghost::getMemberStrong;
+        _ghost_type_strong.set_member = &Ghost::setMemberStrong;
 
         _ghost_type_weak.destroy =
           SG_GET_TEMPLATE_MEMBER(Ghost, queueDestroy<weak_ref>);
@@ -1149,10 +1160,8 @@ namespace nasal
 
         if( supports_weak_ref<T>::value )
         {
-          _ghost_type_weak.get_member =
-            SG_GET_TEMPLATE_MEMBER(Ghost, getMember<true>);
-          _ghost_type_weak.set_member =
-            SG_GET_TEMPLATE_MEMBER(Ghost, setMember<true>);
+          _ghost_type_weak.get_member = &Ghost::getMemberWeak;
+          _ghost_type_weak.set_member = &Ghost::setMemberWeak;
         }
         else
         {
@@ -1170,7 +1179,10 @@ namespace nasal
       template<class Type>
       static void destroy(void *ptr)
       {
-        delete static_cast<Type*>(ptr);
+        typedef shared_ptr_storage<Type> storage_type;
+        storage_type::unref(
+          static_cast<typename storage_type::storage_type*>(ptr)
+        );
       }
 
       template<class Type>
@@ -1179,6 +1191,17 @@ namespace nasal
         _destroy_list.push_back( DestroyList::value_type(&destroy<Type>, ptr) );
       }
 
+      static void raiseErrorExpired(naContext c, const char* str)
+      {
+        Ghost* ghost_info = getSingletonPtr();
+        naRuntimeError(
+          c,
+          "Ghost::%s: ghost has expired '%s'",
+          str,
+          ghost_info ? ghost_info->_name_strong.c_str() : "unknown"
+        );
+      }
+
       /**
        * Callback for retrieving a ghost member.
        */
@@ -1218,14 +1241,25 @@ namespace nasal
         return "";
       }
 
-      template<bool is_weak>
-      static const char* getMember( naContext c,
-                                    void* ghost,
-                                    naRef key,
-                                    naRef* out )
+      static const char*
+      getMemberWeak(naContext c, void* ghost, naRef key, naRef* out)
+      {
+        // Keep a strong reference while retrieving member, to prevent deleting
+        // object in between.
+        strong_ref ref = getPtr<strong_ref, true>(ghost);
+        if( !ref )
+          raiseErrorExpired(c, "getMember");
+
+        return getMemberImpl(c, *get_pointer(ref), key, out);
+      }
+
+      static const char*
+      getMemberStrong(naContext c, void* ghost, naRef key, naRef* out)
       {
-        strong_ref const& ptr = getPtr<strong_ref, is_weak>(ghost);
-        return getMemberImpl(c, *get_pointer(ptr), key, out);
+        // Just get a raw pointer as we are keeping a strong reference as ghost
+        // anyhow.
+        raw_type* ptr = getPtr<raw_type*, false>(ghost);
+        return getMemberImpl(c, *ptr, key, out);
       }
 
       /**
@@ -1256,14 +1290,25 @@ namespace nasal
           member->second.setter(obj, c, val);
       }
 
-      template<bool is_weak>
-      static void setMember( naContext c,
-                             void* ghost,
-                             naRef field,
-                             naRef val )
+      static void
+      setMemberWeak(naContext c, void* ghost, naRef field, naRef val)
       {
-        strong_ref const& ptr = getPtr<strong_ref, is_weak>(ghost);
-        return setMemberImpl(c, *get_pointer(ptr), field, val);
+        // Keep a strong reference while retrieving member, to prevent deleting
+        // object in between.
+        strong_ref ref = getPtr<strong_ref, true>(ghost);
+        if( !ref )
+          raiseErrorExpired(c, "setMember");
+
+        setMemberImpl(c, *get_pointer(ref), field, val);
+      }
+
+      static void
+      setMemberStrong(naContext c, void* ghost, naRef field, naRef val)
+      {
+        // Just get a raw pointer as we are keeping a strong reference as ghost
+        // anyhow.
+        raw_type* ptr = getPtr<raw_type*, false>(ghost);
+        setMemberImpl(c, *ptr, field, val);
       }
   };
 
@@ -1313,7 +1358,7 @@ from_nasal_helper(naContext c, naRef ref, const T*)
 }
 
 /**
- * Convert any pointer to a SGReference based object to a ghost.
+ * Convert any pointer to a SGReferenced based object to a ghost.
  */
 template<class T>
 typename boost::enable_if_c<
@@ -1327,7 +1372,7 @@ to_nasal_helper(naContext c, T* ptr)
 }
 
 /**
- * Convert nasal ghosts/hashes to pointer (of a SGReference based ghost).
+ * Convert nasal ghosts/hashes to pointer (of a SGReferenced based ghost).
  */
 template<class T>
 typename boost::enable_if_c<
@@ -1344,7 +1389,34 @@ typename boost::enable_if_c<
 from_nasal_helper(naContext c, naRef ref, const T*)
 {
   typedef SGSharedPtr<typename boost::remove_pointer<T>::type> TypeRef;
-  return nasal::Ghost<TypeRef>::template fromNasal<TypeRef>(c, ref).release();
+  return nasal::Ghost<TypeRef>::template fromNasal<T>(c, ref);
+}
+
+/**
+ * Convert any pointer to a osg::Referenced based object to a ghost.
+ */
+template<class T>
+typename boost::enable_if<
+  boost::is_base_of<osg::Referenced, T>,
+  naRef
+>::type
+to_nasal_helper(naContext c, T* ptr)
+{
+  return nasal::Ghost<osg::ref_ptr<T> >::makeGhost(c, osg::ref_ptr<T>(ptr));
+}
+
+/**
+ * Convert nasal ghosts/hashes to pointer (of a osg::Referenced based ghost).
+ */
+template<class T>
+typename boost::enable_if<
+  boost::is_base_of<osg::Referenced, typename boost::remove_pointer<T>::type>,
+  T
+>::type
+from_nasal_helper(naContext c, naRef ref, const T*)
+{
+  typedef osg::ref_ptr<typename boost::remove_pointer<T>::type> TypeRef;
+  return nasal::Ghost<TypeRef>::template fromNasal<T>(c, ref);
 }
 
 #endif /* SG_NASAL_GHOST_HXX_ */
index 569e8e7adfaa9439f4dea19e8af26f897c210511..67a265bdbcd44bb17533eb9aae0eaf19277d18ce 100644 (file)
@@ -2,6 +2,8 @@
 #include <BoostTestTargetConfig.h>
 
 #include "Ghost.hxx"
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
 
 class Base1:
   public virtual SGVirtualWeakReferenced
@@ -36,6 +38,7 @@ typedef SGSharedPtr<SGReferenced> SGReferencedPtr;
   CHECK_PTR_TRAIT_TYPE(weak, weak_ref, weak)\
 
 CHECK_PTR_TRAIT(DerivedPtr, DerivedWeakPtr)
+CHECK_PTR_TRAIT(boost::shared_ptr<Base1>, boost::weak_ptr<Base1>)
 
 #undef CHECK_PTR_TRAIT
 #undef CHECK_PTR_TRAIT_TYPE
@@ -87,3 +90,52 @@ BOOST_AUTO_TEST_CASE( ghost_weak_strong_nasal_conversion )
 
   BOOST_REQUIRE( !weak.lock() );
 }
+
+#define CHECK_PTR_STORAGE_TRAIT_TYPE(ptr_t, storage)\
+  BOOST_STATIC_ASSERT((boost::is_same<\
+    nasal::shared_ptr_storage<ptr_t>::storage_type,\
+    storage\
+  >::value));
+
+CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedPtr, Derived)
+CHECK_PTR_STORAGE_TRAIT_TYPE(DerivedWeakPtr, DerivedWeakPtr)
+
+typedef boost::shared_ptr<Derived> BoostDerivedPtr;
+CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedPtr, BoostDerivedPtr)
+
+typedef boost::weak_ptr<Derived> BoostDerivedWeakPtr;
+CHECK_PTR_STORAGE_TRAIT_TYPE(BoostDerivedWeakPtr, BoostDerivedWeakPtr)
+
+#undef CHECK_PTR_STORAGE_TRAIT_TYPE
+
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<Base1Ptr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<Base2Ptr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedPtr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<DerivedWeakPtr>::is_intrusive::value));
+BOOST_STATIC_ASSERT(( nasal::shared_ptr_traits<SGReferencedPtr>::is_intrusive::value));
+
+BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::shared_ptr<Derived> >::is_intrusive::value));
+BOOST_STATIC_ASSERT((!nasal::shared_ptr_traits<boost::weak_ptr<Derived> >::is_intrusive::value));
+
+BOOST_AUTO_TEST_CASE( storage_traits )
+{
+  DerivedPtr d = new Derived();
+
+  Derived* d_raw = nasal::shared_ptr_storage<DerivedPtr>::ref(d);
+  BOOST_REQUIRE_EQUAL(d_raw, d.get());
+  BOOST_REQUIRE_EQUAL(d.getNumRefs(), 2);
+
+  DerivedWeakPtr* d_weak = nasal::shared_ptr_storage<DerivedWeakPtr>::ref(d);
+  BOOST_REQUIRE_EQUAL(
+    nasal::shared_ptr_storage<DerivedWeakPtr>::get<Derived*>(d_weak),
+    d_raw
+  );
+
+  d.reset();
+  BOOST_REQUIRE_EQUAL(Derived::count(d_raw), 1);
+
+  nasal::shared_ptr_storage<DerivedPtr>::unref(d_raw);
+  BOOST_REQUIRE(d_weak->expired());
+
+  nasal::shared_ptr_storage<DerivedWeakPtr>::unref(d_weak);
+}
index 62c4d302bb253f86f314fab2484c575c3e5cdd65..f3126918de6cd9735638ff27b844804fa8c2d8d9 100644 (file)
 
 #include <boost/type_traits/integral_constant.hpp>
 #include <boost/type_traits/is_base_of.hpp>
+#include <boost/utility/enable_if.hpp>
 
 // Forward declarations
+class SGReferenced;
 class SGWeakReferenced;
 template<class T> class SGSharedPtr;
 template<class T> class SGWeakPtr;
@@ -36,6 +38,7 @@ namespace boost
 }
 namespace osg
 {
+  class Referenced;
   template<class T> class ref_ptr;
   template<class T> class observer_ptr;
 
@@ -77,25 +80,27 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
     public boost::integral_constant<bool, false>
   {};
 
-#define SG_MAKE_SHARED_PTR_TRAIT(ref, weak)\
+#define SG_MAKE_SHARED_PTR_TRAIT(strong, weak, intrusive)\
   template<class T>\
-  struct shared_ptr_traits<ref<T> >\
+  struct shared_ptr_traits<strong<T> >\
   {\
-    typedef ref<T>  strong_ref;\
-    typedef weak<T> weak_ref;\
-    typedef T       element_type;\
+    typedef strong<T> strong_ref;\
+    typedef weak<T>   weak_ref;\
+    typedef T         element_type;\
     typedef boost::integral_constant<bool, true> is_strong;\
+    typedef boost::integral_constant<bool, intrusive> is_intrusive;\
   };\
   template<class T>\
   struct shared_ptr_traits<weak<T> >\
   {\
-    typedef ref<T>  strong_ref;\
-    typedef weak<T> weak_ref;\
-    typedef T       element_type;\
+    typedef strong<T> strong_ref;\
+    typedef weak<T>   weak_ref;\
+    typedef T         element_type;\
     typedef boost::integral_constant<bool, false> is_strong;\
+    typedef boost::integral_constant<bool, intrusive> is_intrusive;\
   };\
   template<class T>\
-  struct is_strong_ref<ref<T> >:\
+  struct is_strong_ref<strong<T> >:\
     public boost::integral_constant<bool, true>\
   {};\
   template<class T>\
@@ -103,9 +108,9 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
     public boost::integral_constant<bool, true>\
   {};
 
-  SG_MAKE_SHARED_PTR_TRAIT(SGSharedPtr, SGWeakPtr)
-  SG_MAKE_SHARED_PTR_TRAIT(osg::ref_ptr, osg::observer_ptr)
-  SG_MAKE_SHARED_PTR_TRAIT(boost::shared_ptr, boost::weak_ptr)
+  SG_MAKE_SHARED_PTR_TRAIT(SGSharedPtr, SGWeakPtr, true)
+  SG_MAKE_SHARED_PTR_TRAIT(osg::ref_ptr, osg::observer_ptr, true)
+  SG_MAKE_SHARED_PTR_TRAIT(boost::shared_ptr, boost::weak_ptr, false)
 
 #undef SG_MAKE_SHARED_PTR_TRAIT
 
@@ -122,5 +127,140 @@ SG_MAKE_TRAIT(<>, osg::Vec2s, is_vec2)
     >
   {};
 
+  template<class T>
+  struct shared_ptr_storage
+  {
+    typedef T                                           storage_type;
+    typedef typename T::element_type                    element_type;
+    typedef typename shared_ptr_traits<T>::strong_ref   strong_ref;
+    typedef typename shared_ptr_traits<T>::weak_ref     weak_ref;
+
+    template<class U>
+    static storage_type* ref(U ptr)
+    {
+      return new storage_type(ptr);
+    }
+    static void unref(storage_type* ptr)
+    {
+      delete ptr;
+    }
+
+    template<class U>
+    static
+    typename boost::enable_if<
+      boost::is_same<U, element_type*>,
+      element_type*
+    >::type
+    get(storage_type* ptr)
+    {
+      return get_pointer(*ptr);
+    }
+    template<class U>
+    static
+    typename boost::enable_if_c<
+         boost::is_same<U, strong_ref>::value
+      || (boost::is_same<U, weak_ref>::value && supports_weak_ref<U>::value),
+      U
+    >::type
+    get(storage_type* ptr)
+    {
+      return U(*ptr);
+    }
+    template<class U>
+    static
+    typename boost::enable_if_c<
+         boost::is_same<U, weak_ref>::value
+      && !supports_weak_ref<U>::value,
+      U
+    >::type
+    get(storage_type* ptr)
+    {
+      return U();
+    }
+  };
+
+  namespace internal
+  {
+    template<class T>
+    struct intrusive_ptr_storage
+    {
+      typedef typename T::element_type                    storage_type;
+      typedef typename T::element_type                    element_type;
+      typedef typename shared_ptr_traits<T>::strong_ref   strong_ref;
+      typedef typename shared_ptr_traits<T>::weak_ref     weak_ref;
+
+      template<class U>
+      static
+      typename boost::enable_if<
+        boost::is_same<U, element_type*>,
+        element_type*
+      >::type
+      get(storage_type* ptr)
+      {
+        return ptr;
+      }
+      template<class U>
+      static
+      typename boost::enable_if_c<
+           boost::is_same<U, strong_ref>::value
+        || (boost::is_same<U, weak_ref>::value && supports_weak_ref<U>::value),
+        U
+      >::type
+      get(storage_type* ptr)
+      {
+        return U(ptr);
+      }
+      template<class U>
+      static
+      typename boost::enable_if_c<
+           boost::is_same<U, weak_ref>::value
+        && !supports_weak_ref<U>::value,
+        U
+      >::type
+      get(storage_type* ptr)
+      {
+        return U();
+      }
+    };
+  }
+
+  template<class T>
+  struct shared_ptr_storage<SGSharedPtr<T> >:
+    public internal::intrusive_ptr_storage<SGSharedPtr<T> >
+  {
+    typedef T storage_type;
+    typedef T element_type;
+
+    static storage_type* ref(element_type* ptr)
+    {
+      T::get(ptr);
+      return ptr;
+    }
+    static void unref(storage_type* ptr)
+    {
+      if( !T::put(ptr) )
+        delete ptr;
+    }
+  };
+
+  template<class T>
+  struct shared_ptr_storage<osg::ref_ptr<T> >:
+    public internal::intrusive_ptr_storage<osg::ref_ptr<T> >
+  {
+    typedef T storage_type;
+    typedef T element_type;
+
+
+    static storage_type* ref(element_type* ptr)
+    {
+      ptr->ref();
+      return ptr;
+    }
+    static void unref(storage_type* ptr)
+    {
+      ptr->unref();
+    }
+  };
+
 } // namespace nasal
 #endif /* SG_NASAL_TRAITS_HXX_ */