Compare commits

...

32 Commits

Author SHA1 Message Date
0eda855745 chore: version 1.3.0 2023-08-20 11:45:44 +02:00
2a992ca84d fix: remove explicit from Variant ctor to avoid potential breaking client code 2023-08-20 11:17:10 +02:00
3717e63c64 feat: support std::future-based async methods in codegen tool (#353) 2023-08-19 20:57:32 +02:00
8113bf88ad chore(cmake): add support for libelogind (#352)
* CMakeLists.txt: Fallback to elogind when libsystemd could not be
found.  Set LIBSYSTEMD variable.
* pkgconfig/sdbus-c++.pc.in (Description): Parameterize with above
LIBSYSTEMD variable.

Co-authored-by: Sven Eden <sven.eden@prydeworx.com>
2023-08-18 12:08:37 +02:00
3e84b254e9 refactor: improve type extensibility and its safety (#348) 2023-08-09 12:39:16 +02:00
8728653359 test: add tests for type extensibility (#347) 2023-08-09 12:14:33 +02:00
6620a447d1 docs: add tutorial on extending sdbus-c++ types (#346) 2023-08-09 12:13:47 +02:00
24a3d83c3f chore: fix file permissions 2023-08-04 13:30:34 +02:00
dcd9d46b9c style: remove trailing whitespace (#345)
* style: restore correct 644 file permission

* style: remove trailing whitespace
2023-08-04 13:26:45 +02:00
605fbe48c0 fix: moving instead of copying std::string argument 2023-08-03 14:25:33 +02:00
fb61420bf0 feat: support serialization of array, span and unordered_map (#342)
* feat: support serialization of array, span and unordered_map

* fix some spelling mistakes

* docs: update table of valid c++ types

---------

Co-authored-by: Marcel Hellwig <github@cookiesoft.de>
2023-08-03 13:55:37 +02:00
0a2bda9c67 feat: make Struct tuple-like class (#343) 2023-08-03 13:00:01 +02:00
f6e597a583 fix: add cast to void to silence compiler warning in case of empty parameter list 2023-08-03 12:59:48 +02:00
29c877a89a perf: optimize serialization of arrays of trivial D-Bus types (#340)
* Improve performance of std::vector<> per Issue #339.

* refactor: improve performance of vector serialization

---------

Co-authored-by: Plinio Andrade <plinio.andrade@oracle.com>
Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2023-07-27 18:31:49 +02:00
98f4929337 fix: pseudo-connection static lifetime issue 2023-07-26 08:57:21 +02:00
8d0d9b0d40 chore(ci): remove ubuntu-18.04 container image 2023-07-26 08:46:00 +02:00
c39bc637b8 fix: race condition in async Proxy::callMethod
Signed-off-by: Anthony Brandon <anthony@amarulasolutions.com>
2023-02-07 22:23:35 +01:00
737f04abc7 feat: add support for std::future-based async calls 2023-02-07 12:31:31 +01:00
3a56113422 fix: fix namespace name in ScopeGuard 2023-01-21 01:34:34 +01:00
f332f46087 fix: use correct runtime component in CMake file 2023-01-17 09:58:21 +01:00
c9e157e3e1 fix: flush long messages after sending 2023-01-05 15:12:39 +01:00
8ca3fdd5ce chore: update issue templates 2023-01-04 22:05:56 +01:00
55c306ce05 docs: strip absolute paths from doxygen documentation 2023-01-04 21:51:28 +01:00
6c5e72326c refactor(ci): use newer github actions and ubuntu v22 image 2023-01-04 21:51:13 +01:00
8bbeeeb4ce fix: integration tests in release mode 2023-01-03 16:39:54 +01:00
7a09e9bcc8 chore: install namelink to dev component 2023-01-03 15:24:17 +01:00
c812d03bc7 fix: integration tests for libsystemd v251 2023-01-03 15:20:30 +01:00
e7d4e07926 fix: broken D-Bus specs link in README (#297)
- D-Bus specification broken url link is corrected

Signed-off-by: Bhavith C <bhavithc.acharya@gmail.com>

Signed-off-by: Bhavith C <bhavithc.acharya@gmail.com>
2023-01-02 15:31:08 +01:00
031f4687ca fix: compilation warnings 2022-09-21 15:37:57 +02:00
aeae79003a refactor: support move semantics in generated adaptor and proxy classes 2022-09-20 17:05:59 +02:00
74d849d933 feat: add support for proxy with no event loop thread 2022-09-05 17:25:37 +02:00
f336811fc7 docs: add section on D-Bus types to C++ types mapping (#285)
* Added D-Bus type C++ type table

* docs: do corrections and rewordings in the proposed chapter

Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2022-09-05 13:51:05 +02:00
61 changed files with 1878 additions and 397 deletions

23
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,23 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Real (buggy) behavior**
A clear and concise description of what really happened in contrast to your expectation.
**Additional context**
Add any other context about the problem here, like version of sdbus-c++ library, version of systemd used, OS used.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -12,7 +12,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-18.04, ubuntu-20.04] os: [ubuntu-20.04, ubuntu-22.04]
compiler: [g++, clang] compiler: [g++, clang]
build: [shared-libsystemd] build: [shared-libsystemd]
include: include:
@ -20,7 +20,7 @@ jobs:
compiler: g++ compiler: g++
build: embedded-static-libsystemd build: embedded-static-libsystemd
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: install-libsystemd-toolchain - name: install-libsystemd-toolchain
if: matrix.build == 'embedded-static-libsystemd' if: matrix.build == 'embedded-static-libsystemd'
run: | run: |
@ -39,33 +39,37 @@ jobs:
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10 sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
sudo update-alternatives --remove-all c++ sudo update-alternatives --remove-all c++
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10 sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
- name: configure-debug - name: install-googletest
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-18.04' if: matrix.os == 'ubuntu-22.04' # On older ubuntus the libgmock-dev package is either unavailable or has faulty pkg-config file
run: | run: |
mkdir build sudo apt-get install -y libgmock-dev
cd build - name: configure-debug
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
- name: configure-release
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04' if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
run: | run: |
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON .. cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
- name: configure-release
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
- name: configure-with-embedded-libsystemd - name: configure-with-embedded-libsystemd
if: matrix.build == 'embedded-static-libsystemd' if: matrix.build == 'embedded-static-libsystemd'
run: | run: |
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 .. cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 ..
- name: make - name: make
run: | run: |
cd build cd build
cmake --build . -j2 cmake --build . -j4
- name: verify - name: verify
run: | run: |
cd build cd build
sudo cmake --build . --target install sudo cmake --build . --target install
ctest ctest --output-on-failure
- name: pack - name: pack
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04' if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
run: | run: |
@ -73,7 +77,7 @@ jobs:
cpack -G DEB cpack -G DEB
- name: 'Upload Artifact' - name: 'Upload Artifact'
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04' && matrix.compiler == 'g++' if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04' && matrix.compiler == 'g++'
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}" name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"
path: | path: |

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
project(sdbus-c++ VERSION 1.2.0 LANGUAGES C CXX) project(sdbus-c++ VERSION 1.3.0 LANGUAGES C CXX)
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
@ -12,11 +12,22 @@ include(GNUInstallDirs) # Installation directories for `install` command and pkg
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES # PERFORMING CHECKS & PREPARING THE DEPENDENCIES
#------------------------------- #-------------------------------
set(LIBSYSTEMD "libsystemd")
option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into libsdbus-c++" OFF) option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into libsdbus-c++" OFF)
if(NOT BUILD_LIBSYSTEMD) if(NOT BUILD_LIBSYSTEMD)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236) pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236)
if(NOT TARGET PkgConfig::Systemd)
message(WARNING "libsystemd not found, checking for libelogind instead")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=236)
if(TARGET PkgConfig::Systemd)
set(LIBSYSTEMD "libelogind")
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
list(GET VERSION_LIST 0 Systemd_VERSION)
endif()
endif()
if(NOT TARGET PkgConfig::Systemd) if(NOT TARGET PkgConfig::Systemd)
message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found " message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found "
"(if you have systemd in your OS, you may want to install package containing pkgconfig " "(if you have systemd in your OS, you may want to install package containing pkgconfig "
@ -140,7 +151,7 @@ endif()
install(TARGETS ${EXPORT_SET} install(TARGETS ${EXPORT_SET}
EXPORT sdbus-c++-targets EXPORT sdbus-c++-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime NAMELINK_COMPONENT dev
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT dev) PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT dev)

View File

@ -4,7 +4,7 @@ As an additional permission to the GNU Lesser General Public License version
2.1, the object code form of a "work that uses the Library" may incorporate 2.1, the object code form of a "work that uses the Library" may incorporate
material from a header file that is part of the Library. You may distribute material from a header file that is part of the Library. You may distribute
such object code under terms of your choice, provided that: such object code under terms of your choice, provided that:
(i) the header files of the Library have not been modified; and (i) the header files of the Library have not been modified; and
(ii) the incorporated material is limited to numerical parameters, data (ii) the incorporated material is limited to numerical parameters, data
structure layouts, accessors, macros, inline functions and structure layouts, accessors, macros, inline functions and
templates; and templates; and

View File

@ -216,3 +216,19 @@ v1.2.0
- Add printer for std::chrono in googletest v1.11.0 - Add printer for std::chrono in googletest v1.11.0
- Fix potential undefined behavior in creation of sdbus::Error - Fix potential undefined behavior in creation of sdbus::Error
- Additional little fixes and improvements in code, build system, and documentation - Additional little fixes and improvements in code, build system, and documentation
v1.3.0
- Add support for light-weight proxies (proxies without own event loop threads)
- Extend documentation with explicit mapping between D-Bus and corresponding C++ types
- Support move semantics in generated adaptor and proxy classes
- Adaptations for libsystemd v251
- Fix for proper complete sending of long D-Bus messages by explicitly flushing them
- Add support for std::future-based async calls
- Fix race condition in async Proxy::callMethod
- Fix pseudo-connection static lifetime issue with Phoenix pattern
- Speed up performance of of serialization of arrays of trivial D-Bus types
- Make sdbus::Struct a tuple-like class, so it's usable wherever std::tuple is
- Add support for std::array, std::span and std::unordered_map as additional C++ types for D-Bus array types
- Add support for libelogind as an addition to libsystemd
- Add support for std::future-based async methods in codegen tool
- Additional little fixes and improvements in code, build system, CI, and documentation

View File

@ -97,7 +97,7 @@ References/documentation
* [Using sdbus-c++](docs/using-sdbus-c++.md) - *the* main, comprehensive tutorial on sdbus-c++ * [Using sdbus-c++](docs/using-sdbus-c++.md) - *the* main, comprehensive tutorial on sdbus-c++
* [Systemd and dbus configuration](docs/systemd-dbus-config.md) * [Systemd and dbus configuration](docs/systemd-dbus-config.md)
* [D-Bus Specification](https://dbus.freedesktop.org/docs/dbus-specification.html) * [D-Bus Specification](https://dbus.freedesktop.org/doc/dbus-specification.html)
* [sd-bus Overview](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html) * [sd-bus Overview](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html)
Contributing Contributing

View File

@ -46,7 +46,7 @@ ExternalProject_Add(LibsystemdBuildProject
GIT_SHALLOW 1 GIT_SHALLOW 1
UPDATE_COMMAND "" UPDATE_COMMAND ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/* CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${LIBSYSTEMD_EXTRA_CONFIG_OPTS} COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Drootprefix=<INSTALL_DIR> -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${LIBSYSTEMD_EXTRA_CONFIG_OPTS}
BUILD_COMMAND ${BUILD_VERSION_H} BUILD_COMMAND ${BUILD_VERSION_H}
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
BUILD_ALWAYS 0 BUILD_ALWAYS 0

View File

@ -15,7 +15,7 @@ if(BUILD_DOXYGEN_DOC)
# workaround bug https://github.com/doxygen/doxygen/pull/6787 # workaround bug https://github.com/doxygen/doxygen/pull/6787
add_custom_command(TARGET doc POST_BUILD add_custom_command(TARGET doc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/sdbus-c++-class-diagram.png ${CMAKE_CURRENT_BINARY_DIR}/html/.) COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/sdbus-c++-class-diagram.png ${CMAKE_CURRENT_BINARY_DIR}/html/.)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT doc) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT doc)
else() else()
message(WARNING "Documentation enabled, but Doxygen cannot be found") message(WARNING "Documentation enabled, but Doxygen cannot be found")

View File

@ -162,7 +162,7 @@ FULL_PATH_NAMES = YES
# will be relative from the directory where doxygen is started. # will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES. # This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH = STRIP_FROM_PATH = @PROJECT_SOURCE_DIR@ @PROJECT_BINARY_DIR@
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which # path mentioned in the documentation of a class, which tells the reader which

View File

@ -14,13 +14,14 @@ Using sdbus-c++ library
9. [An example: Number concatenator](#an-example-number-concatenator) 9. [An example: Number concatenator](#an-example-number-concatenator)
10. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer) 10. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
11. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer) 11. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
12. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs) 12. [Implementing the Concatenator example using generated C++ bindings](#implementing-the-concatenator-example-using-generated-c-bindings)
13. [Asynchronous server-side methods](#asynchronous-server-side-methods) 13. [Asynchronous server-side methods](#asynchronous-server-side-methods)
14. [Asynchronous client-side methods](#asynchronous-client-side-methods) 14. [Asynchronous client-side methods](#asynchronous-client-side-methods)
15. [Using D-Bus properties](#using-d-bus-properties) 15. [Using D-Bus properties](#using-d-bus-properties)
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces) 16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
17. [Support for match rules](#support-for-match-rules) 17. [Representing D-Bus Types in sdbus-c++](#representing-d-bus-types-in-sdbus-c)
18. [Conclusion](#conclusion) 18. [Support for match rules](#support-for-match-rules)
19. [Conclusion](#conclusion)
Introduction Introduction
------------ ------------
@ -54,7 +55,7 @@ PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.6],,
> **_Note_:** sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6). > **_Note_:** sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6).
If you intend to use stub generator (explained later) in your project to generate interface headers from XML, you can integrate that too with CMake or `pkg-config`: If you intend to use xml-to-c++ generator tool (explained later) in your project to generate interface headers from XML, you can integrate that too with CMake or `pkg-config`:
```cmake ```cmake
# First, find sdbus-c++-tools # First, find sdbus-c++-tools
@ -99,7 +100,7 @@ $ ninja libsystemd.so.0.26.0 # or another version number depending which system
### Building and distributing libsystemd as part of sdbus-c++ ### Building and distributing libsystemd as part of sdbus-c++
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd enviroments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Be sure to check out the systemd documentation for the Also, when distributing, make sure these dependency libraries are installed on the production machine. sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd environments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Be sure to check out the systemd documentation for the Also, when distributing, make sure these dependency libraries are installed on the production machine.
You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242). You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242).
@ -202,7 +203,7 @@ sdbus-c++ is completely thread-aware by design. Though sdbus-c++ is not thread-s
* Creating and emitting signals on an `Object` instance. * Creating and emitting signals on an `Object` instance.
* Creating and sending method calls (both synchronously and asynchronously) on an `Proxy` instance. (But it's generally better that our threads use their own exclusive instances of proxy, to minimize shared state and contention.) * Creating and sending method calls (both synchronously and asynchronously) on an `Proxy` instance. (But it's generally better that our threads use their own exclusive instances of proxy, to minimize shared state and contention.)
sdbus-c++ is designed such that all the above operations are thread-safe also on a connection that is running an event loop (usually in a separate thread) at that time. It's an internal thread safety. For example, a signal arrives and is processed by sdbus-c++ even loop at an appropriate `Proxy` instance, while the user is going to destroy that instance in their application thread. The user cannot explicitly control these situations (or they could, but that would be very limiting and cubersome on the API level). sdbus-c++ is designed such that all the above operations are thread-safe also on a connection that is running an event loop (usually in a separate thread) at that time. It's an internal thread safety. For example, a signal arrives and is processed by sdbus-c++ even loop at an appropriate `Proxy` instance, while the user is going to destroy that instance in their application thread. The user cannot explicitly control these situations (or they could, but that would be very limiting and cumbersome on the API level).
However, other combinations, that the user invokes explicitly from within more threads are NOT thread-safe in sdbus-c++ by design, and the user should make sure by their design that these cases never occur. For example, destroying an `Object` instance in one thread while emitting a signal on it in another thread is not thread-safe. In this specific case, the user should make sure in their application that all threads stop working with a specific instance before a thread proceeds with deleting that instance. However, other combinations, that the user invokes explicitly from within more threads are NOT thread-safe in sdbus-c++ by design, and the user should make sure by their design that these cases never occur. For example, destroying an `Object` instance in one thread while emitting a signal on it in another thread is not thread-safe. In this specific case, the user should make sure in their application that all threads stop working with a specific instance before a thread proceeds with deleting that instance.
@ -214,7 +215,7 @@ sdbus-c++ API comes in two layers:
* [the basic layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer), which is a simple wrapper layer on top of sd-bus, using mechanisms that are native to C++ (e.g. serialization/deserialization of data from messages), * [the basic layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer), which is a simple wrapper layer on top of sd-bus, using mechanisms that are native to C++ (e.g. serialization/deserialization of data from messages),
* [the convenience layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer), building on top of the basic layer, which aims at alleviating users from unnecessary details and enables them to write shorter, safer, and more expressive code. * [the convenience layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer), building on top of the basic layer, which aims at alleviating users from unnecessary details and enables them to write shorter, safer, and more expressive code.
sdbus-c++ also ships with a stub generator tool that converts D-Bus IDL in XML format into stub code for the adaptor as well as the proxy part. Hierarchically, these stubs provide yet another layer of convenience (the "stubs layer"), making it possible for D-Bus RPC calls to completely look like native C++ calls on a local object. sdbus-c++ also ships with sdbus-c++-xml2cpp tool that converts D-Bus IDL in XML format into C++ bindings for the adaptor as well as the proxy part. This is the highest level of API provided by sdbus-c++ (the "C++ bindings layer"), which makes it possible for D-Bus RPC calls to completely look like native C++ calls on a local object.
An example: Number concatenator An example: Number concatenator
------------------------------- -------------------------------
@ -306,7 +307,7 @@ int main(int argc, char *argv[])
} }
``` ```
We establish a D-Bus sytem connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We register interfaces, its methods, signals that the object provides, and, through `finishRegistration()`, export the object (i.e., make it visible to clients) on the bus. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests. We establish a D-Bus system connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We register interfaces, its methods, signals that the object provides, and, through `finishRegistration()`, export the object (i.e., make it visible to clients) on the bus. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests.
The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall call)`. The `call` parameter is the incoming method call message. We need to deserialize our method input arguments from it. Then we can invoke the logic of the method and get the results. Then for the given `call`, we create a `reply` message, pack results into it and send it back to the caller through `send()`. (If we had a void-returning method, we'd just send an empty `reply` back.) We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`. The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall call)`. The `call` parameter is the incoming method call message. We need to deserialize our method input arguments from it. Then we can invoke the logic of the method and get the results. Then for the given `call`, we create a `reply` message, pack results into it and send it back to the caller through `send()`. (If we had a void-returning method, we'd just send an empty `reply` back.) We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`.
@ -410,19 +411,23 @@ On the **server** side, we generally need to create D-Bus objects and publish th
* or in a non-blocking async way, through `enterEventLoopAsync()`, * 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. * 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.
The object takes the D-Bus connection as a reference in its constructor. This is the only way to wire connection and object together. We must make sure the connection exists as long as objects using it exist.
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. 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.
#### Using D-Bus connections on the client side #### Using D-Bus connections on the client side
On the **client** side we have more options when creating D-Bus proxies. That corresponds to three overloads of the `createProxy()` factory: On the **client** side we likewise need a connection -- just that unlike on the server side, we don't need to request a unique bus name on it. We have more options here when creating a proxy:
* 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. * Pass an already existing connection as a reference. This is the typical approach when the application already maintains a D-Bus connection (maybe it provide D-Bus API on it, and/or it already has some proxies hooked on it). The proxy will share the connection with others. 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): * Or -- and this is typical when we have a simple D-Bus client application -- we have another option: we let the proxy maintain its own connection (and potentially an associated event loop thread, see below):
* 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). * 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. * 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. Quite **simple**, but as you can see, this hurts scalability in case of many proxies, as each would spawn and maintain its own event loop thread (see discussion higher above). But we don't necessarily need an event loop thread, in case our proxy doesn't need to listen to signals or async method call replies. Read on.
It's also possible in this case to instruct the proxy to **not spawn an event loop thread** for its connection. There are many situations that we want to quickly create a proxy, carry out one or a few (synchronous) D-Bus calls, and let go of proxy. We call them light-weight proxies. For that purpose, spawning a new event loop thread comes with time and resource penalty, for nothing. To create such **a light-weight proxy**, use the factory/constructor overload with `dont_run_event_loop_thread_t`. All in above two bullet sub-points holds; the proxy just won't spawn a thread with an event loop in it. Note that such a proxy can be used only for synchronous D-Bus calls; it may not receive signals or async call replies.
#### Stopping I/O event loops graciously #### Stopping I/O event loops graciously
@ -460,7 +465,7 @@ int main(int argc, char *argv[])
// Create concatenator D-Bus object. // Create concatenator D-Bus object.
const char* objectPath = "/org/sdbuscpp/concatenator"; const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenator = sdbus::createObject(*connection, objectPath); auto concatenator = sdbus::createObject(*connection, objectPath);
auto concatenate = [&concatenator](const std::vector<int> numbers, const std::string& separator) auto concatenate = [&concatenator](const std::vector<int> numbers, const std::string& separator)
{ {
// Return error if there are no numbers in the collection // Return error if there are no numbers in the collection
@ -549,7 +554,7 @@ int main(int argc, char *argv[])
When registering methods, calling methods or emitting signals, multiple lines of code have shrunk into simple one-liners. Signatures of provided callbacks are introspected and types of provided arguments are deduced at compile time, so the D-Bus signatures as well as serialization and deserialization of arguments to and from D-Bus messages are generated for us completely by the compiler. When registering methods, calling methods or emitting signals, multiple lines of code have shrunk into simple one-liners. Signatures of provided callbacks are introspected and types of provided arguments are deduced at compile time, so the D-Bus signatures as well as serialization and deserialization of arguments to and from D-Bus messages are generated for us completely by the compiler.
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. We recommend that sdbus-c++ users prefer the convenience API to the lower level, basic API. When feasible, using generated adaptor and proxy C++ bindings is even better as it provides yet slightly higher abstraction built on top of the convenience API, where remote calls look simply like local, native calls of object methods. They are described in the following section.
> **_Note_:** By default, signal callback handlers are not invoked (i.e., the signal is silently dropped) if there is a signal signature mismatch. If clients want to be informed of such situations, they can prepend `const sdbus::Error*` parameter to their signal callback handler's parameter list. This argument will be `nullptr` in normal cases, and will provide access to the corresponding `sdbus::Error` object in case of deserialization failures. An example of a handler with the signature (`int`) different from the real signal contents (`string`): > **_Note_:** By default, signal callback handlers are not invoked (i.e., the signal is silently dropped) if there is a signal signature mismatch. If clients want to be informed of such situations, they can prepend `const sdbus::Error*` parameter to their signal callback handler's parameter list. This argument will be `nullptr` in normal cases, and will provide access to the corresponding `sdbus::Error` object in case of deserialization failures. An example of a handler with the signature (`int`) different from the real signal contents (`string`):
> ```c++ > ```c++
@ -582,7 +587,7 @@ Yes, there is -- we can access the corresponding D-Bus message in:
* property set implementation callback handlers (server side), * property set implementation callback handlers (server side),
* signal callback handlers (client side). * signal callback handlers (client side).
Both `IObject` and `IProxy` provide the `getCurrentlyProcessedMessage()` method. This method is meant to be called from within a callback handler. It returns a pointer to the corresponding D-Bus message that caused invocation of the handler. The pointer is only valid (dereferencable) as long as the flow of execution does not leave the callback handler. When called from other contexts/threads, the pointer may be both zero or non-zero, and its dereferencing is undefined behavior. Both `IObject` and `IProxy` provide the `getCurrentlyProcessedMessage()` method. This method is meant to be called from within a callback handler. It returns a pointer to the corresponding D-Bus message that caused invocation of the handler. The pointer is only valid (dereferenceable) as long as the flow of execution does not leave the callback handler. When called from other contexts/threads, the pointer may be both zero or non-zero, and its dereferencing is undefined behavior.
An excerpt of the above example of concatenator modified to print out a name of the sender of method call: An excerpt of the above example of concatenator modified to print out a name of the sender of method call:
@ -596,15 +601,15 @@ An excerpt of the above example of concatenator modified to print out a name of
}; };
``` ```
Implementing the Concatenator example using sdbus-c++-generated stubs Implementing the Concatenator example using generated C++ bindings
--------------------------------------------------------------------- ------------------------------------------------------------------
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. sdbus-c++ ships with native C++ binding 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: 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:
```bash ```bash
sdbus-c++-xml2cpp database-bindings.xml --adaptor=database-server-glue.h --proxy=database-client-glue.h sdbus-c++-xml2cpp concatenator-bindings.xml --adaptor=concatenator-server-glue.h --proxy=concatenator-client-glue.h
``` ```
The adaptor header file contains classes that can be used to implement interfaces described in the IDL (these classes represent object interfaces). The proxy header file contains classes that can be used to make calls to remote objects (these classes represent remote object interfaces). The adaptor header file contains classes that can be used to implement interfaces described in the IDL (these classes represent object interfaces). The proxy header file contains classes that can be used to make calls to remote objects (these classes represent remote object interfaces).
@ -630,12 +635,14 @@ As an example, let's look at an XML description of our Concatenator's interfaces
</node> </node>
``` ```
After running this through the stubs generator, we get the stub code that is described in the following two subsections. After running this through the code generator, we get the generated code that is described in the following two subsections.
### concatenator-server-glue.h ### concatenator-server-glue.h
For each interface in the XML IDL file the generator creates one class that represents it. The class is de facto an interface which shall be implemented by the class inheriting it. The class' constructor takes care of registering all methods, signals and properties. For each D-Bus method there is a pure virtual member function. These pure virtual functions must be implemented in the child class. For each signal, there is a public function member that emits this signal. For each interface in the XML IDL file the generator creates one class that represents it. The class is de facto an interface which shall be implemented by the class inheriting it. The class' constructor takes care of registering all methods, signals and properties. For each D-Bus method there is a pure virtual member function. These pure virtual functions must be implemented in the child class. For each signal, there is a public function member that emits this signal.
Generated adaptor classes support move semantics. They are moveable but not copyable.
```cpp ```cpp
/* /*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
@ -658,25 +665,30 @@ public:
protected: protected:
Concatenator_adaptor(sdbus::IObject& object) Concatenator_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
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_->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"); object_->registerSignal("concatenated").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
} }
Concatenator_adaptor(const Concatenator_adaptor&) = delete;
Concatenator_adaptor& operator=(const Concatenator_adaptor&) = delete;
Concatenator_adaptor(Concatenator_adaptor&&) = default;
Concatenator_adaptor& operator=(Concatenator_adaptor&&) = default;
~Concatenator_adaptor() = default; ~Concatenator_adaptor() = default;
public: public:
void emitConcatenated(const std::string& concatenatedString) void emitConcatenated(const std::string& concatenatedString)
{ {
object_.emitSignal("concatenated").onInterface(INTERFACE_NAME).withArguments(concatenatedString); object_->emitSignal("concatenated").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
} }
private: private:
virtual std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) = 0; virtual std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}} // namespaces }} // namespaces
@ -686,7 +698,9 @@ private:
### concatenator-client-glue.h ### concatenator-client-glue.h
Analogously to the adaptor classes described above, there is one class generated for one interface in the XML IDL file. The class is de facto a proxy to the concrete single interface of a remote object. For each D-Bus signal there is a pure virtual member function whose body must be provided in a child class. For each method, there is a public function member that calls the method remotely. Analogously to the adaptor classes described above, there is one proxy class generated for one interface in the XML IDL file. The class is de facto a proxy to the concrete single interface of a remote object. For each D-Bus signal there is a pure virtual member function whose body must be provided in a child class. For each method, there is a public function member that calls the method remotely.
Generated proxy classes support move semantics. They are moveable but not copyable.
```cpp ```cpp
/* /*
@ -710,11 +724,16 @@ public:
protected: protected:
Concatenator_proxy(sdbus::IProxy& proxy) Concatenator_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
proxy_.uponSignal("concatenated").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); }); proxy_->uponSignal("concatenated").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
} }
Concatenator_proxy(const Concatenator_proxy&) = delete;
Concatenator_proxy& operator=(const Concatenator_proxy&) = delete;
Concatenator_proxy(Concatenator_proxy&&) = default;
Concatenator_proxy& operator=(Concatenator_proxy&&) = default;
~Concatenator_proxy() = default; ~Concatenator_proxy() = default;
virtual void onConcatenated(const std::string& concatenatedString) = 0; virtual void onConcatenated(const std::string& concatenatedString) = 0;
@ -723,12 +742,12 @@ public:
std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)
{ {
std::string result; std::string result;
proxy_.callMethod("concatenate").onInterface(INTERFACE_NAME).withArguments(numbers, separator).storeResultsTo(result); proxy_->callMethod("concatenate").onInterface(INTERFACE_NAME).withArguments(numbers, separator).storeResultsTo(result);
return result; return result;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
}} // namespaces }} // namespaces
@ -746,7 +765,7 @@ In our object class we need to:
* Give an implementation to the D-Bus object's methods by overriding corresponding virtual functions, * Give an implementation to the D-Bus object's methods by overriding corresponding virtual functions,
* call `registerAdaptor()` in the constructor, which makes the adaptor (the D-Bus object underneath it) available for remote calls, * call `registerAdaptor()` in the constructor, which makes the adaptor (the D-Bus object underneath it) available for remote calls,
* call `unregisterAdaptor()`, which, conversely, unregisters the adaptor from the bus. * call `unregisterAdaptor()`, which, conversely, deregisters the adaptor from the bus.
Calling `registerAdaptor()` and `unregisterAdaptor()` was not necessary in previous sdbus-c++ versions, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders. Calling `registerAdaptor()` and `unregisterAdaptor()` was not necessary in previous sdbus-c++ versions, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders.
@ -825,7 +844,7 @@ In our proxy class we need to:
* Give an implementation to signal handlers and asynchronous method reply handlers (if any) by overriding corresponding virtual functions, * Give an implementation to signal handlers and asynchronous method reply handlers (if any) by overriding corresponding virtual functions,
* call `registerProxy()` in the constructor, which makes the proxy (the D-Bus proxy object underneath it) ready to receive signals and async call replies, * call `registerProxy()` in the constructor, which makes the proxy (the D-Bus proxy object underneath it) ready to receive signals and async call replies,
* call `unregisterProxy()`, which, conversely, unregisters the proxy from the bus. * call `unregisterProxy()`, which, conversely, deregisters the proxy from the bus.
Calling `registerProxy()` and `unregisterProxy()` was not necessary in previous versions of sdbus-c++, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders. Calling `registerProxy()` and `unregisterProxy()` was not necessary in previous versions of sdbus-c++, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders.
@ -983,7 +1002,7 @@ Callbacks of async methods based on convenience sdbus-c++ API have slightly diff
* The result holder is of type `Result<Types...>&&`, where `Types...` is a list of method output argument types. * The result holder is of type `Result<Types...>&&`, where `Types...` is a list of method output argument types.
* The result object must be the first physical parameter of the callback taken by r-value ref. `Result` class template is move-only. * The result object must be the first physical parameter of the callback taken by r-value ref. `Result` class template is move-only.
* The callback itself is physically a void-returning function. * The callback itself is physically a void-returning function.
* Method input arguments are taken by value rathern than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, this falls back to copying. * Method input arguments are taken by value rather than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, this falls back to copying.
So the concatenate callback signature would change from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator)`: So the concatenate callback signature would change from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator)`:
@ -1018,11 +1037,11 @@ void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbe
The `Result` is a convenience class that represents a future method result, and it is where we write the results (`returnResults()`) or an error (`returnError()`) which we want to send back to the client. The `Result` is a convenience class that represents a future method result, and it is where we write the results (`returnResults()`) or an error (`returnError()`) which we want to send back to the client.
Registraion (`implementedAs()`) doesn't change. Nothing else needs to change. Registration (`implementedAs()`) doesn't change. Nothing else needs to change.
### Marking server-side async methods in the IDL ### Marking server-side async methods in the IDL
sdbus-c++ stub generator can generate stub code for server-side async methods. We just need to annotate the method with `org.freedesktop.DBus.Method.Async`. The annotation element value must be either `server` (async method on server-side only) or `clientserver` (async method on both client- and server-side): sdbus-c++-xml2cpp tool can generate C++ code for server-side async methods. We just need to annotate the method with `org.freedesktop.DBus.Method.Async`. The annotation element value must be either `server` (async method on server-side only) or `client-server` (async method on both client- and server-side):
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -1047,11 +1066,11 @@ For a real example of a server-side asynchronous D-Bus method, please look at sd
Asynchronous client-side methods Asynchronous client-side methods
-------------------------------- --------------------------------
sdbus-c++ also supports asynchronous approach at the client (the proxy) side. With this approach, we can issue a D-Bus method call without blocking current thread's execution while waiting for the reply. We go on doing other things, and when the reply comes, a given callback is invoked within the context of the D-Bus dispatcher thread. sdbus-c++ also supports asynchronous approach at the client (the proxy) side. With this approach, we can issue a D-Bus method call without blocking current thread's execution while waiting for the reply. We go on doing other things, and when the reply comes, either a given callback handler will be invoked within the context of the event loop thread, or a future object returned by the async call will be set the returned value.6
### Lower-level API ### Lower-level API
Considering the Concatenator example based on lower-level API, if we wanted to call `concatenate` in an async way, we'd have to pass a callback to the proxy when issuing the call, and that callback gets invoked when the reply arrives: Considering the Concatenator example based on lower-level API, if we wanted to call `concatenate` in an async way, we have two options: We either pass a callback to the proxy when issuing the call, and that callback gets invoked when the reply arrives:
```c++ ```c++
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -1096,9 +1115,34 @@ int main(int argc, char *argv[])
The callback is a void-returning function taking two arguments: a reference to the reply message, and a pointer to the prospective `sdbus::Error` instance. Zero `Error` pointer means that no D-Bus error occurred while making the call, and the reply message contains valid reply. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred. Error name and message can then be read out by the client from that instance. The callback is a void-returning function taking two arguments: a reference to the reply message, and a pointer to the prospective `sdbus::Error` instance. Zero `Error` pointer means that no D-Bus error occurred while making the call, and the reply message contains valid reply. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred. Error name and message can then be read out by the client from that instance.
There is also an overload of this `IProxy::callMethod()` function taking method call timeout argument.
Another option is to use `std::future`-based overload of the `IProxy::callMethod()` function. A future object will be returned which will later, when the reply arrives, be set to contain the returned reply message. Or if the call returns an error, `sdbus::Error` will be thrown by `std::future::get()`.
```c++
...
// Invoke concatenate on given interface of the object
{
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
method << numbers << separator;
auto future = concatenatorProxy->callMethod(method, sdbus::with_future);
try
{
auto reply = future.get(); // This will throw if call ends with an error
std::string result;
reply >> result;
std::cout << "Got concatenate result: " << result << std::endl;
}
catch (const sdbus::Error& e)
{
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
}
}
```
### Convenience API ### Convenience API
On the convenience API level, the call statement starts with `callMethodAsync()`, and ends with `uponReplyInvoke()` that takes a callback handler. The callback is a void-returning function that takes at least one argument: pointer to the `sdbus::Error` instance. All subsequent arguments shall exactly reflect the D-Bus method output arguments. A concatenator example: On the convenience API level, the call statement starts with `callMethodAsync()`, and one option is to finish the statement with `uponReplyInvoke()` that takes a callback handler. The callback is a void-returning function that takes at least one argument: pointer to the `sdbus::Error` instance. All subsequent arguments shall exactly reflect the D-Bus method output arguments. A concatenator example:
```c++ ```c++
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -1133,9 +1177,28 @@ int main(int argc, char *argv[])
When the `Error` pointer is zero, it means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred during the call (and subsequent arguments are simply default-constructed). Error name and message can then be read out by the client from `Error` instance. When the `Error` pointer is zero, it means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred during the call (and subsequent arguments are simply default-constructed). Error name and message can then be read out by the client from `Error` instance.
Another option is to finish the async call statement with `getResultAsFuture()`, which is a template function which takes the list of types returned by the D-Bus method (empty list in case of `void`-returning method) which returns a `std::future` object, which will later, when the reply arrives, be set to contain the return value(s). Or if the call returns an error, `sdbus::Error` will be thrown by `std::future::get()`.
The future object will contain void for a void-returning D-Bus method, a single type for a single value returning D-Bus method, and a `std::tuple` to hold multiple return values of a D-Bus method.
```c++
...
auto future = concatenatorProxy->callMethodAsync("concatenate").onInterface(interfaceName).withArguments(numbers, separator).getResultAsFuture<std::string>();
try
{
auto concatenatedString = future.get(); // This waits for the reply
std::cout << "Got concatenate result: " << concatenatedString << std::endl;
}
catch (const sdbus::Error& e)
{
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
}
...
```
### Marking client-side async methods in the IDL ### Marking client-side async methods in the IDL
sdbus-c++ stub generator can generate stub code for client-side async methods. We just need to annotate the method with `org.freedesktop.DBus.Method.Async`. The annotation element value must be either `client` (async on the client-side only) or `clientserver` (async method on both client- and server-side): sdbus-c++-xml2cpp can generate C++ code for client-side async methods. We just need to annotate the method with `org.freedesktop.DBus.Method.Async`. The annotation element value must be either `client` (async on the client-side only) or `client-server` (async method on both client- and server-side):
```xml ```xml
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -1155,11 +1218,19 @@ sdbus-c++ stub generator can generate stub code for client-side async methods. W
</node> </node>
``` ```
An asynchronous method can be generated as a callback-based method or `std::future`-based method. This can optionally be customized through an additional `org.freedesktop.DBus.Method.Async.ClientImpl` annotation. Its supported values are `callback` and `std::future`. The default behavior is callback-based method.
#### Generating callback-based async methods
For each client-side async method, a corresponding `on<MethodName>Reply` pure virtual function, where `<MethodName>` is the capitalized D-Bus method name, is generated in the generated proxy class. This function is the callback invoked when the D-Bus method reply arrives, and must be provided a body by overriding it in the implementation class. For each client-side async method, a corresponding `on<MethodName>Reply` pure virtual function, where `<MethodName>` is the capitalized D-Bus method name, is generated in the generated proxy class. This function is the callback invoked when the D-Bus method reply arrives, and must be provided a body by overriding it in the implementation class.
So in the specific example above, the stub generator will generate a `Concatenator_proxy` class similar to one shown in a [dedicated section above](#concatenator-client-glueh), with the difference that it will also generate an additional `virtual void onConcatenateReply(const sdbus::Error* error, const std::string& concatenatedString);` method, which we shall override in derived `ConcatenatorProxy`. So in the specific example above, the tool will generate a `Concatenator_proxy` class similar to one shown in a [dedicated section above](#concatenator-client-glueh), with the difference that it will also generate an additional `virtual void onConcatenateReply(const sdbus::Error* error, const std::string& concatenatedString);` method, which we shall override in derived `ConcatenatorProxy`.
For a real example of a client-side asynchronous D-Bus method, please look at sdbus-c++ [stress tests](/tests/stresstests). #### Generating std:future-based async methods
In this case, a `std::future` is returned by the method, which will later, when the reply arrives, get set to contain the return value. Or if the call returns an error, `sdbus::Error` will be thrown by `std::future::get()`.
For a real example of a client-side asynchronous D-Bus methods, please look at sdbus-c++ [stress tests](/tests/stresstests).
## Method call timeout ## Method call timeout
@ -1205,7 +1276,7 @@ An example of a read-write property `status`:
</node> </node>
``` ```
### Generated stubs ### Generated C++ bindings
This is how generated adaptor and proxy classes would look like with the read-write `status` property. The adaptor: This is how generated adaptor and proxy classes would look like with the read-write `status` property. The adaptor:
@ -1216,9 +1287,9 @@ class PropertyProvider_adaptor
public: public:
PropertyProvider_adaptor(sdbus::IObject& object) PropertyProvider_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
object_.registerProperty("status").onInterface(INTERFACE_NAME).withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); }); object_->registerProperty("status").onInterface(INTERFACE_NAME).withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); });
} }
~PropertyProvider_adaptor() = default; ~PropertyProvider_adaptor() = default;
@ -1245,13 +1316,13 @@ public:
// getting the property value // getting the property value
uint32_t status() uint32_t status()
{ {
return object_.getProperty("status").onInterface(INTERFACE_NAME); return object_->getProperty("status").onInterface(INTERFACE_NAME);
} }
// setting the property value // setting the property value
void status(const uint32_t& value) void status(const uint32_t& value)
{ {
object_.setProperty("status").onInterface(INTERFACE_NAME).toValue(value); object_->setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
} }
/*...*/ /*...*/
@ -1280,6 +1351,149 @@ Note that signals of afore-mentioned standard D-Bus interfaces are not emitted b
Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp) or the [examples](/examples) directory. Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp) or the [examples](/examples) directory.
Representing D-Bus Types in sdbus-c++
-------------------------------------
sdbus-c++ provides many default, pre-defined C++ type representations for D-Bus types. The table below shows which C++ type corresponds to which D-Bus type.
| Category | Code | Code ASCII | Conventional Name | C++ Type |
|---------------------|-------------|------------|--------------------|---------------------------------|
| reserved | 0 | NUL | INVALID | - |
| fixed, basic | 121 | y | BYTE | `uint8_t` |
| fixed, basic | 98 | b | BOOLEAN | `bool` |
| fixed, basic | 110 | n | INT16 | `int16_t` |
| fixed, basic | 113 | q | UINT16 | `uint16_t` |
| fixed, basic | 105 | i | INT32 | `int32_t` |
| fixed, basic | 117 | u | UINT32 | `uint32_t` |
| fixed, basic | 120 | x | INT64 | `int64_t` |
| fixed, basic | 116 | t | UINT64 | `uint64_t` |
| fixed, basic | 100 | d | DOUBLE | `double` |
| string-like, basic | 115 | s | STRING | `const char*`, `std::string` |
| string-like, basic | 111 | o | OBJECT_PATH | `sdbus::ObjectPath` |
| string-like, basic | 103 | g | SIGNATURE | `sdbus::Signature` |
| container | 97 | a | ARRAY | `std::vector<T>`, `std::array<T>`, `std::span<T>` - if used as an array followed by a single complete type `T` <br /> `std::map<T1, T2>`, `std::unordered_map<T1, T2>` - if used as an array of dict entries |
| container | 114,40,41 | r() | STRUCT | `sdbus::Struct<T1, T2, ...>` variadic class template |
| container | 118 | v | VARIANT | `sdbus::Variant` |
| container | 101,123,125 | e{} | DICT_ENTRY | - |
| fixed, basic | 104 | h | UNIX_FD | `sdbus::UnixFd` |
| reserved | 109 | m | (reserved) | - |
| reserved | 42 | * | (reserved) | - |
| reserved | 63 | ? | (reserved) | - |
| reserved | 64,38,94 | @&^ | (reserved) | - |
A few examples:
* The D-Bus signature of an output argument of method `GetManagedObjects()` on standard interface `org.freedesktop.DBus.ObjectManager` is `a{oa{sa{sv}}}`. For this the corresponding C++ method return type is: `std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>>`.
* Or an input argument of method `InterfacesRemoved` on that interface has signature `as`. Ths corresponds to the C++ parameter of type `std::vector<std::string>`.
* Or a D-Bus signature `a(bdh)` corresponds to the array of D-Bus structures: `std::vector<sdbus::Struct<bool, double, sdbus::UnixFd>>`.
To see how C++ types are mapped to D-Bus types (including container types) in sdbus-c++, have a look at individual [specializations of `sdbus::signature_of` class template](https://github.com/Kistler-Group/sdbus-cpp/blob/master/include/sdbus-c%2B%2B/TypeTraits.h#L87) in TypeTraits.h header file. For more examples of type mappings, look into [TypeTraits unit tests](https://github.com/Kistler-Group/sdbus-cpp/blob/master/tests/unittests/TypeTraits_test.cpp#L62).
For more information on basic D-Bus types, D-Bus container types, and D-Bus type system in general, make sure to consult the [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#type-system).
### Extending sdbus-c++ type system
The above mapping between D-Bus and C++ types is what sdbus-c++ provides by default. However, the mapping can be extended. Clients can implement additional mapping between a D-Bus type and their custom type.
We need two things to do that:
* implement `sdbus::Message` insertion and extraction operators, so sdbus-c++ knows how to serialize/deserialize our custom type,
* specialize `sdbus::signature_of` template for our custom type, so sdbus-c++ knows the mapping to D-Bus type and other necessary information about our type.
Say, we would like to represent D-Bus arrays as `std::list`s in our application. Since sdbus-c++ comes with pre-defined support for `std::vector`s, `std::array`s and `std::span`s as D-Bus array representations, we have to provide an extension:
```c++
#include <list>
#include <sdbus-c++/sdbus-c++.h>
namespace sdbus {
// Implementing serialization for std::list
template <typename _ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
{
msg.openContainer(sdbus::signature_of<_ElementType>::str());
for (const auto& item : items)
msg << item;
msg.closeContainer();
return msg;
}
// Implementing deserialization for std::list
template <typename _ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
{
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
return msg;
while (true)
{
_ElementType elem;
if (msg >> elem)
items.emplace_back(std::move(elem));
else
break;
}
msg.clearFlags();
msg.exitContainer();
return msg;
}
} // namespace sdbus
// Implementing type traits for std::list, and since we map it to D-Bus array,
// we can re-use std::vector type traits because it's the same stuff.
template <typename _Element, typename _Allocator>
struct sdbus::signature_of<std::list<_Element, _Allocator>>
: sdbus::signature_of<std::vector<_Element, _Allocator>>
{};
};
```
Then we can simply use `std::list`s, serialize/deserialize them in a D-Bus message, in D-Bus method calls or return values... and they will be simply transmitted as D-Bus arrays.
As another example, say we have our custom type `my::Struct` which we'd like to use as a D-Bus structure representation (sdbus-c++ provides `sdbus::Struct` type for that, but we don't want to use it because using our custom type directly is more convenient). Again, we have to provide type traits and message serialization/deserialization functions for our custom type. We build our functions and specializations on top of `sdbus::Struct`, so we don't have to copy and write a lot of boiler-plate:
```c++
namespace my {
struct Struct
{
int i;
std::string s;
std::list<double> l;
};
sdbus::Message& operator<<(sdbus::Message& msg, const Struct& items)
{
// Re-use sdbus::Struct functionality for simplicity -- view of my::Struct through sdbus::Struct with reference types
return msg << sdbus::Struct{std::forward_as_tuple(items.i, items.s, items.l)};
}
sdbus::Message& operator>>(sdbus::Message& msg, Struct& items)
{
// Re-use sdbus::Struct functionality for simplicity -- view of my::Struct through sdbus::Struct with reference types
sdbus::Struct s{std::forward_as_tuple(items.i, items.s, items.l)};
return msg >> s;
}
}
template <>
struct sdbus::signature_of<my::Struct>
: sdbus::signature_of<sdbus::Struct<int, std::string, std::list<double>>>
{};
```
> **_Note_:** One of `my::Struct` members is `std::list`. Thanks to the above custom support for `std::list`, it's now automatically accepted by sdbus-c++ as a D-Bus array representation.
Live examples of extending sdbus-c++ types can be found in [Message unit tests](/tests/unittests/Message_test.cpp).
Support for match rules Support for match rules
----------------------- -----------------------

View File

@ -141,6 +141,11 @@ namespace sdbus {
protected: protected:
using base_type = AdaptorInterfaces; using base_type = AdaptorInterfaces;
AdaptorInterfaces(const AdaptorInterfaces&) = delete;
AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete;
AdaptorInterfaces(AdaptorInterfaces&&) = default;
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = default;
~AdaptorInterfaces() = default; ~AdaptorInterfaces() = default;
}; };

View File

@ -34,6 +34,7 @@
#include <vector> #include <vector>
#include <type_traits> #include <type_traits>
#include <chrono> #include <chrono>
#include <future>
#include <cstdint> #include <cstdint>
// Forward declarations // Forward declarations
@ -50,7 +51,7 @@ namespace sdbus {
class MethodRegistrator class MethodRegistrator
{ {
public: public:
MethodRegistrator(IObject& object, const std::string& methodName); MethodRegistrator(IObject& object, std::string methodName);
MethodRegistrator(MethodRegistrator&& other) = default; MethodRegistrator(MethodRegistrator&& other) = default;
~MethodRegistrator() noexcept(false); ~MethodRegistrator() noexcept(false);
@ -66,7 +67,7 @@ namespace sdbus {
private: private:
IObject& object_; IObject& object_;
const std::string& methodName_; std::string methodName_;
std::string interfaceName_; std::string interfaceName_;
std::string inputSignature_; std::string inputSignature_;
std::vector<std::string> inputParamNames_; std::vector<std::string> inputParamNames_;
@ -80,7 +81,7 @@ namespace sdbus {
class SignalRegistrator class SignalRegistrator
{ {
public: public:
SignalRegistrator(IObject& object, const std::string& signalName); SignalRegistrator(IObject& object, std::string signalName);
SignalRegistrator(SignalRegistrator&& other) = default; SignalRegistrator(SignalRegistrator&& other) = default;
~SignalRegistrator() noexcept(false); ~SignalRegistrator() noexcept(false);
@ -92,7 +93,7 @@ namespace sdbus {
private: private:
IObject& object_; IObject& object_;
const std::string& signalName_; std::string signalName_;
std::string interfaceName_; std::string interfaceName_;
std::string signalSignature_; std::string signalSignature_;
std::vector<std::string> paramNames_; std::vector<std::string> paramNames_;
@ -195,6 +196,10 @@ namespace sdbus {
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout); AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args); template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback); template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
// Returned future will be std::future<void> for no (void) D-Bus method return value
// or std::future<T> for single D-Bus method return value
// or std::future<std::tuple<...>> for multiple method return values
template <typename... _Args> std::future<future_return_t<_Args...>> getResultAsFuture();
private: private:
IProxy& proxy_; IProxy& proxy_;

View File

@ -45,9 +45,9 @@ namespace sdbus {
/*** MethodRegistrator ***/ /*** MethodRegistrator ***/
/*** ----------------- ***/ /*** ----------------- ***/
inline MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName) inline MethodRegistrator::MethodRegistrator(IObject& object, std::string methodName)
: object_(object) : object_(object)
, methodName_(methodName) , methodName_(std::move(methodName))
, exceptions_(std::uncaught_exceptions()) , exceptions_(std::uncaught_exceptions())
{ {
} }
@ -73,9 +73,9 @@ namespace sdbus {
object_.registerMethod( interfaceName_ object_.registerMethod( interfaceName_
, std::move(methodName_) , std::move(methodName_)
, std::move(inputSignature_) , std::move(inputSignature_)
, std::move(inputParamNames_) , inputParamNames_
, std::move(outputSignature_) , std::move(outputSignature_)
, std::move(outputParamNames_) , outputParamNames_
, std::move(methodCallback_) , std::move(methodCallback_)
, std::move(flags_)); , std::move(flags_));
} }
@ -177,9 +177,9 @@ namespace sdbus {
/*** SignalRegistrator ***/ /*** SignalRegistrator ***/
/*** ----------------- ***/ /*** ----------------- ***/
inline SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName) inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
: object_(object) : object_(object)
, signalName_(signalName) , signalName_(std::move(signalName))
, exceptions_(std::uncaught_exceptions()) , exceptions_(std::uncaught_exceptions())
{ {
} }
@ -204,7 +204,7 @@ namespace sdbus {
object_.registerSignal( interfaceName_ object_.registerSignal( interfaceName_
, std::move(signalName_) , std::move(signalName_)
, std::move(signalSignature_) , std::move(signalSignature_)
, std::move(paramNames_) , paramNames_
, std::move(flags_) ); , std::move(flags_) );
} }
@ -610,6 +610,29 @@ namespace sdbus {
return proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_); return proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
} }
template <typename... _Args>
std::future<future_return_t<_Args...>> AsyncMethodInvoker::getResultAsFuture()
{
auto promise = std::make_shared<std::promise<future_return_t<_Args...>>>();
auto future = promise->get_future();
uponReplyInvoke([promise = std::move(promise)](const Error* error, _Args... args)
{
if (error == nullptr)
if constexpr (!std::is_void_v<future_return_t<_Args...>>)
promise->set_value({std::move(args)...});
else
promise->set_value();
else
promise->set_exception(std::make_exception_ptr(*error));
});
// Will be std::future<void> for no D-Bus method return value
// or std::future<T> for single D-Bus method return value
// or std::future<std::tuple<...>> for multiple method return values
return future;
}
/*** ---------------- ***/ /*** ---------------- ***/
/*** SignalSubscriber ***/ /*** SignalSubscriber ***/
/*** ---------------- ***/ /*** ---------------- ***/

View File

@ -93,7 +93,7 @@ namespace sdbus {
private: private:
std::bitset<FLAG_COUNT> flags_; std::bitset<FLAG_COUNT> flags_;
}; };
} }
#endif /* SDBUS_CXX_FLAGS_H_ */ #endif /* SDBUS_CXX_FLAGS_H_ */

View File

@ -293,7 +293,6 @@ namespace sdbus {
* *
* @param[in] match Match expression to filter incoming D-Bus message * @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule * @param[in] callback Callback handler to be called upon incoming D-Bus message matching the rule
* @param[in] Floating slot tag
* *
* The method installs a floating match rule for messages received on the specified bus connection. * The method installs a floating match rule for messages received on the specified bus connection.
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule * Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
@ -314,21 +313,21 @@ namespace sdbus {
[[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop(); [[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop();
/*! /*!
* @copydoc IConnection::enterProcessingLoopAsync() * @copydoc IConnection::enterEventLoopAsync()
* *
* @deprecated This function has been replaced by enterEventLoopAsync() * @deprecated This function has been replaced by enterEventLoopAsync()
*/ */
[[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync(); [[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync();
/*! /*!
* @copydoc IConnection::leaveProcessingLoop() * @copydoc IConnection::leaveEventLoop()
* *
* @deprecated This function has been replaced by leaveEventLoop() * @deprecated This function has been replaced by leaveEventLoop()
*/ */
[[deprecated("This function has been replaced by leaveEventLoop()")]] void leaveProcessingLoop(); [[deprecated("This function has been replaced by leaveEventLoop()")]] void leaveProcessingLoop();
/*! /*!
* @copydoc IConnection::getProcessLoopPollData() * @copydoc IConnection::getEventLoopPollData()
* *
* @deprecated This function has been replaced by getEventLoopPollData() * @deprecated This function has been replaced by getEventLoopPollData()
*/ */

View File

@ -28,10 +28,12 @@
#define SDBUS_CXX_IPROXY_H_ #define SDBUS_CXX_IPROXY_H_
#include <sdbus-c++/ConvenienceApiClasses.h> #include <sdbus-c++/ConvenienceApiClasses.h>
#include <sdbus-c++/TypeTraits.h>
#include <string> #include <string>
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <chrono> #include <chrono>
#include <future>
// Forward declarations // Forward declarations
namespace sdbus { namespace sdbus {
@ -322,6 +324,33 @@ namespace sdbus {
* @return A pointer to the currently processed D-Bus message * @return A pointer to the currently processed D-Bus message
*/ */
virtual const Message* getCurrentlyProcessedMessage() const = 0; virtual const Message* getCurrentlyProcessedMessage() const = 0;
/*!
* @brief Calls method on the proxied D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @param[in] timeout Timeout for dbus call in microseconds
* @return Cookie for the the pending asynchronous call
*
* 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
* I/O event loop thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual std::future<MethodReply> callMethod(const MethodCall& message, with_future_t) = 0;
virtual std::future<MethodReply> callMethod(const MethodCall& message, uint64_t timeout, with_future_t) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t)
*/
template <typename _Rep, typename _Period>
std::future<MethodReply> callMethod( const MethodCall& message
, const std::chrono::duration<_Rep, _Period>& timeout
, with_future_t );
}; };
/********************************************//** /********************************************//**
@ -382,6 +411,15 @@ namespace sdbus {
return callMethod(message, std::move(asyncReplyCallback), microsecs.count()); return callMethod(message, std::move(asyncReplyCallback), microsecs.count());
} }
template <typename _Rep, typename _Period>
inline std::future<MethodReply> IProxy::callMethod( const MethodCall& message
, const std::chrono::duration<_Rep, _Period>& timeout
, with_future_t )
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return callMethod(message, microsecs.count(), with_future);
}
inline MethodInvoker IProxy::callMethod(const std::string& methodName) inline MethodInvoker IProxy::callMethod(const std::string& methodName)
{ {
return MethodInvoker(*this, methodName); return MethodInvoker(*this, methodName);
@ -458,6 +496,30 @@ namespace sdbus {
, std::string destination , std::string destination
, std::string objectPath ); , std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* The provided connection will be used by the proxy to issue calls against the object.
* The Object proxy becomes an exclusive owner of this connection, but will not start an event loop
* thread on this connection. This is cheap construction and is suitable for short-lived proxies
* created just to execute simple synchronous D-Bus calls and then destroyed. Such blocking request-reply
* calls will work without an event loop (but signals, async calls, etc. won't).
*
* Code example:
* @code
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo", sdbus::dont_run_event_loop_thread);
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t );
/*! /*!
* @brief Creates a proxy object for a specific remote D-Bus object * @brief Creates a proxy object for a specific remote D-Bus object
* *
@ -466,7 +528,7 @@ namespace sdbus {
* @return Pointer to the object proxy instance * @return Pointer to the object proxy instance
* *
* No D-Bus connection is provided here, so the object proxy will create and manage * No D-Bus connection is provided here, so the object proxy will create and manage
* his own connection, and will automatically start a procesing loop upon that connection * his own connection, and will automatically start an event loop upon that connection
* in a separate internal thread. Handlers for incoming signals and asynchronous * in a separate internal thread. Handlers for incoming signals and asynchronous
* method replies will be executed in the context of that thread. * method replies will be executed in the context of that thread.
* *
@ -478,6 +540,28 @@ namespace sdbus {
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination [[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath ); , std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* No D-Bus connection is provided here, so the object proxy will create and manage
* his own connection, but it will not start an event loop thread. This is cheap
* construction and is suitable for short-lived proxies created just to execute simple
* synchronous D-Bus calls and then destroyed. Such blocking request-reply calls
* will work without an event loop (but signals, async calls, etc. won't).
*
* Code example:
* @code
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo", sdbus::dont_run_event_loop_thread );
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t );
} }
#include <sdbus-c++/ConvenienceApiClasses.inl> #include <sdbus-c++/ConvenienceApiClasses.inl>

View File

@ -31,7 +31,12 @@
#include <sdbus-c++/Error.h> #include <sdbus-c++/Error.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <map> #include <map>
#include <unordered_map>
#include <utility> #include <utility>
#include <cstdint> #include <cstdint>
#include <cassert> #include <cassert>
@ -86,6 +91,23 @@ namespace sdbus {
Message& operator<<(const Signature &item); Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item); Message& operator<<(const UnixFd &item);
template <typename _Element, typename _Allocator>
Message& operator<<(const std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator<<(const std::array<_Element, _Size>& items);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator<<(const std::span<_Element, _Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator<<(const Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
Message& operator<<(const std::tuple<_ValueTypes...>& item);
Message& operator>>(bool& item); Message& operator>>(bool& item);
Message& operator>>(int16_t& item); Message& operator>>(int16_t& item);
Message& operator>>(int32_t& item); Message& operator>>(int32_t& item);
@ -101,6 +123,22 @@ namespace sdbus {
Message& operator>>(ObjectPath &item); Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item); Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item); Message& operator>>(UnixFd &item);
template <typename _Element, typename _Allocator>
Message& operator>>(std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator>>(std::array<_Element, _Size>& items);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator>>(std::span<_Element, _Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator>>(Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
Message& operator>>(std::tuple<_ValueTypes...>& item);
Message& openContainer(const std::string& signature); Message& openContainer(const std::string& signature);
Message& closeContainer(); Message& closeContainer();
@ -120,6 +158,9 @@ namespace sdbus {
Message& enterStruct(const std::string& signature); Message& enterStruct(const std::string& signature);
Message& exitStruct(); Message& exitStruct();
Message& appendArray(char type, const void *ptr, size_t size);
Message& readArray(char type, const void **ptr, size_t *size);
explicit operator bool() const; explicit operator bool() const;
void clearFlags(); void clearFlags();
@ -131,6 +172,7 @@ namespace sdbus {
void peekType(std::string& type, std::string& contents) const; void peekType(std::string& type, std::string& contents) const;
bool isValid() const; bool isValid() const;
bool isEmpty() const; bool isEmpty() const;
bool isAtEnd(bool complete) const;
void copyTo(Message& destination, bool complete) const; void copyTo(Message& destination, bool complete) const;
void seal(); void seal();
@ -146,6 +188,25 @@ namespace sdbus {
class Factory; class Factory;
private:
template <typename _Array>
void serializeArray(const _Array& items);
template <typename _Array>
void deserializeArray(_Array& items);
template <typename _Array>
void deserializeArrayFast(_Array& items);
template <typename _Element, typename _Allocator>
void deserializeArrayFast(std::vector<_Element, _Allocator>& items);
template <typename _Array>
void deserializeArraySlow(_Array& items);
template <typename _Element, typename _Allocator>
void deserializeArraySlow(std::vector<_Element, _Allocator>& items);
template <typename _Dictionary>
void serializeDictionary(const _Dictionary& items);
template <typename _Dictionary>
void deserializeDictionary(_Dictionary& items);
protected: protected:
Message() = default; Message() = default;
explicit Message(internal::ISdBus* sdbus) noexcept; explicit Message(internal::ISdBus* sdbus) noexcept;
@ -244,38 +305,90 @@ namespace sdbus {
PlainMessage() = default; PlainMessage() = default;
}; };
template <typename _Element> template <typename _Element, typename _Allocator>
inline Message& operator<<(Message& msg, const std::vector<_Element>& items) inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
{ {
msg.openContainer(signature_of<_Element>::str()); serializeArray(items);
for (const auto& item : items) return *this;
msg << item;
msg.closeContainer();
return msg;
} }
template <typename _Key, typename _Value> template <typename _Element, std::size_t _Size>
inline Message& operator<<(Message& msg, const std::map<_Key, _Value>& items) inline Message& Message::operator<<(const std::array<_Element, _Size>& items)
{ {
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str(); serializeArray(items);
return *this;
}
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator<<(const std::span<_Element, _Extent>& items)
{
serializeArray(items);
return *this;
}
#endif
template <typename _Array>
inline void Message::serializeArray(const _Array& items)
{
using ElementType = typename _Array::value_type;
// Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step serialization of individual elements.
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
appendArray(*signature_of<ElementType>::str().c_str(), items.data(), items.size() * sizeof(ElementType));
}
else
{
openContainer(signature_of<ElementType>::str());
for (const auto& item : items)
*this << item;
closeContainer();
}
}
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items)
{
serializeDictionary(items);
return *this;
}
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
{
serializeDictionary(items);
return *this;
}
template <typename _Dictionary>
inline void Message::serializeDictionary(const _Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}"; const std::string arraySignature = "{" + dictEntrySignature + "}";
msg.openContainer(arraySignature); openContainer(arraySignature);
for (const auto& item : items) for (const auto& item : items)
{ {
msg.openDictEntry(dictEntrySignature); openDictEntry(dictEntrySignature);
msg << item.first; *this << item.first;
msg << item.second; *this << item.second;
msg.closeDictEntry(); closeDictEntry();
} }
msg.closeContainer(); closeContainer();
return msg;
} }
namespace detail namespace detail
@ -296,78 +409,182 @@ namespace sdbus {
} }
template <typename... _ValueTypes> template <typename... _ValueTypes>
inline Message& operator<<(Message& msg, const Struct<_ValueTypes...>& item) inline Message& Message::operator<<(const Struct<_ValueTypes...>& item)
{ {
auto structSignature = signature_of<Struct<_ValueTypes...>>::str(); auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
assert(structSignature.size() > 2); assert(structSignature.size() > 2);
// Remove opening and closing parenthesis from the struct signature to get contents signature // Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2); auto structContentSignature = structSignature.substr(1, structSignature.size() - 2);
msg.openStruct(structContentSignature); openStruct(structContentSignature);
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{}); detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
msg.closeStruct(); closeStruct();
return msg; return *this;
} }
template <typename... _ValueTypes> template <typename... _ValueTypes>
inline Message& operator<<(Message& msg, const std::tuple<_ValueTypes...>& item) inline Message& Message::operator<<(const std::tuple<_ValueTypes...>& item)
{ {
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{}); detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
return msg; return *this;
} }
template <typename _Element, typename _Allocator>
template <typename _Element> inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
inline Message& operator>>(Message& msg, std::vector<_Element>& items)
{ {
if(!msg.enterContainer(signature_of<_Element>::str())) deserializeArray(items);
return msg;
return *this;
}
template <typename _Element, std::size_t _Size>
inline Message& Message::operator>>(std::array<_Element, _Size>& items)
{
deserializeArray(items);
return *this;
}
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator>>(std::span<_Element, _Extent>& items)
{
deserializeArray(items);
return *this;
}
#endif
template <typename _Array>
inline void Message::deserializeArray(_Array& items)
{
using ElementType = typename _Array::value_type;
// Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step deserialization of individual elements.
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
deserializeArrayFast(items);
}
else
{
deserializeArraySlow(items);
}
}
template <typename _Array>
inline void Message::deserializeArrayFast(_Array& items)
{
using ElementType = typename _Array::value_type;
size_t arraySize{};
const ElementType* arrayPtr{};
readArray(*signature_of<ElementType>::str().c_str(), (const void**)&arrayPtr, &arraySize);
size_t elementsInMsg = arraySize / sizeof(ElementType);
bool notEnoughSpace = items.size() < elementsInMsg;
SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL);
std::copy_n(arrayPtr, elementsInMsg, items.begin());
}
template <typename _Element, typename _Allocator>
void Message::deserializeArrayFast(std::vector<_Element, _Allocator>& items)
{
size_t arraySize{};
const _Element* arrayPtr{};
readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
}
template <typename _Array>
inline void Message::deserializeArraySlow(_Array& items)
{
using ElementType = typename _Array::value_type;
if(!enterContainer(signature_of<ElementType>::str()))
return;
for (auto& elem : items)
if (!(*this >> elem))
break; // Keep the rest in the destination sequence untouched
SDBUS_THROW_ERROR_IF(!isAtEnd(false), "Failed to deserialize array: not enough space in destination sequence", EINVAL);
clearFlags();
exitContainer();
}
template <typename _Element, typename _Allocator>
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
{
if(!enterContainer(signature_of<_Element>::str()))
return;
while (true) while (true)
{ {
_Element elem; _Element elem;
if (msg >> elem) if (*this >> elem)
items.emplace_back(std::move(elem)); items.emplace_back(std::move(elem));
else else
break; break;
} }
msg.clearFlags(); clearFlags();
msg.exitContainer(); exitContainer();
return msg;
} }
template <typename _Key, typename _Value> template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& operator>>(Message& msg, std::map<_Key, _Value>& items) inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items)
{ {
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str(); deserializeDictionary(items);
return *this;
}
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
{
deserializeDictionary(items);
return *this;
}
template <typename _Dictionary>
inline void Message::deserializeDictionary(_Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}"; const std::string arraySignature = "{" + dictEntrySignature + "}";
if (!msg.enterContainer(arraySignature)) if (!enterContainer(arraySignature))
return msg; return;
while (true) while (true)
{ {
if (!msg.enterDictEntry(dictEntrySignature)) if (!enterDictEntry(dictEntrySignature))
break; break;
_Key key; KeyType key;
_Value value; ValueType value;
msg >> key >> value; *this >> key >> value;
items.emplace(std::move(key), std::move(value)); items.emplace(std::move(key), std::move(value));
msg.exitDictEntry(); exitDictEntry();
} }
msg.clearFlags(); clearFlags();
msg.exitContainer(); exitContainer();
return msg;
} }
namespace detail namespace detail
@ -388,27 +605,27 @@ namespace sdbus {
} }
template <typename... _ValueTypes> template <typename... _ValueTypes>
inline Message& operator>>(Message& msg, Struct<_ValueTypes...>& item) inline Message& Message::operator>>(Struct<_ValueTypes...>& item)
{ {
auto structSignature = signature_of<Struct<_ValueTypes...>>::str(); auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
// Remove opening and closing parenthesis from the struct signature to get contents signature // Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2); auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
if (!msg.enterStruct(structContentSignature)) if (!enterStruct(structContentSignature))
return msg; return *this;
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{}); detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
msg.exitStruct(); exitStruct();
return msg; return *this;
} }
template <typename... _ValueTypes> template <typename... _ValueTypes>
inline Message& operator>>(Message& msg, std::tuple<_ValueTypes...>& item) inline Message& Message::operator>>(std::tuple<_ValueTypes...>& item)
{ {
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{}); detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
return msg; return *this;
} }
} }

View File

@ -76,7 +76,7 @@ namespace sdbus {
{ {
assert(call_.isValid()); assert(call_.isValid());
auto reply = call_.createReply(); auto reply = call_.createReply();
(reply << ... << results); (void)(reply << ... << results);
reply.send(); reply.send();
} }

View File

@ -109,6 +109,21 @@ namespace sdbus {
{ {
} }
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
* For more information on its behavior, consult @ref createProxy(std::string,std::string,sdbus::dont_run_event_loop_thread_t)
*/
ProxyInterfaces(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
, _Interfaces(getProxy())...
{
}
/*! /*!
* @brief Creates native-like proxy object instance * @brief Creates native-like proxy object instance
* *
@ -141,6 +156,22 @@ namespace sdbus {
{ {
} }
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* The proxy created this way becomes an owner of the connection.
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string,sdbus::dont_run_event_loop_thread_t)
*/
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
, _Interfaces(getProxy())...
{
}
/*! /*!
* @brief Finishes proxy registration and makes the proxy ready for use * @brief Finishes proxy registration and makes the proxy ready for use
* *
@ -175,6 +206,11 @@ namespace sdbus {
protected: protected:
using base_type = ProxyInterfaces; using base_type = ProxyInterfaces;
ProxyInterfaces(const ProxyInterfaces&) = delete;
ProxyInterfaces& operator=(const ProxyInterfaces&) = delete;
ProxyInterfaces(ProxyInterfaces&&) = default;
ProxyInterfaces& operator=(ProxyInterfaces&&) = default;
~ProxyInterfaces() = default; ~ProxyInterfaces() = default;
}; };

View File

@ -43,27 +43,32 @@ namespace sdbus {
protected: protected:
Peer_proxy(sdbus::IProxy& proxy) Peer_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
} }
Peer_proxy(const Peer_proxy&) = delete;
Peer_proxy& operator=(const Peer_proxy&) = delete;
Peer_proxy(Peer_proxy&&) = default;
Peer_proxy& operator=(Peer_proxy&&) = default;
~Peer_proxy() = default; ~Peer_proxy() = default;
public: public:
void Ping() void Ping()
{ {
proxy_.callMethod("Ping").onInterface(INTERFACE_NAME); proxy_->callMethod("Ping").onInterface(INTERFACE_NAME);
} }
std::string GetMachineId() std::string GetMachineId()
{ {
std::string machineUUID; std::string machineUUID;
proxy_.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID); proxy_->callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
return machineUUID; return machineUUID;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
// Proxy for introspection // Proxy for introspection
@ -73,22 +78,27 @@ namespace sdbus {
protected: protected:
Introspectable_proxy(sdbus::IProxy& proxy) Introspectable_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
} }
Introspectable_proxy(const Introspectable_proxy&) = delete;
Introspectable_proxy& operator=(const Introspectable_proxy&) = delete;
Introspectable_proxy(Introspectable_proxy&&) = default;
Introspectable_proxy& operator=(Introspectable_proxy&&) = default;
~Introspectable_proxy() = default; ~Introspectable_proxy() = default;
public: public:
std::string Introspect() std::string Introspect()
{ {
std::string xml; std::string xml;
proxy_.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml); proxy_->callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
return xml; return xml;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
// Proxy for properties // Proxy for properties
@ -98,10 +108,10 @@ namespace sdbus {
protected: protected:
Properties_proxy(sdbus::IProxy& proxy) Properties_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
proxy_ proxy_
.uponSignal("PropertiesChanged") ->uponSignal("PropertiesChanged")
.onInterface(INTERFACE_NAME) .onInterface(INTERFACE_NAME)
.call([this]( const std::string& interfaceName .call([this]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties , const std::map<std::string, sdbus::Variant>& changedProperties
@ -111,6 +121,11 @@ namespace sdbus {
}); });
} }
Properties_proxy(const Properties_proxy&) = delete;
Properties_proxy& operator=(const Properties_proxy&) = delete;
Properties_proxy(Properties_proxy&&) = default;
Properties_proxy& operator=(Properties_proxy&&) = default;
~Properties_proxy() = default; ~Properties_proxy() = default;
virtual void onPropertiesChanged( const std::string& interfaceName virtual void onPropertiesChanged( const std::string& interfaceName
@ -120,23 +135,23 @@ namespace sdbus {
public: public:
sdbus::Variant Get(const std::string& interfaceName, const std::string& propertyName) sdbus::Variant Get(const std::string& interfaceName, const std::string& propertyName)
{ {
return proxy_.getProperty(propertyName).onInterface(interfaceName); return proxy_->getProperty(propertyName).onInterface(interfaceName);
} }
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value) void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
{ {
proxy_.setProperty(propertyName).onInterface(interfaceName).toValue(value); proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value);
} }
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName) std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
{ {
std::map<std::string, sdbus::Variant> props; std::map<std::string, sdbus::Variant> props;
proxy_.callMethod("GetAll").onInterface(INTERFACE_NAME).withArguments(interfaceName).storeResultsTo(props); proxy_->callMethod("GetAll").onInterface(INTERFACE_NAME).withArguments(interfaceName).storeResultsTo(props);
return props; return props;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
// Proxy for object manager // Proxy for object manager
@ -146,10 +161,10 @@ namespace sdbus {
protected: protected:
ObjectManager_proxy(sdbus::IProxy& proxy) ObjectManager_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
proxy_ proxy_
.uponSignal("InterfacesAdded") ->uponSignal("InterfacesAdded")
.onInterface(INTERFACE_NAME) .onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath .call([this]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties ) , const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
@ -157,8 +172,7 @@ namespace sdbus {
this->onInterfacesAdded(objectPath, interfacesAndProperties); this->onInterfacesAdded(objectPath, interfacesAndProperties);
}); });
proxy_ proxy_->uponSignal("InterfacesRemoved")
.uponSignal("InterfacesRemoved")
.onInterface(INTERFACE_NAME) .onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath .call([this]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces ) , const std::vector<std::string>& interfaces )
@ -167,6 +181,11 @@ namespace sdbus {
}); });
} }
ObjectManager_proxy(const ObjectManager_proxy&) = delete;
ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete;
ObjectManager_proxy(ObjectManager_proxy&&) = default;
ObjectManager_proxy& operator=(ObjectManager_proxy&&) = default;
~ObjectManager_proxy() = default; ~ObjectManager_proxy() = default;
virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath
@ -178,12 +197,12 @@ namespace sdbus {
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects() std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects()
{ {
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties; std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
proxy_.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties); proxy_->callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
return objectsInterfacesAndProperties; return objectsInterfacesAndProperties;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality // Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
@ -197,25 +216,30 @@ namespace sdbus {
protected: protected:
Properties_adaptor(sdbus::IObject& object) Properties_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
} }
Properties_adaptor(const Properties_adaptor&) = delete;
Properties_adaptor& operator=(const Properties_adaptor&) = delete;
Properties_adaptor(Properties_adaptor&&) = default;
Properties_adaptor& operator=(Properties_adaptor&&) = default;
~Properties_adaptor() = default; ~Properties_adaptor() = default;
public: public:
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties) void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
{ {
object_.emitPropertiesChangedSignal(interfaceName, properties); object_->emitPropertiesChangedSignal(interfaceName, properties);
} }
void emitPropertiesChangedSignal(const std::string& interfaceName) void emitPropertiesChangedSignal(const std::string& interfaceName)
{ {
object_.emitPropertiesChangedSignal(interfaceName); object_->emitPropertiesChangedSignal(interfaceName);
} }
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
/*! /*!
@ -234,15 +258,20 @@ namespace sdbus {
protected: protected:
explicit ObjectManager_adaptor(sdbus::IObject& object) explicit ObjectManager_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
object_.addObjectManager(); object_->addObjectManager();
} }
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor(ObjectManager_adaptor&&) = default;
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = default;
~ObjectManager_adaptor() = default; ~ObjectManager_adaptor() = default;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
/*! /*!
@ -259,10 +288,16 @@ namespace sdbus {
class ManagedObject_adaptor class ManagedObject_adaptor
{ {
protected: protected:
explicit ManagedObject_adaptor(sdbus::IObject& object) : object_(object) explicit ManagedObject_adaptor(sdbus::IObject& object)
: object_(&object)
{ {
} }
ManagedObject_adaptor(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor(ManagedObject_adaptor&&) = default;
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = default;
~ManagedObject_adaptor() = default; ~ManagedObject_adaptor() = default;
public: public:
@ -273,7 +308,7 @@ namespace sdbus {
*/ */
void emitInterfacesAddedSignal() void emitInterfacesAddedSignal()
{ {
object_.emitInterfacesAddedSignal(); object_->emitInterfacesAddedSignal();
} }
/*! /*!
@ -283,7 +318,7 @@ namespace sdbus {
*/ */
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
{ {
object_.emitInterfacesAddedSignal(interfaces); object_->emitInterfacesAddedSignal(interfaces);
} }
/*! /*!
@ -293,7 +328,7 @@ namespace sdbus {
*/ */
void emitInterfacesRemovedSignal() void emitInterfacesRemovedSignal()
{ {
object_.emitInterfacesRemovedSignal(); object_->emitInterfacesRemovedSignal();
} }
/*! /*!
@ -303,11 +338,11 @@ namespace sdbus {
*/ */
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
{ {
object_.emitInterfacesRemovedSignal(interfaces); object_->emitInterfacesRemovedSignal(interfaces);
} }
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
} }

View File

@ -30,7 +30,12 @@
#include <type_traits> #include <type_traits>
#include <string> #include <string>
#include <vector> #include <vector>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <map> #include <map>
#include <unordered_map>
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <memory> #include <memory>
@ -81,12 +86,20 @@ namespace sdbus {
// Tag denoting the assumption that the caller has already obtained fd ownership // Tag denoting the assumption that the caller has already obtained fd ownership
struct adopt_fd_t { explicit adopt_fd_t() = default; }; struct adopt_fd_t { explicit adopt_fd_t() = default; };
inline constexpr adopt_fd_t adopt_fd{}; inline constexpr adopt_fd_t adopt_fd{};
// Tag specifying that the proxy shall not run an event loop thread on its D-Bus connection.
// Such proxies are typically created to carry out a simple synchronous D-Bus call(s) and then are destroyed.
struct dont_run_event_loop_thread_t { explicit dont_run_event_loop_thread_t() = default; };
inline constexpr dont_run_event_loop_thread_t dont_run_event_loop_thread{};
// Tag denoting an asynchronous call that returns std::future as a handle
struct with_future_t { explicit with_future_t() = default; };
inline constexpr with_future_t with_future{};
// Template specializations for getting D-Bus signatures from C++ types // Template specializations for getting D-Bus signatures from C++ types
template <typename _T> template <typename _T>
struct signature_of struct signature_of
{ {
static constexpr bool is_valid = false; static constexpr bool is_valid = false;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -97,10 +110,21 @@ namespace sdbus {
} }
}; };
template <typename _T>
struct signature_of<const _T>
: public signature_of<_T>
{};
template <typename _T>
struct signature_of<_T&>
: public signature_of<_T>
{};
template <> template <>
struct signature_of<void> struct signature_of<void>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -112,6 +136,7 @@ namespace sdbus {
struct signature_of<bool> struct signature_of<bool>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -123,6 +148,7 @@ namespace sdbus {
struct signature_of<uint8_t> struct signature_of<uint8_t>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -134,6 +160,7 @@ namespace sdbus {
struct signature_of<int16_t> struct signature_of<int16_t>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -145,6 +172,7 @@ namespace sdbus {
struct signature_of<uint16_t> struct signature_of<uint16_t>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -156,6 +184,7 @@ namespace sdbus {
struct signature_of<int32_t> struct signature_of<int32_t>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -167,6 +196,7 @@ namespace sdbus {
struct signature_of<uint32_t> struct signature_of<uint32_t>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -178,6 +208,7 @@ namespace sdbus {
struct signature_of<int64_t> struct signature_of<int64_t>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -189,6 +220,7 @@ namespace sdbus {
struct signature_of<uint64_t> struct signature_of<uint64_t>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -200,6 +232,7 @@ namespace sdbus {
struct signature_of<double> struct signature_of<double>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str() static const std::string str()
{ {
@ -211,6 +244,7 @@ namespace sdbus {
struct signature_of<char*> struct signature_of<char*>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -222,6 +256,7 @@ namespace sdbus {
struct signature_of<const char*> struct signature_of<const char*>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -233,6 +268,7 @@ namespace sdbus {
struct signature_of<char[_N]> struct signature_of<char[_N]>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -244,6 +280,7 @@ namespace sdbus {
struct signature_of<const char[_N]> struct signature_of<const char[_N]>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -255,6 +292,7 @@ namespace sdbus {
struct signature_of<std::string> struct signature_of<std::string>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -266,6 +304,7 @@ namespace sdbus {
struct signature_of<Struct<_ValueTypes...>> struct signature_of<Struct<_ValueTypes...>>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -281,6 +320,7 @@ namespace sdbus {
struct signature_of<Variant> struct signature_of<Variant>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -292,6 +332,7 @@ namespace sdbus {
struct signature_of<ObjectPath> struct signature_of<ObjectPath>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -303,6 +344,7 @@ namespace sdbus {
struct signature_of<Signature> struct signature_of<Signature>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -314,6 +356,7 @@ namespace sdbus {
struct signature_of<UnixFd> struct signature_of<UnixFd>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -321,10 +364,11 @@ namespace sdbus {
} }
}; };
template <typename _Element> template <typename _Element, typename _Allocator>
struct signature_of<std::vector<_Element>> struct signature_of<std::vector<_Element, _Allocator>>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -332,10 +376,49 @@ namespace sdbus {
} }
}; };
template <typename _Key, typename _Value> template <typename _Element, std::size_t _Size>
struct signature_of<std::map<_Key, _Value>> struct signature_of<std::array<_Element, _Size>>
{ {
static constexpr bool is_valid = true; static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a" + signature_of<_Element>::str();
}
};
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
struct signature_of<std::span<_Element, _Extent>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a" + signature_of<_Element>::str();
}
};
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
struct signature_of<std::map<_Key, _Value, _Compare, _Allocator>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a{" + signature_of<_Key>::str() + signature_of<_Value>::str() + "}";
}
};
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
struct signature_of<std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str() static const std::string str()
{ {
@ -540,6 +623,26 @@ namespace sdbus {
} }
}; };
template <typename... _Args> struct future_return
{
typedef std::tuple<_Args...> type;
};
template <> struct future_return<>
{
typedef void type;
};
template <typename _Type> struct future_return<_Type>
{
typedef _Type type;
};
template <typename... _Args>
using future_return_t = typename future_return<_Args...>::type;
namespace detail namespace detail
{ {
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I> template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>

View File

@ -42,7 +42,7 @@ namespace sdbus {
* @class Variant * @class Variant
* *
* Variant can hold value of any D-Bus-supported type. * Variant can hold value of any D-Bus-supported type.
* *
* Note: Even though thread-aware, Variant objects are not thread-safe. * Note: Even though thread-aware, Variant objects are not thread-safe.
* Some const methods are conceptually const, but not physically const, * Some const methods are conceptually const, but not physically const,
* thus are not thread-safe. This is by design: normally, clients * thus are not thread-safe. This is by design: normally, clients
@ -56,7 +56,7 @@ namespace sdbus {
Variant(); Variant();
template <typename _ValueType> template <typename _ValueType>
Variant(const _ValueType& value) /*explicit*/ Variant(const _ValueType& value) // TODO: Mark explicit in new major version so we don't break client code within v1
: Variant() : Variant()
{ {
msg_.openVariant(signature_of<_ValueType>::str()); msg_.openVariant(signature_of<_ValueType>::str());
@ -104,6 +104,10 @@ namespace sdbus {
* *
* Representation of struct D-Bus type * Representation of struct D-Bus type
* *
* Struct implements tuple protocol, i.e. it's a tuple-like class.
* It can be used with std::get<>(), std::tuple_element,
* std::tuple_size and in structured bindings.
*
***********************************************/ ***********************************************/
template <typename... _ValueTypes> template <typename... _ValueTypes>
class Struct class Struct
@ -112,7 +116,7 @@ namespace sdbus {
public: public:
using std::tuple<_ValueTypes...>::tuple; using std::tuple<_ValueTypes...>::tuple;
// Disable constructor if an older then 7.1.0 version of GCC is used // Disable constructor if an older then 7.1.0 version of GCC is used
#if !((defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !(__GNUC__ > 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 0))))) #if !((defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !(__GNUC__ > 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 0)))))
Struct() = default; Struct() = default;
@ -135,6 +139,9 @@ namespace sdbus {
} }
}; };
template <typename... _Elements>
Struct(_Elements...) -> Struct<_Elements...>;
template<typename... _Elements> template<typename... _Elements>
constexpr Struct<std::decay_t<_Elements>...> constexpr Struct<std::decay_t<_Elements>...>
make_struct(_Elements&&... args) make_struct(_Elements&&... args)
@ -280,4 +287,14 @@ namespace sdbus {
} }
template <size_t _I, typename... _ValueTypes>
struct std::tuple_element<_I, sdbus::Struct<_ValueTypes...>>
: std::tuple_element<_I, std::tuple<_ValueTypes...>>
{};
template <typename... _ValueTypes>
struct std::tuple_size<sdbus::Struct<_ValueTypes...>>
: std::tuple_size<std::tuple<_ValueTypes...>>
{};
#endif /* SDBUS_CXX_TYPES_H_ */ #endif /* SDBUS_CXX_TYPES_H_ */

View File

@ -5,7 +5,7 @@ includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: @PROJECT_NAME@ Name: @PROJECT_NAME@
Description: C++ library on top of sd-bus, a systemd D-Bus library Description: C++ library on top of sd-bus, a systemd D-Bus library
Requires@PKGCONFIG_REQS@: libsystemd Requires@PKGCONFIG_REQS@: @LIBSYSTEMD@
Version: @SDBUSCPP_VERSION@ Version: @SDBUSCPP_VERSION@
Libs: -L${libdir} -l@PROJECT_NAME@ Libs: -L${libdir} -l@PROJECT_NAME@
Cflags: -I${includedir} Cflags: -I${includedir}

View File

@ -47,7 +47,7 @@ namespace sdbus
sdbusFlags |= SD_BUS_VTABLE_PROPERTY_CONST; sdbusFlags |= SD_BUS_VTABLE_PROPERTY_CONST;
else if (flags_.test(Flags::EMITS_NO_SIGNAL)) else if (flags_.test(Flags::EMITS_NO_SIGNAL))
sdbusFlags |= 0; sdbusFlags |= 0;
return sdbusFlags; return sdbusFlags;
} }

View File

@ -226,6 +226,13 @@ Message& Message::operator<<(const UnixFd &item)
return *this; return *this;
} }
Message& Message::appendArray(char type, const void *ptr, size_t size)
{
auto r = sd_bus_message_append_array((sd_bus_message*)msg_, type, ptr, size);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an array", -r);
return *this;
}
Message& Message::operator>>(bool& item) Message& Message::operator>>(bool& item)
{ {
@ -318,6 +325,17 @@ Message& Message::operator>>(uint64_t& item)
return *this; return *this;
} }
Message& Message::readArray(char type, const void **ptr, size_t *size)
{
auto r = sd_bus_message_read_array((sd_bus_message*)msg_, type, ptr, size);
if (r == 0)
ok_ = false;
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize an array", -r);
return *this;
}
Message& Message::operator>>(double& item) Message& Message::operator>>(double& item)
{ {
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item); auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
@ -630,6 +648,11 @@ bool Message::isEmpty() const
return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0; return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0;
} }
bool Message::isAtEnd(bool complete) const
{
return sd_bus_message_at_end((sd_bus_message*)msg_, complete) > 0;
}
pid_t Message::getCredsPid() const pid_t Message::getCredsPid() const
{ {
uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT; uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT;
@ -862,15 +885,57 @@ void Signal::setDestination(const std::string& destination)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r); SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
} }
namespace {
// Pseudo-connection lifetime handling. In standard cases, we could do with simply function-local static pseudo
// connection instance below. However, it may happen that client's sdbus-c++ objects outlive this static connection
// instance (because they are used in global application objects that were created before this connection, and thus
// are destroyed later). This by itself sounds like a smell in client's application design, but it is downright bad
// in sdbus-c++ because it has no control over when client's dependent statics get destroyed. A "Phoenix" pattern
// (see Modern C++ Design - Generic Programming and Design Patterns Applied, by Andrei Alexandrescu) is applied to fix
// this by re-creating the connection again in such cases and keeping it alive until the next exit handler is invoked.
// Please note that the solution is NOT thread-safe.
// Another common solution is global sdbus-c++ startup/shutdown functions, but that would be an intrusive change.
/*constinit (C++20 keyword) */ static bool pseudoConnectionDestroyed{};
std::unique_ptr<sdbus::internal::IConnection, void(*)(sdbus::internal::IConnection*)> createPseudoConnection()
{
auto deleter = [](sdbus::internal::IConnection* con)
{
delete con;
pseudoConnectionDestroyed = true;
};
return {internal::createPseudoConnection().release(), std::move(deleter)};
}
sdbus::internal::IConnection& getPseudoConnectionInstance()
{
static auto connection = createPseudoConnection();
if (pseudoConnectionDestroyed)
{
connection = createPseudoConnection(); // Phoenix rising from the ashes
atexit([](){ connection.~unique_ptr(); }); // We have to manually take care of deleting the phoenix
pseudoConnectionDestroyed = false;
}
assert(connection != nullptr);
return *connection;
}
}
PlainMessage createPlainMessage() PlainMessage createPlainMessage()
{ {
//static auto connection = internal::createConnection();
// Let's create a pseudo connection -- one that does not really connect to the real bus. // Let's create a pseudo connection -- one that does not really connect to the real bus.
// This is a bit of a hack, but it enables use to work with D-Bus message locally without // This is a bit of a hack, but it enables use to work with D-Bus message locally without
// the need of D-Bus daemon. This is especially useful in unit tests of both sdbus-c++ and client code. // the need of D-Bus daemon. This is especially useful in unit tests of both sdbus-c++ and client code.
// Additionally, it's light-weight and fast solution. // Additionally, it's light-weight and fast solution.
static auto connection = internal::createPseudoConnection(); auto& connection = getPseudoConnectionInstance();
return connection->createPlainMessage(); return connection.createPlainMessage();
} }
} }

View File

@ -66,6 +66,21 @@ Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
connection_->enterEventLoopAsync(); connection_->enterEventLoopAsync();
} }
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t )
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
// Even though the connection is ours only, we don't start an event loop thread.
// This proxy is meant to be created, used for simple synchronous D-Bus call(s) and then dismissed.
}
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName) MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
{ {
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName); return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
@ -110,12 +125,34 @@ PendingAsyncCall Proxy::callMethod(const MethodCall& message, async_reply_handle
callData->slot = message.send(callback, callData.get(), timeout); callData->slot = message.send(callback, callData.get(), timeout);
auto slotPtr = callData->slot.get(); pendingAsyncCalls_.addCall(std::move(callData));
pendingAsyncCalls_.addCall(slotPtr, std::move(callData));
return {weakData}; return {weakData};
} }
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, with_future_t)
{
return Proxy::callMethod(message, {}, with_future);
}
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, uint64_t timeout, with_future_t)
{
auto promise = std::make_shared<std::promise<MethodReply>>();
auto future = promise->get_future();
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply& reply, const Error* error) noexcept
{
if (error == nullptr)
promise->set_value(reply); // TODO: std::move? Can't move now because currently processed message. TODO: Refactor
else
promise->set_exception(std::make_exception_ptr(*error));
};
(void)Proxy::callMethod(message, std::move(asyncReplyCallback), timeout);
return future;
}
MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout) MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout)
{ {
/*thread_local*/ SyncCallReplyData syncCallReplyData; /*thread_local*/ SyncCallReplyData syncCallReplyData;
@ -238,7 +275,6 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
assert(asyncCallData != nullptr); assert(asyncCallData != nullptr);
assert(asyncCallData->callback); assert(asyncCallData->callback);
auto& proxy = asyncCallData->proxy; auto& proxy = asyncCallData->proxy;
auto slot = asyncCallData->slot.get();
// We are removing the CallData item at the complete scope exit, after the callback has been invoked. // We are removing the CallData item at the complete scope exit, after the callback has been invoked.
// We can't do it earlier (before callback invocation for example), because CallBack data (slot release) // We can't do it earlier (before callback invocation for example), because CallBack data (slot release)
@ -246,8 +282,7 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
SCOPE_EXIT SCOPE_EXIT
{ {
// Remove call meta-data if it's a real async call (a sync call done in terms of async has slot == nullptr) // Remove call meta-data if it's a real async call (a sync call done in terms of async has slot == nullptr)
if (slot) proxy.pendingAsyncCalls_.removeCall(asyncCallData);
proxy.pendingAsyncCalls_.removeCall(slot);
}; };
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface()); auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
@ -319,7 +354,7 @@ void PendingAsyncCall::cancel()
if (auto ptr = callData_.lock(); ptr != nullptr) if (auto ptr = callData_.lock(); ptr != nullptr)
{ {
auto* callData = static_cast<internal::Proxy::AsyncCalls::CallData*>(ptr.get()); auto* callData = static_cast<internal::Proxy::AsyncCalls::CallData*>(ptr.get());
callData->proxy.pendingAsyncCalls_.removeCall(callData->slot.get()); callData->proxy.pendingAsyncCalls_.removeCall(callData);
// At this point, the callData item is being deleted, leading to the release of the // At this point, the callData item is being deleted, leading to the release of the
// sd-bus slot pointer. This release locks the global sd-bus mutex. If the async // sd-bus slot pointer. This release locks the global sd-bus mutex. If the async
@ -363,6 +398,22 @@ std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& conne
, std::move(objectPath) ); , std::move(objectPath) );
} }
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
connection.release();
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
, std::move(destination)
, std::move(objectPath)
, dont_run_event_loop_thread );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath ) , std::string objectPath )
{ {
@ -376,4 +427,19 @@ std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::move(objectPath) ); , std::move(objectPath) );
} }
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t )
{
auto connection = sdbus::createConnection();
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
assert(sdbusConnection != nullptr);
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
, std::move(destination)
, std::move(objectPath)
, dont_run_event_loop_thread );
}
} }

