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