aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/ftw/qbipointer_p.h
blob: 1597b9e4fcd776187ab7fe10fa9cfc39b172f11d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QBIPOINTER_P_H
#define QBIPOINTER_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <QtCore/private/qglobal_p.h>

#include <QtCore/qhashfunctions.h>

QT_BEGIN_NAMESPACE

namespace QtPrivate {
template <typename T> struct QFlagPointerAlignment
{
    enum : size_t { Value = Q_ALIGNOF(T) };
};
template <> struct QFlagPointerAlignment<void>
{
    enum : size_t { Value = ~size_t(0) };
};
}

/*!
    \internal
    \class template<typename T1, typename T2> QBiPointer<T1, T2>

    \short QBiPointer can be thought of as a space-optimized std::variant<T1*, T2*>
    with a nicer API to check the active pointer. Its other main feature is that
    it only requires sizeof(void *) space.

    \note It can also store one additional flag for a user defined purpose.
 */
template<typename T, typename T2>
class QBiPointer {
public:
    Q_NODISCARD_CTOR constexpr QBiPointer() noexcept = default;
    ~QBiPointer() noexcept = default;
    Q_NODISCARD_CTOR QBiPointer(const QBiPointer &o) noexcept = default;
    Q_NODISCARD_CTOR QBiPointer(QBiPointer &&o) noexcept = default;
    QBiPointer<T, T2> &operator=(const QBiPointer<T, T2> &o) noexcept = default;
    QBiPointer<T, T2> &operator=(QBiPointer<T, T2> &&o) noexcept = default;

    void swap(QBiPointer &other) noexcept { std::swap(ptr_value, other.ptr_value); }

    Q_NODISCARD_CTOR inline QBiPointer(T *);
    Q_NODISCARD_CTOR inline QBiPointer(T2 *);

    inline bool isNull() const;
    inline bool isT1() const;
    inline bool isT2() const;

    inline bool flag() const;
    inline void setFlag();
    inline void clearFlag();
    inline void setFlagValue(bool);

    inline QBiPointer<T, T2> &operator=(T *);
    inline QBiPointer<T, T2> &operator=(T2 *);

    friend inline bool operator==(QBiPointer<T, T2> ptr1, QBiPointer<T, T2> ptr2)
    {
        if (ptr1.isNull() && ptr2.isNull())
            return true;
        if (ptr1.isT1() && ptr2.isT1())
            return ptr1.asT1() == ptr2.asT1();
        if (ptr1.isT2() && ptr2.isT2())
            return ptr1.asT2() == ptr2.asT2();
        return false;
    }
    friend inline bool operator!=(QBiPointer<T, T2> ptr1, QBiPointer<T, T2> ptr2)
    {
        return !(ptr1 == ptr2);
    }

    friend void swap(QBiPointer &lhs, QBiPointer &rhs) noexcept { lhs.swap(rhs); }

    inline T *asT1() const;
    inline T2 *asT2() const;

    friend size_t qHash(const QBiPointer<T, T2> &ptr, size_t seed = 0)
    {
        return qHash(ptr.isNull() ? quintptr(0) : ptr.ptr_value, seed);
    }

private:
    quintptr ptr_value = 0;

    static const quintptr FlagBit = 0x1;
    static const quintptr Flag2Bit = 0x2;
    static const quintptr FlagsMask = FlagBit | Flag2Bit;
};

template <typename...Ts> // can't use commas in macros
Q_DECLARE_TYPEINFO_BODY(QBiPointer<Ts...>, Q_PRIMITIVE_TYPE);

template<typename T, typename T2>
QBiPointer<T, T2>::QBiPointer(T *v)
: ptr_value(quintptr(v))
{
    Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T>::Value >= 4,
                      "Type T does not have sufficient alignment");
    Q_ASSERT((quintptr(v) & FlagsMask) == 0);
}

template<typename T, typename T2>
QBiPointer<T, T2>::QBiPointer(T2 *v)
: ptr_value(quintptr(v) | Flag2Bit)
{
    Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment<T2>::Value >= 4,
                      "Type T2 does not have sufficient alignment");
    Q_ASSERT((quintptr(v) & FlagsMask) == 0);
}

template<typename T, typename T2>
bool QBiPointer<T, T2>::isNull() const
{
    return 0 == (ptr_value & (~FlagsMask));
}

template<typename T, typename T2>
bool QBiPointer<T, T2>::isT1() const
{
    return !(ptr_value & Flag2Bit);
}

template<typename T, typename T2>
bool QBiPointer<T, T2>::isT2() const
{
    return ptr_value & Flag2Bit;
}

template<typename T, typename T2>
bool QBiPointer<T, T2>::flag() const
{
    return ptr_value & FlagBit;
}

template<typename T, typename T2>
void QBiPointer<T, T2>::setFlag()
{
    ptr_value |= FlagBit;
}

template<typename T, typename T2>
void QBiPointer<T, T2>::clearFlag()
{
    ptr_value &= ~FlagBit;
}

template<typename T, typename T2>
void QBiPointer<T, T2>::setFlagValue(bool v)
{
    if (v) setFlag();
    else clearFlag();
}

template<typename T, typename T2>
QBiPointer<T, T2> &QBiPointer<T, T2>::operator=(T *o)
{
    Q_ASSERT((quintptr(o) & FlagsMask) == 0);

    ptr_value = quintptr(o) | (ptr_value & FlagBit);
    return *this;
}

template<typename T, typename T2>
QBiPointer<T, T2> &QBiPointer<T, T2>::operator=(T2 *o)
{
    Q_ASSERT((quintptr(o) & FlagsMask) == 0);

    ptr_value = quintptr(o) | (ptr_value & FlagBit) | Flag2Bit;
    return *this;
}

template<typename T, typename T2>
T *QBiPointer<T, T2>::asT1() const
{
    Q_ASSERT(isT1());
    return (T *)(ptr_value & ~FlagsMask);
}

template<typename T, typename T2>
T2 *QBiPointer<T, T2>::asT2() const
{
    Q_ASSERT(isT2());
    return (T2 *)(ptr_value & ~FlagsMask);
}

QT_END_NAMESPACE

#endif // QBIPOINTER_P_H