#ifndef __REFCOUNT_H__ #define __REFCOUNT_H__ #include #include // Template class to refcount objects with COW semantics. // This class is thread-safe under the following assumptions: // - The object stored in it must be thread-safe for reading // - Any instance Get() is called on must never be used in // different threads. template class CRefcountObject final { public: CRefcountObject(); CRefcountObject(CRefcountObject const& v) = default; CRefcountObject(CRefcountObject && v) noexcept = default; explicit CRefcountObject(const T& v); void clear(); T& Get(); const T& operator*() const; const T* operator->() const; // Comparison operators are deep. If two intances point to // different objects, those objects are compared bool operator==(CRefcountObject const& cmp) const; bool operator==(T const& cmp) const; bool operator<(CRefcountObject const& cmp) const; bool operator<(T const& cmp) const; inline bool operator!=(const CRefcountObject& cmp) const { return !(*this == cmp); } CRefcountObject& operator=(CRefcountObject const& v) = default; CRefcountObject& operator=(CRefcountObject && v) noexcept = default; protected: std::shared_ptr data_; }; template class CRefcountObject_Uninitialized final { /* Almost same as CRefcountObject but does not allocate an object initially. You need to ensure to assign some data prior to calling operator* or ->, otherwise you'll dereference the null-pointer. */ public: CRefcountObject_Uninitialized() = default; CRefcountObject_Uninitialized(CRefcountObject_Uninitialized const& v) = default; CRefcountObject_Uninitialized(CRefcountObject_Uninitialized && v) noexcept = default; explicit CRefcountObject_Uninitialized(T const& v); void clear(); T& Get(); const T& operator*() const; const T* operator->() const; bool operator==(const CRefcountObject_Uninitialized& cmp) const; inline bool operator!=(const CRefcountObject_Uninitialized& cmp) const { return !(*this == cmp); } bool operator<(const CRefcountObject_Uninitialized& cmp) const; CRefcountObject_Uninitialized& operator=(CRefcountObject_Uninitialized const& v) = default; CRefcountObject_Uninitialized& operator=(CRefcountObject_Uninitialized && v) noexcept = default; bool operator!() const { return !data_; } explicit operator bool() const { return data_; } bool empty() const { return data_.get(); } protected: std::shared_ptr data_; }; template bool CRefcountObject::operator==(CRefcountObject const& cmp) const { if (data_ == cmp.data_) return true; return *data_ == *cmp.data_; } template bool CRefcountObject::operator==(T const& cmp) const { return *data_ == cmp; } template CRefcountObject::CRefcountObject() : data_(std::make_shared()) { } template CRefcountObject::CRefcountObject(const T& v) : data_(std::make_shared(v)) { } template T& CRefcountObject::Get() { if (!data_.unique()) { data_ = std::make_shared(*data_); } return *data_.get(); } template bool CRefcountObject::operator<(CRefcountObject const& cmp) const { if (data_ == cmp.data_) return false; return *data_.get() < *cmp.data_.get(); } template bool CRefcountObject::operator<(T const& cmp) const { if (!data_) { return true; } return *data_.get() < cmp; } template void CRefcountObject::clear() { if (data_.unique()) { *data_.get() = T(); } else { data_ = std::make_shared(); } } template const T& CRefcountObject::operator*() const { return *data_; } template const T* CRefcountObject::operator->() const { return data_.get(); } // The same for the uninitialized version template bool CRefcountObject_Uninitialized::operator==(const CRefcountObject_Uninitialized& cmp) const { if (data_ == cmp.data_) return true; if (!data_) { return !cmp.data_; } else if (!cmp.data_) { return false; } return *data_.get() == *cmp.data_.get(); } template CRefcountObject_Uninitialized::CRefcountObject_Uninitialized(T const& v) : data_(std::make_shared(v)) { } template T& CRefcountObject_Uninitialized::Get() { if (!data_) { data_ = std::make_shared(); } else if (!data_.unique()) { data_ = std::make_shared(*data_); } return *data_.get(); } template bool CRefcountObject_Uninitialized::operator<(const CRefcountObject_Uninitialized& cmp) const { if (data_ == cmp.data_) { return false; } else if (!data_) { return cmp.data_; } else if (!cmp.data_) { return false; } return *data_.get() < *cmp.data_.get(); } template void CRefcountObject_Uninitialized::clear() { data_.reset(); } template const T& CRefcountObject_Uninitialized::operator*() const { return *data_.get(); } template const T* CRefcountObject_Uninitialized::operator->() const { return data_.get(); } #endif //__REFCOUNT_H__