SharedPtr static/dynamic casts

Discussion area about developing or extending OGRE, adding plugins for it or building applications on it. No newbie questions please, use the Help forum for that.
Post Reply
User avatar
sparkprime
Ogre Magi
Posts: 1137
Joined: Mon May 07, 2007 3:43 am
Location: Ossining, New York
x 13
Contact:

SharedPtr static/dynamic casts

Post by sparkprime »

I added the following code in my ogre fork, now seeking feedback:

Code: Select all

        template<class U> Ogre::SharedPtr<U> staticCast (void) const {
            Ogre::SharedPtr<U> r;
            if (isNull()) {
                r.setNull();
                return r;
            }
            assert(pUseCount);
            r.pRep = static_cast<U*>(pRep);
            r.pUseCount = pUseCount;
            r.useFreeMethod = useFreeMethod;
#if OGRE_THREAD_SUPPORT
            r.OGRE_AUTO_MUTEX_NAME = OGRE_AUTO_MUTEX_NAME;
#endif
            OGRE_LOCK_AUTO_SHARED_MUTEX
            (*pUseCount)++; // we are creating a copy
            return r;
        }

        template<class U> Ogre::SharedPtr<U> dynamicCast (void) const {
            Ogre::SharedPtr<U> r;
            if (isNull()) {
                r.setNull();
                return r;
            }
            assert(pUseCount);
            r.pRep = dynamic_cast<U*>(pRep);
            r.pUseCount = pUseCount;
            r.useFreeMethod = useFreeMethod;
#if OGRE_THREAD_SUPPORT
            r.OGRE_AUTO_MUTEX_NAME = OGRE_AUTO_MUTEX_NAME;
#endif
            OGRE_LOCK_AUTO_SHARED_MUTEX
            (*pUseCount)++; // we are creating a copy
            return r;
        }

...

        template<class U> friend class SharedPtr;

Note that the friend declaration is important to avoid a protected member access error.

This allows the following sort of thing (where B extends A)

Ogre::SharedPtr<B> = some_shared_ptr_a.staticCast<B>();

(it relies on the c++ template instantiation time type checking to check that B and A are actually related by subtyping)


edit: corrected the code to copy the mutex ptr
Last edited by sparkprime on Sun Sep 16, 2012 9:04 pm, edited 1 time in total.
User avatar
sparkprime
Ogre Magi
Posts: 1137
Joined: Mon May 07, 2007 3:43 am
Location: Ossining, New York
x 13
Contact:

Re: SharedPtr static/dynamic casts

Post by sparkprime »

I think that a lot of these classes that extend SharedPtr and add only the ability to do implicit downcasts between ResourcePtr and derived resource classes should be removed in favour of something like this.
User avatar
m2codeGEN
Halfling
Posts: 52
Joined: Tue Apr 26, 2011 9:13 am
Location: Russia, Tver
x 2

Re: SharedPtr static/dynamic casts

Post by m2codeGEN »