View File

@ -33,7 +33,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <map> #include <map>
#include <unordered_map> #include <deque>
#include <mutex> #include <mutex>
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
@ -50,10 +50,16 @@ namespace sdbus::internal {
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination , std::string destination
, std::string objectPath ); , std::string objectPath );
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t );
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override; MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override; MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override; PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
std::future<MethodReply> callMethod(const MethodCall& message, with_future_t) override;
std::future<MethodReply> callMethod(const MethodCall& message, uint64_t timeout, with_future_t) override;
void registerSignalHandler( const std::string& interfaceName void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName , const std::string& signalName
@ -132,6 +138,7 @@ namespace sdbus::internal {
Proxy& proxy; Proxy& proxy;
async_reply_handler callback; async_reply_handler callback;
Slot slot; Slot slot;
bool finished { false };
}; };
~AsyncCalls() ~AsyncCalls()
@ -139,18 +146,20 @@ namespace sdbus::internal {
clear(); clear();
} }
bool addCall(void* slot, std::shared_ptr<CallData> asyncCallData) void addCall(std::shared_ptr<CallData> asyncCallData)
{ {
std::lock_guard lock(mutex_); std::lock_guard lock(mutex_);
return calls_.emplace(slot, std::move(asyncCallData)).second; if (!asyncCallData->finished) // The call may have finished in the mean time
calls_.emplace_back(std::move(asyncCallData));
} }
void removeCall(void* slot) void removeCall(CallData* data)
{ {
std::unique_lock lock(mutex_); std::unique_lock lock(mutex_);
if (auto it = calls_.find(slot); it != calls_.end()) data->finished = true;
if (auto it = std::find_if(calls_.begin(), calls_.end(), [data](auto const& entry){ return entry.get() == data; }); it != calls_.end())
{ {
auto callData = std::move(it->second); auto callData = std::move(*it);
calls_.erase(it); calls_.erase(it);
lock.unlock(); lock.unlock();
@ -176,7 +185,7 @@ namespace sdbus::internal {
private: private:
std::mutex mutex_; std::mutex mutex_;
std::unordered_map<void*, std::shared_ptr<CallData>> calls_; std::deque<std::shared_ptr<CallData>> calls_;
} pendingAsyncCalls_; } pendingAsyncCalls_;
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr}; std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};

View File

@ -55,18 +55,17 @@
// return; // exiting scope normally // return; // exiting scope normally
// } // }
#define SCOPE_EXIT \ #define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \ auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= ::skybase::utils::detail::ScopeGuardOnExit() + [&]() \ = ::sdbus::internal::detail::ScopeGuardOnExit() + [&]() \
/**/ /**/
#define SCOPE_EXIT_NAMED(NAME) \ #define SCOPE_EXIT_NAMED(NAME) \
auto NAME \ auto NAME \
= ::skybase::utils::detail::ScopeGuardOnExit() + [&]() \ = ::sdbus::internal::detail::ScopeGuardOnExit() + [&]() \
/**/ /**/
namespace skybase { namespace sdbus::internal {
namespace utils {
template <class _Fun> template <class _Fun>
class ScopeGuard class ScopeGuard
@ -114,7 +113,7 @@ namespace utils {
} }
} }
}} }
#define CONCATENATE_IMPL(s1, s2) s1##s2 #define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2) #define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)

