Implement [variant.get] for QVariant [2/2]: get()
This second patch of the series implements get(). Unlike other get() implementations in Qt, don't use my trick with the constrained friend free function here. Instead, provide the four overloads manually, like mandated by the standard library for std::variant (and, indeed, tuple), such that these functions can also be used on subclasses of QVariant. [ChangeLog][QtCore][QVariant] Implemented the type-based std::variant access protocol (get<T>()/get_if<T>()) to allow easier access to the contained element than the previous solution of casting data(), as well as to allow generic code to treat QVariant and std::variant the same. The holds_alternative<T>() function is not provided, since it's the same as get_if<T> != nullptr. The index-based variant access functions (get<I>()/get_if<I>()) are also not provided, because, unlike std::variant, QVariant does not have a bounded number of alternative types, and QMetaType IDs are not (all) compile-time constants. Fixes: QTBUG-111598 Change-Id: Id7bc41f7d91761b3483ec5604f1a4685c8079844 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>bb10
parent
de3e970889
commit
a9e8034e8c
|
|
@ -2370,6 +2370,25 @@ void *QVariant::data()
|
|||
\sa data()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\since 6.6
|
||||
\fn template <typename T> T &QVariant::get(QVariant &v)
|
||||
\fn template <typename T> const T &QVariant::get(const QVariant &v)
|
||||
\fn template <typename T> T &&QVariant::get(QVariant &&v)
|
||||
\fn template <typename T> const T &&QVariant::get(const QVariant &&v)
|
||||
|
||||
If \a v contains an object of type \c T, returns a reference to the contained
|
||||
object, otherwise the call has undefined behavior.
|
||||
|
||||
The overloads taking a mutable \a v detach \a v: When called on a
|
||||
\l{isNull()}{null} \a v with matching type \c T, \a v will not be null
|
||||
after the call.
|
||||
|
||||
These functions are provided for compatibility with \c{std::variant}.
|
||||
|
||||
\sa get_if(), data()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns \c true if this is a null variant, false otherwise.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@
|
|||
#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>
|
||||
|
|
@ -25,6 +28,7 @@
|
|||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QT_ENABLE_P0846_SEMANTICS_FOR(get_if)
|
||||
QT_ENABLE_P0846_SEMANTICS_FOR(get)
|
||||
|
||||
class QBitArray;
|
||||
class QDataStream;
|
||||
|
|
@ -494,6 +498,22 @@ private:
|
|||
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
|
||||
|
||||
template<typename T>
|
||||
friend inline T qvariant_cast(const QVariant &);
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -17,9 +17,17 @@ constexpr inline bool my_is_same_v<T, T> = true;
|
|||
#define CHECK_GET_IF(Variant, cvref) \
|
||||
CHECK_IMPL(get_if, int, Variant, cvref *, int)
|
||||
|
||||
#define CHECK_GET(Variant, cvref) \
|
||||
CHECK_IMPL(get, int, Variant, cvref, int)
|
||||
|
||||
CHECK_GET_IF(QVariant, /* unadorned */);
|
||||
CHECK_GET_IF(QVariant, const);
|
||||
|
||||
CHECK_GET(QVariant, &);
|
||||
CHECK_GET(QVariant, const &);
|
||||
CHECK_GET(QVariant, &&);
|
||||
CHECK_GET(QVariant, const &&);
|
||||
|
||||
// check for a type derived from QVariant:
|
||||
|
||||
struct MyVariant : QVariant
|
||||
|
|
@ -30,7 +38,13 @@ struct MyVariant : QVariant
|
|||
CHECK_GET_IF(MyVariant, /* unadorned */);
|
||||
CHECK_GET_IF(MyVariant, const);
|
||||
|
||||
CHECK_GET(MyVariant, &);
|
||||
CHECK_GET(MyVariant, const &);
|
||||
CHECK_GET(MyVariant, &&);
|
||||
CHECK_GET(MyVariant, const &&);
|
||||
|
||||
#undef CHECK_GET_IF
|
||||
#undef CHECK_GET
|
||||
#undef CHECK_IMPL
|
||||
|
||||
#include <QTest>
|
||||
|
|
@ -368,6 +382,10 @@ private slots:
|
|||
void getIf_QString() { getIf_impl(u"string"_s); };
|
||||
void getIf_NonDefaultConstructible();
|
||||
|
||||
void get_int() { get_impl(42); }
|
||||
void get_QString() { get_impl(u"string"_s); }
|
||||
void get_NonDefaultConstructible();
|
||||
|
||||
private:
|
||||
using StdVariant = std::variant<std::monostate,
|
||||
// list here all the types with which we instantiate getIf_impl:
|
||||
|
|
@ -377,6 +395,8 @@ private:
|
|||
>;
|
||||
template <typename T>
|
||||
void getIf_impl(T t) const;
|
||||
template <typename T>
|
||||
void get_impl(T t) const;
|
||||
void dataStream_data(QDataStream::Version version);
|
||||
void loadQVariantFromDataStream(QDataStream::Version version);
|
||||
void saveQVariantFromDataStream(QDataStream::Version version);
|
||||
|
|
@ -5688,12 +5708,14 @@ void tst_QVariant::copyNonDefaultConstructible()
|
|||
QVERIFY(var.constData() != &ndc);
|
||||
|
||||
// qvariant_cast<T> and QVariant::value<T> don't compile
|
||||
QCOMPARE(*static_cast<const NonDefaultConstructible *>(var.constData()), ndc);
|
||||
QCOMPARE(get<NonDefaultConstructible>(std::as_const(var)), ndc);
|
||||
|
||||
QVariant var2 = var;
|
||||
var2.detach(); // force another copy
|
||||
QVERIFY(var2.isDetached());
|
||||
QVERIFY(var2.constData() != var.constData());
|
||||
QCOMPARE(get<NonDefaultConstructible>(std::as_const(var2)),
|
||||
get<NonDefaultConstructible>(std::as_const(var)));
|
||||
QCOMPARE(var2, var);
|
||||
}
|
||||
|
||||
|
|
@ -5702,6 +5724,11 @@ void tst_QVariant::getIf_NonDefaultConstructible()
|
|||
getIf_impl(NonDefaultConstructible{42});
|
||||
}
|
||||
|
||||
void tst_QVariant::get_NonDefaultConstructible()
|
||||
{
|
||||
get_impl(NonDefaultConstructible{42});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T mutate(const T &t) { return t + t; }
|
||||
template <>
|
||||
|
|
@ -5813,5 +5840,65 @@ void tst_QVariant::getIf_impl(T t) const
|
|||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void tst_QVariant::get_impl(T t) const
|
||||
{
|
||||
QVariant v = QVariant::fromValue(t);
|
||||
|
||||
// for behavioral comparison:
|
||||
StdVariant stdv = t;
|
||||
|
||||
#define FOR_EACH_CVREF(op) \
|
||||
op(/*unadorned*/, &&) \
|
||||
op(&, &) \
|
||||
op(&&, &&) \
|
||||
op(const, const &&) \
|
||||
op(const &, const &) \
|
||||
op(const &&, const &&) \
|
||||
/* end */
|
||||
|
||||
|
||||
#define CHECK_RETURN_TYPE_OF(Variant, cvref_in, cvref_out) \
|
||||
static_assert(std::is_same_v< \
|
||||
decltype(get<T>(std::declval<Variant cvref_in >())), \
|
||||
T cvref_out \
|
||||
>); \
|
||||
/* end */
|
||||
#define CHECK_RETURN_TYPE(cvref_in, cvref_out) \
|
||||
CHECK_RETURN_TYPE_OF(StdVariant, cvref_in, cvref_out) \
|
||||
CHECK_RETURN_TYPE_OF(QVariant, cvref_in, cvref_out) \
|
||||
/* end */
|
||||
FOR_EACH_CVREF(CHECK_RETURN_TYPE)
|
||||
#undef CHECK_RETURN_TYPE
|
||||
|
||||
#undef FOR_EACH_CVREF
|
||||
|
||||
// const access:
|
||||
{
|
||||
auto &&rs = get<T>(std::as_const(stdv));
|
||||
QCOMPARE_EQ(rs, t);
|
||||
|
||||
auto &&rv = get<T>(std::as_const(v));
|
||||
QCOMPARE_EQ(rv, t);
|
||||
}
|
||||
|
||||
// mutable access:
|
||||
{
|
||||
T t2 = mutate(t);
|
||||
|
||||
auto &&rs = get<T>(stdv);
|
||||
QCOMPARE_EQ(rs, t);
|
||||
rs = t2;
|
||||
auto &&rs2 = get<T>(stdv);
|
||||
QCOMPARE_EQ(rs2, t2);
|
||||
|
||||
auto &&rv = get<T>(v);
|
||||
QCOMPARE_EQ(rv, t);
|
||||
rv = t2;
|
||||
auto &&rv2 = get<T>(v);
|
||||
QCOMPARE_EQ(rv2, t2);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QVariant)
|
||||
#include "tst_qvariant.moc"
|
||||
|
|
|
|||
Loading…
Reference in New Issue