QHash: add support for heterogeneous key lookups
This implements support in QHash and QMultiHash for lookups of heterogeneous key types that produce the same hash value. This is implemented by duplicating each of the following functions into an overload on Key and one a template that is enable_if-constrained to a key type that meets the requirement: * contains * count * equals_range * find * operator[] (const only) * remove * take * value * values (QMultiHash) The non-const operator[] may insert into the hash, so it's not part of this commit. Change-Id: I664b9f014ffc48cbb49bfffd17b037852f0fd192 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>bb10
parent
52abff8cc1
commit
b53c153a10
|
|
@ -677,8 +677,10 @@ struct Data
|
|||
return size >= (numBuckets >> 1);
|
||||
}
|
||||
|
||||
Bucket findBucket(const Key &key) const noexcept
|
||||
template <typename K> Bucket findBucket(const K &key) const noexcept
|
||||
{
|
||||
static_assert(std::is_same_v<std::remove_cv_t<Key>, K> ||
|
||||
QHashHeterogeneousSearch<std::remove_cv_t<Key>, K>::value);
|
||||
Q_ASSERT(numBuckets > 0);
|
||||
size_t hash = QHashPrivate::calculateHash(key, seed);
|
||||
Bucket bucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
|
||||
|
|
@ -697,7 +699,7 @@ struct Data
|
|||
}
|
||||
}
|
||||
|
||||
Node *findNode(const Key &key) const noexcept
|
||||
template <typename K> Node *findNode(const K &key) const noexcept
|
||||
{
|
||||
auto bucket = findBucket(key);
|
||||
if (bucket.isUnused())
|
||||
|
|
@ -954,6 +956,11 @@ public:
|
|||
}
|
||||
|
||||
bool remove(const Key &key)
|
||||
{
|
||||
return removeImpl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> bool removeImpl(const K &key)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
return false;
|
||||
|
|
@ -967,12 +974,20 @@ public:
|
|||
d->erase(it);
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename Predicate>
|
||||
qsizetype removeIf(Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(*this, pred);
|
||||
}
|
||||
|
||||
T take(const Key &key)
|
||||
{
|
||||
return takeImpl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> T takeImpl(const K &key)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
return T();
|
||||
|
|
@ -988,6 +1003,7 @@ public:
|
|||
return value;
|
||||
}
|
||||
|
||||
public:
|
||||
bool contains(const Key &key) const noexcept
|
||||
{
|
||||
if (!d)
|
||||
|
|
@ -1025,7 +1041,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
template <typename Fn> T valueImpl(const Key &key, Fn &&defaultValue) const noexcept
|
||||
template <typename K, typename Fn> T valueImpl(const K &key, Fn &&defaultValue) const noexcept
|
||||
{
|
||||
if (d) {
|
||||
Node *n = d->findNode(key);
|
||||
|
|
@ -1240,11 +1256,7 @@ private:
|
|||
return std::make_pair(first, second);
|
||||
}
|
||||
|
||||
public:
|
||||
typedef iterator Iterator;
|
||||
typedef const_iterator ConstIterator;
|
||||
inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
|
||||
iterator find(const Key &key)
|
||||
template <typename K> iterator findImpl(const K &key)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
return end();
|
||||
|
|
@ -1256,7 +1268,7 @@ public:
|
|||
return end();
|
||||
return iterator(it.toIterator(d));
|
||||
}
|
||||
const_iterator find(const Key &key) const noexcept
|
||||
template <typename K> const_iterator constFindImpl(const K &key) const noexcept
|
||||
{
|
||||
if (isEmpty())
|
||||
return end();
|
||||
|
|
@ -1265,6 +1277,19 @@ public:
|
|||
return end();
|
||||
return const_iterator({d, it.toBucketIndex(d)});
|
||||
}
|
||||
|
||||
public:
|
||||
typedef iterator Iterator;
|
||||
typedef const_iterator ConstIterator;
|
||||
inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
|
||||
iterator find(const Key &key)
|
||||
{
|
||||
return findImpl(key);
|
||||
}
|
||||
const_iterator find(const Key &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
const_iterator constFind(const Key &key) const noexcept
|
||||
{
|
||||
return find(key);
|
||||
|
|
@ -1328,8 +1353,61 @@ private:
|
|||
result.it.node()->emplaceValue(std::forward<Args>(args)...);
|
||||
return iterator(result.it);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
#ifdef __cpp_concepts
|
||||
bool remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return removeImpl(key);
|
||||
}
|
||||
T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return takeImpl(key);
|
||||
}
|
||||
bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return d ? d->findNode(key) != nullptr : false;
|
||||
}
|
||||
qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return contains(key) ? 1 : 0;
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return valueImpl(key, [] { return T(); });
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
|
||||
{
|
||||
return valueImpl(key, [&] { return defaultValue; });
|
||||
}
|
||||
const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return value(key);
|
||||
}
|
||||
std::pair<iterator, iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return equal_range_impl(*this, key);
|
||||
}
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return equal_range_impl(*this, key);
|
||||
}
|
||||
iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return findImpl(key);
|
||||
}
|
||||
const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return find(key);
|
||||
}
|
||||
#endif // __cpp_concepts
|
||||
};
|
||||
|
||||
|
||||
template <typename Key, typename T>
|
||||
|
|
@ -1506,6 +1584,11 @@ public:
|
|||
}
|
||||
|
||||
qsizetype remove(const Key &key)
|
||||
{
|
||||
return removeImpl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> qsizetype removeImpl(const K &key)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
return 0;
|
||||
|
|
@ -1522,12 +1605,20 @@ public:
|
|||
d->erase(it);
|
||||
return n;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename Predicate>
|
||||
qsizetype removeIf(Predicate pred)
|
||||
{
|
||||
return QtPrivate::associative_erase_if(*this, pred);
|
||||
}
|
||||
|
||||
T take(const Key &key)
|
||||
{
|
||||
return takeImpl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> T takeImpl(const K &key)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
return T();
|
||||
|
|
@ -1553,6 +1644,7 @@ public:
|
|||
return t;
|
||||
}
|
||||
|
||||
public:
|
||||
bool contains(const Key &key) const noexcept
|
||||
{
|
||||
if (!d)
|
||||
|
|
@ -1586,7 +1678,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
template <typename Fn> T valueImpl(const Key &key, Fn &&defaultValue) const noexcept
|
||||
template <typename K, typename Fn> T valueImpl(const K &key, Fn &&defaultValue) const noexcept
|
||||
{
|
||||
if (d) {
|
||||
Node *n = d->findNode(key);
|
||||
|
|
@ -1650,8 +1742,14 @@ public:
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QList<T> values() const { return QList<T>(begin(), end()); }
|
||||
QList<T> values(const Key &key) const
|
||||
{
|
||||
return valuesImpl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> QList<T> valuesImpl(const K &key) const
|
||||
{
|
||||
QList<T> values;
|
||||
if (d) {
|
||||
|
|
@ -1667,6 +1765,7 @@ public:
|
|||
return values;
|
||||
}
|
||||
|
||||
public:
|
||||
class const_iterator;
|
||||
|
||||
class iterator
|
||||
|
|
@ -1878,7 +1977,9 @@ public:
|
|||
typedef iterator Iterator;
|
||||
typedef const_iterator ConstIterator;
|
||||
inline qsizetype count() const noexcept { return size(); }
|
||||
iterator find(const Key &key)
|
||||
|
||||
private:
|
||||
template <typename K> iterator findImpl(const K &key)
|
||||
{
|
||||
if (isEmpty())
|
||||
return end();
|
||||
|
|
@ -1891,11 +1992,7 @@ public:
|
|||
return end();
|
||||
return iterator(it.toIterator(d));
|
||||
}
|
||||
const_iterator find(const Key &key) const noexcept
|
||||
{
|
||||
return constFind(key);
|
||||
}
|
||||
const_iterator constFind(const Key &key) const noexcept
|
||||
template <typename K> const_iterator constFindImpl(const K &key) const noexcept
|
||||
{
|
||||
if (isEmpty())
|
||||
return end();
|
||||
|
|
@ -1904,6 +2001,20 @@ public:
|
|||
return constEnd();
|
||||
return const_iterator(it.toIterator(d));
|
||||
}
|
||||
public:
|
||||
iterator find(const Key &key)
|
||||
{
|
||||
return findImpl(key);
|
||||
}
|
||||
const_iterator constFind(const Key &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
const_iterator find(const Key &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
|
||||
iterator insert(const Key &key, const T &value)
|
||||
{
|
||||
return emplace(key, value);
|
||||
|
|
@ -1968,6 +2079,11 @@ public:
|
|||
{ QMultiHash result = *this; result += other; return result; }
|
||||
|
||||
bool contains(const Key &key, const T &value) const noexcept
|
||||
{
|
||||
return containsImpl(key, value);
|
||||
}
|
||||
private:
|
||||
template <typename K> bool containsImpl(const K &key, const T &value) const noexcept
|
||||
{
|
||||
if (isEmpty())
|
||||
return false;
|
||||
|
|
@ -1977,7 +2093,13 @@ public:
|
|||
return n->value->contains(value);
|
||||
}
|
||||
|
||||
public:
|
||||
qsizetype remove(const Key &key, const T &value)
|
||||
{
|
||||
return removeImpl(key, value);
|
||||
}
|
||||
private:
|
||||
template <typename K> qsizetype removeImpl(const K &key, const T &value)
|
||||
{
|
||||
if (isEmpty()) // prevents detaching shared null
|
||||
return 0;
|
||||
|
|
@ -2007,7 +2129,13 @@ public:
|
|||
return n;
|
||||
}
|
||||
|
||||
public:
|
||||
qsizetype count(const Key &key) const noexcept
|
||||
{
|
||||
return countImpl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> qsizetype countImpl(const K &key) const noexcept
|
||||
{
|
||||
if (!d)
|
||||
return 0;
|
||||
|
|
@ -2024,7 +2152,13 @@ public:
|
|||
return n;
|
||||
}
|
||||
|
||||
public:
|
||||
qsizetype count(const Key &key, const T &value) const noexcept
|
||||
{
|
||||
return countImpl(key, value);
|
||||
}
|
||||
private:
|
||||
template <typename K> qsizetype countImpl(const K &key, const T &value) const noexcept
|
||||
{
|
||||
if (!d)
|
||||
return 0;
|
||||
|
|
@ -2042,7 +2176,7 @@ public:
|
|||
return n;
|
||||
}
|
||||
|
||||
iterator find(const Key &key, const T &value)
|
||||
template <typename K> iterator findImpl(const K &key, const T &value)
|
||||
{
|
||||
if (isEmpty())
|
||||
return end();
|
||||
|
|
@ -2051,11 +2185,7 @@ public:
|
|||
auto it = constFind(key, value);
|
||||
return iterator(it.i, it.e);
|
||||
}
|
||||
const_iterator find(const Key &key, const T &value) const noexcept
|
||||
{
|
||||
return constFind(key, value);
|
||||
}
|
||||
const_iterator constFind(const Key &key, const T &value) const noexcept
|
||||
template <typename K> const_iterator constFindImpl(const K &key, const T &value) const noexcept
|
||||
{
|
||||
const_iterator i(constFind(key));
|
||||
const_iterator end(constEnd());
|
||||
|
|
@ -2067,6 +2197,21 @@ public:
|
|||
return end;
|
||||
}
|
||||
|
||||
public:
|
||||
iterator find(const Key &key, const T &value)
|
||||
{
|
||||
return findImpl(key, value);
|
||||
}
|
||||
|
||||
const_iterator constFind(const Key &key, const T &value) const noexcept
|
||||
{
|
||||
return constFindImpl(key, value);
|
||||
}
|
||||
const_iterator find(const Key &key, const T &value) const noexcept
|
||||
{
|
||||
return constFind(key, value);
|
||||
}
|
||||
|
||||
QMultiHash &unite(const QMultiHash &other)
|
||||
{
|
||||
if (isEmpty()) {
|
||||
|
|
@ -2103,6 +2248,11 @@ public:
|
|||
}
|
||||
|
||||
std::pair<iterator, iterator> equal_range(const Key &key)
|
||||
{
|
||||
return equal_range_impl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> std::pair<iterator, iterator> equal_range_impl(const K &key)
|
||||
{
|
||||
const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
|
||||
detach();
|
||||
|
|
@ -2110,7 +2260,13 @@ public:
|
|||
return {iterator(pair.first.i), iterator(pair.second.i)};
|
||||
}
|
||||
|
||||
public:
|
||||
std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
|
||||
{
|
||||
return equal_range_impl(key);
|
||||
}
|
||||
private:
|
||||
template <typename K> std::pair<const_iterator, const_iterator> equal_range_impl(const K &key) const noexcept
|
||||
{
|
||||
if (!d)
|
||||
return {end(), end()};
|
||||
|
|
@ -2124,7 +2280,6 @@ public:
|
|||
return {const_iterator(it), const_iterator(end)};
|
||||
}
|
||||
|
||||
private:
|
||||
void detach_helper()
|
||||
{
|
||||
if (!d) {
|
||||
|
|
@ -2161,6 +2316,90 @@ private:
|
|||
}
|
||||
return iterator(result.it);
|
||||
}
|
||||
|
||||
public:
|
||||
#ifdef __cpp_concepts
|
||||
qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return removeImpl(key);
|
||||
}
|
||||
T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return takeImpl(key);
|
||||
}
|
||||
bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
if (!d)
|
||||
return false;
|
||||
return d->findNode(key) != nullptr;
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return valueImpl(key, [] { return T(); });
|
||||
}
|
||||
T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
|
||||
{
|
||||
return valueImpl(key, [&] { return defaultValue; });
|
||||
}
|
||||
const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return value(key);
|
||||
}
|
||||
QList<T> values(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return valuesImpl(key);
|
||||
}
|
||||
iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return findImpl(key);
|
||||
}
|
||||
const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return constFindImpl(key);
|
||||
}
|
||||
bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
{
|
||||
return containsImpl(key, value);
|
||||
}
|
||||
qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
|
||||
{
|
||||
return removeImpl(key, value);
|
||||
}
|
||||
qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return countImpl(key);
|
||||
}
|
||||
qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
{
|
||||
return countImpl(key, value);
|
||||
}
|
||||
iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
|
||||
{
|
||||
return findImpl(key, value);
|
||||
}
|
||||
const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
{
|
||||
return constFindImpl(key, value);
|
||||
}
|
||||
const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
|
||||
{
|
||||
return constFind(key, value);
|
||||
}
|
||||
std::pair<iterator, iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
|
||||
{
|
||||
return equal_range_impl(key);
|
||||
}
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
|
||||
{
|
||||
return equal_range_impl(key);
|
||||
}
|
||||
#endif // __cpp_concepts
|
||||
};
|
||||
|
||||
Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ private slots:
|
|||
void qhash();
|
||||
void take(); // copied from tst_QMap
|
||||
void operator_eq(); // slightly modified from tst_QMap
|
||||
void heterogeneousSearch();
|
||||
void heterogeneousSearchConstKey();
|
||||
void heterogeneousSearchByteArray();
|
||||
void heterogeneousSearchString();
|
||||
|
||||
void rehash_isnt_quadratic();
|
||||
void dont_need_default_constructor();
|
||||
void qmultihash_specific();
|
||||
|
|
@ -45,6 +50,10 @@ private slots:
|
|||
void qmultihash_qhash_rvalue_ref_unite();
|
||||
void qmultihashUnite();
|
||||
void qmultihashSize();
|
||||
void qmultihashHeterogeneousSearch();
|
||||
void qmultihashHeterogeneousSearchConstKey();
|
||||
void qmultihashHeterogeneousSearchByteArray();
|
||||
void qmultihashHeterogeneousSearchString();
|
||||
|
||||
void compare();
|
||||
void compare2();
|
||||
|
|
@ -1158,6 +1167,187 @@ void tst_QHash::operator_eq()
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef __cpp_concepts
|
||||
struct HeterogeneousHashingType
|
||||
{
|
||||
inline static int conversionCount = 0;
|
||||
QString s;
|
||||
|
||||
Q_IMPLICIT operator QString() const
|
||||
{
|
||||
++conversionCount;
|
||||
return s;
|
||||
}
|
||||
|
||||
// std::equality_comparable_with requires we be self-comparable too
|
||||
friend bool operator==(const HeterogeneousHashingType &t1, const HeterogeneousHashingType &t2) = default;
|
||||
|
||||
friend bool operator==(const QString &string, const HeterogeneousHashingType &tester)
|
||||
{ return tester.s == string; }
|
||||
friend bool operator!=(const QString &string, const HeterogeneousHashingType &tester)
|
||||
{ return !(tester.s == string); }
|
||||
|
||||
friend size_t qHash(const HeterogeneousHashingType &tester, size_t seed)
|
||||
{ return qHash(tester.s, seed); }
|
||||
};
|
||||
QT_BEGIN_NAMESPACE
|
||||
template <> struct QHashHeterogeneousSearch<QString, HeterogeneousHashingType> : std::true_type {};
|
||||
template <> struct QHashHeterogeneousSearch<HeterogeneousHashingType, QString> : std::true_type {};
|
||||
QT_END_NAMESPACE
|
||||
static_assert(std::is_same_v<QString, std::common_type_t<QString, HeterogeneousHashingType>>);
|
||||
static_assert(std::equality_comparable_with<QString, HeterogeneousHashingType>);
|
||||
static_assert(QHashPrivate::HeterogeneouslySearchableWith<QString, HeterogeneousHashingType>);
|
||||
static_assert(QHashPrivate::HeterogeneouslySearchableWith<HeterogeneousHashingType, QString>);
|
||||
|
||||
template <typename T> struct HeterogeneousSearchTestHelper
|
||||
{
|
||||
static void resetCounter() {}
|
||||
static void checkCounter() {}
|
||||
};
|
||||
template <> struct HeterogeneousSearchTestHelper<HeterogeneousHashingType>
|
||||
{
|
||||
static void resetCounter()
|
||||
{
|
||||
HeterogeneousHashingType::conversionCount = 0;
|
||||
}
|
||||
static void checkCounter()
|
||||
{
|
||||
QTest::setThrowOnFail(true);
|
||||
auto scopeExit = qScopeGuard([] { QTest::setThrowOnFail(false); });
|
||||
QCOMPARE(HeterogeneousHashingType::conversionCount, 0);
|
||||
}
|
||||
};
|
||||
#else
|
||||
using HeterogeneousHashingType = QString;
|
||||
#endif
|
||||
|
||||
template <template <typename, typename> class Hash, typename String, typename View>
|
||||
static void heterogeneousSearchTest(const QList<std::remove_const_t<String>> &keys)
|
||||
{
|
||||
#ifdef __cpp_concepts
|
||||
using Helper = HeterogeneousSearchTestHelper<View>;
|
||||
String key = keys.last();
|
||||
String otherKey = keys.first();
|
||||
View keyView(key);
|
||||
View otherKeyView(otherKey);
|
||||
|
||||
Hash<String, qsizetype> hash;
|
||||
static constexpr bool IsMultiHash = !std::is_same_v<decltype(hash.remove(String())), bool>;
|
||||
hash[key] = keys.size();
|
||||
|
||||
Helper::resetCounter();
|
||||
QVERIFY(hash.contains(keyView));
|
||||
QCOMPARE_EQ(hash.count(keyView), 1);
|
||||
QCOMPARE_EQ(hash.value(keyView), keys.size());
|
||||
QCOMPARE_EQ(hash.value(keyView, -1), keys.size());
|
||||
QCOMPARE_EQ(std::as_const(hash)[keyView], keys.size());
|
||||
QCOMPARE_EQ(hash.find(keyView), hash.begin());
|
||||
QCOMPARE_EQ(std::as_const(hash).find(keyView), hash.constBegin());
|
||||
QCOMPARE_EQ(hash.constFind(keyView), hash.constBegin());
|
||||
QCOMPARE_EQ(hash.equal_range(keyView), std::make_pair(hash.begin(), hash.end()));
|
||||
QCOMPARE_EQ(std::as_const(hash).equal_range(keyView),
|
||||
std::make_pair(hash.constBegin(), hash.constEnd()));
|
||||
Helper::checkCounter();
|
||||
|
||||
QVERIFY(!hash.contains(otherKeyView));
|
||||
QCOMPARE_EQ(hash.count(otherKeyView), 0);
|
||||
QCOMPARE_EQ(hash.value(otherKeyView), 0);
|
||||
QCOMPARE_EQ(hash.value(otherKeyView, -1), -1);
|
||||
QCOMPARE_EQ(std::as_const(hash)[otherKeyView], 0);
|
||||
QCOMPARE_EQ(hash.find(otherKeyView), hash.end());
|
||||
QCOMPARE_EQ(std::as_const(hash).find(otherKeyView), hash.constEnd());
|
||||
QCOMPARE_EQ(hash.constFind(otherKeyView), hash.constEnd());
|
||||
QCOMPARE_EQ(hash.equal_range(otherKeyView), std::make_pair(hash.end(), hash.end()));
|
||||
QCOMPARE_EQ(std::as_const(hash).equal_range(otherKeyView),
|
||||
std::make_pair(hash.constEnd(), hash.constEnd()));
|
||||
Helper::checkCounter();
|
||||
|
||||
if constexpr (IsMultiHash) {
|
||||
hash.insert(key, keys.size());
|
||||
QCOMPARE_EQ(hash.count(keyView), 2);
|
||||
|
||||
// not depending on which of the two the current implementation finds
|
||||
QCOMPARE_NE(hash.value(keyView), 0);
|
||||
QCOMPARE_NE(hash.value(keyView, -1000), -1000);
|
||||
QCOMPARE_NE(std::as_const(hash)[keyView], 0);
|
||||
QCOMPARE_NE(hash.find(keyView), hash.end());
|
||||
QCOMPARE_NE(std::as_const(hash).find(keyView), hash.constEnd());
|
||||
QCOMPARE_NE(hash.constFind(keyView), hash.constEnd());
|
||||
QCOMPARE_NE(hash.equal_range(keyView), std::make_pair(hash.end(), hash.end()));
|
||||
QCOMPARE_NE(std::as_const(hash).equal_range(keyView),
|
||||
std::make_pair(hash.constEnd(), hash.constEnd()));
|
||||
|
||||
// QMultiHash-specific functions
|
||||
QVERIFY(hash.contains(keyView, keys.size()));
|
||||
QCOMPARE_EQ(hash.count(keyView, 0), 0);
|
||||
QCOMPARE_EQ(hash.count(keyView, keys.size()), 2);
|
||||
QCOMPARE_EQ(hash.values(keyView), QList<qsizetype>({ keys.size(), keys.size() }));
|
||||
|
||||
hash.insert(key, -keys.size());
|
||||
QCOMPARE_EQ(hash.count(keyView), 3);
|
||||
QCOMPARE_EQ(hash.find(keyView, 0), hash.end());
|
||||
QCOMPARE_NE(hash.find(keyView, keys.size()), hash.end());
|
||||
QCOMPARE_NE(hash.find(keyView, -keys.size()), hash.end());
|
||||
QCOMPARE_EQ(std::as_const(hash).find(keyView, 0), hash.constEnd());
|
||||
QCOMPARE_NE(std::as_const(hash).find(keyView, keys.size()), hash.constEnd());
|
||||
QCOMPARE_NE(std::as_const(hash).find(keyView, -keys.size()), hash.constEnd());
|
||||
QCOMPARE_EQ(hash.constFind(keyView, 0), hash.constEnd());
|
||||
QCOMPARE_NE(hash.constFind(keyView, keys.size()), hash.constEnd());
|
||||
QCOMPARE_NE(hash.constFind(keyView, -keys.size()), hash.constEnd());
|
||||
|
||||
// removals
|
||||
QCOMPARE_EQ(hash.remove(keyView, -keys.size()), 1);
|
||||
QCOMPARE_EQ(hash.remove(keyView), 2);
|
||||
} else {
|
||||
// removals
|
||||
QCOMPARE_EQ(hash.remove(keyView), true);
|
||||
}
|
||||
|
||||
QCOMPARE_EQ(hash.take(otherKeyView), 0);
|
||||
QVERIFY(hash.isEmpty());
|
||||
Helper::checkCounter();
|
||||
|
||||
// repeat with more keys
|
||||
for (qsizetype i = 0; i < keys.size() - 1; ++i) {
|
||||
hash.insert(keys[i], -(i + 1));
|
||||
hash.insert(keys[i], i + 1);
|
||||
}
|
||||
|
||||
QVERIFY(!hash.contains(keyView));
|
||||
QCOMPARE_EQ(hash.count(keyView), 0);
|
||||
QCOMPARE_EQ(hash.value(keyView), 0);
|
||||
QCOMPARE_EQ(hash.value(keyView, -1), -1);
|
||||
QCOMPARE_EQ(std::as_const(hash)[keyView], 0);
|
||||
QCOMPARE_EQ(hash.find(keyView), hash.end());
|
||||
QCOMPARE_EQ(hash.constFind(keyView), hash.constEnd());
|
||||
Helper::checkCounter();
|
||||
#else
|
||||
Q_UNUSED(keys);
|
||||
QSKIP("This feature requires C++20 (concepts)");
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QHash::heterogeneousSearch()
|
||||
{
|
||||
heterogeneousSearchTest<QHash, QString, HeterogeneousHashingType>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::heterogeneousSearchConstKey()
|
||||
{
|
||||
// QHash<const QString, X> seen in the wild (e.g. Qt Creator)
|
||||
heterogeneousSearchTest<QHash, const QString, HeterogeneousHashingType>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::heterogeneousSearchByteArray()
|
||||
{
|
||||
heterogeneousSearchTest<QHash, QByteArray, QByteArrayView>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::heterogeneousSearchString()
|
||||
{
|
||||
heterogeneousSearchTest<QHash, QString, QStringView>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::compare()
|
||||
{
|
||||
QHash<int, QString> hash1,hash2;
|
||||
|
|
@ -2127,6 +2317,26 @@ void tst_QHash::qmultihashSize()
|
|||
}
|
||||
}
|
||||
|
||||
void tst_QHash::qmultihashHeterogeneousSearch()
|
||||
{
|
||||
heterogeneousSearchTest<QMultiHash, QString, HeterogeneousHashingType>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::qmultihashHeterogeneousSearchConstKey()
|
||||
{
|
||||
heterogeneousSearchTest<QMultiHash, const QString, HeterogeneousHashingType>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::qmultihashHeterogeneousSearchByteArray()
|
||||
{
|
||||
heterogeneousSearchTest<QMultiHash, QByteArray, QByteArrayView>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::qmultihashHeterogeneousSearchString()
|
||||
{
|
||||
heterogeneousSearchTest<QMultiHash, QString, QStringView>({ "Hello", {}, "World" });
|
||||
}
|
||||
|
||||
void tst_QHash::keys_values_uniqueKeys()
|
||||
{
|
||||
QMultiHash<QString, int> hash;
|
||||
|
|
|
|||
Loading…
Reference in New Issue