Provide D-Bus de-/marshalling operators for std::tuple

Currently Qt provides only generic operators for pairs which can be used
generically for structs with two elements. A tuple is the natural fit
for dealing generically with structs that have more elements.
This can be helpful when wanting to make a one-of calls to APIs that
take or return such types or deferring to tuple marshalling without
having to write custom operators.

[ChangeLog][QtDBus][QDBusArgument] Added generic support for
marshalling and demarshalling D-Bus STRUCTs from/to std::tuple.

Change-Id: Id5cf49063c9b43ed68ad7821111376f6fa887a73
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
David Redondo 2024-12-11 12:23:45 +01:00
parent 95f02adf75
commit 11518e92c2
4 changed files with 67 additions and 2 deletions

View File

@ -295,8 +295,9 @@
Qt D-Bus provides template specializations for arrays and maps for
use with Qt's \l{Container classes}{container classes}, such as
QMap and QList, so it is not necessary to write the streaming
operator functions for those. For other types, and specially for
types implementing structures, the operators have to be explicitly
operator functions for those. For structures Qt provides generic
specializations mapping to and from \c std::tuple. In order to use
a custom type instead or for other types the operators have to be explicitly
implemented.
See the documentation for QDBusArgument for examples for

View File

@ -321,6 +321,26 @@ inline const QDBusArgument &operator>>(const QDBusArgument &arg, std::pair<T1, T
return arg;
}
template <typename... T>
QDBusArgument &operator<<(QDBusArgument &argument, const std::tuple<T...> &tuple)
{
static_assert(std::tuple_size_v<std::tuple<T...>> != 0, "D-Bus doesn't allow empty structs");
argument.beginStructure();
std::apply([&argument](const auto &...elements) { (argument << ... << elements); }, tuple);
argument.endStructure();
return argument;
}
template <typename... T>
const QDBusArgument &operator>>(const QDBusArgument &argument, std::tuple<T...> &tuple)
{
static_assert(std::tuple_size_v<std::tuple<T...>> != 0, "D-Bus doesn't allow empty structs");
argument.beginStructure();
std::apply([&argument](auto &...elements) { (argument >> ... >> elements); }, tuple);
argument.endStructure();
return argument;
}
QT_END_NAMESPACE
#endif // QT_NO_DBUS

View File

@ -160,6 +160,14 @@ void commonInit()
qDBusRegisterMetaType<QList<MyVariantMapStruct> >();
qDBusRegisterMetaType<MyFileDescriptorStruct>();
qDBusRegisterMetaType<QList<MyFileDescriptorStruct> >();
qDBusRegisterMetaType<std::tuple<int>>();
qDBusRegisterMetaType<std::tuple<QString>>();
qDBusRegisterMetaType<std::tuple<QVariantMap>>();
qDBusRegisterMetaType<std::tuple<QPoint>>();
qDBusRegisterMetaType<std::tuple<std::tuple<int>>>();
qDBusRegisterMetaType<std::tuple<QList<int>>>();
qDBusRegisterMetaType<std::tuple<int, QString, QVariantMap>>();
}
#ifdef USE_PRIVATE_CODE
#include "private/qdbusintrospection_p.h"
@ -510,6 +518,21 @@ bool compareToArgument(const QDBusArgument &arg, const QVariant &v2)
return compare<MyFileDescriptorStruct>(arg, v2);
else if (id == qMetaTypeId<QList<MyFileDescriptorStruct> >())
return compare<QList<MyFileDescriptorStruct> >(arg, v2);
else if (id == qMetaTypeId<std::tuple<int>>())
return compare<std::tuple<int>>(arg, v2);
else if (id == qMetaTypeId<std::tuple<QString>>())
return compare<std::tuple<QString>>(arg, v2);
else if (id == qMetaTypeId<std::tuple<QVariantMap>>())
return compare<std::tuple<QVariantMap>>(arg, v2);
else if (id == qMetaTypeId<std::tuple<QPoint>>())
return compare<std::tuple<QPoint>>(arg, v2);
else if (id == qMetaTypeId<std::tuple<std::tuple<int>>>())
return compare<std::tuple<std::tuple<int>>>(arg, v2);
else if (id == qMetaTypeId<std::tuple<QList<int>>>())
return compare<std::tuple<QList<int>>>(arg, v2);
else if (id == qMetaTypeId<std::tuple<int, QString, QVariantMap>>())
return compare<std::tuple<int, QString, QVariantMap>>(arg, v2);
}
qWarning() << "Unexpected QVariant type" << v2.userType()

View File

@ -549,6 +549,27 @@ void tst_QDBusMarshall::sendStructs_data()
list << mvms;
QTest::newRow("list-of-string-variantmap") << QVariant::fromValue(list) << "a(sa{sv})" << "[Argument: a(sa{sv}) {[Argument: (sa{sv}) \"Hello, World\", [Argument: a{sv} {\"bytearray\" = [Variant(QByteArray): {72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100}], \"int\" = [Variant(int): 42], \"short\" = [Variant(short): -47], \"uint\" = [Variant(uint): 42]}]]}]";
QTest::newRow("std::tuple<int>")
<< QVariant::fromValue(std::tuple<int>{ 1 }) << "(i)" << "[Argument: (i) 1]";
QTest::newRow("std::tuple<QString>") << QVariant::fromValue(std::tuple<QString>{ "foo" })
<< "(s)" << "[Argument: (s) \"foo\"]";
QTest::newRow("std::tuple<QVariantMap>")
<< QVariant::fromValue(std::tuple<QVariantMap>{ { { "foo", 1 } } }) << "(a{sv})"
<< "[Argument: (a{sv}) [Argument: a{sv} {\"foo\" = [Variant(int): 1]}]]";
QTest::newRow("std::tuple<QPoint>") << QVariant::fromValue(std::tuple<QPoint>{ { 1, 2 } })
<< "((ii))" << "[Argument: ((ii)) [Argument: (ii) 1, 2]]";
QTest::newRow("std::tuple<std::tuple<int>>")
<< QVariant::fromValue(std::tuple<std::tuple<int>>{ 1 }) << "((i))"
<< "[Argument: ((i)) [Argument: (i) 1]]";
QTest::newRow("std::tuple<QList<int>>")
<< QVariant::fromValue(std::tuple<QList<int>>{ { 1, 2, 3 } }) << "(ai)"
<< "[Argument: (ai) [Argument: ai {1, 2, 3}]]";
QTest::newRow("std::tuple<int, QString, QVariantMap>")
<< QVariant::fromValue(
std::tuple<int, QString, QVariantMap>{ 1, "foo", { { "bar", 2 } } })
<< "(isa{sv})"
<< "[Argument: (isa{sv}) 1, \"foo\", [Argument: a{sv} {\"bar\" = [Variant(int): 2]}]]";
if (fileDescriptorPassing) {
MyFileDescriptorStruct fds;
fds.fd = QDBusUnixFileDescriptor(fileDescriptorForTest());