#pragma once #include #include #include "util/assert.h" // Use this wrapper class to clearly represent a raw pointer that is owned by the QT object tree. // Objects which both derive from QObject AND have a parent object, have their lifetime governed by // the QT object tree, and thus such pointers do not require a manual delete to free the heap memory // when they go out of scope. // // NOTE: A parented_ptr must not dangle! Therefore, the lifetime of the parent must exceed the // lifetime of the parented_ptr. template class parented_ptr final { public: parented_ptr() noexcept : m_ptr{nullptr} { } parented_ptr(std::nullptr_t) noexcept : m_ptr{nullptr} { } explicit parented_ptr(T* t) noexcept : m_ptr{t} { } ~parented_ptr() noexcept { DEBUG_ASSERT(!m_ptr || static_cast(m_ptr)->parent()); } // Delete copy constructor and copy assignment operator parented_ptr(const parented_ptr&) = delete; parented_ptr& operator=(const parented_ptr&) = delete; // If U* is convertible to T* then parented_ptr is convertible to parented_ptr template< typename U, typename = typename std::enable_if::value, U>::type> parented_ptr(parented_ptr&& u) noexcept : m_ptr{u.m_ptr} { u.m_ptr = nullptr; } // If U* is convertible to T* then parented_ptr is assignable to parented_ptr template< typename U, typename = typename std::enable_if::value, U>::type> parented_ptr& operator=(parented_ptr&& u) noexcept { parented_ptr temp{std::move(u)}; std::swap(temp.m_ptr, m_ptr); return *this; } parented_ptr& operator=(std::nullptr_t) noexcept { parented_ptr{std::move(*this)}; // move *this into a temporary that gets destructed return *this; } // Prevent unintended invocation of delete on parented_ptr operator void*() const = delete; operator T*() const noexcept { return m_ptr; } T* get() const noexcept { return m_ptr; } T& operator*() const noexcept { return *m_ptr; } T* operator->() const noexcept { return m_ptr; } operator bool() const noexcept { return m_ptr != nullptr; } QPointer toWeakRef() { return m_ptr; } private: T* m_ptr; template friend class parented_ptr; }; template inline parented_ptr make_parented(Args&&... args) { return parented_ptr(new T(std::forward(args)...)); } // A use case for this function is when giving an object owned by `std::unique_ptr` to a Qt // function, that will make the object owned by the Qt object tree. Example: // ``` // parent->someFunctionThatAddsAChild(to_parented(child)) // ``` // where `child` is a `std::unique_ptr`. After the call, the created `parented_ptr` will // automatically be destructed such that the DEBUG_ASSERT that checks whether a parent exists is // triggered. template inline parented_ptr to_parented(std::unique_ptr& u) noexcept { // the DEBUG_ASSERT in the parented_ptr constructor will catch cases where the unique_ptr should // not have been released return parented_ptr{u.release()}; } // Comparison operator definitions: template inline bool operator==(const T* lhs, const parented_ptr& rhs) noexcept { return lhs == rhs.get(); } template inline bool operator==(const parented_ptr& lhs, const U* rhs) noexcept { return lhs.get() == rhs; } template inline bool operator==(const parented_ptr& lhs, const parented_ptr& rhs) noexcept { return lhs.get() == rhs.get(); } template inline bool operator!=(const T* lhs, const parented_ptr& rhs) noexcept { return !(lhs == rhs.get()); } template inline bool operator!=(const parented_ptr& lhs, const U* rhs) noexcept { return !(lhs.get() == rhs); } template inline bool operator!=(const parented_ptr& lhs, const parented_ptr& rhs) noexcept { return !(lhs.get() == rhs.get()); }