/*! \file \brief BDM's own smart pointers. \author Vaclav Barta. ----------------------------------- BDM++ - C++ library for Bayesian Decision Making under Uncertainty Using IT++ for numerical operations ----------------------------------- */ #ifndef shared_ptr_h #define shared_ptr_h #include #include #include #include #include "itpp_ext.h" namespace bdm { /*! \brief A naive implementation of roughly a subset of the std::tr1:shared_ptr spec Really just roughly - it ignores memory exceptions, for example; also note I didn't read the spec. The standard template would naturally be preferable, _if_ it was included in the standard libraries of all supported compilers - but as of 2009, that's still a problem... */ template class shared_ptr { template friend class shared_ptr; private: T *payload; unsigned *refCnt; public: //! Creates an empty shared_ptr - one that doesn't point anywhere. shared_ptr() : payload ( 0 ), refCnt ( 0 ) { } /*! Constructs a shared_ptr that owns the pointer p (unless p is NULL, in which case this constructor creates an empty shared_ptr). When p isn't null, it must have been alllocated by new! */ shared_ptr ( T *p ) : payload ( p ), refCnt ( p ? new unsigned ( 1 ) : 0 ) { } //! If other is empty, constructs an empty shared_ptr; otherwise, //! constructs a shared_ptr that shares ownership with other. shared_ptr ( const shared_ptr &other ) : payload ( other.payload ), refCnt ( other.refCnt ) { add_ref(); } //! If other is empty, constructs an empty shared_ptr; otherwise, //! constructs a shared_ptr that shares ownership with other. template shared_ptr ( const shared_ptr &other ) : payload ( other.payload ), refCnt ( other.refCnt ) { add_ref(); } ~shared_ptr() { del_ref(); } shared_ptr &operator= ( const shared_ptr &other ) { other.add_ref(); del_ref(); payload = other.payload; refCnt = other.refCnt; return *this; } //! Returns the stored pointer (which remains owned by this //! instance). T *get() { return payload; } //! Returns the stored pointer (which remains owned by this //! instance). This method may only be called when the stored //! pointer isn't NULL. T *operator->() { it_assert_debug ( payload, "dereferencing NULL" ); return payload; } //! Returns a reference to the object pointed to by the stored //! pointer. This method may only be called when the stored pointer //! isn't NULL. T &operator*() { it_assert_debug ( payload, "dereferencing NULL" ); return *payload; } //! Returns the stored pointer (which remains owned by this //! instance). const T* get() const { return payload; } //! Returns the stored pointer (which remains owned by this //! instance). This method may only be called when the stored //! pointer isn't NULL. const T *operator->() const { it_assert_debug ( payload, "dereferencing NULL" ); return payload; } //! Returns a reference to the object pointed to by the stored //! pointer. This method may only be called when the stored pointer //! isn't NULL. const T &operator*() const { it_assert_debug ( payload, "dereferencing NULL" ); return *payload; } bool unique() const { return refCnt && ( *refCnt == 1 ); } long use_count() const { return refCnt ? *refCnt : 0; } operator bool() const { return payload; } void swap ( shared_ptr &other ) { std::swap ( payload, other.payload ); std::swap ( refCnt, other.refCnt ); } private: void add_ref() const { if ( refCnt ) { if ( *refCnt == UINT_MAX ) { throw std::overflow_error ( std::string ( "Shared pointer has too many references." ) ); } ++*refCnt; } } void del_ref() { if ( refCnt ) { if ( ! ( --*refCnt ) ) { delete payload; delete refCnt; } } } }; //! Compare shared pointers template bool operator== ( shared_ptr const &a, shared_ptr const &b ) { return a.get() == b.get(); } //! Compare shared pointers template bool operator!= ( shared_ptr const &a, shared_ptr const &b ) { return a.get() != b.get(); } //! Compare shared pointers template bool operator< ( shared_ptr const &a, shared_ptr const &b ) { return a.get() < b.get(); } /*! \brief A wrapper of shared_ptr which is never empty. T must have a default constructor. Note that shared_ptr's destructor isn't virtual - don't call delete on pointers to instances of this class. */ template class object_ptr : public shared_ptr { public: /*! \brief Default constructor Calls T's default constructor. */ object_ptr() : shared_ptr ( new T() ) { } /*! \brief Upcast from shared_ptr to object_ptr \param b The shared pointer, which must not be empty. */ object_ptr ( const shared_ptr &b ) : shared_ptr ( b ) { it_assert_debug ( this->get(), "object_ptr cannot be empty" ); } /*! Constructs an object_ptr that owns the pointer p. p must have been alllocated by new! */ object_ptr ( T *p ) : shared_ptr ( p ) { it_assert_debug ( p, "object_ptr cannot be empty" ); } object_ptr &operator= ( const object_ptr &other ) { shared_ptr::operator= ( other ); return *this; } }; #define SHAREDPTR(class_name) typedef bdm::object_ptr< class_name > class_name##_ptr #define SHAREDPTR2(template_class_name, template_parameter_name) typedef bdm::object_ptr< template_class_name < template_parameter_name > > template_class_name##_##template_parameter_name##_ptr } #endif