feat: introduce support for struct-as-dict serialization (#459)

This extends the functionality of SDBUSCPP_REGISTER_STRUCT macro.

It now provides functionality for serializing a user-defined struct as an a{sv} dictionary, and for deserializing an a{sv} dictionary into a user-defined struct. The former one is achieved by decorating the struct with sdbus::as_dictionary(struct), the latter one is an automatic behavior -- when sdbus-c++ is asked to deserialize into a struct but the data in the message is of type a{sv}, then the dict-to-struct deserialization is performed automatically.

There are some aspects of behavior in the serialization/deserialization functionality that can be customized by the client. Newly introduced SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION and SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION macros serve the purpose.
This commit is contained in:
Stanislav Angelovič
2024-11-20 23:25:13 +01:00
committed by GitHub
parent 8a117f8b42
commit 02ca7212d1
9 changed files with 501 additions and 136 deletions
@@ -48,8 +48,22 @@ using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace std::string_literals;
using namespace sdbus::test;
namespace my {
struct Struct
{
int i;
std::string s;
std::vector<double> l;
friend bool operator==(const Struct &lhs, const Struct &rhs) = default;
};
}
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l);
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
@@ -249,6 +263,21 @@ TYPED_TEST(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
}
TYPED_TEST(SdbusTestObject, CanSendAndReceiveDictionariesAsCustomStructsImplicitly)
{
// This test demonstrates that sdbus-c++ can send a SDBUSCPP_REGISTER_STRUCT-described struct as a dictionary of strings to variants,
// and that sdbus-c++ can automatically deserialize a dictionary of strings to variants into such a struct (instead of a map).
const my::Struct structSent{3545342, "hello"s, {3.14, 2.4568546}};
my::Struct structReceived;
this->m_proxy->getProxy().callMethod("returnDictionary")
.onInterface("org.sdbuscpp.integrationtests")
.withArguments(sdbus::as_dictionary(structSent))
.storeResultsTo(structReceived);
ASSERT_THAT(structReceived, Eq(structSent));
}
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
{
this->m_proxy->doOperation(10); // This will save pointer to method call message on server side
+5
View File
@@ -246,6 +246,11 @@ void TestAdaptor::sendLargeMessage(const std::map<int, std::string>& /*collectio
//printf("Adaptor: got collection with %zu items", collection.size());
}
std::map<std::string, sdbus::Variant> TestAdaptor::returnDictionary(const std::map<std::string, sdbus::Variant>& dict)
{
return dict;
}
std::string TestAdaptor::state()
{
return m_state;
+2
View File
@@ -85,6 +85,7 @@ protected:
void doPrivilegedStuff() override;
void emitTwoSimpleSignals() override;
void sendLargeMessage(const std::map<int, std::string>& collection) override;
std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>& dict) override;
uint32_t action() override;
void action(const uint32_t& value) override;
@@ -147,6 +148,7 @@ protected:
void doPrivilegedStuff() override {}
void emitTwoSimpleSignals() override {}
void sendLargeMessage(const std::map<int, std::string>&) override {}
std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>&) override { return {}; }
uint32_t action() override { return {}; }
void action(const uint32_t&) override {}
@@ -58,6 +58,7 @@ protected:
, sdbus::registerMethod("doPrivilegedStuff").implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged()
, sdbus::registerMethod("emitTwoSimpleSignals").implementedAs([this](){ return this->emitTwoSimpleSignals(); })
, sdbus::registerMethod("sendLargeMessage").implementedAs([this](const std::map<int, std::string>& collection){ this->sendLargeMessage(collection); })
, sdbus::registerMethod("returnDictionary").implementedAs([this](const std::map<std::string, sdbus::Variant>& dictionary){ return this->returnDictionary(dictionary); })
, sdbus::registerSignal("simpleSignal").markAsDeprecated()
, sdbus::registerSignal("signalWithMap").withParameters<std::map<int32_t, std::string>>("aMap")
, sdbus::registerSignal("signalWithVariant").withParameters<sdbus::Variant>("aVariant")
@@ -108,6 +109,7 @@ private:
virtual void doPrivilegedStuff() = 0;
virtual void emitTwoSimpleSignals() = 0;
virtual void sendLargeMessage(const std::map<int, std::string>& collection) = 0;
virtual std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>& dict) = 0;
private:
virtual uint32_t action() = 0;