summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qtypeinfo.h114
-rw-r--r--src/corelib/tools/qcontainerfwd.h5
-rw-r--r--src/corelib/tools/qmap.h1
-rw-r--r--tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp97
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