if dynamic_cast failed you don`t need increment user counter
(*pUseCount)++; // we are creating a copy

i suggest this implementation

Code: Select all

template<class U> Ogre::SharedPtr<U> dynamicCast (void) const
{
   Ogre::SharedPtr<U> r;
   r.pRep = dynamic_cast<U*>(pRep);
   
   if (r.isNull())
      return r;

   r.pUseCount = pUseCount;
   r.useFreeMethod = useFreeMethod;

#if OGRE_THREAD_SUPPORT
   r.OGRE_AUTO_MUTEX_NAME = OGRE_AUTO_MUTEX_NAME;
#endif
   OGRE_LOCK_AUTO_SHARED_MUTEX
   (*pUseCount)++; // we are creating a copy
   return r;
}

User avatar
sparkprime
Ogre Magi
Posts: 1137
Joined: Mon May 07, 2007 3:43 am
Location: Ossining, New York
x 13
Contact:

Re: SharedPtr static/dynamic casts

Post by sparkprime »

Yeah I didn't handle that case properly, in fact I only needed staticCast in my own code :)
User avatar
sparkprime
Ogre Magi
Posts: 1137
Joined: Mon May 07, 2007 3:43 am
Location: Ossining, New York
x 13
Contact:

Re: SharedPtr static/dynamic casts

Post by sparkprime »

I think it has to be something like this to avoid leaving unitialised fields though:

Code: Select all

        template<class U> Ogre::SharedPtr<U> dynamicCast (void) const {
            Ogre::SharedPtr<U> r;
            U *casted_prep = dynamic_cast<U*>(pRep);
            if (casted_prep == NULL) {
                r.setNull();
                return r;
            }
            assert(pUseCount);
            r.pRep = casted_prep;
            r.pUseCount = pUseCount;
            r.useFreeMethod = useFreeMethod;
#if OGRE_THREAD_SUPPORT
            r.OGRE_AUTO_MUTEX_NAME = OGRE_AUTO_MUTEX_NAME;
#endif
            OGRE_LOCK_AUTO_SHARED_MUTEX
            (*pUseCount)++; // we are creating a copy
            return r;
        }
User avatar
m2codeGEN
Halfling
Posts: 52
Joined: Tue Apr 26, 2011 9:13 am
Location: Russia, Tver
x 2

Re: SharedPtr static/dynamic casts

Post by m2codeGEN »

moreover, for processing reference counter it is possible to use a AtomicScalar class http://www.ogre3d.org/docs/api/html/cla ... calar.html
But I don't like its current implementation. In our project interlocked functions implemented by us under Win (Win32 API) and Linux (gcc extension). In Intel TBB there is an example of quite good implementation.
User avatar
sparkprime
Ogre Magi
Posts: 1137
Joined: Mon May 07, 2007 3:43 am
Location: Ossining, New York
x 13
Contact:

Re: SharedPtr static/dynamic casts

Post by sparkprime »

I actually wrote that class :) I intended it to be used in the Ogre::SharedPtr but I punted that because the code was such a mess and it was much less of a hit on performance than the resource loading stuff (which was hit many more times every pass).

Why don't you like the implementation of it?
User avatar
m2codeGEN
Halfling
Posts: 52
Joined: Tue Apr 26, 2011 9:13 am
Location: Russia, Tver
x 2

Re: SharedPtr static/dynamic casts

Post by m2codeGEN »

Here are our atomic functions implementation

Code: Select all

#if !defined(__INTERLOCKED_11_05_2012_H__)
#define __INTERLOCKED_11_05_2012_H__

#if defined(__GNUC__)
   #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2))
      #include <ext/atomicity.h>
   #elif defined __LCC__
      #include "asm/e2k_api.h"
   #else
      #include <bits/atomicity.h>
   #endif
#endif

////////////////////////////////////////////////////////////
////Experimental////////////////////////////////////////////
/// New atomic increment, decrement, addvalue, setvalue, getvalue functions.
/// The functions prevents more than one thread from using the same variable simultaneously.
///
namespace bm
{
#ifdef WIN32
   /// The atomicIncrement function increments (increases by one) the value of the specified 32-bit variable 
   ///  and checks the resulting value. 
   /// The function returns the resulting incremented value.
   inline int atomicIncrement(int volatile* addend) { return InterlockedIncrement(reinterpret_cast<long volatile*>(addend)); }

   /// The atomicDecrement function decrements (decreases by one) the value of the specified 32-bit variable 
   /// and checks the resulting value.
   /// The function returns the resulting decremented value. 
   inline int atomicDecrement(int volatile* addend) { return InterlockedDecrement(reinterpret_cast<long volatile*>(addend)); }

   /// The atomicAddValue function performs an atomic addition of a 32-bit increment value to a 32-bit addend variable. 
   /// The function returns the initial value of the addend parameter.
   inline int atomicAddValue(int volatile* addend, int value) { return InterlockedExchangeAdd(reinterpret_cast<long volatile*>(addend), value);}

   /// The atomicSetValue function atomically exchanges a pair of 32-bit values. 
   /// The function returns the initial value of the target.
   inline int atomicSetValue(int volatile* target, int value) { return InterlockedExchange(reinterpret_cast<long volatile*>(target), value);}

#elif defined __LCC__
   inline int atomicIncrement(int volatile* addend) { return __api_atomic32_add(1, addend); }
   inline int atomicDecrement(int volatile* addend) { return __api_atomic32_sub(1, addend); }
   inline int atomicAddValue(int volatile* addend, int value) { return __api_atomic32_add(value, addend) - value; }
   inline int atomicSetValue(int volatile* target, int value) { return __api_xchg32_return(value, target);}
#else
   /// The atomicIncrement function increments (increases by one) the value of the specified 32-bit variable 
   ///  and checks the resulting value. 
   /// The function returns the resulting incremented value.
   inline int atomicIncrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, 1) + 1; }

   /// The atomicDecrement function decrements (decreases by one) the value of the specified 32-bit variable 
   /// and checks the resulting value.
   /// The function returns the resulting decremented value. 
   inline int atomicDecrement(int volatile* addend) { return __gnu_cxx::__exchange_and_add(addend, -1) - 1; }

   /// The atomicAddValue function performs an atomic addition of a 32-bit increment value to a 32-bit addend variable. 
   /// The function returns the initial value of the addend parameter.
   inline int atomicAddValue(int volatile* addend, int value) {return __gnu_cxx::__exchange_and_add(addend, value);}

   /// The atomicSetValue function atomically exchanges a pair of 32-bit values. 
   /// The function returns the initial value of the target.
   int atomic_exchange( int volatile* pw, int dv );
   inline int atomicSetValue(int volatile* addend, int value) {return atomic_exchange(addend, -value);}

   /// аналоги на assebler-e
   ///
   inline int atomic_exchange( int volatile* pw, int dv )
   {
      // int r = *pw;
      // *pw = dv;
      // return r;

      int r;

      __asm__ __volatile__
         (
            "lock\n\t"
            "xchg %1, %0":
            "=m"( *pw ), "=r"( r ): // outputs (%0, %1)
            "m"( *pw ), "1"( dv ): // inputs (%2, %3 == %1)
            "memory", "cc" // clobbers
         );

      return r;
   }

   ///
   inline int atomic_exchange_and_add( int * pw, int dv )
   {
      // int r = *pw;
      // *pw += dv;
      // return r;

      int r;

      __asm__ __volatile__
         (
            "lock\n\t"
            "xadd %1, %0":
            "=m"( *pw ), "=r"( r ): // outputs (%0, %1)
            "m"( *pw ), "1"( dv ): // inputs (%2, %3 == %1)
            "memory", "cc" // clobbers
         );

      return r;
   }

   ///
   inline void atomic_increment( int * pw )
   {
      //atomic_exchange_and_add( pw, 1 );

      __asm__ __volatile__
         (
            "lock\n\t"
            "incl %0":
            "=m"( *pw ): // output (%0)
            "m"( *pw ): // input (%1)
            "cc" // clobbers
         );
   }

   ///
   inline void atomic_decrement( int * pw )
   {
      //atomic_exchange_and_add( pw, -1 );

      __asm__ __volatile__
         (
            "lock\n\t"
            "decl %0":
            "=m"( *pw ): // output (%0)
            "m"( *pw ): // input (%1)
            "cc" // clobbers
         );
   }
#endif

   /// The atomicGetValue function atomically returns the current 32-bit value of the target.
   inline int atomicGetValue(int const volatile* target) { return static_cast<int const volatile &>(*target); }
}
///
////////////////////////////////////////////////////////////

#endif // #if !defined(__INTERLOCKED_11_05_2012_H__)

User avatar
sparkprime
Ogre Magi
Posts: 1137
Joined: Mon May 07, 2007 3:43 am
Location: Ossining, New York
x 13
Contact:

Re: SharedPtr static/dynamic casts

Post by sparkprime »

They look very similar, what don't you like about the Ogre implementation?
User avatar
m2codeGEN
Halfling
Posts: 52
Joined: Tue Apr 26, 2011 9:13 am
Location: Russia, Tver
x 2

Re: SharedPtr static/dynamic casts

Post by m2codeGEN »

sparkprime wrote:They look very similar, what don't you like about the Ogre implementation?
Yes, it`s very similar. But less code. There are 3 class implementations in Ogre.
User avatar
sparkprime
Ogre Magi
Posts: 1137
Joined: Mon May 07, 2007 3:43 am
Location: Ossining, New York
x 13
Contact:

Re: SharedPtr static/dynamic casts

Post by sparkprime »

Well ogre has to handle the case where the compiler or system isn't recognised and uses locks in this case. That makes it a lot more complicated. Also Ogre handles more types than just int.
Post Reply