From 722c7edf03ae16f784553934aae9ad327709d1db Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 14 Aug 2024 09:47:29 +0200 Subject: [PATCH] QHash: Unconceptify heterogeneous search code That allows the code to run in C++17 mode - at the expense of significantly worse compile time error messages, potentially worse compilation speed and harder to read code. Adjust the test type to actually model detail::is_equality_comparable_with (which requires all four relational operators even in C++17). As a drive-by, fix the return value of remove() from auto to bool. Done-with: Marc Mutz Fixes: QTBUG-128470 Change-Id: I68df26db579c60812a18e09b76dd5712e73ccaa2 Reviewed-by: Thiago Macieira (cherry picked from commit 7fe3cee36352c74cbaaff52e863bd0da1170affa) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/tools/qhash.h | 109 ++++++++++++------- src/corelib/tools/qhashfunctions.h | 53 ++++++--- tests/auto/corelib/tools/qhash/tst_qhash.cpp | 31 +++--- 3 files changed, 124 insertions(+), 69 deletions(-) diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index faf3e9f9535..1c28791d743 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -1377,69 +1377,83 @@ private: return iterator(result.it); } + template + using if_heterogeneously_seachable = QHashPrivate::if_heterogeneously_seachable_with; + public: -#ifdef __cpp_concepts - bool remove(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + bool remove(const K &key) { return removeImpl(key); } - T take(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + T take(const K &key) { return takeImpl(key); } - bool contains(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const + template = true> + bool contains(const K &key) const { return d ? d->findNode(key) != nullptr : false; } - qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const + template = true> + qsizetype count(const K &key) const { return contains(key) ? 1 : 0; } - T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + T value(const K &key) const noexcept { if (auto *v = valueImpl(key)) return *v; else return T(); } - T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &defaultValue) const noexcept + template = true> + T value(const K &key, const T &defaultValue) const noexcept { if (auto *v = valueImpl(key)) return *v; else return defaultValue; } - T &operator[](const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + T &operator[](const K &key) { return operatorIndexImpl(key); } - const T operator[](const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + const T operator[](const K &key) const noexcept { return value(key); } + template = true> std::pair - equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + equal_range(const K &key) { return equal_range_impl(*this, key); } + template = true> std::pair - equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + equal_range(const K &key) const noexcept { return equal_range_impl(*this, key); } - iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + iterator find(const K &key) { return findImpl(key); } - const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + const_iterator find(const K &key) const noexcept { return constFindImpl(key); } - const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + const_iterator constFind(const K &key) const noexcept { return find(key); } -#endif // __cpp_concepts }; @@ -2372,99 +2386,120 @@ private: return iterator(result.it); } + template + using if_heterogeneously_seachable = QHashPrivate::if_heterogeneously_seachable_with; + public: -#ifdef __cpp_concepts - qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + qsizetype remove(const K &key) { return removeImpl(key); } - T take(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + T take(const K &key) { return takeImpl(key); } - bool contains(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + bool contains(const K &key) const noexcept { if (!d) return false; return d->findNode(key) != nullptr; } - T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + T value(const K &key) const noexcept { if (auto *v = valueImpl(key)) return *v; else return T(); } - T value(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &defaultValue) const noexcept + template = true> + T value(const K &key, const T &defaultValue) const noexcept { if (auto *v = valueImpl(key)) return *v; else return defaultValue; } - T &operator[](const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + T &operator[](const K &key) { return operatorIndexImpl(key); } - const T operator[](const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + const T operator[](const K &key) const noexcept { return value(key); } - QList values(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + QList values(const K &key) { return valuesImpl(key); } - iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + template = true> + iterator find(const K &key) { return findImpl(key); } - const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + const_iterator constFind(const K &key) const noexcept { return constFindImpl(key); } - const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + const_iterator find(const K &key) const noexcept { return constFindImpl(key); } - bool contains(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + template = true> + bool contains(const K &key, const T &value) const noexcept { return containsImpl(key, value); } - qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) + template = true> + qsizetype remove(const K &key, const T &value) { return removeImpl(key, value); } - qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + template = true> + qsizetype count(const K &key) const noexcept { return countImpl(key); } - qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + template = true> + qsizetype count(const K &key, const T &value) const noexcept { return countImpl(key, value); } - iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) + template = true> + iterator find(const K &key, const T &value) { return findImpl(key, value); } - const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + template = true> + const_iterator constFind(const K &key, const T &value) const noexcept { return constFindImpl(key, value); } - const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith auto &key, const T &value) const noexcept + template = true> + const_iterator find(const K &key, const T &value) const noexcept { return constFind(key, value); } + template = true> std::pair - equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) + equal_range(const K &key) { return equal_range_impl(key); } + template = true> std::pair - equal_range(const QHashPrivate::HeterogeneouslySearchableWith auto &key) const noexcept + equal_range(const K &key) const noexcept { return equal_range_impl(key); } -#endif // __cpp_concepts }; Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash) diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index 3a72dd47acc..a865c055d10 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -9,9 +9,6 @@ #include #include -#ifdef __cpp_concepts -#include -#endif #include // for std::accumulate #include // for std::hash #include // For std::pair @@ -238,19 +235,44 @@ size_t qHash(const T &t, size_t seed, Args&&...) noexcept(noexcept(qHash(t))) #endif // < Qt 7 namespace QHashPrivate { -#ifdef __cpp_concepts -template concept HeterogeneouslySearchableWithHelper = + +namespace detail { +// approximates std::equality_comparable_with +template +struct is_equality_comparable_with : std::false_type {}; + +template +struct is_equality_comparable_with() == std::declval())), + decltype(bool(std::declval() == std::declval())), + decltype(bool(std::declval() != std::declval())), + decltype(bool(std::declval() != std::declval())) + >> + : std::true_type {}; +} + +template struct HeterogeneouslySearchableWithHelper + : std::conjunction< // if Key and T are not the same (member already exists) - !std::is_same_v + std::negation>, // but are comparable amongst each other - && std::equality_comparable_with + detail::is_equality_comparable_with, // and supports heteregenous hashing - && QHashHeterogeneousSearch::value; -template concept HeterogeneouslySearchableWith = - HeterogeneouslySearchableWithHelper, q20::remove_cvref_t>; -#else -template constexpr bool HeterogeneouslySearchableWith = false; -#endif + QHashHeterogeneousSearch + > {}; + +template +using HeterogeneouslySearchableWith = HeterogeneouslySearchableWithHelper< + q20::remove_cvref_t, + q20::remove_cvref_t +>; + +template +using if_heterogeneously_seachable_with = std::enable_if_t< + QHashPrivate::HeterogeneouslySearchableWith::value, + bool>; + } template @@ -259,9 +281,8 @@ bool qHashEquals(const T &a, const T &b) return a == b; } -template -std::enable_if_t, bool> -qHashEquals(const T1 &a, const T2 &b) +template = true> +bool qHashEquals(const T1 &a, const T2 &b) { return a == b; } diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index b3dbdfa40c4..833e2e1d94a 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -1169,9 +1169,12 @@ void tst_QHash::operator_eq() } } -#ifdef __cpp_concepts struct HeterogeneousHashingType { +#ifndef __cpp_aggregate_paren_init + HeterogeneousHashingType() = default; + HeterogeneousHashingType(const QString &string) : s(string) {} +#endif inline static int conversionCount = 0; QString s; @@ -1182,12 +1185,18 @@ struct HeterogeneousHashingType } // std::equality_comparable_with requires we be self-comparable too - friend bool operator==(const HeterogeneousHashingType &t1, const HeterogeneousHashingType &t2) = default; + friend bool operator==(const HeterogeneousHashingType &t1, const HeterogeneousHashingType &t2) { return t1.s == t2.s; }; friend bool operator==(const QString &string, const HeterogeneousHashingType &tester) { return tester.s == string; } +#ifndef __cpp_impl_three_way_compare // full set required for detail::is_equality_comparable_with friend bool operator!=(const QString &string, const HeterogeneousHashingType &tester) - { return !(tester.s == string); } + { return !operator==(string, tester); } + friend bool operator==(const HeterogeneousHashingType &tester, const QString &string) + { return operator==(string, tester); } + friend bool operator!=(const HeterogeneousHashingType &tester, const QString &string) + { return !operator==(string, tester); } +#endif friend size_t qHash(const HeterogeneousHashingType &tester, size_t seed) { return qHash(tester.s, seed); } @@ -1196,10 +1205,9 @@ QT_BEGIN_NAMESPACE template <> struct QHashHeterogeneousSearch : std::true_type {}; template <> struct QHashHeterogeneousSearch : std::true_type {}; QT_END_NAMESPACE -static_assert(std::is_same_v>); -static_assert(std::equality_comparable_with); -static_assert(QHashPrivate::HeterogeneouslySearchableWith); -static_assert(QHashPrivate::HeterogeneouslySearchableWith); +static_assert(QHashPrivate::detail::is_equality_comparable_with::value); +static_assert(QHashPrivate::HeterogeneouslySearchableWith::value); +static_assert(QHashPrivate::HeterogeneouslySearchableWith::value); template struct HeterogeneousSearchTestHelper { @@ -1219,14 +1227,10 @@ template <> struct HeterogeneousSearchTestHelper QCOMPARE(HeterogeneousHashingType::conversionCount, 0); } }; -#else -using HeterogeneousHashingType = QString; -#endif template