diff options
Diffstat (limited to 'src/corelib/global/qtypeinfo.h')
-rw-r--r-- | src/corelib/global/qtypeinfo.h | 462 |
1 files changed, 259 insertions, 203 deletions
diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h index 121c9fdcd8..255a2b33c6 100644 --- a/src/corelib/global/qtypeinfo.h +++ b/src/corelib/global/qtypeinfo.h @@ -1,64 +1,48 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qglobal.h> +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QTYPEINFO_H #define QTYPEINFO_H +#include <QtCore/qcompilerdetection.h> +#include <QtCore/qcontainerfwd.h> + +#include <variant> +#include <optional> +#include <tuple> +#include <type_traits> + QT_BEGIN_NAMESPACE +class QDebug; + /* QTypeInfo - type trait functionality */ -template <typename T> -static constexpr bool qIsRelocatable() -{ - return std::is_trivially_copyable<T>::value && std::is_trivially_destructible<T>::value; -} +namespace QtPrivate { +// A trivially copyable class must also have a trivial, non-deleted +// destructor [class.prop/1.3], CWG1734. Some implementations don't +// check for a trivial destructor, because of backwards compatibility +// with C++98's definition of trivial copyability. +// Since trivial copiability has implications for the ABI, implementations +// can't "just fix" their traits. So, although formally redundant, we +// explicitly check for trivial destruction here. template <typename T> -static constexpr bool qIsTrivial() -{ - return std::is_trivial<T>::value; +inline constexpr bool qIsRelocatable = std::is_trivially_copyable_v<T> && std::is_trivially_destructible_v<T>; + +// Denotes types that are trivially default constructible, and for which +// value-initialization can be achieved by filling their storage with 0 bits. +// There is no type trait we can use for this, so we hardcode a list of +// possibilities that we know are OK on the architectures that we support. +// The most notable exception are pointers to data members, which for instance +// on the Itanium ABI are initialized to -1. +template <typename T> +inline constexpr bool qIsValueInitializationBitwiseZero = + std::is_scalar_v<T> && !std::is_member_object_pointer_v<T>; + } /* @@ -70,12 +54,11 @@ class QTypeInfo { public: enum { - isPointer = false, - isIntegral = std::is_integral<T>::value, - isComplex = !qIsTrivial<T>(), - isStatic = true, - isRelocatable = qIsRelocatable<T>(), - sizeOf = sizeof(T) + isPointer [[deprecated("Use std::is_pointer instead")]] = std::is_pointer_v<T>, + isIntegral [[deprecated("Use std::is_integral instead")]] = std::is_integral_v<T>, + isComplex = !std::is_trivial_v<T>, + isRelocatable = QtPrivate::qIsRelocatable<T>, + isValueInitializationBitwiseZero = QtPrivate::qIsValueInitializationBitwiseZero<T>, }; }; @@ -84,55 +67,14 @@ class QTypeInfo<void> { public: enum { - isPointer = false, - isIntegral = false, + isPointer [[deprecated("Use std::is_pointer instead")]] = false, + isIntegral [[deprecated("Use std::is_integral instead")]] = false, isComplex = false, - isStatic = false, isRelocatable = false, - sizeOf = 0 + isValueInitializationBitwiseZero = false, }; }; -template <typename T> -class QTypeInfo<T*> -{ -public: - enum { - isPointer = true, - isIntegral = false, - isComplex = false, - isStatic = false, - isRelocatable = true, - sizeOf = sizeof(T*) - }; -}; - -/*! - \class QTypeInfoQuery - \inmodule QtCore - \internal - \brief QTypeInfoQuery is used to query the values of a given QTypeInfo<T> - - We use it because there may be some QTypeInfo<T> specializations in user - code that don't provide certain flags that we added after Qt 5.0. They are: - \list - \li isRelocatable: defaults to !isStatic - \endlist - - DO NOT specialize this class elsewhere. -*/ -// apply defaults for a generic QTypeInfo<T> that didn't provide the new values -template <typename T, typename = void> -struct QTypeInfoQuery : public QTypeInfo<T> -{ - enum { isRelocatable = !QTypeInfo<T>::isStatic }; -}; - -// if QTypeInfo<T>::isRelocatable exists, use it -template <typename T> -struct QTypeInfoQuery<T, typename std::enable_if<QTypeInfo<T>::isRelocatable || true>::type> : public QTypeInfo<T> -{}; - /*! \class QTypeInfoMerger \inmodule QtCore @@ -158,27 +100,35 @@ class QTypeInfoMerger { static_assert(sizeof...(Ts) > 0); public: - static constexpr bool isComplex = ((QTypeInfoQuery<Ts>::isComplex) || ...); - static constexpr bool isStatic = ((QTypeInfoQuery<Ts>::isStatic) || ...); - static constexpr bool isRelocatable = ((QTypeInfoQuery<Ts>::isRelocatable) && ...); - static constexpr bool isPointer = false; - static constexpr bool isIntegral = false; - static constexpr std::size_t sizeOf = sizeof(T); + static constexpr bool isComplex = ((QTypeInfo<Ts>::isComplex) || ...); + static constexpr bool isRelocatable = ((QTypeInfo<Ts>::isRelocatable) && ...); + [[deprecated("Use std::is_pointer instead")]] static constexpr bool isPointer = false; + [[deprecated("Use std::is_integral instead")]] static constexpr bool isIntegral = false; + static constexpr bool isValueInitializationBitwiseZero = false; + static_assert(!isRelocatable || + std::is_copy_constructible_v<T> || + std::is_move_constructible_v<T>, + "All Ts... are Q_RELOCATABLE_TYPE, but T is neither copy- nor move-constructible, " + "so cannot be Q_RELOCATABLE_TYPE. Please mark T as Q_COMPLEX_TYPE manually."); }; +// QTypeInfo for std::pair: +// std::pair is spec'ed to be struct { T1 first; T2 second; }, so, unlike tuple<>, +// we _can_ specialize QTypeInfo for pair<>: +template <class T1, class T2> +class QTypeInfo<std::pair<T1, T2>> : public QTypeInfoMerger<std::pair<T1, T2>, T1, T2> {}; + #define Q_DECLARE_MOVABLE_CONTAINER(CONTAINER) \ -template <typename T> class CONTAINER; \ -template <typename T> \ -class QTypeInfo< CONTAINER<T> > \ +template <typename ...T> \ +class QTypeInfo<CONTAINER<T...>> \ { \ public: \ enum { \ - isPointer = false, \ - isIntegral = false, \ + isPointer [[deprecated("Use std::is_pointer instead")]] = false, \ + isIntegral [[deprecated("Use std::is_integral instead")]] = false, \ isComplex = true, \ isRelocatable = true, \ - isStatic = false, \ - sizeOf = sizeof(CONTAINER<T>) \ + isValueInitializationBitwiseZero = false, \ }; \ } @@ -186,29 +136,11 @@ Q_DECLARE_MOVABLE_CONTAINER(QList); Q_DECLARE_MOVABLE_CONTAINER(QQueue); Q_DECLARE_MOVABLE_CONTAINER(QStack); Q_DECLARE_MOVABLE_CONTAINER(QSet); - -#undef Q_DECLARE_MOVABLE_CONTAINER - -#define Q_DECLARE_MOVABLE_CONTAINER(CONTAINER) \ -template <typename K, typename V> class CONTAINER; \ -template <typename K, typename V> \ -class QTypeInfo< CONTAINER<K, V> > \ -{ \ -public: \ - enum { \ - isPointer = false, \ - isIntegral = false, \ - isComplex = true, \ - isStatic = false, \ - isRelocatable = true, \ - sizeOf = sizeof(CONTAINER<K, V>) \ - }; \ -} - Q_DECLARE_MOVABLE_CONTAINER(QMap); Q_DECLARE_MOVABLE_CONTAINER(QMultiMap); Q_DECLARE_MOVABLE_CONTAINER(QHash); Q_DECLARE_MOVABLE_CONTAINER(QMultiHash); +Q_DECLARE_MOVABLE_CONTAINER(QCache); #undef Q_DECLARE_MOVABLE_CONTAINER @@ -223,10 +155,9 @@ Q_DECLARE_MOVABLE_CONTAINER(QMultiHash); enum { /* TYPEINFO flags */ Q_COMPLEX_TYPE = 0, Q_PRIMITIVE_TYPE = 0x1, - Q_STATIC_TYPE = 0, - Q_MOVABLE_TYPE = 0x2, // ### Qt6: merge movable and relocatable once QList no longer depends on it + Q_RELOCATABLE_TYPE = 0x2, + Q_MOVABLE_TYPE = 0x2, Q_DUMMY_TYPE = 0x4, - Q_RELOCATABLE_TYPE = 0x8 }; #define Q_DECLARE_TYPEINFO_BODY(TYPE, FLAGS) \ @@ -234,14 +165,16 @@ class QTypeInfo<TYPE > \ { \ public: \ enum { \ - isComplex = (((FLAGS) & Q_PRIMITIVE_TYPE) == 0) && !qIsTrivial<TYPE>(), \ - isStatic = (((FLAGS) & (Q_MOVABLE_TYPE | Q_PRIMITIVE_TYPE)) == 0), \ - isRelocatable = !isStatic || ((FLAGS) & Q_RELOCATABLE_TYPE) || qIsRelocatable<TYPE>(), \ - isPointer = false, \ - isIntegral = std::is_integral< TYPE >::value, \ - sizeOf = sizeof(TYPE) \ + isComplex = (((FLAGS) & Q_PRIMITIVE_TYPE) == 0) && !std::is_trivial_v<TYPE>, \ + isRelocatable = !isComplex || ((FLAGS) & Q_RELOCATABLE_TYPE) || QtPrivate::qIsRelocatable<TYPE>, \ + isPointer [[deprecated("Use std::is_pointer instead")]] = std::is_pointer_v< TYPE >, \ + isIntegral [[deprecated("Use std::is_integral instead")]] = std::is_integral< TYPE >::value, \ + isValueInitializationBitwiseZero = QtPrivate::qIsValueInitializationBitwiseZero<TYPE>, \ }; \ - static inline const char *name() { return #TYPE; } \ + static_assert(!isRelocatable || \ + std::is_copy_constructible_v<TYPE > || \ + std::is_move_constructible_v<TYPE >, \ + #TYPE " is neither copy- nor move-constructible, so cannot be Q_RELOCATABLE_TYPE"); \ } #define Q_DECLARE_TYPEINFO(TYPE, FLAGS) \ @@ -253,74 +186,197 @@ template<typename T> class QFlags; template<typename T> Q_DECLARE_TYPEINFO_BODY(QFlags<T>, Q_PRIMITIVE_TYPE); -/* - Specialize a shared type with: +namespace QTypeTraits +{ - Q_DECLARE_SHARED(type) +/* + The templates below aim to find out whether one can safely instantiate an operator==() or + operator<() for a type. - where 'type' is the name of the type to specialize. NOTE: shared - types must define a member-swap, and be defined in the same - namespace as Qt for this to work. + 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. - If the type was already released without Q_DECLARE_SHARED applied, - _and_ without an explicit Q_DECLARE_TYPEINFO(type, Q_MOVABLE_TYPE), - then use Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(type) to mark the - type shared (incl. swap()), without marking it movable (which - would change the memory layout of QList, a BiC change. + 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 { -#define Q_DECLARE_SHARED_IMPL(TYPE, FLAGS) \ -Q_DECLARE_TYPEINFO(TYPE, FLAGS); \ -inline void swap(TYPE &value1, TYPE &value2) \ - noexcept(noexcept(value1.swap(value2))) \ -{ value1.swap(value2); } -#define Q_DECLARE_SHARED(TYPE) Q_DECLARE_SHARED_IMPL(TYPE, Q_MOVABLE_TYPE) -#define Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(TYPE) \ - Q_DECLARE_SHARED_IMPL(TYPE, QT_VERSION >= QT_VERSION_CHECK(6,0,0) ? Q_MOVABLE_TYPE : Q_RELOCATABLE_TYPE) +// find out whether T is a conteiner +// this is required to check the value type of containers for the existence of the comparison operator +template <typename, typename = void> +struct is_container : std::false_type {}; +template <typename T> +struct is_container<T, std::void_t< + typename T::value_type, + std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool> +>> : std::true_type {}; + + +// Checks the existence of the comparison operator for the class itself +QT_WARNING_PUSH +QT_WARNING_DISABLE_FLOAT_COMPARE +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 {}; +QT_WARNING_POP + +// Two forward declarations +template<typename T, bool = is_container<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 isn't a container 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< + std::disjunction< + std::is_same<T, typename T::value_type>, // avoid endless recursion + 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 T> +struct expand_operator_equal_tuple<std::optional<T>> : expand_operator_equal_recursive<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 = is_container<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< + std::disjunction< + std::is_same<T, typename T::value_type>, + 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 T> +struct expand_operator_less_than_tuple<std::optional<T>> : expand_operator_less_than_recursive<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, typename = void> +struct is_dereferenceable : std::false_type {}; + +template<typename T> +struct is_dereferenceable<T, std::void_t<decltype(std::declval<T>().operator->())> > + : std::true_type {}; + +template <typename T> +inline constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value; + +template<typename T> +struct has_operator_equal : detail::expand_operator_equal<T> {}; +template<typename T> +inline constexpr bool has_operator_equal_v = has_operator_equal<T>::value; + +template <typename Container, typename T> +using has_operator_equal_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_equal<T>>; + +template<typename T> +struct has_operator_less_than : detail::expand_operator_less_than<T> {}; +template<typename T> +inline constexpr bool has_operator_less_than_v = has_operator_less_than<T>::value; + +template <typename Container, typename T> +using has_operator_less_than_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_operator_less_than<T>>; + +template <typename ...T> +using compare_eq_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal<T>...>, bool>; + +template <typename Container, typename ...T> +using compare_eq_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_equal_container<Container, T>...>, bool>; + +template <typename ...T> +using compare_lt_result = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than<T>...>, bool>; + +template <typename Container, typename ...T> +using compare_lt_result_container = std::enable_if_t<std::conjunction_v<QTypeTraits::has_operator_less_than_container<Container, T>...>, bool>; + +namespace detail { + +template<typename T> +const T &const_reference(); +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_reference<T>())>> + : std::true_type {}; +template <typename Stream, typename T> +inline constexpr bool has_ostream_operator_v = has_ostream_operator<Stream, T>::value; + +template <typename Stream, typename Container, typename T> +using has_ostream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_ostream_operator<Stream, T>>; + +template <typename Stream, typename, typename = void> +struct has_istream_operator : std::false_type {}; +template <typename Stream, typename T> +struct has_istream_operator<Stream, T, std::void_t<decltype(detail::reference<Stream>() >> detail::reference<T>())>> + : std::true_type {}; +template <typename Stream, typename T> +inline constexpr bool has_istream_operator_v = has_istream_operator<Stream, T>::value; +template <typename Stream, typename Container, typename T> +using has_istream_operator_container = std::disjunction<std::is_base_of<Container, T>, QTypeTraits::has_istream_operator<Stream, T>>; + +template <typename Stream, typename T> +inline constexpr bool has_stream_operator_v = has_ostream_operator_v<Stream, T> && has_istream_operator_v<Stream, T>; + +} -/* - QTypeInfo primitive specializations -*/ -Q_DECLARE_TYPEINFO(bool, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(char, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(signed char, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(uchar, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(short, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(ushort, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(int, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(uint, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(long, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(ulong, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(qint64, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(quint64, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(float, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(double, Q_PRIMITIVE_TYPE); - -#if QT_VERSION >= QT_VERSION_CHECK(6,0,0) -// ### Qt 6: remove the other branch -// This was required so that QList<T> for these types allocates out of the array storage -Q_DECLARE_TYPEINFO(long double, Q_PRIMITIVE_TYPE); -# ifdef Q_COMPILER_UNICODE_STRINGS -Q_DECLARE_TYPEINFO(char16_t, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(char32_t, Q_PRIMITIVE_TYPE); -# endif -# if !defined(Q_CC_MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) -Q_DECLARE_TYPEINFO(wchar_t, Q_PRIMITIVE_TYPE); -# endif -#else -# ifndef Q_OS_DARWIN -Q_DECLARE_TYPEINFO(long double, Q_PRIMITIVE_TYPE); -# else -Q_DECLARE_TYPEINFO(long double, Q_RELOCATABLE_TYPE); -# endif -# ifdef Q_COMPILER_UNICODE_STRINGS -Q_DECLARE_TYPEINFO(char16_t, Q_RELOCATABLE_TYPE); -Q_DECLARE_TYPEINFO(char32_t, Q_RELOCATABLE_TYPE); -# endif -# if !defined(Q_CC_MSVC) || defined(_NATIVE_WCHAR_T_DEFINED) -Q_DECLARE_TYPEINFO(wchar_t, Q_RELOCATABLE_TYPE); -# endif -#endif // Qt 6 QT_END_NAMESPACE #endif // QTYPEINFO_H |