summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qvariant.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qvariant.h')
-rw-r--r--src/corelib/kernel/qvariant.h262
1 files changed, 240 insertions, 22 deletions
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index 98193cfc4e..d567bcbb7c 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)
{
@@ -417,7 +513,7 @@ public:
}
template<typename T>
- inline T value() const
+ inline T value() const &
{ return qvariant_cast<T>(*this); }
template<typename T>
@@ -429,7 +525,44 @@ public:
}
template<typename T>
-#ifndef Q_CLANG_QDOC
+ 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>)
-> std::enable_if_t<std::is_copy_constructible_v<T> && std::is_destructible_v<T>, QVariant>
@@ -439,17 +572,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 +604,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 +626,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 +687,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 +711,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 +767,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)