diff options
Diffstat (limited to 'src/corelib/kernel/qvariant.h')
-rw-r--r-- | src/corelib/kernel/qvariant.h | 261 |
1 files changed, 240 insertions, 21 deletions
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 844ad3cd65..306e5b3a38 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -5,14 +5,18 @@ #define QVARIANT_H #include <QtCore/qatomic.h> +#include <QtCore/qcompare.h> #include <QtCore/qcontainerfwd.h> #include <QtCore/qmetatype.h> #ifndef QT_NO_DEBUG_STREAM #include <QtCore/qdebug.h> #endif + #include <memory> -#include <type_traits> +#include <QtCore/q20type_traits.h> +#include <QtCore/q23utility.h> #include <variant> + #if !defined(QT_LEAN_HEADERS) || QT_LEAN_HEADERS < 1 # include <QtCore/qlist.h> # include <QtCore/qstringlist.h> @@ -24,6 +28,9 @@ QT_BEGIN_NAMESPACE +QT_ENABLE_P0846_SEMANTICS_FOR(get_if) +QT_ENABLE_P0846_SEMANTICS_FOR(get) + class QBitArray; class QDataStream; class QDate; @@ -51,9 +58,23 @@ class QVariant; template<typename T> inline T qvariant_cast(const QVariant &); +namespace QtPrivate { template<> constexpr inline bool qIsRelocatable<QVariant> = true; +} class Q_CORE_EXPORT QVariant { + template <typename T, typename... Args> + using if_constructible = std::enable_if_t< + std::conjunction_v< + 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 @@ -61,6 +82,8 @@ public: private: inline PrivateShared() : ref(1) { } public: + static int computeOffset(PrivateShared *ps, size_t align); + static size_t computeAllocationSize(size_t size, size_t align); static PrivateShared *create(size_t size, size_t align); static void free(PrivateShared *p); @@ -70,6 +93,7 @@ public: const void *data() const { return reinterpret_cast<const uchar *>(this) + offset; } void *data() { return reinterpret_cast<uchar *>(this) + offset; } }; + struct Private { static constexpr size_t MaxInternalSize = 3 * sizeof(void *); @@ -196,6 +220,37 @@ public: explicit QVariant(QMetaType type, const void *copy = nullptr); QVariant(const QVariant &other); +private: + template <typename T, typename ...Args> + using is_noexcept_constructible = std::conjunction< + std::bool_constant<Private::CanUseInternalSpace<T>>, + std::is_nothrow_constructible<T, Args...> + >; + +public: + 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) T(std::forward<Args>(args)...); + } + + 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<T>>()) + { + char *data = static_cast<char *>(const_cast<void *>(constData())); + new (data) T(il, std::forward<Args>(args)...); + } + // primitives QVariant(int i) noexcept; QVariant(uint ui) noexcept; @@ -209,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; @@ -318,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; @@ -393,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) { @@ -401,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)); } @@ -417,7 +514,7 @@ public: } template<typename T> - inline T value() const + inline T value() const & { return qvariant_cast<T>(*this); } template<typename T> @@ -429,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>) @@ -439,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 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>()); } @@ -461,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> { @@ -472,8 +627,48 @@ private: } QDebug qdebugHelper(QDebug) const; #endif + + template <typename T> + friend T *get_if(QVariant *v) noexcept + { + // data() will detach from is_null, returning non-nullptr + if (!v || v->d.type() != QMetaType::fromType<T>()) + return nullptr; + return static_cast<T*>(v->data()); + } + template <typename T> + friend const T *get_if(const QVariant *v) noexcept + { + // (const) data() will not detach from is_null, return nullptr + if (!v || v->d.is_null || v->d.type() != QMetaType::fromType<T>()) + return nullptr; + return static_cast<const T*>(v->data()); + } + +#define Q_MK_GET(cvref) \ + template <typename T> \ + friend T cvref get(QVariant cvref v) \ + { \ + if constexpr (std::is_const_v<T cvref>) \ + Q_ASSERT(!v.d.is_null); \ + Q_ASSERT(v.d.type() == QMetaType::fromType<q20::remove_cvref_t<T>>()); \ + return static_cast<T cvref>(*get_if<T>(&v)); \ + } \ + /* end */ + Q_MK_GET(&) + Q_MK_GET(const &) + Q_MK_GET(&&) + 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); @@ -493,6 +688,11 @@ private: // int variant, so delete this constructor: QVariant(QMetaType::Type) = delete; + // 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 // type int with the value of the enum value. @@ -512,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(); @@ -580,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) |