View File

@ -48,7 +48,14 @@ int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
{ {
std::lock_guard lock(sdbusMutex_); std::lock_guard lock(sdbusMutex_);
return ::sd_bus_send(bus, m, cookie); auto r = ::sd_bus_send(bus, m, cookie);
if (r < 0)
return r;
// Make sure long messages are not only stored in outgoing queues but also really sent out
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
return r;
} }
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
@ -62,7 +69,14 @@ int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m,
{ {
std::lock_guard lock(sdbusMutex_); std::lock_guard lock(sdbusMutex_);
return ::sd_bus_call_async(bus, slot, m, callback, userdata, usec); auto r = ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
if (r < 0)
return r;
// Make sure long messages are not only stored in outgoing queues but also really sent out
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
return r;
} }
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type) int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type)

View File

@ -162,4 +162,4 @@ endif()
if(NOT CMAKE_CROSSCOMPILING) if(NOT CMAKE_CROSSCOMPILING)
add_test(NAME sdbus-c++-unit-tests COMMAND sdbus-c++-unit-tests) add_test(NAME sdbus-c++-unit-tests COMMAND sdbus-c++-unit-tests)
add_test(NAME sdbus-c++-integration-tests COMMAND sdbus-c++-integration-tests) add_test(NAME sdbus-c++-integration-tests COMMAND sdbus-c++-integration-tests)
endif() endif()

