Skip to content

Commit

Permalink
feat: add support for std::variant as D-Bus variant representation (#415
Browse files Browse the repository at this point in the history
)

Signed-off-by: Anthony Brandon <[email protected]>
Co-authored-by: Stanislav Angelovič <[email protected]>
  • Loading branch information
anthonybrandon and sangelovic authored Apr 1, 2024
1 parent 700a011 commit a73eb9b
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/using-sdbus-c++.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>`, `std::array<T>`, `std::span<T>` - if used as an array followed by a single complete type `T` <br /> `std::map<T1, T2>`, `std::unordered_map<T1, T2>` - if used as an array of dict entries |
| container | 114,40,41 | r() | STRUCT | `sdbus::Struct<T1, T2, ...>` variadic class template |
| container | 118 | v | VARIANT | `sdbus::Variant` |
| container | 118 | v | VARIANT | `sdbus::Variant`, `std::variant<T1, ...>` |
| container | 101,123,125 | e{} | DICT_ENTRY | - |
| fixed, basic | 104 | h | UNIX_FD | `sdbus::UnixFd` |
| reserved | 109 | m | (reserved) | - |
Expand Down
46 changes: 45 additions & 1 deletion include/sdbus-c++/Message.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <functional>
#include <sys/types.h>
#include <algorithm>
#include <variant>

// Forward declarations
namespace sdbus {
Expand Down Expand Up @@ -89,10 +90,11 @@ namespace sdbus {
Message& operator<<(const char *item);
Message& operator<<(const std::string &item);
Message& operator<<(const Variant &item);
template <typename ...Elements>
Message& operator<<(const std::variant<Elements...>& value);
Message& operator<<(const ObjectPath &item);
Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item);

template <typename _Element, typename _Allocator>
Message& operator<<(const std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Expand Down Expand Up @@ -124,6 +126,8 @@ namespace sdbus {
Message& operator>>(char*& item);
Message& operator>>(std::string &item);
Message& operator>>(Variant &item);
template <typename ...Elements>
Message& operator>>(std::variant<Elements...>& value);
Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item);
Expand Down Expand Up @@ -311,6 +315,18 @@ namespace sdbus {
PlainMessage() = default;
};

template <typename ...Elements>
inline Message& Message::operator<<(const std::variant<Elements...>& value)
{
std::visit([this](const auto& inner){
openVariant(signature_of<decltype(inner)>::str());
*this << inner;
closeVariant();
}, value);

return *this;
}

template <typename _Element, typename _Allocator>
inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
{
Expand Down Expand Up @@ -442,6 +458,34 @@ namespace sdbus {
return *this;
}

namespace detail
{
template <typename _Element, typename... _Elements>
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 <typename... Elements>
inline Message& Message::operator>>(std::variant<Elements...>& value)
{
std::string type;
std::string contentType;
peekType(type, contentType);
bool result = (detail::deserialize_variant<Elements>(*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 <typename _Element, typename _Allocator>
inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
{
Expand Down
5 changes: 5 additions & 0 deletions include/sdbus-c++/TypeTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <string>
#include <vector>
#include <array>
#include <variant>
#if __cplusplus >= 202002L
#include <span>
#endif
Expand Down Expand Up @@ -331,6 +332,10 @@ namespace sdbus {
}
};

template <typename... Elements>
struct signature_of<std::variant<Elements...>> : signature_of<Variant>
{};

template <>
struct signature_of<ObjectPath>
{
Expand Down
36 changes: 36 additions & 0 deletions tests/unittests/Message_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,39 @@ TEST(AMessage, CanCarryDBusStructGivenAsCustomType)

ASSERT_THAT(dataRead, Eq(dataWritten));
}

class AMessage : public ::testing::TestWithParam<std::variant<int32_t, std::string, my::Struct>>
{
};

TEST_P(AMessage, CanCarryDBusVariantGivenAsStdVariant)
{
auto msg = sdbus::createPlainMessage();

const std::variant<int32_t, std::string, my::Struct> dataWritten{GetParam()};

msg << dataWritten;
msg.seal();

std::variant<int32_t, std::string, my::Struct> dataRead;
msg >> dataRead;

ASSERT_THAT(dataRead, Eq(dataWritten));
}

TEST_P(AMessage, ThrowsWhenDestinationStdVariantHasWrongTypeDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();

const std::variant<int32_t, std::string, my::Struct> dataWritten{GetParam()};

msg << dataWritten;
msg.seal();

std::variant<std::vector<bool>> dataRead;
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}

INSTANTIATE_TEST_SUITE_P( StringIntStruct
, AMessage
, ::testing::Values("hello"s, 1, my::Struct{}));
2 changes: 2 additions & 0 deletions tests/unittests/TypeTraits_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ namespace
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
TYPE(std::array<int16_t, 3>)HAS_DBUS_TYPE_SIGNATURE("an")
TYPE(std::variant<int16_t, std::string>)HAS_DBUS_TYPE_SIGNATURE("v")
#if __cplusplus >= 202002L
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
#endif
Expand Down Expand Up @@ -142,6 +143,7 @@ namespace
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
, std::vector<int16_t>
, std::array<int16_t, 3>
, std::variant<int16_t, std::string>
#if __cplusplus >= 202002L
, std::span<int16_t>
#endif
Expand Down

0 comments on commit a73eb9b

Please sign in to comment.