diff options
-rw-r--r-- | src/corelib/global/qtypeinfo.h | 114 | ||||
-rw-r--r-- | src/corelib/tools/qcontainerfwd.h | 5 | ||||
-rw-r--r-- | src/corelib/tools/qmap.h | 1 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp | 97 |
4 files changed, 216 insertions, 1 deletions
diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h index 121c9fdcd8..8b31fb4a81 100644 --- a/src/corelib/global/qtypeinfo.h +++ b/src/corelib/global/qtypeinfo.h @@ -39,6 +39,8 @@ ****************************************************************************/ #include <QtCore/qglobal.h> +#include <QtCore/qcontainerfwd.h> +#include <variant> #ifndef QTYPEINFO_H #define QTYPEINFO_H @@ -322,5 +324,117 @@ Q_DECLARE_TYPEINFO(wchar_t, Q_RELOCATABLE_TYPE); # endif #endif // Qt 6 +namespace QTypeTraits +{ + +/* + The templates below aim to find out whether one can safely instantiate an operator==() or + operator<() for a type. + + This is tricky for containers, as most containers have unconstrained comparison operators, even though they + rely on the corresponding operators for its content. + This is especially true for all of the STL template classes that have a comparison operator defined, and + leads to the situation, that the compiler would try to instantiate the operator, and fail if any + of its template arguments does not have the operator implemented. + + The code tries to cover the relevant cases for Qt and the STL, by checking (recusrsively) the value_type + of a container (if it exists), and checking the template arguments of pair, tuple and variant. +*/ +namespace detail { + +// find out whether T has a value_type typedef +// this is required to check the value type of containers for the existence of the comparison operator +template <typename, typename = void> +struct has_value_type : std::false_type {}; +template <typename T> +struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {}; + +// Checks the existence of the comparison operator for the class itself +template <typename, typename = void> +struct has_operator_equal : std::false_type {}; +template <typename T> +struct has_operator_equal<T, std::void_t<decltype(bool(std::declval<const T&>() == std::declval<const T&>()))>> + : std::true_type {}; + +// Two forward declarations +template<typename T, bool = has_value_type<T>::value> +struct expand_operator_equal_container; +template<typename T> +struct expand_operator_equal_tuple; + +// the entry point for the public method +template<typename T> +using expand_operator_equal = expand_operator_equal_container<T>; + +// if T doesn't have a value_type member check if it's a tuple like object +template<typename T, bool> +struct expand_operator_equal_container : expand_operator_equal_tuple<T> {}; +// if T::value_type exists, check first T::value_type, then T itself +template<typename T> +struct expand_operator_equal_container<T, true> : + std::conjunction<expand_operator_equal<typename T::value_type>, expand_operator_equal_tuple<T>> {}; + +// recursively check the template arguments of a tuple like object +template<typename ...T> +using expand_operator_equal_recursive = std::conjunction<expand_operator_equal<T>...>; + +template<typename T> +struct expand_operator_equal_tuple : has_operator_equal<T> {}; +template<typename T1, typename T2> +struct expand_operator_equal_tuple<std::pair<T1, T2>> : expand_operator_equal_recursive<T1, T2> {}; +template<typename ...T> +struct expand_operator_equal_tuple<std::tuple<T...>> : expand_operator_equal_recursive<T...> {}; +template<typename ...T> +struct expand_operator_equal_tuple<std::variant<T...>> : expand_operator_equal_recursive<T...> {}; + +// the same for operator<(), see above for explanations +template <typename, typename = void> +struct has_operator_less_than : std::false_type{}; +template <typename T> +struct has_operator_less_than<T, std::void_t<decltype(bool(std::declval<const T&>() < std::declval<const T&>()))>> + : std::true_type{}; + +template<typename T, bool = has_value_type<T>::value> +struct expand_operator_less_than_container; +template<typename T> +struct expand_operator_less_than_tuple; + +template<typename T> +using expand_operator_less_than = expand_operator_less_than_container<T>; + +template<typename T, bool> +struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {}; +template<typename T> +struct expand_operator_less_than_container<T, true> : + std::conjunction<expand_operator_less_than<typename T::value_type>, expand_operator_less_than_tuple<T>> {}; + +template<typename ...T> +using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>; + +template<typename T> +struct expand_operator_less_than_tuple : has_operator_less_than<T> {}; +template<typename T1, typename T2> +struct expand_operator_less_than_tuple<std::pair<T1, T2>> : expand_operator_less_than_recursive<T1, T2> {}; +template<typename ...T> +struct expand_operator_less_than_tuple<std::tuple<T...>> : expand_operator_less_than_recursive<T...> {}; +template<typename ...T> +struct expand_operator_less_than_tuple<std::variant<T...>> : expand_operator_less_than_recursive<T...> {}; + +} + +template<typename T> +struct has_operator_equal : detail::expand_operator_equal<T> {}; +template<typename T> +constexpr bool has_operator_equal_v = has_operator_equal<T>::value; + +template<typename T> +struct has_operator_less_than : detail::expand_operator_less_than<T> {}; +template<typename T> +constexpr bool has_operator_less_than_v = has_operator_less_than<T>::value; + + +} + + QT_END_NAMESPACE #endif // QTYPEINFO_H diff --git a/src/corelib/tools/qcontainerfwd.h b/src/corelib/tools/qcontainerfwd.h index 715583e03b..4aec3574ff 100644 --- a/src/corelib/tools/qcontainerfwd.h +++ b/src/corelib/tools/qcontainerfwd.h @@ -42,8 +42,11 @@ #include <QtCore/qglobal.h> -QT_BEGIN_NAMESPACE +// std headers can unfortunately not be forward declared +#include <tuple> +#include <variant> +QT_BEGIN_NAMESPACE template <class Key, class T> class QCache; template <class Key, class T> class QHash; diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index 848ba5c4d4..a44f0088d3 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -567,6 +567,7 @@ public: // STL compatibility typedef Key key_type; typedef T mapped_type; + typedef T value_type; typedef qptrdiff difference_type; typedef qsizetype size_type; inline bool empty() const { return isEmpty(); } diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 8d4cc05eb9..22d481f5e3 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -48,6 +48,103 @@ Q_DECLARE_METATYPE(QMetaType::Type) +namespace CheckTypeTraits +{ +struct NoOperators +{ + int x; +}; +using Nested = QVector<std::pair<int, QMap<QStringList, QVariant>>>; +using Nested2 = QVector<std::pair<int, QVector<QPair<QStringList, QVariant>>>>; + +// basic types +static_assert(QTypeTraits::has_operator_equal_v<bool>); +static_assert(QTypeTraits::has_operator_less_than_v<bool>); +static_assert(QTypeTraits::has_operator_equal_v<int>); +static_assert(QTypeTraits::has_operator_less_than_v<int>); +static_assert(QTypeTraits::has_operator_equal_v<double>); +static_assert(QTypeTraits::has_operator_less_than_v<double>); + +// no comparison operators +static_assert(!QTypeTraits::has_operator_equal_v<NoOperators>); +static_assert(!QTypeTraits::has_operator_less_than_v<NoOperators>); + +// standard Qt types +static_assert(QTypeTraits::has_operator_equal_v<QString>); +static_assert(QTypeTraits::has_operator_less_than_v<QString>); +static_assert(QTypeTraits::has_operator_equal_v<QVariant>); +static_assert(!QTypeTraits::has_operator_less_than_v<QVariant>); + +// QList +static_assert(QTypeTraits::has_operator_equal_v<QStringList>); +static_assert(QTypeTraits::has_operator_less_than_v<QStringList>); +static_assert(!QTypeTraits::has_operator_equal_v<QList<NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<QList<NoOperators>>); +static_assert(QTypeTraits::has_operator_equal_v<QList<QVariant>>); +static_assert(!QTypeTraits::has_operator_less_than_v<QList<QVariant>>); + +// QPair +static_assert(QTypeTraits::has_operator_equal_v<QPair<int, QString>>); +static_assert(QTypeTraits::has_operator_less_than_v<QPair<int, QString>>); +static_assert(!QTypeTraits::has_operator_equal_v<QPair<int, NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<QPair<int, NoOperators>>); + +// QMap +static_assert(QTypeTraits::has_operator_equal_v<QMap<int, QString>>); +static_assert(!QTypeTraits::has_operator_less_than_v<QMap<int, QString>>); +static_assert(!QTypeTraits::has_operator_equal_v<QMap<int, NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<QMap<int, NoOperators>>); + +// QHash +static_assert(QTypeTraits::has_operator_equal_v<QHash<int, QString>>); +static_assert(!QTypeTraits::has_operator_less_than_v<QHash<int, QString>>); +static_assert(!QTypeTraits::has_operator_equal_v<QHash<int, NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<QHash<int, NoOperators>>); + +// std::vector +static_assert(QTypeTraits::has_operator_equal_v<std::vector<QString>>); +static_assert(QTypeTraits::has_operator_less_than_v<std::vector<QString>>); +static_assert(!QTypeTraits::has_operator_equal_v<std::vector<NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::vector<NoOperators>>); +static_assert(QTypeTraits::has_operator_equal_v<std::vector<QVariant>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::vector<QVariant>>); + +// std::pair +static_assert(QTypeTraits::has_operator_equal_v<std::pair<int, QString>>); +static_assert(QTypeTraits::has_operator_less_than_v<std::pair<int, QString>>); +static_assert(!QTypeTraits::has_operator_equal_v<std::pair<int, NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::pair<int, NoOperators>>); + +// std::tuple +static_assert(QTypeTraits::has_operator_equal_v<std::tuple<int, QString, double>>); +static_assert(QTypeTraits::has_operator_less_than_v<std::tuple<int, QString, double>>); +static_assert(!QTypeTraits::has_operator_equal_v<std::tuple<int, QString, NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::tuple<int, QString, NoOperators>>); + +// std::map +static_assert(QTypeTraits::has_operator_equal_v<std::map<int, QString>>); +static_assert(QTypeTraits::has_operator_less_than_v<std::map<int, QString>>); +static_assert(!QTypeTraits::has_operator_equal_v<std::map<int, NoOperators>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::map<int, NoOperators>>); + +// nested types +static_assert(QTypeTraits::has_operator_equal_v<Nested>); +static_assert(!QTypeTraits::has_operator_less_than_v<Nested>); +static_assert(QTypeTraits::has_operator_equal_v<std::tuple<int, Nested>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::tuple<int, Nested>>); +static_assert(QTypeTraits::has_operator_equal_v<std::tuple<int, Nested>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::tuple<int, Nested>>); + +static_assert(QTypeTraits::has_operator_equal_v<Nested2>); +static_assert(!QTypeTraits::has_operator_less_than_v<Nested2>); +static_assert(QTypeTraits::has_operator_equal_v<std::tuple<int, Nested2>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::tuple<int, Nested2>>); +static_assert(QTypeTraits::has_operator_equal_v<std::tuple<int, Nested2>>); +static_assert(!QTypeTraits::has_operator_less_than_v<std::tuple<int, Nested2>>); + +} + + class tst_QMetaType: public QObject { Q_OBJECT |