View File

@ -161,6 +161,24 @@ TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
ASSERT_THAT(future.get(), Eq(100)); ASSERT_THAT(future.get(), Eq(100));
} }
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
{
auto future = m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
{
auto future = m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
auto methodReply = future.get();
uint32_t returnValue{};
methodReply >> returnValue;
ASSERT_THAT(returnValue, Eq(100));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress) TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
{ {
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){}); m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
@ -222,7 +240,7 @@ TEST_F(SdbusTestObject, SupportsAsyncCallCopyAssignment)
ASSERT_TRUE(call.isPending()); ASSERT_TRUE(call.isPending());
} }
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide) TEST_F(SdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
{ {
std::promise<uint32_t> promise; std::promise<uint32_t> promise;
auto future = promise.get_future(); auto future = promise.get_future();
@ -238,3 +256,10 @@ TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
ASSERT_THROW(future.get(), sdbus::Error); ASSERT_THROW(future.get(), sdbus::Error);
} }
TEST_F(SdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
{
auto future = m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
ASSERT_THROW(future.get(), sdbus::Error);
}

View File

@ -38,6 +38,7 @@
#include <fstream> #include <fstream>
#include <future> #include <future>
#include <unistd.h> #include <unistd.h>
#include <variant>
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::Eq; using ::testing::Eq;
@ -59,6 +60,18 @@ TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH)); ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
} }
TEST(AProxy, SupportsMoveSemantics)
{
static_assert(std::is_move_constructible_v<DummyTestProxy>);
static_assert(std::is_move_assignable_v<DummyTestProxy>);
}
TEST(AnAdaptor, SupportsMoveSemantics)
{
static_assert(std::is_move_constructible_v<DummyTestAdaptor>);
static_assert(std::is_move_assignable_v<DummyTestAdaptor>);
}
TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule) TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
{ {
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'"; auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
@ -103,7 +116,8 @@ TEST_F(AConnection, CanAddFloatingMatchRule)
}; };
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot); con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
m_adaptor->emitSimpleSignal(); m_adaptor->emitSimpleSignal();
assert(waitUntil(matchingMessageReceived, 2s)); [[maybe_unused]] auto gotMessage = waitUntil(matchingMessageReceived, 2s);
assert(gotMessage);
matchingMessageReceived = false; matchingMessageReceived = false;
con.reset(); con.reset();

