diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2017-05-02 19:23:13 +0200 |
---|---|---|
committer | Fabian Kosmale <fabian.kosmale@qt.io> | 2020-03-17 10:01:04 +0100 |
commit | 165de1002719175c75df61913d36c2189538a84b (patch) | |
tree | 39f3be146633ad2f25b1a55d000edb1475149082 | |
parent | 7ef382649754c261ca9eb99dd50b67050e748efb (diff) |
Introduce helper class QTaggedPointer
Useful for attaching small bits of information in the alignment bits of
a naked pointer. For use in the new property system as well as in
qtdeclarative (where currently a similar class exists as private API).
Change-Id: Idf9b93e714e15129f302e16425dbeda94bcd207b
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r-- | src/corelib/.prev_CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/io/qdebug.h | 10 | ||||
-rw-r--r-- | src/corelib/tools/qtaggedpointer.h | 207 | ||||
-rw-r--r-- | src/corelib/tools/tools.pri | 1 | ||||
-rw-r--r-- | tests/auto/corelib/tools/.prev_CMakeLists.txt | 45 | ||||
-rw-r--r-- | tests/auto/corelib/tools/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qtaggedpointer/.gitignore | 1 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt | 12 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qtaggedpointer/qtaggedpointer.pro | 4 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp | 400 | ||||
-rw-r--r-- | tests/auto/corelib/tools/tools.pro | 1 |
12 files changed, 684 insertions, 0 deletions
diff --git a/src/corelib/.prev_CMakeLists.txt b/src/corelib/.prev_CMakeLists.txt index 28882a93aa..d60945cee3 100644 --- a/src/corelib/.prev_CMakeLists.txt +++ b/src/corelib/.prev_CMakeLists.txt @@ -214,6 +214,7 @@ qt_add_module(Core tools/qsimd.cpp tools/qsimd_p.h tools/qsize.cpp tools/qsize.h tools/qstack.h + tools/qtaggedpointer_p.h tools/qtools_p.h tools/qvarlengtharray.h tools/qvector.h diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 0435a7a4fc..e28df10290 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -230,6 +230,7 @@ qt_add_module(Core tools/qsimd.cpp tools/qsimd_p.h tools/qsize.cpp tools/qsize.h tools/qstack.h + tools/qtaggedpointer.h tools/qtools_p.h tools/qvarlengtharray.h tools/qvector.h diff --git a/src/corelib/io/qdebug.h b/src/corelib/io/qdebug.h index e14d31f38a..aa69a1ae23 100644 --- a/src/corelib/io/qdebug.h +++ b/src/corelib/io/qdebug.h @@ -356,6 +356,16 @@ inline QDebug operator<<(QDebug debug, const QSharedPointer<T> &ptr) return debug; } +template <typename T, typename Tag> class QTaggedPointer; + +template <typename T, typename Tag> +inline QDebug operator<<(QDebug debug, const QTaggedPointer<T, Tag> &ptr) +{ + QDebugStateSaver saver(debug); + debug.nospace() << "QTaggedPointer(" << ptr.pointer() << ", " << ptr.tag() << ")"; + return debug; +} + Q_CORE_EXPORT void qt_QMetaEnum_flagDebugOperator(QDebug &debug, size_t sizeofT, int value); template <typename Int> diff --git a/src/corelib/tools/qtaggedpointer.h b/src/corelib/tools/qtaggedpointer.h new file mode 100644 index 0000000000..7d631a5e0c --- /dev/null +++ b/src/corelib/tools/qtaggedpointer.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTAGGEDPOINTER_H +#define QTAGGEDPOINTER_H + +#include <QtCore/qglobal.h> +#include <QtCore/qalgorithms.h> +#include <QtCore/qmath.h> + +#include <limits.h> + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { + constexpr quint8 nextByteSize(quint8 bits) { return (bits + 7) / 8; } + + template <typename T> + struct TagInfo + { + static constexpr size_t alignment = alignof(T); + static_assert((alignment & (alignment - 1)) == 0, + "Alignment of template parameter must be power of two"); + + static constexpr quint8 tagBits = QtPrivate::qConstexprCountTrailingZeroBits(alignment); + static_assert(tagBits > 0, + "Alignment of template parameter does not allow any tags"); + + static constexpr size_t tagSize = QtPrivate::qConstexprNextPowerOfTwo(nextByteSize(tagBits)); + static_assert(tagSize < sizeof(quintptr), + "Alignment of template parameter allows tags masking away pointer"); + + using TagType = typename QIntegerForSize<tagSize>::Unsigned; + }; +} + +template <typename T, typename Tag = typename QtPrivate::TagInfo<T>::TagType> +class QTaggedPointer +{ + static constexpr quintptr tagMask = QtPrivate::TagInfo<T>::alignment - 1; + static constexpr quintptr pointerMask = ~tagMask; + + using TagInternalType = typename QtPrivate::TagInfo<T>::TagType; + +public: + using Type = T; + using TagType = Tag; + + explicit QTaggedPointer(Type *pointer = nullptr, TagType tag = TagType()) noexcept + : d(quintptr(pointer)) + { + Q_STATIC_ASSERT(sizeof(Type*) == sizeof(QTaggedPointer<Type>)); + + Q_ASSERT_X((quintptr(pointer) & tagMask) == 0, + "QTaggedPointer<T, Tag>", "Pointer is not aligned"); + + setTag(tag); + } + + Type &operator*() const + { + Q_ASSERT(pointer()); + return *pointer(); + } + + Type *operator->() const noexcept + { + return pointer(); + } + + explicit operator bool() const + { + return !isNull(); + } + + QTaggedPointer<T, Tag> &operator=(T *other) noexcept + { + d = reinterpret_cast<quintptr>(other) | (d & tagMask); + return *this; + } + + QTaggedPointer<T, Tag> &operator=(std::nullptr_t) noexcept + { + d &= tagMask; + return *this; + } + + static constexpr TagType maximumTag() noexcept + { + return TagType(TagInternalType(tagMask)); + } + + void setTag(TagType tag) + { + Q_ASSERT_X((static_cast<TagInternalType>(tag) & pointerMask) == 0, + "QTaggedPointer<T, Tag>::setTag", "Tag is larger than allowed by number of available tag bits"); + + d = (d & pointerMask) | (static_cast<TagInternalType>(tag) & tagMask); + } + + TagType tag() const noexcept + { + return TagType(TagInternalType(d & tagMask)); + } + + Type* pointer() const noexcept + { + return reinterpret_cast<T*>(d & pointerMask); + } + + bool isNull() const noexcept + { + return !pointer(); + } + + void swap(QTaggedPointer<Type, Tag> &other) noexcept + { + qSwap(d, other.d); + } + + friend inline bool operator==(const QTaggedPointer<T, Tag> &lhs, const QTaggedPointer<T, Tag> &rhs) noexcept + { + return lhs.pointer() == rhs.pointer(); + } + + friend inline bool operator!=(const QTaggedPointer<T, Tag> &lhs, const QTaggedPointer<T, Tag> &rhs) noexcept + { + return lhs.pointer() != rhs.pointer(); + } + + friend inline bool operator==(const QTaggedPointer<T, Tag> &lhs, std::nullptr_t) noexcept + { + return lhs.isNull(); + } + + friend inline bool operator==(std::nullptr_t, const QTaggedPointer<T, Tag> &rhs) noexcept + { + return rhs.isNull(); + } + + friend inline bool operator!=(const QTaggedPointer<T, Tag> &lhs, std::nullptr_t) noexcept + { + return !lhs.isNull(); + } + + friend inline bool operator!=(std::nullptr_t, const QTaggedPointer<T, Tag> &rhs) noexcept + { + return !rhs.isNull(); + } + + friend inline bool operator!(const QTaggedPointer<T, Tag> &ptr) noexcept + { + return !ptr.pointer(); + } + +protected: + quintptr d; +}; + +template <typename T> +Q_DECLARE_TYPEINFO_BODY(QTaggedPointer<T>, Q_MOVABLE_TYPE); + +template <typename T, typename Tag> +inline void swap(QTaggedPointer<T, Tag> &p1, QTaggedPointer<T, Tag> &p2) noexcept +{ + p1.swap(p2); +} + +QT_END_NAMESPACE + +#endif // QTAGGEDPOINTER_H diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index edf4248c44..d7c66e85be 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -44,6 +44,7 @@ HEADERS += \ tools/qsize.h \ tools/qstack.h \ tools/qtools_p.h \ + tools/qtaggedpointer.h \ tools/qvarlengtharray.h \ tools/qvector.h \ tools/qversionnumber.h diff --git a/tests/auto/corelib/tools/.prev_CMakeLists.txt b/tests/auto/corelib/tools/.prev_CMakeLists.txt new file mode 100644 index 0000000000..5350c29b79 --- /dev/null +++ b/tests/auto/corelib/tools/.prev_CMakeLists.txt @@ -0,0 +1,45 @@ +# Generated from tools.pro. + +add_subdirectory(collections) +add_subdirectory(containerapisymmetry) +add_subdirectory(qalgorithms) +add_subdirectory(qarraydata) +add_subdirectory(qbitarray) +add_subdirectory(qcache) +add_subdirectory(qcommandlineparser) +add_subdirectory(qcontiguouscache) +add_subdirectory(qcryptographichash) +add_subdirectory(qeasingcurve) +add_subdirectory(qexplicitlyshareddatapointer) +add_subdirectory(qflatmap) +add_subdirectory(qfreelist) +add_subdirectory(qhash) +add_subdirectory(qhashfunctions) +add_subdirectory(qline) +add_subdirectory(qmakearray) +add_subdirectory(qmap) +add_subdirectory(qmargins) +add_subdirectory(qmessageauthenticationcode) +add_subdirectory(qoffsetstringarray) +add_subdirectory(qpair) +add_subdirectory(qpoint) +add_subdirectory(qpointf) +add_subdirectory(qqueue) +add_subdirectory(qrect) +add_subdirectory(qringbuffer) +add_subdirectory(qscopedpointer) +add_subdirectory(qscopedvaluerollback) +add_subdirectory(qscopeguard) +add_subdirectory(qtaggedpointer) +add_subdirectory(qset) +add_subdirectory(qsharedpointer) +add_subdirectory(qsize) +add_subdirectory(qsizef) +add_subdirectory(qstl) +add_subdirectory(qtimeline) +add_subdirectory(qvarlengtharray) +add_subdirectory(qvector) +add_subdirectory(qversionnumber) +if(APPLE) + add_subdirectory(qmacautoreleasepool) +endif() diff --git a/tests/auto/corelib/tools/CMakeLists.txt b/tests/auto/corelib/tools/CMakeLists.txt index f1967c3664..2a7025f97c 100644 --- a/tests/auto/corelib/tools/CMakeLists.txt +++ b/tests/auto/corelib/tools/CMakeLists.txt @@ -34,6 +34,7 @@ add_subdirectory(qringbuffer) add_subdirectory(qscopedpointer) add_subdirectory(qscopedvaluerollback) add_subdirectory(qscopeguard) +add_subdirectory(qtaggedpointer) add_subdirectory(qset) # add_subdirectory(qsharedpointer) # special case not ported add_subdirectory(qsize) diff --git a/tests/auto/corelib/tools/qtaggedpointer/.gitignore b/tests/auto/corelib/tools/qtaggedpointer/.gitignore new file mode 100644 index 0000000000..d027eb73b2 --- /dev/null +++ b/tests/auto/corelib/tools/qtaggedpointer/.gitignore @@ -0,0 +1 @@ +tst_qtaggedpointer diff --git a/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt b/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt new file mode 100644 index 0000000000..1057e5ebe5 --- /dev/null +++ b/tests/auto/corelib/tools/qtaggedpointer/CMakeLists.txt @@ -0,0 +1,12 @@ +# Generated from qtaggedpointer.pro. + +##################################################################### +## tst_qtaggedpointer Test: +##################################################################### + +qt_add_test(tst_qtaggedpointer + SOURCES + tst_qtaggedpointer.cpp + PUBLIC_LIBRARIES + Qt::CorePrivate +) diff --git a/tests/auto/corelib/tools/qtaggedpointer/qtaggedpointer.pro b/tests/auto/corelib/tools/qtaggedpointer/qtaggedpointer.pro new file mode 100644 index 0000000000..48bc503c56 --- /dev/null +++ b/tests/auto/corelib/tools/qtaggedpointer/qtaggedpointer.pro @@ -0,0 +1,4 @@ +CONFIG += testcase +TARGET = tst_qtaggedpointer +QT = core-private testlib +SOURCES = tst_qtaggedpointer.cpp diff --git a/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp b/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp new file mode 100644 index 0000000000..36308d61bc --- /dev/null +++ b/tests/auto/corelib/tools/qtaggedpointer/tst_qtaggedpointer.cpp @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/qtaggedpointer.h> + +class tst_QTaggedPointer : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void construction(); + void dereferenceOperator(); + void pointerOperator(); + void negationOperator(); + void operatorBool(); + void comparison(); + void tag(); + void objectMember(); + void customTagType(); +}; + +void tst_QTaggedPointer::construction() +{ + { + QTaggedPointer<int> p; + QCOMPARE(p.pointer(), nullptr); + QVERIFY(!p.tag()); + } + { + QTaggedPointer<int> p(nullptr, 0x1); + QCOMPARE(p.pointer(), nullptr); + QCOMPARE(p.tag(), quintptr(0x1)); + } + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data()); + QCOMPARE(p.pointer(), rawPointer.data()); + QVERIFY(!p.tag()); + } + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QCOMPARE(p.pointer(), rawPointer.data()); + QCOMPARE(p.tag(), quintptr(0x1)); + } +} + +class AbstractClass +{ +public: + virtual ~AbstractClass() {} + virtual int member() const = 0; +}; + +class SubClass : public AbstractClass +{ +public: + int member() const { return 5; } +}; + +void tst_QTaggedPointer::dereferenceOperator() +{ + /* Dereference a basic value. */ + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data()); + const int value = *p; + QCOMPARE(value, 5); + } + + /* Dereference a basic value with tag. */ + { + QScopedPointer<int> rawPointer(new int(5)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + const int value = *p; + QCOMPARE(value, 5); + } + + /* Dereference a pointer to an abstract class. This verifies + * that the operator returns a reference, when compiling + * with MSVC 2005. */ + { + QScopedPointer<SubClass> ptr(new SubClass()); + QTaggedPointer<AbstractClass> p(ptr.data()); + QCOMPARE((*p).member(), 5); + } + + /* The operator should be const. */ + { + QScopedPointer<int> rawPointer(new int(5)); + const QTaggedPointer<int> p(rawPointer.data()); + *p; + } + + /* A reference should be returned, not a value. */ + { + QScopedPointer<int> rawPointer(new int(5)); + const QTaggedPointer<int> p(rawPointer.data()); + Q_UNUSED(static_cast<int &>(*p)); + } + + /* Instantiated on a const object, the returned object is a const reference. */ + { + QScopedPointer<int> rawPointer(new int(5)); + const QTaggedPointer<const int> p(rawPointer.data()); + Q_UNUSED(static_cast<const int &>(*p)); + } +} + +class Value +{ +public: + int value; +}; + +void tst_QTaggedPointer::pointerOperator() +{ + { + QScopedPointer<Value> valuePtr(new Value{5}); + QTaggedPointer<Value> p(valuePtr.data()); + QCOMPARE(p->value, 5); + } + + { + QScopedPointer<Value> valuePtr(new Value{5}); + QTaggedPointer<Value> p(valuePtr.data(), 0x1); + QCOMPARE(p->value, 5); + } + + /* The operator should be const. */ + { + QScopedPointer<Value> valuePtr(new Value{5}); + const QTaggedPointer<Value> p(valuePtr.data()); + QVERIFY(p->value); + } +} + +void tst_QTaggedPointer::negationOperator() +{ + /* Invoke on default constructed value. */ + { + QTaggedPointer<int> p; + QVERIFY(!p); + } + + /* Invoke on nullptr value with tag. */ + { + QTaggedPointer<int> p(nullptr, 0x1); + QVERIFY(!p); + } + + /* Invoke on a value. */ + { + QScopedPointer<int> rawPointer(new int(2)); + QTaggedPointer<int> p(rawPointer.data()); + QCOMPARE(!p, false); + } + + /* Invoke on a value with tag. */ + { + QScopedPointer<int> rawPointer(new int(2)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QCOMPARE(!p, false); + } + + /* The signature should be const. */ + { + const QTaggedPointer<int> p; + !p; + } + + /* The return value should be bool. */ + { + const QTaggedPointer<int> p; + Q_UNUSED(static_cast<bool>(!p)); + } +} + +void tst_QTaggedPointer::operatorBool() +{ + /* Invoke on default constructed value. */ + { + QTaggedPointer<int> p; + QCOMPARE(bool(p), false); + } + + /* Invoke on nullptr value with tag. */ + { + QTaggedPointer<int> p(nullptr, 0x1); + QCOMPARE(bool(p), false); + } + + /* Invoke on active value. */ + { + QScopedPointer<int> rawPointer(new int(3)); + QTaggedPointer<int> p(rawPointer.data()); + QVERIFY(p); + } + + /* Invoke on active value with tag. */ + { + QScopedPointer<int> rawPointer(new int(3)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QVERIFY(p); + } + + /* The signature should be const and return bool. */ + { + const QTaggedPointer<int> p; + (void)static_cast<bool>(p); + } +} + +template <class A1, class A2, class B> +void comparisonTest(const A1 &a1, const A2 &a2, const B &b) +{ + // test equality on equal pointers + QVERIFY(a1 == a2); + QVERIFY(a2 == a1); + + // test inequality on equal pointers + QVERIFY(!(a1 != a2)); + QVERIFY(!(a2 != a1)); + + // test equality on unequal pointers + QVERIFY(!(a1 == b)); + QVERIFY(!(a2 == b)); + QVERIFY(!(b == a1)); + QVERIFY(!(b == a2)); + + // test inequality on unequal pointers + QVERIFY(b != a1); + QVERIFY(b != a2); + QVERIFY(a1 != b); + QVERIFY(a2 != b); +} + +void tst_QTaggedPointer::comparison() +{ + QScopedPointer<int> a(new int(5)); + + { + QTaggedPointer<int> a1(a.data()); + QTaggedPointer<int> a2(a.data()); + QScopedPointer<int> rawPointer(new int(6)); + QTaggedPointer<int> b(rawPointer.data()); + + comparisonTest(a1, a1, b); + comparisonTest(a2, a2, b); + comparisonTest(a1, a2, b); + } + { + QTaggedPointer<int> a1(a.data(), 0x1); + QTaggedPointer<int> a2(a.data(), 0x1); + QScopedPointer<int> rawPointer(new int(6)); + QTaggedPointer<int> b(rawPointer.data(), 0x1); + + comparisonTest(a1, a1, b); + comparisonTest(a2, a2, b); + comparisonTest(a1, a2, b); + } + { + QTaggedPointer<int> a1(a.data(), 0x1); + QTaggedPointer<int> a2(a.data(), 0x2); + QScopedPointer<int> rawPointer(new int(6)); + QTaggedPointer<int> b(rawPointer.data(), 0x2); + + comparisonTest(a1, a1, b); + comparisonTest(a2, a2, b); + comparisonTest(a1, a2, b); + } + { + QTaggedPointer<int> p; + QVERIFY(p.isNull()); + QVERIFY(p == nullptr); + QVERIFY(nullptr == p); + } + { + QTaggedPointer<int> p(nullptr, 0x1); + QVERIFY(p.isNull()); + QVERIFY(p == nullptr); + QVERIFY(nullptr == p); + } + { + QScopedPointer<int> rawPointer(new int(42)); + QTaggedPointer<int> p(rawPointer.data()); + QVERIFY(!p.isNull()); + QVERIFY(p != nullptr); + QVERIFY(nullptr != p); + } + { + QScopedPointer<int> rawPointer(new int(42)); + QTaggedPointer<int> p(rawPointer.data(), 0x1); + QVERIFY(!p.isNull()); + QVERIFY(p != nullptr); + QVERIFY(nullptr != p); + } +} + +void tst_QTaggedPointer::tag() +{ + QScopedPointer<int> rawPointer(new int(3)); + QTaggedPointer<int> p(rawPointer.data()); + QCOMPARE(*p.pointer(), 3); + QVERIFY(!p.tag()); + + p.setTag(0x1); + QCOMPARE(p.tag(), 0x1); + + p.setTag(0x2); + QCOMPARE(p.tag(), 0x2); +} + +struct Foo +{ + Foo() : p(nullptr) {} + Foo(const Foo &other) : p(other.p) {} + Foo &operator=(const Foo &other) { + p = other.p; + return *this; + } + QTaggedPointer<int> p; +}; + +void tst_QTaggedPointer::objectMember() +{ + QScopedPointer<int> rawPointer(new int(42)); + Foo f; + f.p = QTaggedPointer<int>(rawPointer.data(), 0x1); + + Foo f2(f); + QCOMPARE(f2.p.pointer(), f.p.pointer()); + QCOMPARE(f2.p.tag(), f.p.tag()); + + Foo f3 = f; + QCOMPARE(f3.p.pointer(), f.p.pointer()); + QCOMPARE(f3.p.tag(), f.p.tag()); +} + +class Bar +{ + Q_GADGET +public: + enum Tag { + NoTag = 0, + FirstTag = 1, + SecondTag = 2 + }; + Q_DECLARE_FLAGS(Tags, Tag) + Q_FLAG(Tags) + + int value; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Bar::Tags) + +void tst_QTaggedPointer::customTagType() +{ + QScopedPointer<Bar> barPtr(new Bar{5}); + typedef QTaggedPointer<Bar, Bar::Tags> TaggedBar; + TaggedBar p(barPtr.data()); + QCOMPARE(p->value, 5); + QVERIFY(TaggedBar::maximumTag()); + + QVERIFY(!p.tag()); + QCOMPARE(p.tag(), Bar::NoTag); + + p.setTag(Bar::FirstTag | Bar::SecondTag); + QCOMPARE(p->value, 5); + QCOMPARE(p.tag(), Bar::FirstTag | Bar::SecondTag); +} + +QTEST_MAIN(tst_QTaggedPointer) +#include "tst_qtaggedpointer.moc" diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index e51be90100..21dfdf8c47 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -30,6 +30,7 @@ SUBDIRS=\ qscopedpointer \ qscopedvaluerollback \ qscopeguard \ + qtaggedpointer \ qset \ qsharedpointer \ qsize \ |