Commit 38dea780 authored by Jakub Klinkovský's avatar Jakub Klinkovský
Browse files

Fixed copy/move-constructors/assignments in SharedPointer

This can handle non-const -> const constructions and assignments. Due to
the automatically generated copy- and move-constructors and assignment
operators, some SFINAE tricks and little code duplication are needed to
make it work.
parent 741111a8
Loading
Loading
Loading
Loading
+243 −78
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
   #include <memory>
   #include <cstdlib>

   inline
   std::string demangle(const char* mangled)
   {
      int status;
@@ -57,11 +58,24 @@ class SharedPointer
};

/****
 * Non-const specialization
 * Specialization for Devices::Host
 */
template< typename Object, bool lazy >
class SharedPointer< Object, Devices::Host, lazy > : public SmartPointer
{
   private:
      // Convenient template alias for controlling the selection of copy- and
      // move-constructors and assignment operators using SFINAE.
      // The type Object_ is "enabled" iff Object_ and Object are not the same,
      // but after removing const and volatile qualifiers they are the same.
      template< typename Object_ >
      using Enabler = std::enable_if< ! std::is_same< Object_, Object >::value &&
                                      std::is_same< typename std::remove_cv< Object >::type, Object_ >::value >;

      // friend class will be needed for templated assignment operators
      template< typename Object_, typename Device_ >
      friend class DevicePointer;

   public:

      typedef Object ObjectType;
@@ -82,6 +96,7 @@ class SharedPointer< Object, Devices::Host, lazy > : public SmartPointer
         }
      }

      // this is needed only to avoid the default compiler-generated constructor
      SharedPointer( const ThisType& pointer )
      : pointer( pointer.pointer ),
        counter( pointer.counter )
@@ -89,6 +104,36 @@ class SharedPointer< Object, Devices::Host, lazy > : public SmartPointer
         *counter += 1;
      }

      // conditional constructor for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      SharedPointer( const SharedPointer< Object_, DeviceType, lazy_ >& pointer )
      : pointer( pointer.pointer ),
        counter( pointer.counter )
      {
         *counter += 1;
      }

      // this is needed only to avoid the default compiler-generated constructor
      SharedPointer( ThisType&& pointer )
      : pointer( pointer.pointer ),
        counter( pointer.counter )
      {
         pointer.pointer = nullptr;
         pointer.counter = nullptr;
      }

      // conditional constructor for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      SharedPointer( SharedPointer< Object_, DeviceType, lazy_ >&& pointer )
      : pointer( pointer.pointer ),
        counter( pointer.counter )
      {
         pointer.pointer = nullptr;
         pointer.counter = nullptr;
      }

      template< typename... Args >
      bool recreate( Args... args )
      {
@@ -152,6 +197,7 @@ class SharedPointer< Object, Devices::Host, lazy > : public SmartPointer
         return *( this->pointer );
      }

      // this is needed only to avoid the default compiler-generated operator
      const ThisType& operator=( const ThisType& ptr )
      {
         this->free();
@@ -161,13 +207,39 @@ class SharedPointer< Object, Devices::Host, lazy > : public SmartPointer
         return *this;
      }

      const ThisType& operator=( const ThisType&& ptr )
      // conditional operator for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      const ThisType& operator=( const SharedPointer< Object_, DeviceType, lazy_ >& ptr )
      {
         this->free();
         this->pointer = ptr.pointer;
         this->counter = ptr.counter;
         *( this->counter ) += 1;
         return *this;
      }

      // this is needed only to avoid the default compiler-generated operator
      const ThisType& operator=( ThisType&& ptr )
      {
         this->free();
         this->pointer = ptr.pointer;
         ptr.pointer = nullptr;
         this->counter = ptr.counter;
         ptr.counter = nullptr;
         return *this;
      }

      // conditional operator for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      const ThisType& operator=( SharedPointer< Object_, DeviceType, lazy_ >&& ptr )
      {
         this->free();
         this->pointer = ptr.pointer;
         ptr.pointer= NULL;
         ptr.pointer = nullptr;
         this->counter = ptr.counter;
         ptr.counter = NULL;
         ptr.counter = nullptr;
         return *this;
      }