View File

@ -103,7 +103,9 @@ TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
std::vector<int32_t> x{-2, 0, 2}; std::vector<int32_t> x{-2, 0, 2};
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true}; sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
auto mapOfVariants = m_proxy->getMapOfVariants(x, y); auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
decltype(mapOfVariants) res{{-2, false}, {0, false}, {2, true}}; decltype(mapOfVariants) res{ {sdbus::Variant{-2}, sdbus::Variant{false}}
, {sdbus::Variant{0}, sdbus::Variant{false}}
, {sdbus::Variant{2}, sdbus::Variant{true}}};
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>())); ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>())); ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
@ -125,8 +127,8 @@ TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully) TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
{ {
auto val = m_proxy->sumVectorItems({1, 7}, {2, 3}); auto val = m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3)); ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
} }
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully) TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
@ -272,3 +274,12 @@ TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessTh
ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error); ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
} }
#endif #endif
TEST_F(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
{
auto proxy = std::make_unique<TestProxy>(BUS_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
auto multiplyRes = proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}

View File

@ -89,10 +89,22 @@ TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("one")); ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("one"));
} }
TEST_F(SdbusTestObject, EmitsSignalWithLargeMapSuccesfully)
{
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 20'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
m_adaptor->emitSignalWithMap(largeMap);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
}
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully) TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
{ {
double d = 3.14; double d = 3.14;
m_adaptor->emitSignalWithVariant(d); m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant)); ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d)); ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));

