summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2020-02-14 16:16:56 +0100
committerGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2020-05-12 01:51:20 +0200
commit707129fd5a7c6390fbdf4270119226df2a427fcd (patch)
tree3394b456eae374078330684d0523b7874ac630de /src
parent2844631aa977fb1619ec3910090d1bdd4c7459c2 (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>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_tools_qhash.cpp2
-rw-r--r--src/corelib/text/qbytearray.cpp5
-rw-r--r--src/corelib/text/qlocale.cpp5
-rw-r--r--src/corelib/text/qregexp.cpp6
-rw-r--r--src/corelib/text/qregularexpression.cpp5
-rw-r--r--src/corelib/tools/qhash.cpp53
-rw-r--r--src/corelib/tools/qhashfunctions.h51
-rw-r--r--src/gui/text/qfont_p.h27
-rw-r--r--src/gui/text/qfontengine_p.h7
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);
}