diff options
-rw-r--r-- | src/corelib/tools/qcontainertools_impl.h | 52 | ||||
-rw-r--r-- | src/corelib/tools/qhash.cpp | 58 | ||||
-rw-r--r-- | src/corelib/tools/qhash.h | 22 | ||||
-rw-r--r-- | src/corelib/tools/qmap.h | 24 | ||||
-rw-r--r-- | src/corelib/tools/qmap.qdoc | 29 | ||||
-rw-r--r-- | src/corelib/tools/qmultimap.qdoc | 29 | ||||
-rw-r--r-- | tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp | 67 |
7 files changed, 281 insertions, 0 deletions
diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h index 54de6a8815..e4812eefef 100644 --- a/src/corelib/tools/qcontainertools_impl.h +++ b/src/corelib/tools/qcontainertools_impl.h @@ -215,6 +215,58 @@ qsizetype qset_erase_if(QSet<T> &set, Predicate &pred) return result; } + +// Prerequisite: F is invocable on ArgTypes +template <typename R, typename F, typename ... ArgTypes> +struct is_invoke_result_explicitly_convertible : std::is_constructible<R, std::invoke_result_t<F, ArgTypes...>> +{}; + +// is_invocable_r checks for implicit conversions, but we need to check +// for explicit conversions in remove_if. So, roll our own trait. +template <typename R, typename F, typename ... ArgTypes> +constexpr bool is_invocable_explicit_r_v = std::conjunction_v< + std::is_invocable<F, ArgTypes...>, + is_invoke_result_explicitly_convertible<R, F, ArgTypes...> +>; + +template <typename Container, typename Predicate> +auto associative_erase_if(Container &c, Predicate &pred) +{ + // we support predicates callable with either Container::iterator + // or with std::pair<const Key &, Value &> + using Iterator = typename Container::iterator; + using Key = typename Container::key_type; + using Value = typename Container::mapped_type; + using KeyValuePair = std::pair<const Key &, Value &>; + + typename Container::size_type result = 0; + + auto it = c.begin(); + const auto e = c.end(); + while (it != e) { + if constexpr (is_invocable_explicit_r_v<bool, Predicate &, Iterator &>) { + if (pred(it)) { + it = c.erase(it); + ++result; + } else { + ++it; + } + } else if constexpr (is_invocable_explicit_r_v<bool, Predicate &, KeyValuePair &&>) { + KeyValuePair p(it.key(), it.value()); + if (pred(std::move(p))) { + it = c.erase(it); + ++result; + } else { + ++it; + } + } else { + static_assert(sizeof(Container) == 0, "Predicate has an incompatible signature"); + } + } + + return result; +} + } // namespace QtPrivate QT_END_NAMESPACE diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 8a94695ec3..19fed905dc 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -1521,6 +1521,21 @@ size_t qHash(long double key, size_t seed) noexcept \sa clear(), take() */ +/*! \fn template <class Key, class T> template <typename Predicate> qsizetype QHash<Key, T>::removeIf(Predicate pred) + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the hash. + + The function supports predicates which take either an argument of + type \c{QHash<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template <class Key, class T> T QHash<Key, T>::take(const Key &key) Removes the item with the \a key from the hash and returns @@ -2651,6 +2666,21 @@ size_t qHash(long double key, size_t seed) noexcept \sa remove() */ +/*! \fn template <class Key, class T> template <typename Predicate> qsizetype QMultiHash<Key, T>::removeIf(Predicate pred) + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the multi hash. + + The function supports predicates which take either an argument of + type \c{QMultiHash<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template <class Key, class T> T QMultiHash<Key, T>::take(const Key &key) Removes the item with the \a key from the hash and returns @@ -3338,4 +3368,32 @@ size_t qHash(long double key, size_t seed) noexcept Type \c T must be supported by qHash(). */ +/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QHash<Key, T> &hash, Predicate pred) + \relates QHash + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the hash \a hash. + + The function supports predicates which take either an argument of + type \c{QHash<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. +*/ + +/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred) + \relates QMultiHash + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the multi hash \a hash. + + The function supports predicates which take either an argument of + type \c{QMultiHash<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. +*/ + QT_END_NAMESPACE diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 8134f4402c..65ae9b75fd 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -872,6 +872,11 @@ public: d->erase(it); return true; } + template <typename Predicate> + qsizetype removeIf(Predicate pred) + { + return QtPrivate::associative_erase_if(*this, pred); + } T take(const Key &key) { if (isEmpty()) // prevents detaching shared null @@ -1354,6 +1359,11 @@ public: d->erase(it); return n; } + template <typename Predicate> + qsizetype removeIf(Predicate pred) + { + return QtPrivate::associative_erase_if(*this, pred); + } T take(const Key &key) { if (isEmpty()) // prevents detaching shared null @@ -1946,6 +1956,18 @@ inline size_t qHash(const QMultiHash<Key, T> &key, size_t seed = 0) return hash; } +template <typename Key, typename T, typename Predicate> +qsizetype erase_if(QHash<Key, T> &hash, Predicate pred) +{ + return QtPrivate::associative_erase_if(hash, pred); +} + +template <typename Key, typename T, typename Predicate> +qsizetype erase_if(QMultiHash<Key, T> &hash, Predicate pred) +{ + return QtPrivate::associative_erase_if(hash, pred); +} + QT_END_NAMESPACE #endif // QHASH_H diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index a426a202e1..b64989eadd 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -343,6 +343,12 @@ public: return result; } + template <typename Predicate> + size_type removeIf(Predicate pred) + { + return QtPrivate::associative_erase_if(*this, pred); + } + T take(const Key &key) { if (!d) @@ -742,6 +748,12 @@ public: Q_DECLARE_ASSOCIATIVE_ITERATOR(Map) Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Map) +template <typename Key, typename T, typename Predicate> +qsizetype erase_if(QMap<Key, T> &map, Predicate pred) +{ + return QtPrivate::associative_erase_if(map, pred); +} + // // QMultiMap // @@ -938,6 +950,12 @@ public: return result; } + template <typename Predicate> + size_type removeIf(Predicate pred) + { + return QtPrivate::associative_erase_if(*this, pred); + } + T take(const Key &key) { if (!d) @@ -1441,6 +1459,12 @@ QMultiMap<Key, T> operator+=(QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rh return lhs.unite(rhs); } +template <typename Key, typename T, typename Predicate> +qsizetype erase_if(QMultiMap<Key, T> &map, Predicate pred) +{ + return QtPrivate::associative_erase_if(map, pred); +} + QT_END_NAMESPACE #endif // QMAP_H diff --git a/src/corelib/tools/qmap.qdoc b/src/corelib/tools/qmap.qdoc index ace09e7391..3fec85e5b2 100644 --- a/src/corelib/tools/qmap.qdoc +++ b/src/corelib/tools/qmap.qdoc @@ -334,6 +334,21 @@ \sa clear(), take() */ +/*! \fn template <class Key, class T> template <typename Predicate> size_type QMap<Key, T>::removeIf(Predicate pred) + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the map. + + The function supports predicates which take either an argument of + type \c{QMap<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template <class Key, class T> T QMap<Key, T>::take(const Key &key) Removes the item with the key \a key from the map and returns @@ -1372,3 +1387,17 @@ \sa{Serializing Qt Data Types}{Format of the QDataStream operators} */ + +/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QMap<Key, T> &map, Predicate pred) + \relates QMap + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the map \a map. + + The function supports predicates which take either an argument of + type \c{QMap<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. +*/ diff --git a/src/corelib/tools/qmultimap.qdoc b/src/corelib/tools/qmultimap.qdoc index 487480a927..3cfc6ea727 100644 --- a/src/corelib/tools/qmultimap.qdoc +++ b/src/corelib/tools/qmultimap.qdoc @@ -352,6 +352,21 @@ \sa clear(), take() */ +/*! \fn template <class Key, class T> template <typename Predicate> size_type QMultiMap<Key, T>::removeIf(Predicate pred) + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the multi map. + + The function supports predicates which take either an argument of + type \c{QMultiMap<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. + + \sa clear(), take() +*/ + /*! \fn template <class Key, class T> T QMultiMap<Key, T>::take(const Key &key) Removes the item with the key \a key from the multi map and returns @@ -1508,3 +1523,17 @@ \sa{Serializing Qt Data Types}{Format of the QDataStream operators} */ + +/*! \fn template <typename Key, typename T, typename Predicate> qsizetype erase_if(QMultiMap<Key, T> &map, Predicate pred) + \relates QMultiMap + \since 6.1 + + Removes all elements for which the predicate \a pred returns true + from the multi map \a map. + + The function supports predicates which take either an argument of + type \c{QMultiMap<Key, T>::iterator}, or an argument of type + \c{std::pair<const Key &, T &>}. + + Returns the number of elements removed, if any. +*/ diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index 4625353139..08fb9dcdc6 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -334,6 +334,9 @@ private: template <typename Container> void erase_if_impl() const; + template <typename Container> + void erase_if_associative_impl() const; + private Q_SLOTS: void erase_QList() { erase_impl<QList<int>>(); } void erase_QVarLengthArray() { erase_impl<QVarLengthArray<int>>(); } @@ -355,6 +358,10 @@ private Q_SLOTS: erase_if_impl<std::vector<int>>(); #endif } + void erase_if_QMap() { erase_if_associative_impl<QMap<int, int>>(); } + void erase_if_QMultiMap() {erase_if_associative_impl<QMultiMap<int, int>>(); } + void erase_if_QHash() { erase_if_associative_impl<QHash<int, int>>(); } + void erase_if_QMultIHash() { erase_if_associative_impl<QMultiHash<int, int>>(); } }; void tst_ContainerApiSymmetry::init() @@ -647,6 +654,17 @@ Container make(int size) return c; } +template <typename Container> +Container makeAssociative(int size) +{ + using K = typename Container::key_type; + using V = typename Container::mapped_type; + Container c; + for (int i = 1; i <= size; ++i) + c.insert(K(i), V(i)); + return c; +} + static QString s_string = QStringLiteral("\1\2\3\4\5\6\7"); template <> QString make(int size) { return s_string.left(size); } @@ -728,5 +746,54 @@ void tst_ContainerApiSymmetry::erase_if_impl() const QCOMPARE(c.size(), S(0)); } +template <typename Container> +void tst_ContainerApiSymmetry::erase_if_associative_impl() const +{ + using S = typename Container::size_type; + using K = typename Container::key_type; + using V = typename Container::mapped_type; + using I = typename Container::iterator; + using P = std::pair<const K &, V &>; + + auto c = makeAssociative<Container>(20); + QCOMPARE(c.size(), S(20)); + + auto result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 0; }); + QCOMPARE(result, S(10)); + QCOMPARE(c.size(), S(10)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 3 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 42 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const P &p) { return Conv::toInt(p.first) % 2 == 1; }); + QCOMPARE(result, S(7)); + QCOMPARE(c.size(), S(0)); + + // same, but with a predicate taking a Qt iterator + c = makeAssociative<Container>(20); + QCOMPARE(c.size(), S(20)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 0; }); + QCOMPARE(result, S(10)); + QCOMPARE(c.size(), S(10)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 3 == 0; }); + QCOMPARE(result, S(3)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 42 == 0; }); + QCOMPARE(result, S(0)); + QCOMPARE(c.size(), S(7)); + + result = erase_if(c, [](const I &it) { return Conv::toInt(it.key()) % 2 == 1; }); + QCOMPARE(result, S(7)); + QCOMPARE(c.size(), S(0)); +} + QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry) #include "tst_containerapisymmetry.moc" |