View File

@ -86,7 +86,7 @@ TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
{ {
uint32_t newActionValue = 2345; uint32_t newActionValue = 2345;
m_proxy->Set(INTERFACE_NAME, "action", newActionValue); m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
ASSERT_THAT(m_proxy->action(), Eq(newActionValue)); ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
} }
@ -201,7 +201,13 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties ) , const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{ {
EXPECT_THAT(objectPath, Eq(OBJECT_PATH)); EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
#else
// Since systemd v251, ObjectManager standard interface is not listed among the interfaces
// if the object does not have object manager functionality explicitly enabled.
EXPECT_THAT(interfacesAndProperties, SizeIs(4)); // INTERFACE_NAME + 3 standard interfaces
#endif
#if LIBSYSTEMD_VERSION<=244 #if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case. // Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3)); EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
@ -248,7 +254,13 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
, const std::vector<std::string>& interfaces ) , const std::vector<std::string>& interfaces )
{ {
EXPECT_THAT(objectPath, Eq(OBJECT_PATH)); EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
#else
// Since systemd v251, ObjectManager standard interface is not listed among the interfaces
// if the object does not have object manager functionality explicitly enabled.
ASSERT_THAT(interfaces, SizeIs(4)); // INTERFACE_NAME + 3 standard interfaces
#endif
signalReceived = true; signalReceived = true;
}; };

View File

@ -30,7 +30,7 @@
#include <atomic> #include <atomic>
namespace sdbus { namespace test { namespace sdbus { namespace test {
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) : TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) :
AdaptorInterfaces(connection, path) AdaptorInterfaces(connection, path)
{ {
@ -77,7 +77,7 @@ std::vector<int16_t> TestAdaptor::getInts16FromStruct(const sdbus::Struct<uint8_
sdbus::Variant TestAdaptor::processVariant(const sdbus::Variant& v) sdbus::Variant TestAdaptor::processVariant(const sdbus::Variant& v)
{ {
sdbus::Variant res = static_cast<int32_t>(v.get<double>()); sdbus::Variant res{static_cast<int32_t>(v.get<double>())};
return res; return res;
} }
@ -93,7 +93,7 @@ std::map<int32_t, sdbus::Variant> TestAdaptor::getMapOfVariants(const std::vecto
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> TestAdaptor::getStructInStruct() sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> TestAdaptor::getStructInStruct()
{ {
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}})); return sdbus::Struct{STRING_VALUE, sdbus::Struct{std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}}};
} }
int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
@ -104,7 +104,7 @@ int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, c
return res; return res;
} }
uint32_t TestAdaptor::sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) uint32_t TestAdaptor::sumArrayItems(const std::vector<uint16_t>& a, const std::array<uint64_t, 3>& b)
{ {
uint32_t res{0}; uint32_t res{0};
for (auto x : a) for (auto x : a)
@ -162,9 +162,9 @@ sdbus::UnixFd TestAdaptor::getUnixFd()
return sdbus::UnixFd{UNIX_FD_VALUE}; return sdbus::UnixFd{UNIX_FD_VALUE};
} }
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex() std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex()
{ {
return { // map return { // unordered_map
{ {
0, // uint_64_t 0, // uint_64_t
{ // struct { // struct
@ -390,7 +390,7 @@ R"delimiter(
<arg type="(ix)" direction="in"/> <arg type="(ix)" direction="in"/>
<arg type="i" direction="out"/> <arg type="i" direction="out"/>
</method> </method>
<method name="sumVectorItems"> <method name="sumArrayItems">
<arg type="aq" direction="in"/> <arg type="aq" direction="in"/>
<arg type="at" direction="in"/> <arg type="at" direction="in"/>
<arg type="u" direction="out"/> <arg type="u" direction="out"/>

View File

@ -70,13 +70,13 @@ protected:
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) override; std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) override;
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override; sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override;
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) override; int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) override;
uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) override; uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1) override;
uint32_t doOperation(const uint32_t& arg0) override; uint32_t doOperation(const uint32_t& arg0) override;
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override; void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override;
sdbus::Signature getSignature() override; sdbus::Signature getSignature() override;
sdbus::ObjectPath getObjPath() override; sdbus::ObjectPath getObjPath() override;
sdbus::UnixFd getUnixFd() override; sdbus::UnixFd getUnixFd() override;
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override; std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override;
void throwError() override; void throwError() override;
void throwErrorWithNoReply() override; void throwErrorWithNoReply() override;
void doPrivilegedStuff() override; void doPrivilegedStuff() override;
@ -109,6 +109,43 @@ public: // for tests
std::string m_propertySetSender; std::string m_propertySetSender;
}; };
class DummyTestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integrationtests_adaptor
, sdbus::Properties_adaptor
, sdbus::ManagedObject_adaptor >
{
public:
DummyTestAdaptor(sdbus::IConnection& connection, const std::string& path) : AdaptorInterfaces(connection, path) {}
protected:
void noArgNoReturn() override {}
int32_t getInt() override { return {}; }
std::tuple<uint32_t, std::string> getTuple() override { return {}; }
double multiply(const int64_t&, const double&) override { return {}; }
void multiplyWithNoReply(const int64_t&, const double&) override {}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>&) override { return {}; }
sdbus::Variant processVariant(const sdbus::Variant&) override { return {}; }
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>&, const sdbus::Struct<sdbus::Variant, sdbus::Variant>&) override { return {}; }
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override { return {}; }
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>&, const sdbus::Struct<int32_t, int64_t>&) override { return {}; }
uint32_t sumArrayItems(const std::vector<uint16_t>&, const std::array<uint64_t, 3>&) override { return {}; }
uint32_t doOperation(const uint32_t&) override { return {}; }
void doOperationAsync(sdbus::Result<uint32_t>&&, uint32_t) override {}
sdbus::Signature getSignature() override { return {}; }
sdbus::ObjectPath getObjPath() override { return {}; }
sdbus::UnixFd getUnixFd() override { return {}; }
std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override { return {}; }
void throwError() override {}
void throwErrorWithNoReply() override {}
void doPrivilegedStuff() override {}
void emitTwoSimpleSignals() override {}
uint32_t action() override { return {}; }
void action(const uint32_t&) override {}
bool blocking() override { return {}; }
void blocking(const bool&) override {}
std::string state() override { return {}; }
};
}} }}
#endif /* INTEGRATIONTESTS_TESTADAPTOR_H_ */ #endif /* INTEGRATIONTESTS_TESTADAPTOR_H_ */

View File

@ -27,7 +27,7 @@
#include "TestFixture.h" #include "TestFixture.h"
namespace sdbus { namespace test { namespace sdbus { namespace test {
std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection(); std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection(); std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection();

View File

@ -30,7 +30,7 @@
#include <atomic> #include <atomic>
namespace sdbus { namespace test { namespace sdbus { namespace test {
TestProxy::TestProxy(std::string destination, std::string objectPath) TestProxy::TestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath)) : ProxyInterfaces(std::move(destination), std::move(objectPath))
{ {
@ -39,6 +39,13 @@ TestProxy::TestProxy(std::string destination, std::string objectPath)
registerProxy(); registerProxy();
} }
TestProxy::TestProxy(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
: ProxyInterfaces(std::move(destination), std::move(objectPath), dont_run_event_loop_thread)
{
// It doesn't make sense to register any signals here since proxy upon a D-Bus connection with no event loop thread
// will not receive any incoming messages except replies to synchronous D-Bus calls.
}
TestProxy::TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath) TestProxy::TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath)) : ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{ {
@ -116,6 +123,22 @@ sdbus::PendingAsyncCall TestProxy::doOperationClientSideAsync(uint32_t param)
}); });
} }
std::future<uint32_t> TestProxy::doOperationClientSideAsync(uint32_t param, with_future_t)
{
return getProxy().callMethodAsync("doOperation")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(param)
.getResultAsFuture<uint32_t>();
}
std::future<MethodReply> TestProxy::doOperationClientSideAsyncOnBasicAPILevel(uint32_t param)
{
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, "doOperation");
methodCall << param;
return getProxy().callMethod(methodCall, sdbus::with_future);
}
void TestProxy::doErroneousOperationClientSideAsync() void TestProxy::doErroneousOperationClientSideAsync()
{ {
getProxy().callMethodAsync("throwError") getProxy().callMethodAsync("throwError")
@ -126,6 +149,13 @@ void TestProxy::doErroneousOperationClientSideAsync()
}); });
} }
std::future<void> TestProxy::doErroneousOperationClientSideAsync(with_future_t)
{
return getProxy().callMethodAsync("throwError")
.onInterface(sdbus::test::INTERFACE_NAME)
.getResultAsFuture<>();
}
void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param) void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
{ {
using namespace std::chrono_literals; using namespace std::chrono_literals;

View File

@ -32,6 +32,7 @@
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <atomic> #include <atomic>
#include <future>
namespace sdbus { namespace test { namespace sdbus { namespace test {
@ -73,6 +74,7 @@ class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integratio
{ {
public: public:
TestProxy(std::string destination, std::string objectPath); TestProxy(std::string destination, std::string objectPath);
TestProxy(std::string destination, std::string objectPath, dont_run_event_loop_thread_t);
TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath); TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath);
~TestProxy(); ~TestProxy();
@ -93,6 +95,9 @@ public:
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler); void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler);
uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param); uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param); sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param);
std::future<uint32_t> doOperationClientSideAsync(uint32_t param, with_future_t);
std::future<MethodReply> doOperationClientSideAsyncOnBasicAPILevel(uint32_t param);
std::future<void> doErroneousOperationClientSideAsync(with_future_t);
void doErroneousOperationClientSideAsync(); void doErroneousOperationClientSideAsync();
void doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param); void doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
int32_t callNonexistentMethod(); int32_t callNonexistentMethod();
@ -117,6 +122,29 @@ public: // for tests
std::string m_signalMemberName; std::string m_signalMemberName;
}; };
class DummyTestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
, sdbus::Peer_proxy
, sdbus::Introspectable_proxy
, sdbus::Properties_proxy >
{
public:
DummyTestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(destination, objectPath)
{
}
protected:
void onSimpleSignal() override {}
void onSignalWithMap(const std::map<int32_t, std::string>&) override {}
void onSignalWithVariant(const sdbus::Variant&) override {}
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>&) {}
void onDoOperationReply(uint32_t, const sdbus::Error*) {}
// Signals of standard D-Bus interfaces
void onPropertiesChanged( const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>& ) override {}
};
}} }}
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_ */ #endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_ */

View File

@ -20,54 +20,59 @@ public:
protected: protected:
integrationtests_adaptor(sdbus::IObject& object) integrationtests_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
object_.setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL); object_->setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); }); object_->registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); }); object_->registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); });
object_.registerMethod("getTuple").onInterface(INTERFACE_NAME).withOutputParamNames("arg0", "arg1").implementedAs([this](){ return this->getTuple(); }); object_->registerMethod("getTuple").onInterface(INTERFACE_NAME).withOutputParamNames("arg0", "arg1").implementedAs([this](){ return this->getTuple(); });
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("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).withInputParamNames("a", "b").implementedAs([this](const int64_t& a, const double& b){ return this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply(); object_->registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").implementedAs([this](const int64_t& a, const double& b){ return this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
object_.registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0){ return this->getInts16FromStruct(arg0); }); object_->registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0){ return this->getInts16FromStruct(arg0); });
object_.registerMethod("processVariant").onInterface(INTERFACE_NAME).withInputParamNames("variant").withOutputParamNames("result").implementedAs([this](const sdbus::Variant& variant){ return this->processVariant(variant); }); object_->registerMethod("processVariant").onInterface(INTERFACE_NAME).withInputParamNames("variant").withOutputParamNames("result").implementedAs([this](const sdbus::Variant& variant){ return this->processVariant(variant); });
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("getMapOfVariants").onInterface(INTERFACE_NAME).withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x, y); });
object_.registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); }); object_->registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); });
object_.registerMethod("sumStructItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1){ return this->sumStructItems(arg0, arg1); }); object_->registerMethod("sumStructItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1){ return this->sumStructItems(arg0, arg1); });
object_.registerMethod("sumVectorItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1){ return this->sumVectorItems(arg0, arg1); }); object_->registerMethod("sumArrayItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1){ return this->sumArrayItems(arg0, arg1); });
object_.registerMethod("doOperation").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); }); object_->registerMethod("doOperation").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); });
object_.registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result<uint32_t>&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); }); object_->registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result<uint32_t>&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); });
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); }); object_->registerMethod("getSignature").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); });
object_.registerMethod("getObjPath").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getObjPath(); }); object_->registerMethod("getObjPath").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getObjPath(); });
object_.registerMethod("getUnixFd").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getUnixFd(); }); object_->registerMethod("getUnixFd").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getUnixFd(); });
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getComplex(); }).markAsDeprecated(); object_->registerMethod("getComplex").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
object_.registerMethod("throwError").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwError(); }); object_->registerMethod("throwError").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwError(); });
object_.registerMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwErrorWithNoReply(); }).withNoReply(); object_->registerMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwErrorWithNoReply(); }).withNoReply();
object_.registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged(); object_->registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged();
object_.registerMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->emitTwoSimpleSignals(); }); object_->registerMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->emitTwoSimpleSignals(); });
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated(); object_->registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>("aMap"); 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_->registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>("aVariant");
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); 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);
object_.registerProperty("blocking").onInterface(INTERFACE_NAME).withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); }); object_->registerProperty("blocking").onInterface(INTERFACE_NAME).withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); });
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE); object_->registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
} }
integrationtests_adaptor(const integrationtests_adaptor&) = delete;
integrationtests_adaptor& operator=(const integrationtests_adaptor&) = delete;
integrationtests_adaptor(integrationtests_adaptor&&) = default;
integrationtests_adaptor& operator=(integrationtests_adaptor&&) = default;
~integrationtests_adaptor() = default; ~integrationtests_adaptor() = default;
public: public:
void emitSimpleSignal() void emitSimpleSignal()
{ {
object_.emitSignal("simpleSignal").onInterface(INTERFACE_NAME); object_->emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
} }
void emitSignalWithMap(const std::map<int32_t, std::string>& aMap) void emitSignalWithMap(const std::map<int32_t, std::string>& aMap)
{ {
object_.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap); object_->emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
} }
void emitSignalWithVariant(const sdbus::Variant& aVariant) void emitSignalWithVariant(const sdbus::Variant& aVariant)
{ {
object_.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant); object_->emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
} }
private: private:
@ -81,13 +86,13 @@ private:
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) = 0; virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) = 0;
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() = 0; virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() = 0;
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) = 0; virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) = 0;
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) = 0; virtual uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1) = 0;
virtual uint32_t doOperation(const uint32_t& arg0) = 0; virtual uint32_t doOperation(const uint32_t& arg0) = 0;
virtual void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0; virtual void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0;
virtual sdbus::Signature getSignature() = 0; virtual sdbus::Signature getSignature() = 0;
virtual sdbus::ObjectPath getObjPath() = 0; virtual sdbus::ObjectPath getObjPath() = 0;
virtual sdbus::UnixFd getUnixFd() = 0; virtual sdbus::UnixFd getUnixFd() = 0;
virtual std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0; virtual std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
virtual void throwError() = 0; virtual void throwError() = 0;
virtual void throwErrorWithNoReply() = 0; virtual void throwErrorWithNoReply() = 0;
virtual void doPrivilegedStuff() = 0; virtual void doPrivilegedStuff() = 0;
@ -101,7 +106,7 @@ private:
virtual std::string state() = 0; virtual std::string state() = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}} // namespaces }} // namespaces

View File

