Compare commits

..
16 Commits
Author SHA1 Message Date
sangelovic 3a4f343fb9 Provide better names to event loop-related IConnection methods 2020-02-02 22:22:26 +01:00
sangelovic dee6adce02 Bump revision up to v0.8.1 2020-02-02 02:31:39 +01:00
sangelovic 3e68fee4cd Switch to more modern CMake 2020-02-02 02:29:32 +01:00
sangelovic 8dfd29b0f0 Fix clang-9 warnings 2020-02-01 23:26:27 +01:00
Stanislav Angelovič db71707be4 Update tutorial for for full use of C++17 2020-02-01 23:03:44 +01:00
Stanislav Angelovič c9583f2887 Update README for full usage of C++17 2020-02-01 23:01:09 +01:00
sangelovic 975f1bf07f Switch to fully supported C++17 2020-02-01 22:58:34 +01:00
sangelovic d591b69f92 Bump revision up to 0.8.0 2020-02-01 13:56:13 +01:00
Stanislav Angelovič 49586001d6 Try to better explain the design and how to use connections in objects and proxies 2020-02-01 13:38:51 +01:00
Stanislav Angelovič aa8e9123de Update the tutorial for new method parameter names feature 2020-02-01 12:38:39 +01:00
sangelovic eade6a0e44 Add support for method and signal parameter names in introspection 2020-02-01 12:38:39 +01:00
sangelovic 10977c6137 Bump revision up to 0.7.8 2020-01-25 23:38:54 +01:00
sangelovic 1e455b8ef3 Fix inline variable since in v0.7 version line we still support gcc 6 2020-01-25 22:31:41 +01:00
sangelovic 75709e31f1 Fix sporadic race condition between Variant and underlying bus
The underlying bus was thread_local, but the design assumption that Variants built on top of that instance won't outlive the thread was incorrect. In stress tests, Variants were moved (and this is completely legal) to a different thread.
2020-01-25 22:31:41 +01:00
Stanislav Angelovič 245db893b8 Remove obsolete section on Yocto recipes for sdbus-c++ 2020-01-23 16:58:48 +01:00
Stanislav Angelovič 477c5dd714 Update section on Yocto recipes for sdbus-c++ 2020-01-23 16:57:42 +01:00
45 changed files with 731 additions and 524 deletions
+23 -18
View File
@@ -2,9 +2,9 @@
# PROJECT INFORMATION
#-------------------------------
cmake_minimum_required(VERSION 3.6)
cmake_minimum_required(VERSION 3.12)
project(sdbus-c++ VERSION 0.7.7 LANGUAGES C CXX)
project(sdbus-c++ VERSION 0.8.1 LANGUAGES C CXX)
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
@@ -16,12 +16,14 @@ option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into
if(NOT BUILD_LIBSYSTEMD)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SYSTEMD libsystemd>=236)
if(NOT SYSTEMD_FOUND)
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236)
if(NOT TARGET PkgConfig::Systemd)
message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found "
"(you may turn BUILD_LIBSYSTEMD on for sdbus-c++ to try downloading "
"and building libsystemd in as part of sdbus-c++ during configuration)")
endif()
add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd)
set(LIBSYSTEMD_VERSION ${Systemd_VERSION})
else()
# Build static libsystemd library as an external project
include(cmake/LibsystemdExternalProject.cmake)
@@ -80,11 +82,7 @@ set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HD
# GENERAL COMPILER CONFIGURATION
#-------------------------------
if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
else()
set(CMAKE_CXX_STANDARD 17) # Supported in CMake>=3.8
endif()
set(CMAKE_CXX_STANDARD 17)
#----------------------------------
# LIBRARY BUILD INFORMATION
@@ -93,22 +91,25 @@ endif()
set(SDBUSCPP_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(SDBUSCPP_VERSION "${PROJECT_VERSION}")
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
# We promote BUILD_SHARED_LIBS flags to (global) option only if we are the main project
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
endif()
# Having an object target allows unit tests to reuse already built sources without re-building
add_library(sdbus-c++-objlib OBJECT ${SDBUSCPP_SRCS})
target_compile_definitions(sdbus-c++-objlib PRIVATE BUILD_LIB=1 LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_compile_definitions(sdbus-c++-objlib PRIVATE BUILD_LIB=1 LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${SYSTEMD_INCLUDE_DIRS}>)
if(BUILD_SHARED_LIBS)
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
if(DEFINED BUILD_SHARED_LIBS)
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS})
endif()
if(BUILD_LIBSYSTEMD)
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
endif()
target_link_libraries(sdbus-c++-objlib PUBLIC Systemd::Libsystemd)
add_library(sdbus-c++ $<TARGET_OBJECTS:sdbus-c++-objlib>)
add_library(sdbus-c++)
target_include_directories(sdbus-c++ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
set_target_properties(sdbus-c++
@@ -116,13 +117,17 @@ set_target_properties(sdbus-c++
VERSION "${SDBUSCPP_VERSION}"
SOVERSION "${SDBUSCPP_VERSION_MAJOR}"
OUTPUT_NAME "sdbus-c++")
target_link_libraries(sdbus-c++ PRIVATE ${SYSTEMD_LIBRARIES})
target_link_libraries(sdbus-c++ PRIVATE sdbus-c++-objlib)
#----------------------------------
# INSTALLATION
#----------------------------------
set(EXPORT_SET sdbus-c++)
if(NOT BUILD_SHARED_LIBS)
list(APPEND EXPORT_SET "sdbus-c++-objlib")
endif()
install(TARGETS sdbus-c++
install(TARGETS ${EXPORT_SET}
EXPORT sdbus-c++-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries
+12
View File
@@ -135,3 +135,15 @@ v0.7.6
v0.7.7
- Fix race condition of polling on D-Bus fd from two threads (event loop thread and sync D-Bus call thread)
- Little ordering fix in stress tests
v0.7.8
- Switch from thread_local to global bus instance that is used to create Variant instances (thread_local caused issues with Variant in very special inter-thread situations)
v0.8.0
- [[Breaking ABI change]] Implement support for input & output parameter names for D-Bus methods and signals, which are used in introspection
- Explain better in tutorial the design and how to use connections in relation to objects and proxies
v0.8.1
- Switch to full C++17 support
- Switch to more modern CMake (>=3.12)
- Provide better names to event loop-related IConnection methods, keep old ones marked as deprecated for backwards compatibility
+1 -1
View File
@@ -65,7 +65,7 @@ $ sudo make install
Dependencies
------------
* `C++17` - the library uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature (gcc >= 6, clang >= 3.7)
* `C++17` - the library uses C++17 features.
* `libsystemd` - systemd library containing sd-bus implementation. This library is part of systemd. Systemd at least v236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
+7 -8
View File
@@ -38,20 +38,19 @@ ExternalProject_Add(LibsystemdBuildProject
GIT_SHALLOW 1
UPDATE_COMMAND ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
COMMAND ${MESON} --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic <SOURCE_DIR> <BINARY_DIR>
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic <SOURCE_DIR> <BINARY_DIR>
BUILD_COMMAND ${BUILD_VERSION_H}
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
BUILD_ALWAYS 1
INSTALL_COMMAND ""
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/src/libsystemd <INSTALL_DIR>/include
LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1)
ExternalProject_Get_property(LibsystemdBuildProject SOURCE_DIR)
set(SYSTEMD_INCLUDE_DIRS ${SOURCE_DIR}/src)
ExternalProject_Get_property(LibsystemdBuildProject BINARY_DIR)
set(SYSTEMD_LIBRARY_DIRS ${BINARY_DIR})
set(SYSTEMD_VERSION ${LIBSYSTEMD_VERSION})
ExternalProject_Get_property(LibsystemdBuildProject INSTALL_DIR)
add_library(Systemd::Libsystemd STATIC IMPORTED)
set_target_properties(Systemd::Libsystemd PROPERTIES IMPORTED_LOCATION ${SYSTEMD_LIBRARY_DIRS}/libsystemd.a)
set(SYSTEMD_LIBRARIES Systemd::Libsystemd ${CAP_LIBRARIES} ${GLIBC_RT_LIBRARY} ${MOUNT_LIBRARIES})
set_target_properties(Systemd::Libsystemd PROPERTIES IMPORTED_LOCATION ${BINARY_DIR}/libsystemd.a)
file(MAKE_DIRECTORY ${INSTALL_DIR}/include) # Trick for CMake to stop complaining about non-existent ${INSTALL_DIR}/include directory
target_include_directories(Systemd::Libsystemd INTERFACE ${INSTALL_DIR}/include)
target_link_libraries(Systemd::Libsystemd INTERFACE ${CAP_LIBRARIES} ${GLIBC_RT_LIBRARY} ${MOUNT_LIBRARIES})
-7
View File
@@ -2,10 +2,3 @@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
check_required_components("@PROJECT_NAME@")
# This is here for backwards-compatibility. Please use more modern target-based approach.
set(SDBUSCPP_VERSION "@SDBUSCPP_VERSION@")
set(SDBUSCPP_FOUND "TRUE")
set_and_check(SDBUSCPP_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
set_and_check(SDBUSCPP_LIBRARY_DIR "@CMAKE_INSTALL_FULL_LIBDIR@")
set(SDBUSCPP_LIBRARIES sdbus-c++)
+49 -36
View File
@@ -51,7 +51,7 @@ PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.6],,
)
```
Note: sdbus-c++ library depends on C++17, since it uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature. To use the library, make sure you have a C++ standard library that supports the feature. The feature is supported by e.g. gcc >= 6, and clang >= 3.7.
Note: sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6).
Solving libsystemd dependency
-----------------------------
@@ -89,25 +89,17 @@ You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tun
Distributing sdbus-c++
----------------------
sdbus-c++ recipes for Yocto are available. Contributors willing to help with bringing sdbus-c++ to popular package systems are welcome.
### Yocto
There are sdbus-c++ recipes for already released Yocto versions (for versions Sumo and newer) available in the meta-oe layer of meta-openembedded fork at Kistler-Group:
There are Yocto recipes for sdbus-c++ available in the [`meta-oe`](https://github.com/openembedded/meta-openembedded/tree/master/meta-oe/recipes-core/sdbus-c%2B%2B) layer of the `meta-openembedded` project. There are two recipes:
* [sdbus-c++ recipes for Yocto Sumo](https://github.com/Kistler-Group/meta-openembedded/tree/sumo-with-sdbus-c%2B%2B/meta-oe/recipes-core/sdbus-c%2B%2B)
* [sdbus-c++ recipes for Yocto Thud](https://github.com/Kistler-Group/meta-openembedded/tree/thud-with-sdbus-c%2B%2B/meta-oe/recipes-core/sdbus-c%2B%2B)
* [sdbus-c++ recipes for Yocto Warrior](https://github.com/Kistler-Group/meta-openembedded/tree/warrior-with-sdbus-c%2B%2B/meta-oe/recipes-core/sdbus-c%2B%2B)
Also, there is currently a pull request pending that pushes there recipes upstream -- to the OpenEmbedded project, so they will be officially available for the upcoming Yocto release.
There are two recipes:
* One for sdbus-c++ itself. It detects whether systemd feature is turned on in the poky linux configuration. If so, it simply depends on systemd and makes use of libsystemd shared library available in the target system. Otherwise it automatically downloads and builds libsystemd static library and makes it an opaque part of sdbus-c++ shared library. The recipe also supports ptest.
* One for sdbus-c++ itself. It detects whether systemd feature is turned on in the poky linux configuration. If so, it simply depends on systemd and makes use of libsystemd shared library available in the target system. Otherwise it automatically downloads and builds libsystemd static library and links it into the sdbus-c++ shared library. The recipe also supports ptest.
* One for sdbus-c++ native tools, namely sdbus-c++ code generator to generate C++ adaptor and proxy binding classes.
Tip: If you get `ERROR: Program or command 'getent' not found or not executable` when building sdbus-c++ in Yocto, please make sure you've added `getent` to `HOSTTOOLS`. For example, you can add `HOSTTOOLS_NONFATAL += "getent"` into your local.conf file.
Contributors willing to help with bringing sdbus-c++ to other popular package systems are welcome.
Header files and namespaces
---------------------------
@@ -143,7 +135,7 @@ The following diagram illustrates the major entities in sdbus-c++.
![class](sdbus-c++-class-diagram.png)
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign unique service names to those connections. A processing loop should be run on the connection.
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign unique service names to those connections. An I/O event loop should be run on the bus connection.
`IObject` represents the concept of an object that exposes its methods, signals and properties. Its responsibilities are:
@@ -270,8 +262,8 @@ int main(int argc, char *argv[])
concatenator->registerSignal(interfaceName, "concatenated", "s");
concatenator->finishRegistration();
// Run the loop on the connection.
connection->enterProcessingLoop();
// Run the I/O event loop on the bus connection.
connection->enterEventLoop();
}
```
@@ -361,27 +353,41 @@ The design of D-Bus connections in sdbus-c++ allows for certain flexibility and
How shall we use connections in relation to D-Bus objects and object proxies?
A D-Bus connection is represented by a `IConnection` instance. Each connection needs an event loop being run upon it. So it needs a thread handling the event loop. This thread serves all incoming and outgoing messages and all communication towards D-Bus daemon.
A D-Bus connection is represented by a `IConnection` instance. Each connection needs an event loop being run upon it. So it needs a thread handling the event loop. This thread serves all incoming and outgoing messages and all communication towards D-Bus daemon. One process can have one but also multiple D-Bus connections (we just have to make certain that the connections with assigned bus names don't share a common name; the name must be unique).
One process can have multiple D-Bus connections, with assigned unique bus names or without, as long as those with assigned bus names do not share a common bus name.
A typical use case for most services is **one** D-Bus connection in the application. The application runs event loop on that connection. When creating objects or proxies, the application provides reference of that connection to those objects and proxies. This means all these objects and proxies share the same connection. This is nicely scalable, because with whatever number of objects or proxies, there is only one connection and one event loop thread. Yet, services that provide objects at various bus names have to create and maintain multiple D-Bus connections, each with the unique bus name.
A D-Bus connection can be created for and used exclusively by one D-Bus object (represented by one `IObject` instance) or one D-Bus proxy (represented by one `IProxy` instance), but can very well be used by and shared across multiple objects, multiple proxies or even both multiple objects and proxies at the same time. When shared, one must bear in mind that the access to the connection is mutually exclusive and is serialized. This means, for example, that if an object's callback is going to be invoked for an incoming remote method call and in another thread we use a proxy to call remote method in another process, the threads are contending and only one can go on while the other must wait and can only proceed after the first one has finished, because both are using a shared resource -- the connection.
The connection is thread-safe and objects and proxies can invoke operations on it from multiple threads simultaneously, but the operations are serialized. This means, for example, that if an object's callback for an incoming remote method call is going to be invoked in an event loop thread, and in another thread we use a proxy to call remote method in another process, the threads are contending and only one can go on while the other must wait and can only proceed after the first one has finished, because both are using a shared resource -- the connection.
The former case (1:1) is one extreme; it's usually simple, has zero resource contention, but hurts scalability (for example, 50 proxies in our program need 50 D-Bus connections and 50 event loop threads upon them). The latter case (1:N) is the other extreme -- all D-Bus objects and proxies share one single connection. This is the most scalable solution (since, for example, 5 or 200 objects/proxies use always one single connection), but may increase contention and hurt concurrency (since only one of all those objects/proxies can work with the connection at a time). And then there are limitless options between the two (for example, we can use one connection for all objects, and another connection for all proxies in our service...). sdbus-c++ gives its users freedom to choose whatever approach is more suitable to them in their application at fine granularity.
We should bear that in mind when designing more complex, multi-threaded services with high parallelism. If we have undesired contention on a connection, creating a specific, dedicated connection for a hot spot helps to increase concurrency. sdbus-c++ provides us freedom to create as many connections as we want and assign objects and proxies to those connections at our will. We, as application developers, choose whatever approach is more suitable to us at quite a fine granularity.
How can we use connections from the server and the client perspective?
So, more technically, how can we use connections from the server and the client perspective?
* On the *server* side, we generally need to create D-Bus objects and publish their APIs. For that we first need a connection with a unique bus name. We need to create the D-Bus connection manually ourselves, request bus name on it, and manually launch its event loop (in a blocking way, through `enterProcessingLoop()`, or non-blocking async way, through `enterProcessingLoopAsync()`). At any time before or after running the event loop on the connection, we can create and "hook", as well as remove, objects and proxies upon that connection.
#### Using D-Bus connections on the server side
* On the *client* side, for our D-Bus object proxies, we have more options (corresponding to three overloads of the `createProxy()` factory):
On the **server** side, we generally need to create D-Bus objects and publish their APIs. For that we first need a connection with a unique bus name. We need to create the D-Bus connection manually ourselves, request bus name on it, and manually launch its event loop:
* We don't bother about any connection when creating a proxy. For each proxy instance sdbus-c++ also creates an internal connection instance to be used just by this proxy, and it will be a *system bus* connection. Additionally, an event loop thread for that connection is created and run internally.
* either in a blocking way, through `enterEventLoop()`,
* or in a non-blocking async way, through `enterEventLoopAsync()`,
* or, when we have our own implementation of an event loop (e.g. we are using sd-event event loop), we can ask the connection for its underlying fd, I/O events and timeouts through `getEventLoopPollData()` and use that data in our event loop mechanism.
This hurts scalability (see discussion above), but our code is simpler, and since each proxy has its own connection, there is zero contention.
Of course, at any time before or after running the event loop on the connection, we can create and "hook", as well as remove, objects and proxies upon that connection.
* We create a connection explicitly by ourselves and `std::move` it to the proxy object factory. The proxy becomes an owner of this connection, and will run the event loop on that connection. This is the same as in the above bullet point, but with a flexibility that we can choose the bus type (system, session bus).
#### Using D-Bus connections on the client side
* We are always full owners of the connection. We create the connection, and a proxy only takes and keeps a reference to it. We take care of the event loop upon that connection (and we must ensure the connection exists as long as all its users exist).
On the **client** side we have more options when creating D-Bus proxies. That corresponds to three overloads of the `createProxy()` factory:
* In case we (the application) already maintain a D-Bus connection, e.g. because we a D-Bus service anyway, the simple and typical approach is to create proxy upon that connection. The proxy will share the connection with others. So we pass connection reference to proxy factory. With this approach we must of course ensure that the connection exists as long as the proxy exists.
* Or -- and this is typical when we have a simple D-Bus client application -- we have another option: we let proxy maintain its own connection (and an associated thread):
* We either create the connection ourselves and `std::move` it to the proxy object factory. The proxy becomes an owner of this connection, and will run the event loop on that connection. This had the advantage that we may choose the type of connection (system, session, remote).
* Or we don't bother about any connection at all when creating a proxy (the factory overload with no connection parameter). Under the hood, the proxy creates its own *system bus* connection, creates a separate thread and runs an event loop in it. This is **the simplest approach** for non-complex D-Bus clients. For more complex ones, with big number of proxies, this hurts scalability but may improve concurrency (see discussion higher above), so we should make a conscious choice.
#### Stopping I/O event loops graciously
A connection with an asynchronous event loop (i.e. one initiated through `enterEventLoopAsync()`) will stop and join its event loop thread automatically in its destructor. An event loop that blocks in the synchronous `enterEventLoop()` call can be unblocked through `leaveEventLoop()` call on the respective bus connection issued from a different thread or from an OS signal handler.
Implementing the Concatenator example using convenience sdbus-c++ API layer
---------------------------------------------------------------------------
@@ -393,11 +399,11 @@ The convenience API layer abstracts the concept of underlying D-Bus messages awa
Thus, in the end of the day, the code written using the convenience API is:
- more expressive,
- closer to the abstraction level of the problem being solved,
- shorter,
- at a higher level of abstraction (closer to the abstraction level of the problem being solved),
- significantly shorter,
- almost as fast as one written using the basic API layer.
The code written using this layer expresses *what* it does, rather then *how*. Let's look at code samples.
The code written using this layer expresses in a declarative way *what* it does, rather then *how*. Let's look at code samples.
### Server side
@@ -449,7 +455,7 @@ int main(int argc, char *argv[])
concatenator->finishRegistration();
// Run the loop on the connection.
connection->enterProcessingLoop();
connection->enterEventLoop();
}
```
@@ -513,10 +519,17 @@ When registering methods, calling methods or emitting signals, multiple lines of
sdbus-c++ users shall prefer the convenience API to the lower level, basic API. When feasible, using generated adaptor and proxy stubs is even better. These stubs provide yet another, higher API level built on top of the convenience API. They are described in the following section.
Tip: When registering a D-Bus object, we can additionally provide names of input and output parameters of its methods and names of parameters of its signals. When the object is introspected, these names are listed in the resulting introspection XML, which improves the description of object's interfaces:
```c++
concatenator->registerMethod("concatenate").onInterface(interfaceName).withInputParamNames("numbers", "separator").withOutputParamNames("concatenatedString").implementedAs(&concatenate);
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>("concatenatedString");
```
Implementing the Concatenator example using sdbus-c++-generated stubs
---------------------------------------------------------------------
sdbus-c++ ships with the native stub generator tool called `sdbus-c++-xml2cpp`. The tool is very similar to `dbusxx-xml2cpp` tool that comes with the dbus-c++ project.
sdbus-c++ ships with the native stub generator tool called `sdbus-c++-xml2cpp`. The tool is very similar to `dbusxx-xml2cpp` tool that comes with the dbus-c++ library.
The generator tool takes D-Bus XML IDL description of D-Bus interfaces on its input, and can be instructed to generate one or both of these: an adaptor header file for use on the server side, and a proxy header file for use on the client side. Like this:
@@ -577,8 +590,8 @@ protected:
Concatenator_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("concatenate").onInterface(INTERFACE_NAME).implementedAs([this](const std::vector<int32_t>& numbers, const std::string& separator){ return this->concatenate(numbers, separator); });
object_.registerSignal("concatenated").onInterface(INTERFACE_NAME).withParameters<std::string>();
object_.registerMethod("concatenate").onInterface(INTERFACE_NAME).withInputParamNames("numbers", "separator").withOutputParamNames("concatenatedString").implementedAs([this](const std::vector<int32_t>& numbers, const std::string& separator){ return this->concatenate(numbers, separator); });
object_.registerSignal("concatenated").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
}
~Concatenator_adaptor() = default;
@@ -724,7 +737,7 @@ int main(int argc, char *argv[])
Concatenator concatenator(*connection, objectPath);
// Run the loop on the connection.
connection->enterProcessingLoop();
connection->enterEventLoop();
}
```
@@ -1058,7 +1071,7 @@ Using D-Bus properties
Defining and working with D-Bus properties using XML description is quite easy.
### Defining a property in the XML
### Defining a property in the IDL
A property element has no arg child element. It just has the attributes name, type and access, which are all mandatory. The access attribute allows the values readwrite, read, and write.
+11 -4
View File
@@ -31,6 +31,7 @@
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Flags.h>
#include <string>
#include <vector>
#include <type_traits>
#include <chrono>
#include <cstdint>
@@ -53,10 +54,11 @@ namespace sdbus {
~MethodRegistrator() noexcept(false);
MethodRegistrator& onInterface(std::string interfaceName);
template <typename _Function>
std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&> implementedAs(_Function&& callback);
template <typename _Function>
std::enable_if_t<is_async_method_v<_Function>, MethodRegistrator&> implementedAs(_Function&& callback);
template <typename _Function> MethodRegistrator& implementedAs(_Function&& callback);
MethodRegistrator& withInputParamNames(std::vector<std::string> paramNames);
template <typename... _String> MethodRegistrator& withInputParamNames(_String... paramNames);
MethodRegistrator& withOutputParamNames(std::vector<std::string> paramNames);
template <typename... _String> MethodRegistrator& withOutputParamNames(_String... paramNames);
MethodRegistrator& markAsDeprecated();
MethodRegistrator& markAsPrivileged();
MethodRegistrator& withNoReply();
@@ -66,7 +68,9 @@ namespace sdbus {
const std::string& methodName_;
std::string interfaceName_;
std::string inputSignature_;
std::vector<std::string> inputParamNames_;
std::string outputSignature_;
std::vector<std::string> outputParamNames_;
method_callback methodCallback_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
@@ -81,6 +85,8 @@ namespace sdbus {
SignalRegistrator& onInterface(std::string interfaceName);
template <typename... _Args> SignalRegistrator& withParameters();
template <typename... _Args> SignalRegistrator& withParameters(std::vector<std::string> paramNames);
template <typename... _Args, typename... _String> SignalRegistrator& withParameters(_String... paramNames);
SignalRegistrator& markAsDeprecated();
private:
@@ -88,6 +94,7 @@ namespace sdbus {
const std::string& signalName_;
std::string interfaceName_;
std::string signalSignature_;
std::vector<std::string> paramNames_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
};
+79 -36
View File
@@ -70,7 +70,14 @@ namespace sdbus {
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
object_.registerMethod( interfaceName_
, std::move(methodName_)
, std::move(inputSignature_)
, std::move(inputParamNames_)
, std::move(outputSignature_)
, std::move(outputParamNames_)
, std::move(methodCallback_)
, std::move(flags_));
}
inline MethodRegistrator& MethodRegistrator::onInterface(std::string interfaceName)
@@ -81,36 +88,7 @@ namespace sdbus {
}
template <typename _Function>
inline std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&> MethodRegistrator::implementedAs(_Function&& callback)
{
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
methodCallback_ = [callback = std::forward<_Function>(callback)](MethodCall call)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the message.
tuple_of_function_input_arg_types_t<_Function> inputArgs;
// Deserialize input arguments from the message into the tuple
call >> inputArgs;
// Invoke callback with input arguments from the tuple.
// For callbacks returning a non-void value, `apply' also returns that value.
// For callbacks returning void, `apply' returns an empty tuple.
auto ret = sdbus::apply(callback, inputArgs); // We don't yet have C++17's std::apply :-(
// The return value is stored to the reply message.
// In case of void functions, ret is an empty tuple and thus nothing is stored.
auto reply = call.createReply();
reply << ret;
reply.send();
};
return *this;
}
template <typename _Function>
inline std::enable_if_t<is_async_method_v<_Function>, MethodRegistrator&> MethodRegistrator::implementedAs(_Function&& callback)
MethodRegistrator& MethodRegistrator::implementedAs(_Function&& callback)
{
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
@@ -123,13 +101,57 @@ namespace sdbus {
// Deserialize input arguments from the message into the tuple.
call >> inputArgs;
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, typename function_traits<_Function>::async_result_t{std::move(call)}, std::move(inputArgs));
if constexpr (!is_async_method_v<_Function>)
{
// Invoke callback with input arguments from the tuple.
auto ret = sdbus::apply(callback, inputArgs);
// Store output arguments to the reply message and send it back.
auto reply = call.createReply();
reply << ret;
reply.send();
}
else
{
// Invoke callback with input arguments from the tuple and with result object to be set later
using AsyncResult = typename function_traits<_Function>::async_result_t;
sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
}
};
return *this;
}
inline MethodRegistrator& MethodRegistrator::withInputParamNames(std::vector<std::string> paramNames)
{
inputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline MethodRegistrator& MethodRegistrator::withInputParamNames(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withInputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(std::vector<std::string> paramNames)
{
outputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withOutputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
@@ -179,7 +201,11 @@ namespace sdbus {
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerSignal(interfaceName_, signalName_, signalSignature_, flags_);
object_.registerSignal( interfaceName_
, std::move(signalName_)
, std::move(signalSignature_)
, std::move(paramNames_)
, std::move(flags_) );
}
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
@@ -197,6 +223,23 @@ namespace sdbus {
return *this;
}
template <typename... _Args>
inline SignalRegistrator& SignalRegistrator::withParameters(std::vector<std::string> paramNames)
{
paramNames_ = std::move(paramNames);
return withParameters<_Args...>();
}
template <typename... _Args, typename... _String>
inline SignalRegistrator& SignalRegistrator::withParameters(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
return withParameters<_Args...>({paramNames...});
}
inline SignalRegistrator& SignalRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
@@ -548,7 +591,7 @@ namespace sdbus {
reply >> args;
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, error, args); // TODO: Use std::apply when switching to full C++17 support
sdbus::apply(callback, error, args);
};
proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
@@ -588,7 +631,7 @@ namespace sdbus {
signal >> signalArgs;
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, signalArgs); // We don't yet have C++17's std::apply :-(
sdbus::apply(callback, signalArgs);
});
}
+79 -26
View File
@@ -38,7 +38,7 @@ namespace sdbus {
* @class IConnection
*
* An interface to D-Bus bus connection. Incorporates implementation
* of both synchronous and asynchronous processing loop.
* of both synchronous and asynchronous D-Bus I/O event loop.
*
* All methods throw sdbus::Error in case of failure. All methods in
* this class are thread-aware, but not thread-safe.
@@ -82,31 +82,31 @@ namespace sdbus {
virtual std::string getUniqueName() const = 0;
/*!
* @brief Enters the D-Bus processing loop
* @brief Enters I/O event loop on this bus connection
*
* The incoming D-Bus messages are processed in the loop. The method
* blocks indefinitely, until unblocked via leaveProcessingLoop.
* blocks indefinitely, until unblocked through leaveEventLoop().
*
* @throws sdbus::Error in case of failure
*/
virtual void enterProcessingLoop() = 0;
virtual void enterEventLoop() = 0;
/*!
* @brief Enters the D-Bus processing loop in a separate thread
* @brief Enters I/O event loop on this bus connection in a separate thread
*
* The same as enterProcessingLoop, except that it doesn't block
* because it runs the loop in a separate thread managed internally.
* The same as enterEventLoop, except that it doesn't block
* because it runs the loop in a separate, internally managed thread.
*/
virtual void enterProcessingLoopAsync() = 0;
virtual void enterEventLoopAsync() = 0;
/*!
* @brief Leaves the D-Bus processing loop
* @brief Leaves the I/O event loop running on this bus connection
*
* Ends the previously started processing loop.
* This causes the loop to exit and frees the thread serving the loop
*
* @throws sdbus::Error in case of failure
*/
virtual void leaveProcessingLoop() = 0;
virtual void leaveEventLoop() = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
@@ -120,7 +120,7 @@ namespace sdbus {
virtual void addObjectManager(const std::string& objectPath) = 0;
/*!
* @brief Returns parameters you can pass to poll
* @brief Returns fd, I/O events and timeout data you can pass to poll
*
* To integrate sdbus with your app's own custom event handling system
* (without the requirement of an extra thread), you can use this
@@ -130,26 +130,31 @@ namespace sdbus {
* to process the event. This means that all of sdbus's callbacks will
* arrive on your app's main event thread (opposed to on a thread created
* by sdbus-c++). If you are unsure what this all means then use
* enterProcessingLoop() or enterProcessingLoopAsync() instead.
* enterEventLoop() or enterEventLoopAsync() instead.
*
* To integrate sdbus-c++ into a gtk app, pass the file descriptor returned
* by this method to g_main_context_add_poll.
*
* @throws sdbus::Error in case of failure
*/
virtual PollData getProcessLoopPollData() const = 0;
virtual PollData getEventLoopPollData() const = 0;
/*!
* @brief Process a pending request
*
* @returns true if an event was processed, false if poll should be called
*
* Processes a single dbus event. All of sdbus-c++'s callbacks will be called
* from within this method. This method should ONLY be used in conjuction
* with getProcessLoopPollData(). enterProcessingLoop() and
* enterProcessingLoopAsync() will call this method for you, so there is no
* need to call it when using these. If you are unsure what this all means then
* don't use this method.
* with getEventLoopPollData().
* This method returns true if an I/O message was processed. This you can try
* to call this method again before going to poll on I/O events. The method
* returns false if no operations were pending, and the caller should then
* poll for I/O events before calling this method again.
* enterEventLoop() and enterEventLoopAsync() will call this method for you,
* so there is no need to call it when using these. If you are unsure what
* this all means then don't use this method.
*
* @returns true if an event was processed
* @throws sdbus::Error in case of failure
*/
virtual bool processPendingRequest() = 0;
@@ -184,6 +189,34 @@ namespace sdbus {
* @throws sdbus::Error in case of failure
*/
virtual uint64_t getMethodCallTimeout() const = 0;
/*!
* @copydoc IConnection::enterEventLoop()
*
* @deprecated This function has been replaced by enterEventLoop()
*/
[[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop();
/*!
* @copydoc IConnection::enterProcessingLoopAsync()
*
* @deprecated This function has been replaced by enterEventLoopAsync()
*/
[[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync();
/*!
* @copydoc IConnection::leaveProcessingLoop()
*
* @deprecated This function has been replaced by leaveEventLoop()
*/
[[deprecated("This function has been replaced by leaveEventLoop()")]] void leaveProcessingLoop();
/*!
* @copydoc IConnection::getProcessLoopPollData()
*
* @deprecated This function has been replaced by getEventLoopPollData()
*/
[[deprecated("This function has been replaced by getEventLoopPollData()")]] PollData getProcessLoopPollData() const;
};
template <typename _Rep, typename _Period>
@@ -193,6 +226,26 @@ namespace sdbus {
return setMethodCallTimeout(microsecs.count());
}
inline void IConnection::enterProcessingLoop()
{
enterEventLoop();
}
inline void IConnection::enterProcessingLoopAsync()
{
enterEventLoopAsync();
}
inline void IConnection::leaveProcessingLoop()
{
leaveEventLoop();
}
inline IConnection::PollData IConnection::getProcessLoopPollData() const
{
return getEventLoopPollData();
}
/*!
* @brief Creates/opens D-Bus system connection
*
@@ -200,7 +253,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createConnection();
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection();
/*!
* @brief Creates/opens D-Bus system connection with a name
@@ -210,7 +263,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus system connection
@@ -219,7 +272,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
/*!
* @brief Creates/opens D-Bus system connection with a name
@@ -229,7 +282,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus session connection
@@ -238,7 +291,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
/*!
* @brief Creates/opens D-Bus session connection with a name
@@ -248,7 +301,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus system connection on a remote host using ssh
@@ -258,7 +311,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
}
#endif /* SDBUS_CXX_ICONNECTION_H_ */
+66 -16
View File
@@ -74,9 +74,37 @@ namespace sdbus {
* @throws sdbus::Error in case of failure
*/
virtual void registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags = {} ) = 0;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the method will belong to
* @param[in] methodName Name of the method
* @param[in] inputSignature D-Bus signature of method input parameters
* @param[in] inputNames Names of input parameters
* @param[in] outputSignature D-Bus signature of method output parameters
* @param[in] outputNames Names of output parameters
* @param[in] methodCallback Callback that implements the body of the method
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
*
* Provided names of input and output parameters will be included in the introspection
* description (given that at least version 242 of underlying libsystemd library is
* used; otherwise, names of parameters are ignored). This usually helps better describe
* the API to the introspector.
*
* @throws sdbus::Error in case of failure
*/
virtual void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags = {} ) = 0;
@@ -91,8 +119,30 @@ namespace sdbus {
* @throws sdbus::Error in case of failure
*/
virtual void registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
, std::string signalName
, std::string signature
, Flags flags = {} ) = 0;
/*!
* @brief Registers signal that the object will emit on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] paramNames Names of parameters of the signal
* @param[in] flags D-Bus signal flags (deprecated)
*
* Provided names of signal output parameters will be included in the introspection
* description (given that at least version 242 of underlying libsystemd library is
* used; otherwise, names of parameters are ignored). This usually helps better describe
* the API to the introspector.
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags = {} ) = 0;
/*!
@@ -107,8 +157,8 @@ namespace sdbus {
* @throws sdbus::Error in case of failure
*/
virtual void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags = {} ) = 0;
@@ -125,8 +175,8 @@ namespace sdbus {
* @throws sdbus::Error in case of failure
*/
virtual void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags = {} ) = 0;
@@ -308,7 +358,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
MethodRegistrator registerMethod(const std::string& methodName);
[[nodiscard]] MethodRegistrator registerMethod(const std::string& methodName);
/*!
* @brief Registers signal that the object will provide on D-Bus
@@ -327,7 +377,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
SignalRegistrator registerSignal(const std::string& signalName);
[[nodiscard]] SignalRegistrator registerSignal(const std::string& signalName);
/*!
* @brief Registers property that the object will provide on D-Bus
@@ -346,7 +396,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
PropertyRegistrator registerProperty(const std::string& propertyName);
[[nodiscard]] PropertyRegistrator registerProperty(const std::string& propertyName);
/*!
* @brief Sets flags (annotations) for a given interface
@@ -363,7 +413,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
[[nodiscard]] InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
/*!
* @brief Emits signal on D-Bus
@@ -384,7 +434,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
SignalEmitter emitSignal(const std::string& signalName);
[[nodiscard]] SignalEmitter emitSignal(const std::string& signalName);
};
// Out-of-line member definitions
@@ -425,14 +475,14 @@ namespace sdbus {
* issue signals and provide properties.
*
* Creating a D-Bus object instance is (thread-)safe even upon the connection
* which is already running its processing loop.
* which is already running its I/O event loop.
*
* Code example:
* @code
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
[[nodiscard]] std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
}
+15 -15
View File
@@ -111,7 +111,7 @@ namespace sdbus {
*
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the connection
* event loop processing thread.
* I/O event loop thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
@@ -178,7 +178,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
MethodInvoker callMethod(const std::string& methodName);
[[nodiscard]] MethodInvoker callMethod(const std::string& methodName);
/*!
* @brief Calls method on the proxied D-Bus object asynchronously
@@ -202,7 +202,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
AsyncMethodInvoker callMethodAsync(const std::string& methodName);
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName);
/*!
* @brief Registers signal handler for a given signal of the proxied D-Bus object
@@ -222,7 +222,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
SignalSubscriber uponSignal(const std::string& signalName);
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
/*!
* @brief Gets value of a property of the proxied D-Bus object
@@ -241,7 +241,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
PropertyGetter getProperty(const std::string& propertyName);
[[nodiscard]] PropertyGetter getProperty(const std::string& propertyName);
/*!
* @brief Sets value of a property of the proxied D-Bus object
@@ -260,7 +260,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
PropertySetter setProperty(const std::string& propertyName);
[[nodiscard]] PropertySetter setProperty(const std::string& propertyName);
};
// Out-of-line member definitions
@@ -315,7 +315,7 @@ namespace sdbus {
* The provided connection will be used by the proxy to issue calls against the object,
* and signals, if any, will be subscribed to on this connection. The caller still
* remains the owner of the connection (the proxy just keeps a reference to it), and
* should make sure that a processing loop is running on that connection, so the proxy
* should make sure that an I/O event loop is running on that connection, so the proxy
* may receive incoming signals and asynchronous method replies.
*
* Code example:
@@ -323,9 +323,9 @@ namespace sdbus {
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
, std::string destination
, std::string objectPath );
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
@@ -346,9 +346,9 @@ namespace sdbus {
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
, std::string destination
, std::string objectPath );
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
@@ -367,8 +367,8 @@ namespace sdbus {
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath );
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath );
}
+3 -13
View File
@@ -55,11 +55,7 @@ namespace sdbus {
// Assume the caller has already obtained message ownership
struct adopt_message_t { explicit adopt_message_t() = default; };
#ifdef __cpp_inline_variables
inline constexpr adopt_message_t adopt_message{};
#else
constexpr adopt_message_t adopt_message{};
#endif
/********************************************//**
* @class Message
@@ -75,7 +71,7 @@ namespace sdbus {
* of @c IObject and @c IProxy.
*
***********************************************/
class Message
class [[nodiscard]] Message
{
public:
Message& operator<<(bool item);
@@ -279,10 +275,7 @@ namespace sdbus {
template <typename... _Args>
void serialize_pack(Message& msg, _Args&&... args)
{
// Use initializer_list because it guarantees left to right order, and can be empty
using _ = std::initializer_list<int>;
// We are not interested in the list itself, but in the side effects
(void)_{(void(msg << std::forward<_Args>(args)), 0)...};
(void)(msg << ... << args);
}
template <class _Tuple, std::size_t... _Is>
@@ -374,10 +367,7 @@ namespace sdbus {
template <typename... _Args>
void deserialize_pack(Message& msg, _Args&... args)
{
// Use initializer_list because it guarantees left to right order, and can be empty
using _ = std::initializer_list<int>;
// We are not interested in the list itself, but in the side effects
(void)_{(void(msg >> args), 0)...};
(void)(msg >> ... >> args);
}
template <class _Tuple, std::size_t... _Is>
-5
View File
@@ -76,12 +76,7 @@ namespace sdbus {
{
assert(call_.isValid());
auto reply = call_.createReply();
#ifdef __cpp_fold_expressions
(reply << ... << results);
#else
using _ = std::initializer_list<int>;
(void)_{(void(reply << results), 0)...};
#endif
reply.send();
}
+9 -24
View File
@@ -245,11 +245,9 @@ namespace sdbus {
static const std::string str()
{
std::initializer_list<std::string> signatures{signature_of<_ValueTypes>::str()...};
std::string signature;
signature += "(";
for (const auto& item : signatures)
signature += item;
(signature += ... += signature_of<_ValueTypes>::str());
signature += ")";
return signature;
}
@@ -489,11 +487,8 @@ namespace sdbus {
{
static const std::string str()
{
// TODO: This could be a fold expression in C++17...
std::initializer_list<std::string> signatures{signature_of<std::decay_t<_Types>>::str()...};
std::string signature;
for (const auto& item : signatures)
signature += item;
(void)(signature += ... += signature_of<std::decay_t<_Types>>::str());
return signature;
}
};
@@ -536,27 +531,17 @@ namespace sdbus {
return std::forward<_Function>(f)(e, std::get<_I>(std::forward<_Tuple>(t))...);
}
// Version of apply_impl for functions returning non-void values.
// In this case just forward function return value.
// For non-void returning functions, apply_impl simply returns function return value (a tuple of values).
// For void-returning functions, apply_impl returns an empty tuple.
template <class _Function, class _Tuple, std::size_t... _I>
constexpr decltype(auto) apply_impl( _Function&& f
, _Tuple&& t
, std::index_sequence<_I...>
, std::enable_if_t<!std::is_void<function_result_t<_Function>>::value>* = nullptr)
, std::index_sequence<_I...> )
{
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
}
// Version of apply_impl for functions returning void.
// In this case, to have uniform code on the caller side, return empty tuple, our synonym for `void'.
template <class _Function, class _Tuple, std::size_t... _I>
constexpr decltype(auto) apply_impl( _Function&& f
, _Tuple&& t
, std::index_sequence<_I...>
, std::enable_if_t<std::is_void<function_result_t<_Function>>::value>* = nullptr)
{
std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
return std::tuple<>{};
if constexpr (!std::is_void_v<function_result_t<_Function>>)
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
else
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...), std::tuple<>{};
}
}
-4
View File
@@ -178,11 +178,7 @@ namespace sdbus {
};
struct adopt_fd_t { explicit adopt_fd_t() = default; };
#ifdef __cpp_inline_variables
inline constexpr adopt_fd_t adopt_fd{};
#else
constexpr adopt_fd_t adopt_fd{};
#endif
/********************************************//**
* @struct UnixFd
+41 -18
View File
@@ -35,7 +35,7 @@
#include <poll.h>
#include <sys/eventfd.h>
namespace sdbus { namespace internal {
namespace sdbus::internal {
Connection::Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory)
: iface_(std::move(interface))
@@ -61,7 +61,7 @@ Connection::Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t,
Connection::~Connection()
{
leaveProcessingLoop();
Connection::leaveEventLoop();
}
void Connection::requestName(const std::string& name)
@@ -84,7 +84,7 @@ std::string Connection::getUniqueName() const
return unique;
}
void Connection::enterProcessingLoop()
void Connection::enterEventLoop()
{
loopThreadId_ = std::this_thread::get_id();
@@ -98,25 +98,25 @@ void Connection::enterProcessingLoop()
auto success = waitForNextRequest();
if (!success)
break; // Exit processing loop
break; // Exit I/O event loop
}
loopThreadId_ = std::thread::id{};
}
void Connection::enterProcessingLoopAsync()
void Connection::enterEventLoopAsync()
{
if (!asyncLoopThread_.joinable())
asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); });
asyncLoopThread_ = std::thread([this](){ enterEventLoop(); });
}
void Connection::leaveProcessingLoop()
void Connection::leaveEventLoop()
{
notifyProcessingLoopToExit();
joinWithProcessingLoop();
notifyEventLoopToExit();
joinWithEventLoop();
}
sdbus::IConnection::PollData Connection::getProcessLoopPollData() const
Connection::PollData Connection::getEventLoopPollData() const
{
ISdBus::PollData pollData;
auto r = iface_->sd_bus_get_poll_data(bus_.get(), &pollData);
@@ -190,6 +190,17 @@ SlotPtr Connection::addObjectVTable( const std::string& objectPath
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
PlainMessage Connection::createPlainMessage() const
{
sd_bus_message* sdbusMsg{};
auto r = iface_->sd_bus_message_new(bus_.get(), &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a plain message", -r);
return Message::Factory::create<PlainMessage>(sdbusMsg, iface_.get(), adopt_message);
}
MethodCall Connection::createMethodCall( const std::string& destination
, const std::string& objectPath
, const std::string& interfaceName
@@ -213,17 +224,17 @@ Signal Connection::createSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName ) const
{
sd_bus_message *sdbusSignal{};
sd_bus_message *sdbusMsg{};
auto r = iface_->sd_bus_message_new_signal( bus_.get()
, &sdbusSignal
, &sdbusMsg
, objectPath.c_str()
, interfaceName.c_str()
, signalName.c_str() );
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
return Message::Factory::create<Signal>(sdbusSignal, iface_.get(), adopt_message);
return Message::Factory::create<Signal>(sdbusMsg, iface_.get(), adopt_message);
}
void Connection::emitPropertiesChangedSignal( const std::string& objectPath
@@ -347,13 +358,13 @@ void Connection::finishHandshake(sd_bus* bus)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
}
void Connection::notifyProcessingLoopToExit()
void Connection::notifyEventLoopToExit()
{
assert(loopExitFd_.fd >= 0);
uint64_t value = 1;
auto r = write(loopExitFd_.fd, &value, sizeof(value));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify event loop", -errno);
}
void Connection::clearExitNotification()
@@ -363,7 +374,7 @@ void Connection::clearExitNotification()
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
}
void Connection::joinWithProcessingLoop()
void Connection::joinWithEventLoop()
{
if (asyncLoopThread_.joinable())
asyncLoopThread_.join();
@@ -387,7 +398,7 @@ bool Connection::waitForNextRequest()
assert(bus != nullptr);
assert(loopExitFd_.fd != 0);
auto sdbusPollData = getProcessLoopPollData();
auto sdbusPollData = getEventLoopPollData();
struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_.fd, POLLIN, 0}};
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
@@ -443,7 +454,19 @@ Connection::LoopExitEventFd::~LoopExitEventFd()
close(fd);
}
}}
}
namespace sdbus::internal {
std::unique_ptr<sdbus::internal::IConnection> createConnection()
{
auto connection = sdbus::createConnection();
SCOPE_EXIT{ connection.release(); };
auto connectionInternal = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
return std::unique_ptr<sdbus::internal::IConnection>(connectionInternal);
}
}
namespace sdbus {
+10 -10
View File
@@ -40,9 +40,9 @@
#include <atomic>
#include <mutex>
namespace sdbus { namespace internal {
namespace sdbus::internal {
class Connection
class Connection final
: public sdbus::IConnection // External, public interface
, public sdbus::internal::IConnection // Internal, private interface
{
@@ -60,10 +60,10 @@ namespace sdbus { namespace internal {
void requestName(const std::string& name) override;
void releaseName(const std::string& name) override;
std::string getUniqueName() const override;
void enterProcessingLoop() override;
void enterProcessingLoopAsync() override;
void leaveProcessingLoop() override;
sdbus::IConnection::PollData getProcessLoopPollData() const override;
void enterEventLoop() override;
void enterEventLoopAsync() override;
void leaveEventLoop() override;
PollData getEventLoopPollData() const override;
bool processPendingRequest() override;
void addObjectManager(const std::string& objectPath) override;
@@ -80,11 +80,11 @@ namespace sdbus { namespace internal {
, const sd_bus_vtable* vtable
, void* userData ) override;
PlainMessage createPlainMessage() const override;
MethodCall createMethodCall( const std::string& destination
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& methodName ) const override;
Signal createSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName ) const override;
@@ -118,9 +118,9 @@ namespace sdbus { namespace internal {
static std::string composeSignalMatchFilter( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName );
void notifyProcessingLoopToExit();
void notifyEventLoopToExit();
void clearExitNotification();
void joinWithProcessingLoop();
void joinWithEventLoop();
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
struct LoopExitEventFd
@@ -139,6 +139,6 @@ namespace sdbus { namespace internal {
LoopExitEventFd loopExitFd_;
};
}}
}
#endif /* SDBUS_CXX_INTERNAL_CONNECTION_H_ */
+17 -16
View File
@@ -38,13 +38,13 @@ namespace sdbus {
class MethodCall;
class MethodReply;
class Signal;
class PlainMessage;
namespace internal {
class ISdBus;
}
}
namespace sdbus {
namespace internal {
namespace sdbus::internal {
using SlotPtr = std::unique_ptr<void, std::function<void(void*)>>;
@@ -56,16 +56,16 @@ namespace internal {
virtual const ISdBus& getSdBusInterface() const = 0;
virtual ISdBus& getSdBusInterface() = 0;
virtual SlotPtr addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) = 0;
[[nodiscard]] virtual SlotPtr addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) = 0;
virtual PlainMessage createPlainMessage() const = 0;
virtual MethodCall createMethodCall( const std::string& destination
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& methodName ) const = 0;
virtual Signal createSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName ) const = 0;
@@ -80,21 +80,22 @@ namespace internal {
virtual void emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) = 0;
virtual SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/ = nullptr) = 0;
[[nodiscard]] virtual SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/ = nullptr) = 0;
virtual SlotPtr registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData ) = 0;
[[nodiscard]] virtual SlotPtr registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData ) = 0;
virtual void enterProcessingLoopAsync() = 0;
virtual void leaveProcessingLoop() = 0;
virtual void enterEventLoopAsync() = 0;
virtual void leaveEventLoop() = 0;
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0;
};
}
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createConnection();
}
#endif /* SDBUS_CXX_INTERNAL_ICONNECTION_H_ */
+3 -2
View File
@@ -30,7 +30,7 @@
#include <systemd/sd-bus.h>
namespace sdbus { namespace internal {
namespace sdbus::internal {
class ISdBus
{
@@ -51,6 +51,7 @@ namespace sdbus { namespace internal {
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) = 0;
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) = 0;
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type) = 0;
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) = 0;
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) = 0;
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) = 0;
@@ -83,6 +84,6 @@ namespace sdbus { namespace internal {
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
};
}}
}
#endif //SDBUS_CXX_ISDBUS_H
+4 -55
View File
@@ -28,7 +28,8 @@
#include <sdbus-c++/Types.h>
#include <sdbus-c++/Error.h>
#include "MessageUtils.h"
#include "SdBus.h"
#include "ISdBus.h"
#include "IConnection.h"
#include "ScopeGuard.h"
#include <systemd/sd-bus.h>
#include <cassert>
@@ -712,60 +713,8 @@ void Signal::send() const
PlainMessage createPlainMessage()
{
int r;
// All references to the bus (like created messages) must not outlive this thread (because messages refer to sdbus
// which is thread-local, and because BusReferenceKeeper below destroys the bus at thread exit).
// A more flexible solution would be that the caller would already provide an ISdBus reference as a parameter.
// Variant is one of the callers. This means Variant could no more be created in a stand-alone way, but
// through a factory of some existing facility (Object, Proxy, Connection...).
// TODO: Consider this alternative of creating Variant, it may live next to the current one. This function would
// get IConnection* parameter and IConnection would provide createPlainMessage factory (just like it already
// provides e.g. createMethodCall). If this parameter were null, the current mechanism would be used.
thread_local internal::SdBus sdbus;
sd_bus* bus{};
SCOPE_EXIT{ sd_bus_unref(bus); };
r = sd_bus_default_system(&bus);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get default system bus", -r);
thread_local struct BusReferenceKeeper
{
explicit BusReferenceKeeper(sd_bus* bus) : bus_(sd_bus_ref(bus)) { sd_bus_flush(bus_); }
~BusReferenceKeeper() { sd_bus_flush_close_unref(bus_); }
sd_bus* bus_{};
} busReferenceKeeper{bus};
// thread_local struct BusKeeper
// {
// BusKeeper()
// {
// auto r = sd_bus_open_system(&bus);
// sd_bus_flush(bus);
// SDBUS_THROW_ERROR_IF(r < 0, "Failed to get default system bus", -r);
// }
// ~BusKeeper()
// {
// sd_bus_flush_close_unref(bus);
// }
// sd_bus* bus{};
// internal::SdBus intf;
// } busKeeper;
// Shelved here as handy thing for potential future tracing purposes:
//#include <unistd.h>
//#include <sys/syscall.h>
//#define gettid() syscall(SYS_gettid)
//printf("createPlainMessage: sd_bus*=[%p], n_ref=[%d], TID=[%d]\n", bus, *(unsigned*)bus, gettid());
sd_bus_message* sdbusMsg{};
r = sd_bus_message_new(bus, &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a new message", -r);
return Message::Factory::create<PlainMessage>(sdbusMsg, &sdbus, adopt_message);
static auto connection = internal::createConnection();
return connection->createPlainMessage();
}
}
+86 -41
View File
@@ -37,7 +37,7 @@
#include <utility>
#include <cassert>
namespace sdbus { namespace internal {
namespace sdbus::internal {
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
: connection_(connection), objectPath_(std::move(objectPath))
@@ -45,51 +45,83 @@ Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
}
void Object::registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags )
{
registerMethod( interfaceName
, std::move(methodName)
, std::move(inputSignature)
, {}
, std::move(outputSignature)
, {}
, std::move(methodCallback)
, std::move(flags) );
}
void Object::registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags )
{
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
auto& interface = interfaces_[interfaceName];
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback), flags};
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
InterfaceData::MethodData methodData{ std::move(inputSignature)
, std::move(outputSignature)
, paramNamesToString(inputNames) + paramNamesToString(outputNames)
, std::move(methodCallback)
, std::move(flags) };
auto inserted = interface.methods.emplace(std::move(methodName), std::move(methodData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
}
void Object::registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
, std::string signalName
, std::string signature
, Flags flags )
{
registerSignal(interfaceName, std::move(signalName), std::move(signature), {}, std::move(flags));
}
void Object::registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags )
{
auto& interface = interfaces_[interfaceName];
InterfaceData::SignalData signalData{signature, flags};
auto inserted = interface.signals_.emplace(signalName, std::move(signalData)).second;
InterfaceData::SignalData signalData{std::move(signature), paramNamesToString(paramNames), std::move(flags)};
auto inserted = interface.signals.emplace(std::move(signalName), std::move(signalData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
}
void Object::registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags )
{
registerProperty( interfaceName
, propertyName
, signature
, getCallback
, property_set_callback{}
, flags );
, std::move(propertyName)
, std::move(signature)
, std::move(getCallback)
, {}
, std::move(flags) );
}
void Object::registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags )
@@ -98,8 +130,11 @@ void Object::registerProperty( const std::string& interfaceName
auto& interface = interfaces_[interfaceName];
InterfaceData::PropertyData propertyData{signature, std::move(getCallback), std::move(setCallback), flags};
auto inserted = interface.properties_.emplace(propertyName, std::move(propertyData)).second;
InterfaceData::PropertyData propertyData{ std::move(signature)
, std::move(getCallback)
, std::move(setCallback)
, std::move(flags)};
auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
}
@@ -107,7 +142,7 @@ void Object::registerProperty( const std::string& interfaceName
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
{
auto& interface = interfaces_[interfaceName];
interface.flags_ = flags;
interface.flags = flags;
}
void Object::finishRegistration()
@@ -192,10 +227,10 @@ sdbus::IConnection& Object::getConnection() const
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
{
auto& vtable = interfaceData.vtable_;
auto& vtable = interfaceData.vtable;
assert(vtable.empty());
vtable.push_back(createVTableStartItem(interfaceData.flags_.toSdBusInterfaceFlags()));
vtable.push_back(createVTableStartItem(interfaceData.flags.toSdBusInterfaceFlags()));
registerMethodsToVTable(interfaceData, vtable);
registerSignalsToVTable(interfaceData, vtable);
registerPropertiesToVTable(interfaceData, vtable);
@@ -206,14 +241,15 @@ const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& i
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.methods_)
for (const auto& item : interfaceData.methods)
{
const auto& methodName = item.first;
const auto& methodData = item.second;
vtable.push_back(createVTableMethodItem( methodName.c_str()
, methodData.inputArgs_.c_str()
, methodData.outputArgs_.c_str()
, methodData.inputArgs.c_str()
, methodData.outputArgs.c_str()
, methodData.paramNames.c_str()
, &Object::sdbus_method_callback
, methodData.flags_.toSdBusMethodFlags() ));
}
@@ -221,35 +257,36 @@ void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::ve
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.signals_)
for (const auto& item : interfaceData.signals)
{
const auto& signalName = item.first;
const auto& signalData = item.second;
vtable.push_back(createVTableSignalItem( signalName.c_str()
, signalData.signature_.c_str()
, signalData.flags_.toSdBusSignalFlags() ));
, signalData.signature.c_str()
, signalData.paramNames.c_str()
, signalData.flags.toSdBusSignalFlags() ));
}
}
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.properties_)
for (const auto& item : interfaceData.properties)
{
const auto& propertyName = item.first;
const auto& propertyData = item.second;
if (!propertyData.setCallback_)
if (!propertyData.setCallback)
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
, propertyData.signature_.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, propertyData.flags_.toSdBusPropertyFlags() ));
, propertyData.flags.toSdBusPropertyFlags() ));
else
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
, propertyData.signature_.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, &Object::sdbus_property_set_callback
, propertyData.flags_.toSdBusWritablePropertyFlags() ));
, propertyData.flags.toSdBusWritablePropertyFlags() ));
}
}
@@ -257,7 +294,15 @@ void Object::activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable )
{
interfaceData.slot_ = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
}
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
{
std::string names;
for (const auto& name : paramNames)
names += name + '\0';
return names;
}
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
@@ -268,7 +313,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object->connection_.getSdBusInterface());
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
auto& callback = object->interfaces_[message.getInterfaceName()].methods[message.getMemberName()].callback;
assert(callback);
try
@@ -295,7 +340,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
assert(object != nullptr);
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties_[property].getCallback_;
auto& callback = object->interfaces_[interface].properties[property].getCallback;
// Getter can be empty - the case of "write-only" property
if (!callback)
{
@@ -329,7 +374,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
assert(object != nullptr);
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties_[property].setCallback_;
auto& callback = object->interfaces_[interface].properties[property].setCallback;
assert(callback);
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object->connection_.getSdBusInterface());
@@ -346,7 +391,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
return 1;
}
}}
}
namespace sdbus {
+41 -28
View File
@@ -37,8 +37,7 @@
#include <memory>
#include <cassert>
namespace sdbus {
namespace internal {
namespace sdbus::internal {
class Object
: public IObject
@@ -47,26 +46,38 @@ namespace internal {
Object(sdbus::internal::IConnection& connection, std::string objectPath);
void registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags ) override;
void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags ) override;
void registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
, std::string signalName
, std::string signature
, Flags flags ) override;
void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags ) override;
void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags ) override;
void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags ) override;
@@ -98,32 +109,34 @@ namespace internal {
using MethodName = std::string;
struct MethodData
{
std::string inputArgs_;
std::string outputArgs_;
method_callback callback_;
const std::string inputArgs;
const std::string outputArgs;
const std::string paramNames;
method_callback callback;
Flags flags_;
};
std::map<MethodName, MethodData> methods_;
std::map<MethodName, MethodData> methods;
using SignalName = std::string;
struct SignalData
{
std::string signature_;
Flags flags_;
const std::string signature;
const std::string paramNames;
Flags flags;
};
std::map<SignalName, SignalData> signals_;
std::map<SignalName, SignalData> signals;
using PropertyName = std::string;
struct PropertyData
{
std::string signature_;
property_get_callback getCallback_;
property_set_callback setCallback_;
Flags flags_;
const std::string signature;
property_get_callback getCallback;
property_set_callback setCallback;
Flags flags;
};
std::map<PropertyName, PropertyData> properties_;
std::vector<sd_bus_vtable> vtable_;
Flags flags_;
std::map<PropertyName, PropertyData> properties;
std::vector<sd_bus_vtable> vtable;
Flags flags;
SlotPtr slot_;
SlotPtr slot;
};
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData);
@@ -133,6 +146,7 @@ namespace internal {
void activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable );
static std::string paramNamesToString(const std::vector<std::string>& paramNames);
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_property_get_callback( sd_bus *bus
@@ -158,6 +172,5 @@ namespace internal {
};
}
}
#endif /* SDBUS_CXX_INTERNAL_OBJECT_H_ */
+3 -3
View File
@@ -36,7 +36,7 @@
#include <chrono>
#include <thread>
namespace sdbus { namespace internal {
namespace sdbus::internal {
Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
@@ -56,7 +56,7 @@ Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
{
// The connection is ours only, i.e. it's us who has to manage the event loop upon this connection,
// in order that we get and process signals, async call replies, and other messages from D-Bus.
connection_->enterProcessingLoopAsync();
connection_->enterEventLoopAsync();
}
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
@@ -241,7 +241,7 @@ int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd
return 1;
}
}}
}
namespace sdbus {
+4 -5
View File
@@ -37,8 +37,7 @@
#include <mutex>
#include <condition_variable>
namespace sdbus {
namespace internal {
namespace sdbus::internal {
class Proxy
: public IProxy
@@ -122,13 +121,13 @@ namespace internal {
bool addCall(void* slot, std::unique_ptr<CallData>&& asyncCallData)
{
std::lock_guard<std::mutex> lock(mutex_);
std::lock_guard lock(mutex_);
return calls_.emplace(slot, std::move(asyncCallData)).second;
}
bool removeCall(void* slot)
{
std::lock_guard<std::mutex> lock(mutex_);
std::lock_guard lock(mutex_);
return calls_.erase(slot) > 0;
}
@@ -147,6 +146,6 @@ namespace internal {
} pendingAsyncCalls_;
};
}}
}
#endif /* SDBUS_CXX_INTERNAL_PROXY_H_ */
+34 -27
View File
@@ -28,67 +28,74 @@
#include "SdBus.h"
#include <sdbus-c++/Error.h>
namespace sdbus { namespace internal {
namespace sdbus::internal {
sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *m)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_ref(m);
}
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_unref(m);
}
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_send(bus, m, cookie);
}
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_call(bus, m, usec, ret_error, reply);
}
int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
}
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new(bus, m, type);
}
int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member);
}
int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_signal(bus, m, path, interface, member);
}
int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_method_return(call, m);
}
int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_method_error(call, m, e);
}
@@ -96,7 +103,7 @@ int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message
int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
{
#if LIBSYSTEMD_VERSION>=240
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_set_method_call_timeout(bus, usec);
#else
@@ -109,7 +116,7 @@ int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
{
#if LIBSYSTEMD_VERSION>=240
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_get_method_call_timeout(bus, ret);
#else
@@ -121,35 +128,35 @@ int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_properties_changed_strv(bus, path, interface, names);
}
int SdBus::sd_bus_emit_object_added(sd_bus *bus, const char *path)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_object_added(bus, path);
}
int SdBus::sd_bus_emit_object_removed(sd_bus *bus, const char *path)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_object_removed(bus, path);
}
int SdBus::sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
}
int SdBus::sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
}
@@ -171,62 +178,62 @@ int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_request_name(bus, name, flags);
}
int SdBus::sd_bus_release_name(sd_bus *bus, const char *name)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_release_name(bus, name);
}
int SdBus::sd_bus_get_unique_name(sd_bus *bus, const char **name)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_get_unique_name(bus, name);
}
int SdBus::sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_add_object_vtable(bus, slot, path, interface, vtable, userdata);
}
int SdBus::sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_add_object_manager(bus, slot, path);
}
int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return :: sd_bus_add_match(bus, slot, match, callback, userdata);
}
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_slot_unref(slot);
}
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_process(bus, r);
}
int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
std::lock_guard lock(sdbusMutex_);
auto r = ::sd_bus_get_fd(bus);
if (r < 0)
@@ -253,4 +260,4 @@ sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus)
return ::sd_bus_flush_close_unref(bus);
}
}}
}
+3 -2
View File
@@ -31,7 +31,7 @@
#include "ISdBus.h"
#include <mutex>
namespace sdbus { namespace internal {
namespace sdbus::internal {
class SdBus final : public ISdBus
{
@@ -43,6 +43,7 @@ public:
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) override;
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) override;
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type) override;
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) override;
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) override;
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) override;
@@ -78,6 +79,6 @@ private:
std::recursive_mutex sdbusMutex_;
};
}}
}
#endif //SDBUS_C_SDBUS_H
+28
View File
@@ -36,18 +36,46 @@ sd_bus_vtable createVTableStartItem(uint64_t flags)
sd_bus_vtable createVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags )
{
#if LIBSYSTEMD_VERSION>=242
// We have to expand macro SD_BUS_METHOD_WITH_NAMES manually here, because the macro expects literal char strings
/*struct sd_bus_vtable vtableItem = SD_BUS_METHOD_WITH_NAMES(member, signature, innames, result, outnames, handler, flags);*/
struct sd_bus_vtable vtableItem =
{
.type = _SD_BUS_VTABLE_METHOD,
.flags = flags,
.x = {
.method = {
.member = member,
.signature = signature,
.result = result,
.handler = handler,
.offset = 0,
.names = paramNames,
},
},
};
#else
(void)paramNames;
struct sd_bus_vtable vtableItem = SD_BUS_METHOD(member, signature, result, handler, flags);
#endif
return vtableItem;
}
sd_bus_vtable createVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags )
{
#if LIBSYSTEMD_VERSION>=242
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL_WITH_NAMES(member, signature, outnames, flags);
#else
(void)outnames;
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL(member, signature, flags);
#endif
return vtableItem;
}
+2
View File
@@ -38,10 +38,12 @@ sd_bus_vtable createVTableStartItem(uint64_t flags);
sd_bus_vtable createVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags );
sd_bus_vtable createVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags );
sd_bus_vtable createVTablePropertyItem( const char *member
, const char *signature
+22 -33
View File
@@ -1,37 +1,29 @@
#-------------------------------
# DOWNLOAD AND BUILD OF GOOGLETEST
# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project
#-------------------------------
configure_file(googletest-download/CMakeLists.txt.in googletest-download/CMakeLists.txt)
include(FetchContent)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
message("Fetching googletest...")
FetchContent_Declare(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
GIT_SHALLOW 1
UPDATE_COMMAND "")
if(result)
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
#FetchContent_MakeAvailable(googletest) # Not available in CMake 3.13 :-( Let's do it manually:
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
endif()
execute_process(COMMAND ${CMAKE_COMMAND} --build .
RESULT_VARIABLE result
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download)
if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
#-------------------------------
# SOURCE FILES CONFIGURATION
#-------------------------------
@@ -84,15 +76,12 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# BUILD INFORMATION
#----------------------------------
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS} $<TARGET_OBJECTS:sdbus-c++-objlib>)
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_include_directories(sdbus-c++-unit-tests PRIVATE ${SYSTEMD_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include)
target_link_libraries(sdbus-c++-unit-tests ${SYSTEMD_LIBRARIES} gmock gmock_main)
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib gmock gmock_main)
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ gmock gmock_main)
# Manual performance and stress tests
@@ -62,12 +62,12 @@ public:
static void SetUpTestCase()
{
s_connection->requestName(INTERFACE_NAME);
s_connection->enterProcessingLoopAsync();
s_connection->enterEventLoopAsync();
}
static void TearDownTestCase()
{
s_connection->leaveProcessingLoop();
s_connection->leaveEventLoop();
s_connection->releaseName(INTERFACE_NAME);
}
+4 -4
View File
@@ -47,7 +47,7 @@ using ::testing::Eq;
TEST(Connection, CanBeDefaultConstructed)
{
ASSERT_NO_THROW(sdbus::createConnection());
ASSERT_NO_THROW(auto con = sdbus::createConnection());
}
TEST(Connection, CanRequestRegisteredDbusName)
@@ -78,13 +78,13 @@ TEST(Connection, CannotReleaseNonrequestedName)
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
}
TEST(Connection, CanEnterAndLeaveProcessingLoop)
TEST(Connection, CanEnterAndLeaveEventLoop)
{
auto connection = sdbus::createConnection();
connection->requestName(INTERFACE_NAME);
std::thread t([&](){ connection->enterProcessingLoop(); });
connection->leaveProcessingLoop();
std::thread t([&](){ connection->enterEventLoop(); });
connection->leaveEventLoop();
t.join();
+23 -23
View File
@@ -50,32 +50,32 @@ public:
protected:
void noArgNoReturn() const
void noArgNoReturn() const override
{
}
int32_t getInt() const
int32_t getInt() const override
{
return INT32_VALUE;
}
std::tuple<uint32_t, std::string> getTuple() const
std::tuple<uint32_t, std::string> getTuple() const override
{
return std::make_tuple(UINT32_VALUE, STRING_VALUE);
}
double multiply(const int64_t& a, const double& b) const
double multiply(const int64_t& a, const double& b) const override
{
return a * b;
}
void multiplyWithNoReply(const int64_t& a, const double& b) const
void multiplyWithNoReply(const int64_t& a, const double& b) const override
{
m_multiplyResult = a * b;
m_wasMultiplyCalled = true;
}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const override
{
std::vector<int16_t> res{x.get<1>()};
auto y = std::get<std::vector<int16_t>>(x);
@@ -83,13 +83,13 @@ protected:
return res;
}
sdbus::Variant processVariant(sdbus::Variant& v)
sdbus::Variant processVariant(sdbus::Variant& v) override
{
sdbus::Variant res = static_cast<int32_t>(v.get<double>());
return res;
}
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const override
{
std::map<int32_t, sdbus::Variant> res;
for (auto item : x)
@@ -99,12 +99,12 @@ protected:
return res;
}
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const override
{
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}));
}
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) override
{
int32_t res{0};
res += std::get<0>(a) + std::get<1>(a);
@@ -112,7 +112,7 @@ protected:
return res;
}
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) override
{
uint32_t res{0};
for (auto x : a)
@@ -126,13 +126,13 @@ protected:
return res;
}
uint32_t doOperation(uint32_t param)
uint32_t doOperation(uint32_t param) override
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
return param;
}
void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result)
void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) override
{
if (param == 0)
{
@@ -150,20 +150,20 @@ protected:
}
}
sdbus::Signature getSignature() const
sdbus::Signature getSignature() const override
{
return SIGNATURE_VALUE;
}
sdbus::ObjectPath getObjectPath() const
sdbus::ObjectPath getObjectPath() const override
{
return OBJECT_PATH_VALUE;
}
sdbus::UnixFd getUnixFd() const
sdbus::UnixFd getUnixFd() const override
{
return sdbus::UnixFd{UNIX_FD_VALUE};
}
ComplexType getComplex() const
ComplexType getComplex() const override
{
return { // map
{
@@ -191,7 +191,7 @@ protected:
};
}
void throwError() const
void throwError() const override
{
m_wasThrowErrorCalled = true;
throw sdbus::createError(1, "A test error occurred");
@@ -204,27 +204,27 @@ protected:
emitSignalWithMap({});
}
std::string state()
std::string state() override
{
return m_state;
}
uint32_t action()
uint32_t action() override
{
return m_action;
}
void action(const uint32_t& value)
void action(const uint32_t& value) override
{
m_action = value;
}
bool blocking()
bool blocking() override
{
return m_blocking;
}
void blocking(const bool& value)
void blocking(const bool& value) override
{
m_blocking = value;
}
+14 -12
View File
@@ -60,17 +60,18 @@ protected:
object_.setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getInt(); });
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); });
object_.registerMethod("getTuple").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getTuple(); });
object_.registerMethod("multiply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
object_.registerMethod("multiply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
object_.registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
object_.registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).implementedAs([this](
const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x){ return this->getInts16FromStruct(x); });
object_.registerMethod("processVariant").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Variant& v){ return this->processVariant(v); });
object_.registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).implementedAs([this](
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).implementedAs([this](){ return this->getStructInStruct(); });
@@ -110,8 +111,9 @@ protected:
// registration of signals is optional, it is useful because of introspection
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>();
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>();
// Note: sd-bus of libsystemd up to v244 has a bug where it doesn't generate signal parameter names in introspection XML. Signal param names commented temporarily.
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>(/*"aMap"*/);
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>(/*"aVariant"*/);
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL);
@@ -250,16 +252,16 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</method>
<method name="getInt">
<arg type="i" direction="out"/>
<arg type="i" name="anInt" direction="out"/>
</method>
<method name="getInts16FromStruct">
<arg type="(yndsan)" direction="in"/>
<arg type="an" direction="out"/>
</method>
<method name="getMapOfVariants">
<arg type="ai" direction="in"/>
<arg type="(vv)" direction="in"/>
<arg type="a{iv}" direction="out"/>
<arg type="ai" name="x" direction="in"/>
<arg type="(vv)" name="y" direction="in"/>
<arg type="a{iv}" name="aMapOfVariants" direction="out"/>
</method>
<method name="getObjectPath">
<arg type="o" direction="out"/>
@@ -278,9 +280,9 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<arg type="h" direction="out"/>
</method>
<method name="multiply">
<arg type="x" direction="in"/>
<arg type="d" direction="in"/>
<arg type="d" direction="out"/>
<arg type="x" name="a" direction="in"/>
<arg type="d" name="b" direction="in"/>
<arg type="d" name="result" direction="out"/>
</method>
<method name="multiplyWithNoReply">
<arg type="x" direction="in"/>
+3 -3
View File
@@ -22,9 +22,9 @@ protected:
perftests_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("sendDataSignals").onInterface(INTERFACE_NAME).implementedAs([this](const uint32_t& numberOfSignals, const uint32_t& signalMsgSize){ return this->sendDataSignals(numberOfSignals, signalMsgSize); });
object_.registerMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).implementedAs([this](const std::string& string1, const std::string& string2){ return this->concatenateTwoStrings(string1, string2); });
object_.registerSignal("dataSignal").onInterface(INTERFACE_NAME).withParameters<std::string>();
object_.registerMethod("sendDataSignals").onInterface(INTERFACE_NAME).withInputParamNames("numberOfSignals", "signalMsgSize").implementedAs([this](const uint32_t& numberOfSignals, const uint32_t& signalMsgSize){ return this->sendDataSignals(numberOfSignals, signalMsgSize); });
object_.registerMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withInputParamNames("string1", "string2").withOutputParamNames("result").implementedAs([this](const std::string& string1, const std::string& string2){ return this->concatenateTwoStrings(string1, string2); });
object_.registerSignal("dataSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("data");
}
~perftests_adaptor() = default;
+1 -1
View File
@@ -97,5 +97,5 @@ int main(int /*argc*/, char */*argv*/[])
const char* objectPath = "/org/sdbuscpp/perftests";
PerftestAdaptor server(*connection, objectPath);
connection->enterProcessingLoop();
connection->enterEventLoop();
}
@@ -24,7 +24,7 @@ protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getCurrentTemperature(); });
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
}
~thermometer_adaptor() = default;
+2 -2
View File
@@ -23,8 +23,8 @@ protected:
concatenator_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("concatenate").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); });
object_.registerSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withParameters<std::string>();
object_.registerMethod("concatenate").onInterface(INTERFACE_NAME).withInputParamNames("params").withOutputParamNames("result").implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); });
object_.registerSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
}
~concatenator_adaptor() = default;
@@ -24,7 +24,7 @@ protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getCurrentTemperature(); });
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
}
~thermometer_adaptor() = default;
@@ -53,8 +53,8 @@ protected:
factory_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("createDelegateObject").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); });
object_.registerMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
object_.registerMethod("createDelegateObject").onInterface(INTERFACE_NAME).withOutputParamNames("delegate").implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); });
object_.registerMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withInputParamNames("delegate").implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
}
~factory_adaptor() = default;
+7 -7
View File
@@ -392,7 +392,7 @@ int main(int argc, char *argv[])
{
CelsiusThermometerAdaptor thermometer(con, CELSIUS_THERMOMETER_OBJECT_PATH);
service2ThreadReady = true;
con.enterProcessingLoop();
con.enterEventLoop();
});
auto service1Connection = sdbus::createSystemBusConnection(SERVICE_1_BUS_NAME);
@@ -402,7 +402,7 @@ int main(int argc, char *argv[])
ConcatenatorAdaptor concatenator(con, CONCATENATOR_OBJECT_PATH);
FahrenheitThermometerAdaptor thermometer(con, FAHRENHEIT_THERMOMETER_OBJECT_PATH, false);
service1ThreadReady = true;
con.enterProcessingLoop();
con.enterEventLoop();
});
// Wait for both services to export their D-Bus objects
@@ -480,8 +480,8 @@ int main(int argc, char *argv[])
// We could run the loop in a sync way, but we want it to run also when proxies are destroyed for better
// coverage of multi-threaded scenarios, so we run it async and use condition variable for exit notification
//con.enterProcessingLoop();
con.enterProcessingLoopAsync();
//con.enterEventLoop();
con.enterEventLoopAsync();
std::unique_lock<std::mutex> lock(clientThreadExitMutex);
clientThreadExitCond.wait(lock, [&]{return clientThreadExit;});
@@ -493,17 +493,17 @@ int main(int argc, char *argv[])
std::this_thread::sleep_for(std::chrono::milliseconds(loopDuration));
//clientConnection->leaveProcessingLoop();
//clientConnection->leaveEventLoop();
std::unique_lock<std::mutex> lock(clientThreadExitMutex);
clientThreadExit = true;
lock.unlock();
clientThreadExitCond.notify_one();
clientThread.join();
service1Connection->leaveProcessingLoop();
service1Connection->leaveEventLoop();
service1Thread.join();
service2Connection->leaveProcessingLoop();
service2Connection->leaveEventLoop();
service2Thread.join();
}
+1 -1
View File
@@ -69,7 +69,7 @@ TEST(AVariant, CanBeConstructedFromAComplexValue)
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello"s, ANY_DOUBLE), sdbus::make_struct("world"s, ANY_DOUBLE)}} };
ASSERT_NO_THROW(sdbus::Variant(value));
ASSERT_NO_THROW(sdbus::Variant{value});
}
TEST(AVariant, CanBeCopied)
+1
View File
@@ -42,6 +42,7 @@ public:
MOCK_METHOD5(sd_bus_call, int(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply));
MOCK_METHOD6(sd_bus_call_async, int(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec));
MOCK_METHOD3(sd_bus_message_new, int(sd_bus *bus, sd_bus_message **m, uint8_t type));
MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member));
MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member));
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **m));
+8 -5
View File
@@ -204,14 +204,17 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
Nodes inArgs = args.select("direction" , "in");
Nodes outArgs = args.select("direction" , "out");
std::string argStr, argTypeStr;
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs, async);
std::string argStr, argTypeStr, argStringsStr, outArgStringsStr;
std::tie(argStr, argTypeStr, std::ignore, argStringsStr) = argsToNamesAndTypes(inArgs, async);
std::tie(std::ignore, std::ignore, std::ignore, outArgStringsStr) = argsToNamesAndTypes(outArgs);
using namespace std::string_literals;
registrationSS << tab << tab << "object_.registerMethod(\""
<< methodName << "\")"
<< ".onInterface(INTERFACE_NAME)"
<< (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
<< (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "")
<< ".implementedAs("
<< "[this]("
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
@@ -259,8 +262,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
Nodes args = (*signal)["arg"];
std::string argStr, argTypeStr, typeStr;;
std::tie(argStr, argTypeStr, typeStr) = argsToNamesAndTypes(args);
std::string argStr, argTypeStr, typeStr, argStringsStr;
std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
signalRegistrationSS << tab << tab
<< "object_.registerSignal(\"" << name << "\")"
@@ -268,7 +271,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
if (args.size() > 0)
{
signalRegistrationSS << ".withParameters<" << typeStr << ">()";
signalRegistrationSS << ".withParameters<" << typeStr << ">(" << argStringsStr << ")";
}
signalRegistrationSS << annotationRegistration;
+5 -3
View File
@@ -105,9 +105,9 @@ std::tuple<unsigned, std::string> BaseGenerator::generateNamespaces(const std::s
}
std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndTypes(const Nodes& args, bool async) const
std::tuple<std::string, std::string, std::string, std::string> BaseGenerator::argsToNamesAndTypes(const Nodes& args, bool async) const
{
std::ostringstream argSS, argTypeSS, typeSS;
std::ostringstream argSS, argTypeSS, typeSS, argStringsSS;
for (size_t i = 0; i < args.size(); ++i)
{
@@ -115,6 +115,7 @@ std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndT
if (i > 0)
{
argSS << ", ";
argStringsSS << ", ";
argTypeSS << ", ";
typeSS << ", ";
}
@@ -125,6 +126,7 @@ std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndT
argName = "arg" + std::to_string(i);
}
auto type = signature_to_type(arg->get("type"));
argStringsSS << "\"" << argName << "\"";
if (!async)
{
argSS << argName;
@@ -138,7 +140,7 @@ std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndT
typeSS << type;
}
return std::make_tuple(argSS.str(), argTypeSS.str(), typeSS.str());
return std::make_tuple(argSS.str(), argTypeSS.str(), typeSS.str(), argStringsSS.str());
}
/**
+1 -1
View File
@@ -88,7 +88,7 @@ protected:
* @param args
* @return tuple: argument names, argument types and names, argument types
*/
std::tuple<std::string, std::string, std::string> argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args, bool async = false) const;
std::tuple<std::string, std::string, std::string, std::string> argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args, bool async = false) const;
/**
* Output arguments to return type
+3 -3
View File
@@ -169,9 +169,9 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
auto retType = outArgsToType(outArgs);
std::string inArgStr, inArgTypeStr;
std::tie(inArgStr, inArgTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
std::tie(inArgStr, inArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(inArgs);
std::string outArgStr, outArgTypeStr;
std::tie(outArgStr, outArgTypeStr, std::ignore) = argsToNamesAndTypes(outArgs);
std::tie(outArgStr, outArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(outArgs);
definitionSS << tab << (async ? "void" : retType) << " " << name << "(" << inArgTypeStr << ")" << endl
<< tab << "{" << endl;
@@ -239,7 +239,7 @@ std::tuple<std::string, std::string> ProxyGenerator::processSignals(const Nodes&
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
std::string argStr, argTypeStr;
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(args);
std::tie(argStr, argTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(args);
registrationSS << tab << tab << "proxy_"
".uponSignal(\"" << name << "\")"