diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f79e3df..f364cb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 }}" diff --git a/docs/using-sdbus-c++.md b/docs/using-sdbus-c++.md index cf92c5c..1cde2cf 100644 --- a/docs/using-sdbus-c++.md +++ b/docs/using-sdbus-c++.md @@ -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 @@ -1580,7 +1580,7 @@ namespace sdbus { template 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 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 struct sdbus::signature_of> - : sdbus::signature_of> + : sdbus::signature_of> {}; ``` 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`, 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. diff --git a/include/sdbus-c++/Message.h b/include/sdbus-c++/Message.h index 811720e..9dd793e 100644 --- a/include/sdbus-c++/Message.h +++ b/include/sdbus-c++/Message.h @@ -161,22 +161,38 @@ namespace sdbus { template Message& operator>>(std::tuple<_ValueTypes...>& item); - Message& openContainer(const std::string& signature); + template + Message& openContainer(); + Message& openContainer(const char* signature); Message& closeContainer(); - Message& openDictEntry(const std::string& signature); + template + Message& openDictEntry(); + Message& openDictEntry(const char* signature); Message& closeDictEntry(); - Message& openVariant(const std::string& signature); + template + Message& openVariant(); + Message& openVariant(const char* signature); Message& closeVariant(); - Message& openStruct(const std::string& signature); + template + Message& openStruct(); + Message& openStruct(const char* signature); Message& closeStruct(); - Message& enterContainer(const std::string& signature); + template + Message& enterContainer(); + Message& enterContainer(const char* signature); Message& exitContainer(); - Message& enterDictEntry(const std::string& signature); + template + Message& enterDictEntry(); + Message& enterDictEntry(const char* signature); Message& exitDictEntry(); - Message& enterVariant(const std::string& signature); + template + Message& enterVariant(); + Message& enterVariant(const char* signature); Message& exitVariant(); - Message& enterStruct(const std::string& signature); + template + 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 inline Message& Message::operator<<(const std::variant& value) { - std::visit([this](const auto& inner){ - openVariant(signature_of::str()); + std::visit([this](const auto& inner) + { + openVariant(); *this << inner; closeVariant(); }, value); @@ -370,11 +387,12 @@ namespace sdbus { // otherwise use step-by-step serialization of individual elements. if constexpr (signature_of::is_trivial_dbus_type && !std::is_same_v) { - appendArray(*signature_of::str().c_str(), items.data(), items.size() * sizeof(ElementType)); + constexpr auto signature = as_null_terminated(signature_of_v); + appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType)); } else { - openContainer(signature_of::str()); + openContainer(); 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::str() + signature_of::str(); - const std::string arraySignature = "{" + dictEntrySignature + "}"; - - openContainer(arraySignature); + openContainer>(); for (const auto& item : items) { - openDictEntry(dictEntrySignature); + openDictEntry(); *this << item.first; *this << item.second; closeDictEntry(); @@ -441,12 +456,7 @@ namespace sdbus { template inline Message& Message::operator<<(const Struct<_ValueTypes...>& item) { - 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); - - openStruct(structContentSignature); + openStruct<_ValueTypes...>(); detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{}); closeStruct(); @@ -465,11 +475,13 @@ namespace sdbus { template 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(*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::str().c_str(), (const void**)&arrayPtr, &arraySize); + constexpr auto signature = as_null_terminated(sdbus::signature_of_v); + 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::str())) + if(!enterContainer()) return; for (auto& elem : items) @@ -590,7 +605,7 @@ namespace sdbus { template 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::str() + signature_of::str(); - const std::string arraySignature = "{" + dictEntrySignature + "}"; - - if (!enterContainer(arraySignature)) + if (!enterContainer>()) return; while (true) { - if (!enterDictEntry(dictEntrySignature)) + if (!enterDictEntry()) 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 inline Message& Message::operator>>(Struct<_ValueTypes...>& item) { - auto structSignature = signature_of>::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 + inline Message& Message::openContainer() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>); + return openContainer(signature.data()); + } + + template + inline Message& Message::openDictEntry() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return openDictEntry(signature.data()); + } + + template + inline Message& Message::openVariant() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>); + return openVariant(signature.data()); + } + + template + inline Message& Message::openStruct() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return openStruct(signature.data()); + } + + template + inline Message& Message::enterContainer() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>); + return enterContainer(signature.data()); + } + + template + inline Message& Message::enterDictEntry() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return enterDictEntry(signature.data()); + } + + template + inline Message& Message::enterVariant() + { + constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>); + return enterVariant(signature.data()); + } + + template + inline Message& Message::enterStruct() + { + constexpr auto signature = as_null_terminated(signature_of_v>); + return enterStruct(signature.data()); + } + } #endif /* SDBUS_CXX_MESSAGE_H_ */ diff --git a/include/sdbus-c++/TypeTraits.h b/include/sdbus-c++/TypeTraits.h index cec5f93..a0e6e2c 100644 --- a/include/sdbus-c++/TypeTraits.h +++ b/include/sdbus-c++/TypeTraits.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -54,6 +55,7 @@ namespace sdbus { class ObjectPath; class Signature; class UnixFd; + template using DictEntry = std::pair<_T1, _T2>; class BusName; class InterfaceName; class MemberName; @@ -65,6 +67,7 @@ namespace sdbus { class PropertyGetReply; template class Result; class Error; + template struct signature_of; } namespace sdbus { @@ -106,253 +109,171 @@ namespace sdbus { // Helper for static assert template constexpr bool always_false = false; + // Helper operator+ for concatenation of `std::array`s + template + 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 + template + constexpr auto signature_of_v = signature_of<_T>::value; + + template 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 - struct signature_of - : public signature_of<_T> + struct signature_of : signature_of<_T> {}; template - struct signature_of<_T&> - : public signature_of<_T> + struct signature_of : signature_of<_T> + {}; + + template + struct signature_of<_T&> : signature_of<_T> {}; template <> struct signature_of { + static constexpr std::array 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 - { - 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 - { - 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 - { - 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 - { - 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 { + 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 : signature_of + {}; + + template <> + struct signature_of : signature_of + {}; + + template + struct signature_of : signature_of + {}; + + template + struct signature_of : signature_of + {}; + template <> struct signature_of : signature_of - { - }; + {}; template <> struct signature_of : signature_of - { - }; + {}; template <> struct signature_of : signature_of - { - }; + {}; template struct signature_of> { + 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 { + 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 @@ -362,74 +283,52 @@ namespace sdbus { template <> struct signature_of { + 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 { + 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 { + 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 + struct signature_of> + { + static constexpr std::array value = std::array{'{'} + signature_of_v> + std::array{'}'}; + static constexpr bool is_valid = true; + static constexpr bool is_trivial_dbus_type = false; }; template struct signature_of> { + 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 - struct signature_of> + struct signature_of> : 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(); - } }; #ifdef __cpp_lib_span template - struct signature_of> + struct signature_of> : 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 @@ -438,46 +337,49 @@ namespace sdbus { : public signature_of> {}; - template struct signature_of> { + static constexpr std::array contents = signature_of_v>; + 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 struct signature_of> + : 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> // A simple concatenation of signatures of _Types + { + static constexpr std::array value = (std::array{} + ... + 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 + 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 - struct function_traits - : public function_traits + struct function_traits : function_traits {}; template - struct function_traits - : public function_traits<_Type> + struct function_traits : function_traits<_Type> {}; template - struct function_traits<_Type&> - : public function_traits<_Type> + struct function_traits<_Type&> : function_traits<_Type> {}; template @@ -517,72 +419,62 @@ namespace sdbus { }; template - 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 - struct function_traits, _Args...)> - : public function_traits_base + struct function_traits, _Args...)> : function_traits_base { static constexpr bool has_error_param = true; }; template - struct function_traits, _Args...)> - : public function_traits_base, _Args...> + struct function_traits, _Args...)> : function_traits_base, _Args...> { static constexpr bool is_async = true; using async_result_t = Result<_Results...>; }; template - struct function_traits&&, _Args...)> - : public function_traits_base, _Args...> + struct function_traits&&, _Args...)> : function_traits_base, _Args...> { static constexpr bool is_async = true; using async_result_t = Result<_Results...>; }; template - struct function_traits<_ReturnType(*)(_Args...)> - : public function_traits<_ReturnType(_Args...)> + struct function_traits<_ReturnType(*)(_Args...)> : function_traits<_ReturnType(_Args...)> {}; template - 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 - 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 - 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 - 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 - struct function_traits> - : public function_traits + struct function_traits> : function_traits {}; template @@ -621,45 +513,33 @@ namespace sdbus { template using tuple_of_function_output_arg_types_t = typename tuple_of_function_output_arg_types<_Function>::type; - template - struct aggregate_signature + template + struct signature_of_function_input_arguments : signature_of> { - static const std::string str() + static std::string value_as_string() { - return signature_of>::str(); - } - }; - - template - struct aggregate_signature> - { - static const std::string str() - { - std::string signature; - (void)(signature += ... += signature_of>::str()); - return signature; + constexpr auto signature = as_null_terminated(signature_of_v>); + return signature.data(); } }; template - struct signature_of_function_input_arguments + inline auto signature_of_function_input_arguments_v = signature_of_function_input_arguments<_Function>::value_as_string(); + + template + struct signature_of_function_output_arguments : signature_of> { - static const std::string str() + static std::string value_as_string() { - return aggregate_signature>::str(); + constexpr auto signature = as_null_terminated(signature_of_v>); + return signature.data(); } }; template - struct signature_of_function_output_arguments - { - static const std::string str() - { - return aggregate_signature>::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 struct future_return { typedef std::tuple<_Args...> type; @@ -678,7 +558,6 @@ namespace sdbus { template using future_return_t = typename future_return<_Args...>::type; - // Credit: Piotr Skotnicki (https://stackoverflow.com/a/57639506) template constexpr bool is_one_of_variants_types = false; @@ -687,7 +566,6 @@ namespace sdbus { constexpr bool is_one_of_variants_types, _QueriedType> = (std::is_same_v<_QueriedType, _VariantTypes> || ...); - namespace detail { template @@ -753,6 +631,26 @@ namespace sdbus { , std::forward<_Tuple>(t) , std::make_index_sequence>::value>{} ); } + + // Convenient concatenation of arrays + template + 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_ */ diff --git a/include/sdbus-c++/Types.h b/include/sdbus-c++/Types.h index 19bfd69..776bdc8 100644 --- a/include/sdbus-c++/Types.h +++ b/include/sdbus-c++/Types.h @@ -57,10 +57,9 @@ namespace sdbus { Variant(); template - 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 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 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 + using DictEntry = std::pair<_T1, _T2>; + } template diff --git a/include/sdbus-c++/VTableItems.inl b/include/sdbus-c++/VTableItems.inl index b3f6044..b8e5e09 100644 --- a/include/sdbus-c++/VTableItems.inl +++ b/include/sdbus-c++/VTableItems.inl @@ -42,8 +42,8 @@ namespace sdbus { template 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 inline SignalVTableItem& SignalVTableItem::withParameters() { - signature = signature_of_function_input_arguments::str(); + signature = signature_of_function_input_arguments_v; return *this; } @@ -192,7 +192,7 @@ namespace sdbus { static_assert(!std::is_void>::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>::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) { diff --git a/src/Message.cpp b/src/Message.cpp index ae281e2..52421c9 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -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; diff --git a/tests/unittests/Message_test.cpp b/tests/unittests/Message_test.cpp index 1db7660..964b650 100644 --- a/tests/unittests/Message_test.cpp +++ b/tests/unittests/Message_test.cpp @@ -51,7 +51,7 @@ namespace sdbus { template 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 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) diff --git a/tests/unittests/TypeTraits_test.cpp b/tests/unittests/TypeTraits_test.cpp index 0ccc7bd..f06491a 100644 --- a/tests/unittests/TypeTraits_test.cpp +++ b/tests/unittests/TypeTraits_test.cpp @@ -170,7 +170,8 @@ namespace TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature) { - ASSERT_THAT(sdbus::signature_of::str(), Eq(this->dbusTypeSignature_)); + constexpr auto signature = sdbus::as_null_terminated(sdbus::signature_of_v); + ASSERT_THAT(signature.data(), Eq(this->dbusTypeSignature_)); } TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)