@ -20,13 +20,18 @@ public:
protected: protected:
integrationtests_proxy(sdbus::IProxy& proxy) integrationtests_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
proxy_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }); proxy_->uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
proxy_.uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& aMap){ this->onSignalWithMap(aMap); }); proxy_->uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& aMap){ this->onSignalWithMap(aMap); });
proxy_.uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& aVariant){ this->onSignalWithVariant(aVariant); }); proxy_->uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& aVariant){ this->onSignalWithVariant(aVariant); });
} }
integrationtests_proxy(const integrationtests_proxy&) = delete;
integrationtests_proxy& operator=(const integrationtests_proxy&) = delete;
integrationtests_proxy(integrationtests_proxy&&) = default;
integrationtests_proxy& operator=(integrationtests_proxy&&) = default;
~integrationtests_proxy() = default; ~integrationtests_proxy() = default;
virtual void onSimpleSignal() = 0; virtual void onSimpleSignal() = 0;
@ -36,178 +41,178 @@ protected:
public: public:
void noArgNoReturn() void noArgNoReturn()
{ {
proxy_.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME); proxy_->callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
} }
int32_t getInt() int32_t getInt()
{ {
int32_t result; int32_t result;
proxy_.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
std::tuple<uint32_t, std::string> getTuple() std::tuple<uint32_t, std::string> getTuple()
{ {
std::tuple<uint32_t, std::string> result; std::tuple<uint32_t, std::string> result;
proxy_.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
double multiply(const int64_t& a, const double& b) double multiply(const int64_t& a, const double& b)
{ {
double result; double result;
proxy_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result); proxy_->callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
return result; return result;
} }
void multiplyWithNoReply(const int64_t& a, const double& b) void multiplyWithNoReply(const int64_t& a, const double& b)
{ {
proxy_.callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply(); proxy_->callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
} }
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0)
{ {
std::vector<int16_t> result; std::vector<int16_t> result;
proxy_.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result); proxy_->callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result; return result;
} }
sdbus::Variant processVariant(const sdbus::Variant& variant) sdbus::Variant processVariant(const sdbus::Variant& variant)
{ {
sdbus::Variant result; sdbus::Variant result;
proxy_.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result); proxy_->callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
return result; return result;
} }
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
{ {
std::map<int32_t, sdbus::Variant> result; std::map<int32_t, sdbus::Variant> result;
proxy_.callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result); proxy_->callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
return result; return result;
} }
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct()
{ {
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> result; sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> result;
proxy_.callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1)
{ {
int32_t result; int32_t result;
proxy_.callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result); proxy_->callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
return result; return result;
} }
uint32_t sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1)
{ {
uint32_t result; uint32_t result;
proxy_.callMethod("sumVectorItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result); proxy_->callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
return result; return result;
} }
uint32_t doOperation(const uint32_t& arg0) uint32_t doOperation(const uint32_t& arg0)
{ {
uint32_t result; uint32_t result;
proxy_.callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result); proxy_->callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result; return result;
} }
uint32_t doOperationAsync(const uint32_t& arg0) uint32_t doOperationAsync(const uint32_t& arg0)
{ {
uint32_t result; uint32_t result;
proxy_.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result); proxy_->callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result; return result;
} }
sdbus::Signature getSignature() sdbus::Signature getSignature()
{ {
sdbus::Signature result; sdbus::Signature result;
proxy_.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
sdbus::ObjectPath getObjPath() sdbus::ObjectPath getObjPath()
{ {
sdbus::ObjectPath result; sdbus::ObjectPath result;
proxy_.callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
sdbus::UnixFd getUnixFd() sdbus::UnixFd getUnixFd()
{ {
sdbus::UnixFd result; sdbus::UnixFd result;
proxy_.callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex()
{ {
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> result; std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> result;
proxy_.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
void throwError() void throwError()
{ {
proxy_.callMethod("throwError").onInterface(INTERFACE_NAME); proxy_->callMethod("throwError").onInterface(INTERFACE_NAME);
} }
void throwErrorWithNoReply() void throwErrorWithNoReply()
{ {
proxy_.callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply(); proxy_->callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
} }
void doPrivilegedStuff() void doPrivilegedStuff()
{ {
proxy_.callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME); proxy_->callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
} }
void emitTwoSimpleSignals() void emitTwoSimpleSignals()
{ {
proxy_.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME); proxy_->callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
} }
void unregisterSimpleSignalHandler() void unregisterSimpleSignalHandler()
{ {
proxy_.muteSignal("simpleSignal").onInterface(INTERFACE_NAME); proxy_->muteSignal("simpleSignal").onInterface(INTERFACE_NAME);
} }
void reRegisterSimpleSignalHandler() void reRegisterSimpleSignalHandler()
{ {
proxy_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }); proxy_->uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
proxy_.finishRegistration(); proxy_->finishRegistration();
} }
public: public:
uint32_t action() uint32_t action()
{ {
return proxy_.getProperty("action").onInterface(INTERFACE_NAME); return proxy_->getProperty("action").onInterface(INTERFACE_NAME);
} }
void action(const uint32_t& value) void action(const uint32_t& value)
{ {
proxy_.setProperty("action").onInterface(INTERFACE_NAME).toValue(value); proxy_->setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
} }
bool blocking() bool blocking()
{ {
return proxy_.getProperty("blocking").onInterface(INTERFACE_NAME); return proxy_->getProperty("blocking").onInterface(INTERFACE_NAME);
} }
void blocking(const bool& value) void blocking(const bool& value)
{ {
proxy_.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value); proxy_->setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
} }
std::string state() std::string state()
{ {
return proxy_.getProperty("state").onInterface(INTERFACE_NAME); return proxy_->getProperty("state").onInterface(INTERFACE_NAME);
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
}} // namespaces }} // namespaces

View File

@ -45,7 +45,7 @@
<arg type="(ix)" direction="in" /> <arg type="(ix)" direction="in" />
<arg type="i" direction="out" /> <arg type="i" direction="out" />
</method> </method>
<method name="sumVectorItems"> <method name="sumArrayItems">
<arg type="aq" direction="in" /> <arg type="aq" direction="in" />
<arg type="at" direction="in" /> <arg type="at" direction="in" />
<arg type="u" direction="out" /> <arg type="u" direction="out" />
@ -82,7 +82,7 @@
</method> </method>
<method name="emitTwoSimpleSignals"> <method name="emitTwoSimpleSignals">
</method> </method>
<signal name="simpleSignal"> <signal name="simpleSignal">
<annotation name="org.freedesktop.DBus.Deprecated" value="true" /> <annotation name="org.freedesktop.DBus.Deprecated" value="true" />
</signal> </signal>
@ -102,4 +102,4 @@
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
</property> </property>
</interface> </interface>
</node> </node>

View File

@ -20,19 +20,24 @@ public:
protected: protected:
perftests_adaptor(sdbus::IObject& object) perftests_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
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("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_->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"); object_->registerSignal("dataSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("data");
} }
perftests_adaptor(const perftests_adaptor&) = delete;
perftests_adaptor& operator=(const perftests_adaptor&) = delete;
perftests_adaptor(perftests_adaptor&&) = default;
perftests_adaptor& operator=(perftests_adaptor&&) = default;
~perftests_adaptor() = default; ~perftests_adaptor() = default;
public: public:
void emitDataSignal(const std::string& data) void emitDataSignal(const std::string& data)
{ {
object_.emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data); object_->emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
} }
private: private:
@ -40,7 +45,7 @@ private:
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) = 0; virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}} // namespaces }} // namespaces

View File

@ -20,11 +20,16 @@ public:
protected: protected:
perftests_proxy(sdbus::IProxy& proxy) perftests_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
proxy_.uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); }); proxy_->uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); });
} }
perftests_proxy(const perftests_proxy&) = delete;
perftests_proxy& operator=(const perftests_proxy&) = delete;
perftests_proxy(perftests_proxy&&) = default;
perftests_proxy& operator=(perftests_proxy&&) = default;
~perftests_proxy() = default; ~perftests_proxy() = default;
virtual void onDataSignal(const std::string& data) = 0; virtual void onDataSignal(const std::string& data) = 0;
@ -32,18 +37,18 @@ protected:
public: public:
void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize)
{ {
proxy_.callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize); proxy_->callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
} }
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) std::string concatenateTwoStrings(const std::string& string1, const std::string& string2)
{ {
std::string result; std::string result;
proxy_.callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result); proxy_->callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
return result; return result;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
}} // namespaces }} // namespaces

View File

@ -22,18 +22,23 @@ public:
protected: protected:
thermometer_adaptor(sdbus::IObject& object) thermometer_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); }); object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
} }
thermometer_adaptor(const thermometer_adaptor&) = delete;
thermometer_adaptor& operator=(const thermometer_adaptor&) = delete;
thermometer_adaptor(thermometer_adaptor&&) = default;
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
~thermometer_adaptor() = default; ~thermometer_adaptor() = default;
private: private:
virtual uint32_t getCurrentTemperature() = 0; virtual uint32_t getCurrentTemperature() = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}}}} // namespaces }}}} // namespaces

View File

@ -22,22 +22,27 @@ public:
protected: protected:
thermometer_proxy(sdbus::IProxy& proxy) thermometer_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
} }
thermometer_proxy(const thermometer_proxy&) = delete;
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
thermometer_proxy(thermometer_proxy&&) = default;
thermometer_proxy& operator=(thermometer_proxy&&) = default;
~thermometer_proxy() = default; ~thermometer_proxy() = default;
public: public:
uint32_t getCurrentTemperature() uint32_t getCurrentTemperature()
{ {
uint32_t result; uint32_t result;
proxy_.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
}}}} // namespaces }}}} // namespaces

View File

@ -21,25 +21,30 @@ public:
protected: protected:
concatenator_adaptor(sdbus::IObject& object) concatenator_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
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_->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"); object_->registerSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
} }
concatenator_adaptor(const concatenator_adaptor&) = delete;
concatenator_adaptor& operator=(const concatenator_adaptor&) = delete;
concatenator_adaptor(concatenator_adaptor&&) = default;
concatenator_adaptor& operator=(concatenator_adaptor&&) = default;
~concatenator_adaptor() = default; ~concatenator_adaptor() = default;
public: public:
void emitConcatenatedSignal(const std::string& concatenatedString) void emitConcatenatedSignal(const std::string& concatenatedString)
{ {
object_.emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString); object_->emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
} }
private: private:
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) = 0; virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}}} // namespaces }}} // namespaces

View File

@ -21,11 +21,16 @@ public:
protected: protected:
concatenator_proxy(sdbus::IProxy& proxy) concatenator_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
proxy_.uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); }); proxy_->uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
} }
concatenator_proxy(const concatenator_proxy&) = delete;
concatenator_proxy& operator=(const concatenator_proxy&) = delete;
concatenator_proxy(concatenator_proxy&&) = default;
concatenator_proxy& operator=(concatenator_proxy&&) = default;
~concatenator_proxy() = default; ~concatenator_proxy() = default;
virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0; virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0;
@ -35,11 +40,11 @@ protected:
public: public:
sdbus::PendingAsyncCall concatenate(const std::map<std::string, sdbus::Variant>& params) sdbus::PendingAsyncCall concatenate(const std::map<std::string, sdbus::Variant>& params)
{ {
return proxy_.callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); }); return proxy_->callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
}}} // namespaces }}} // namespaces

View File

@ -22,18 +22,23 @@ public:
protected: protected:
thermometer_adaptor(sdbus::IObject& object) thermometer_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); }); object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
} }
thermometer_adaptor(const thermometer_adaptor&) = delete;
thermometer_adaptor& operator=(const thermometer_adaptor&) = delete;
thermometer_adaptor(thermometer_adaptor&&) = default;
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
~thermometer_adaptor() = default; ~thermometer_adaptor() = default;
private: private:
virtual uint32_t getCurrentTemperature() = 0; virtual uint32_t getCurrentTemperature() = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}}}} // namespaces }}}} // namespaces
@ -51,12 +56,17 @@ public:
protected: protected:
factory_adaptor(sdbus::IObject& object) factory_adaptor(sdbus::IObject& object)
: object_(object) : object_(&object)
{ {
object_.registerMethod("createDelegateObject").onInterface(INTERFACE_NAME).withOutputParamNames("delegate").implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); }); 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(); 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(const factory_adaptor&) = delete;
factory_adaptor& operator=(const factory_adaptor&) = delete;
factory_adaptor(factory_adaptor&&) = default;
factory_adaptor& operator=(factory_adaptor&&) = default;
~factory_adaptor() = default; ~factory_adaptor() = default;
private: private:
@ -64,7 +74,7 @@ private:
virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0; virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}}}}} // namespaces }}}}} // namespaces

View File

@ -22,22 +22,27 @@ public:
protected: protected:
thermometer_proxy(sdbus::IProxy& proxy) thermometer_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
} }
thermometer_proxy(const thermometer_proxy&) = delete;
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
thermometer_proxy(thermometer_proxy&&) = default;
thermometer_proxy& operator=(thermometer_proxy&&) = default;
~thermometer_proxy() = default; ~thermometer_proxy() = default;
public: public:
uint32_t getCurrentTemperature() uint32_t getCurrentTemperature()
{ {
uint32_t result; uint32_t result;
proxy_.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
}}}} // namespaces }}}} // namespaces
@ -55,27 +60,32 @@ public:
protected: protected:
factory_proxy(sdbus::IProxy& proxy) factory_proxy(sdbus::IProxy& proxy)
: proxy_(proxy) : proxy_(&proxy)
{ {
} }
factory_proxy(const factory_proxy&) = delete;
factory_proxy& operator=(const factory_proxy&) = delete;
factory_proxy(factory_proxy&&) = default;
factory_proxy& operator=(factory_proxy&&) = default;
~factory_proxy() = default; ~factory_proxy() = default;
public: public:
sdbus::ObjectPath createDelegateObject() sdbus::ObjectPath createDelegateObject()
{ {
sdbus::ObjectPath result; sdbus::ObjectPath result;
proxy_.callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result); proxy_->callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result; return result;
} }
void destroyDelegateObject(const sdbus::ObjectPath& delegate) void destroyDelegateObject(const sdbus::ObjectPath& delegate)
{ {
proxy_.callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply(); proxy_->callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
} }
private: private:
sdbus::IProxy& proxy_; sdbus::IProxy* proxy_;
}; };
}}}}} // namespaces }}}}} // namespaces

View File

@ -12,7 +12,7 @@
<allow send_destination="org.sdbuscpp.stresstests.service1"/> <allow send_destination="org.sdbuscpp.stresstests.service1"/>
<allow send_interface="org.sdbuscpp.stresstests.service1"/> <allow send_interface="org.sdbuscpp.stresstests.service1"/>
</policy> </policy>
<policy context="default"> <policy context="default">
<allow own="org.sdbuscpp.stresstests.service2"/> <allow own="org.sdbuscpp.stresstests.service2"/>
<allow send_destination="org.sdbuscpp.stresstests.service2"/> <allow send_destination="org.sdbuscpp.stresstests.service2"/>

View File

@ -427,8 +427,8 @@ int main(int argc, char *argv[])
while (!stopClients) while (!stopClients)
{ {
std::map<std::string, sdbus::Variant> param; std::map<std::string, sdbus::Variant> param;
param["key1"] = "sdbus-c++-stress-tests"; param["key1"] = sdbus::Variant{"sdbus-c++-stress-tests"};
param["key2"] = ++localCounter; param["key2"] = sdbus::Variant{++localCounter};
concatenator.concatenate(param); concatenator.concatenate(param);
@ -509,6 +509,6 @@ int main(int argc, char *argv[])
exitLogger = true; exitLogger = true;
loggerThread.join(); loggerThread.join();
return 0; return 0;
} }

View File

@ -29,6 +29,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <cstdint> #include <cstdint>
#include <list>
using ::testing::Eq; using ::testing::Eq;
using ::testing::Gt; using ::testing::Gt;
@ -45,6 +46,82 @@ namespace
} }
} }
namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
{
msg.openContainer(sdbus::signature_of<_ElementType>::str());
for (const auto& item : items)
msg << item;
msg.closeContainer();
return msg;
}
template <typename _ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
{
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
return msg;
while (true)
{
_ElementType elem;
if (msg >> elem)
items.emplace_back(std::move(elem));
else
break;
}
msg.clearFlags();
msg.exitContainer();
return msg;
}
}
template <typename _Element, typename _Allocator>
struct sdbus::signature_of<std::list<_Element, _Allocator>>
: sdbus::signature_of<std::vector<_Element, _Allocator>>
{};
namespace my {
struct Struct
{
int i;
std::string s;
std::list<double> l;
};
bool operator==(const Struct& lhs, const Struct& rhs)
{
return lhs.i == rhs.i && lhs.s == rhs.s && lhs.l == rhs.l;
}
sdbus::Message& operator<<(sdbus::Message& msg, const Struct& items)
{
return msg << sdbus::Struct{std::forward_as_tuple(items.i, items.s, items.l)};
}
sdbus::Message& operator>>(sdbus::Message& msg, Struct& items)
{
sdbus::Struct s{std::forward_as_tuple(items.i, items.s, items.l)};
return msg >> s;
}
}
template <>
struct sdbus::signature_of<my::Struct>
: sdbus::signature_of<sdbus::Struct<int, std::string, std::list<double>>>
{};
/*-------------------------------------*/ /*-------------------------------------*/
/* -- TEST CASES -- */ /* -- TEST CASES -- */
/*-------------------------------------*/ /*-------------------------------------*/
@ -116,7 +193,7 @@ TEST(AMessage, CanCarryASimpleInteger)
{ {
auto msg = sdbus::createPlainMessage(); auto msg = sdbus::createPlainMessage();
int dataWritten = 5; const int dataWritten = 5;
msg << dataWritten; msg << dataWritten;
msg.seal(); msg.seal();
@ -131,7 +208,7 @@ TEST(AMessage, CanCarryAUnixFd)
{ {
auto msg = sdbus::createPlainMessage(); auto msg = sdbus::createPlainMessage();
sdbus::UnixFd dataWritten{0}; const sdbus::UnixFd dataWritten{0};
msg << dataWritten; msg << dataWritten;
msg.seal(); msg.seal();
@ -146,7 +223,7 @@ TEST(AMessage, CanCarryAVariant)
{ {
auto msg = sdbus::createPlainMessage(); auto msg = sdbus::createPlainMessage();
auto dataWritten = sdbus::Variant((double)3.14); const auto dataWritten = sdbus::Variant((double)3.14);
msg << dataWritten; msg << dataWritten;
msg.seal(); msg.seal();
@ -161,8 +238,8 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
{ {
auto msg = sdbus::createPlainMessage(); auto msg = sdbus::createPlainMessage();
auto value = std::vector<sdbus::Variant>{"hello"s, (double)3.14}; std::vector<sdbus::Variant> value{sdbus::Variant{"hello"s}, sdbus::Variant{(double)3.14}};
auto dataWritten = sdbus::Variant{value}; const auto dataWritten = sdbus::Variant{value};
msg << dataWritten; msg << dataWritten;
msg.seal(); msg.seal();
@ -174,11 +251,11 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
ASSERT_THAT(dataRead.get<decltype(value)>()[1].get<double>(), Eq(value[1].get<double>())); ASSERT_THAT(dataRead.get<decltype(value)>()[1].get<double>(), Eq(value[1].get<double>()));
} }
TEST(AMessage, CanCarryAnArray) TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdVector)
{ {
auto msg = sdbus::createPlainMessage(); auto msg = sdbus::createPlainMessage();
std::vector<int64_t> dataWritten{3545342, 43643532, 324325}; const std::vector<int64_t> dataWritten{3545342, 43643532, 324325};
msg << dataWritten; msg << dataWritten;
msg.seal(); msg.seal();
@ -189,6 +266,116 @@ TEST(AMessage, CanCarryAnArray)
ASSERT_THAT(dataRead, Eq(dataWritten)); ASSERT_THAT(dataRead, Eq(dataWritten));
} }
TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdVector)
{
auto msg = sdbus::createPlainMessage();
const std::vector<sdbus::Signature> dataWritten{"s", "u", "b"};
msg << dataWritten;
msg.seal();
std::vector<sdbus::Signature> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdArray)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> dataWritten{3545342, 43643532, 324325};
msg << dataWritten;
msg.seal();
std::array<int, 3> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
{
auto msg = sdbus::createPlainMessage();
const std::array<sdbus::Signature, 3> dataWritten{"s", "u", "b"};
msg << dataWritten;
msg.seal();
std::array<sdbus::Signature, 3> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
#if __cplusplus >= 202002L
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdSpan)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> sourceArray{3545342, 43643532, 324325};
const std::span dataWritten{sourceArray};
msg << dataWritten;
msg.seal();
std::array<int, 3> destinationArray;
std::span dataRead{destinationArray};
msg >> dataRead;
ASSERT_THAT(std::vector(dataRead.begin(), dataRead.end()), Eq(std::vector(dataWritten.begin(), dataWritten.end())));
}
TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan)
{
auto msg = sdbus::createPlainMessage();
const std::array<sdbus::Signature, 3> sourceArray{"s", "u", "b"};
const std::span dataWritten{sourceArray};
msg << dataWritten;
msg.seal();
std::array<sdbus::Signature, 3> destinationArray;
std::span dataRead{destinationArray};
msg >> dataRead;
ASSERT_THAT(std::vector(dataRead.begin(), dataRead.end()), Eq(std::vector(dataWritten.begin(), dataWritten.end())));
}
#endif
TEST(AMessage, ThrowsWhenDestinationStdArrayIsTooSmallDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();
const std::vector<int> dataWritten{3545342, 43643532, 324325, 89789, 15343};
msg << dataWritten;
msg.seal();
std::array<int, 3> dataRead;
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
#if __cplusplus >= 202002L
TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> dataWritten{3545342, 43643532, 324325};
msg << dataWritten;
msg.seal();
std::array<int, 2> destinationArray;
std::span dataRead{destinationArray};
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
#endif
TEST(AMessage, CanCarryADictionary) TEST(AMessage, CanCarryADictionary)
{ {
auto msg = sdbus::createPlainMessage(); auto msg = sdbus::createPlainMessage();
@ -264,3 +451,35 @@ TEST(AMessage, CanPeekContainerContents)
ASSERT_THAT(type, "a"); ASSERT_THAT(type, "a");
ASSERT_THAT(contents, "{is}"); ASSERT_THAT(contents, "{is}");
} }
TEST(AMessage, CanCarryDBusArrayGivenAsCustomType)
{
auto msg = sdbus::createPlainMessage();
const std::list<int64_t> dataWritten{3545342, 43643532, 324325};
//custom::MyType t;
msg << dataWritten;
// msg << t;
msg.seal();
std::list<int64_t> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryDBusStructGivenAsCustomType)
{
auto msg = sdbus::createPlainMessage();
const my::Struct dataWritten{3545342, "hello"s, {3.14, 2.4568546}};
msg << dataWritten;
msg.seal();
my::Struct dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}

View File

@ -77,7 +77,12 @@ namespace
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)") TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)") TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an") TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
TYPE(std::array<int16_t, 3>)HAS_DBUS_TYPE_SIGNATURE("an")
#if __cplusplus >= 202002L
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("ao")
#endif
TYPE(std::map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}") TYPE(std::map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
TYPE(std::unordered_map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
using ComplexType = std::map< using ComplexType = std::map<
uint64_t, uint64_t,
sdbus::Struct< sdbus::Struct<
@ -86,9 +91,10 @@ namespace
std::vector< std::vector<
sdbus::Struct< sdbus::Struct<
sdbus::ObjectPath, sdbus::ObjectPath,
std::array<int16_t, 3>,
bool, bool,
sdbus::Variant, sdbus::Variant,
std::map<int, std::string> std::unordered_map<int, std::string>
> >
> >
>, >,
@ -97,7 +103,7 @@ namespace
const char* const char*
> >
>; >;
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}ghs)}") TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghs)}")
typedef ::testing::Types< bool typedef ::testing::Types< bool
, uint8_t , uint8_t
@ -117,7 +123,12 @@ namespace
, sdbus::Struct<bool> , sdbus::Struct<bool>
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant> , sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
, std::vector<int16_t> , std::vector<int16_t>
, std::array<int16_t, 3>
#if __cplusplus >= 202002L
, std::span<int16_t>
#endif
, std::map<int32_t, int64_t> , std::map<int32_t, int64_t>
, std::unordered_map<int32_t, int64_t>
, ComplexType , ComplexType
> DBusSupportedTypes; > DBusSupportedTypes;

