diff --git a/docs/using-sdbus-c++.md b/docs/using-sdbus-c++.md index feb7f08..6fb510d 100644 --- a/docs/using-sdbus-c++.md +++ b/docs/using-sdbus-c++.md @@ -1516,7 +1516,7 @@ sdbus-c++ provides many default, pre-defined C++ type representations for D-Bus | string-like, basic | 103 | g | SIGNATURE | `sdbus::Signature` | | container | 97 | a | ARRAY | `std::vector`, `std::array`, `std::span` - if used as an array followed by a single complete type `T`
`std::map`, `std::unordered_map` - if used as an array of dict entries | | container | 114,40,41 | r() | STRUCT | `sdbus::Struct` variadic class template | -| container | 118 | v | VARIANT | `sdbus::Variant` | +| container | 118 | v | VARIANT | `sdbus::Variant`, `std::variant` | | container | 101,123,125 | e{} | DICT_ENTRY | - | | fixed, basic | 104 | h | UNIX_FD | `sdbus::UnixFd` | | reserved | 109 | m | (reserved) | - | diff --git a/include/sdbus-c++/Message.h b/include/sdbus-c++/Message.h index e17892a..2ee94d5 100644 --- a/include/sdbus-c++/Message.h +++ b/include/sdbus-c++/Message.h @@ -44,6 +44,7 @@ #include #include #include +#include // Forward declarations namespace sdbus { @@ -89,10 +90,11 @@ namespace sdbus { Message& operator<<(const char *item); Message& operator<<(const std::string &item); Message& operator<<(const Variant &item); + template + Message& operator<<(const std::variant& value); Message& operator<<(const ObjectPath &item); Message& operator<<(const Signature &item); Message& operator<<(const UnixFd &item); - template Message& operator<<(const std::vector<_Element, _Allocator>& items); template @@ -124,6 +126,8 @@ namespace sdbus { Message& operator>>(char*& item); Message& operator>>(std::string &item); Message& operator>>(Variant &item); + template + Message& operator>>(std::variant& value); Message& operator>>(ObjectPath &item); Message& operator>>(Signature &item); Message& operator>>(UnixFd &item); @@ -311,6 +315,18 @@ namespace sdbus { PlainMessage() = default; }; + template + inline Message& Message::operator<<(const std::variant& value) + { + std::visit([this](const auto& inner){ + openVariant(signature_of::str()); + *this << inner; + closeVariant(); + }, value); + + return *this; + } + template inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items) { @@ -442,6 +458,34 @@ namespace sdbus { return *this; } + namespace detail + { + template + bool deserialize_variant(Message& msg, std::variant<_Elements...>& value, const std::string& signature) + { + if (signature != signature_of<_Element>::str()) + return false; + + _Element temp; + msg.enterVariant(signature); + msg >> temp; + msg.exitVariant(); + value = std::move(temp); + return true; + } + } + + template + inline Message& Message::operator>>(std::variant& value) + { + std::string type; + std::string contentType; + peekType(type, contentType); + bool result = (detail::deserialize_variant(*this, value, contentType) || ...); + SDBUS_THROW_ERROR_IF(!result, "Failed to deserialize variant: signature did not match any of the variant types", EINVAL); + return *this; + } + template inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items) { diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index 5112d61..1893065 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -31,6 +31,7 @@ #include #include #include +#include #if __cplusplus >= 202002L #include #endif @@ -331,6 +332,10 @@ namespace sdbus { } }; + template + struct signature_of> : signature_of + {}; + template <> struct signature_of { diff --git a/tests/unittests/Message_test.cpp b/tests/unittests/Message_test.cpp index ede790d..23c0a16 100644 --- a/tests/unittests/Message_test.cpp +++ b/tests/unittests/Message_test.cpp @@ -501,3 +501,39 @@ TEST(AMessage, CanCarryDBusStructGivenAsCustomType) ASSERT_THAT(dataRead, Eq(dataWritten)); } + +class AMessage : public ::testing::TestWithParam> +{ +}; + +TEST_P(AMessage, CanCarryDBusVariantGivenAsStdVariant) +{ + auto msg = sdbus::createPlainMessage(); + + const std::variant dataWritten{GetParam()}; + + msg << dataWritten; + msg.seal(); + + std::variant dataRead; + msg >> dataRead; + + ASSERT_THAT(dataRead, Eq(dataWritten)); +} + +TEST_P(AMessage, ThrowsWhenDestinationStdVariantHasWrongTypeDuringDeserialization) +{ + auto msg = sdbus::createPlainMessage(); + + const std::variant dataWritten{GetParam()}; + + msg << dataWritten; + msg.seal(); + + std::variant> dataRead; + ASSERT_THROW(msg >> dataRead, sdbus::Error); +} + +INSTANTIATE_TEST_SUITE_P( StringIntStruct + , AMessage + , ::testing::Values("hello"s, 1, my::Struct{})); diff --git a/tests/unittests/TypeTraits_test.cpp b/tests/unittests/TypeTraits_test.cpp index 662f472..6cdc451 100644 --- a/tests/unittests/TypeTraits_test.cpp +++ b/tests/unittests/TypeTraits_test.cpp @@ -93,6 +93,7 @@ namespace TYPE(sdbus::Struct)HAS_DBUS_TYPE_SIGNATURE("(qdsv)") TYPE(std::vector)HAS_DBUS_TYPE_SIGNATURE("an") TYPE(std::array)HAS_DBUS_TYPE_SIGNATURE("an") + TYPE(std::variant)HAS_DBUS_TYPE_SIGNATURE("v") #if __cplusplus >= 202002L TYPE(std::span)HAS_DBUS_TYPE_SIGNATURE("an") #endif @@ -142,6 +143,7 @@ namespace , sdbus::Struct , std::vector , std::array + , std::variant #if __cplusplus >= 202002L , std::span #endif