// 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 #include QT_BEGIN_NAMESPACE namespace QtPrivate { template struct QFlagPointerAlignment { enum : size_t { Value = Q_ALIGNOF(T) }; }; template <> struct QFlagPointerAlignment { enum : size_t { Value = ~size_t(0) }; }; } /*! \internal \class template QBiPointer \short QBiPointer can be thought of as a space-optimized std::variant 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 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 &operator=(const QBiPointer &o) noexcept = default; QBiPointer &operator=(QBiPointer &&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 &operator=(T *); inline QBiPointer &operator=(T2 *); friend inline bool operator==(QBiPointer ptr1, QBiPointer 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 ptr1, QBiPointer 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 &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 // can't use commas in macros Q_DECLARE_TYPEINFO_BODY(QBiPointer, Q_PRIMITIVE_TYPE); template QBiPointer::QBiPointer(T *v) : ptr_value(quintptr(v)) { Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment::Value >= 4, "Type T does not have sufficient alignment"); Q_ASSERT((quintptr(v) & FlagsMask) == 0); } template QBiPointer::QBiPointer(T2 *v) : ptr_value(quintptr(v) | Flag2Bit) { Q_STATIC_ASSERT_X(QtPrivate::QFlagPointerAlignment::Value >= 4, "Type T2 does not have sufficient alignment"); Q_ASSERT((quintptr(v) & FlagsMask) == 0); } template bool QBiPointer::isNull() const { return 0 == (ptr_value & (~FlagsMask)); } template bool QBiPointer::isT1() const { return !(ptr_value & Flag2Bit); } template bool QBiPointer::isT2() const { return ptr_value & Flag2Bit; } template bool QBiPointer::flag() const { return ptr_value & FlagBit; } template void QBiPointer::setFlag() { ptr_value |= FlagBit; } template void QBiPointer::clearFlag() { ptr_value &= ~FlagBit; } template void QBiPointer::setFlagValue(bool v) { if (v) setFlag(); else clearFlag(); } template QBiPointer &QBiPointer::operator=(T *o) { Q_ASSERT((quintptr(o) & FlagsMask) == 0); ptr_value = quintptr(o) | (ptr_value & FlagBit); return *this; } template QBiPointer &QBiPointer::operator=(T2 *o) { Q_ASSERT((quintptr(o) & FlagsMask) == 0); ptr_value = quintptr(o) | (ptr_value & FlagBit) | Flag2Bit; return *this; } template T *QBiPointer::asT1() const { Q_ASSERT(isT1()); return (T *)(ptr_value & ~FlagsMask); } template T2 *QBiPointer::asT2() const { Q_ASSERT(isT2()); return (T2 *)(ptr_value & ~FlagsMask); } QT_END_NAMESPACE #endif // QBIPOINTER_P_H