/**************************************************************************** ** ** 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 #include class tst_QTaggedPointer : public QObject { Q_OBJECT private Q_SLOTS: void constExpr(); void construction(); void dereferenceOperator(); void pointerOperator(); void negationOperator(); void operatorBool(); void comparison(); void tag(); void objectMember(); void customTagType(); void taggedLinkedList(); }; void tst_QTaggedPointer::constExpr() { { constexpr QTaggedPointer p; Q_UNUSED(p); } { enum Foo : uint {}; constexpr QTaggedPointer p; Q_UNUSED(p); } { enum Foo : int {}; constexpr QTaggedPointer p; Q_UNUSED(p); } { constexpr QTaggedPointer p = nullptr; Q_UNUSED(p); } { enum Foo : uint {}; constexpr QTaggedPointer p = nullptr; Q_UNUSED(p); } { enum Foo : int {}; constexpr QTaggedPointer p = nullptr; Q_UNUSED(p); } } void tst_QTaggedPointer::construction() { { QTaggedPointer p; QCOMPARE(p.data(), nullptr); QVERIFY(!p.tag()); } { QTaggedPointer p(nullptr, 0x1); QCOMPARE(p.data(), nullptr); QCOMPARE(p.tag(), quintptr(0x1)); } { QScopedPointer rawPointer(new int(5)); QTaggedPointer p(rawPointer.data()); QCOMPARE(p.data(), rawPointer.data()); QVERIFY(!p.tag()); } { QScopedPointer rawPointer(new int(5)); QTaggedPointer p(rawPointer.data(), 0x1); QCOMPARE(p.data(), 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 rawPointer(new int(5)); QTaggedPointer p(rawPointer.data()); const int value = *p; QCOMPARE(value, 5); } /* Dereference a basic value with tag. */ { QScopedPointer rawPointer(new int(5)); QTaggedPointer 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 ptr(new SubClass()); QTaggedPointer p(ptr.data()); QCOMPARE((*p).member(), 5); } /* The operator should be const. */ { QScopedPointer rawPointer(new int(5)); const QTaggedPointer p(rawPointer.data()); *p; } /* A reference should be returned, not a value. */ { QScopedPointer rawPointer(new int(5)); const QTaggedPointer p(rawPointer.data()); Q_UNUSED(static_cast(*p)); } /* Instantiated on a const object, the returned object is a const reference. */ { QScopedPointer rawPointer(new int(5)); const QTaggedPointer p(rawPointer.data()); Q_UNUSED(static_cast(*p)); } } class Value { public: int value; }; void tst_QTaggedPointer::pointerOperator() { { QScopedPointer valuePtr(new Value{5}); QTaggedPointer p(valuePtr.data()); QCOMPARE(p->value, 5); } { QScopedPointer valuePtr(new Value{5}); QTaggedPointer p(valuePtr.data(), 0x1); QCOMPARE(p->value, 5); } /* The operator should be const. */ { QScopedPointer valuePtr(new Value{5}); const QTaggedPointer p(valuePtr.data()); QVERIFY(p->value); } } void tst_QTaggedPointer::negationOperator() { /* Invoke on default constructed value. */ { QTaggedPointer p; QVERIFY(!p); } /* Invoke on nullptr value with tag. */ { QTaggedPointer p(nullptr, 0x1); QVERIFY(!p); } /* Invoke on a value. */ { QScopedPointer rawPointer(new int(2)); QTaggedPointer p(rawPointer.data()); QCOMPARE(!p, false); } /* Invoke on a value with tag. */ { QScopedPointer rawPointer(new int(2)); QTaggedPointer p(rawPointer.data(), 0x1); QCOMPARE(!p, false); } /* The signature should be const. */ { const QTaggedPointer p; !p; } /* The return value should be bool. */ { const QTaggedPointer p; Q_UNUSED(static_cast(!p)); } } void tst_QTaggedPointer::operatorBool() { /* Invoke on default constructed value. */ { QTaggedPointer p; QCOMPARE(bool(p), false); } /* Invoke on nullptr value with tag. */ { QTaggedPointer p(nullptr, 0x1); QCOMPARE(bool(p), false); } /* Invoke on active value. */ { QScopedPointer rawPointer(new int(3)); QTaggedPointer p(rawPointer.data()); QVERIFY(p); } /* Invoke on active value with tag. */ { QScopedPointer rawPointer(new int(3)); QTaggedPointer p(rawPointer.data(), 0x1); QVERIFY(p); } /* The signature should be const and return bool. */ { const QTaggedPointer p; (void)static_cast(p); } } template 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 a(new int(5)); { QTaggedPointer a1(a.data()); QTaggedPointer a2(a.data()); QScopedPointer rawPointer(new int(6)); QTaggedPointer b(rawPointer.data()); comparisonTest(a1, a1, b); comparisonTest(a2, a2, b); comparisonTest(a1, a2, b); } { QTaggedPointer a1(a.data(), 0x1); QTaggedPointer a2(a.data(), 0x1); QScopedPointer rawPointer(new int(6)); QTaggedPointer b(rawPointer.data(), 0x1); comparisonTest(a1, a1, b); comparisonTest(a2, a2, b); comparisonTest(a1, a2, b); } { QTaggedPointer a1(a.data(), 0x1); QTaggedPointer a2(a.data(), 0x2); QScopedPointer rawPointer(new int(6)); QTaggedPointer b(rawPointer.data(), 0x2); comparisonTest(a1, a1, b); comparisonTest(a2, a2, b); comparisonTest(a1, a2, b); } { QTaggedPointer p; QVERIFY(p.isNull()); QVERIFY(p == nullptr); QVERIFY(nullptr == p); } { QTaggedPointer p(nullptr, 0x1); QVERIFY(p.isNull()); QVERIFY(p == nullptr); QVERIFY(nullptr == p); } { QScopedPointer rawPointer(new int(42)); QTaggedPointer p(rawPointer.data()); QVERIFY(!p.isNull()); QVERIFY(p != nullptr); QVERIFY(nullptr != p); } { QScopedPointer rawPointer(new int(42)); QTaggedPointer p(rawPointer.data(), 0x1); QVERIFY(!p.isNull()); QVERIFY(p != nullptr); QVERIFY(nullptr != p); } } void tst_QTaggedPointer::tag() { QScopedPointer rawPointer(new int(3)); QTaggedPointer p(rawPointer.data()); QCOMPARE(*p.data(), 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 p; }; void tst_QTaggedPointer::objectMember() { QScopedPointer rawPointer(new int(42)); Foo f; f.p = QTaggedPointer(rawPointer.data(), 0x1); Foo f2(f); QCOMPARE(f2.p.data(), f.p.data()); QCOMPARE(f2.p.tag(), f.p.tag()); Foo f3 = f; QCOMPARE(f3.p.data(), f.p.data()); 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 barPtr(new Bar{5}); typedef QTaggedPointer 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); } // Compile-only test to ensure it's possible to use tagged pointers // with incomplete types. struct LinkedListItem { enum Tag { NoTag = 0, FirstTag = 1 }; Q_DECLARE_FLAGS(Tags, Tag) QTaggedPointer next; ~LinkedListItem() { delete next.data(); } }; Q_DECLARE_OPERATORS_FOR_FLAGS(LinkedListItem::Tags) void tst_QTaggedPointer::taggedLinkedList() { QScopedPointer lli(new LinkedListItem); lli->next = QTaggedPointer(new LinkedListItem); lli->next.setTag(LinkedListItem::FirstTag); } QTEST_MAIN(tst_QTaggedPointer) #include "tst_qtaggedpointer.moc"