From da21b474b0f1c22b8aa9881d54521f95c2a8a220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Tue, 1 Aug 2023 12:03:07 +0200 Subject: [PATCH] feat: support serialization of array, span and unordered_map --- include/sdbus-c++/Message.h | 280 +++++++++++++++--- include/sdbus-c++/TypeTraits.h | 56 +++- src/Message.cpp | 5 + tests/integrationtests/DBusMethodsTests.cpp | 4 +- tests/integrationtests/TestAdaptor.cpp | 8 +- tests/integrationtests/TestAdaptor.h | 8 +- .../integrationtests-adaptor.h | 6 +- .../integrationtests/integrationtests-proxy.h | 8 +- .../org.sdbuscpp.integrationtests.xml | 2 +- tests/unittests/Message_test.cpp | 122 +++++++- tests/unittests/TypeTraits_test.cpp | 15 +- 11 files changed, 436 insertions(+), 78 deletions(-) diff --git a/include/sdbus-c++/Message.h b/include/sdbus-c++/Message.h index 742d4e33..91f613cf 100755 --- a/include/sdbus-c++/Message.h +++ b/include/sdbus-c++/Message.h @@ -31,7 +31,12 @@ #include #include #include +#include +#if __cplusplus >= 202002L +#include +#endif #include +#include #include #include #include @@ -85,10 +90,23 @@ namespace sdbus { Message& operator<<(const ObjectPath &item); Message& operator<<(const Signature &item); Message& operator<<(const UnixFd &item); - template Message& operator<<(const std::vector<_Element>& items); - template Message& operator<<(const std::map<_Key, _Value>& items); - template Message& operator<<(const Struct<_ValueTypes...>& item); - template Message& operator<<(const std::tuple<_ValueTypes...>& item); + + template + Message& operator<<(const std::vector<_Element, _Allocator>& items); + template + Message& operator<<(const std::array<_Element, _Size>& items); +#if __cplusplus >= 202002L + template + Message& operator<<(const std::span<_Element, _Extent>& items); +#endif + template + Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items); + template + Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items); + template + Message& operator<<(const Struct<_ValueTypes...>& item); + template + Message& operator<<(const std::tuple<_ValueTypes...>& item); Message& operator>>(bool& item); Message& operator>>(int16_t& item); @@ -105,10 +123,22 @@ namespace sdbus { Message& operator>>(ObjectPath &item); Message& operator>>(Signature &item); Message& operator>>(UnixFd &item); - template Message& operator>>(std::vector<_Element>& items); - template Message& operator>>(std::map<_Key, _Value>& items); - template Message& operator>>(Struct<_ValueTypes...>& item); - template Message& operator>>(std::tuple<_ValueTypes...>& item); + template + Message& operator>>(std::vector<_Element, _Allocator>& items); + template + Message& operator>>(std::array<_Element, _Size>& items); +#if __cplusplus >= 202002L + template + Message& operator>>(std::span<_Element, _Extent>& items); +#endif + template + Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items); + template + Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items); + template + Message& operator>>(Struct<_ValueTypes...>& item); + template + Message& operator>>(std::tuple<_ValueTypes...>& item); Message& openContainer(const std::string& signature); Message& closeContainer(); @@ -139,6 +169,7 @@ namespace sdbus { void peekType(std::string& type, std::string& contents) const; bool isValid() const; bool isEmpty() const; + bool isAtEnd(bool complete) const; void copyTo(Message& destination, bool complete) const; void seal(); @@ -155,9 +186,27 @@ namespace sdbus { class Factory; private: + template + void serializeArray(const _Array& items); + template + void deserializeArray(_Array& items); + template + void deserializeArrayFast(_Array& items); + template + void deserializeArrayFast(std::vector<_Element, _Allocator>& items); + template + void deserializeArraySlow(_Array& items); + template + void deserializeArraySlow(std::vector<_Element, _Allocator>& items); + void appendArray(char type, const void *ptr, size_t size); void readArray(char type, const void **ptr, size_t *size); + template + void serializeDictionary(const _Dictionary& items); + template + void deserializeDictionary(_Dictionary& items); + protected: Message() = default; explicit Message(internal::ISdBus* sdbus) noexcept; @@ -256,32 +305,77 @@ namespace sdbus { PlainMessage() = default; }; - template - inline Message& Message::operator<<(const std::vector<_Element>& items) + template + inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items) + { + serializeArray(items); + + return *this; + } + + template + inline Message& Message::operator<<(const std::array<_Element, _Size>& items) { + serializeArray(items); + + return *this; + } + +#if __cplusplus >= 202002L + template + inline Message& Message::operator<<(const std::span<_Element, _Extent>& items) + { + serializeArray(items); + + return *this; + } +#endif + + template + inline void Message::serializeArray(const _Array& items) + { + using ElementType = typename _Array::value_type; + // Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool, // otherwise use step-by-step serialization of individual elements. - if constexpr (signature_of<_Element>::is_trivial_dbus_type && !std::is_same_v<_Element, bool>) + if constexpr (signature_of::is_trivial_dbus_type && !std::is_same_v) { - appendArray(*signature_of<_Element>::str().c_str(), items.data(), items.size() * sizeof(_Element)); + appendArray(*signature_of::str().c_str(), items.data(), items.size() * sizeof(ElementType)); } else { - openContainer(signature_of<_Element>::str()); + openContainer(signature_of::str()); for (const auto& item : items) *this << item; closeContainer(); } + } + + template + inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items) + { + serializeDictionary(items); + + return *this; + } + + template + inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items) + { + serializeDictionary(items); return *this; } - template - inline Message& Message::operator<<(const std::map<_Key, _Value>& items) + template + inline void Message::serializeDictionary(const _Dictionary& items) { - const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str(); + using KeyType = typename _Dictionary::key_type; + using ValueType = typename _Dictionary::mapped_type; + + const std::string dictEntrySignature = signature_of::str() + signature_of::str(); const std::string arraySignature = "{" + dictEntrySignature + "}"; openContainer(arraySignature); @@ -295,8 +389,6 @@ namespace sdbus { } closeContainer(); - - return *this; } namespace detail @@ -322,7 +414,7 @@ namespace sdbus { auto structSignature = signature_of>::str(); assert(structSignature.size() > 2); // Remove opening and closing parenthesis from the struct signature to get contents signature - auto structContentSignature = structSignature.substr(1, structSignature.size()-2); + auto structContentSignature = structSignature.substr(1, structSignature.size() - 2); openStruct(structContentSignature); detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{}); @@ -338,58 +430,152 @@ namespace sdbus { return *this; } - template - inline Message& Message::operator>>(std::vector<_Element>& items) + template + inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items) + { + deserializeArray(items); + + return *this; + } + + template + inline Message& Message::operator>>(std::array<_Element, _Size>& items) + { + deserializeArray(items); + + return *this; + } + +#if __cplusplus >= 202002L + template + inline Message& Message::operator>>(std::span<_Element, _Extent>& items) + { + deserializeArray(items); + + return *this; + } +#endif + + template + inline void Message::deserializeArray(_Array& items) { + using ElementType = typename _Array::value_type; + // Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool, // otherwise use step-by-step deserialization of individual elements. - if constexpr (signature_of<_Element>::is_trivial_dbus_type && !std::is_same_v<_Element, bool>) + if constexpr (signature_of::is_trivial_dbus_type && !std::is_same_v) { - size_t arraySize{}; - const _Element* arrayPtr{}; - - readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize); - - items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element))); + deserializeArrayFast(items); } else { - if(!enterContainer(signature_of<_Element>::str())) - return *this; + deserializeArraySlow(items); + } + } + + template + inline void Message::deserializeArrayFast(_Array& items) + { + using ElementType = typename _Array::value_type; + + size_t arraySize{}; + const ElementType* arrayPtr{}; + + readArray(*signature_of::str().c_str(), (const void**)&arrayPtr, &arraySize); + + size_t elementsInMsg = arraySize / sizeof(ElementType); + bool notEnoughSpace = items.size() < elementsInMsg; + SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL); + + std::copy_n(arrayPtr, elementsInMsg, items.begin()); + } + + template + void Message::deserializeArrayFast(std::vector<_Element, _Allocator>& items) + { + size_t arraySize{}; + const _Element* arrayPtr{}; + + readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize); + + items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element))); + } + + template + inline void Message::deserializeArraySlow(_Array& items) + { + using ElementType = typename _Array::value_type; + + if(!enterContainer(signature_of::str())) + return; + + for (auto& elem : items) + if (!(*this >> elem)) + break; // Keep the rest in the destination sequence untouched + + SDBUS_THROW_ERROR_IF(!isAtEnd(false), "Failed to deserialize array: not enough space in destination sequence", EINVAL); + + clearFlags(); - while (true) - { - _Element elem; - if (*this >> elem) - items.emplace_back(std::move(elem)); - else - break; - } + exitContainer(); + } - clearFlags(); + template + void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items) + { + if(!enterContainer(signature_of<_Element>::str())) + return; - exitContainer(); + while (true) + { + _Element elem; + // TODO: Is there a way to find D-Bus message container size upfront? We could reserve space in vector upfront. + if (*this >> elem) + items.emplace_back(std::move(elem)); + else + break; } + clearFlags(); + + exitContainer(); + } + + template + inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items) + { + deserializeDictionary(items); + return *this; } - template - inline Message& Message::operator>>(std::map<_Key, _Value>& items) + template + inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items) { - const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str(); + deserializeDictionary(items); + + return *this; + } + + template + inline void Message::deserializeDictionary(_Dictionary& items) + { + using KeyType = typename _Dictionary::key_type; + using ValueType = typename _Dictionary::mapped_type; + + const std::string dictEntrySignature = signature_of::str() + signature_of::str(); const std::string arraySignature = "{" + dictEntrySignature + "}"; if (!enterContainer(arraySignature)) - return *this; + return; while (true) { if (!enterDictEntry(dictEntrySignature)) break; - _Key key; - _Value value; + KeyType key; + ValueType value; *this >> key >> value; items.emplace(std::move(key), std::move(value)); @@ -400,8 +586,6 @@ namespace sdbus { clearFlags(); exitContainer(); - - return *this; } namespace detail diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index f3225692..e5016811 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -30,7 +30,12 @@ #include #include #include +#include +#if __cplusplus >= 202002L +#include +#endif #include +#include #include #include #include @@ -105,6 +110,11 @@ namespace sdbus { } }; + template + struct signature_of + : public signature_of<_T> + {}; + template <> struct signature_of { @@ -349,8 +359,8 @@ namespace sdbus { } }; - template - struct signature_of> + template + struct signature_of> { static constexpr bool is_valid = true; static constexpr bool is_trivial_dbus_type = false; @@ -361,8 +371,46 @@ namespace sdbus { } }; - template - struct signature_of> + template + struct signature_of> + { + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + + static const std::string str() + { + return "a" + signature_of<_Element>::str(); + } + }; + +#if __cplusplus >= 202002L + template + struct signature_of> + { + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + + static const std::string str() + { + return "a" + signature_of<_Element>::str(); + } + }; +#endif + + template + struct signature_of> + { + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; + + static const std::string str() + { + return "a{" + signature_of<_Key>::str() + signature_of<_Value>::str() + "}"; + } + }; + + template + struct signature_of> { static constexpr bool is_valid = true; static constexpr bool is_trivial_dbus_type = false; diff --git a/src/Message.cpp b/src/Message.cpp index 2b2f7351..3373fe03 100755 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -644,6 +644,11 @@ bool Message::isEmpty() const return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0; } +bool Message::isAtEnd(bool complete) const +{ + return sd_bus_message_at_end((sd_bus_message*)msg_, complete) > 0; +} + pid_t Message::getCredsPid() const { uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT; diff --git a/tests/integrationtests/DBusMethodsTests.cpp b/tests/integrationtests/DBusMethodsTests.cpp index 77bb317a..93ca28e6 100644 --- a/tests/integrationtests/DBusMethodsTests.cpp +++ b/tests/integrationtests/DBusMethodsTests.cpp @@ -125,8 +125,8 @@ TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully) TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully) { - auto val = m_proxy->sumVectorItems({1, 7}, {2, 3}); - ASSERT_THAT(val, Eq(1 + 7 + 2 + 3)); + auto val = m_proxy->sumArrayItems({1, 7}, {2, 3, 4}); + ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4)); } TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully) diff --git a/tests/integrationtests/TestAdaptor.cpp b/tests/integrationtests/TestAdaptor.cpp index bc966dae..f7991fb7 100644 --- a/tests/integrationtests/TestAdaptor.cpp +++ b/tests/integrationtests/TestAdaptor.cpp @@ -104,7 +104,7 @@ int32_t TestAdaptor::sumStructItems(const sdbus::Struct& a, c return res; } -uint32_t TestAdaptor::sumVectorItems(const std::vector& a, const std::vector& b) +uint32_t TestAdaptor::sumArrayItems(const std::vector& a, const std::array& b) { uint32_t res{0}; for (auto x : a) @@ -162,9 +162,9 @@ sdbus::UnixFd TestAdaptor::getUnixFd() return sdbus::UnixFd{UNIX_FD_VALUE}; } -std::map>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex() +std::unordered_map>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex() { - return { // map + return { // unordered_map { 0, // uint_64_t { // struct @@ -390,7 +390,7 @@ R"delimiter( - + diff --git a/tests/integrationtests/TestAdaptor.h b/tests/integrationtests/TestAdaptor.h index 3c26f261..550adf8f 100644 --- a/tests/integrationtests/TestAdaptor.h +++ b/tests/integrationtests/TestAdaptor.h @@ -70,13 +70,13 @@ class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integr std::map getMapOfVariants(const std::vector& x, const sdbus::Struct& y) override; sdbus::Struct>> getStructInStruct() override; int32_t sumStructItems(const sdbus::Struct& arg0, const sdbus::Struct& arg1) override; - uint32_t sumVectorItems(const std::vector& arg0, const std::vector& arg1) override; + uint32_t sumArrayItems(const std::vector& arg0, const std::array& arg1) override; uint32_t doOperation(const uint32_t& arg0) override; void doOperationAsync(sdbus::Result&& result, uint32_t arg0) override; sdbus::Signature getSignature() override; sdbus::ObjectPath getObjPath() override; sdbus::UnixFd getUnixFd() override; - std::map>>>, sdbus::Signature, std::string>> getComplex() override; + std::unordered_map>>>, sdbus::Signature, std::string>> getComplex() override; void throwError() override; void throwErrorWithNoReply() override; void doPrivilegedStuff() override; @@ -127,13 +127,13 @@ class DummyTestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::i std::map getMapOfVariants(const std::vector&, const sdbus::Struct&) override { return {}; } sdbus::Struct>> getStructInStruct() override { return {}; } int32_t sumStructItems(const sdbus::Struct&, const sdbus::Struct&) override { return {}; } - uint32_t sumVectorItems(const std::vector&, const std::vector&) override { return {}; } + uint32_t sumArrayItems(const std::vector&, const std::array&) override { return {}; } uint32_t doOperation(const uint32_t&) override { return {}; } void doOperationAsync(sdbus::Result&&, uint32_t) override {} sdbus::Signature getSignature() override { return {}; } sdbus::ObjectPath getObjPath() override { return {}; } sdbus::UnixFd getUnixFd() override { return {}; } - std::map>>>, sdbus::Signature, std::string>> getComplex() override { return {}; } + std::unordered_map>>>, sdbus::Signature, std::string>> getComplex() override { return {}; } void throwError() override {} void throwErrorWithNoReply() override {} void doPrivilegedStuff() override {} diff --git a/tests/integrationtests/integrationtests-adaptor.h b/tests/integrationtests/integrationtests-adaptor.h index cbf74643..5a7ef769 100644 --- a/tests/integrationtests/integrationtests-adaptor.h +++ b/tests/integrationtests/integrationtests-adaptor.h @@ -33,7 +33,7 @@ class integrationtests_adaptor object_->registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector& x, const sdbus::Struct& y){ return this->getMapOfVariants(x, y); }); object_->registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); }); object_->registerMethod("sumStructItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct& arg0, const sdbus::Struct& arg1){ return this->sumStructItems(arg0, arg1); }); - object_->registerMethod("sumVectorItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector& arg0, const std::vector& arg1){ return this->sumVectorItems(arg0, arg1); }); + object_->registerMethod("sumArrayItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector& arg0, const std::array& arg1){ return this->sumArrayItems(arg0, arg1); }); object_->registerMethod("doOperation").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); }); object_->registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); }); object_->registerMethod("getSignature").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); }); @@ -86,13 +86,13 @@ class integrationtests_adaptor virtual std::map getMapOfVariants(const std::vector& x, const sdbus::Struct& y) = 0; virtual sdbus::Struct>> getStructInStruct() = 0; virtual int32_t sumStructItems(const sdbus::Struct& arg0, const sdbus::Struct& arg1) = 0; - virtual uint32_t sumVectorItems(const std::vector& arg0, const std::vector& arg1) = 0; + virtual uint32_t sumArrayItems(const std::vector& arg0, const std::array& arg1) = 0; virtual uint32_t doOperation(const uint32_t& arg0) = 0; virtual void doOperationAsync(sdbus::Result&& result, uint32_t arg0) = 0; virtual sdbus::Signature getSignature() = 0; virtual sdbus::ObjectPath getObjPath() = 0; virtual sdbus::UnixFd getUnixFd() = 0; - virtual std::map>>>, sdbus::Signature, std::string>> getComplex() = 0; + virtual std::unordered_map>>>, sdbus::Signature, std::string>> getComplex() = 0; virtual void throwError() = 0; virtual void throwErrorWithNoReply() = 0; virtual void doPrivilegedStuff() = 0; diff --git a/tests/integrationtests/integrationtests-proxy.h b/tests/integrationtests/integrationtests-proxy.h index 7fc9078f..6704686f 100644 --- a/tests/integrationtests/integrationtests-proxy.h +++ b/tests/integrationtests/integrationtests-proxy.h @@ -105,10 +105,10 @@ class integrationtests_proxy return result; } - uint32_t sumVectorItems(const std::vector& arg0, const std::vector& arg1) + uint32_t sumArrayItems(const std::vector& arg0, const std::array& arg1) { uint32_t result; - proxy_->callMethod("sumVectorItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result); + proxy_->callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result); return result; } @@ -147,9 +147,9 @@ class integrationtests_proxy return result; } - std::map>>>, sdbus::Signature, std::string>> getComplex() + std::map>>>, sdbus::Signature, std::string>> getComplex() { - std::map>>>, sdbus::Signature, std::string>> result; + std::map>>>, sdbus::Signature, std::string>> result; proxy_->callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result); return result; } diff --git a/tests/integrationtests/org.sdbuscpp.integrationtests.xml b/tests/integrationtests/org.sdbuscpp.integrationtests.xml index e6583829..bec5aebc 100644 --- a/tests/integrationtests/org.sdbuscpp.integrationtests.xml +++ b/tests/integrationtests/org.sdbuscpp.integrationtests.xml @@ -45,7 +45,7 @@ - + diff --git a/tests/unittests/Message_test.cpp b/tests/unittests/Message_test.cpp index b4ae32f5..0aa2525a 100644 --- a/tests/unittests/Message_test.cpp +++ b/tests/unittests/Message_test.cpp @@ -116,7 +116,7 @@ TEST(AMessage, CanCarryASimpleInteger) { auto msg = sdbus::createPlainMessage(); - int dataWritten = 5; + const int dataWritten = 5; msg << dataWritten; msg.seal(); @@ -131,7 +131,7 @@ TEST(AMessage, CanCarryAUnixFd) { auto msg = sdbus::createPlainMessage(); - sdbus::UnixFd dataWritten{0}; + const sdbus::UnixFd dataWritten{0}; msg << dataWritten; msg.seal(); @@ -146,7 +146,7 @@ TEST(AMessage, CanCarryAVariant) { auto msg = sdbus::createPlainMessage(); - auto dataWritten = sdbus::Variant((double)3.14); + const auto dataWritten = sdbus::Variant((double)3.14); msg << dataWritten; msg.seal(); @@ -162,7 +162,7 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants) auto msg = sdbus::createPlainMessage(); auto value = std::vector{"hello"s, (double)3.14}; - auto dataWritten = sdbus::Variant{value}; + const auto dataWritten = sdbus::Variant{value}; msg << dataWritten; msg.seal(); @@ -174,11 +174,11 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants) ASSERT_THAT(dataRead.get()[1].get(), Eq(value[1].get())); } -TEST(AMessage, CanCarryAnArray) +TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdVector) { auto msg = sdbus::createPlainMessage(); - std::vector dataWritten{3545342, 43643532, 324325}; + const std::vector dataWritten{3545342, 43643532, 324325}; msg << dataWritten; msg.seal(); @@ -189,6 +189,116 @@ TEST(AMessage, CanCarryAnArray) ASSERT_THAT(dataRead, Eq(dataWritten)); } +TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdVector) +{ + auto msg = sdbus::createPlainMessage(); + + const std::vector dataWritten{"s", "u", "b"}; + + msg << dataWritten; + msg.seal(); + + std::vector dataRead; + msg >> dataRead; + + ASSERT_THAT(dataRead, Eq(dataWritten)); +} + +TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdArray) +{ + auto msg = sdbus::createPlainMessage(); + + const std::array dataWritten{3545342, 43643532, 324325}; + + msg << dataWritten; + msg.seal(); + + std::array dataRead; + msg >> dataRead; + + ASSERT_THAT(dataRead, Eq(dataWritten)); +} + +TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray) +{ + auto msg = sdbus::createPlainMessage(); + + const std::array dataWritten{"s", "u", "b"}; + + msg << dataWritten; + msg.seal(); + + std::array dataRead; + msg >> dataRead; + + ASSERT_THAT(dataRead, Eq(dataWritten)); +} + +#if __cplusplus >= 202002L +TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdSpan) +{ + auto msg = sdbus::createPlainMessage(); + + const std::array sourceArray{3545342, 43643532, 324325}; + const std::span dataWritten{sourceArray}; + + msg << dataWritten; + msg.seal(); + + std::array destinationArray; + std::span dataRead{destinationArray}; + msg >> dataRead; + + ASSERT_THAT(std::vector(dataRead.begin(), dataRead.end()), Eq(std::vector(dataWritten.begin(), dataWritten.end()))); +} + +TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan) +{ + auto msg = sdbus::createPlainMessage(); + + const std::array sourceArray{"s", "u", "b"}; + const std::span dataWritten{sourceArray}; + + msg << dataWritten; + msg.seal(); + + std::array destinationArray; + std::span dataRead{destinationArray}; + msg >> dataRead; + + ASSERT_THAT(std::vector(dataRead.begin(), dataRead.end()), Eq(std::vector(dataWritten.begin(), dataWritten.end()))); +} +#endif + +TEST(AMessage, ThrowsWhenDestinationStdArrayIsTooSmallDuringDeserialization) +{ + auto msg = sdbus::createPlainMessage(); + + const std::vector dataWritten{3545342, 43643532, 324325, 89789, 15343}; + + msg << dataWritten; + msg.seal(); + + std::array dataRead; + ASSERT_THROW(msg >> dataRead, sdbus::Error); +} + +#if __cplusplus >= 202002L +TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization) +{ + auto msg = sdbus::createPlainMessage(); + + const std::array dataWritten{3545342, 43643532, 324325}; + + msg << dataWritten; + msg.seal(); + + std::array destinationArray; + std::span dataRead{destinationArray}; + ASSERT_THROW(msg >> dataRead, sdbus::Error); +} +#endif + TEST(AMessage, CanCarryADictionary) { auto msg = sdbus::createPlainMessage(); diff --git a/tests/unittests/TypeTraits_test.cpp b/tests/unittests/TypeTraits_test.cpp index 825d9c1e..169aa2de 100644 --- a/tests/unittests/TypeTraits_test.cpp +++ b/tests/unittests/TypeTraits_test.cpp @@ -77,7 +77,12 @@ namespace TYPE(sdbus::Struct)HAS_DBUS_TYPE_SIGNATURE("(b)") TYPE(sdbus::Struct)HAS_DBUS_TYPE_SIGNATURE("(qdsv)") TYPE(std::vector)HAS_DBUS_TYPE_SIGNATURE("an") + TYPE(std::array)HAS_DBUS_TYPE_SIGNATURE("an") +#if __cplusplus >= 202002L + TYPE(std::span)HAS_DBUS_TYPE_SIGNATURE("ao") +#endif TYPE(std::map)HAS_DBUS_TYPE_SIGNATURE("a{ix}") + TYPE(std::unordered_map)HAS_DBUS_TYPE_SIGNATURE("a{ix}") using ComplexType = std::map< uint64_t, sdbus::Struct< @@ -86,9 +91,10 @@ namespace std::vector< sdbus::Struct< sdbus::ObjectPath, + std::array, bool, sdbus::Variant, - std::map + std::unordered_map > > >, @@ -97,7 +103,7 @@ namespace const char* > >; - TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}ghs)}") + TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghs)}") typedef ::testing::Types< bool , uint8_t @@ -117,7 +123,12 @@ namespace , sdbus::Struct , sdbus::Struct , std::vector + , std::array +#if __cplusplus >= 202002L + , std::span +#endif , std::map + , std::unordered_map , ComplexType > DBusSupportedTypes;