Skip to content

Commit

Permalink
Provide D-Bus de-/marshalling operators for std::tuple
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Sodivad committed Dec 13, 2024
1 parent 95f02ad commit 11518e9
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 2 deletions.
5 changes: 3 additions & 2 deletions src/dbus/doc/src/dbus-adaptors.qdoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 20 additions & 0 deletions src/dbus/qdbusargument.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions tests/auto/dbus/qdbusmarshall/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()
Expand Down
21 changes: 21 additions & 0 deletions tests/auto/dbus/qdbusmarshall/tst_qdbusmarshall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down

0 comments on commit 11518e9

Please sign in to comment.