1 #pragma once
2 
3 #include <QPointer>
4 #include <memory>
5 
6 #include "util/assert.h"
7 
8 // Use this wrapper class to clearly represent a raw pointer that is owned by the QT object tree.
9 // Objects which both derive from QObject AND have a parent object, have their lifetime governed by
10 // the QT object tree, and thus such pointers do not require a manual delete to free the heap memory
11 // when they go out of scope.
12 //
13 // NOTE: A parented_ptr must not dangle! Therefore, the lifetime of the parent must exceed the
14 // lifetime of the parented_ptr.
15 template<typename T>
16 class parented_ptr final {
17   public:
parented_ptr()18     parented_ptr() noexcept
19             : m_ptr{nullptr} {
20     }
21 
parented_ptr(std::nullptr_t)22     parented_ptr(std::nullptr_t) noexcept
23             : m_ptr{nullptr} {
24     }
25 
parented_ptr(T * t)26     explicit parented_ptr(T* t) noexcept
27             : m_ptr{t} {
28     }
29 
~parented_ptr()30     ~parented_ptr() noexcept {
31         DEBUG_ASSERT(!m_ptr || m_ptr->parent());
32     }
33 
34     // Delete copy constructor and copy assignment operator
35     parented_ptr(const parented_ptr<T>&) = delete;
36     parented_ptr& operator=(const parented_ptr<T>&) = delete;
37 
38     // If U* is convertible to T* then parented_ptr<U> is convertible to parented_ptr<T>
39     template<
40             typename U,
41             typename = typename std::enable_if<std::is_convertible<U*, T*>::value, U>::type>
parented_ptr(parented_ptr<U> && u)42     parented_ptr(parented_ptr<U>&& u) noexcept
43             : m_ptr{u.m_ptr} {
44         u.m_ptr = nullptr;
45     }
46 
47     // If U* is convertible to T* then parented_ptr<U> is assignable to parented_ptr<T>
48     template<
49             typename U,
50             typename = typename std::enable_if<std::is_convertible<U*, T*>::value, U>::type>
51     parented_ptr& operator=(parented_ptr<U>&& u) noexcept {
52         parented_ptr temp{std::move(u)};
53         std::swap(temp.m_ptr, m_ptr);
54         return *this;
55     }
56 
57     parented_ptr& operator=(std::nullptr_t) noexcept {
58         parented_ptr{std::move(*this)}; // move *this into a temporary that gets destructed
59         return *this;
60     }
61 
62     // Prevent unintended invocation of delete on parented_ptr
63     operator void*() const = delete;
64 
65     operator T*() const noexcept {
66         return m_ptr;
67     }
68 
get()69     T* get() const noexcept {
70         return m_ptr;
71     }
72 
73     T& operator*() const noexcept {
74         return *m_ptr;
75     }
76 
77     T* operator->() const noexcept {
78         return m_ptr;
79     }
80 
81     operator bool() const noexcept {
82         return m_ptr != nullptr;
83     }
84 
toWeakRef()85     QPointer<T> toWeakRef() {
86         return m_ptr;
87     }
88 
89   private:
90     T* m_ptr;
91 
92     template<typename>
93     friend class parented_ptr;
94 };
95 
96 template<typename T, typename... Args>
make_parented(Args &&...args)97 inline parented_ptr<T> make_parented(Args&&... args) {
98     return parented_ptr<T>(new T(std::forward<Args>(args)...));
99 }
100 
101 // A use case for this function is when giving an object owned by `std::unique_ptr` to a Qt
102 // function, that will make the object owned by the Qt object tree. Example:
103 // ```
104 // parent->someFunctionThatAddsAChild(to_parented(child))
105 // ```
106 // where `child` is a `std::unique_ptr`. After the call, the created `parented_ptr` will
107 // automatically be destructed such that the DEBUG_ASSERT that checks whether a parent exists is
108 // triggered.
109 template<typename T>
to_parented(std::unique_ptr<T> & u)110 inline parented_ptr<T> to_parented(std::unique_ptr<T>& u) noexcept {
111     // the DEBUG_ASSERT in the parented_ptr constructor will catch cases where the unique_ptr should
112     // not have been released
113     return parented_ptr<T>{u.release()};
114 }
115 
116 // Comparison operator definitions:
117 
118 template<typename T, typename U>
119 inline bool operator==(const T* lhs, const parented_ptr<U>& rhs) noexcept {
120     return lhs == rhs.get();
121 }
122 
123 template<typename T, typename U>
124 inline bool operator==(const parented_ptr<T>& lhs, const U* rhs) noexcept {
125     return lhs.get() == rhs;
126 }
127 
128 template<typename T, typename U>
129 inline bool operator==(const parented_ptr<T>& lhs, const parented_ptr<U>& rhs) noexcept {
130     return lhs.get() == rhs.get();
131 }
132 
133 template<typename T, typename U>
134 inline bool operator!=(const T* lhs, const parented_ptr<U>& rhs) noexcept {
135     return !(lhs == rhs.get());
136 }
137 
138 template<typename T, typename U>
139 inline bool operator!=(const parented_ptr<T>& lhs, const U* rhs) noexcept {
140     return !(lhs.get() == rhs);
141 }
142 
143 template<typename T, typename U>
144 inline bool operator!=(const parented_ptr<T>& lhs, const parented_ptr<U>& rhs) noexcept {
145     return !(lhs.get() == rhs.get());
146 }
147