diff options
author | Marc Mutz <marc.mutz@kdab.com> | 2019-05-15 14:04:11 +0200 |
---|---|---|
committer | Marc Mutz <marc.mutz@kdab.com> | 2019-05-29 11:22:20 +0200 |
commit | 4469e36d7203a55a4e158a50f0e9effc3f2fa3c2 (patch) | |
tree | 64a164183e3a0d56446f9b39389ccd316476b89f /src/corelib/tools/qhashfunctions.h | |
parent | 0f777a3b75cd801775b04433f52d00e426c875b3 (diff) |
qhashfunctions.h: add specializations of std::hash for some Qt types
We have a problem. Our types don't play well with the std unordered
containers, because they do not specialize std::hash. We therefore
force our users to come up with an implementation, hindering
interoperability, since any two developers are unlikely to come up
with compatible implementations. So combining libraries written by
different developers will result in ODR violations.
Now that we depend on C++11, and thus the presence of std::hash, we
still face the problem that the standard does not provide us with a
means to compose new hash functions out of old ones. In particular, we
cannot, yet, depend on C++17's std::hash<std::string_view> to
implement std::hash<QByteArray>, say. There's also no std::hash for
std::tuple, which would allow easy composition by using std::tie().
So piggy-back on the work we have done over the years on qHash()
functions, and implement the std::hash specializations for Qt types
using the existing qHash() functions, with a twist: The standard
allows implementations to provide means against predictable hash
values. Qt has this, too, but the seed is managed by the container and
passed to the qHash() function as a separate argument. The standard
does not have this explicit seed, so any protection must be implicit
in the normal use of std::hash.
To reap whatever protection that std library has on offer, if any, we
calculate a seed value by hashing int(0). This will be subject to
constant folding if there's no actual seed, but will produce a value
dependent on the seed if there is one.
Add some tests.
A question that remains is how to document the specialization. Can we
have a \stdhashable QDoc macro that does everything for us?
Task-number: QTBUG-33428
Change-Id: Idfe775f1661f8489587353c4b148d76611ac76f3
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'src/corelib/tools/qhashfunctions.h')
-rw-r--r-- | src/corelib/tools/qhashfunctions.h | 36 |
1 files changed, 36 insertions, 0 deletions
diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h index 5e714806ff..2ff3464912 100644 --- a/src/corelib/tools/qhashfunctions.h +++ b/src/corelib/tools/qhashfunctions.h @@ -45,6 +45,7 @@ #include <QtCore/qpair.h> #include <numeric> // for std::accumulate +#include <functional> // for std::hash #if 0 #pragma qt_class(QHashFunctions) @@ -172,6 +173,41 @@ template <typename T1, typename T2> inline uint qHash(const std::pair<T1, T2> &k return seed; } +#define QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH(Class, Arguments) \ + QT_BEGIN_INCLUDE_NAMESPACE \ + namespace std { \ + template <> \ + struct hash< QT_PREPEND_NAMESPACE(Class) > { \ + using argument_type = QT_PREPEND_NAMESPACE(Class); \ + using result_type = size_t; \ + size_t operator()(Arguments s) const \ + noexcept(noexcept(QT_PREPEND_NAMESPACE(qHash)(s))) \ + { \ + /* this seeds qHash with the result of */ \ + /* std::hash applied to an int, to reap */ \ + /* any protection against predictable hash */ \ + /* values the std implementation may provide */ \ + return QT_PREPEND_NAMESPACE(qHash)(s, \ + QT_PREPEND_NAMESPACE(qHash)( \ + std::hash<int>{}(0))); \ + } \ + }; \ + } \ + QT_END_INCLUDE_NAMESPACE \ + /*end*/ + +#define QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(Class) \ + QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH(Class, const argument_type &) +#define QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(Class) \ + QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH(Class, argument_type) + +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QString) +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QStringRef) +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QStringView) +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QLatin1String) +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QByteArray) +QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QBitArray) + QT_END_NAMESPACE #if defined(Q_CC_MSVC) |