#pragma once #include #include namespace CesiumUtility { /** * @brief A smart pointer that calls `addReference` and `releaseReference` on * the controlled object. * * Please note that the thread-safety of this type is entirely dependent on the * implementation of `addReference` and `releaseReference`. If these methods are * not thread safe on a particular type - which is common for objects that are * not meant to be used from multiple threads simultaneously - then using an * `IntrusivePointer` from multiple threads is also unsafe. * * @tparam T The type of object controlled. */ template class IntrusivePointer final { public: /** * @brief Default constructor. */ IntrusivePointer(T* p = nullptr) noexcept : _p(p) { this->addReference(); } /** * @brief Copy constructor. */ IntrusivePointer(const IntrusivePointer& rhs) noexcept : _p(rhs._p) { this->addReference(); } /** * @brief Implicit conversion to a pointer to a base (or otherwise * convertible) type. * * @tparam U The new type, usually a base class. * @param rhs The pointer. */ template IntrusivePointer(const IntrusivePointer& rhs) noexcept : _p(rhs._p) { this->addReference(); } /** * @brief Move constructor. */ IntrusivePointer(IntrusivePointer&& rhs) noexcept : _p(std::exchange(rhs._p, nullptr)) { // Reference count is unchanged } /** * @brief Implicit conversion of an r-value to a pointer to a base (or * otherwise convertible) type. * * @tparam U The new type, usually a base class. * @param rhs The pointer. */ template IntrusivePointer(IntrusivePointer&& rhs) noexcept : _p(std::exchange(rhs._p, nullptr)) { // Reference count is unchanged } /** * @brief Default destructor. */ ~IntrusivePointer() noexcept { this->releaseReference(); } /** * @brief Constructs a new instance and assigns it to this IntrusivePointer. * If this IntrusivePointer already points to another instance, * {@link releaseReference} is called on it. * * @param constructorArguments The arguments to the constructor to create the * new instance. * @return A reference to the newly-created instance. */ template T& emplace(ConstructorArgumentTypes&&... constructorArguments) { *this = new T(std::forward(constructorArguments)...); return *this->get(); } /** * @brief Reset this pointer to nullptr. */ void reset() { *this = nullptr; } /** * @brief Assignment operator. */ IntrusivePointer& operator=(const IntrusivePointer& rhs) noexcept { if (this->_p != rhs._p) { // addReference the new pointer before releaseReference'ing the old. T* pOld = this->_p; this->_p = rhs._p; addReference(); if (pOld) { pOld->releaseReference(); } } return *this; } /** * @brief Assigns an \ref IntrusivePointer of another type to this \ref * IntrusivePointer. */ template IntrusivePointer& operator=(const IntrusivePointer& rhs) noexcept { if (this->_p != rhs._p) { // addReference the new pointer before releaseReference'ing the old. T* pOld = this->_p; this->_p = rhs._p; addReference(); if (pOld) { pOld->releaseReference(); } } return *this; } /** * @brief Move assignment operator. */ IntrusivePointer& operator=(IntrusivePointer&& rhs) noexcept { if (this->_p != rhs._p) { std::swap(this->_p, rhs._p); } return *this; } /** * @brief Assignment operator. */ IntrusivePointer& operator=(T* p) noexcept { if (this->_p != p) { // addReference the new pointer before releaseReference'ing the old. T* pOld = this->_p; this->_p = p; addReference(); if (pOld) { pOld->releaseReference(); } } return *this; } /** * @brief Dereferencing operator. */ T& operator*() const noexcept { return *this->_p; } /** * @brief Arrow operator. */ T* operator->() const noexcept { return this->_p; } /** * @brief Implicit conversion to `bool`, being `true` iff this is not the * `nullptr`. */ explicit operator bool() const noexcept { return this->_p != nullptr; } /** * @brief Returns the internal pointer. */ T* get() const noexcept { return this->_p; } /** * @brief Returns `true` if two pointers are equal. */ bool operator==(const IntrusivePointer& rhs) const noexcept { return this->_p == rhs._p; } /** @brief Returns `true` if two pointers are equal. */ template bool operator==(const IntrusivePointer& rhs) const noexcept { return this->_p == rhs._p; } /** * @brief Returns `true` if two pointers are *not* equal. */ bool operator!=(const IntrusivePointer& rhs) const noexcept { return !(*this == rhs); } /** * @brief Returns `true` if the contents of this pointer is equal to the given * pointer. */ bool operator==(const T* pRhs) const noexcept { return this->_p == pRhs; } /** * @brief Returns `true` if the contents of this pointer is *not* equal to the * given pointer. */ bool operator!=(const T* pRhs) const noexcept { return !(*this == pRhs); } private: void addReference() noexcept { if (this->_p) { this->_p->addReference(); } } void releaseReference() noexcept { if (this->_p) { this->_p->releaseReference(); } } T* _p; template friend class IntrusivePointer; }; /** * @brief Casts a `const` \ref IntrusivePointer to its non-const equivalent. * * @param p The `const` \ref IntrusivePointer. * @returns A non-const \ref IntrusivePointer with the same underlying pointer. */ template IntrusivePointer const_intrusive_cast(const IntrusivePointer& p) noexcept { return IntrusivePointer(const_cast(p.get())); } } // namespace CesiumUtility