QSequentialIterableImpl: support append
Task-number: QTBUG-80916 Change-Id: I87e74da0ce454e56b5fe94d9db3693a587d35edf Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>bb10
parent
b31852c4ca
commit
a4fb128b73
|
|
@ -977,6 +977,30 @@ enum IteratorCapability
|
|||
RandomAccessCapability = 4
|
||||
};
|
||||
|
||||
enum ContainerCapability
|
||||
{
|
||||
ContainerIsAppendable = 1
|
||||
};
|
||||
|
||||
template<typename Container, typename T = void>
|
||||
struct ContainerCapabilitiesImpl
|
||||
{
|
||||
enum {ContainerCapabilities = 0};
|
||||
using appendFunction = void(*)(const void *container, const void *newElement);
|
||||
static constexpr const appendFunction appendImpl = nullptr;
|
||||
};
|
||||
|
||||
template<typename Container>
|
||||
struct ContainerCapabilitiesImpl<Container, decltype(std::declval<Container>().push_back(std::declval<typename Container::value_type>()))>
|
||||
{
|
||||
enum {ContainerCapabilities = ContainerIsAppendable};
|
||||
|
||||
// The code below invokes undefined behavior if and only if the pointer passed into QSequentialIterableImpl
|
||||
// pointed to a const object to begin with
|
||||
static void appendImpl(const void *container, const void *value)
|
||||
{ static_cast<Container *>(const_cast<void *>(container))->push_back(*static_cast<const typename Container::value_type *>(value)); }
|
||||
};
|
||||
|
||||
template<typename T, typename Category = typename std::iterator_traits<typename T::const_iterator>::iterator_category>
|
||||
struct CapabilitiesImpl;
|
||||
|
||||
|
|
@ -1012,6 +1036,12 @@ template<typename T>
|
|||
struct ContainerAPI<std::list<T> > : CapabilitiesImpl<std::list<T> >
|
||||
{ static int size(const std::list<T> *t) { return int(t->size()); } };
|
||||
|
||||
/*
|
||||
revision 0: _iteratorCapabilities is simply a uint, where the bits at _revision were never set
|
||||
revision 1: _iteratorCapabilties is treated as a bitfield, the remaining bits are used to introduce
|
||||
_revision, _containerCapabilities and _unused. The latter contains 21 bits that are
|
||||
not used yet
|
||||
*/
|
||||
class QSequentialIterableImpl
|
||||
{
|
||||
public:
|
||||
|
|
@ -1020,19 +1050,37 @@ public:
|
|||
int _metaType_id;
|
||||
uint _metaType_flags;
|
||||
uint _iteratorCapabilities;
|
||||
// Iterator capabilities looks actually like
|
||||
// uint _iteratorCapabilities:4;
|
||||
// uint _revision:3;
|
||||
// uint _containerCapabilities:4;
|
||||
// uint _unused:21;*/
|
||||
typedef int(*sizeFunc)(const void *p);
|
||||
typedef const void * (*atFunc)(const void *p, int);
|
||||
typedef void (*moveIteratorFunc)(const void *p, void **);
|
||||
enum Position { ToBegin, ToEnd };
|
||||
typedef void (*moveIteratorFunc2)(const void *p, void **, Position position);
|
||||
typedef void (*advanceFunc)(void **p, int);
|
||||
typedef VariantData (*getFunc)( void * const *p, int metaTypeId, uint flags);
|
||||
typedef void (*destroyIterFunc)(void **p);
|
||||
typedef bool (*equalIterFunc)(void * const *p, void * const *other);
|
||||
typedef void (*copyIterFunc)(void **, void * const *);
|
||||
typedef void(*appendFunction)(const void *container, const void *newElement);
|
||||
|
||||
IteratorCapability iteratorCapabilities() {return static_cast<IteratorCapability>(_iteratorCapabilities & 0xF);}
|
||||
uint revision() {return _iteratorCapabilities >> 4 & 0x7;}
|
||||
uint containerCapabilities() {return _iteratorCapabilities >> 7 & 0xF;}
|
||||
|
||||
sizeFunc _size;
|
||||
atFunc _at;
|
||||
moveIteratorFunc _moveToBegin;
|
||||
moveIteratorFunc _moveToEnd;
|
||||
union {
|
||||
moveIteratorFunc _moveToBegin;
|
||||
moveIteratorFunc2 _moveTo;
|
||||
};
|
||||
union {
|
||||
moveIteratorFunc _moveToEnd;
|
||||
appendFunction _append;
|
||||
};
|
||||
advanceFunc _advance;
|
||||
getFunc _get;
|
||||
destroyIterFunc _destroyIter;
|
||||
|
|
@ -1059,6 +1107,15 @@ public:
|
|||
static void moveToEndImpl(const void *container, void **iterator)
|
||||
{ IteratorOwner<typename T::const_iterator>::assign(iterator, static_cast<const T*>(container)->end()); }
|
||||
|
||||
template<class Container>
|
||||
static void moveToImpl(const void *container, void **iterator, Position position)
|
||||
{
|
||||
if (position == ToBegin)
|
||||
moveToBeginImpl<Container>(container, iterator);
|
||||
else
|
||||
moveToEndImpl<Container>(container, iterator);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static VariantData getImpl(void * const *iterator, int metaTypeId, uint flags)
|
||||
{ return VariantData(metaTypeId, IteratorOwner<typename T::const_iterator>::getData(iterator), flags); }
|
||||
|
|
@ -1069,11 +1126,11 @@ public:
|
|||
, _iterator(nullptr)
|
||||
, _metaType_id(qMetaTypeId<typename T::value_type>())
|
||||
, _metaType_flags(QTypeInfo<typename T::value_type>::isPointer)
|
||||
, _iteratorCapabilities(ContainerAPI<T>::IteratorCapabilities)
|
||||
, _iteratorCapabilities(ContainerAPI<T>::IteratorCapabilities | (1 << 4) | (ContainerCapabilitiesImpl<T>::ContainerCapabilities << (4+3)))
|
||||
, _size(sizeImpl<T>)
|
||||
, _at(atImpl<T>)
|
||||
, _moveToBegin(moveToBeginImpl<T>)
|
||||
, _moveToEnd(moveToEndImpl<T>)
|
||||
, _moveTo(moveToImpl<T>)
|
||||
, _append(ContainerCapabilitiesImpl<T>::appendImpl)
|
||||
, _advance(IteratorOwner<typename T::const_iterator>::advance)
|
||||
, _get(getImpl<T>)
|
||||
, _destroyIter(IteratorOwner<typename T::const_iterator>::destroy)
|
||||
|
|
@ -1087,7 +1144,7 @@ public:
|
|||
, _iterator(nullptr)
|
||||
, _metaType_id(QMetaType::UnknownType)
|
||||
, _metaType_flags(0)
|
||||
, _iteratorCapabilities(0)
|
||||
, _iteratorCapabilities(0 | (1 << 4) ) // no iterator capabilities, revision 1
|
||||
, _size(nullptr)
|
||||
, _at(nullptr)
|
||||
, _moveToBegin(nullptr)
|
||||
|
|
@ -1100,8 +1157,18 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
inline void moveToBegin() { _moveToBegin(_iterable, &_iterator); }
|
||||
inline void moveToEnd() { _moveToEnd(_iterable, &_iterator); }
|
||||
inline void moveToBegin() {
|
||||
if (revision() == 0)
|
||||
_moveToBegin(_iterable, &_iterator);
|
||||
else
|
||||
_moveTo(_iterable, &_iterator, ToBegin);
|
||||
}
|
||||
inline void moveToEnd() {
|
||||
if (revision() == 0)
|
||||
_moveToEnd(_iterable, &_iterator);
|
||||
else
|
||||
_moveTo(_iterable, &_iterator, ToEnd);
|
||||
}
|
||||
inline bool equal(const QSequentialIterableImpl&other) const { return _equalIter(&_iterator, &other._iterator); }
|
||||
inline QSequentialIterableImpl &advance(int i) {
|
||||
Q_ASSERT(i > 0 || _iteratorCapabilities & BiDirectionalCapability);
|
||||
|
|
@ -1109,6 +1176,11 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
inline void append(const void *newElement) {
|
||||
if (containerCapabilities() & ContainerIsAppendable)
|
||||
_append(_iterable, newElement);
|
||||
}
|
||||
|
||||
inline VariantData getCurrent() const { return _get(&_iterator, _metaType_id, _metaType_flags); }
|
||||
|
||||
VariantData at(int idx) const
|
||||
|
|
|
|||
|
|
@ -281,6 +281,8 @@ private slots:
|
|||
void shouldDeleteVariantDataWorksForAssociative();
|
||||
void fromStdVariant();
|
||||
void qt4UuidDataStream();
|
||||
void sequentialIterableEndianessSanityCheck();
|
||||
void sequentialIterableAppend();
|
||||
|
||||
void preferDirectConversionOverInterfaces();
|
||||
|
||||
|
|
@ -5142,6 +5144,28 @@ void tst_QVariant::qt4UuidDataStream()
|
|||
QCOMPARE(result.value<QUuid>(), source);
|
||||
}
|
||||
|
||||
void tst_QVariant::sequentialIterableEndianessSanityCheck()
|
||||
{
|
||||
namespace QMTP = QtMetaTypePrivate;
|
||||
uint oldIteratorCaps = QMTP::ForwardCapability | QMTP::BiDirectionalCapability | QMTP::RandomAccessCapability;
|
||||
QMTP::QSequentialIterableImpl seqImpl {};
|
||||
QCOMPARE(seqImpl.revision(), 1u);
|
||||
memcpy(&seqImpl._iteratorCapabilities, &oldIteratorCaps, sizeof(oldIteratorCaps));
|
||||
QCOMPARE(seqImpl.revision(), 0u);
|
||||
}
|
||||
|
||||
void tst_QVariant::sequentialIterableAppend()
|
||||
{
|
||||
QVector<int> container {1, 2};
|
||||
auto variant = QVariant::fromValue(container);
|
||||
QVERIFY(variant.canConvert<QtMetaTypePrivate::QSequentialIterableImpl>());
|
||||
auto asIterable = variant.value<QtMetaTypePrivate::QSequentialIterableImpl>();
|
||||
const int i = 3, j = 4;
|
||||
asIterable.append(&i);
|
||||
asIterable.append(&j);
|
||||
QCOMPARE(variant.value<QVector<int>>(), QVector<int> ({1, 2, 3, 4}));
|
||||
}
|
||||
|
||||
void tst_QVariant::preferDirectConversionOverInterfaces()
|
||||
{
|
||||
using namespace QtMetaTypePrivate;
|
||||
|
|
|
|||
Loading…
Reference in New Issue