Constrain the debug stream operators for containers

Check that we can successfully instantiate the debug
stream operator for a container before we actually try.

This is required so we can automate registration of debug
stream operators with QMetaType.

Change-Id: I3943e7a443751d250c33b2ca1b9cf29207cfe6c4
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Lars Knoll 2020-07-06 13:08:40 +02:00
parent 6c36fd8af7
commit 8ad9e81694
3 changed files with 41 additions and 12 deletions

View File

@ -48,6 +48,8 @@
QT_BEGIN_NAMESPACE
class QDebug;
/*
QTypeInfo - type trait functionality
*/
@ -445,6 +447,23 @@ using compare_eq_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_o
template <typename ...T>
using compare_lt_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than<T>...>, bool>;
namespace detail {
template<typename T>
const T const_value();
template<typename T>
T &reference();
}
template <typename Stream, typename, typename = void>
struct has_ostream_operator : std::false_type {};
template <typename Stream, typename T>
struct has_ostream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() << detail::const_value<T>())>>
: std::true_type {};
template <typename Stream, typename T>
constexpr bool has_ostream_operator_v = has_ostream_operator<Stream, T>::value;
}

View File

@ -262,62 +262,66 @@ inline QDebug printAssociativeContainer(QDebug debug, const char *which, const A
} // namespace QtPrivate
template<typename ...T>
using QDebugIfHasDebugStream =
std::enable_if_t<std::conjunction_v<QTypeTraits::has_ostream_operator<QDebug, T>...>, QDebug>;
template<typename T>
inline QDebug operator<<(QDebug debug, const QList<T> &vec)
inline QDebugIfHasDebugStream<T> operator<<(QDebug debug, const QList<T> &vec)
{
return QtPrivate::printSequentialContainer(debug, "QList", vec);
}
template <typename T, typename Alloc>
inline QDebug operator<<(QDebug debug, const std::vector<T, Alloc> &vec)
inline QDebugIfHasDebugStream<T> operator<<(QDebug debug, const std::vector<T, Alloc> &vec)
{
return QtPrivate::printSequentialContainer(debug, "std::vector", vec);
}
template <typename T, typename Alloc>
inline QDebug operator<<(QDebug debug, const std::list<T, Alloc> &vec)
inline QDebugIfHasDebugStream<T> operator<<(QDebug debug, const std::list<T, Alloc> &vec)
{
return QtPrivate::printSequentialContainer(debug, "std::list", vec);
}
template <typename Key, typename T, typename Compare, typename Alloc>
inline QDebug operator<<(QDebug debug, const std::map<Key, T, Compare, Alloc> &map)
inline QDebugIfHasDebugStream<Key, T> operator<<(QDebug debug, const std::map<Key, T, Compare, Alloc> &map)
{
return QtPrivate::printSequentialContainer(debug, "std::map", map); // yes, sequential: *it is std::pair
}
template <typename Key, typename T, typename Compare, typename Alloc>
inline QDebug operator<<(QDebug debug, const std::multimap<Key, T, Compare, Alloc> &map)
inline QDebugIfHasDebugStream<Key, T> operator<<(QDebug debug, const std::multimap<Key, T, Compare, Alloc> &map)
{
return QtPrivate::printSequentialContainer(debug, "std::multimap", map); // yes, sequential: *it is std::pair
}
template <class Key, class T>
inline QDebug operator<<(QDebug debug, const QMap<Key, T> &map)
inline QDebugIfHasDebugStream<Key, T> operator<<(QDebug debug, const QMap<Key, T> &map)
{
return QtPrivate::printAssociativeContainer(debug, "QMap", map);
}
template <class Key, class T>
inline QDebug operator<<(QDebug debug, const QMultiMap<Key, T> &map)
inline QDebugIfHasDebugStream<Key, T> operator<<(QDebug debug, const QMultiMap<Key, T> &map)
{
return QtPrivate::printAssociativeContainer(debug, "QMultiMap", map);
}
template <class Key, class T>
inline QDebug operator<<(QDebug debug, const QHash<Key, T> &hash)
inline QDebugIfHasDebugStream<Key, T> operator<<(QDebug debug, const QHash<Key, T> &hash)
{
return QtPrivate::printAssociativeContainer(debug, "QHash", hash);
}
template <class Key, class T>
inline QDebug operator<<(QDebug debug, const QMultiHash<Key, T> &hash)
inline QDebugIfHasDebugStream<Key, T> operator<<(QDebug debug, const QMultiHash<Key, T> &hash)
{
return QtPrivate::printAssociativeContainer(debug, "QMultiHash", hash);
}
template <class T1, class T2>
inline QDebug operator<<(QDebug debug, const std::pair<T1, T2> &pair)
inline QDebugIfHasDebugStream<T1, T2> operator<<(QDebug debug, const std::pair<T1, T2> &pair)
{
const QDebugStateSaver saver(debug);
debug.nospace() << "std::pair(" << pair.first << ',' << pair.second << ')';
@ -325,13 +329,13 @@ inline QDebug operator<<(QDebug debug, const std::pair<T1, T2> &pair)
}
template <typename T>
inline QDebug operator<<(QDebug debug, const QSet<T> &set)
inline QDebugIfHasDebugStream<T> operator<<(QDebug debug, const QSet<T> &set)
{
return QtPrivate::printSequentialContainer(debug, "QSet", set);
}
template <class T>
inline QDebug operator<<(QDebug debug, const QContiguousCache<T> &cache)
inline QDebugIfHasDebugStream<T> operator<<(QDebug debug, const QContiguousCache<T> &cache)
{
const QDebugStateSaver saver(debug);
debug.nospace() << "QContiguousCache(";

View File

@ -35,6 +35,12 @@
#include <QtConcurrentRun>
#include <QFutureSynchronizer>
static_assert(QTypeTraits::has_ostream_v<QDebug, int>);
static_assert(QTypeTraits::has_ostream_v<QDebug, QList<int>>);
struct NonStreamable {};
static_assert(!QTypeTraits::has_ostream_v<QDebug, NonStreamable>);
static_assert(!QTypeTraits::has_ostream_v<QDebug, QList<NonStreamable>>);
class tst_QDebug: public QObject
{
Q_OBJECT