feat: support serialization of array, span and unordered_map (#342)

* feat: support serialization of array, span and unordered_map

* fix some spelling mistakes

* docs: update table of valid c++ types

---------

Co-authored-by: Marcel Hellwig <github@cookiesoft.de>
This commit is contained in:
Stanislav Angelovič
2023-08-03 13:55:37 +02:00
committed by GitHub
parent 0a2bda9c67
commit fb61420bf0
12 changed files with 468 additions and 110 deletions

View File

@ -100,7 +100,7 @@ $ ninja libsystemd.so.0.26.0 # or another version number depending which system
### Building and distributing libsystemd as part of sdbus-c++
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd enviroments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Be sure to check out the systemd documentation for the Also, when distributing, make sure these dependency libraries are installed on the production machine.
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd environments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Be sure to check out the systemd documentation for the Also, when distributing, make sure these dependency libraries are installed on the production machine.
You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242).
@ -203,7 +203,7 @@ sdbus-c++ is completely thread-aware by design. Though sdbus-c++ is not thread-s
* Creating and emitting signals on an `Object` instance.
* Creating and sending method calls (both synchronously and asynchronously) on an `Proxy` instance. (But it's generally better that our threads use their own exclusive instances of proxy, to minimize shared state and contention.)
sdbus-c++ is designed such that all the above operations are thread-safe also on a connection that is running an event loop (usually in a separate thread) at that time. It's an internal thread safety. For example, a signal arrives and is processed by sdbus-c++ even loop at an appropriate `Proxy` instance, while the user is going to destroy that instance in their application thread. The user cannot explicitly control these situations (or they could, but that would be very limiting and cubersome on the API level).
sdbus-c++ is designed such that all the above operations are thread-safe also on a connection that is running an event loop (usually in a separate thread) at that time. It's an internal thread safety. For example, a signal arrives and is processed by sdbus-c++ even loop at an appropriate `Proxy` instance, while the user is going to destroy that instance in their application thread. The user cannot explicitly control these situations (or they could, but that would be very limiting and cumbersome on the API level).
However, other combinations, that the user invokes explicitly from within more threads are NOT thread-safe in sdbus-c++ by design, and the user should make sure by their design that these cases never occur. For example, destroying an `Object` instance in one thread while emitting a signal on it in another thread is not thread-safe. In this specific case, the user should make sure in their application that all threads stop working with a specific instance before a thread proceeds with deleting that instance.
@ -307,7 +307,7 @@ int main(int argc, char *argv[])
}
```
We establish a D-Bus sytem connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We register interfaces, its methods, signals that the object provides, and, through `finishRegistration()`, export the object (i.e., make it visible to clients) on the bus. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests.
We establish a D-Bus system connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We register interfaces, its methods, signals that the object provides, and, through `finishRegistration()`, export the object (i.e., make it visible to clients) on the bus. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests.
The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall call)`. The `call` parameter is the incoming method call message. We need to deserialize our method input arguments from it. Then we can invoke the logic of the method and get the results. Then for the given `call`, we create a `reply` message, pack results into it and send it back to the caller through `send()`. (If we had a void-returning method, we'd just send an empty `reply` back.) We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`.
@ -587,7 +587,7 @@ Yes, there is -- we can access the corresponding D-Bus message in:
* property set implementation callback handlers (server side),
* signal callback handlers (client side).
Both `IObject` and `IProxy` provide the `getCurrentlyProcessedMessage()` method. This method is meant to be called from within a callback handler. It returns a pointer to the corresponding D-Bus message that caused invocation of the handler. The pointer is only valid (dereferencable) as long as the flow of execution does not leave the callback handler. When called from other contexts/threads, the pointer may be both zero or non-zero, and its dereferencing is undefined behavior.
Both `IObject` and `IProxy` provide the `getCurrentlyProcessedMessage()` method. This method is meant to be called from within a callback handler. It returns a pointer to the corresponding D-Bus message that caused invocation of the handler. The pointer is only valid (dereferenceable) as long as the flow of execution does not leave the callback handler. When called from other contexts/threads, the pointer may be both zero or non-zero, and its dereferencing is undefined behavior.
An excerpt of the above example of concatenator modified to print out a name of the sender of method call:
@ -765,7 +765,7 @@ In our object class we need to:
* Give an implementation to the D-Bus object's methods by overriding corresponding virtual functions,
* call `registerAdaptor()` in the constructor, which makes the adaptor (the D-Bus object underneath it) available for remote calls,
* call `unregisterAdaptor()`, which, conversely, unregisters the adaptor from the bus.
* call `unregisterAdaptor()`, which, conversely, deregisters the adaptor from the bus.
Calling `registerAdaptor()` and `unregisterAdaptor()` was not necessary in previous sdbus-c++ versions, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders.
@ -844,7 +844,7 @@ In our proxy class we need to:
* Give an implementation to signal handlers and asynchronous method reply handlers (if any) by overriding corresponding virtual functions,
* call `registerProxy()` in the constructor, which makes the proxy (the D-Bus proxy object underneath it) ready to receive signals and async call replies,
* call `unregisterProxy()`, which, conversely, unregisters the proxy from the bus.
* call `unregisterProxy()`, which, conversely, deregisters the proxy from the bus.
Calling `registerProxy()` and `unregisterProxy()` was not necessary in previous versions of sdbus-c++, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders.
@ -1002,7 +1002,7 @@ Callbacks of async methods based on convenience sdbus-c++ API have slightly diff
* The result holder is of type `Result<Types...>&&`, where `Types...` is a list of method output argument types.
* The result object must be the first physical parameter of the callback taken by r-value ref. `Result` class template is move-only.
* The callback itself is physically a void-returning function.
* Method input arguments are taken by value rathern than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, this falls back to copying.
* Method input arguments are taken by value rather than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, this falls back to copying.
So the concatenate callback signature would change from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator)`:
@ -1037,7 +1037,7 @@ void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbe
The `Result` is a convenience class that represents a future method result, and it is where we write the results (`returnResults()`) or an error (`returnError()`) which we want to send back to the client.
Registraion (`implementedAs()`) doesn't change. Nothing else needs to change.
Registration (`implementedAs()`) doesn't change. Nothing else needs to change.
### Marking server-side async methods in the IDL
@ -1349,22 +1349,22 @@ Using D-Bus Types
For many D-Bus interactions dealing with D-Bus types is necessary. For that, sdbus-c++ provides many predefined D-Bus types. The table below shows which C++ type corresponds to which D-Bus type.
| Category | Code | Code ASCII | Conventional Name | C++ Type |
| Category | Code | Code ASCII | Conventional Name | C++ Type |
|---------------------|-------------|------------|--------------------|---------------------------------|
| reserved | 0 | NUL | INVALID | - |
| fixed, basic | 121 | y | BYTE | `uint8_t` |
| fixed, basic | 98 | b | BOOLEAN | `bool` |
| fixed, basic | 110 | n | INT16 | `int16_t` |
| fixed, basic | 113 | q | UINT16 | `uint16_t` |
| fixed, basic | 105 | i | INT32 | `int32_t` |
| fixed, basic | 117 | u | UINT32 | `uint32_t` |
| fixed, basic | 120 | x | INT64 | `int64_t` |
| fixed, basic | 116 | t | UINT64 | `uint64_t` |
| fixed, basic | 100 | d | DOUBLE | `double` |
| string-like, basic | 115 | s | STRING | `const char*`, `std::string` |
| string-like, basic | 111 | o | OBJECT_PATH | `sdbus::ObjectPath` |
| string-like, basic | 103 | g | SIGNATURE | `sdbus::Signature` |
| container | 97 | a | ARRAY | `std::vector<T>` (if used as an array followed by a single complete type T), or `std::map<T1, T2>` (if used as an array of dict entries) |
| fixed, basic | 121 | y | BYTE | `uint8_t` |
| fixed, basic | 98 | b | BOOLEAN | `bool` |
| fixed, basic | 110 | n | INT16 | `int16_t` |
| fixed, basic | 113 | q | UINT16 | `uint16_t` |
| fixed, basic | 105 | i | INT32 | `int32_t` |
| fixed, basic | 117 | u | UINT32 | `uint32_t` |
| fixed, basic | 120 | x | INT64 | `int64_t` |
| fixed, basic | 116 | t | UINT64 | `uint64_t` |
| fixed, basic | 100 | d | DOUBLE | `double` |
| string-like, basic | 115 | s | STRING | `const char*`, `std::string` |
| string-like, basic | 111 | o | OBJECT_PATH | `sdbus::ObjectPath` |
| 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 | 101,123,125 | e{} | DICT_ENTRY | - |

View File

@ -31,7 +31,12 @@
#include <sdbus-c++/Error.h>
#include <string>
#include <vector>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <map>
#include <unordered_map>
#include <utility>
#include <cstdint>
#include <cassert>
@ -85,10 +90,23 @@ namespace sdbus {
Message& operator<<(const ObjectPath &item);
Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item);
template <typename _Element> Message& operator<<(const std::vector<_Element>& items);
template <typename _Key, typename _Value> Message& operator<<(const std::map<_Key, _Value>& items);
template <typename... _ValueTypes> Message& operator<<(const Struct<_ValueTypes...>& item);
template <typename... _ValueTypes> Message& operator<<(const std::tuple<_ValueTypes...>& item);
template <typename _Element, typename _Allocator>
Message& operator<<(const std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator<<(const std::array<_Element, _Size>& items);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator<<(const std::span<_Element, _Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator<<(const Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
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 <typename _Element> Message& operator>>(std::vector<_Element>& items);
template <typename _Key, typename _Value> Message& operator>>(std::map<_Key, _Value>& items);
template <typename... _ValueTypes> Message& operator>>(Struct<_ValueTypes...>& item);
template <typename... _ValueTypes> Message& operator>>(std::tuple<_ValueTypes...>& item);
template <typename _Element, typename _Allocator>
Message& operator>>(std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator>>(std::array<_Element, _Size>& items);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator>>(std::span<_Element, _Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator>>(Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
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 <typename _Array>
void serializeArray(const _Array& items);
template <typename _Array>
void deserializeArray(_Array& items);
template <typename _Array>
void deserializeArrayFast(_Array& items);
template <typename _Element, typename _Allocator>
void deserializeArrayFast(std::vector<_Element, _Allocator>& items);
template <typename _Array>
void deserializeArraySlow(_Array& items);
template <typename _Element, typename _Allocator>
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 <typename _Dictionary>
void serializeDictionary(const _Dictionary& items);
template <typename _Dictionary>
void deserializeDictionary(_Dictionary& items);
protected:
Message() = default;
explicit Message(internal::ISdBus* sdbus) noexcept;
@ -256,32 +305,77 @@ namespace sdbus {
PlainMessage() = default;
};
template <typename _Element>
inline Message& Message::operator<<(const std::vector<_Element>& items)
template <typename _Element, typename _Allocator>
inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
{
serializeArray(items);
return *this;
}
template <typename _Element, std::size_t _Size>
inline Message& Message::operator<<(const std::array<_Element, _Size>& items)
{
serializeArray(items);
return *this;
}
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator<<(const std::span<_Element, _Extent>& items)
{
serializeArray(items);
return *this;
}
#endif
template <typename _Array>
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<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
appendArray(*signature_of<_Element>::str().c_str(), items.data(), items.size() * sizeof(_Element));
appendArray(*signature_of<ElementType>::str().c_str(), items.data(), items.size() * sizeof(ElementType));
}
else
{
openContainer(signature_of<_Element>::str());
openContainer(signature_of<ElementType>::str());
for (const auto& item : items)
*this << item;
closeContainer();
}
}
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items)
{
serializeDictionary(items);
return *this;
}
template <typename _Key, typename _Value>
inline Message& Message::operator<<(const std::map<_Key, _Value>& items)
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
{
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
serializeDictionary(items);
return *this;
}
template <typename _Dictionary>
inline void Message::serializeDictionary(const _Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::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<Struct<_ValueTypes...>>::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 <typename _Element>
inline Message& Message::operator>>(std::vector<_Element>& items)
template <typename _Element, typename _Allocator>
inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
{
// 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>)
{
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)));
}
else
{
if(!enterContainer(signature_of<_Element>::str()))
return *this;
while (true)
{
_Element elem;
if (*this >> elem)
items.emplace_back(std::move(elem));
else
break;
}
clearFlags();
exitContainer();
}
deserializeArray(items);
return *this;
}
template <typename _Key, typename _Value>
inline Message& Message::operator>>(std::map<_Key, _Value>& items)
template <typename _Element, std::size_t _Size>
inline Message& Message::operator>>(std::array<_Element, _Size>& items)
{
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
deserializeArray(items);
return *this;
}
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator>>(std::span<_Element, _Extent>& items)
{
deserializeArray(items);
return *this;
}
#endif
template <typename _Array>
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<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
deserializeArrayFast(items);
}
else
{
deserializeArraySlow(items);
}
}
template <typename _Array>
inline void Message::deserializeArrayFast(_Array& items)
{
using ElementType = typename _Array::value_type;
size_t arraySize{};
const ElementType* arrayPtr{};
readArray(*signature_of<ElementType>::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 <typename _Element, typename _Allocator>
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 <typename _Array>
inline void Message::deserializeArraySlow(_Array& items)
{
using ElementType = typename _Array::value_type;
if(!enterContainer(signature_of<ElementType>::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();
exitContainer();
}
template <typename _Element, typename _Allocator>
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
{
if(!enterContainer(signature_of<_Element>::str()))
return;
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 <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items)
{
deserializeDictionary(items);
return *this;
}
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
{
deserializeDictionary(items);
return *this;
}
template <typename _Dictionary>
inline void Message::deserializeDictionary(_Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::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

View File

@ -30,7 +30,12 @@
#include <type_traits>
#include <string>
#include <vector>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <map>
#include <unordered_map>
#include <cstdint>
#include <functional>
#include <memory>
@ -105,6 +110,11 @@ namespace sdbus {
}
};
template <typename _T>
struct signature_of<const _T>
: public signature_of<_T>
{};
template <>
struct signature_of<void>
{
@ -349,8 +359,8 @@ namespace sdbus {
}
};
template <typename _Element>
struct signature_of<std::vector<_Element>>
template <typename _Element, typename _Allocator>
struct signature_of<std::vector<_Element, _Allocator>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
@ -361,8 +371,46 @@ namespace sdbus {
}
};
template <typename _Key, typename _Value>
struct signature_of<std::map<_Key, _Value>>
template <typename _Element, std::size_t _Size>
struct signature_of<std::array<_Element, _Size>>
{
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 <typename _Element, std::size_t _Extent>
struct signature_of<std::span<_Element, _Extent>>
{
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 <typename _Key, typename _Value, typename _Compare, typename _Allocator>
struct signature_of<std::map<_Key, _Value, _Compare, _Allocator>>
{
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 <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
struct signature_of<std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;

View File

@ -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;

View File

@ -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)

View File

@ -104,7 +104,7 @@ int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, c
return res;
}
uint32_t TestAdaptor::sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
uint32_t TestAdaptor::sumArrayItems(const std::vector<uint16_t>& a, const std::array<uint64_t, 3>& 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<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex()
std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex()
{
return { // map
return { // unordered_map
{
0, // uint_64_t
{ // struct
@ -390,7 +390,7 @@ R"delimiter(
<arg type="(ix)" direction="in"/>
<arg type="i" direction="out"/>
</method>
<method name="sumVectorItems">
<method name="sumArrayItems">
<arg type="aq" direction="in"/>
<arg type="at" direction="in"/>
<arg type="u" direction="out"/>

View File

@ -70,13 +70,13 @@ protected:
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) override;
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override;
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) override;
uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) override;
uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1) override;
uint32_t doOperation(const uint32_t& arg0) override;
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override;
sdbus::Signature getSignature() override;
sdbus::ObjectPath getObjPath() override;
sdbus::UnixFd getUnixFd() override;
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override;
std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override;
void throwError() override;
void throwErrorWithNoReply() override;
void doPrivilegedStuff() override;
@ -127,13 +127,13 @@ protected:
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>&, const sdbus::Struct<sdbus::Variant, sdbus::Variant>&) override { return {}; }
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override { return {}; }
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>&, const sdbus::Struct<int32_t, int64_t>&) override { return {}; }
uint32_t sumVectorItems(const std::vector<uint16_t>&, const std::vector<uint64_t>&) override { return {}; }
uint32_t sumArrayItems(const std::vector<uint16_t>&, const std::array<uint64_t, 3>&) override { return {}; }
uint32_t doOperation(const uint32_t&) override { return {}; }
void doOperationAsync(sdbus::Result<uint32_t>&&, uint32_t) override {}
sdbus::Signature getSignature() override { return {}; }
sdbus::ObjectPath getObjPath() override { return {}; }
sdbus::UnixFd getUnixFd() override { return {}; }
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override { return {}; }
std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override { return {}; }
void throwError() override {}
void throwErrorWithNoReply() override {}
void doPrivilegedStuff() override {}

View File

@ -33,7 +33,7 @@ protected:
object_->registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& 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<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1){ return this->sumStructItems(arg0, arg1); });
object_->registerMethod("sumVectorItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1){ return this->sumVectorItems(arg0, arg1); });
object_->registerMethod("sumArrayItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& 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<uint32_t>&& 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 @@ private:
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) = 0;
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() = 0;
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) = 0;
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) = 0;
virtual uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1) = 0;
virtual uint32_t doOperation(const uint32_t& arg0) = 0;
virtual void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0;
virtual sdbus::Signature getSignature() = 0;
virtual sdbus::ObjectPath getObjPath() = 0;
virtual sdbus::UnixFd getUnixFd() = 0;
virtual std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
virtual std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
virtual void throwError() = 0;
virtual void throwErrorWithNoReply() = 0;
virtual void doPrivilegedStuff() = 0;

View File

@ -105,10 +105,10 @@ public:
return result;
}
uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1)
uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& 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 @@ public:
return result;
}
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex()
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex()
{
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> result;
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> result;
proxy_->callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}

View File

@ -45,7 +45,7 @@
<arg type="(ix)" direction="in" />
<arg type="i" direction="out" />
</method>
<method name="sumVectorItems">
<method name="sumArrayItems">
<arg type="aq" direction="in" />
<arg type="at" direction="in" />
<arg type="u" direction="out" />

View File

@ -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<sdbus::Variant>{"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<decltype(value)>()[1].get<double>(), Eq(value[1].get<double>()));
}
TEST(AMessage, CanCarryAnArray)
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdVector)
{
auto msg = sdbus::createPlainMessage();
std::vector<int64_t> dataWritten{3545342, 43643532, 324325};
const std::vector<int64_t> 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<sdbus::Signature> dataWritten{"s", "u", "b"};
msg << dataWritten;
msg.seal();
std::vector<sdbus::Signature> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdArray)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> dataWritten{3545342, 43643532, 324325};
msg << dataWritten;
msg.seal();
std::array<int, 3> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
{
auto msg = sdbus::createPlainMessage();
const std::array<sdbus::Signature, 3> dataWritten{"s", "u", "b"};
msg << dataWritten;
msg.seal();
std::array<sdbus::Signature, 3> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
#if __cplusplus >= 202002L
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdSpan)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> sourceArray{3545342, 43643532, 324325};
const std::span dataWritten{sourceArray};
msg << dataWritten;
msg.seal();
std::array<int, 3> 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<sdbus::Signature, 3> sourceArray{"s", "u", "b"};
const std::span dataWritten{sourceArray};
msg << dataWritten;
msg.seal();
std::array<sdbus::Signature, 3> 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<int> dataWritten{3545342, 43643532, 324325, 89789, 15343};
msg << dataWritten;
msg.seal();
std::array<int, 3> dataRead;
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
#if __cplusplus >= 202002L
TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> dataWritten{3545342, 43643532, 324325};
msg << dataWritten;
msg.seal();
std::array<int, 2> destinationArray;
std::span dataRead{destinationArray};
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
#endif
TEST(AMessage, CanCarryADictionary)
{
auto msg = sdbus::createPlainMessage();

View File

@ -77,7 +77,12 @@ namespace
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
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")
#if __cplusplus >= 202002L
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("ao")
#endif
TYPE(std::map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
TYPE(std::unordered_map<int32_t, int64_t>)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<int16_t, 3>,
bool,
sdbus::Variant,
std::map<int, std::string>
std::unordered_map<int, std::string>
>
>
>,
@ -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<bool>
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
, std::vector<int16_t>
, std::array<int16_t, 3>
#if __cplusplus >= 202002L
, std::span<int16_t>
#endif
, std::map<int32_t, int64_t>
, std::unordered_map<int32_t, int64_t>
, ComplexType
> DBusSupportedTypes;