refactor: enable compile-time D-Bus signature generation (#425)

Since sdbus-c++ knows types of D-Bus call/signal/property input/output parameters at compile time, why not assemble the D-Bus signature strings at compile time, too, instead of assembling them at run time as std::string with likely cost of (unnecessary) heap allocations...

std::array is well supported constexpr type in C++20, so we harness it to build the D-Bus signature string and store it into the binary at compile time.
This commit is contained in:
Stanislav Angelovič
2024-04-16 15:40:43 +02:00
parent 32a97f214f
commit d856969051
9 changed files with 294 additions and 319 deletions

View File

@ -15,9 +15,12 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-22.04]
compiler: [g++, clang]
compiler: [g++]
build: [shared-libsystemd]
include:
- os: ubuntu-22.04
compiler: clang
build: shared-libsystemd
- os: ubuntu-22.04
compiler: g++
build: embedded-static-libsystemd
@ -94,7 +97,7 @@ jobs:
cd build
cpack -G DEB
- name: 'Upload Artifact'
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04' && matrix.compiler == 'g++'
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04' && matrix.compiler == 'g++'
uses: actions/upload-artifact@v3
with:
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"

View File

@ -56,7 +56,7 @@ PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.6],,
)
```
> **_Note_:** sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6).
> **_Note_:** sdbus-c++ library uses a number of modern C++17 (and, conditionally, C++20) features. Please make sure you have a recent compiler (best gcc >= 10, clang >= 11).
If you intend to use xml-to-c++ generator tool (explained later) in your project to generate interface headers from XML, you can integrate that too with CMake or `pkg-config`:
@ -1568,7 +1568,7 @@ We need two things to do that:
* implement `sdbus::Message` insertion (serialization) and extraction (deserialization) operators, so sdbus-c++ knows how to serialize/deserialize our custom type,
* specialize `sdbus::signature_of` template for our custom type, so sdbus-c++ knows the mapping to D-Bus type and other necessary information about our type.
Say, we would like to represent D-Bus arrays as `std::list`s in our application. Since sdbus-c++ comes with pre-defined support for `std::vector`s, `std::array`s and `std::span`s as D-Bus array representations, we have to provide an extension. To implement message serialization and deserialization functions for `std::list`, we can simply copy the sdbus-c++ implementation of these functions for `std::vector`, and simply adjust for `std::list`. Then we provide `signature_of` specialization, again written on terms of one specialized for `std::vector`:
Say, we would like to represent D-Bus arrays as `std::list`s in our application. Since sdbus-c++ comes with pre-defined support for `std::vector`s, `std::array`s and `std::span`s as D-Bus array representations, we have to provide an extension. To implement message serialization and deserialization functions for `std::list`, we can simply copy the sdbus-c++ implementation of these functions for `std::vector`, and simply adjust for `std::list`. Then we provide `signature_of` specialization, again written in terms of one specialized for `std::vector`:
```c++
#include <list>
@ -1580,7 +1580,7 @@ namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
{
msg.openContainer(sdbus::signature_of<_ElementType>::str());
msg.openContainer<_ElementType>();
for (const auto& item : items)
msg << item;
@ -1594,7 +1594,7 @@ sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& i
template <typename _ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
{
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
if(!msg.enterContainer<_ElementType>())
return msg;
while (true)
@ -1615,16 +1615,18 @@ sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
} // namespace sdbus
// Implementing type traits for std::list, and since we map it to D-Bus array,
// we can re-use std::vector type traits because it's the same stuff.
// Implementing type traits for std::list -- we re-use by inheriting
// from type traits already provided by sdbus-c++ for D-Bus arrays
template <typename _Element, typename _Allocator>
struct sdbus::signature_of<std::list<_Element, _Allocator>>
: sdbus::signature_of<std::vector<_Element, _Allocator>>
: sdbus::signature_of<std::vector<_Element>>
{};
```
Then we can simply use `std::list`s, serialize/deserialize them in a D-Bus message, in D-Bus method calls or return values... and they will be simply transmitted as D-Bus arrays.
Similarly, say we have our own `lockfree_map` which we would like to use natively with sdbus-c++ as a C++ type for D-Bus dictionary -- we can copy or build on top of `std::map` specializations.
As another example, say we have our custom type `my::Struct` which we'd like to use as a D-Bus structure representation (sdbus-c++ provides `sdbus::Struct` type for that, but we don't want to use it because using our custom type directly is more convenient). Again, we have to provide type traits and message serialization/deserialization functions for our custom type. We build our functions and specializations on top of `sdbus::Struct`, so we don't have to copy and write a lot of boiler-plate. Serialization/deserialization functions can be placed in the same namespace as our custom type, and will be found thanks to the ADR lookup. The `signature_of` specialization must always be in either `sdbus` namespace or in a global namespace:
```c++
@ -1778,6 +1780,7 @@ sdbus-c++ v2 is a major release that comes with a number of breaking API/ABI/beh
* `createDefaultBusConnection()` has been renamed to `createBusConnection()`.
* Change in behavior: `Proxy`s now by default call `createBusConnection()` to get a connection when the connection is not provided explicitly by the caller, so they connect to either the session bus or the system bus depending on the context (as opposed to always to the system bus like before).
* Callbacks taking `const sdbus::Error* error` were changed to take `std::optional<sdbus::Error>`, which better expresses the intent and meaning.
* D-Bus signatures when using high-level API are now assembled at compile time. There are breaking changes inside `signature_of` type traits and `Message` serialization/deserialization methods. This only interests you if you extend sdbus-c++ type system with your own types. See the updated tutorial on extending sdbus-c++ type system.
* Types and methods marked deprecated in sdbus-c++ v1 were removed completely.
* CMake options got `SDBUSCPP_` prefix for better usability and minimal risk of conflicts in downstream CMake projects. `SDBUSCPP_INSTALL` CMake option was added.
* CMake components got `sdbus-c++-` prefix.

View File

@ -161,22 +161,38 @@ namespace sdbus {
template <typename... _ValueTypes>
Message& operator>>(std::tuple<_ValueTypes...>& item);
Message& openContainer(const std::string& signature);
template <typename _ElementType>
Message& openContainer();
Message& openContainer(const char* signature);
Message& closeContainer();
Message& openDictEntry(const std::string& signature);
template <typename _KeyType, typename _ValueType>
Message& openDictEntry();
Message& openDictEntry(const char* signature);
Message& closeDictEntry();
Message& openVariant(const std::string& signature);
template <typename _ValueType>
Message& openVariant();
Message& openVariant(const char* signature);
Message& closeVariant();
Message& openStruct(const std::string& signature);
template <typename... _ValueTypes>
Message& openStruct();
Message& openStruct(const char* signature);
Message& closeStruct();
Message& enterContainer(const std::string& signature);
template <typename _ElementType>
Message& enterContainer();
Message& enterContainer(const char* signature);
Message& exitContainer();
Message& enterDictEntry(const std::string& signature);
template <typename _KeyType, typename _ValueType>
Message& enterDictEntry();
Message& enterDictEntry(const char* signature);
Message& exitDictEntry();
Message& enterVariant(const std::string& signature);
template <typename _ValueType>
Message& enterVariant();
Message& enterVariant(const char* signature);
Message& exitVariant();
Message& enterStruct(const std::string& signature);
template <typename... _ValueTypes>
Message& enterStruct();
Message& enterStruct(const char* signature);
Message& exitStruct();
Message& appendArray(char type, const void *ptr, size_t size);
@ -320,8 +336,9 @@ namespace sdbus {
template <typename ...Elements>
inline Message& Message::operator<<(const std::variant<Elements...>& value)
{
std::visit([this](const auto& inner){
openVariant(signature_of<decltype(inner)>::str());
std::visit([this](const auto& inner)
{
openVariant<decltype(inner)>();
*this << inner;
closeVariant();
}, value);
@ -370,11 +387,12 @@ namespace sdbus {
// otherwise use step-by-step serialization of individual elements.
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
appendArray(*signature_of<ElementType>::str().c_str(), items.data(), items.size() * sizeof(ElementType));
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType));
}
else
{
openContainer(signature_of<ElementType>::str());
openContainer<ElementType>();
for (const auto& item : items)
*this << item;
@ -403,16 +421,13 @@ namespace sdbus {
inline void Message::serializeDictionary(const _Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
using MappedType = typename _Dictionary::mapped_type;
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
openContainer(arraySignature);
openContainer<DictEntry<KeyType, MappedType>>();
for (const auto& item : items)
{
openDictEntry(dictEntrySignature);
openDictEntry<KeyType, MappedType>();
*this << item.first;
*this << item.second;
closeDictEntry();
@ -441,12 +456,7 @@ namespace sdbus {
template <typename... _ValueTypes>
inline Message& Message::operator<<(const Struct<_ValueTypes...>& item)
{
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);
openStruct(structContentSignature);
openStruct<_ValueTypes...>();
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
closeStruct();
@ -465,11 +475,13 @@ namespace sdbus {
template <typename _Element, typename... _Elements>
bool deserialize_variant(Message& msg, std::variant<_Elements...>& value, const std::string& signature)
{
if (signature != signature_of<_Element>::str())
constexpr auto elemSignature = sdbus::signature_of_v<_Element>;
// TODO: Try to optimize
if (signature != std::string(elemSignature.begin(), elemSignature.end()))
return false;
_Element temp;
msg.enterVariant(signature);
msg.enterVariant(signature.c_str());
msg >> temp;
msg.exitVariant();
value = std::move(temp);
@ -482,6 +494,7 @@ namespace sdbus {
{
std::string type;
std::string contentType;
// TODO: Refactor ppekType prior to release/v2.0 to return pair of const char* or string_view...
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);
@ -548,7 +561,8 @@ namespace sdbus {
size_t arraySize{};
const ElementType* arrayPtr{};
readArray(*signature_of<ElementType>::str().c_str(), (const void**)&arrayPtr, &arraySize);
constexpr auto signature = as_null_terminated(sdbus::signature_of_v<ElementType>);
readArray(*signature.data(), (const void**)&arrayPtr, &arraySize);
size_t elementsInMsg = arraySize / sizeof(ElementType);
bool notEnoughSpace = items.size() < elementsInMsg;
@ -563,7 +577,8 @@ namespace sdbus {
size_t arraySize{};
const _Element* arrayPtr{};
readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);
constexpr auto signature = as_null_terminated(sdbus::signature_of_v<_Element>);
readArray(*signature.data(), (const void**)&arrayPtr, &arraySize);
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
}
@ -573,7 +588,7 @@ namespace sdbus {
{
using ElementType = typename _Array::value_type;
if(!enterContainer(signature_of<ElementType>::str()))
if(!enterContainer<ElementType>())
return;
for (auto& elem : items)
@ -590,7 +605,7 @@ namespace sdbus {
template <typename _Element, typename _Allocator>
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
{
if(!enterContainer(signature_of<_Element>::str()))
if(!enterContainer<_Element>())
return;
while (true)
@ -627,21 +642,18 @@ namespace sdbus {
inline void Message::deserializeDictionary(_Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
using MappedType = typename _Dictionary::mapped_type;
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
if (!enterContainer(arraySignature))
if (!enterContainer<DictEntry<KeyType, MappedType>>())
return;
while (true)
{
if (!enterDictEntry(dictEntrySignature))
if (!enterDictEntry<KeyType, MappedType>())
break;
KeyType key;
ValueType value;
MappedType value;
*this >> key >> value;
items.emplace(std::move(key), std::move(value));
@ -674,11 +686,7 @@ namespace sdbus {
template <typename... _ValueTypes>
inline Message& Message::operator>>(Struct<_ValueTypes...>& item)
{
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
if (!enterStruct(structContentSignature))
if (!enterStruct<_ValueTypes...>())
return *this;
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
@ -695,6 +703,62 @@ namespace sdbus {
return *this;
}
template <typename _ElementType>
inline Message& Message::openContainer()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>);
return openContainer(signature.data());
}
template <typename _KeyType, typename _ValueType>
inline Message& Message::openDictEntry()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_KeyType, _ValueType>>);
return openDictEntry(signature.data());
}
template <typename _ValueType>
inline Message& Message::openVariant()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>);
return openVariant(signature.data());
}
template <typename... _ValueTypes>
inline Message& Message::openStruct()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_ValueTypes...>>);
return openStruct(signature.data());
}
template <typename _ElementType>
inline Message& Message::enterContainer()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>);
return enterContainer(signature.data());
}
template <typename _KeyType, typename _ValueType>
inline Message& Message::enterDictEntry()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_KeyType, _ValueType>>);
return enterDictEntry(signature.data());
}
template <typename _ValueType>
inline Message& Message::enterVariant()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>);
return enterVariant(signature.data());
}
template <typename... _ValueTypes>
inline Message& Message::enterStruct()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_ValueTypes...>>);
return enterStruct(signature.data());
}
}
#endif /* SDBUS_CXX_MESSAGE_H_ */

View File

@ -44,6 +44,7 @@
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
@ -54,6 +55,7 @@ namespace sdbus {
class ObjectPath;
class Signature;
class UnixFd;
template<typename _T1, typename _T2> using DictEntry = std::pair<_T1, _T2>;
class BusName;
class InterfaceName;
class MemberName;
@ -65,6 +67,7 @@ namespace sdbus {
class PropertyGetReply;
template <typename... _Results> class Result;
class Error;
template <typename _T, typename _Enable = void> struct signature_of;
}
namespace sdbus {
@ -106,253 +109,171 @@ namespace sdbus {
// Helper for static assert
template <class... _T> constexpr bool always_false = false;
// Helper operator+ for concatenation of `std::array`s
template <typename _T, std::size_t _N1, std::size_t _N2>
constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs);
// Template specializations for getting D-Bus signatures from C++ types
template <typename _T, typename _Enable = void>
template <typename _T>
constexpr auto signature_of_v = signature_of<_T>::value;
template <typename _T, typename _Enable>
struct signature_of
{
static constexpr bool is_valid = false;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
static constexpr void* value = []
{
// See using-sdbus-c++.md, section "Extending sdbus-c++ type system",
// on how to teach sdbus-c++ about your custom types
static_assert(always_false<_T>, "Unsupported DBus type (template specializations are needed for your custom types)");
return "";
}
static_assert(always_false<_T>, "Unsupported D-Bus type (specialize `signature_of` for your custom types)");
};
};
template <typename _T>
struct signature_of<const _T>
: public signature_of<_T>
struct signature_of<const _T> : signature_of<_T>
{};
template <typename _T>
struct signature_of<_T&>
: public signature_of<_T>
struct signature_of<volatile _T> : signature_of<_T>
{};
template <typename _T>
struct signature_of<_T&> : signature_of<_T>
{};
template <>
struct signature_of<void>
{
static constexpr std::array<char, 0> value{};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "";
}
};
template <>
struct signature_of<bool>
{
static constexpr std::array value{'b'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "b";
}
};
template <>
struct signature_of<uint8_t>
{
static constexpr std::array value{'y'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "y";
}
};
template <>
struct signature_of<int16_t>
{
static constexpr std::array value{'n'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "n";
}
};
template <>
struct signature_of<uint16_t>
{
static constexpr std::array value{'q'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "q";
}
};
template <>
struct signature_of<int32_t>
{
static constexpr std::array value{'i'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "i";
}
};
template <>
struct signature_of<uint32_t>
{
static constexpr std::array value{'u'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "u";
}
};
template <>
struct signature_of<int64_t>
{
static constexpr std::array value{'x'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "x";
}
};
template <>
struct signature_of<uint64_t>
{
static constexpr std::array value{'t'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "t";
}
};
template <>
struct signature_of<double>
{
static constexpr std::array value{'d'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "d";
}
};
template <>
struct signature_of<char*>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <>
struct signature_of<const char*>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <std::size_t _N>
struct signature_of<char[_N]>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <std::size_t _N>
struct signature_of<const char[_N]>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <>
struct signature_of<std::string>
{
static constexpr std::array value{'s'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <>
struct signature_of<char*> : signature_of<std::string>
{};
template <>
struct signature_of<const char*> : signature_of<std::string>
{};
template <std::size_t _N>
struct signature_of<char[_N]> : signature_of<std::string>
{};
template <std::size_t _N>
struct signature_of<const char[_N]> : signature_of<std::string>
{};
template <>
struct signature_of<BusName> : signature_of<std::string>
{
};
{};
template <>
struct signature_of<InterfaceName> : signature_of<std::string>
{
};
{};
template <>
struct signature_of<MemberName> : signature_of<std::string>
{
};
{};
template <typename... _ValueTypes>
struct signature_of<Struct<_ValueTypes...>>
{
static constexpr std::array contents = (signature_of_v<_ValueTypes> + ...);
static constexpr std::array value = std::array{'('} + contents + std::array{')'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
std::string signature;
signature += "(";
(signature += ... += signature_of<_ValueTypes>::str());
signature += ")";
return signature;
}
};
template <>
struct signature_of<Variant>
{
static constexpr std::array value{'v'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "v";
}
};
template <typename... Elements>
@ -362,74 +283,52 @@ namespace sdbus {
template <>
struct signature_of<ObjectPath>
{
static constexpr std::array value{'o'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "o";
}
};
template <>
struct signature_of<Signature>
{
static constexpr std::array value{'g'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "g";
}
};
template <>
struct signature_of<UnixFd>
{
static constexpr std::array value{'h'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
};
static const std::string str()
{
return "h";
}
template <typename _T1, typename _T2>
struct signature_of<DictEntry<_T1, _T2>>
{
static constexpr std::array value = std::array{'{'} + signature_of_v<std::tuple<_T1, _T2>> + std::array{'}'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
};
template <typename _Element, typename _Allocator>
struct signature_of<std::vector<_Element, _Allocator>>
{
static constexpr std::array value = std::array{'a'} + signature_of_v<_Element>;
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();
}
};
template <typename _Element, std::size_t _Size>
struct signature_of<std::array<_Element, _Size>>
struct signature_of<std::array<_Element, _Size>> : signature_of<std::vector<_Element>>
{
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();
}
};
#ifdef __cpp_lib_span
template <typename _Element, std::size_t _Extent>
struct signature_of<std::span<_Element, _Extent>>
struct signature_of<std::span<_Element, _Extent>> : signature_of<std::vector<_Element>>
{
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
@ -438,46 +337,49 @@ namespace sdbus {
: public signature_of<std::underlying_type_t<_Enum>>
{};
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
struct signature_of<std::map<_Key, _Value, _Compare, _Allocator>>
{
static constexpr std::array contents = signature_of_v<std::tuple<_Key, _Value>>;
static constexpr std::array dict_entry = std::array{'{'} + contents + std::array{'}'};
static constexpr std::array value = std::array{'a'} + dict_entry;
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>>
: signature_of<std::map<_Key, _Value>>
{
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... _Types>
struct signature_of<std::tuple<_Types...>> // A simple concatenation of signatures of _Types
{
static constexpr std::array value = (std::array<char, 0>{} + ... + signature_of_v<_Types>);
static constexpr bool is_valid = false;
static constexpr bool is_trivial_dbus_type = false;
};
// To simplify conversions of arrays to C strings
template <typename _T, std::size_t _N>
constexpr auto as_null_terminated(std::array<_T, _N> arr)
{
return arr + std::array<_T, 1>{0};
}
// Function traits implementation inspired by (c) kennytm,
// https://github.com/kennytm/utils/blob/master/traits.hpp
template <typename _Type>
struct function_traits
: public function_traits<decltype(&_Type::operator())>
struct function_traits : function_traits<decltype(&_Type::operator())>
{};
template <typename _Type>
struct function_traits<const _Type>
: public function_traits<_Type>
struct function_traits<const _Type> : function_traits<_Type>
{};
template <typename _Type>
struct function_traits<_Type&>
: public function_traits<_Type>
struct function_traits<_Type&> : function_traits<_Type>
{};
template <typename _ReturnType, typename... _Args>
@ -517,72 +419,62 @@ namespace sdbus {
};
template <typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_Args...)>
: public function_traits_base<_ReturnType, _Args...>
struct function_traits<_ReturnType(_Args...)> : function_traits_base<_ReturnType, _Args...>
{
static constexpr bool is_async = false;
static constexpr bool has_error_param = false;
};
template <typename... _Args>
struct function_traits<void(std::optional<Error>, _Args...)>
: public function_traits_base<void, _Args...>
struct function_traits<void(std::optional<Error>, _Args...)> : function_traits_base<void, _Args...>
{
static constexpr bool has_error_param = true;
};
template <typename... _Args, typename... _Results>
struct function_traits<void(Result<_Results...>, _Args...)>
: public function_traits_base<std::tuple<_Results...>, _Args...>
struct function_traits<void(Result<_Results...>, _Args...)> : function_traits_base<std::tuple<_Results...>, _Args...>
{
static constexpr bool is_async = true;
using async_result_t = Result<_Results...>;
};
template <typename... _Args, typename... _Results>
struct function_traits<void(Result<_Results...>&&, _Args...)>
: public function_traits_base<std::tuple<_Results...>, _Args...>
struct function_traits<void(Result<_Results...>&&, _Args...)> : function_traits_base<std::tuple<_Results...>, _Args...>
{
static constexpr bool is_async = true;
using async_result_t = Result<_Results...>;
};
template <typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(*)(_Args...)>
: public function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(*)(_Args...)> : function_traits<_ReturnType(_Args...)>
{};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...)>
: public function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...)> : function_traits<_ReturnType(_Args...)>
{
typedef _ClassType& owner_type;
};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const>
: public function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const> : function_traits<_ReturnType(_Args...)>
{
typedef const _ClassType& owner_type;
};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile>
: public function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile> : function_traits<_ReturnType(_Args...)>
{
typedef volatile _ClassType& owner_type;
};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile>
: public function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile> : function_traits<_ReturnType(_Args...)>
{
typedef const volatile _ClassType& owner_type;
};
template <typename FunctionType>
struct function_traits<std::function<FunctionType>>
: public function_traits<FunctionType>
struct function_traits<std::function<FunctionType>> : function_traits<FunctionType>
{};
template <class _Function>
@ -621,45 +513,33 @@ namespace sdbus {
template <typename _Function>
using tuple_of_function_output_arg_types_t = typename tuple_of_function_output_arg_types<_Function>::type;
template <typename _Type>
struct aggregate_signature
template <typename _Function>
struct signature_of_function_input_arguments : signature_of<tuple_of_function_input_arg_types_t<_Function>>
{
static const std::string str()
static std::string value_as_string()
{
return signature_of<std::decay_t<_Type>>::str();
}
};
template <typename... _Types>
struct aggregate_signature<std::tuple<_Types...>>
{
static const std::string str()
{
std::string signature;
(void)(signature += ... += signature_of<std::decay_t<_Types>>::str());
return signature;
constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_input_arg_types_t<_Function>>);
return signature.data();
}
};
template <typename _Function>
struct signature_of_function_input_arguments
inline auto signature_of_function_input_arguments_v = signature_of_function_input_arguments<_Function>::value_as_string();
template <typename _Function>
struct signature_of_function_output_arguments : signature_of<tuple_of_function_output_arg_types_t<_Function>>
{
static const std::string str()
static std::string value_as_string()
{
return aggregate_signature<tuple_of_function_input_arg_types_t<_Function>>::str();
constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_output_arg_types_t<_Function>>);
return signature.data();
}
};
template <typename _Function>
struct signature_of_function_output_arguments
{
static const std::string str()
{
return aggregate_signature<tuple_of_function_output_arg_types_t<_Function>>::str();
}
};
inline auto signature_of_function_output_arguments_v = signature_of_function_output_arguments<_Function>::value_as_string();
// std::future stuff for return values of async calls
template <typename... _Args> struct future_return
{
typedef std::tuple<_Args...> type;
@ -678,7 +558,6 @@ namespace sdbus {
template <typename... _Args>
using future_return_t = typename future_return<_Args...>::type;
// Credit: Piotr Skotnicki (https://stackoverflow.com/a/57639506)
template <typename, typename>
constexpr bool is_one_of_variants_types = false;
@ -687,7 +566,6 @@ namespace sdbus {
constexpr bool is_one_of_variants_types<std::variant<_VariantTypes...>, _QueriedType>
= (std::is_same_v<_QueriedType, _VariantTypes> || ...);
namespace detail
{
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
@ -753,6 +631,26 @@ namespace sdbus {
, std::forward<_Tuple>(t)
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
}
// Convenient concatenation of arrays
template <typename _T, std::size_t _N1, std::size_t _N2>
constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs)
{
std::array<_T, _N1 + _N2> result{};
std::size_t index = 0;
for (auto& el : lhs) {
result[index] = std::move(el);
++index;
}
for (auto& el : rhs) {
result[index] = std::move(el);
++index;
}
return result;
}
}
#endif /* SDBUS_CXX_TYPETRAITS_H_ */

View File

@ -57,10 +57,9 @@ namespace sdbus {
Variant();
template <typename _ValueType>
explicit Variant(const _ValueType& value)
: Variant()
explicit Variant(const _ValueType& value) : Variant()
{
msg_.openVariant(signature_of<_ValueType>::str());
msg_.openVariant<_ValueType>();
msg_ << value;
msg_.closeVariant();
msg_.seal();
@ -79,7 +78,8 @@ namespace sdbus {
{
_ValueType val;
msg_.rewind(false);
msg_.enterVariant(signature_of<_ValueType>::str());
msg_.enterVariant<_ValueType>();
msg_ >> val;
msg_.exitVariant();
return val;
@ -104,7 +104,8 @@ namespace sdbus {
template <typename _Type>
bool containsValueOfType() const
{
return signature_of<_Type>::str() == peekValueType();
constexpr auto signature = as_null_terminated(signature_of_v<_Type>);
return signature.data() == peekValueType();
}
bool isEmpty() const;
@ -134,15 +135,12 @@ namespace sdbus {
public:
using std::tuple<_ValueTypes...>::tuple;
// Disable constructor if an older then 7.1.0 version of GCC is used
#if !((defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !(__GNUC__ > 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 0)))))
Struct() = default;
explicit Struct(const std::tuple<_ValueTypes...>& t)
: std::tuple<_ValueTypes...>(t)
{
}
#endif
template <std::size_t _I>
auto& get()
@ -374,6 +372,16 @@ namespace sdbus {
int fd_ = -1;
};
/********************************************//**
* @typedef DictEntry
*
* DictEntry is implemented as std::pair, a standard
* value_type in STL(-like) associative containers.
*
***********************************************/
template<typename _T1, typename _T2>
using DictEntry = std::pair<_T1, _T2>;
}
template <size_t _I, typename... _ValueTypes>

View File

@ -42,8 +42,8 @@ namespace sdbus {
template <typename _Function>
MethodVTableItem& MethodVTableItem::implementedAs(_Function&& callback)
{
inputSignature = signature_of_function_input_arguments<_Function>::str();
outputSignature = signature_of_function_output_arguments<_Function>::str();
inputSignature = signature_of_function_input_arguments_v<_Function>;
outputSignature = signature_of_function_output_arguments_v<_Function>;
callbackHandler = [callback = std::forward<_Function>(callback)](MethodCall call)
{
// Create a tuple of callback input arguments' types, which will be used
@ -142,7 +142,7 @@ namespace sdbus {
template <typename... _Args>
inline SignalVTableItem& SignalVTableItem::withParameters()
{
signature = signature_of_function_input_arguments<void(_Args...)>::str();
signature = signature_of_function_input_arguments_v<void(_Args...)>;
return *this;
}
@ -192,7 +192,7 @@ namespace sdbus {
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
if (signature.empty())
signature = signature_of_function_output_arguments<_Function>::str();
signature = signature_of_function_output_arguments_v<_Function>;
getter = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply)
{
@ -210,7 +210,7 @@ namespace sdbus {
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
if (signature.empty())
signature = signature_of_function_input_arguments<_Function>::str();
signature = signature_of_function_input_arguments_v<_Function>;
setter = [callback = std::forward<_Function>(callback)](PropertySetCall call)
{

View File

@ -429,10 +429,9 @@ Message& Message::operator>>(UnixFd &item)
return *this;
}
Message& Message::openContainer(const std::string& signature)
Message& Message::openContainer(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a container", -r);
return *this;
@ -446,9 +445,9 @@ Message& Message::closeContainer()
return *this;
}
Message& Message::openDictEntry(const std::string& signature)
Message& Message::openDictEntry(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a dictionary entry", -r);
return *this;
@ -462,9 +461,9 @@ Message& Message::closeDictEntry()
return *this;
}
Message& Message::openVariant(const std::string& signature)
Message& Message::openVariant(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a variant", -r);
return *this;
@ -478,9 +477,9 @@ Message& Message::closeVariant()
return *this;
}
Message& Message::openStruct(const std::string& signature)
Message& Message::openStruct(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a struct", -r);
return *this;
@ -494,10 +493,9 @@ Message& Message::closeStruct()
return *this;
}
Message& Message::enterContainer(const std::string& signature)
Message& Message::enterContainer(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature);
if (r == 0)
ok_ = false;
@ -514,9 +512,9 @@ Message& Message::exitContainer()
return *this;
}
Message& Message::enterDictEntry(const std::string& signature)
Message& Message::enterDictEntry(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature);
if (r == 0)
ok_ = false;
@ -533,9 +531,9 @@ Message& Message::exitDictEntry()
return *this;
}
Message& Message::enterVariant(const std::string& signature)
Message& Message::enterVariant(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature);
if (r == 0)
ok_ = false;
@ -552,9 +550,9 @@ Message& Message::exitVariant()
return *this;
}
Message& Message::enterStruct(const std::string& signature)
Message& Message::enterStruct(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature);
if (r == 0)
ok_ = false;

View File

@ -51,7 +51,7 @@ namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
{
msg.openContainer(sdbus::signature_of<_ElementType>::str());
msg.openContainer<_ElementType>();
for (const auto& item : items)
msg << item;
@ -64,7 +64,7 @@ namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
{
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
if(!msg.enterContainer<_ElementType>())
return msg;
while (true)

View File

@ -170,7 +170,8 @@ namespace
TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
{
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
constexpr auto signature = sdbus::as_null_terminated(sdbus::signature_of_v<TypeParam>);
ASSERT_THAT(signature.data(), Eq(this->dbusTypeSignature_));
}
TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)