@@ -205,11 +277,24 @@ class SharedPointer< Object, Devices::Host, lazy > : public SmartPointer
};

/****
 * Non-const specialization for CUDA
 * Specialization for CUDA
 */
template< typename Object, bool lazy >
class SharedPointer< Object, Devices::Cuda, lazy > : public SmartPointer
{
   private:
      // Convenient template alias for controlling the selection of copy- and
      // move-constructors and assignment operators using SFINAE.
      // The type Object_ is "enabled" iff Object_ and Object are not the same,
      // but after removing const and volatile qualifiers they are the same.
      template< typename Object_ >
      using Enabler = std::enable_if< ! std::is_same< Object_, Object >::value &&
                                      std::is_same< typename std::remove_cv< Object >::type, Object_ >::value >;

      // friend class will be needed for templated assignment operators
      template< typename Object_, typename Device_ >
      friend class DevicePointer;

   public:

      typedef Object ObjectType;
@@ -221,9 +306,6 @@ class SharedPointer< Object, Devices::Cuda, lazy > : public SmartPointer
      : counter( 0 ), cuda_pointer( 0 ),
        pointer( 0 ), modified( false )
      {
#ifdef TNL_DEBUG_SHARED_POINTERS
         std::cerr << "Creating shared pointer to " << demangle(typeid(ObjectType).name()) << std::endl;
#endif
         if( ! lazy )
         {
            this->counter = new int( 1 );
@@ -232,11 +314,15 @@ class SharedPointer< Object, Devices::Cuda, lazy > : public SmartPointer
            this->cuda_pointer = Devices::Cuda::passToDevice( *this->pointer );
            if( ! this->cuda_pointer )
               return;
#ifdef TNL_DEBUG_SHARED_POINTERS
            std::cerr << "Created shared pointer to " << demangle(typeid(ObjectType).name()) << " (cuda_pointer = " << this->cuda_pointer << ")" << std::endl;
#endif
            Devices::Cuda::insertSmartPointer( this );
#endif
         }
      }

