diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2020-02-14 16:16:56 +0100 |
---|---|---|
committer | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2020-05-12 01:51:20 +0200 |
commit | 707129fd5a7c6390fbdf4270119226df2a427fcd (patch) | |
tree | 3394b456eae374078330684d0523b7874ac630de | |
parent | 2844631aa977fb1619ec3910090d1bdd4c7459c2 (diff) |
Long live qHashMulti(Commutative)
Add a helper function so that we have a shortcut.
Instead of writing:
QHashCombine hash;
seed = hash(seed, fieldA);
seed = hash(seed, fieldB);
// etc.
return seed;
one can now simply write:
return qHashMulti(seed, fieldA, fieldB, fieldC);
Port a few usages inside qtbase as a demonstration.
[ChangeLog][QtCore][QHash] Added the qHashMulti and
qHashMultiCommutative functions as convenience helpers
to calculate a hash from multiple variables (typically,
data members of a class).
Change-Id: I881a9ad41168df20ceecc6588a94abe7ddc6a532
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
-rw-r--r-- | src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp | 2 | ||||
-rw-r--r-- | src/corelib/text/qbytearray.cpp | 5 | ||||
-rw-r--r-- | src/corelib/text/qlocale.cpp | 5 | ||||
-rw-r--r-- | src/corelib/text/qregexp.cpp | 6 | ||||
-rw-r--r-- | src/corelib/text/qregularexpression.cpp | 5 | ||||
-rw-r--r-- | src/corelib/tools/qhash.cpp | 53 | ||||
-rw-r--r-- | src/corelib/tools/qhashfunctions.h | 51 | ||||
-rw-r--r-- | src/gui/text/qfont_p.h | 27 | ||||
-rw-r--r-- | src/gui/text/qfontengine_p.h | 7 |
9 files changed, 114 insertions, 47 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp index 1f2c505a02..344fbb241d 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp @@ -151,7 +151,7 @@ inline bool operator==(const Employee &e1, const Employee &e2) inline size_t qHash(const Employee &key, size_t seed) { - return qHash(key.name(), seed) ^ key.dateOfBirth().day(); + return qHashMulti(seed, key.name(), key.dateOfBirth()); } #endif // EMPLOYEE_H diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index b778316a15..8d70955065 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -5030,10 +5030,7 @@ QByteArray QByteArray::toPercentEncoding(const QByteArray &exclude, const QByteA */ size_t qHash(const QByteArray::FromBase64Result &key, size_t seed) noexcept { - QtPrivate::QHashCombine hash; - seed = hash(seed, key.decoded); - seed = hash(seed, static_cast<int>(key.decodingStatus)); - return seed; + return qHashMulti(seed, key.decoded, static_cast<int>(key.decodingStatus)); } QT_END_NAMESPACE diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 2ddb27fc6e..8df28797bd 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -1091,10 +1091,7 @@ bool QLocale::operator!=(const QLocale &other) const */ size_t qHash(const QLocale &key, size_t seed) noexcept { - QtPrivate::QHashCombine hash; - seed = hash(seed, key.d->m_data); - seed = hash(seed, key.d->m_numberOptions); - return seed; + return qHashMulti(seed, key.d->m_data, key.d->m_numberOptions); } /*! diff --git a/src/corelib/text/qregexp.cpp b/src/corelib/text/qregexp.cpp index bf76a1992c..758a3695c9 100644 --- a/src/corelib/text/qregexp.cpp +++ b/src/corelib/text/qregexp.cpp @@ -1030,11 +1030,7 @@ static bool operator==(const QRegExpEngineKey &key1, const QRegExpEngineKey &key static size_t qHash(const QRegExpEngineKey &key, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; - seed = hash(seed, key.pattern); - seed = hash(seed, key.patternSyntax); - seed = hash(seed, key.cs); - return seed; + return qHashMulti(seed, key.pattern, key.patternSyntax, key.cs); } class QRegExpEngine; diff --git a/src/corelib/text/qregularexpression.cpp b/src/corelib/text/qregularexpression.cpp index f479425bed..8197407146 100644 --- a/src/corelib/text/qregularexpression.cpp +++ b/src/corelib/text/qregularexpression.cpp @@ -1712,10 +1712,7 @@ bool QRegularExpression::operator==(const QRegularExpression &re) const */ size_t qHash(const QRegularExpression &key, size_t seed) noexcept { - QtPrivate::QHashCombine hash; - seed = hash(seed, key.d->pattern); - seed = hash(seed, key.d->patternOptions); - return seed; + return qHashMulti(seed, key.d->pattern, key.d->patternOptions); } #if QT_STRINGVIEW_LEVEL < 2 diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index bf5031523d..b1ebd29f09 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -596,6 +596,48 @@ uint qt_hash(QStringView key, uint chained) noexcept constraints, we cannot change the QPair algorithm to match the std::pair one before Qt 6. */ +/*! + \fn template <typename... T> size_t qHashMulti(size_t seed, const T &...args) + \relates QHash + \since 6.0 + + Returns the hash value for the \a{args}, using \a seed to seed + the calculation, by successively applying qHash() to each + element and combining the hash values into a single one. + + Note that the order of the arguments is significant. If order does + not matter, use qHashMultiCommutative() instead. If you are hashing raw + memory, use qHashBits(); if you are hashing a range, use qHashRange(). + + This function is provided as a convenience to implement qHash() for + your own custom types. For example, here's how you could implement + a qHash() overload for a class \c{Employee}: + + \snippet code/src_corelib_tools_qhash.cpp 13 + + \sa qHashMultiCommutative, qHashRange +*/ + +/*! + \fn template <typename... T> size_t qHashMultiCommutative(size_t seed, const T &...args) + \relates QHash + \since 6.0 + + Returns the hash value for the \a{args}, using \a seed to seed + the calculation, by successively applying qHash() to each + element and combining the hash values into a single one. + + The order of the arguments is insignificant. If order does + matter, use qHashMulti() instead, as it may produce better quality + hashing. If you are hashing raw memory, use qHashBits(); if you are + hashing a range, use qHashRange(). + + This function is provided as a convenience to implement qHash() for + your own custom types. + + \sa qHashMulti, qHashRange +*/ + /*! \fn template <typename InputIterator> size_t qHashRange(InputIterator first, InputIterator last, size_t seed = 0) \relates QHash \since 5.5 @@ -1059,15 +1101,16 @@ size_t qHash(long double key, size_t seed) noexcept the documentation of each class. If you want to use other types as the key, make sure that you provide - operator==() and a qHash() implementation. + operator==() and a qHash() implementation. The convenience qHashMulti() + function can be used to implement qHash() for a custom type, where + one usually wants to produce a hash value from multiple fields: Example: \snippet code/src_corelib_tools_qhash.cpp 13 - In the example above, we've relied on Qt's global qHash(const - QString &, uint) to give us a hash value for the employee's name, and - XOR'ed this with the day they were born to help produce unique - hashes for people with the same name. + In the example above, we've relied on Qt's own implementation of + qHash() for QString and QDate to give us a hash value for the + employee's name and date of birth respectively. Note that the implementation of the qHash() overloads offered by Qt may change at any time. You \b{must not} rely on the fact that qHash() diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index aaa7758758..5742540061 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -190,8 +190,54 @@ struct QHashCombineCommutative { { return seed + qHash(t); } // don't use xor! }; +template <typename... T> +using QHashMultiReturnType = decltype( + std::declval< std::enable_if_t<(sizeof...(T) > 0)> >(), + (qHash(std::declval<const T &>()), ...), + size_t{} +); + +// workaround for a MSVC ICE, +// https://developercommunity.visualstudio.com/content/problem/996540/internal-compiler-error-on-msvc-1924-when-doing-sf.html +template <typename T> +inline constexpr bool QNothrowHashableHelper_v = noexcept(qHash(std::declval<const T &>())); + +template <typename T, typename Enable = void> +struct QNothrowHashable : std::false_type {}; + +template <typename T> +struct QNothrowHashable<T, std::enable_if_t<QNothrowHashableHelper_v<T>>> : std::true_type {}; + } // namespace QtPrivate +template <typename... T> +constexpr +#ifdef Q_QDOC +size_t +#else +QtPrivate::QHashMultiReturnType<T...> +#endif +qHashMulti(size_t seed, const T &... args) + noexcept(std::conjunction_v<QtPrivate::QNothrowHashable<T>...>) +{ + QtPrivate::QHashCombine hash; + return ((seed = hash(seed, args)), ...), seed; +} + +template <typename... T> +constexpr +#ifdef Q_QDOC +size_t +#else +QtPrivate::QHashMultiReturnType<T...> +#endif +qHashMultiCommutative(size_t seed, const T &... args) + noexcept(std::conjunction_v<QtPrivate::QNothrowHashable<T>...>) +{ + QtPrivate::QHashCombineCommutative hash; + return ((seed = hash(seed, args)), ...), seed; +} + template <typename InputIterator> inline size_t qHashRange(InputIterator first, InputIterator last, size_t seed = 0) noexcept(noexcept(qHash(*first))) // assume iterator operations don't throw @@ -218,10 +264,7 @@ template <typename T1, typename T2> inline size_t qHash(const QPair<T1, T2> &key template <typename T1, typename T2> inline size_t qHash(const std::pair<T1, T2> &key, size_t seed = 0) noexcept(noexcept(qHash(key.first, seed)) && noexcept(qHash(key.second, seed))) { - QtPrivate::QHashCombine hash; - seed = hash(seed, key.first); - seed = hash(seed, key.second); - return seed; + return qHashMulti(seed, key.first, key.second); } #define QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH(Class, Arguments) \ diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index cc210a695b..5d8f0daacd 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -138,20 +138,19 @@ struct QFontDef inline size_t qHash(const QFontDef &fd, size_t seed = 0) noexcept { - QtPrivate::QHashCombine hash; - seed = hash(seed, qRound64(fd.pixelSize*10000)); // use only 4 fractional digits - seed = hash(seed, fd.weight); - seed = hash(seed, fd.style); - seed = hash(seed, fd.stretch); - seed = hash(seed, fd.styleHint); - seed = hash(seed, fd.styleStrategy); - seed = hash(seed, fd.ignorePitch); - seed = hash(seed, fd.fixedPitch); - seed = hash(seed, fd.family); - seed = hash(seed, fd.families); - seed = hash(seed, fd.styleName); - seed = hash(seed, fd.hintingPreference); - return seed; + return qHashMulti(seed, + qRound64(fd.pixelSize*10000), // use only 4 fractional digits + fd.weight, + fd.style, + fd.stretch, + fd.styleHint, + fd.styleStrategy, + fd.ignorePitch, + fd.fixedPitch, + fd.family, + fd.families, + fd.styleName, + fd.hintingPreference); } class QFontEngineData diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index cbfc985eec..7a0ccca6a0 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -410,12 +410,7 @@ inline bool operator ==(const QFontEngine::FaceId &f1, const QFontEngine::FaceId inline size_t qHash(const QFontEngine::FaceId &f, size_t seed = 0) noexcept(noexcept(qHash(f.filename))) { - QtPrivate::QHashCombine hash; - seed = hash(seed, f.filename); - seed = hash(seed, f.uuid); - seed = hash(seed, f.index); - seed = hash(seed, f.encoding); - return seed; + return qHashMulti(seed, f.filename, f.uuid, f.index, f.encoding); } |