diff options
Diffstat (limited to 'src/corelib/kernel/qvariant.h')
-rw-r--r-- | src/corelib/kernel/qvariant.h | 211 |
1 files changed, 171 insertions, 40 deletions
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 9ed6fedd3c..306e5b3a38 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -5,6 +5,7 @@ #define QVARIANT_H #include <QtCore/qatomic.h> +#include <QtCore/qcompare.h> #include <QtCore/qcontainerfwd.h> #include <QtCore/qmetatype.h> #ifndef QT_NO_DEBUG_STREAM @@ -62,15 +63,18 @@ template<> constexpr inline bool qIsRelocatable<QVariant> = true; } class Q_CORE_EXPORT QVariant { - template <typename Type, typename... Args> + template <typename T, typename... Args> using if_constructible = std::enable_if_t< std::conjunction_v< - std::is_copy_constructible<q20::remove_cvref_t<Type>>, - std::is_destructible<q20::remove_cvref_t<Type>>, - std::is_constructible<q20::remove_cvref_t<Type>, Args...> + std::is_copy_constructible<q20::remove_cvref_t<T>>, + std::is_destructible<q20::remove_cvref_t<T>>, + std::is_constructible<q20::remove_cvref_t<T>, Args...> >, bool>; + template <typename T> + using if_rvalue = std::enable_if_t<!std::is_reference_v<T>, bool>; + struct CborValueStandIn { qint64 n; void *c; int t; }; public: struct PrivateShared @@ -217,34 +221,34 @@ public: QVariant(const QVariant &other); private: - template<typename Type, typename ...Args> + template <typename T, typename ...Args> using is_noexcept_constructible = std::conjunction< - std::bool_constant<Private::CanUseInternalSpace<Type>>, - std::is_nothrow_constructible<Type, Args...> + std::bool_constant<Private::CanUseInternalSpace<T>>, + std::is_nothrow_constructible<T, Args...> >; public: - template <typename Type, typename... Args, - if_constructible<Type, Args...> = true> - explicit QVariant(std::in_place_type_t<Type>, Args&&... args) - noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, Args...>::value) - : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<Type>>() ) + template <typename T, typename... Args, + if_constructible<T, Args...> = true> + explicit QVariant(std::in_place_type_t<T>, Args&&... args) + noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, Args...>::value) + : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>() ) { void *data = const_cast<void *>(constData()); - new (data) Type(std::forward<Args>(args)...); + new (data) T(std::forward<Args>(args)...); } - template <typename Type, typename List, typename... Args, - if_constructible<Type, std::initializer_list<List> &, Args...> = true> - explicit QVariant(std::in_place_type_t<Type>, std::initializer_list<List> il, Args&&... args) - noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, - std::initializer_list<List> &, + template <typename T, typename U, typename... Args, + if_constructible<T, std::initializer_list<U> &, Args...> = true> + explicit QVariant(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args) + noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, + std::initializer_list<U> &, Args... >::value) - : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<Type>>()) + : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>()) { char *data = static_cast<char *>(const_cast<void *>(constData())); - new (data) Type(il, std::forward<Args>(args)...); + new (data) T(il, std::forward<Args>(args)...); } // primitives @@ -260,7 +264,9 @@ public: QVariant(QChar qchar) noexcept; QVariant(QDate date) noexcept; QVariant(QTime time) noexcept; +#ifndef QT_BOOTSTRAPPED QVariant(const QBitArray &bitarray) noexcept; +#endif QVariant(const QByteArray &bytearray) noexcept; QVariant(const QDateTime &datetime) noexcept; QVariant(const QHash<QString, QVariant> &hash) noexcept; @@ -369,7 +375,9 @@ public: float toFloat(bool *ok = nullptr) const; qreal toReal(bool *ok = nullptr) const; QByteArray toByteArray() const; +#ifndef QT_BOOTSTRAPPED QBitArray toBitArray() const; +#endif QString toString() const; QStringList toStringList() const; QChar toChar() const; @@ -444,6 +452,43 @@ public: { return d.storage(); } inline const void *data() const { return constData(); } +private: + template <typename T> + void verifySuitableForEmplace() + { + static_assert(!std::is_reference_v<T>, + "QVariant does not support reference types"); + static_assert(!std::is_const_v<T>, + "QVariant does not support const types"); + static_assert(std::is_copy_constructible_v<T>, + "QVariant requires that the type is copyable"); + static_assert(std::is_destructible_v<T>, + "QVariant requires that the type is destructible"); + } + + template <typename T, typename... Args> + T &emplaceImpl(Args&&... args) + { + verifySuitableForEmplace<T>(); + auto data = static_cast<T *>(prepareForEmplace(QMetaType::fromType<T>())); + return *q20::construct_at(data, std::forward<Args>(args)...); + } + +public: + template <typename T, typename... Args, + if_constructible<T, Args...> = true> + T &emplace(Args&&... args) + { + return emplaceImpl<T>(std::forward<Args>(args)...); + } + + template <typename T, typename U, typename... Args, + if_constructible<T, std::initializer_list<U> &, Args...> = true> + T &emplace(std::initializer_list<U> list, Args&&... args) + { + return emplaceImpl<T>(list, std::forward<Args>(args)...); + } + template<typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, QVariant>>> void setValue(T &&avalue) { @@ -452,6 +497,7 @@ public: // If possible we reuse the current QVariant private. if (isDetached() && d.type() == metaType) { *reinterpret_cast<VT *>(const_cast<void *>(constData())) = std::forward<T>(avalue); + d.is_null = false; } else { *this = QVariant::fromValue<VT>(std::forward<T>(avalue)); } @@ -468,7 +514,7 @@ public: } template<typename T> - inline T value() const + inline T value() const & { return qvariant_cast<T>(*this); } template<typename T> @@ -480,6 +526,43 @@ public: } template<typename T> + inline T value() && + { return qvariant_cast<T>(std::move(*this)); } + + template<typename T, if_rvalue<T> = true> +#ifndef Q_QDOC + /* needs is_copy_constructible for variants semantics, is_move_constructible so that moveConstruct works + (but copy_constructible implies move_constructble, so don't bother checking) + */ + static inline auto fromValue(T &&value) + noexcept(std::is_nothrow_copy_constructible_v<T> && Private::CanUseInternalSpace<T>) + -> std::enable_if_t<std::conjunction_v<std::is_copy_constructible<T>, + std::is_destructible<T>>, QVariant> +#else + static inline QVariant fromValue(T &&value) +#endif + { + // handle special cases + using Type = std::remove_cv_t<T>; + if constexpr (std::is_null_pointer_v<Type>) + return QVariant::fromMetaType(QMetaType::fromType<std::nullptr_t>()); + else if constexpr (std::is_same_v<Type, QVariant>) + return std::forward<T>(value); + else if constexpr (std::is_same_v<Type, std::monostate>) + return QVariant(); + QMetaType mt = QMetaType::fromType<Type>(); + mt.registerType(); // we want the type stored in QVariant to always be registered + // T is a forwarding reference, so if T satifies the enable-ifery, + // we get this overload even if T is an lvalue reference and thus must check here + // Moreover, we only try to move if the type is actually moveable and not if T is const + // as in const int i; QVariant::fromValue(std::move(i)); + if constexpr (std::conjunction_v<std::is_move_constructible<Type>, std::negation<std::is_const<T>>>) + return moveConstruct(QMetaType::fromType<Type>(), std::addressof(value)); + else + return copyConstruct(mt, std::addressof(value)); + } + + template<typename T> #ifndef Q_QDOC static inline auto fromValue(const T &value) noexcept(std::is_nothrow_copy_constructible_v<T> && Private::CanUseInternalSpace<T>) @@ -490,17 +573,27 @@ public: { if constexpr (std::is_null_pointer_v<T>) return QVariant(QMetaType::fromType<std::nullptr_t>()); + else if constexpr (std::is_same_v<T, QVariant>) + return value; + else if constexpr (std::is_same_v<T, std::monostate>) + return QVariant(); return QVariant(QMetaType::fromType<T>(), std::addressof(value)); } template<typename... Types> static inline QVariant fromStdVariant(const std::variant<Types...> &value) { - if (value.valueless_by_exception()) - return QVariant(); - return std::visit([](const auto &arg) { return QVariant::fromValue(arg); }, value); + return fromStdVariantImpl(value); } + template<typename... Types> + static QVariant fromStdVariant(std::variant<Types...> &&value) + { + return fromStdVariantImpl(std::move(value)); + } + + static QVariant fromMetaType(QMetaType type, const void *copy = nullptr); + template<typename T> bool canConvert() const { return canConvert(QMetaType::fromType<T>()); } @@ -512,10 +605,21 @@ public: static QPartialOrdering compare(const QVariant &lhs, const QVariant &rhs); private: - friend inline bool operator==(const QVariant &a, const QVariant &b) + template <typename StdVariant> + static QVariant fromStdVariantImpl(StdVariant &&v) + { + if (Q_UNLIKELY(v.valueless_by_exception())) + return QVariant(); + auto visitor = [](auto &&arg) { + return QVariant::fromValue(q23::forward_like<StdVariant>(arg)); + }; + return std::visit(visitor, std::forward<StdVariant>(v)); + } + + friend bool comparesEqual(const QVariant &a, const QVariant &b) { return a.equals(b); } - friend inline bool operator!=(const QVariant &a, const QVariant &b) - { return !a.equals(b); } + Q_DECLARE_EQUALITY_COMPARABLE(QVariant) + #ifndef QT_NO_DEBUG_STREAM template <typename T> friend auto operator<<(const QDebug &debug, const T &variant) -> std::enable_if_t<std::is_same_v<T, QVariant>, QDebug> { @@ -557,8 +661,14 @@ private: Q_MK_GET(const &&) #undef Q_MK_GET + static QVariant moveConstruct(QMetaType type, void *data); + static QVariant copyConstruct(QMetaType type, const void *data); + template<typename T> friend inline T qvariant_cast(const QVariant &); + template<typename T> + friend inline T qvariant_cast(QVariant &&); + protected: Private d; void create(int type, const void *copy); @@ -580,6 +690,8 @@ private: // used to setup the QVariant internals for the "real" inplace ctor QVariant(std::in_place_t, QMetaType type); + // helper for emplace + void *prepareForEmplace(QMetaType type); // These constructors don't create QVariants of the type associated // with the enum, as expected, but they would create a QVariant of @@ -600,18 +712,6 @@ public: inline const DataPtr &data_ptr() const { return d; } }; -template<> -inline QVariant QVariant::fromValue(const QVariant &value) -{ - return value; -} - -template<> -inline QVariant QVariant::fromValue(const std::monostate &) noexcept -{ - return QVariant(); -} - inline bool QVariant::isValid() const { return d.type().isValid(); @@ -668,14 +768,45 @@ template<typename T> inline T qvariant_cast(const QVariant &v) return t; } +template<typename T> inline T qvariant_cast(QVariant &&v) +{ + QMetaType targetType = QMetaType::fromType<T>(); + if (v.d.type() == targetType) { + if constexpr (QVariant::Private::CanUseInternalSpace<T>) { + return std::move(*reinterpret_cast<T *>(v.d.data.data)); + } else { + if (v.d.data.shared->ref.loadRelaxed() == 1) + return std::move(*reinterpret_cast<T *>(v.d.data.shared->data())); + else + return v.d.get<T>(); + } + } + if constexpr (std::is_same_v<T, QVariant>) { + // if the metatype doesn't match, but we want a QVariant, just return the current variant + return v; + } if constexpr (std::is_same_v<T,std::remove_const_t<std::remove_pointer_t<T>> const *>) { + // moving a pointer is pointless, just do the same as the const & overload + using nonConstT = std::remove_const_t<std::remove_pointer_t<T>> *; + QMetaType nonConstTargetType = QMetaType::fromType<nonConstT>(); + if (v.d.type() == nonConstTargetType) + return v.d.get<nonConstT>(); + } + + T t{}; + QMetaType::convert(v.metaType(), v.constData(), targetType, &t); + return t; +} + +# ifndef QT_NO_VARIANT template<> inline QVariant qvariant_cast<QVariant>(const QVariant &v) { if (v.metaType().id() == QMetaType::QVariant) return *reinterpret_cast<const QVariant *>(v.constData()); return v; } +# endif -#endif +#endif // QT_MOC #ifndef QT_NO_DEBUG_STREAM #if QT_DEPRECATED_SINCE(6, 0) |