Make it possible to use QTaggedPointer within classes

A common pattern in declarative is to use the unused bits in linked list
next pointers for additional information storage. The "next" pointer is
typically then a tagged pointer of the containing class, which is not
fully defined yet. Therefore alignof() can't be used at tagged pointer
instantiation time. This patch delays the calls to alignment, etc. until
the corresponding functions are used, as in principle the tagged pointer
is just a quintptr and no additional information should be needed until
operating on it.

Change-Id: I87a3578ee921d471e1b60ed5903b549ef0610b97
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Simon Hausmann 2020-02-26 16:48:13 +01:00
parent 749e37efc5
commit 733ae8a04b
2 changed files with 38 additions and 12 deletions

View File

@ -73,10 +73,8 @@ namespace QtPrivate {
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;
static constexpr quintptr tagMask() { return QtPrivate::TagInfo<T>::alignment - 1; }
static constexpr quintptr pointerMask() { return ~tagMask(); }
public:
using Type = T;
@ -87,7 +85,7 @@ public:
{
Q_STATIC_ASSERT(sizeof(Type*) == sizeof(QTaggedPointer<Type>));
Q_ASSERT_X((quintptr(pointer) & tagMask) == 0,
Q_ASSERT_X((quintptr(pointer) & tagMask()) == 0,
"QTaggedPointer<T, Tag>", "Pointer is not aligned");
setTag(tag);
@ -111,37 +109,37 @@ public:
QTaggedPointer<T, Tag> &operator=(T *other) noexcept
{
d = reinterpret_cast<quintptr>(other) | (d & tagMask);
d = reinterpret_cast<quintptr>(other) | (d & tagMask());
return *this;
}
QTaggedPointer<T, Tag> &operator=(std::nullptr_t) noexcept
{
d &= tagMask;
d &= tagMask();
return *this;
}
static constexpr TagType maximumTag() noexcept
{
return TagType(TagInternalType(tagMask));
return TagType(typename QtPrivate::TagInfo<T>::TagType(tagMask()));
}
void setTag(TagType tag)
{
Q_ASSERT_X((static_cast<TagInternalType>(tag) & pointerMask) == 0,
Q_ASSERT_X((static_cast<typename QtPrivate::TagInfo<T>::TagType>(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);
d = (d & pointerMask()) | (static_cast<typename QtPrivate::TagInfo<T>::TagType>(tag) & tagMask());
}
TagType tag() const noexcept
{
return TagType(TagInternalType(d & tagMask));
return TagType(typename QtPrivate::TagInfo<T>::TagType(d & tagMask()));
}
Type* pointer() const noexcept
{
return reinterpret_cast<T*>(d & pointerMask);
return reinterpret_cast<T*>(d & pointerMask());
}
bool isNull() const noexcept

View File

@ -43,6 +43,7 @@ private Q_SLOTS:
void tag();
void objectMember();
void customTagType();
void taggedLinkedList();
};
void tst_QTaggedPointer::construction()
@ -396,5 +397,32 @@ void tst_QTaggedPointer::customTagType()
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<LinkedListItem, Tag> next;
~LinkedListItem()
{
delete next.pointer();
}
};
Q_DECLARE_OPERATORS_FOR_FLAGS(LinkedListItem::Tags)
void tst_QTaggedPointer::taggedLinkedList()
{
QScopedPointer<LinkedListItem> lli(new LinkedListItem);
lli->next = QTaggedPointer<LinkedListItem, LinkedListItem::Tag>(new LinkedListItem);
lli->next.setTag(LinkedListItem::FirstTag);
}
QTEST_MAIN(tst_QTaggedPointer)
#include "tst_qtaggedpointer.moc"