      // this is needed only to avoid the default compiler-generated constructor
      SharedPointer( const ThisType& pointer )
      : pointer( pointer.pointer ),
        cuda_pointer( pointer.cuda_pointer ),
@@ -246,6 +332,46 @@ class SharedPointer< Object, Devices::Cuda, lazy > : public SmartPointer
         *counter += 1;
      }

      // conditional constructor for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      SharedPointer( const SharedPointer< Object_, DeviceType, lazy_ >& pointer )
      : pointer( pointer.pointer ),
        cuda_pointer( pointer.cuda_pointer ),
        counter( pointer.counter ),
        modified( pointer.modified )
      {
         *counter += 1;
      }

      // this is needed only to avoid the default compiler-generated constructor
      SharedPointer( ThisType&& pointer )
      : pointer( pointer.pointer ),
        cuda_pointer( pointer.cuda_pointer ),
        counter( pointer.counter ),
        modified( pointer.modified )
      {
         pointer.pointer = nullptr;
         pointer.cuda_pointer = nullptr;
         pointer.modified = false;
         pointer.counter = nullptr;
      }

      // conditional constructor for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      SharedPointer( SharedPointer< Object_, DeviceType, lazy_ >&& pointer )
      : pointer( pointer.pointer ),
        cuda_pointer( pointer.cuda_pointer ),
        counter( pointer.counter ),
        modified( pointer.modified )
      {
         pointer.pointer = nullptr;
         pointer.cuda_pointer = nullptr;
         pointer.modified = false;
         pointer.counter = nullptr;
      }

      template< typename... Args >
      bool recreate( Args... args )
      {
@@ -345,34 +471,72 @@ class SharedPointer< Object, Devices::Cuda, lazy > : public SmartPointer
         }
      }

      /*const ThisType& operator=( ThisType&& ptr )
      // this is needed only to avoid the default compiler-generated operator
      const ThisType& operator=( const ThisType& ptr )
      {
         this->free();
#ifdef HAVE_CUDA
         if( this->cuda_pointer )
            cudaFree( this->cuda_pointer );
         this->pointer = ptr.pointer;
         this->cuda_pointer = ptr.cuda_pointer;
         this->modified = ptr.modified;
         this->counter = ptr.counter;
         *( this->counter ) += 1;
#ifdef TNL_DEBUG_SHARED_POINTERS
         std::cerr << "Copy-assigned shared pointer: counter = " << *(this->counter) << ", type: " << demangle(typeid(ObjectType).name()) << std::endl;
#endif
         return *this;
      }

      // conditional operator for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      const ThisType& operator=( const SharedPointer< Object_, DeviceType, lazy_ >& ptr )
      {
         this->free();
         this->pointer = ptr.pointer;
         this->cuda_pointer = ptr.cuda_pointer;
         this->modified = ptr.modified;
         this->counter = ptr.counter;
         *( this->counter ) += 1;
#ifdef TNL_DEBUG_SHARED_POINTERS
         std::cerr << "Copy-assigned shared pointer: counter = " << *(this->counter) << ", type: " << demangle(typeid(ObjectType).name()) << std::endl;
#endif
         return *this;
      }

      // this is needed only to avoid the default compiler-generated operator
      const ThisType& operator=( ThisType&& ptr )
      {
         this->free();
         this->pointer = ptr.pointer;
         this->cuda_pointer = ptr.cuda_pointer;
         this->modified = ptr.modified;
         this->counter = ptr.counter;
         ptr.pointer= NULL;
         ptr.cuda_pointer = NULL;
         ptr.pointer = nullptr;
         ptr.cuda_pointer = nullptr;
         ptr.modified = false;
         ptr.counter = NULL;
         ptr.counter = nullptr;
#ifdef TNL_DEBUG_SHARED_POINTERS
         std::cerr << "Move-assigned shared pointer: counter = " << *(this->counter) << ", type: " << demangle(typeid(ObjectType).name()) << std::endl;
#endif
         return *this;
      }*/
      }

      const ThisType& operator=( const ThisType& ptr )
      // conditional operator for non-const -> const data
      template< typename Object_, bool lazy_,
                typename = typename Enabler< Object_ >::type >
      const ThisType& operator=( SharedPointer< Object_, DeviceType, lazy_ >&& ptr )
      {
         this->free();
         this->pointer = ptr.pointer;
         this->cuda_pointer = ptr.cuda_pointer;
         this->modified = ptr.modified;
         this->counter = ptr.counter;
         *( this->counter ) += 1;
         ptr.pointer = nullptr;
         ptr.cuda_pointer = nullptr;
         ptr.modified = false;
         ptr.counter = nullptr;
#ifdef TNL_DEBUG_SHARED_POINTERS
         std::cerr << "Assigned shared pointer: counter = " << *(this->counter) << ", type: " << demangle(typeid(ObjectType).name()) << std::endl;
         std::cerr << "Move-assigned shared pointer: counter = " << *(this->counter) << ", type: " << demangle(typeid(ObjectType).name()) << std::endl;
#endif
         return *this;
      }
@@ -395,6 +559,7 @@ class SharedPointer< Object, Devices::Cuda, lazy > : public SmartPointer
            this->modified = false;
            return true;
         }
         return true;
#else
         return false;
#endif
@@ -415,7 +580,7 @@ class SharedPointer< Object, Devices::Cuda, lazy > : public SmartPointer
         if( this->counter )
         {
#ifdef TNL_DEBUG_SHARED_POINTERS
            std::cerr << "Freeing shared pointer: counter = " << *(this->counter) << ", type: " << demangle(typeid(ObjectType).name()) << std::endl;
            std::cerr << "Freeing shared pointer: counter = " << *(this->counter) << ", cuda_pointer = " << this->cuda_pointer << ", type: " << demangle(typeid(ObjectType).name()) << std::endl;
#endif
            if( ! --*( this->counter ) )
            {