1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QBIPOINTER_P_H |
5 | #define QBIPOINTER_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QtCore/private/qglobal_p.h> |
19 | |
20 | #include <QtCore/qhashfunctions.h> |
21 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | namespace QtPrivate { |
25 | template <typename T> struct QFlagPointerAlignment |
26 | { |
27 | enum : size_t { Value = Q_ALIGNOF(T) }; |
28 | }; |
29 | template <> struct QFlagPointerAlignment<void> |
30 | { |
31 | enum : size_t { Value = ~size_t(0) }; |
32 | }; |
33 | } |
34 | |
35 | /*! |
36 | \internal |
37 | \class template<typename T1, typename T2> QBiPointer<T1, T2> |
38 | |
39 | \short QBiPointer can be thought of as a space-optimized std::variant<T1*, T2*> |
40 | with a nicer API to check the active pointer. Its other main feature is that |
41 | it only requires sizeof(void *) space. |
42 | |
43 | \note It can also store one additional flag for a user defined purpose. |
44 | */ |
45 | template<typename T, typename T2> |
46 | class QBiPointer { |
47 | public: |
48 | Q_NODISCARD_CTOR constexpr QBiPointer() noexcept = default; |
49 | ~QBiPointer() noexcept = default; |
50 | Q_NODISCARD_CTOR QBiPointer(const QBiPointer &o) noexcept = default; |
51 | Q_NODISCARD_CTOR QBiPointer(QBiPointer &&o) noexcept = default; |
52 | QBiPointer<T, T2> &operator=(const QBiPointer<T, T2> &o) noexcept = default; |
53 | QBiPointer<T, T2> &operator=(QBiPointer<T, T2> &&o) noexcept = default; |
54 | |
55 | void swap(QBiPointer &other) noexcept { std::swap(ptr_value, other.ptr_value); } |
56 | |
57 | Q_NODISCARD_CTOR inline QBiPointer(T *); |
58 | Q_NODISCARD_CTOR inline QBiPointer(T2 *); |
59 | |
60 | inline bool isNull() const; |
61 | inline bool isT1() const; |
62 | inline bool isT2() const; |
63 | |
64 | inline bool flag() const; |
65 | inline void setFlag(); |
66 | inline void clearFlag(); |
67 | inline void setFlagValue(bool); |
68 | |
69 | inline QBiPointer<T, T2> &operator=(T *); |
70 | inline QBiPointer<T, T2> &operator=(T2 *); |
71 | |
72 | friend inline bool operator==(QBiPointer<T, T2> ptr1, QBiPointer<T, T2> ptr2) |
73 | { |
74 | if (ptr1.isNull() && ptr2.isNull()) |
75 | return true; |
76 | if (ptr1.isT1() && ptr2.isT1()) |
77 | return ptr1.asT1() == ptr2.asT1(); |
78 | if (ptr1.isT2() && ptr2.isT2()) |
79 | return ptr1.asT2() == ptr2.asT2(); |
80 | return false; |
81 | } |
82 | friend inline bool operator!=(QBiPointer<T, T2> ptr1, QBiPointer<T, T2> ptr2) |
83 | { |
84 | return !(ptr1 == ptr2); |
85 | } |
86 | |
87 | friend void swap(QBiPointer &lhs, QBiPointer &rhs) noexcept { lhs.swap(rhs); } |
88 | |
89 | inline T *asT1() const; |
90 | inline T2 *asT2() const; |
91 | |
92 | friend size_t qHash(const QBiPointer<T, T2> &ptr, size_t seed = 0) |
93 | { |
94 | return qHash(ptr.isNull() ? quintptr(0) : ptr.ptr_value, seed); |
95 | } |
96 | |
97 | private: |
98 | quintptr ptr_value = 0; |
99 | |
100 | static const quintptr FlagBit = 0x1; |
101 | static const quintptr Flag2Bit = 0x2; |
102 | static const quintptr FlagsMask = FlagBit | Flag2Bit; |
103 | }; |
104 | |
105 | template <typename...Ts> // can't use commas in macros |
106 | Q_DECLARE_TYPEINFO_BODY(QBiPointer<Ts...>, Q_PRIMITIVE_TYPE); |
107 | |
108 | template<typename T, typename T2> |
109 | QBiPointer<T, T2>::QBiPointer(T *v) |
110 | : ptr_value(quintptr(v)) |
111 | { |
112 | Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T>::Value >= 4, |
113 | "Type T does not have sufficient alignment" ); |
114 | Q_ASSERT((quintptr(v) & FlagsMask) == 0); |
115 | } |
116 | |
117 | template<typename T, typename T2> |
118 | QBiPointer<T, T2>::QBiPointer(T2 *v) |
119 | : ptr_value(quintptr(v) | Flag2Bit) |
120 | { |
121 | Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T2>::Value >= 4, |
122 | "Type T2 does not have sufficient alignment" ); |
123 | Q_ASSERT((quintptr(v) & FlagsMask) == 0); |
124 | } |
125 | |
126 | template<typename T, typename T2> |
127 | bool QBiPointer<T, T2>::isNull() const |
128 | { |
129 | return 0 == (ptr_value & (~FlagsMask)); |
130 | } |
131 | |
132 | template<typename T, typename T2> |
133 | bool QBiPointer<T, T2>::isT1() const |
134 | { |
135 | return !(ptr_value & Flag2Bit); |
136 | } |
137 | |
138 | template<typename T, typename T2> |
139 | bool QBiPointer<T, T2>::isT2() const |
140 | { |
141 | return ptr_value & Flag2Bit; |
142 | } |
143 | |
144 | template<typename T, typename T2> |
145 | bool QBiPointer<T, T2>::flag() const |
146 | { |
147 | return ptr_value & FlagBit; |
148 | } |
149 | |
150 | template<typename T, typename T2> |
151 | void QBiPointer<T, T2>::setFlag() |
152 | { |
153 | ptr_value |= FlagBit; |
154 | } |
155 | |
156 | template<typename T, typename T2> |
157 | void QBiPointer<T, T2>::clearFlag() |
158 | { |
159 | ptr_value &= ~FlagBit; |
160 | } |
161 | |
162 | template<typename T, typename T2> |
163 | void QBiPointer<T, T2>::setFlagValue(bool v) |
164 | { |
165 | if (v) setFlag(); |
166 | else clearFlag(); |
167 | } |
168 | |
169 | template<typename T, typename T2> |
170 | QBiPointer<T, T2> &QBiPointer<T, T2>::operator=(T *o) |
171 | { |
172 | Q_ASSERT((quintptr(o) & FlagsMask) == 0); |
173 | |
174 | ptr_value = quintptr(o) | (ptr_value & FlagBit); |
175 | return *this; |
176 | } |
177 | |
178 | template<typename T, typename T2> |
179 | QBiPointer<T, T2> &QBiPointer<T, T2>::operator=(T2 *o) |
180 | { |
181 | Q_ASSERT((quintptr(o) & FlagsMask) == 0); |
182 | |
183 | ptr_value = quintptr(o) | (ptr_value & FlagBit) | Flag2Bit; |
184 | return *this; |
185 | } |
186 | |
187 | template<typename T, typename T2> |
188 | T *QBiPointer<T, T2>::asT1() const |
189 | { |
190 | Q_ASSERT(isT1()); |
191 | return (T *)(ptr_value & ~FlagsMask); |
192 | } |
193 | |
194 | template<typename T, typename T2> |
195 | T2 *QBiPointer<T, T2>::asT2() const |
196 | { |
197 | Q_ASSERT(isT2()); |
198 | return (T2 *)(ptr_value & ~FlagsMask); |
199 | } |
200 | |
201 | QT_END_NAMESPACE |
202 | |
203 | #endif // QBIPOINTER_P_H |
204 | |