View File

@ -67,7 +67,7 @@ TEST(AVariant, CanBeConstructedFromASimpleValue)
TEST(AVariant, CanBeConstructedFromAComplexValue) TEST(AVariant, CanBeConstructedFromAComplexValue)
{ {
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>; 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)}} }; ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
ASSERT_NO_THROW(sdbus::Variant{value}); ASSERT_NO_THROW(sdbus::Variant{value});
} }
@ -103,7 +103,7 @@ TEST(ASimpleVariant, ReturnsTheSimpleValueWhenAsked)
TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked) TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked)
{ {
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>; 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)}} }; ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value); sdbus::Variant variant(value);
@ -123,7 +123,7 @@ TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains) TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains)
{ {
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>; 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)}} }; ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value); sdbus::Variant variant(value);
@ -143,8 +143,8 @@ TEST(AVariant, CanContainOtherEmbeddedVariants)
{ {
using TypeWithVariants = std::vector<sdbus::Struct<sdbus::Variant, double>>; using TypeWithVariants = std::vector<sdbus::Struct<sdbus::Variant, double>>;
TypeWithVariants value; TypeWithVariants value;
value.emplace_back(sdbus::make_struct(sdbus::Variant("a string"), ANY_DOUBLE)); value.push_back({sdbus::Variant("a string"), ANY_DOUBLE});
value.emplace_back(sdbus::make_struct(sdbus::Variant(ANY_UINT64), ANY_DOUBLE)); value.push_back({sdbus::Variant(ANY_UINT64), ANY_DOUBLE});
sdbus::Variant variant(value); sdbus::Variant variant(value);
@ -172,7 +172,7 @@ TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully) TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
{ {
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>; 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)}} }; ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value); sdbus::Variant variant(value);
auto msg = sdbus::createPlainMessage(); auto msg = sdbus::createPlainMessage();
@ -187,7 +187,7 @@ TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully) TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
{ {
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>; 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)}} }; ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value); sdbus::Variant variant(value);
auto variantCopy1{variant}; auto variantCopy1{variant};
auto variantCopy2 = variant; auto variantCopy2 = variant;
@ -207,15 +207,41 @@ TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
ASSERT_THAT(receivedVariant3.get<decltype(value)>(), Eq(value)); ASSERT_THAT(receivedVariant3.get<decltype(value)>(), Eq(value));
} }
TEST(AStruct, CreatesStructFromTuple) TEST(AStruct, CanBeCreatedFromStdTuple)
{ {
std::tuple<int32_t, std::string> value{1234, "abcd"}; std::tuple<int32_t, std::string> value{1234, "abcd"};
sdbus::Struct<int32_t, std::string> valueStruct{value}; sdbus::Struct valueStruct{value};
ASSERT_THAT(valueStruct.get<0>(), Eq(std::get<0>(value)));
ASSERT_THAT(valueStruct.get<1>(), Eq(std::get<1>(value)));
}
TEST(AStruct, CanProvideItsDataThroughStdGet)
{
std::tuple<int32_t, std::string> value{1234, "abcd"};
sdbus::Struct valueStruct{value};
ASSERT_THAT(std::get<0>(valueStruct), Eq(std::get<0>(value))); ASSERT_THAT(std::get<0>(valueStruct), Eq(std::get<0>(value)));
ASSERT_THAT(std::get<1>(valueStruct), Eq(std::get<1>(value))); ASSERT_THAT(std::get<1>(valueStruct), Eq(std::get<1>(value)));
} }
TEST(AStruct, CanBeUsedLikeStdTupleType)
{
using StructType = sdbus::Struct<int, std::string, bool>;
static_assert(std::tuple_size_v<StructType> == 3);
static_assert(std::is_same_v<std::tuple_element_t<1, StructType>, std::string>);
}
TEST(AStruct, CanBeUsedInStructuredBinding)
{
sdbus::Struct valueStruct(1234, "abcd", true);
auto [first, second, third] = valueStruct;
ASSERT_THAT(std::tie(first, second, third), Eq(std::tuple{1234, "abcd", true}));
}
TEST(AnObjectPath, CanBeConstructedFromCString) TEST(AnObjectPath, CanBeConstructedFromCString)
{ {
const char* aPath = "/some/path"; const char* aPath = "/some/path";

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project(sdbus-c++-tools VERSION 1.2.0) project(sdbus-c++-tools VERSION 1.3.0)
include(GNUInstallDirs) include(GNUInstallDirs)

View File

@ -85,7 +85,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
<< tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl << tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl << "protected:" << endl
<< tab << className << "(sdbus::IObject& object)" << endl << tab << className << "(sdbus::IObject& object)" << endl
<< tab << tab << ": object_(object)" << endl; << tab << tab << ": object_(&object)" << endl;
Nodes methods = interface["method"]; Nodes methods = interface["method"];
Nodes signals = interface["signal"]; Nodes signals = interface["signal"];
@ -111,7 +111,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
if(!annotationRegistration.empty()) if(!annotationRegistration.empty())
{ {
std::stringstream str; std::stringstream str;
str << tab << tab << "object_.setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl; str << tab << tab << "object_->setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl;
annotationRegistration = str.str(); annotationRegistration = str.str();
} }
@ -131,6 +131,12 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
<< propertyRegistration << propertyRegistration
<< tab << "}" << endl << endl; << tab << "}" << endl << endl;
// Rule of Five
body << tab << className << "(const " << className << "&) = delete;" << endl;
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
body << tab << className << "(" << className << "&&) = default;" << endl;
body << tab << className << "& operator=(" << className << "&&) = default;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl; body << tab << "~" << className << "() = default;" << endl << endl;
if (!signalMethods.empty()) if (!signalMethods.empty())
@ -149,7 +155,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
} }
body << "private:" << endl body << "private:" << endl
<< tab << "sdbus::IObject& object_;" << endl << tab << "sdbus::IObject* object_;" << endl
<< "};" << endl << endl << "};" << endl << endl
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl; << std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
@ -186,7 +192,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
} }
else if (annotationName == "org.freedesktop.DBus.Method.Async") else if (annotationName == "org.freedesktop.DBus.Method.Async")
{ {
if (annotationValue == "server" || annotationValue == "clientserver") if (annotationValue == "server" || annotationValue == "clientserver" || annotationValue == "client-server")
async = true; async = true;
} }
else if (annotationName == "org.freedesktop.systemd1.Privileged") else if (annotationName == "org.freedesktop.systemd1.Privileged")
@ -211,7 +217,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
using namespace std::string_literals; using namespace std::string_literals;
registrationSS << tab << tab << "object_.registerMethod(\"" registrationSS << tab << tab << "object_->registerMethod(\""
<< methodName << "\")" << methodName << "\")"
<< ".onInterface(INTERFACE_NAME)" << ".onInterface(INTERFACE_NAME)"
<< (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "") << (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
@ -267,7 +273,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args); std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
signalRegistrationSS << tab << tab signalRegistrationSS << tab << tab
<< "object_.registerSignal(\"" << name << "\")" << "object_->registerSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)"; ".onInterface(INTERFACE_NAME)";
if (args.size() > 0) if (args.size() > 0)
@ -284,7 +290,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl
<< tab << "{" << endl << tab << "{" << endl
<< tab << tab << "object_.emitSignal(\"" << name << "\")" << tab << tab << "object_->emitSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)"; ".onInterface(INTERFACE_NAME)";
if (!argStr.empty()) if (!argStr.empty())
@ -333,7 +339,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl; << "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
} }
registrationSS << tab << tab << "object_.registerProperty(\"" registrationSS << tab << tab << "object_->registerProperty(\""
<< propertyName << "\")" << propertyName << "\")"
<< ".onInterface(INTERFACE_NAME)"; << ".onInterface(INTERFACE_NAME)";

View File

@ -84,7 +84,7 @@ std::string ProxyGenerator::processInterface(Node& interface) const
<< tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl << tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl << "protected:" << endl
<< tab << className << "(sdbus::IProxy& proxy)" << endl << tab << className << "(sdbus::IProxy& proxy)" << endl
<< tab << tab << ": proxy_(proxy)" << endl; << tab << tab << ": proxy_(&proxy)" << endl;
Nodes methods = interface["method"]; Nodes methods = interface["method"];
Nodes signals = interface["signal"]; Nodes signals = interface["signal"];
@ -97,6 +97,12 @@ std::string ProxyGenerator::processInterface(Node& interface) const
<< registration << registration
<< tab << "}" << endl << endl; << tab << "}" << endl << endl;
// Rule of Five
body << tab << className << "(const " << className << "&) = delete;" << endl;
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
body << tab << className << "(" << className << "&&) = default;" << endl;
body << tab << className << "& operator=(" << className << "&&) = default;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl; body << tab << "~" << className << "() = default;" << endl << endl;
if (!declaration.empty()) if (!declaration.empty())
@ -122,7 +128,7 @@ std::string ProxyGenerator::processInterface(Node& interface) const
} }
body << "private:" << endl body << "private:" << endl
<< tab << "sdbus::IProxy& proxy_;" << endl << tab << "sdbus::IProxy* proxy_;" << endl
<< "};" << endl << endl << "};" << endl << endl
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl; << std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
@ -145,19 +151,30 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
bool dontExpectReply{false}; bool dontExpectReply{false};
bool async{false}; bool async{false};
bool future{false}; // Async methods implemented by means of either std::future or callbacks
std::string timeoutValue; std::string timeoutValue;
std::smatch smTimeout; std::smatch smTimeout;
Nodes annotations = (*method)["annotation"]; Nodes annotations = (*method)["annotation"];
for (const auto& annotation : annotations) for (const auto& annotation : annotations)
{ {
if (annotation->get("name") == "org.freedesktop.DBus.Method.NoReply" && annotation->get("value") == "true") const auto annotationName = annotation->get("name");
const auto annotationValue = annotation->get("value");
if (annotationName == "org.freedesktop.DBus.Method.NoReply" && annotationValue == "true")
dontExpectReply = true; dontExpectReply = true;
else if (annotation->get("name") == "org.freedesktop.DBus.Method.Async" else
&& (annotation->get("value") == "client" || annotation->get("value") == "clientserver")) {
async = true; if (annotationName == "org.freedesktop.DBus.Method.Async"
if (annotation->get("name") == "org.freedesktop.DBus.Method.Timeout") && (annotationValue == "client" || annotationValue == "clientserver" || annotationValue == "client-server"))
timeoutValue = annotation->get("value"); async = true;
else if (annotationName == "org.freedesktop.DBus.Method.Async.ClientImpl" && annotationValue == "callback")
future = false;
else if (annotationName == "org.freedesktop.DBus.Method.Async.ClientImpl" && (annotationValue == "future" || annotationValue == "std::future"))
future = true;
}
if (annotationName == "org.freedesktop.DBus.Method.Timeout")
timeoutValue = annotationValue;
} }
if (dontExpectReply && outArgs.size() > 0) if (dontExpectReply && outArgs.size() > 0)
{ {
@ -180,12 +197,13 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
} }
auto retType = outArgsToType(outArgs); auto retType = outArgsToType(outArgs);
auto retTypeBare = outArgsToType(outArgs, true);
std::string inArgStr, inArgTypeStr; std::string inArgStr, inArgTypeStr;
std::tie(inArgStr, inArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(inArgs); std::tie(inArgStr, inArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(inArgs);
std::string outArgStr, outArgTypeStr; std::string outArgStr, outArgTypeStr;
std::tie(outArgStr, outArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(outArgs); std::tie(outArgStr, outArgTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(outArgs);
const std::string realRetType = (async && !dontExpectReply ? "sdbus::PendingAsyncCall" : async ? "void" : retType); const std::string realRetType = (async && !dontExpectReply ? (future ? "std::future<" + retType + ">" : "sdbus::PendingAsyncCall") : async ? "void" : retType);
definitionSS << tab << realRetType << " " << nameSafe << "(" << inArgTypeStr << ")" << endl definitionSS << tab << realRetType << " " << nameSafe << "(" << inArgTypeStr << ")" << endl
<< tab << "{" << endl; << tab << "{" << endl;
@ -200,7 +218,7 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
} }
definitionSS << tab << tab << (async && !dontExpectReply ? "return " : "") definitionSS << tab << tab << (async && !dontExpectReply ? "return " : "")
<< "proxy_.callMethod" << (async ? "Async" : "") << "(\"" << name << "\").onInterface(INTERFACE_NAME)"; << "proxy_->callMethod" << (async ? "Async" : "") << "(\"" << name << "\").onInterface(INTERFACE_NAME)";
if (!timeoutValue.empty()) if (!timeoutValue.empty())
{ {
@ -219,11 +237,18 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
auto nameBigFirst = name; auto nameBigFirst = name;
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0]; nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
definitionSS << ".uponReplyInvoke([this](const sdbus::Error* error" << (outArgTypeStr.empty() ? "" : ", ") << outArgTypeStr << ")" if (future) // Async methods implemented through future
"{ this->on" << nameBigFirst << "Reply(" << outArgStr << (outArgStr.empty() ? "" : ", ") << "error); })"; {
definitionSS << ".getResultAsFuture<" << retTypeBare << ">()";
}
else // Async methods implemented through callbacks
{
definitionSS << ".uponReplyInvoke([this](const sdbus::Error* error" << (outArgTypeStr.empty() ? "" : ", ") << outArgTypeStr << ")"
"{ this->on" << nameBigFirst << "Reply(" << outArgStr << (outArgStr.empty() ? "" : ", ") << "error); })";
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "Reply(" asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "Reply("
<< outArgTypeStr << (outArgTypeStr.empty() ? "" : ", ") << "const sdbus::Error* error) = 0;" << endl; << outArgTypeStr << (outArgTypeStr.empty() ? "" : ", ") << "const sdbus::Error* error) = 0;" << endl;
}
} }
else if (outArgs.size() > 0) else if (outArgs.size() > 0)
{ {
@ -257,7 +282,7 @@ std::tuple<std::string, std::string> ProxyGenerator::processSignals(const Nodes&
std::tie(argStr, argTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(args); std::tie(argStr, argTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(args);
registrationSS << tab << tab << "proxy_" registrationSS << tab << tab << "proxy_"
".uponSignal(\"" << name << "\")" "->uponSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)" ".onInterface(INTERFACE_NAME)"
".call([this](" << argTypeStr << ")" ".call([this](" << argTypeStr << ")"
"{ this->on" << nameBigFirst << "(" << argStr << "); });" << endl; "{ this->on" << nameBigFirst << "(" << argStr << "); });" << endl;
@ -286,7 +311,7 @@ std::string ProxyGenerator::processProperties(const Nodes& properties) const
{ {
propertySS << tab << propertyType << " " << propertyNameSafe << "()" << endl propertySS << tab << propertyType << " " << propertyNameSafe << "()" << endl
<< tab << "{" << endl; << tab << "{" << endl;
propertySS << tab << tab << "return proxy_.getProperty(\"" << propertyName << "\")" propertySS << tab << tab << "return proxy_->getProperty(\"" << propertyName << "\")"
".onInterface(INTERFACE_NAME)"; ".onInterface(INTERFACE_NAME)";
propertySS << ";" << endl << tab << "}" << endl << endl; propertySS << ";" << endl << tab << "}" << endl << endl;
} }
@ -295,7 +320,7 @@ std::string ProxyGenerator::processProperties(const Nodes& properties) const
{ {
propertySS << tab << "void " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl propertySS << tab << "void " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl
<< tab << "{" << endl; << tab << "{" << endl;
propertySS << tab << tab << "proxy_.setProperty(\"" << propertyName << "\")" propertySS << tab << tab << "proxy_->setProperty(\"" << propertyName << "\")"
".onInterface(INTERFACE_NAME)" ".onInterface(INTERFACE_NAME)"
".toValue(" << propertyArg << ")"; ".toValue(" << propertyArg << ")";
propertySS << ";" << endl << tab << "}" << endl << endl; propertySS << ";" << endl << tab << "}" << endl << endl;

View File

@ -87,7 +87,7 @@ static void _parse_signature(const std::string &signature, std::string &type, un
} }
case '\0': case '\0':
{ {
std::cerr << std::cerr <<
"Invalid array definition. Type is missing after '" << signature "Invalid array definition. Type is missing after '" << signature
<< "'." << "'."
<< std::endl; << std::endl;