forked from Kistler-Group/sdbus-cpp
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f3a10a73c | ||
|
|
071c595717 | ||
|
|
91f0d3fca7 | ||
|
|
126ac9ffbe | ||
|
|
7fbfcec455 | ||
|
|
0430ae0ad9 | ||
|
|
6212b12159 | ||
|
|
4389ea39bf | ||
|
|
48ea775531 | ||
|
|
fafe8487ff | ||
|
|
0261d0ec60 | ||
|
|
a1419ee45d | ||
|
|
02ca7212d1 | ||
|
|
8a117f8b42 | ||
|
|
84130b1406 | ||
|
|
c55907069b | ||
|
|
107c6a1a97 | ||
|
|
1b7acaa735 | ||
|
|
b7a038f11f | ||
|
|
c6705faf2f | ||
|
|
e6b87b106c | ||
|
|
50cc636058 | ||
|
|
3d3aa26255 | ||
|
|
14942db075 | ||
|
|
e62472b210 |
+18
-39
@@ -14,21 +14,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-22.04]
|
||||
compiler: [g++]
|
||||
build: [shared-libsystemd]
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
compiler: clang
|
||||
build: shared-libsystemd
|
||||
- os: ubuntu-22.04
|
||||
compiler: g++
|
||||
build: embedded-static-libsystemd
|
||||
- os: ubuntu-22.04
|
||||
compiler: clang
|
||||
build: embedded-static-libsystemd
|
||||
os: [ubuntu-22.04, ubuntu-24.04]
|
||||
compiler: [g++, clang]
|
||||
build: [shared-libsystemd, embedded-static-libsystemd]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: install-libsystemd-toolchain
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
@@ -42,7 +32,7 @@ jobs:
|
||||
- name: install-clang
|
||||
if: matrix.compiler == 'clang'
|
||||
run: |
|
||||
sudo apt-get install -y clang
|
||||
sudo apt-get install -y clang libc++-dev
|
||||
sudo update-alternatives --remove-all cc
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
|
||||
sudo update-alternatives --remove-all c++
|
||||
@@ -51,32 +41,21 @@ jobs:
|
||||
echo "SDBUSCPP_EXTRA_CXX_FLAGS=-stdlib=libc++" >> $GITHUB_ENV
|
||||
# We don't install googletest but we let it be built within sdbus-c++ builds below, since it needs to be built against libc++ for Clang jobs to pass
|
||||
# - name: install-googletest
|
||||
# if: matrix.os == 'ubuntu-22.04'
|
||||
# run: |
|
||||
# sudo apt-get install -y libgmock-dev
|
||||
# - name: install-googletest
|
||||
# if: matrix.os == 'ubuntu-20.04' # On older ubuntus the libgmock-dev package is either unavailable or has faulty pkg-config file, so we build & install manually
|
||||
# run: |
|
||||
# git clone https://github.com/google/googletest.git
|
||||
# cd googletest
|
||||
# mkdir build
|
||||
# cd build
|
||||
# cmake .. -DCMAKE_CXX_FLAGS="$SDBUSCPP_EXTRA_CXX_FLAGS"
|
||||
# cmake --build . -j4
|
||||
# sudo cmake --build . --target install
|
||||
- name: configure-debug-gcc11 # For gcc 11, turn off the annoying deprecated-copy warning
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.compiler == 'g++' && matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Wno-deprecated-copy -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: configure-debug
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
|
||||
if: matrix.build == 'shared-libsystemd' && (matrix.compiler != 'g++' || matrix.os != 'ubuntu-22.04')
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=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_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: configure-with-embedded-libsystemd
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: configure-release-with-embedded-libsystemd
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
mkdir build
|
||||
@@ -92,13 +71,13 @@ jobs:
|
||||
sudo cmake --build . --target install
|
||||
ctest --output-on-failure
|
||||
- name: pack
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
|
||||
if: matrix.build == 'shared-libsystemd'
|
||||
run: |
|
||||
cd build
|
||||
cpack -G DEB
|
||||
- name: 'Upload Artifact'
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04' && matrix.compiler == 'g++'
|
||||
uses: actions/upload-artifact@v3
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.compiler == 'g++'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"
|
||||
path: |
|
||||
@@ -109,7 +88,7 @@ jobs:
|
||||
name: build (freebsd, clang/libc++, basu)
|
||||
runs-on: ubuntu-22.04 # until https://github.com/actions/runner/issues/385
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test in FreeBSD VM
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
|
||||
+8
-6
@@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(sdbus-c++ VERSION 2.0.0 LANGUAGES CXX C)
|
||||
project(sdbus-c++ VERSION 2.2.0 LANGUAGES CXX C)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
@@ -17,7 +17,7 @@ if(NOT SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
set(SDBUSCPP_SDBUS_LIB "default" CACHE STRING "sd-bus implementation library to search for and use (default, systemd, elogind, or basu)")
|
||||
set_property(CACHE SDBUSCPP_SDBUS_LIB PROPERTY STRINGS default systemd elogind basu)
|
||||
else()
|
||||
set(SDBUSCPP_LIBSYSTEMD_VERSION "242" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
|
||||
set(SDBUSCPP_LIBSYSTEMD_VERSION "252" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
|
||||
set(SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS "" CACHE STRING "Additional configuration options to be passed as-is to libsystemd build system")
|
||||
endif()
|
||||
option(SDBUSCPP_INSTALL "Enable installation of sdbus-c++ (downstream projects embedding sdbus-c++ may want to turn this OFF)" ON)
|
||||
@@ -26,7 +26,7 @@ if (SDBUSCPP_BUILD_TESTS)
|
||||
option(SDBUSCPP_BUILD_PERF_TESTS "Build also sdbus-c++ performance tests" OFF)
|
||||
option(SDBUSCPP_BUILD_STRESS_TESTS "Build also sdbus-c++ stress tests" OFF)
|
||||
set(SDBUSCPP_TESTS_INSTALL_PATH "tests/${PROJECT_NAME}" CACHE STRING "Specifies where the test binaries will be installed")
|
||||
set(SDBUSCPP_GOOGLETEST_VERSION 1.10.0 CACHE STRING "Version of gmock library to use")
|
||||
set(SDBUSCPP_GOOGLETEST_VERSION 1.14.0 CACHE STRING "Version of gmock library to use")
|
||||
set(SDBUSCPP_GOOGLETEST_GIT_REPO "https://github.com/google/googletest.git" CACHE STRING "A git repo to clone and build googletest from if gmock is not found in the system")
|
||||
endif()
|
||||
option(SDBUSCPP_BUILD_CODEGEN "Build generator tool for C++ native bindings" OFF)
|
||||
@@ -86,17 +86,19 @@ else()
|
||||
set(SDBUS_LIBS systemd elogind basu) # This is the default search order
|
||||
endif()
|
||||
|
||||
set(MINIMUM_SDBUS_VERSION 238)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
foreach(LIB ${SDBUS_LIBS})
|
||||
if(LIB STREQUAL "systemd")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=238)
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=${MINIMUM_SDBUS_VERSION})
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(SDBUS_IMPL "systemd")
|
||||
set(SDBUS_LIB "libsystemd")
|
||||
break()
|
||||
endif()
|
||||
elseif(LIB STREQUAL "elogind")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=238)
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=${MINIMUM_SDBUS_VERSION})
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(SDBUS_IMPL "elogind")
|
||||
set(SDBUS_LIB "libelogind")
|
||||
@@ -326,6 +328,6 @@ set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON)
|
||||
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libsystemd-dev (>=236)")
|
||||
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libsystemd-dev (>=${MINIMUM_SDBUS_VERSION})")
|
||||
|
||||
include(CPack)
|
||||
|
||||
@@ -291,3 +291,23 @@ v2.0.0
|
||||
- Add `SDBUSCPP_` prefix to CMake configuration variables to avoid conflicts with downstream projects
|
||||
- Require systemd of at least v238
|
||||
- Many other fixes and updates in code, tests, build system, CI, and documentation
|
||||
|
||||
v2.1.0
|
||||
- Add SDBUSCPP_REGISTER_STRUCT macro to conveniently teach sdbus-c++ about user-defined structs
|
||||
- Extend the SDBUSCPP_REGISTER_STRUCT macro with serialization of user-defined structs as dicts, and deserialization of dicts into user-defined structs
|
||||
- Make createPlainMessage() function public
|
||||
- Solve the problem of sending large D-Bus messages properly (through the event loop thread)
|
||||
- Fix partially renamed BUILD_DOXYGEN_DOC CMake option
|
||||
- Change googletest to default version 1.14.0
|
||||
- Add version parameter to the xml2cpp codegen tool
|
||||
- A few other internal refactorings and improvements
|
||||
|
||||
v2.2.0
|
||||
- Add createLightWeightProxy() overload for convenience
|
||||
- Add cookie-related API to Message class
|
||||
- Add async overloads of GetManagedObjects() function
|
||||
- Add signature_of specialization for r-value references
|
||||
- Create a valid sdbus::Error even when errno is invalid or zero
|
||||
- Fix the return type of some slot-returning functions
|
||||
- Fix annotation generation in xml2cpp tool
|
||||
- A few other, mainly CI related updates
|
||||
|
||||
@@ -28,35 +28,39 @@ $ sudo cmake --build . --target install
|
||||
|
||||
* `SDBUSCPP_BUILD_CODEGEN` [boolean]
|
||||
|
||||
Option for building the stub code generator `sdbus-c++-xml2cpp` for generating the adaptor and proxy interfaces out of the D-Bus IDL XML description. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_CODEGEN=ON` flag to turn on building the code gen.
|
||||
Build the codegen tool `sdbus-c++-xml2cpp` for generating the high level C++ bindings out of the D-Bus IDL XML description. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_CODEGEN=ON` flag to turn on building the code gen.
|
||||
|
||||
* `SDBUSCPP_BUILD_DOCS` [boolean]
|
||||
|
||||
Option for including sdbus-c++ documentation files and tutorials. Default value: `ON`. With this option turned on, you may also enable/disable the following option:
|
||||
Include sdbus-c++ documentation files and tutorials. Default value: `ON`. With this option turned on, you may also enable/disable the following option:
|
||||
|
||||
* `BUILD_DOXYGEN_DOC` [boolean]
|
||||
* `SDBUSCPP_BUILD_DOXYGEN_DOCS` [boolean]
|
||||
|
||||
Option for building Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `cmake --build . --target doc`. Default value: `OFF`. Use `-DBUILD_DOXYGEN_DOC=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
|
||||
Build Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `cmake --build . --target doc`. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_DOXYGEN_DOCS=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
|
||||
|
||||
* `SDBUSCPP_BUILD_TESTS` [boolean]
|
||||
|
||||
Option for building sdbus-c++ unit and integration tests, invokable by `cmake --build . --target test` (Note: before invoking `cmake --build . --target test`, make sure you copy `tests/integrationtests/files/org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory). That incorporates downloading and building static libraries of Google Test. Default value: `OFF`. Use `-DBUILD_TESTS=ON` to enable building the tests. With this option turned on, you may also enable/disable the following options:
|
||||
Build sdbus-c++ unit and integration tests, invokable by `cmake --build . --target test` (Note: before invoking `cmake --build . --target test`, make sure you copy `tests/integrationtests/files/org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory). That incorporates downloading and building static libraries of Google Test. Default value: `OFF`. Use `-DBUILD_TESTS=ON` to enable building the tests. With this option turned on, you may also enable/disable the following options:
|
||||
|
||||
* `SDBUSCPP_BUILD_PERF_TESTS` [boolean]
|
||||
|
||||
Option for building sdbus-c++ performance tests. Default value: `OFF`.
|
||||
Build sdbus-c++ performance tests. Default value: `OFF`.
|
||||
|
||||
* `SDBUSCPP_BUILD_STRESS_TESTS` [boolean]
|
||||
|
||||
Option for building sdbus-c++ stress tests. Default value: `OFF`.
|
||||
Build sdbus-c++ stress tests. Default value: `OFF`.
|
||||
|
||||
* `SDBUSCPP_TESTS_INSTALL_PATH` [string]
|
||||
|
||||
Path where the test binaries shall get installed. Default value: `${CMAKE_INSTALL_PREFIX}/tests/sdbus-c++` (previously: `/opt/test/bin`).
|
||||
|
||||
* `SDBUSCPP_BUILD_EXAMPLES` [boolean]
|
||||
|
||||
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`.
|
||||
|
||||
* `SDBUSCPP_BUILD_LIBSYSTEMD` [boolean]
|
||||
|
||||
Option for building libsystemd as a sd-bus implementation when sdbus-c++ is built, and making libsystemd an integral part of sdbus-c++ library. Default value: `OFF`, which means that the sd-bus implementation library (`libsystemd`, `libelogind`, or `basu`) will be searched via `pkg-config` in the system.
|
||||
Build sd-bus (libsystemd library) instead of searching for it in the system, and make it part of sdbus-c++ library. Default value: `OFF`, which means that the sd-bus implementation library (`libsystemd`, `libelogind`, or `basu`) will be searched via `pkg-config` in the system.
|
||||
|
||||
This option may be very helpful in environments where sd-bus implementation library is unavailable (see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information).
|
||||
|
||||
@@ -70,29 +74,25 @@ $ sudo cmake --build . --target install
|
||||
|
||||
* `SDBUSCPP_LIBSYSTEMD_VERSION` [string]
|
||||
|
||||
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`.
|
||||
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `252`, minimum value: `239`.
|
||||
|
||||
* `SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
|
||||
|
||||
Additional options to be passed as-is to the libsystemd build system (meson for systemd v242) in its configure step. Can be used for passing e.g. toolchain file path in case of cross builds. Default value: empty.
|
||||
Additional options to be passed as-is to the libsystemd build system in its configure step. Can be used for passing e.g. toolchain file path in case of cross builds. Default value: empty.
|
||||
|
||||
* `CMAKE_BUILD_TYPE` [string]
|
||||
|
||||
This is a CMake-builtin option. Set to `Release` to build sdbus-c++ for production use. Set to `Debug` if you want to help further develop (and debug) the library :)
|
||||
CMake-builtin option. Set to `Release` to build sdbus-c++ for production use. Set to `Debug` if you want to help further develop (and debug) the library :)
|
||||
|
||||
* `BUILD_SHARED_LIBS` [boolean]
|
||||
|
||||
This is a global CMake flag, promoted in sdbus-c++ project to a CMake option. Use this to control whether sdbus-c++ is built as either a shared or static library. Default value: `ON`.
|
||||
|
||||
* `SDBUSCPP_BUILD_EXAMPLES` [boolean]
|
||||
|
||||
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`
|
||||
Global CMake flag, promoted in sdbus-c++ project to a CMake option. Use this to control whether sdbus-c++ is built as either a shared or static library. Default value: `ON`.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* `C++17` - the library uses C++17 features.
|
||||
* `libsystemd`/`libelogind`/`basu` - libraries containing sd-bus implementation that sdbus-c++ is written around. In case of `libsystemd` and `libelogind`, version >= 236 is needed. (In case you have you're missing any of those sd-bus implementations, don't worry, see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information.)
|
||||
* `C++20/C++17` - the library uses C++20 features, but its public API is backwards compatible with C++17 and provides optional extra features when C++20 features are available
|
||||
* `libsystemd`/`libelogind`/`basu` - libraries containing sd-bus implementation that sdbus-c++ is written around. In case of `libsystemd` and `libelogind`, version >= 238 is needed. (In case you have you're missing any of those sd-bus implementations, don't worry, see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information.)
|
||||
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
|
||||
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
|
||||
* `expat` - necessary when building the xml2cpp binding code generator (`SDBUSCPP_BUILD_CODEGEN` option is `ON`).
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
find_package(Doxygen)
|
||||
|
||||
if(BUILD_DOXYGEN_DOC)
|
||||
if(SDBUSCPP_BUILD_DOXYGEN_DOCS)
|
||||
if(DOXYGEN_FOUND)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
|
||||
|
||||
@@ -27,7 +27,7 @@ if(SDBUSCPP_INSTALL)
|
||||
using-sdbus-c++.md
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
|
||||
|
||||
if (BUILD_DOXYGEN_DOC AND DOXYGEN_FOUND)
|
||||
if (SDBUSCPP_BUILD_DOXYGEN_DOCS AND DOXYGEN_FOUND)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT sdbus-c++-doc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
+117
-41
@@ -20,11 +20,12 @@ Using sdbus-c++ library
|
||||
15. [Using D-Bus properties](#using-d-bus-properties)
|
||||
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
|
||||
17. [Representing D-Bus Types in sdbus-c++](#representing-d-bus-types-in-sdbus-c)
|
||||
18. [Support for match rules](#support-for-match-rules)
|
||||
19. [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections)
|
||||
20. [Using sdbus-c++ in external event loops](#using-sdbus-c-in-external-event-loops)
|
||||
21. [Migrating to sdbus-c++ v2](#migrating-to-sdbus-c-v2)
|
||||
22. [Conclusion](#conclusion)
|
||||
18. [Adding user-defined types to the sdbus-c++ type system](#adding-user-defined-types-to-the-sdbus-c-type-system)
|
||||
19. [Support for match rules](#support-for-match-rules)
|
||||
20. [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections)
|
||||
21. [Using sdbus-c++ in external event loops](#using-sdbus-c-in-external-event-loops)
|
||||
22. [Migrating to sdbus-c++ v2](#migrating-to-sdbus-c-v2)
|
||||
23. [Conclusion](#conclusion)
|
||||
|
||||
Introduction
|
||||
------------
|
||||
@@ -113,7 +114,7 @@ $ ninja libsystemd.so.0.26.0 # or another version number depending which system
|
||||
|
||||
sdbus-c++ provides `SDBUSCPP_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). Also, when distributing, make sure these dependency libraries are installed on the production machine.
|
||||
|
||||
You may additionally set the `SDBUSCPP_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 `SDBUSCPP_LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242, the minimum value is 239).
|
||||
|
||||
Distributing sdbus-c++
|
||||
----------------------
|
||||
@@ -275,7 +276,7 @@ void concatenate(sdbus::MethodCall call)
|
||||
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
throw sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.Concatenator.Error"}, "No numbers provided");
|
||||
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
@@ -310,8 +311,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Register D-Bus methods and signals on the concatenator object, and exports the object.
|
||||
sdbus::InterfaceName interfaceName{"org.sdbuscpp.Concatenator"};
|
||||
concatenator->addVTable( sdbus::MethodVTableItem{"concatenate", sdbus::Signature{"ais"}, {}, sdbus::Signature{"s"}, {}, &concatenate, {}}
|
||||
, sdbus::SignalVTableItem{"concatenated", sdbus::Signature{"s"}, {}, {}} )
|
||||
concatenator->addVTable( sdbus::MethodVTableItem{sdbus::MethodName{"concatenate"}, sdbus::Signature{"ais"}, {}, sdbus::Signature{"s"}, {}, &concatenate, {}}
|
||||
, sdbus::SignalVTableItem{sdbus::MethodName{"concatenated"}, sdbus::Signature{"s"}, {}, {}} )
|
||||
.forInterface(interfaceName);
|
||||
|
||||
// Run the I/O event loop on the bus connection.
|
||||
@@ -364,7 +365,7 @@ int main(int argc, char *argv[])
|
||||
std::vector<int> numbers = {1, 2, 3};
|
||||
std::string separator = ":";
|
||||
|
||||
MethodName concatenate{"concatenate"};
|
||||
sdbus::MethodName concatenate{"concatenate"};
|
||||
// Invoke concatenate on given interface of the object
|
||||
{
|
||||
auto method = concatenatorProxy->createMethodCall(interfaceName, concatenate);
|
||||
@@ -375,7 +376,7 @@ int main(int argc, char *argv[])
|
||||
assert(result == "1:2:3");
|
||||
}
|
||||
|
||||
/ // Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
// Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
{
|
||||
auto method = concatenatorProxy->createMethodCall(interfaceName, concatenate);
|
||||
method << std::vector<int>() << separator;
|
||||
@@ -465,18 +466,18 @@ On the **client** side we likewise need a connection -- just that unlike on the
|
||||
|
||||
* 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. For discussion on options for running an event loop on that connection, see above section [Using D-Bus connections on the server side](#using-d-bus-connections-on-the-server-side).
|
||||
|
||||
* Or -- and this is a simpler approach for simple D-Bus client applications -- we have another option: we let the proxy maintain its own connection (and potentially an associated event loop thread, see below). We have two options here:
|
||||
* Or -- and this is a simpler approach for simple D-Bus client applications -- we have another option: we let the proxy maintain its own connection (and potentially an associated event loop thread, see below). We have two options here:
|
||||
|
||||
* We either create the connection ourselves and `std::move` it to the proxy object factory. The proxy becomes an owner of this connection, and it will be his dedicated connection. This has the advantage that we may choose the type of connection (system, session, remote). Additionally,
|
||||
|
||||
* when created **without** `dont_run_event_loop_thread_t` tag, the proxy **will start** a dedicated event loop thread on that connection;
|
||||
* or, when created **with** `dont_run_event_loop_thread_t` tag, the proxy will start **no** event loop thread on that connection.
|
||||
|
||||
* Or we don't care about connnections at all (proxy factory overloads with no connection parameter). Under the hood, the proxy creates its own connection, to either the session bus (when in a user context) or the system bus otherwise. Additionally:
|
||||
* Or we don't care about connections at all (proxy factory overloads with no connection parameter). Under the hood, the proxy creates its own connection, to either the session bus (when in a user context) or the system bus otherwise. Additionally:
|
||||
* when created **without** `dont_run_event_loop_thread_t` tag, the proxy **will start** a dedicated event loop thread on that connection;
|
||||
* or, when created **with** `dont_run_event_loop_thread_t` tag, the proxy will start **no** event loop thread on that connection.
|
||||
|
||||
A proxy needs an event loop if it's a "**long-lived**" proxy that listens on incoming messages like signals, async call replies, atc. Sharing one connection with its one event loop is more scalable. Starting a dedicated event loop in a proxy is simpler from API perspective, but comes at a performance and resource cost for each proxy creation/destruction, and it hurts scalability. A simple and scalable option are "**short-lived, light-weight**" proxies. Quite a typical use case is that we occasionally need to carry out one or a few D-Bus calls and that's it. We may create a proxy, do the calls, and let go of proxy. Such a light-weight proxy is created when `dont_run_event_loop_thread_t` tag is passed to the proxy factory. Such a proxy **does not spawn** an event loop thread. It only support synchronous D-Bus calls (no signals, no async calls...), and is meant to be created, used right away, and then destroyed immediately.
|
||||
A proxy needs an event loop if it's a "**long-lived**" proxy that listens on incoming messages like signals, async call replies, atc. Sharing one connection with its one event loop is more scalable. Starting a dedicated event loop in a proxy is simpler from API perspective, but comes at a performance and resource cost for each proxy creation/destruction, and it hurts scalability. A simple and scalable option are "**short-lived, light-weight**" proxies. Quite a typical use case is that we occasionally need to carry out one or a few D-Bus calls and that's it. We may create a proxy, do the calls, and let go of proxy. Such a light-weight proxy is created when `dont_run_event_loop_thread_t` tag is passed to the proxy factory (or with `createLightWeightProxy()`). Such a proxy **does not spawn** an event loop thread. It only support synchronous D-Bus calls (no signals, no async calls...), and is meant to be created, used right away, and then destroyed immediately.
|
||||
|
||||
#### Stopping internal I/O event loops graciously
|
||||
|
||||
@@ -519,7 +520,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
throw sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.Concatenator.Error"}, "No numbers provided");
|
||||
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
@@ -534,8 +535,8 @@ int main(int argc, char *argv[])
|
||||
};
|
||||
|
||||
// Register D-Bus methods and signals on the concatenator object, and exports the object.
|
||||
concatenator->addVTable( sdbus::registerMethod("concatenate").implementedAs(std::move(concatenate))
|
||||
, sdbus::registerSignal{"concatenated").withParameters<std::string>() )
|
||||
concatenator->addVTable(sdbus::registerMethod("concatenate").implementedAs(std::move(concatenate)),
|
||||
sdbus::registerSignal("concatenated").withParameters<std::string>())
|
||||
.forInterface("org.sdbuscpp.Concatenator");
|
||||
|
||||
// Run the loop on the connection.
|
||||
@@ -854,7 +855,7 @@ protected:
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
throw sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.Concatenator.Error"}, "No numbers provided");
|
||||
|
||||
// Concatenate the numbers
|
||||
std::string result;
|
||||
@@ -1577,9 +1578,9 @@ To see how C++ types are mapped to D-Bus types (including container types) in sd
|
||||
|
||||
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
|
||||
## Adding user-defined types to the 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. You can implement additional mapping between a D-Bus type and their custom type.
|
||||
The above mapping between D-Bus and C++ types is what sdbus-c++ provides by default. However, the mapping can be extended. We can implement additional mapping between a D-Bus type and our custom type, i.e. teach sdbus-c++ to recognize and accept our own C++ types.
|
||||
|
||||
We need two things to do that:
|
||||
|
||||
@@ -1645,7 +1646,11 @@ Then we can simply use `std::list`s, serialize/deserialize them in a D-Bus messa
|
||||
|
||||
Similarly, say we have our own `lockfree_map` which we would like to use natively with sdbus-c++ as a C++ type for D-Bus dictionary -- we can copy or build on top of `std::map` specializations.
|
||||
|
||||
As another example, say we have our custom type `my::Struct` which we'd like to use as a D-Bus structure representation (sdbus-c++ provides `sdbus::Struct` type for that, but we don't want to use it because using our custom type directly is more convenient). Again, we have to provide type traits and message serialization/deserialization functions for our custom type. We build our functions and specializations on top of `sdbus::Struct`, so we don't have to copy and write a lot of boiler-plate. Serialization/deserialization functions can be placed in the same namespace as our custom type, and will be found thanks to the ADR lookup. The `signature_of` specialization must always be in either `sdbus` namespace or in a global namespace:
|
||||
### Teaching sdbus-c++ about user-defined structs
|
||||
|
||||
There is `SDBUSCPP_REGISTER_STRUCT` macro that we can use to teach sdbus-c++ about our structs and unlock some struct-related convenience functionality.
|
||||
|
||||
Say we have our custom type `my::Struct`:
|
||||
|
||||
```c++
|
||||
namespace my {
|
||||
@@ -1655,30 +1660,101 @@ namespace my {
|
||||
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;
|
||||
}
|
||||
} // namespace my
|
||||
|
||||
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.
|
||||
This is how we introduce the struct to sdbus-c++:
|
||||
|
||||
Live examples of extending sdbus-c++ types can be found in [Message unit tests](/tests/unittests/Message_test.cpp).
|
||||
```c++
|
||||
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l);
|
||||
```
|
||||
|
||||
The macro must be placed in the global namespace. The first argument is the struct type name and the remaining arguments are names of struct members. Of course, struct members must be of types supported by sdbus-c++ (or of user-defined types that sdbus-c++ was taught to recognize). This also means that members can be other structs -- provided that sdbus-c++ was taught about them with `SDBUSCPP_REGISTER_STRUCT` prior to this one.
|
||||
|
||||
`SDBUSCPP_REGISTER_STRUCT` enables us:
|
||||
|
||||
* to use user-defined structs in place of (more generic, less expressive) `sdbus::Struct`s
|
||||
* to serialize a user-defined struct as a dictionary of strings to variants (`a{sv}` dictionary)
|
||||
* to deserialize the `a{sv}` dictionary into a user-defined struct.
|
||||
|
||||
This is described in detail in the following sections.
|
||||
|
||||
> **_Note_:** The macro supports **max 16 struct members**. If you need more, feel free to open an issue, or implement the teaching code yourself :o)
|
||||
|
||||
> **_Another note_:** You may have noticed one of `my::Struct` members is `std::list`. Thanks to the custom support for `std::list` implemented higher above, it's now automatically accepted by sdbus-c++ as a D-Bus array representation.
|
||||
|
||||
### Using user-defined structs in place of `sdbus::Struct`
|
||||
|
||||
Many times, we have our own structs defined in our business logic code, and it would be very convenient to pass these structs directly to or from the sdbus-c++ IPC API where a D-Bus struct is expected, without having to translate them to or from `sdbus::Struct`.
|
||||
|
||||
For example, a D-Bus method `foo` that takes an argument of signature `(isad)` can simply be called with `my::Struct` instance instead of `sdbus::Struct<int, std::string, std::vector<dobule>>` instance:
|
||||
|
||||
```c++
|
||||
my::Struct s{77, "hello"s, {3.14, 285.9}};
|
||||
proxy->callMethod("foo").onInterface(INTERFACE_NAME).withArguments(s);
|
||||
```
|
||||
|
||||
For this purpose, the macro simply generates the `sdbus::Message` serialization and deserialization operators and the type traits (the `sdbus::signature_of` specialization) for `my::Struct`.
|
||||
|
||||
Nesting structs is supported by default.
|
||||
|
||||
### Serializing a user-defined struct as the a{sv} dictionary
|
||||
|
||||
`SDBUSCPP_REGISTER_STRUCT` macro additionally teaches sdbus-c++ to serialize our structs as `a{sv}` dictionaries. This can be quite a handy feature.
|
||||
|
||||
For example, a D-Bus method `foo` that takes an argument of signature `a{sv}` can be passed `my::Struct` instance:
|
||||
|
||||
```c++
|
||||
my::Struct s{77, "hello"s, {3.14, 285.9}};
|
||||
proxy->callMethod("foo").onInterface(INTERFACE_NAME).withArguments(sdbus::as_dictionary(s));
|
||||
```
|
||||
|
||||
Decorating the struct instance with `sdbus::as_dictionary()` instructs sdbus-c++ to serialize the struct as an `a{sv}` dictionary, with struct field name being the key and struct field value being the value. Here is a C++ representation of the resulting dictionary:
|
||||
|
||||
```c++
|
||||
std::map<std::string, sdbus::Variant> dict{{"i"s, sdbus::Variant{77}}, {"s"s, sdbus::Variant{"hello"s}}, {"l"s, sdbus::Variant{std::list<double>{3.14, 285.9}}}};
|
||||
```
|
||||
|
||||
The default struct-as-dict serialization strategy is single-level (as opposed to nested). Single-level means that struct members that are structs themselves are serialized as D-Bus structs (the variant in the dict entry contains a struct value). Nested means that also struct members that are structs are all serialized as an `a{sv}` dictionary (the variant in the dict entry contains `a{sv}` dictionary). We can turn on nested serialization with the `SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION` macro:
|
||||
|
||||
```c++
|
||||
SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(my::Struct);
|
||||
```
|
||||
|
||||
If nested strategy is also enabled for the nested struct type, then the same behavior applies for that struct, recursively. (It goes without saying that member struct type needs to be registered through `SDBUSCPP_REGISTER_STRUCT` macro, too.)
|
||||
|
||||
The macro must be placed before the `SDBUSCPP_REGISTER_STRUCT(my::Struct);` macro.
|
||||
|
||||
### Deserializing the a{sv} dictionary into a user-defined struct
|
||||
|
||||
Another handy feature enabled by the `SDBUSCPP_REGISTER_STRUCT` macro is an automatic deserialization of `a{sv}` dictionaries to user-defined structs.
|
||||
|
||||
For example, a D-Bus signal `bar` that carries data of signature `a{sv}` can be deserialized not only into a C++ dictionary type, but also directly into a user-defined struct, leading to shorter and more natural code:
|
||||
|
||||
```c++
|
||||
proxy->uponSignal("bar").onInterface(INTERFACE_NAME).call([](const my::Struct& s){ std::cout << "Got signal with s.i == " << s.i << "\n"; });
|
||||
```
|
||||
|
||||
How easy and convenient, right?
|
||||
|
||||
The requirements:
|
||||
|
||||
* All keys in the dictionary must exactly match the names of fields in the struct. Ordering of struct fields vs. items in the dictionary is irrelevant; the field in the struct is found by its name given by the dict key. If the corresponding struct field is not found, `sdbus::Error` exception is thrown.
|
||||
* The type of value in the dictionary item and the corresponding struct field must also exactly match. Otherwise, `sdbus::Error` exception is thrown.
|
||||
|
||||
The first bullet point is a so-called strict dict-to-struct deserialization strategy. There is also a relaxed one -- meaning that a dict entry key that does not have a matching struct member counterpart is not an error and is silently skipped. We can turn on relaxed deserialization with the `SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION` macro:
|
||||
|
||||
```c++
|
||||
SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(my::Struct);
|
||||
```
|
||||
|
||||
The macro must be placed before the `SDBUSCPP_REGISTER_STRUCT(my::Struct);` macro.
|
||||
|
||||
Real examples of extending sdbus-c++ types, including the use of all above-mentioned struct-related macros, can be found in [Message unit tests](/tests/unittests/Message_test.cpp) and also in test case [SdbusTestObject.CanSendAndReceiveDictionariesAsCustomStructsImplicitly](/tests/integrationtests/DBusMethodsTests.cpp#L266) in integration tests.
|
||||
|
||||
Happy `struct`ing!
|
||||
|
||||
> **_Wait!_:** You might say. What about XML IDL and generated C++ bindings? Well, there is no user-defined struct support in there. Yet. An extended XML syntax would be required. But we may implement something like that in the future (and you can help us).
|
||||
|
||||
Support for match rules
|
||||
-----------------------
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
Error(Name name, std::string message)
|
||||
: std::runtime_error("[" + name + "] " + message)
|
||||
: std::runtime_error(!message.empty() ? "[" + name + "] " + message : "[" + name + "]")
|
||||
, name_(std::move(name))
|
||||
, message_(std::move(message))
|
||||
{
|
||||
@@ -90,7 +90,7 @@ namespace sdbus {
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
Error createError(int errNo, std::string customMsg);
|
||||
Error createError(int errNo, std::string customMsg = {});
|
||||
|
||||
inline const Error::Name SDBUSCPP_ERROR_NAME{"org.sdbuscpp.Error"};
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) = 0;
|
||||
[[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits signal for this object path
|
||||
@@ -419,7 +419,7 @@ namespace sdbus {
|
||||
protected: // Internal API for efficiency reasons used by high-level API helper classes
|
||||
friend SignalEmitter;
|
||||
|
||||
[[nodiscard]] virtual Signal createSignal(const char* interfaceName, const char* signalName) = 0;
|
||||
[[nodiscard]] virtual Signal createSignal(const char* interfaceName, const char* signalName) const = 0;
|
||||
};
|
||||
|
||||
// Out-of-line member definitions
|
||||
|
||||
@@ -364,7 +364,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) = 0;
|
||||
[[nodiscard]] virtual MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the remote D-Bus object
|
||||
@@ -385,7 +385,7 @@ namespace sdbus {
|
||||
* callMethod() function overload, which does not block the bus connection, or do the synchronous
|
||||
* call from another Proxy instance created just before the call and then destroyed (which is
|
||||
* anyway quite a typical approach in D-Bus implementations). Such proxy instance must have
|
||||
* its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread`
|
||||
* its own bus connection. So-called light-weight proxies (ones running without an event loop thread)
|
||||
* tag are designed for exactly that purpose.
|
||||
*
|
||||
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
@@ -416,7 +416,7 @@ namespace sdbus {
|
||||
* callMethod() function overload, which does not block the bus connection, or do the synchronous
|
||||
* call from another Proxy instance created just before the call and then destroyed (which is
|
||||
* anyway quite a typical approach in D-Bus implementations). Such proxy instance must have
|
||||
* its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread`
|
||||
* its own bus connection. So-called light-weight proxies (ones running without an event loop thread)
|
||||
* tag are designed for exactly that purpose.
|
||||
*
|
||||
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
@@ -652,7 +652,7 @@ namespace sdbus {
|
||||
friend AsyncMethodInvoker;
|
||||
friend SignalSubscriber;
|
||||
|
||||
[[nodiscard]] virtual MethodCall createMethodCall(const char* interfaceName, const char* methodName) = 0;
|
||||
[[nodiscard]] virtual MethodCall createMethodCall(const char* interfaceName, const char* methodName) const = 0;
|
||||
virtual void registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler ) = 0;
|
||||
@@ -889,7 +889,7 @@ namespace sdbus {
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
* @brief Creates a light-weight 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
|
||||
@@ -916,6 +916,15 @@ namespace sdbus {
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* Does the same thing as createProxy(std::unique_ptr<sdbus::IConnection>&&, ServiceName, ObjectPath, dont_run_event_loop_thread_t);
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createLightWeightProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
*
|
||||
@@ -937,7 +946,7 @@ namespace sdbus {
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
* @brief Creates a light-weight 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
|
||||
@@ -958,6 +967,13 @@ namespace sdbus {
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* Does the same thing as createProxy(ServiceName, ObjectPath, dont_run_event_loop_thread_t);
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath);
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
|
||||
+69
-45
@@ -58,7 +58,7 @@ namespace sdbus {
|
||||
class UnixFd;
|
||||
class MethodReply;
|
||||
namespace internal {
|
||||
class ISdBus;
|
||||
class IConnection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +114,8 @@ namespace sdbus {
|
||||
#endif
|
||||
template <typename _Enum, typename = std::enable_if_t<std::is_enum_v<_Enum>>>
|
||||
Message& operator<<(const _Enum& item);
|
||||
template <typename _Key, typename _Value>
|
||||
Message& operator<<(const DictEntry<_Key, _Value>& value);
|
||||
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>
|
||||
@@ -150,6 +152,8 @@ namespace sdbus {
|
||||
#endif
|
||||
template <typename _Enum, typename = std::enable_if_t<std::is_enum_v<_Enum>>>
|
||||
Message& operator>>(_Enum& item);
|
||||
template <typename _Key, typename _Value>
|
||||
Message& operator>>(DictEntry<_Key, _Value>& value);
|
||||
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>
|
||||
@@ -196,6 +200,13 @@ namespace sdbus {
|
||||
Message& appendArray(char type, const void *ptr, size_t size);
|
||||
Message& readArray(char type, const void **ptr, size_t *size);
|
||||
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
Message& serializeDictionary(const _Callback& callback);
|
||||
template <typename _Key, typename _Value>
|
||||
Message& serializeDictionary(const std::initializer_list<DictEntry<_Key, _Value>>& dictEntries);
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
Message& deserializeDictionary(const _Callback& callback);
|
||||
|
||||
explicit operator bool() const;
|
||||
void clearFlags();
|
||||
|
||||
@@ -204,6 +215,7 @@ namespace sdbus {
|
||||
const char* getSender() const;
|
||||
const char* getPath() const;
|
||||
const char* getDestination() const;
|
||||
uint64_t getCookie() const;
|
||||
// TODO: short docs in whole Message API
|
||||
std::pair<char, const char*> peekType() const;
|
||||
bool isValid() const;
|
||||
@@ -238,22 +250,17 @@ namespace sdbus {
|
||||
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:
|
||||
Message() = default;
|
||||
explicit Message(internal::ISdBus* sdbus) noexcept;
|
||||
Message(void *msg, internal::ISdBus* sdbus) noexcept;
|
||||
Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
|
||||
explicit Message(internal::IConnection* connection) noexcept;
|
||||
Message(void *msg, internal::IConnection* connection) noexcept;
|
||||
Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
|
||||
|
||||
friend Factory;
|
||||
|
||||
protected:
|
||||
void* msg_{};
|
||||
internal::ISdBus* sdbus_{};
|
||||
internal::IConnection* connection_{};
|
||||
mutable bool ok_{true};
|
||||
};
|
||||
|
||||
@@ -275,7 +282,7 @@ namespace sdbus {
|
||||
bool doesntExpectReply() const;
|
||||
|
||||
protected:
|
||||
MethodCall(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
|
||||
MethodCall(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
|
||||
|
||||
private:
|
||||
MethodReply sendWithReply(uint64_t timeout = 0) const;
|
||||
@@ -290,6 +297,7 @@ namespace sdbus {
|
||||
public:
|
||||
MethodReply() = default;
|
||||
void send() const;
|
||||
uint64_t getReplyCookie() const;
|
||||
};
|
||||
|
||||
class Signal : public Message
|
||||
@@ -332,6 +340,8 @@ namespace sdbus {
|
||||
PlainMessage() = default;
|
||||
};
|
||||
|
||||
PlainMessage createPlainMessage();
|
||||
|
||||
template <typename ...Elements>
|
||||
inline Message& Message::operator<<(const std::variant<Elements...>& value)
|
||||
{
|
||||
@@ -400,10 +410,21 @@ namespace sdbus {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& Message::operator<<(const DictEntry<_Key, _Value>& value)
|
||||
{
|
||||
openDictEntry<_Key, _Value>();
|
||||
*this << value.first;
|
||||
*this << value.second;
|
||||
closeDictEntry();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items)
|
||||
{
|
||||
serializeDictionary(items);
|
||||
serializeDictionary<_Key, _Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -411,28 +432,27 @@ namespace sdbus {
|
||||
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);
|
||||
serializeDictionary<_Key, _Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Dictionary>
|
||||
inline void Message::serializeDictionary(const _Dictionary& items)
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& Message::serializeDictionary(const std::initializer_list<DictEntry<_Key, _Value>>& items)
|
||||
{
|
||||
using KeyType = typename _Dictionary::key_type;
|
||||
using MappedType = typename _Dictionary::mapped_type;
|
||||
serializeDictionary<_Key, _Value>([&](Message& msg){ for (const auto& item : items) msg << item; });
|
||||
|
||||
openContainer<DictEntry<KeyType, MappedType>>();
|
||||
|
||||
for (const auto& item : items)
|
||||
{
|
||||
openDictEntry<KeyType, MappedType>();
|
||||
*this << item.first;
|
||||
*this << item.second;
|
||||
closeDictEntry();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
inline Message& Message::serializeDictionary(const _Callback& callback)
|
||||
{
|
||||
openContainer<DictEntry<_Key, _Value>>();
|
||||
callback(*this);
|
||||
closeContainer();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
@@ -617,10 +637,21 @@ namespace sdbus {
|
||||
exitContainer();
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& Message::operator>>(DictEntry<_Key, _Value>& value)
|
||||
{
|
||||
if (!enterDictEntry<_Key, _Value>())
|
||||
return *this;
|
||||
*this >> value.first >> value.second;
|
||||
exitDictEntry();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items)
|
||||
{
|
||||
deserializeDictionary(items);
|
||||
deserializeDictionary<_Key, _Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -628,37 +659,30 @@ namespace sdbus {
|
||||
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);
|
||||
deserializeDictionary<_Key, _Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Dictionary>
|
||||
inline void Message::deserializeDictionary(_Dictionary& items)
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
inline Message& Message::deserializeDictionary(const _Callback& callback)
|
||||
{
|
||||
using KeyType = typename _Dictionary::key_type;
|
||||
using MappedType = typename _Dictionary::mapped_type;
|
||||
|
||||
if (!enterContainer<DictEntry<KeyType, MappedType>>())
|
||||
return;
|
||||
if (!enterContainer<DictEntry<_Key, _Value>>())
|
||||
return *this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!enterDictEntry<KeyType, MappedType>())
|
||||
DictEntry<_Key, _Value> dictEntry;
|
||||
*this >> dictEntry;
|
||||
if (!*this)
|
||||
break;
|
||||
|
||||
KeyType key;
|
||||
MappedType value;
|
||||
*this >> key >> value;
|
||||
|
||||
items.emplace(std::move(key), std::move(value));
|
||||
|
||||
exitDictEntry();
|
||||
callback(std::move(dictEntry));
|
||||
}
|
||||
|
||||
clearFlags();
|
||||
|
||||
exitContainer();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
|
||||
@@ -168,6 +168,11 @@ namespace sdbus {
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<sdbus::Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback)
|
||||
{
|
||||
@@ -180,11 +185,6 @@ namespace sdbus {
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<sdbus::Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
std::future<sdbus::Variant> GetAsync(std::string_view interfaceName, std::string_view propertyName, with_future_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
@@ -217,11 +217,16 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback)
|
||||
{
|
||||
@@ -229,16 +234,11 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
@@ -261,11 +261,16 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot GetAllAsync(const InterfaceName& interfaceName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(const InterfaceName& interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback)
|
||||
{
|
||||
@@ -273,16 +278,11 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot GetAllAsync(std::string_view interfaceName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(const InterfaceName& interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(std::string_view interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
@@ -344,6 +344,23 @@ namespace sdbus {
|
||||
return objectsInterfacesAndProperties;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetManagedObjectsAsync(_Function&& callback)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot GetManagedObjectsAsync(_Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>>> GetManagedObjectsAsync(with_future_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsFuture<std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>>>();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
@@ -106,6 +106,9 @@ namespace sdbus {
|
||||
// Tag denoting a call where the reply shouldn't be waited for
|
||||
struct dont_expect_reply_t { explicit dont_expect_reply_t() = default; };
|
||||
inline constexpr dont_expect_reply_t dont_expect_reply{};
|
||||
// Tag denoting that the variant shall embed the other variant as its value, instead of creating a copy
|
||||
struct embed_variant_t { explicit embed_variant_t() = default; };
|
||||
inline constexpr embed_variant_t embed_variant{};
|
||||
|
||||
// Helper for static assert
|
||||
template <class... _T> constexpr bool always_false = false;
|
||||
@@ -140,10 +143,18 @@ namespace sdbus {
|
||||
struct signature_of<volatile _T> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<const volatile _T> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<_T&> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<_T&&> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<void>
|
||||
{
|
||||
@@ -269,6 +280,7 @@ namespace sdbus {
|
||||
{
|
||||
static constexpr std::array contents = (signature_of_v<_ValueTypes> + ...);
|
||||
static constexpr std::array value = std::array{'('} + contents + std::array{')'};
|
||||
static constexpr char type_value{'r'}; /* Not actually used in signatures on D-Bus, see specs */
|
||||
static constexpr bool is_valid = true;
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
@@ -313,6 +325,7 @@ namespace sdbus {
|
||||
struct signature_of<DictEntry<_T1, _T2>>
|
||||
{
|
||||
static constexpr std::array value = std::array{'{'} + signature_of_v<std::tuple<_T1, _T2>> + std::array{'}'};
|
||||
static constexpr char type_value{'e'}; /* Not actually used in signatures on D-Bus, see specs */
|
||||
static constexpr bool is_valid = true;
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
@@ -337,17 +350,15 @@ namespace sdbus {
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename _Enum>
|
||||
struct signature_of<_Enum, typename std::enable_if_t<std::is_enum_v<_Enum>>>
|
||||
: public signature_of<std::underlying_type_t<_Enum>>
|
||||
template <typename _Enum> // is_const_v and is_volatile_v to avoid ambiguity conflicts with const and volatile specializations of signature_of
|
||||
struct signature_of<_Enum, typename std::enable_if_t<std::is_enum_v<_Enum> && !std::is_const_v<_Enum> && !std::is_volatile_v<_Enum>>>
|
||||
: signature_of<std::underlying_type_t<_Enum>>
|
||||
{};
|
||||
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
struct signature_of<std::map<_Key, _Value, _Compare, _Allocator>>
|
||||
{
|
||||
static constexpr std::array contents = signature_of_v<std::tuple<_Key, _Value>>;
|
||||
static constexpr std::array dict_entry = std::array{'{'} + contents + std::array{'}'};
|
||||
static constexpr std::array value = std::array{'a'} + dict_entry;
|
||||
static constexpr std::array value = std::array{'a'} + signature_of_v<DictEntry<_Key, _Value>>;
|
||||
static constexpr bool is_valid = true;
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
@@ -571,6 +582,36 @@ namespace sdbus {
|
||||
constexpr bool is_one_of_variants_types<std::variant<_VariantTypes...>, _QueriedType>
|
||||
= (std::is_same_v<_QueriedType, _VariantTypes> || ...);
|
||||
|
||||
// Wrapper (tag) denoting we want to serialize user-defined struct
|
||||
// into a D-Bus message as a dictionary of strings to variants.
|
||||
template <typename _Struct>
|
||||
struct as_dictionary
|
||||
{
|
||||
explicit as_dictionary(const _Struct& s) : m_struct(s) {}
|
||||
const _Struct& m_struct;
|
||||
};
|
||||
|
||||
template <typename _Type>
|
||||
const _Type& as_dictionary_if_struct(const _Type& object)
|
||||
{
|
||||
return object; // identity in case _Type is not struct (user-defined structs shall provide an overload)
|
||||
}
|
||||
|
||||
// By default, the dict-as-struct deserialization strategy is strict.
|
||||
// Strict means that every key of the deserialized dictionary must have its counterpart member in the struct, otherwise an exception is thrown.
|
||||
// Relaxed means that a key that does not have a matching struct member is silently ignored.
|
||||
// The behavior can be overridden for user-defined struct by specializing this variable template.
|
||||
template <typename _Struct>
|
||||
constexpr auto strict_dict_as_struct_deserialization_v = true;
|
||||
|
||||
// By default, the struct-as-dict serialization strategy is single-level only (as opposed to nested).
|
||||
// Single-level means that only the specific struct is serialized as a dictionary, serializing members that are structs always as structs.
|
||||
// Nested means that the struct *and* its members that are structs are all serialized as a dictionary. If nested strategy is also
|
||||
// defined for the nested struct, then the same behavior applies for that struct, recursively.
|
||||
// The behavior can be overridden for user-defined struct by specializing this variable template.
|
||||
template <typename _Struct>
|
||||
constexpr auto nested_struct_as_dict_serialization_v = false;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
|
||||
|
||||
+201
-2
@@ -31,6 +31,7 @@
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@@ -66,6 +67,23 @@ namespace sdbus {
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
Variant(const Variant& value, embed_variant_t) : Variant()
|
||||
{
|
||||
msg_.openVariant<Variant>();
|
||||
msg_ << value;
|
||||
msg_.closeVariant();
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename _Struct>
|
||||
explicit Variant(const as_dictionary<_Struct>& value) : Variant()
|
||||
{
|
||||
msg_.openVariant<std::map<std::string, Variant>>();
|
||||
msg_ << as_dictionary(value.m_struct);
|
||||
msg_.closeVariant();
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename... _Elements>
|
||||
Variant(const std::variant<_Elements...>& value)
|
||||
: Variant()
|
||||
@@ -77,10 +95,10 @@ namespace sdbus {
|
||||
template <typename _ValueType>
|
||||
_ValueType get() const
|
||||
{
|
||||
_ValueType val;
|
||||
msg_.rewind(false);
|
||||
|
||||
msg_.enterVariant<_ValueType>();
|
||||
_ValueType val;
|
||||
msg_ >> val;
|
||||
msg_.exitVariant();
|
||||
return val;
|
||||
@@ -385,14 +403,195 @@ namespace sdbus {
|
||||
|
||||
}
|
||||
|
||||
// Making sdbus::Struct implement the tuple-protocol, i.e. be a tuple-like type
|
||||
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...>>
|
||||
{};
|
||||
|
||||
/********************************************//**
|
||||
* @name SDBUSCPP_REGISTER_STRUCT
|
||||
*
|
||||
* A convenient way to extend sdbus-c++ type system with user-defined structs.
|
||||
*
|
||||
* The macro teaches sdbus-c++ to recognize the user-defined struct
|
||||
* as a valid C++ representation of a D-Bus Struct type, and enables
|
||||
* clients to use their struct conveniently instead of the (too
|
||||
* generic and less expressive) `sdbus::Struct<...>` in sdbus-c++ API.
|
||||
*
|
||||
* It also enables to serialize a user-defined struct as an a{sv} dictionary,
|
||||
* and to deserialize an a{sv} dictionary into the user-defined struct.
|
||||
*
|
||||
* The first argument is the struct type name and the remaining arguments
|
||||
* are names of struct members. Members must be of types supported by
|
||||
* sdbus-c++ (or of user-defined types that sdbus-c++ was taught to support).
|
||||
* Members can be other structs (nesting is supported).
|
||||
* The macro must be placed in the global namespace.
|
||||
*
|
||||
* For example, given the user-defined struct `ABC`:
|
||||
*
|
||||
* namespace foo {
|
||||
* struct ABC
|
||||
* {
|
||||
* int number;
|
||||
* std::string name;
|
||||
* std::vector<double> data;
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* one can teach sdbus-c++ about the contents of this struct simply with:
|
||||
*
|
||||
* SDBUSCPP_REGISTER_STRUCT(foo::ABC, number, name, data);
|
||||
*
|
||||
* Up to 16 struct members are supported by the macro.
|
||||
*
|
||||
***********************************************/
|
||||
#define SDBUSCPP_REGISTER_STRUCT(STRUCT, ...) \
|
||||
namespace sdbus { \
|
||||
static_assert(SDBUSCPP_PP_NARG(__VA_ARGS__) <= 16, \
|
||||
"Not more than 16 struct members are supported, please open an issue if you need more"); \
|
||||
\
|
||||
template <> \
|
||||
struct signature_of<STRUCT> \
|
||||
: signature_of<sdbus::Struct<SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, __VA_ARGS__)>> \
|
||||
{}; \
|
||||
\
|
||||
inline auto as_dictionary_if_struct(const STRUCT& object) \
|
||||
{ \
|
||||
return as_dictionary<STRUCT>(object); \
|
||||
} \
|
||||
\
|
||||
inline sdbus::Message& operator<<(sdbus::Message& msg, const STRUCT& items) \
|
||||
{ \
|
||||
return msg << sdbus::Struct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(items, __VA_ARGS__))}; \
|
||||
} \
|
||||
\
|
||||
inline Message& operator<<(Message& msg, const as_dictionary<STRUCT>& s) \
|
||||
{ \
|
||||
if constexpr (!nested_struct_as_dict_serialization_v<STRUCT>) \
|
||||
return msg.serializeDictionary<std::string, Variant>({SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \
|
||||
else \
|
||||
return msg.serializeDictionary<std::string, Variant>({SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \
|
||||
} \
|
||||
\
|
||||
inline Message& operator>>(Message& msg, STRUCT& s) \
|
||||
{ \
|
||||
/* First, try to deserialize as a struct */ \
|
||||
if (msg.peekType().first == signature_of<STRUCT>::type_value) \
|
||||
{ \
|
||||
Struct sdbusStruct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(s, __VA_ARGS__))}; \
|
||||
return msg >> sdbusStruct; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise try to deserialize as a dictionary of strings to variants */ \
|
||||
\
|
||||
return msg.deserializeDictionary<std::string, Variant>([&s](const auto& dictEntry) \
|
||||
{ \
|
||||
const std::string& key = dictEntry.first; /* Intentionally not using structured bindings */ \
|
||||
const Variant& value = dictEntry.second; \
|
||||
\
|
||||
using namespace std::string_literals; \
|
||||
/* This also handles members which are structs serialized as dict of strings to variants, recursively */ \
|
||||
SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(s, __VA_ARGS__) \
|
||||
SDBUS_THROW_ERROR_IF( strict_dict_as_struct_deserialization_v<STRUCT> \
|
||||
, ("Failed to deserialize struct from a dictionary: could not find field '"s += key) += "' in struct 'my::Struct'" \
|
||||
, EINVAL ); \
|
||||
}); \
|
||||
} \
|
||||
} \
|
||||
/**/
|
||||
|
||||
/********************************************//**
|
||||
* @name SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION
|
||||
*
|
||||
* Enables relaxed deserialization of an a{sv} dictionary into a user-defined struct STRUCT.
|
||||
*
|
||||
* The default (strict) deserialization mode is that if there are entries in the dictionary
|
||||
* which do not have a corresponding field in the struct, an exception is thrown.
|
||||
* In the relaxed mode, such entries are silently skipped.
|
||||
*
|
||||
* The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro,
|
||||
* and must be placed before SDBUSCPP_REGISTER_STRUCT macro.
|
||||
***********************************************/
|
||||
#define SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(STRUCT) \
|
||||
template <> \
|
||||
constexpr auto sdbus::strict_dict_as_struct_deserialization_v<STRUCT> = false; \
|
||||
/**/
|
||||
|
||||
/********************************************//**
|
||||
* @name SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION
|
||||
*
|
||||
* Enables nested serialization of user-defined struct STRUCT as an a{sv} dictionary.
|
||||
*
|
||||
* By default, STRUCT fields which are structs themselves are serialized as D-Bus structs.
|
||||
* This macro tells sdbus-c++ to also serialize nested structs, in a recursive fashion,
|
||||
* as a{sv} dictionaries.
|
||||
*
|
||||
* The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro,
|
||||
* and must be placed before SDBUSCPP_REGISTER_STRUCT macro.
|
||||
***********************************************/
|
||||
#define SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(STRUCT) \
|
||||
template <> \
|
||||
constexpr auto sdbus::nested_struct_as_dict_serialization_v<STRUCT> = true \
|
||||
/**/
|
||||
|
||||
/*!
|
||||
* @cond SDBUSCPP_INTERNAL
|
||||
*
|
||||
* Internal helper preprocessor facilities
|
||||
*/
|
||||
#define SDBUSCPP_STRUCT_MEMBERS(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER(STRUCT, MEMBER) STRUCT.MEMBER
|
||||
|
||||
#define SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_TYPE, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER_TYPE(STRUCT, MEMBER) decltype(STRUCT::MEMBER)
|
||||
|
||||
#define SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{STRUCT.MEMBER}}
|
||||
|
||||
#define SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{as_dictionary_if_struct(STRUCT.MEMBER)}}
|
||||
|
||||
#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER, SDBUSCPP_PP_SPACE, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER(STRUCT, MEMBER) if (key == #MEMBER) STRUCT.MEMBER = value.get<decltype(STRUCT.MEMBER)>(); else
|
||||
|
||||
#define SDBUSCPP_FOR_EACH_1(M, D, S, M1) M(S, M1)
|
||||
#define SDBUSCPP_FOR_EACH_2(M, D, S, M1, M2) M(S, M1) D M(S, M2)
|
||||
#define SDBUSCPP_FOR_EACH_3(M, D, S, M1, M2, M3) M(S, M1) D M(S, M2) D M(S, M3)
|
||||
#define SDBUSCPP_FOR_EACH_4(M, D, S, M1, M2, M3, M4) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4)
|
||||
#define SDBUSCPP_FOR_EACH_5(M, D, S, M1, M2, M3, M4, M5) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5)
|
||||
#define SDBUSCPP_FOR_EACH_6(M, D, S, M1, M2, M3, M4, M5, M6) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6)
|
||||
#define SDBUSCPP_FOR_EACH_7(M, D, S, M1, M2, M3, M4, M5, M6, M7) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7)
|
||||
#define SDBUSCPP_FOR_EACH_8(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8)
|
||||
#define SDBUSCPP_FOR_EACH_9(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9)
|
||||
#define SDBUSCPP_FOR_EACH_10(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10)
|
||||
#define SDBUSCPP_FOR_EACH_11(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11)
|
||||
#define SDBUSCPP_FOR_EACH_12(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12)
|
||||
#define SDBUSCPP_FOR_EACH_13(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13)
|
||||
#define SDBUSCPP_FOR_EACH_14(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14)
|
||||
#define SDBUSCPP_FOR_EACH_15(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15)
|
||||
#define SDBUSCPP_FOR_EACH_16(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15, M16) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15) D M(S, M16)
|
||||
|
||||
#define SDBUSCPP_PP_CAT(X, Y) SDBUSCPP_PP_CAT_IMPL(X, Y)
|
||||
#define SDBUSCPP_PP_CAT_IMPL(X, Y) X##Y
|
||||
#define SDBUSCPP_PP_NARG(...) SDBUSCPP_PP_NARG_IMPL(__VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#define SDBUSCPP_PP_NARG_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _N, ...) _N
|
||||
|
||||
#define SDBUSCPP_PP_COMMA ,
|
||||
#define SDBUSCPP_PP_SPACE
|
||||
|
||||
#endif /* SDBUS_CXX_TYPES_H_ */
|
||||
|
||||
+119
-50
@@ -179,16 +179,6 @@ Connection::PollData Connection::getEventLoopPollData() const
|
||||
return {pollData.fd, pollData.events, timeout, eventFd_.fd};
|
||||
}
|
||||
|
||||
const ISdBus& Connection::getSdBusInterface() const
|
||||
{
|
||||
return *sdbus_.get();
|
||||
}
|
||||
|
||||
ISdBus& Connection::getSdBusInterface()
|
||||
{
|
||||
return *sdbus_.get();
|
||||
}
|
||||
|
||||
void Connection::addObjectManager(const ObjectPath& objectPath)
|
||||
{
|
||||
auto r = sdbus_->sd_bus_add_object_manager(bus_.get(), nullptr, objectPath.c_str());
|
||||
@@ -488,7 +478,7 @@ PlainMessage Connection::createPlainMessage() const
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a plain message", -r);
|
||||
|
||||
return Message::Factory::create<PlainMessage>(sdbusMsg, sdbus_.get(), adopt_message);
|
||||
return Message::Factory::create<PlainMessage>(sdbusMsg, const_cast<Connection*>(this), adopt_message);
|
||||
}
|
||||
|
||||
MethodCall Connection::createMethodCall( const ServiceName& destination
|
||||
@@ -515,7 +505,7 @@ MethodCall Connection::createMethodCall( const char* destination
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
|
||||
|
||||
return Message::Factory::create<MethodCall>(sdbusMsg, sdbus_.get(), adopt_message);
|
||||
return Message::Factory::create<MethodCall>(sdbusMsg, const_cast<Connection*>(this), adopt_message);
|
||||
}
|
||||
|
||||
Signal Connection::createSignal( const ObjectPath& objectPath
|
||||
@@ -535,34 +525,7 @@ Signal Connection::createSignal( const char* objectPath
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
|
||||
|
||||
return Message::Factory::create<Signal>(sdbusMsg, sdbus_.get(), adopt_message);
|
||||
}
|
||||
|
||||
MethodReply Connection::callMethod(const MethodCall& message, uint64_t timeout)
|
||||
{
|
||||
// If the call expects reply, this call will block the bus connection from
|
||||
// serving other messages until the reply arrives or the call times out.
|
||||
auto reply = message.send(timeout);
|
||||
|
||||
// Wake up event loop to process messages that may have arrived in the meantime...
|
||||
wakeUpEventLoopIfMessagesInQueue();
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
Slot Connection::callMethod(const MethodCall& message, void* callback, void* userData, uint64_t timeout, return_slot_t)
|
||||
{
|
||||
// TODO: Think of ways of optimizing these three locking/unlocking of sdbus mutex (merge into one call?)
|
||||
auto timeoutBefore = getEventLoopPollData().timeout;
|
||||
auto slot = message.send(callback, userData, timeout, return_slot);
|
||||
auto timeoutAfter = getEventLoopPollData().timeout;
|
||||
|
||||
// An event loop may wait in poll with timeout `t1', while in another thread an async call is made with
|
||||
// timeout `t2'. If `t2' < `t1', then we have to wake up the event loop thread to update its poll timeout.
|
||||
if (timeoutAfter < timeoutBefore)
|
||||
notifyEventLoopToWakeUpFromPoll();
|
||||
|
||||
return slot;
|
||||
return Message::Factory::create<Signal>(sdbusMsg, const_cast<Connection*>(this), adopt_message);
|
||||
}
|
||||
|
||||
void Connection::emitPropertiesChangedSignal( const ObjectPath& objectPath
|
||||
@@ -648,6 +611,105 @@ Slot Connection::registerSignalHandler( const char* sender
|
||||
return {slot, [this](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
}
|
||||
|
||||
sd_bus_message* Connection::incrementMessageRefCount(sd_bus_message* sdbusMsg)
|
||||
{
|
||||
return sdbus_->sd_bus_message_ref(sdbusMsg);
|
||||
}
|
||||
|
||||
sd_bus_message* Connection::decrementMessageRefCount(sd_bus_message* sdbusMsg)
|
||||
{
|
||||
return sdbus_->sd_bus_message_unref(sdbusMsg);
|
||||
}
|
||||
|
||||
int Connection::querySenderCredentials(sd_bus_message* sdbusMsg, uint64_t mask, sd_bus_creds **creds)
|
||||
{
|
||||
return sdbus_->sd_bus_query_sender_creds(sdbusMsg, mask, creds);
|
||||
}
|
||||
|
||||
sd_bus_creds* Connection::incrementCredsRefCount(sd_bus_creds* creds)
|
||||
{
|
||||
return sdbus_->sd_bus_creds_ref(creds);
|
||||
}
|
||||
|
||||
sd_bus_creds* Connection::decrementCredsRefCount(sd_bus_creds* creds)
|
||||
{
|
||||
return sdbus_->sd_bus_creds_unref(creds);
|
||||
}
|
||||
|
||||
sd_bus_message* Connection::callMethod(sd_bus_message* sdbusMsg, uint64_t timeout)
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
|
||||
// This call will block the bus connection from serving other messages
|
||||
// until the reply arrives or the call times out.
|
||||
sd_bus_message* sdbusReply{};
|
||||
auto r = sdbus_->sd_bus_call(nullptr, sdbusMsg, timeout, &sdbusError, &sdbusReply);
|
||||
|
||||
if (sd_bus_error_is_set(&sdbusError))
|
||||
throw Error(Error::Name{sdbusError.name}, sdbusError.message);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
|
||||
|
||||
// Wake up event loop to process messages that may have arrived in the meantime,
|
||||
// or to dispatch the outbound message that hasn't yet been fully sent out.
|
||||
wakeUpEventLoopIfMessagesInQueue();
|
||||
|
||||
return sdbusReply;
|
||||
}
|
||||
|
||||
Slot Connection::callMethodAsync(sd_bus_message* sdbusMsg, sd_bus_message_handler_t callback, void* userData, uint64_t timeout, return_slot_t)
|
||||
{
|
||||
sd_bus_slot *slot{};
|
||||
|
||||
// TODO: Think of ways of optimizing these three locking/unlocking of sdbus mutex (merge into one call?)
|
||||
auto timeoutBefore = getEventLoopPollData().timeout;
|
||||
auto r = sdbus_->sd_bus_call_async(nullptr, &slot, sdbusMsg, (sd_bus_message_handler_t)callback, userData, timeout);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r);
|
||||
auto timeoutAfter = getEventLoopPollData().timeout;
|
||||
|
||||
// An event loop may wait in poll with timeout `t1', while in another thread an async call is made with
|
||||
// timeout `t2'. If `t2' < `t1', then we have to wake up the event loop thread to update its poll timeout.
|
||||
// We also have to wake up the event loop to process the messages that may be in the read/write queues.
|
||||
if (timeoutAfter < timeoutBefore || arePendingMessagesInQueues())
|
||||
notifyEventLoopToWakeUpFromPoll();
|
||||
|
||||
return {slot, [this](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
}
|
||||
|
||||
void Connection::sendMessage(sd_bus_message* sdbusMsg)
|
||||
{
|
||||
auto r = sdbus_->sd_bus_send(nullptr, sdbusMsg, nullptr);
|
||||
|
||||
// Wake up event loop to continue dispatching the (fairly large) outbound message that hasn't yet been fully sent
|
||||
wakeUpEventLoopIfMessagesInQueue();
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send D-Bus message", -r);
|
||||
}
|
||||
|
||||
sd_bus_message* Connection::createMethodReply(sd_bus_message* sdbusMsg)
|
||||
{
|
||||
sd_bus_message* sdbusReply{};
|
||||
|
||||
auto r = sdbus_->sd_bus_message_new_method_return(sdbusMsg, &sdbusReply);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
|
||||
|
||||
return sdbusReply;
|
||||
}
|
||||
|
||||
sd_bus_message* Connection::createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error)
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str());
|
||||
|
||||
sd_bus_message* sdbusErrorReply{};
|
||||
auto r = sdbus_->sd_bus_message_new_method_error(sdbusMsg, &sdbusErrorReply, &sdbusError);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
|
||||
|
||||
return sdbusErrorReply;
|
||||
}
|
||||
|
||||
Connection::BusPtr Connection::openBus(const BusFactory& busFactory)
|
||||
{
|
||||
sd_bus* bus{};
|
||||
@@ -701,11 +763,15 @@ void Connection::notifyEventLoopToWakeUpFromPoll()
|
||||
|
||||
void Connection::wakeUpEventLoopIfMessagesInQueue()
|
||||
{
|
||||
// When doing a sync call, other D-Bus messages may have arrived, waiting in the read queue.
|
||||
// We need this in two cases:
|
||||
// 1. When doing a sync call, other D-Bus messages may have arrived, waiting in the read queue.
|
||||
// In case an event loop is inside a poll in another thread, or an external event loop polls in the
|
||||
// same thread but as an unrelated event source, then we need to wake up the poll explicitly so the
|
||||
// event loop 1. processes all messages in the read queue, 2. updates poll timeout before next poll.
|
||||
if (arePendingMessagesInReadQueue())
|
||||
// 2. Additionally, when sending out messages, these may be too long to be sent out entirely within
|
||||
// the single sd_bus_send() or sd_bus_call_async() call, in which case they are queued in the write
|
||||
// queue. We need to wake up the event loop to continue sending the message until it's fully sent.
|
||||
if (arePendingMessagesInQueues())
|
||||
notifyEventLoopToWakeUpFromPoll();
|
||||
}
|
||||
|
||||
@@ -744,6 +810,8 @@ bool Connection::waitForNextEvent()
|
||||
, {loopExitFd_.fd, POLLIN, 0} };
|
||||
constexpr auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
||||
|
||||
// Are there pending messages in the inbound queue? Then sd-bus will set timeout to 0, so poll() will wake up right away.
|
||||
// Are there pending messages in the outbound queue? Then sd-bus will add POLLOUT to events, so poll() will wake up right away.
|
||||
auto timeout = sdbusPollData.getPollTimeout();
|
||||
auto r = poll(fds, fdsCount, timeout);
|
||||
|
||||
@@ -757,7 +825,7 @@ bool Connection::waitForNextEvent()
|
||||
{
|
||||
auto cleared = eventFd_.clear();
|
||||
SDBUS_THROW_ERROR_IF(!cleared, "Failed to read from the event descriptor", -errno);
|
||||
// Go poll() again, but with up-to-date timeout (which will wake poll() up right away if there are messages to process)
|
||||
// Go poll() again, but with freshly calculated, up-to-date timeout and with up-to-date events to watch
|
||||
return waitForNextEvent();
|
||||
}
|
||||
// Loop exit notification
|
||||
@@ -771,21 +839,22 @@ bool Connection::waitForNextEvent()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Connection::arePendingMessagesInReadQueue() const
|
||||
bool Connection::arePendingMessagesInQueues() const
|
||||
{
|
||||
uint64_t readQueueSize{};
|
||||
uint64_t writeQueueSize{};
|
||||
|
||||
auto r = sdbus_->sd_bus_get_n_queued_read(bus_.get(), &readQueueSize);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get number of pending messages in read queue", -r);
|
||||
auto r = sdbus_->sd_bus_get_n_queued(bus_.get(), &readQueueSize, &writeQueueSize);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get number of pending messages in sd-bus queues", -r);
|
||||
|
||||
return readQueueSize > 0;
|
||||
return readQueueSize > 0 || writeQueueSize > 0;
|
||||
}
|
||||
|
||||
Message Connection::getCurrentlyProcessedMessage() const
|
||||
{
|
||||
auto* sdbusMsg = sdbus_->sd_bus_get_current_message(bus_.get());
|
||||
|
||||
return Message::Factory::create<Message>(sdbusMsg, sdbus_.get());
|
||||
return Message::Factory::create<Message>(sdbusMsg, const_cast<Connection*>(this));
|
||||
}
|
||||
|
||||
template <typename StringBasedType>
|
||||
@@ -804,7 +873,7 @@ int Connection::sdbus_match_callback(sd_bus_message *sdbusMessage, void *userDat
|
||||
assert(matchInfo != nullptr);
|
||||
assert(matchInfo->callback);
|
||||
|
||||
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection.getSdBusInterface());
|
||||
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection);
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ matchInfo->callback(std::move(message)); }, retError);
|
||||
|
||||
@@ -817,7 +886,7 @@ int Connection::sdbus_match_install_callback(sd_bus_message *sdbusMessage, void
|
||||
assert(matchInfo != nullptr);
|
||||
assert(matchInfo->installCallback);
|
||||
|
||||
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection.getSdBusInterface());
|
||||
auto message = Message::Factory::create<PlainMessage>(sdbusMessage, &matchInfo->connection);
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ matchInfo->installCallback(std::move(message)); }, retError);
|
||||
|
||||
|
||||
+15
-7
@@ -120,9 +120,6 @@ namespace sdbus::internal {
|
||||
void detachSdEventLoop() override;
|
||||
sd_event *getSdEventLoop() override;
|
||||
|
||||
[[nodiscard]] const ISdBus& getSdBusInterface() const override;
|
||||
[[nodiscard]] ISdBus& getSdBusInterface() override;
|
||||
|
||||
Slot addObjectVTable( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
@@ -145,9 +142,6 @@ namespace sdbus::internal {
|
||||
, const char* interfaceName
|
||||
, const char* signalName ) const override;
|
||||
|
||||
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
|
||||
Slot callMethod(const MethodCall& message, void* callback, void* userData, uint64_t timeout, return_slot_t) override;
|
||||
|
||||
void emitPropertiesChangedSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) override;
|
||||
@@ -169,6 +163,20 @@ namespace sdbus::internal {
|
||||
, void* userData
|
||||
, return_slot_t ) override;
|
||||
|
||||
sd_bus_message* incrementMessageRefCount(sd_bus_message* sdbusMsg) override;
|
||||
sd_bus_message* decrementMessageRefCount(sd_bus_message* sdbusMsg) override;
|
||||
|
||||
int querySenderCredentials(sd_bus_message* sdbusMsg, uint64_t mask, sd_bus_creds **creds) override;
|
||||
sd_bus_creds* incrementCredsRefCount(sd_bus_creds* creds) override;
|
||||
sd_bus_creds* decrementCredsRefCount(sd_bus_creds* creds) override;
|
||||
|
||||
sd_bus_message* callMethod(sd_bus_message* sdbusMsg, uint64_t timeout) override;
|
||||
Slot callMethodAsync(sd_bus_message* sdbusMsg, sd_bus_message_handler_t callback, void* userData, uint64_t timeout, return_slot_t) override;
|
||||
void sendMessage(sd_bus_message* sdbusMsg) override;
|
||||
|
||||
sd_bus_message* createMethodReply(sd_bus_message* sdbusMsg) override;
|
||||
sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) override;
|
||||
|
||||
private:
|
||||
using BusFactory = std::function<int(sd_bus**)>;
|
||||
using BusPtr = std::unique_ptr<sd_bus, std::function<sd_bus*(sd_bus*)>>;
|
||||
@@ -179,7 +187,7 @@ namespace sdbus::internal {
|
||||
void finishHandshake(sd_bus* bus);
|
||||
bool waitForNextEvent();
|
||||
|
||||
[[nodiscard]] bool arePendingMessagesInReadQueue() const;
|
||||
[[nodiscard]] bool arePendingMessagesInQueues() const;
|
||||
|
||||
void notifyEventLoopToExit();
|
||||
void notifyEventLoopToWakeUpFromPoll();
|
||||
|
||||
+12
-5
@@ -32,17 +32,24 @@
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
sdbus::Error createError(int errNo, std::string customMsg)
|
||||
Error createError(int errNo, std::string customMsg)
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
sd_bus_error_set_errno(&sdbusError, errNo);
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
|
||||
Error::Name name(sdbusError.name);
|
||||
Error::Name name(sd_bus_error_is_set(&sdbusError) ? sdbusError.name : "");
|
||||
std::string message(std::move(customMsg));
|
||||
message.append(" (");
|
||||
message.append(sdbusError.message);
|
||||
message.append(")");
|
||||
if (!message.empty() && sdbusError.message != nullptr)
|
||||
{
|
||||
message.append(" (");
|
||||
message.append(sdbusError.message);
|
||||
message.append(")");
|
||||
}
|
||||
else if (sdbusError.message != nullptr)
|
||||
{
|
||||
message = sdbusError.message;
|
||||
}
|
||||
|
||||
return Error(std::move(name), std::move(message));
|
||||
}
|
||||
|
||||
+20
-10
@@ -51,6 +51,7 @@ namespace sdbus {
|
||||
using MethodName = MemberName;
|
||||
using SignalName = MemberName;
|
||||
using PropertyName = MemberName;
|
||||
class Error;
|
||||
namespace internal {
|
||||
class ISdBus;
|
||||
}
|
||||
@@ -64,9 +65,6 @@ namespace sdbus::internal {
|
||||
public:
|
||||
~IConnection() override = default;
|
||||
|
||||
[[nodiscard]] virtual const ISdBus& getSdBusInterface() const = 0;
|
||||
[[nodiscard]] virtual ISdBus& getSdBusInterface() = 0;
|
||||
|
||||
[[nodiscard]] virtual Slot addObjectVTable( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
@@ -89,13 +87,6 @@ namespace sdbus::internal {
|
||||
, const char* interfaceName
|
||||
, const char* signalName ) const = 0;
|
||||
|
||||
virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout) = 0;
|
||||
[[nodiscard]] virtual Slot callMethod( const MethodCall& message
|
||||
, void* callback
|
||||
, void* userData
|
||||
, uint64_t timeout
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
virtual void emitPropertiesChangedSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) = 0;
|
||||
@@ -116,6 +107,25 @@ namespace sdbus::internal {
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
virtual sd_bus_message* incrementMessageRefCount(sd_bus_message* sdbusMsg) = 0;
|
||||
virtual sd_bus_message* decrementMessageRefCount(sd_bus_message* sdbusMsg) = 0;
|
||||
|
||||
// TODO: Refactor to higher level (Creds class will ownership handling and getters)
|
||||
virtual int querySenderCredentials(sd_bus_message* sdbusMsg, uint64_t mask, sd_bus_creds **creds) = 0;
|
||||
virtual sd_bus_creds* incrementCredsRefCount(sd_bus_creds* creds) = 0;
|
||||
virtual sd_bus_creds* decrementCredsRefCount(sd_bus_creds* creds) = 0;
|
||||
|
||||
virtual sd_bus_message* callMethod(sd_bus_message* sdbusMsg, uint64_t timeout) = 0;
|
||||
[[nodiscard]] virtual Slot callMethodAsync( sd_bus_message* sdbusMsg
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData
|
||||
, uint64_t timeout
|
||||
, return_slot_t ) = 0;
|
||||
virtual void sendMessage(sd_bus_message* sdbusMsg) = 0;
|
||||
|
||||
virtual sd_bus_message* createMethodReply(sd_bus_message* sdbusMsg) = 0;
|
||||
virtual sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createPseudoConnection();
|
||||
|
||||
+2
-1
@@ -90,7 +90,7 @@ namespace sdbus::internal {
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0;
|
||||
virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) = 0;
|
||||
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0;
|
||||
virtual int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret) = 0;
|
||||
virtual int sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) = 0;
|
||||
virtual int sd_bus_flush(sd_bus *bus) = 0;
|
||||
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
|
||||
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) = 0;
|
||||
@@ -98,6 +98,7 @@ namespace sdbus::internal {
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) = 0;
|
||||
|
||||
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) = 0;
|
||||
virtual sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *c) = 0;
|
||||
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) = 0;
|
||||
|
||||
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) = 0;
|
||||
|
||||
+76
-79
@@ -30,7 +30,6 @@
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "ISdBus.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
@@ -40,27 +39,27 @@
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
Message::Message(internal::ISdBus* sdbus) noexcept
|
||||
: sdbus_(sdbus)
|
||||
Message::Message(internal::IConnection* connection) noexcept
|
||||
: connection_(connection)
|
||||
{
|
||||
assert(sdbus_ != nullptr);
|
||||
assert(connection_ != nullptr);
|
||||
}
|
||||
|
||||
Message::Message(void *msg, internal::ISdBus* sdbus) noexcept
|
||||
Message::Message(void *msg, internal::IConnection* connection) noexcept
|
||||
: msg_(msg)
|
||||
, sdbus_(sdbus)
|
||||
, connection_(connection)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
assert(sdbus_ != nullptr);
|
||||
sdbus_->sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
assert(connection_ != nullptr);
|
||||
connection_->incrementMessageRefCount((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message::Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept
|
||||
Message::Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept
|
||||
: msg_(msg)
|
||||
, sdbus_(sdbus)
|
||||
, connection_(connection)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
assert(sdbus_ != nullptr);
|
||||
assert(connection_ != nullptr);
|
||||
}
|
||||
|
||||
Message::Message(const Message& other) noexcept
|
||||
@@ -71,13 +70,13 @@ Message::Message(const Message& other) noexcept
|
||||
Message& Message::operator=(const Message& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
sdbus_ = other.sdbus_;
|
||||
connection_ = other.connection_;
|
||||
ok_ = other.ok_;
|
||||
|
||||
sdbus_->sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
connection_->incrementMessageRefCount((sd_bus_message*)msg_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -90,12 +89,12 @@ Message::Message(Message&& other) noexcept
|
||||
Message& Message::operator=(Message&& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
other.msg_ = nullptr;
|
||||
sdbus_ = other.sdbus_;
|
||||
other.sdbus_ = nullptr;
|
||||
connection_ = other.connection_;
|
||||
other.connection_ = nullptr;
|
||||
ok_ = other.ok_;
|
||||
other.ok_ = true;
|
||||
|
||||
@@ -105,13 +104,17 @@ Message& Message::operator=(Message&& other) noexcept
|
||||
Message::~Message()
|
||||
{
|
||||
if (msg_)
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message& Message::operator<<(bool item)
|
||||
{
|
||||
int intItem = item;
|
||||
|
||||
// Direct sd-bus method, bypassing SdBus mutex, are called here, since Message serialization/deserialization,
|
||||
// as well as getter/setter methods are not thread safe by design. Additionally, they are called frequently,
|
||||
// so some overhead is spared. What is thread-safe in Message class is Message constructors, copy/move operations
|
||||
// and the destructor, so the Message instance can be passed from one thread to another safely.
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a bool value", -r);
|
||||
|
||||
@@ -637,6 +640,14 @@ const char* Message::getDestination() const
|
||||
return sd_bus_message_get_destination((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
uint64_t Message::getCookie() const
|
||||
{
|
||||
uint64_t cookie;
|
||||
auto r = sd_bus_message_get_cookie((sd_bus_message*)msg_, &cookie);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
std::pair<char, const char*> Message::peekType() const
|
||||
{
|
||||
char typeSignature{};
|
||||
@@ -648,7 +659,7 @@ std::pair<char, const char*> Message::peekType() const
|
||||
|
||||
bool Message::isValid() const
|
||||
{
|
||||
return msg_ != nullptr && sdbus_ != nullptr;
|
||||
return msg_ != nullptr && connection_ != nullptr;
|
||||
}
|
||||
|
||||
bool Message::isEmpty() const
|
||||
@@ -661,17 +672,20 @@ bool Message::isAtEnd(bool complete) const
|
||||
return sd_bus_message_at_end((sd_bus_message*)msg_, complete) > 0;
|
||||
}
|
||||
|
||||
// TODO: Create a RAII ownership class for creds with copy&move semantics, doing ref()/unref() under the hood.
|
||||
// Create a method Message::querySenderCreds() that will return an object of this class by value, through IConnection and SdBus mutex.
|
||||
// The class will expose methods like getPid(), getUid(), etc. that will directly call sd_bus_creds_* functions, no need for mutex here.
|
||||
pid_t Message::getCredsPid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
pid_t pid = 0;
|
||||
r = sdbus_->sd_bus_creds_get_pid(creds, &pid);
|
||||
r = sd_bus_creds_get_pid(creds, &pid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred pid", -r);
|
||||
return pid;
|
||||
}
|
||||
@@ -680,12 +694,12 @@ uid_t Message::getCredsUid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_UID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
uid_t uid = (uid_t)-1;
|
||||
r = sdbus_->sd_bus_creds_get_uid(creds, &uid);
|
||||
r = sd_bus_creds_get_uid(creds, &uid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred uid", -r);
|
||||
return uid;
|
||||
}
|
||||
@@ -694,12 +708,12 @@ uid_t Message::getCredsEuid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_EUID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
uid_t euid = (uid_t)-1;
|
||||
r = sdbus_->sd_bus_creds_get_euid(creds, &euid);
|
||||
r = sd_bus_creds_get_euid(creds, &euid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred euid", -r);
|
||||
return euid;
|
||||
}
|
||||
@@ -708,12 +722,12 @@ gid_t Message::getCredsGid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_GID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
gid_t gid = (gid_t)-1;
|
||||
r = sdbus_->sd_bus_creds_get_gid(creds, &gid);
|
||||
r = sd_bus_creds_get_gid(creds, &gid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred gid", -r);
|
||||
return gid;
|
||||
}
|
||||
@@ -722,12 +736,12 @@ gid_t Message::getCredsEgid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_EGID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
gid_t egid = (gid_t)-1;
|
||||
r = sdbus_->sd_bus_creds_get_egid(creds, &egid);
|
||||
r = sd_bus_creds_get_egid(creds, &egid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred egid", -r);
|
||||
return egid;
|
||||
}
|
||||
@@ -736,12 +750,12 @@ std::vector<gid_t> Message::getCredsSupplementaryGids() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_SUPPLEMENTARY_GIDS | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
const gid_t *cGids = nullptr;
|
||||
r = sdbus_->sd_bus_creds_get_supplementary_gids(creds, &cGids);
|
||||
r = sd_bus_creds_get_supplementary_gids(creds, &cGids);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred supplementary gids", -r);
|
||||
|
||||
std::vector<gid_t> gids{};
|
||||
@@ -758,21 +772,21 @@ std::string Message::getSELinuxContext() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_AUGMENT | SD_BUS_CREDS_SELINUX_CONTEXT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
const char *cLabel = nullptr;
|
||||
r = sdbus_->sd_bus_creds_get_selinux_context(creds, &cLabel);
|
||||
r = sd_bus_creds_get_selinux_context(creds, &cLabel);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred selinux context", -r);
|
||||
return cLabel;
|
||||
}
|
||||
|
||||
|
||||
MethodCall::MethodCall( void *msg
|
||||
, internal::ISdBus *sdbus
|
||||
, internal::IConnection *connection
|
||||
, adopt_message_t) noexcept
|
||||
: Message(msg, sdbus, adopt_message)
|
||||
: Message(msg, connection, adopt_message)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -799,70 +813,53 @@ MethodReply MethodCall::send(uint64_t timeout) const
|
||||
|
||||
MethodReply MethodCall::sendWithReply(uint64_t timeout) const
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
auto* sdbusReply = connection_->callMethod((sd_bus_message*)msg_, timeout);
|
||||
|
||||
sd_bus_message* sdbusReply{};
|
||||
auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, timeout, &sdbusError, &sdbusReply);
|
||||
|
||||
if (sd_bus_error_is_set(&sdbusError))
|
||||
throw Error(Error::Name{sdbusError.name}, sdbusError.message);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
|
||||
|
||||
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
|
||||
return Factory::create<MethodReply>(sdbusReply, connection_, adopt_message);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::sendWithNoReply() const
|
||||
{
|
||||
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method with no reply", -r);
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
|
||||
return Factory::create<MethodReply>(); // No reply
|
||||
}
|
||||
|
||||
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout, return_slot_t) const
|
||||
{
|
||||
sd_bus_slot* slot;
|
||||
|
||||
auto r = sdbus_->sd_bus_call_async(nullptr, &slot, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r);
|
||||
|
||||
return {slot, [sdbus_ = sdbus_](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
return connection_->callMethodAsync((sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout, return_slot);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::createReply() const
|
||||
{
|
||||
sd_bus_message* sdbusReply{};
|
||||
auto r = sdbus_->sd_bus_message_new_method_return((sd_bus_message*)msg_, &sdbusReply);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
|
||||
auto* sdbusReply = connection_->createMethodReply((sd_bus_message*)msg_);
|
||||
|
||||
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
|
||||
return Factory::create<MethodReply>(sdbusReply, connection_, adopt_message);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::createErrorReply(const Error& error) const
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str());
|
||||
sd_bus_message* sdbusErrorReply = connection_->createErrorReplyMessage((sd_bus_message*)msg_, error);
|
||||
|
||||
sd_bus_message* sdbusErrorReply{};
|
||||
auto r = sdbus_->sd_bus_message_new_method_error((sd_bus_message*)msg_, &sdbusErrorReply, &sdbusError);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
|
||||
|
||||
return Factory::create<MethodReply>(sdbusErrorReply, sdbus_, adopt_message);
|
||||
return Factory::create<MethodReply>(sdbusErrorReply, connection_, adopt_message);
|
||||
}
|
||||
|
||||
void MethodReply::send() const
|
||||
{
|
||||
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
uint64_t MethodReply::getReplyCookie() const
|
||||
{
|
||||
uint64_t cookie;
|
||||
auto r = sd_bus_message_get_reply_cookie((sd_bus_message*)msg_, &cookie);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
void Signal::send() const
|
||||
{
|
||||
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
void Signal::setDestination(const std::string& destination)
|
||||
@@ -872,7 +869,7 @@ void Signal::setDestination(const std::string& destination)
|
||||
|
||||
void Signal::setDestination(const char* destination)
|
||||
{
|
||||
auto r = sdbus_->sd_bus_message_set_destination((sd_bus_message*)msg_, destination);
|
||||
auto r = sd_bus_message_set_destination((sd_bus_message*)msg_, destination);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
|
||||
}
|
||||
|
||||
@@ -929,7 +926,7 @@ PlainMessage createPlainMessage()
|
||||
// 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.
|
||||
// Additionally, it's light-weight and fast solution.
|
||||
auto& connection = getPseudoConnectionInstance();
|
||||
const auto& connection = getPseudoConnectionInstance();
|
||||
return connection.createPlainMessage();
|
||||
}
|
||||
|
||||
|
||||
+4
-6
@@ -47,19 +47,17 @@ namespace sdbus
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg, internal::ISdBus* sdbus)
|
||||
static _Msg create(void *msg, internal::IConnection* connection)
|
||||
{
|
||||
return _Msg{msg, sdbus};
|
||||
return _Msg{msg, connection};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg, internal::ISdBus* sdbus, adopt_message_t)
|
||||
static _Msg create(void *msg, internal::IConnection* connection, adopt_message_t)
|
||||
{
|
||||
return _Msg{msg, sdbus, adopt_message};
|
||||
return _Msg{msg, connection, adopt_message};
|
||||
}
|
||||
};
|
||||
|
||||
PlainMessage createPlainMessage();
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */
|
||||
|
||||
+5
-5
@@ -83,12 +83,12 @@ void Object::unregister()
|
||||
objectManagerSlot_.reset();
|
||||
}
|
||||
|
||||
sdbus::Signal Object::createSignal(const InterfaceName& interfaceName, const SignalName& signalName)
|
||||
Signal Object::createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const
|
||||
{
|
||||
return connection_.createSignal(objectPath_, interfaceName, signalName);
|
||||
}
|
||||
|
||||
sdbus::Signal Object::createSignal(const char* interfaceName, const char* signalName)
|
||||
Signal Object::createSignal(const char* interfaceName, const char* signalName) const
|
||||
{
|
||||
return connection_.createSignal(objectPath_.c_str(), interfaceName, signalName);
|
||||
}
|
||||
@@ -326,7 +326,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
|
||||
assert(vtable != nullptr);
|
||||
assert(vtable->object != nullptr);
|
||||
|
||||
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &vtable->object->connection_.getSdBusInterface());
|
||||
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &vtable->object->connection_);
|
||||
|
||||
const auto* methodItem = findMethod(*vtable, message.getMemberName());
|
||||
assert(methodItem != nullptr);
|
||||
@@ -359,7 +359,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &vtable->object->connection_.getSdBusInterface());
|
||||
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &vtable->object->connection_);
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->getCallback(reply); }, retError);
|
||||
|
||||
@@ -382,7 +382,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
assert(propertyItem != nullptr);
|
||||
assert(propertyItem->setCallback);
|
||||
|
||||
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &vtable->object->connection_.getSdBusInterface());
|
||||
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &vtable->object->connection_);
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->setCallback(std::move(value)); }, retError);
|
||||
|
||||
|
||||
+2
-2
@@ -53,8 +53,8 @@ namespace sdbus::internal {
|
||||
Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) override;
|
||||
void unregister() override;
|
||||
|
||||
sdbus::Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) override;
|
||||
sdbus::Signal createSignal(const char* interfaceName, const char* signalName) override;
|
||||
Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const override;
|
||||
Signal createSignal(const char* interfaceName, const char* signalName) const override;
|
||||
void emitSignal(const sdbus::Signal& message) override;
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames) override;
|
||||
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames) override;
|
||||
|
||||
+19
-16
@@ -84,12 +84,12 @@ Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
// This proxy is meant to be created, used for simple synchronous D-Bus call(s) and then dismissed.
|
||||
}
|
||||
|
||||
MethodCall Proxy::createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName)
|
||||
MethodCall Proxy::createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const
|
||||
{
|
||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||
}
|
||||
|
||||
MethodCall Proxy::createMethodCall(const char* interfaceName, const char* methodName)
|
||||
MethodCall Proxy::createMethodCall(const char* interfaceName, const char* methodName) const
|
||||
{
|
||||
return connection_->createMethodCall(destination_.c_str(), objectPath_.c_str(), interfaceName, methodName);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
|
||||
|
||||
return connection_->callMethod(message, timeout);
|
||||
return message.send(timeout);
|
||||
}
|
||||
|
||||
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback)
|
||||
@@ -124,11 +124,7 @@ PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_h
|
||||
, .proxy = *this
|
||||
, .floating = false });
|
||||
|
||||
asyncCallInfo->slot = connection_->callMethod( message
|
||||
, (void*)&Proxy::sdbus_async_reply_handler
|
||||
, asyncCallInfo.get()
|
||||
, timeout
|
||||
, return_slot );
|
||||
asyncCallInfo->slot = message.send((void*)&Proxy::sdbus_async_reply_handler, asyncCallInfo.get(), timeout, return_slot);
|
||||
|
||||
auto asyncCallInfoWeakPtr = std::weak_ptr{asyncCallInfo};
|
||||
|
||||
@@ -145,11 +141,7 @@ Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler async
|
||||
, .proxy = *this
|
||||
, .floating = true });
|
||||
|
||||
asyncCallInfo->slot = connection_->callMethod( message
|
||||
, (void*)&Proxy::sdbus_async_reply_handler
|
||||
, asyncCallInfo.get()
|
||||
, timeout
|
||||
, return_slot );
|
||||
asyncCallInfo->slot = message.send((void*)&Proxy::sdbus_async_reply_handler, asyncCallInfo.get(), timeout, return_slot);
|
||||
|
||||
return {asyncCallInfo.release(), [](void *ptr){ delete static_cast<AsyncCallInfo*>(ptr); }};
|
||||
}
|
||||
@@ -259,7 +251,7 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
|
||||
proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
|
||||
};
|
||||
|
||||
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
|
||||
auto message = Message::Factory::create<MethodReply>(sdbusMessage, proxy.connection_.get());
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&]
|
||||
{
|
||||
@@ -284,8 +276,7 @@ int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd
|
||||
assert(signalInfo != nullptr);
|
||||
assert(signalInfo->callback);
|
||||
|
||||
// TODO: Hide Message factory invocation under Connection API (tell, don't ask principle), then we can remove getSdBusInterface()
|
||||
auto message = Message::Factory::create<Signal>(sdbusMessage, &signalInfo->proxy.connection_->getSdBusInterface());
|
||||
auto message = Message::Factory::create<Signal>(sdbusMessage, signalInfo->proxy.connection_.get());
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ signalInfo->callback(std::move(message)); }, retError);
|
||||
|
||||
@@ -409,6 +400,13 @@ std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& conne
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createLightWeightProxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
return createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
@@ -437,4 +435,9 @@ std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath)
|
||||
{
|
||||
return createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -56,8 +56,8 @@ namespace sdbus::internal {
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) override;
|
||||
MethodCall createMethodCall(const char* interfaceName, const char* methodName) override;
|
||||
MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const override;
|
||||
MethodCall createMethodCall(const char* interfaceName, const char* methodName) const override;
|
||||
MethodReply callMethod(const MethodCall& message) override;
|
||||
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
|
||||
PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) override;
|
||||
|
||||
+15
-14
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "SdBus.h"
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
@@ -52,11 +53,6 @@ int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// Make sure long messages are not only stored in outgoing queues but also really sent out
|
||||
// TODO: This is a workaround. We should not block here until everything is physically sent out.
|
||||
// Refactor: if sd_bus_get_n_queued_write() > 0 then wake up event loop through event fd
|
||||
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -75,11 +71,6 @@ int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// Make sure long messages are not only stored in outgoing queues but also really sent out
|
||||
// TODO: This is a workaround. We should not block here until everything is physically sent out.
|
||||
// Refactor: if sd_bus_get_n_queued_write() > 0 then wake up event loop through event fd
|
||||
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -127,7 +118,7 @@ int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
|
||||
#else
|
||||
(void)bus;
|
||||
(void)usec;
|
||||
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Setting general method call timeout not supported by underlying version of libsystemd");
|
||||
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Setting general method call timeout not supported by underlying version of libsystemd");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -140,7 +131,7 @@ int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
|
||||
#else
|
||||
(void)bus;
|
||||
(void)ret;
|
||||
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Getting general method call timeout not supported by underlying version of libsystemd");
|
||||
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Getting general method call timeout not supported by underlying version of libsystemd");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -413,11 +404,14 @@ int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
|
||||
return r;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret)
|
||||
int SdBus::sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_get_n_queued_read(bus, ret);
|
||||
auto r1 = ::sd_bus_get_n_queued_read(bus, read);
|
||||
auto r2 = ::sd_bus_get_n_queued_write(bus, write);
|
||||
|
||||
return std::min(r1, r2);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_flush(sd_bus *bus)
|
||||
@@ -454,6 +448,13 @@ int SdBus::sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_cr
|
||||
return ::sd_bus_query_sender_creds(m, mask, c);
|
||||
}
|
||||
|
||||
sd_bus_creds* SdBus::sd_bus_creds_ref(sd_bus_creds *c)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_ref(c);
|
||||
}
|
||||
|
||||
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *c)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
+2
-1
@@ -82,7 +82,7 @@ public:
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override;
|
||||
virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) override;
|
||||
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
|
||||
virtual int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret) override;
|
||||
virtual int sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) override;
|
||||
virtual int sd_bus_flush(sd_bus *bus) override;
|
||||
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override;
|
||||
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) override;
|
||||
@@ -90,6 +90,7 @@ public:
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) override;
|
||||
|
||||
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) override;
|
||||
virtual sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *c) override;
|
||||
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) override;
|
||||
|
||||
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) override;
|
||||
|
||||
@@ -87,7 +87,7 @@ TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTime
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
|
||||
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchronousMethodAsynchronously)
|
||||
{
|
||||
// Yeah, this is kinda timing-dependent test, but times should be safe...
|
||||
std::mutex mtx;
|
||||
@@ -142,6 +142,19 @@ TYPED_TEST(AsyncSdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncM
|
||||
ASSERT_THAT(resultCount, Eq(1500));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchronousMethodWithLargeMessage)
|
||||
{
|
||||
std::map<int32_t, std::string> largeMap;
|
||||
for (int32_t i = 0; i < 40'000; ++i)
|
||||
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
|
||||
|
||||
auto result1 = this->m_proxy->doOperationAsyncWithLargeData(0, largeMap); // Sends large map back in the context of the callback (event loop thread)
|
||||
auto result2 = this->m_proxy->doOperationAsyncWithLargeData(500, largeMap); // Sends large map back outside the context of the event loop thread
|
||||
|
||||
ASSERT_THAT(result1, Eq(largeMap));
|
||||
ASSERT_THAT(result2, Eq(largeMap));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
@@ -177,6 +190,17 @@ TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutu
|
||||
ASSERT_THAT(returnValue, Eq(100));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodWithLargeDataAsynchronouslyOnClientSideWithFuture)
|
||||
{
|
||||
std::map<int32_t, std::string> largeMap;
|
||||
for (int32_t i = 0; i < 40'000; ++i)
|
||||
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
|
||||
|
||||
auto future = this->m_proxy->doOperationWithLargeDataClientSideAsync(largeMap, sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(largeMap));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
|
||||
{
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){});
|
||||
|
||||
@@ -52,7 +52,7 @@ using ADirectConnection = TestFixtureWithDirectConnection;
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccessfully)
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
connection->requestName(SERVICE_NAME);
|
||||
|
||||
@@ -48,18 +48,32 @@ using ::testing::ElementsAre;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::NotNull;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
namespace my {
|
||||
struct Struct
|
||||
{
|
||||
int i;
|
||||
std::string s;
|
||||
std::vector<double> l;
|
||||
|
||||
friend bool operator==(const Struct &lhs, const Struct &rhs) = default;
|
||||
};
|
||||
}
|
||||
|
||||
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l);
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsEmptyMethodSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsEmptyMethodSuccessfully)
|
||||
{
|
||||
ASSERT_NO_THROW(this->m_proxy->noArgNoReturn());
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccessfully)
|
||||
{
|
||||
auto resInt = this->m_proxy->getInt();
|
||||
ASSERT_THAT(resInt, Eq(INT32_VALUE));
|
||||
@@ -68,14 +82,14 @@ TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithTuplesSuccessfully)
|
||||
{
|
||||
auto resTuple = this->m_proxy->getTuple();
|
||||
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
|
||||
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccessfully)
|
||||
{
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
|
||||
auto vectorRes = this->m_proxy->getInts16FromStruct(a);
|
||||
@@ -88,21 +102,21 @@ TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccesfully)
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithVariantSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithVariantSuccessfully)
|
||||
{
|
||||
sdbus::Variant v{DOUBLE_VALUE};
|
||||
sdbus::Variant variantRes = this->m_proxy->processVariant(v);
|
||||
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStdVariantSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStdVariantSuccessfully)
|
||||
{
|
||||
std::variant<int32_t, double, std::string> v{DOUBLE_VALUE};
|
||||
auto variantRes = this->m_proxy->processVariant(v);
|
||||
ASSERT_THAT(std::get<int32_t>(variantRes), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccessfully)
|
||||
{
|
||||
std::vector<int32_t> x{-2, 0, 2};
|
||||
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
|
||||
@@ -116,44 +130,44 @@ TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
|
||||
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructInStructSuccessfully)
|
||||
{
|
||||
auto val = this->m_proxy->getStructInStruct();
|
||||
ASSERT_THAT(val.template get<0>(), Eq(STRING_VALUE));
|
||||
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoStructsSuccessfully)
|
||||
{
|
||||
auto val = this->m_proxy->sumStructItems({1, 2}, {3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoVectorsSuccessfully)
|
||||
{
|
||||
auto val = this->m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithSignatureSuccessfully)
|
||||
{
|
||||
auto resSignature = this->m_proxy->getSignature();
|
||||
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithObjectPathSuccessfully)
|
||||
{
|
||||
auto resObjectPath = this->m_proxy->getObjPath();
|
||||
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithUnixFdSuccessfully)
|
||||
{
|
||||
auto resUnixFd = this->m_proxy->getUnixFd();
|
||||
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithComplexTypeSuccessfully)
|
||||
{
|
||||
auto resComplex = this->m_proxy->getComplex();
|
||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||
@@ -249,6 +263,21 @@ TYPED_TEST(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
|
||||
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanSendAndReceiveDictionariesAsCustomStructsImplicitly)
|
||||
{
|
||||
// This test demonstrates that sdbus-c++ can send a SDBUSCPP_REGISTER_STRUCT-described struct as a dictionary of strings to variants,
|
||||
// and that sdbus-c++ can automatically deserialize a dictionary of strings to variants into such a struct (instead of a map).
|
||||
const my::Struct structSent{3545342, "hello"s, {3.14, 2.4568546}};
|
||||
my::Struct structReceived;
|
||||
|
||||
this->m_proxy->getProxy().callMethod("returnDictionary")
|
||||
.onInterface("org.sdbuscpp.integrationtests")
|
||||
.withArguments(sdbus::as_dictionary(structSent))
|
||||
.storeResultsTo(structReceived);
|
||||
|
||||
ASSERT_THAT(structReceived, Eq(structSent));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
|
||||
{
|
||||
this->m_proxy->doOperation(10); // This will save pointer to method call message on server side
|
||||
@@ -257,6 +286,14 @@ TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHand
|
||||
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperation"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ProvidesSerialInMethodCallAndMethodReplyMessage)
|
||||
{
|
||||
auto reply = this->m_proxy->doOperationOnBasicAPILevel(ANY_UNSIGNED_NUMBER);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->m_methodCallMsg->getCookie(), Gt(0));
|
||||
ASSERT_THAT(reply.getReplyCookie(), Eq(this->m_proxy->m_methodCallMsg->getCookie())); // Pairing method reply with method call message
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
|
||||
{
|
||||
this->m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
|
||||
@@ -265,6 +302,30 @@ TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCal
|
||||
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperationAsync"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithLargeArgument)
|
||||
{
|
||||
std::map<int, std::string> collection;
|
||||
//std::size_t totalSize{};
|
||||
for (int i = 0; i < 400'000; i++)
|
||||
{
|
||||
collection[i] = ("This is a string of fifty characters. This is a string of fifty " + std::to_string(i));
|
||||
//totalSize += sizeof(int) + collection[i].size();
|
||||
}
|
||||
//printf("Sending large message with collection of size %zu bytes\n", totalSize);
|
||||
this->m_proxy->sendLargeMessage(collection);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanSendCallsAndReceiveRepliesWithLargeData)
|
||||
{
|
||||
std::map<int32_t, std::string> largeMap;
|
||||
for (int32_t i = 0; i < 40'000; ++i)
|
||||
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
|
||||
|
||||
auto returnedMap = this->m_proxy->doOperationWithLargeData(largeMap);
|
||||
|
||||
ASSERT_THAT(returnedMap, Eq(largeMap));
|
||||
}
|
||||
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
TYPED_TEST(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
|
||||
{
|
||||
@@ -298,7 +359,7 @@ TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime)
|
||||
, sdbus::return_slot );
|
||||
|
||||
// The new remote vtable is registered as long as we keep vtableSlot, so remote method calls now should pass
|
||||
auto proxy = sdbus::createProxy(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
|
||||
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
int result{};
|
||||
proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2).storeResultsTo(result);
|
||||
|
||||
@@ -317,6 +378,6 @@ TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime)
|
||||
vtableSlot.reset(); // Letting the slot go means letting go the associated vtable registration
|
||||
|
||||
// No such remote D-Bus method under given interface exists anymore...
|
||||
auto proxy = sdbus::createProxy(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
|
||||
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
ASSERT_THROW(proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2), sdbus::Error);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ using namespace sdbus::test;
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, ReadsReadOnlyPropertySuccessfully)
|
||||
{
|
||||
ASSERT_THAT(this->m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
@@ -65,7 +65,7 @@ TYPED_TEST(SdbusTestObject, FailsWritingToReadOnlyProperty)
|
||||
ASSERT_THROW(this->m_proxy->setStateProperty("new_value"), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWritePropertySuccessfully)
|
||||
{
|
||||
uint32_t newActionValue = 5678;
|
||||
|
||||
@@ -81,3 +81,12 @@ TYPED_TEST(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHa
|
||||
ASSERT_THAT(this->m_adaptor->m_propertySetMsg, NotNull());
|
||||
ASSERT_THAT(this->m_adaptor->m_propertySetSender, Not(IsEmpty()));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWriteVariantPropertySuccessfully)
|
||||
{
|
||||
sdbus::Variant newActionValue{5678};
|
||||
|
||||
this->m_proxy->actionVariant(newActionValue);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->actionVariant().template get<int>(), Eq(5678));
|
||||
}
|
||||
|
||||
@@ -48,14 +48,14 @@ using namespace sdbus::test;
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalSuccessfully)
|
||||
{
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccessfully)
|
||||
{
|
||||
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
|
||||
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
|
||||
@@ -78,7 +78,7 @@ TYPED_TEST(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccessfully)
|
||||
{
|
||||
this->m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
|
||||
|
||||
@@ -87,19 +87,19 @@ TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccesfully)
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("one"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithLargeMapSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithLargeMapSuccessfully)
|
||||
{
|
||||
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));
|
||||
this->m_adaptor->emitSignalWithMap(largeMap);
|
||||
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));
|
||||
this->m_adaptor->emitSignalWithMap(largeMap);
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccessfully)
|
||||
{
|
||||
double d = 3.14;
|
||||
this->m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
|
||||
@@ -108,7 +108,7 @@ TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
|
||||
ASSERT_THAT(this->m_proxy->m_variantFromSignal, DoubleEq(d));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccessfully)
|
||||
{
|
||||
this->m_adaptor->emitSignalWithoutRegistration({"platform", sdbus::Signature{"av"}});
|
||||
|
||||
|
||||
@@ -127,6 +127,26 @@ TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CancelsAsynchronousPropertySettingViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2346;
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
{
|
||||
auto slot = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value();
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
}, sdbus::return_slot);
|
||||
// Now the slot is destroyed, cancelling the async call
|
||||
}
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
{
|
||||
uint32_t newActionValue = 2347;
|
||||
@@ -141,9 +161,10 @@ TYPED_TEST(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
||||
{
|
||||
const auto properties = this->m_proxy->GetAll(INTERFACE_NAME);
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(3));
|
||||
ASSERT_THAT(properties, SizeIs(4));
|
||||
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
|
||||
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
@@ -161,9 +182,10 @@ TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfac
|
||||
});
|
||||
const auto properties = future.get();
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(3));
|
||||
ASSERT_THAT(properties, SizeIs(4));
|
||||
EXPECT_THAT(properties.at(STATE_PROPERTY).get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_PROPERTY).get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
|
||||
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
@@ -173,9 +195,10 @@ TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfac
|
||||
|
||||
auto properties = future.get();
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(3));
|
||||
ASSERT_THAT(properties, SizeIs(4));
|
||||
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
|
||||
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
@@ -241,6 +264,43 @@ TYPED_TEST(SdbusTestObject, GetsManagedObjectsSuccessfully)
|
||||
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronously)
|
||||
{
|
||||
std::promise<size_t> promise;
|
||||
auto future = promise.get_future();
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
this->m_objectManagerProxy->GetManagedObjectsAsync([&](std::optional<sdbus::Error> /*err*/, const std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>>& objectsInterfacesAndProperties)
|
||||
{
|
||||
promise.set_value(objectsInterfacesAndProperties.size());
|
||||
});
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronouslyViaSlotReturningOverload)
|
||||
{
|
||||
std::promise<size_t> promise;
|
||||
auto future = promise.get_future();
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
auto slot = this->m_objectManagerProxy->GetManagedObjectsAsync([&](std::optional<sdbus::Error> /*err*/, const std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>>& objectsInterfacesAndProperties)
|
||||
{
|
||||
promise.set_value(objectsInterfacesAndProperties.size());
|
||||
}, sdbus::return_slot);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronouslyViaFutureOverload)
|
||||
{
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
auto future = this->m_objectManagerProxy->GetManagedObjectsAsync(sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get().size(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
@@ -252,17 +312,19 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterface
|
||||
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
|
||||
#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.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(4));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
#else
|
||||
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
|
||||
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
|
||||
// So in this specific instance, `action' property is no more added to the list.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
@@ -288,17 +350,19 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
|
||||
#endif
|
||||
#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.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(4));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
#else
|
||||
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
|
||||
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
|
||||
// So in this specific instance, `action' property is no more added to the list.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ const ObjectPath OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
|
||||
const ObjectPath OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
|
||||
const PropertyName STATE_PROPERTY{"state"};
|
||||
const PropertyName ACTION_PROPERTY{"action"};
|
||||
const PropertyName ACTION_VARIANT_PROPERTY{"actionVariant"};
|
||||
const PropertyName BLOCKING_PROPERTY{"blocking"};
|
||||
const std::string DIRECT_CONNECTION_SOCKET_PATH{std::filesystem::temp_directory_path() / "sdbus-cpp-direct-connection-test"};
|
||||
|
||||
@@ -58,6 +59,7 @@ const int UNIX_FD_VALUE = 0;
|
||||
|
||||
const std::string DEFAULT_STATE_VALUE{"default-state-value"};
|
||||
const uint32_t DEFAULT_ACTION_VALUE{999};
|
||||
const std::string DEFAULT_ACTION_VARIANT_VALUE{"ahoj"};
|
||||
const bool DEFAULT_BLOCKING_VALUE{true};
|
||||
|
||||
constexpr const double DOUBLE_VALUE{3.24L};
|
||||
|
||||
@@ -128,6 +128,14 @@ uint32_t TestAdaptor::doOperation(const uint32_t& param)
|
||||
return param;
|
||||
}
|
||||
|
||||
std::map<int32_t, std::string> TestAdaptor::doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam)
|
||||
{
|
||||
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
|
||||
m_methodName = m_methodCallMsg->getMemberName();
|
||||
|
||||
return largeParam;
|
||||
}
|
||||
|
||||
void TestAdaptor::doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t param)
|
||||
{
|
||||
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
|
||||
@@ -149,6 +157,27 @@ void TestAdaptor::doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t pa
|
||||
}
|
||||
}
|
||||
|
||||
void TestAdaptor::doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t param, const std::map<int32_t, std::string>& largeMap)
|
||||
{
|
||||
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
|
||||
m_methodName = m_methodCallMsg->getMemberName();
|
||||
|
||||
if (param == 0)
|
||||
{
|
||||
// Don't sleep and return the result from this thread
|
||||
result.returnResults(largeMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process asynchronously in another thread and return the result from there
|
||||
std::thread([param, largeMap, result = std::move(result)]()
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||
result.returnResults(largeMap);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
sdbus::Signature TestAdaptor::getSignature()
|
||||
{
|
||||
return SIGNATURE_VALUE;
|
||||
@@ -212,6 +241,16 @@ void TestAdaptor::emitTwoSimpleSignals()
|
||||
emitSignalWithMap({});
|
||||
}
|
||||
|
||||
void TestAdaptor::sendLargeMessage(const std::map<int, std::string>& /*collection*/)
|
||||
{
|
||||
//printf("Adaptor: got collection with %zu items", collection.size());
|
||||
}
|
||||
|
||||
std::map<std::string, sdbus::Variant> TestAdaptor::returnDictionary(const std::map<std::string, sdbus::Variant>& dict)
|
||||
{
|
||||
return dict;
|
||||
}
|
||||
|
||||
std::string TestAdaptor::state()
|
||||
{
|
||||
return m_state;
|
||||
@@ -227,6 +266,16 @@ void TestAdaptor::action(const uint32_t& value)
|
||||
m_action = value;
|
||||
}
|
||||
|
||||
sdbus::Variant TestAdaptor::actionVariant()
|
||||
{
|
||||
return m_actionVariant;
|
||||
}
|
||||
|
||||
void TestAdaptor::actionVariant(const sdbus::Variant& value)
|
||||
{
|
||||
m_actionVariant = value;
|
||||
}
|
||||
|
||||
bool TestAdaptor::blocking()
|
||||
{
|
||||
return m_blocking;
|
||||
|
||||
@@ -73,7 +73,9 @@ protected:
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_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;
|
||||
std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam) override;
|
||||
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override;
|
||||
void doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t arg0, const std::map<int32_t, std::string>& largeParam) override;
|
||||
sdbus::Signature getSignature() override;
|
||||
sdbus::ObjectPath getObjPath() override;
|
||||
sdbus::UnixFd getUnixFd() override;
|
||||
@@ -82,9 +84,13 @@ protected:
|
||||
void throwErrorWithNoReply() override;
|
||||
void doPrivilegedStuff() override;
|
||||
void emitTwoSimpleSignals() override;
|
||||
void sendLargeMessage(const std::map<int, std::string>& collection) override;
|
||||
std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>& dict) override;
|
||||
|
||||
uint32_t action() override;
|
||||
void action(const uint32_t& value) override;
|
||||
sdbus::Variant actionVariant() override;
|
||||
void actionVariant(const sdbus::Variant& value) override;
|
||||
bool blocking() override;
|
||||
void blocking(const bool& value) override;
|
||||
std::string state() override;
|
||||
@@ -97,6 +103,7 @@ private:
|
||||
const std::string m_state{DEFAULT_STATE_VALUE};
|
||||
uint32_t m_action{DEFAULT_ACTION_VALUE};
|
||||
bool m_blocking{DEFAULT_BLOCKING_VALUE};
|
||||
sdbus::Variant m_actionVariant{"ahoj"};
|
||||
|
||||
public: // for tests
|
||||
// For dont-expect-reply method call verifications
|
||||
@@ -132,7 +139,9 @@ protected:
|
||||
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 {}; }
|
||||
std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>&) override { return {}; }
|
||||
void doOperationAsync(sdbus::Result<uint32_t>&&, uint32_t) override {}
|
||||
void doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&&, uint32_t, const std::map<int32_t, std::string>&) override {}
|
||||
sdbus::Signature getSignature() override { return {}; }
|
||||
sdbus::ObjectPath getObjPath() override { return {}; }
|
||||
sdbus::UnixFd getUnixFd() override { return {}; }
|
||||
@@ -141,9 +150,13 @@ protected:
|
||||
void throwErrorWithNoReply() override {}
|
||||
void doPrivilegedStuff() override {}
|
||||
void emitTwoSimpleSignals() override {}
|
||||
void sendLargeMessage(const std::map<int, std::string>&) override {}
|
||||
std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>&) override { return {}; }
|
||||
|
||||
uint32_t action() override { return {}; }
|
||||
void action(const uint32_t&) override {}
|
||||
sdbus::Variant actionVariant() override { return {}; }
|
||||
void actionVariant(const sdbus::Variant&) override {}
|
||||
bool blocking() override { return {}; }
|
||||
void blocking(const bool&) override {}
|
||||
std::string state() override { return {}; }
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
inline const uint32_t ANY_UNSIGNED_NUMBER{123};
|
||||
|
||||
class BaseTestFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -113,6 +113,16 @@ uint32_t TestProxy::doOperationWithTimeout(const std::chrono::microseconds &time
|
||||
return result;
|
||||
}
|
||||
|
||||
MethodReply TestProxy::doOperationOnBasicAPILevel(uint32_t param)
|
||||
{
|
||||
auto methodCall = getProxy().createMethodCall(test::INTERFACE_NAME, MethodName{"doOperation"});
|
||||
methodCall << param;
|
||||
|
||||
m_methodCallMsg = std::make_unique<MethodCall>(methodCall);
|
||||
|
||||
return getProxy().callMethod(methodCall);
|
||||
}
|
||||
|
||||
sdbus::PendingAsyncCall TestProxy::doOperationClientSideAsync(uint32_t param)
|
||||
{
|
||||
return getProxy().callMethodAsync("doOperation")
|
||||
@@ -151,6 +161,14 @@ std::future<MethodReply> TestProxy::doOperationClientSideAsyncOnBasicAPILevel(ui
|
||||
return getProxy().callMethodAsync(methodCall, sdbus::with_future);
|
||||
}
|
||||
|
||||
std::future<std::map<int32_t, std::string>> TestProxy::doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, with_future_t)
|
||||
{
|
||||
return getProxy().callMethodAsync("doOperationWithLargeData")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withArguments(largeParam)
|
||||
.getResultAsFuture<std::map<int32_t, std::string>>();
|
||||
}
|
||||
|
||||
void TestProxy::doErroneousOperationClientSideAsync()
|
||||
{
|
||||
getProxy().callMethodAsync("throwError")
|
||||
|
||||
@@ -95,9 +95,11 @@ protected:
|
||||
public:
|
||||
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler);
|
||||
uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
|
||||
MethodReply doOperationOnBasicAPILevel(uint32_t param);
|
||||
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param);
|
||||
[[nodiscard]] sdbus::Slot doOperationClientSideAsync(uint32_t param, sdbus::return_slot_t);
|
||||
std::future<uint32_t> doOperationClientSideAsync(uint32_t param, with_future_t);
|
||||
std::future<std::map<int32_t, std::string>> doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, with_future_t);
|
||||
std::future<MethodReply> doOperationClientSideAsyncOnBasicAPILevel(uint32_t param);
|
||||
std::future<void> doErroneousOperationClientSideAsync(with_future_t);
|
||||
void doErroneousOperationClientSideAsync();
|
||||
@@ -120,6 +122,7 @@ public: // for tests
|
||||
std::function<void(uint32_t res, std::optional<sdbus::Error> err)> m_DoOperationClientSideAsyncReplyHandler;
|
||||
std::function<void(const sdbus::InterfaceName&, const std::map<PropertyName, sdbus::Variant>&, const std::vector<PropertyName>&)> m_onPropertiesChangedHandler;
|
||||
|
||||
std::unique_ptr<const MethodCall> m_methodCallMsg;
|
||||
std::unique_ptr<const Message> m_signalMsg;
|
||||
SignalName m_signalName;
|
||||
};
|
||||
|
||||
@@ -46,7 +46,9 @@ protected:
|
||||
, sdbus::registerMethod("sumStructItems").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); })
|
||||
, sdbus::registerMethod("sumArrayItems").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); })
|
||||
, sdbus::registerMethod("doOperation").withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); })
|
||||
, sdbus::registerMethod("doOperationWithLargeData").withInputParamNames("largeMap").withOutputParamNames("largeMap").implementedAs([this](const std::map<int32_t, std::string>& largeMap){ return this->doOperationWithLargeData(largeMap); })
|
||||
, sdbus::registerMethod("doOperationAsync").withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result<uint32_t>&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); })
|
||||
, sdbus::registerMethod("doOperationAsyncWithLargeData").withInputParamNames("arg0", "largeMap").withOutputParamNames("largeMap").implementedAs([this](sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t arg0, const std::map<int32_t, std::string>& largeMap){ this->doOperationAsyncWithLargeData(std::move(result), std::move(arg0), largeMap); })
|
||||
, sdbus::registerMethod("getSignature").withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); })
|
||||
, sdbus::registerMethod("getObjPath").withOutputParamNames("arg0").implementedAs([this](){ return this->getObjPath(); })
|
||||
, sdbus::registerMethod("getUnixFd").withOutputParamNames("arg0").implementedAs([this](){ return this->getUnixFd(); })
|
||||
@@ -55,10 +57,13 @@ protected:
|
||||
, sdbus::registerMethod("throwErrorWithNoReply").implementedAs([this](){ return this->throwErrorWithNoReply(); }).withNoReply()
|
||||
, sdbus::registerMethod("doPrivilegedStuff").implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged()
|
||||
, sdbus::registerMethod("emitTwoSimpleSignals").implementedAs([this](){ return this->emitTwoSimpleSignals(); })
|
||||
, sdbus::registerMethod("sendLargeMessage").implementedAs([this](const std::map<int, std::string>& collection){ this->sendLargeMessage(collection); })
|
||||
, sdbus::registerMethod("returnDictionary").implementedAs([this](const std::map<std::string, sdbus::Variant>& dictionary){ return this->returnDictionary(dictionary); })
|
||||
, sdbus::registerSignal("simpleSignal").markAsDeprecated()
|
||||
, sdbus::registerSignal("signalWithMap").withParameters<std::map<int32_t, std::string>>("aMap")
|
||||
, sdbus::registerSignal("signalWithVariant").withParameters<sdbus::Variant>("aVariant")
|
||||
, sdbus::registerProperty("action").withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL)
|
||||
, sdbus::registerProperty("actionVariant").withGetter([this](){ return this->actionVariant(); }).withSetter([this](const sdbus::Variant& value){ this->actionVariant(value); }).withUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL)
|
||||
, sdbus::registerProperty("blocking").withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); })
|
||||
, sdbus::registerProperty("state").withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE)
|
||||
).forInterface(INTERFACE_NAME);
|
||||
@@ -93,7 +98,9 @@ private:
|
||||
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_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 std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam) = 0;
|
||||
virtual void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0;
|
||||
virtual void doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t arg0, const std::map<int32_t, std::string>& largeParam) = 0;
|
||||
virtual sdbus::Signature getSignature() = 0;
|
||||
virtual sdbus::ObjectPath getObjPath() = 0;
|
||||
virtual sdbus::UnixFd getUnixFd() = 0;
|
||||
@@ -102,10 +109,14 @@ private:
|
||||
virtual void throwErrorWithNoReply() = 0;
|
||||
virtual void doPrivilegedStuff() = 0;
|
||||
virtual void emitTwoSimpleSignals() = 0;
|
||||
virtual void sendLargeMessage(const std::map<int, std::string>& collection) = 0;
|
||||
virtual std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>& dict) = 0;
|
||||
|
||||
private:
|
||||
virtual uint32_t action() = 0;
|
||||
virtual sdbus::Variant actionVariant() = 0;
|
||||
virtual void action(const uint32_t& value) = 0;
|
||||
virtual void actionVariant(const sdbus::Variant& value) = 0;
|
||||
virtual bool blocking() = 0;
|
||||
virtual void blocking(const bool& value) = 0;
|
||||
virtual std::string state() = 0;
|
||||
|
||||
@@ -130,6 +130,13 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam)
|
||||
{
|
||||
std::map<int32_t, std::string> result;
|
||||
m_proxy.callMethod("doOperationWithLargeData").onInterface(INTERFACE_NAME).withArguments(largeParam).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t doOperationAsync(const uint32_t& arg0)
|
||||
{
|
||||
uint32_t result;
|
||||
@@ -137,6 +144,13 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<int32_t, std::string> doOperationAsyncWithLargeData(const uint32_t& arg0, const std::map<int32_t, std::string>& largeParam)
|
||||
{
|
||||
std::map<int32_t, std::string> result;
|
||||
m_proxy.callMethod("doOperationAsyncWithLargeData").onInterface(INTERFACE_NAME).withArguments(arg0, largeParam).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Signature getSignature()
|
||||
{
|
||||
sdbus::Signature result;
|
||||
@@ -185,6 +199,11 @@ public:
|
||||
m_proxy.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void sendLargeMessage(const std::map<int, std::string>& collection)
|
||||
{
|
||||
m_proxy.callMethod("sendLargeMessage").onInterface(INTERFACE_NAME).withArguments(collection);
|
||||
}
|
||||
|
||||
void unregisterSimpleSignalHandler()
|
||||
{
|
||||
simpleSignalSlot_.reset();
|
||||
@@ -206,6 +225,16 @@ public:
|
||||
m_proxy.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
sdbus::Variant actionVariant()
|
||||
{
|
||||
return m_proxy.getProperty("actionVariant").onInterface(INTERFACE_NAME).get<sdbus::Variant>();
|
||||
}
|
||||
|
||||
void actionVariant(const sdbus::Variant& value)
|
||||
{
|
||||
m_proxy.setProperty("actionVariant").onInterface(INTERFACE_NAME).toValue({value, sdbus::embed_variant});
|
||||
}
|
||||
|
||||
bool blocking()
|
||||
{
|
||||
return m_proxy.getProperty("blocking").onInterface(INTERFACE_NAME).get<bool>();
|
||||
|
||||
@@ -36,6 +36,8 @@ using ::testing::StrEq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::DoubleEq;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::ElementsAre;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
@@ -53,6 +55,7 @@ namespace sdbus {
|
||||
template <typename _ElementType>
|
||||
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
|
||||
{
|
||||
// TODO: This can also be simplified on the basis of a callback (see dictionary...)
|
||||
msg.openContainer<_ElementType>();
|
||||
|
||||
for (const auto& item : items)
|
||||
@@ -89,40 +92,55 @@ namespace sdbus {
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
struct sdbus::signature_of<std::list<_Element, _Allocator>>
|
||||
: sdbus::signature_of<std::vector<_Element, _Allocator>>
|
||||
: sdbus::signature_of<std::vector<_Element, _Allocator>>
|
||||
{};
|
||||
|
||||
namespace my {
|
||||
enum class Enum
|
||||
{
|
||||
Value1,
|
||||
Value2,
|
||||
Value3
|
||||
};
|
||||
|
||||
struct Struct
|
||||
{
|
||||
int i;
|
||||
std::string s;
|
||||
std::list<double> l;
|
||||
Enum e;
|
||||
|
||||
friend bool operator==(const Struct& lhs, const Struct& rhs) = default;
|
||||
};
|
||||
|
||||
bool operator==(const Struct& lhs, const Struct& rhs)
|
||||
struct RelaxedStruct
|
||||
{
|
||||
return lhs.i == rhs.i && lhs.s == rhs.s && lhs.l == rhs.l;
|
||||
}
|
||||
int i;
|
||||
std::string s;
|
||||
std::list<double> l;
|
||||
Enum e;
|
||||
|
||||
sdbus::Message& operator<<(sdbus::Message& msg, const Struct& items)
|
||||
friend bool operator==(const RelaxedStruct& lhs, const RelaxedStruct& rhs) = default;
|
||||
};
|
||||
|
||||
struct NestedStruct
|
||||
{
|
||||
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;
|
||||
}
|
||||
int i;
|
||||
std::string s;
|
||||
Enum e;
|
||||
Struct x;
|
||||
|
||||
friend bool operator==(const NestedStruct& lhs, const NestedStruct& rhs) = default;
|
||||
};
|
||||
}
|
||||
|
||||
template <>
|
||||
struct sdbus::signature_of<my::Struct>
|
||||
: sdbus::signature_of<sdbus::Struct<int, std::string, std::list<double>>>
|
||||
{};
|
||||
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l, e);
|
||||
|
||||
SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(my::RelaxedStruct);
|
||||
SDBUSCPP_REGISTER_STRUCT(my::RelaxedStruct, i, s, l, e);
|
||||
|
||||
SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(my::NestedStruct);
|
||||
SDBUSCPP_REGISTER_STRUCT(my::NestedStruct, i, s, e, x);
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
@@ -502,11 +520,11 @@ TEST(AMessage, CanCarryDBusArrayGivenAsCustomType)
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryDBusStructGivenAsCustomType)
|
||||
TEST(AMessage, CanCarryUserDefinedStruct)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const my::Struct dataWritten{3545342, "hello"s, {3.14, 2.4568546}};
|
||||
const my::Struct dataWritten{3545342, "hello"s, {3.14, 2.4568546}, my::Enum::Value2};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
@@ -517,6 +535,116 @@ TEST(AMessage, CanCarryDBusStructGivenAsCustomType)
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryNestedUserDefinedStruct)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const my::NestedStruct dataWritten{3545342, "hello"s, my::Enum::Value2, {12, "world"s, {3.14, 2.4568546}, my::Enum::Value3}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
my::NestedStruct dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanSerializeUserDefinedStructAsDictionaryOfStringsToVariants)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const my::Struct dataWritten{3545342, "hello"s, {3.14, 2.4568546}, my::Enum::Value2};
|
||||
|
||||
msg << sdbus::as_dictionary{dataWritten};
|
||||
msg.seal();
|
||||
|
||||
std::map<std::string, sdbus::Variant> dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, SizeIs(4));
|
||||
ASSERT_THAT(dataRead.at("i").get<int>(), Eq(3545342));
|
||||
ASSERT_THAT(dataRead.at("s").get<std::string>(), Eq("hello"));
|
||||
ASSERT_THAT(dataRead.at("l").get<std::list<double>>(), ElementsAre(3.14, 2.4568546));
|
||||
ASSERT_THAT(dataRead.at("e").get<my::Enum>(), Eq(my::Enum::Value2));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanRecursivelySerializeUserDefinedStructAsDictionaryOfStringsToVariants)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const my::NestedStruct dataWritten{3545342, "hello"s, my::Enum::Value2, {12, "world"s, {3.14, 2.4568546}, my::Enum::Value3}};
|
||||
|
||||
msg << sdbus::as_dictionary{dataWritten};
|
||||
msg.seal();
|
||||
|
||||
std::map<std::string, sdbus::Variant> dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, SizeIs(4));
|
||||
ASSERT_THAT(dataRead.at("i").get<int>(), Eq(3545342));
|
||||
ASSERT_THAT(dataRead.at("s").get<std::string>(), Eq("hello"));
|
||||
ASSERT_THAT(dataRead.at("e").get<my::Enum>(), Eq(my::Enum::Value2));
|
||||
auto nestedStructRead = dataRead.at("x").get<std::map<std::string, sdbus::Variant>>(); // Nested struct serialized as dict
|
||||
ASSERT_THAT(nestedStructRead.at("i").get<int>(), Eq(12));
|
||||
ASSERT_THAT(nestedStructRead.at("s").get<std::string>(), Eq("world"));
|
||||
ASSERT_THAT(nestedStructRead.at("l").get<std::list<double>>(), ElementsAre(3.14, 2.4568546));
|
||||
ASSERT_THAT(nestedStructRead.at("e").get<my::Enum>(), Eq(my::Enum::Value3));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanDeserializeDictionaryOfStringsToVariantsIntoUserDefinedStruct)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
std::map<std::string, sdbus::Variant> dataWritten{ {"i", sdbus::Variant{3545342}}
|
||||
, {"s", sdbus::Variant{"hello"s}}
|
||||
, {"l", sdbus::Variant{std::list<double>{3.14, 2.4568546}}}
|
||||
, {"e", sdbus::Variant{my::Enum::Value2}} };
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
my::Struct dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(my::Struct{3545342, "hello"s, {3.14, 2.4568546}, my::Enum::Value2}));
|
||||
}
|
||||
|
||||
TEST(AMessage, FailsDeserializingDictionaryIntoUserDefinedStructIfStructMemberIsNotFound)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
std::map<std::string, sdbus::Variant> dataWritten{ {"i", sdbus::Variant{3545342}}
|
||||
, {"nonexistent", sdbus::Variant{"hello"s}}
|
||||
, {"l", sdbus::Variant{std::list<double>{3.14, 2.4568546}}}
|
||||
, {"e", sdbus::Variant{my::Enum::Value2}} };
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
my::Struct dataRead;
|
||||
|
||||
ASSERT_THROW(msg >> dataRead, sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(AMessage, DeserializesDictionaryIntoStructWithMissingMembersSuccessfullyIfRelaxedOptionIsSet)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
std::map<std::string, sdbus::Variant> dataWritten{ {"some_nonexistent_struct_member", sdbus::Variant{3545342}}
|
||||
, {"another_nonexistent_struct_member", sdbus::Variant{"hello"s}}
|
||||
, {"l", sdbus::Variant{std::list<double>{3.14, 2.4568546}}}
|
||||
, {"e", sdbus::Variant{my::Enum::Value2}} };
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
my::RelaxedStruct dataRead{};
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(my::RelaxedStruct{{}, {}, {3.14, 2.4568546}, my::Enum::Value2}));
|
||||
}
|
||||
|
||||
class AMessage : public ::testing::TestWithParam<std::variant<int32_t, std::string, my::Struct>>
|
||||
{
|
||||
};
|
||||
|
||||
@@ -102,6 +102,9 @@ namespace
|
||||
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
|
||||
#endif
|
||||
TYPE(SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
|
||||
TYPE(const SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
|
||||
TYPE(volatile SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
|
||||
TYPE(const volatile SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
|
||||
TYPE(SomeEnumStruct)HAS_DBUS_TYPE_SIGNATURE("x")
|
||||
TYPE(SomeClassicEnum)HAS_DBUS_TYPE_SIGNATURE("u")
|
||||
TYPE(std::map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
|
||||
@@ -157,6 +160,9 @@ namespace
|
||||
, std::span<int16_t>
|
||||
#endif
|
||||
, SomeEnumClass
|
||||
, const SomeEnumClass
|
||||
, volatile SomeEnumClass
|
||||
, const volatile SomeEnumClass
|
||||
, SomeEnumStruct
|
||||
, SomeClassicEnum
|
||||
, std::map<int32_t, int64_t>
|
||||
|
||||
@@ -97,6 +97,29 @@ TEST(AVariant, CanBeCopied)
|
||||
ASSERT_THAT(variantCopy2.get<std::string>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeMoved)
|
||||
{
|
||||
auto value = "hello"s;
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
auto movedVariant{std::move(variant)};
|
||||
|
||||
ASSERT_THAT(movedVariant.get<std::string>(), Eq(value));
|
||||
ASSERT_TRUE(variant.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeMovedIntoAMap)
|
||||
{
|
||||
auto value = "hello"s;
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
std::map<std::string, sdbus::Variant> mymap;
|
||||
mymap.try_emplace("payload", std::move(variant));
|
||||
|
||||
ASSERT_THAT(mymap["payload"].get<std::string>(), Eq(value));
|
||||
ASSERT_TRUE(variant.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AVariant, IsNotEmptyWhenContainsAValue)
|
||||
{
|
||||
sdbus::Variant v("hello");
|
||||
@@ -430,6 +453,7 @@ TEST(AnError, CanBeConstructedFromANameAndAMessage)
|
||||
auto error = sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.error"}, "message");
|
||||
EXPECT_THAT(error.getName(), Eq<std::string>("org.sdbuscpp.error"));
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("message"));
|
||||
EXPECT_TRUE(error.isValid());
|
||||
}
|
||||
|
||||
TEST(AnError, CanBeConstructedFromANameOnly)
|
||||
@@ -439,6 +463,47 @@ TEST(AnError, CanBeConstructedFromANameOnly)
|
||||
EXPECT_THAT(error1.getName(), Eq<std::string>("org.sdbuscpp.error"));
|
||||
EXPECT_THAT(error2.getName(), Eq<std::string>("org.sdbuscpp.error"));
|
||||
|
||||
EXPECT_THAT(error1.getMessage(), Eq<std::string>(""));
|
||||
EXPECT_THAT(error2.getMessage(), Eq<std::string>(""));
|
||||
EXPECT_TRUE(error1.getMessage().empty());
|
||||
EXPECT_TRUE(error2.getMessage().empty());
|
||||
|
||||
EXPECT_TRUE(error1.isValid());
|
||||
EXPECT_TRUE(error2.isValid());
|
||||
}
|
||||
|
||||
TEST(AnError, IsInvalidWhenConstructedWithAnEmptyName)
|
||||
{
|
||||
auto error = sdbus::Error({});
|
||||
|
||||
EXPECT_TRUE(error.getName().empty());
|
||||
EXPECT_TRUE(error.getMessage().empty());
|
||||
EXPECT_FALSE(error.isValid());
|
||||
}
|
||||
|
||||
TEST(AnErrorFactory, CanCreateAnErrorFromErrno)
|
||||
{
|
||||
auto error = sdbus::createError(ENOENT, "custom message");
|
||||
|
||||
EXPECT_THAT(error.getName(), Eq<std::string>("org.freedesktop.DBus.Error.FileNotFound"));
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("custom message (No such file or directory)"));
|
||||
EXPECT_TRUE(error.isValid());
|
||||
}
|
||||
|
||||
#ifndef SDBUS_basu // Creating error from invalid errno is not supported on basu backend
|
||||
TEST(AnErrorFactory, CreatesGenericErrorWhenErrnoIsUnknown)
|
||||
{
|
||||
auto error = sdbus::createError(123456, "custom message");
|
||||
|
||||
EXPECT_THAT(error.getName(), Eq<std::string>("org.freedesktop.DBus.Error.Failed"));
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("custom message (Unknown error 123456)"));
|
||||
EXPECT_TRUE(error.isValid());
|
||||
}
|
||||
#endif // SDBUS_basu
|
||||
|
||||
TEST(AnErrorFactory, CreatesEmptyInvalidErrorWhenErrnoIsZero)
|
||||
{
|
||||
auto error = sdbus::createError(0, "custom message");
|
||||
|
||||
EXPECT_TRUE(error.getName().empty());
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("custom message"));
|
||||
EXPECT_FALSE(error.isValid());
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public:
|
||||
MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r));
|
||||
MOCK_METHOD1(sd_bus_get_current_message, sd_bus_message*(sd_bus *bus));
|
||||
MOCK_METHOD2(sd_bus_get_poll_data, int(sd_bus *bus, PollData* data));
|
||||
MOCK_METHOD2(sd_bus_get_n_queued_read, int(sd_bus *bus, uint64_t *ret));
|
||||
MOCK_METHOD3(sd_bus_get_n_queued, int(sd_bus *bus, uint64_t *read, uint64_t* write));
|
||||
MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus));
|
||||
MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus));
|
||||
MOCK_METHOD1(sd_bus_close_unref, sd_bus *(sd_bus *bus));
|
||||
@@ -89,6 +89,7 @@ public:
|
||||
MOCK_METHOD2(sd_bus_message_set_destination, int(sd_bus_message *m, const char *destination));
|
||||
|
||||
MOCK_METHOD3(sd_bus_query_sender_creds, int(sd_bus_message *, uint64_t, sd_bus_creds **));
|
||||
MOCK_METHOD1(sd_bus_creds_ref, sd_bus_creds*(sd_bus_creds *));
|
||||
MOCK_METHOD1(sd_bus_creds_unref, sd_bus_creds*(sd_bus_creds *));
|
||||
|
||||
MOCK_METHOD2(sd_bus_creds_get_pid, int(sd_bus_creds *, pid_t *));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(sdbus-c++-tools VERSION 2.0.0)
|
||||
project(sdbus-c++-tools VERSION 2.2.0)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -44,6 +44,7 @@ set(CMAKE_CXX_STANDARD 14)
|
||||
add_executable(sdbus-c++-xml2cpp ${SDBUSCPP_XML2CPP_SRCS})
|
||||
target_link_libraries (sdbus-c++-xml2cpp ${EXPAT_LIBRARIES})
|
||||
target_include_directories(sdbus-c++-xml2cpp PRIVATE ${EXPAT_INCLUDE_DIRS})
|
||||
target_compile_definitions(sdbus-c++-xml2cpp PRIVATE SDBUS_XML2CPP_VERSION="${CMAKE_PROJECT_VERSION}")
|
||||
|
||||
#----------------------------------
|
||||
# CMAKE CONFIG & PACKAGE CONFIG
|
||||
|
||||
@@ -121,7 +121,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
if(!annotationRegistration.empty())
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "sdbus::setInterfaceFlags()" << annotationRegistration << ";";
|
||||
str << "sdbus::setInterfaceFlags()" << annotationRegistration;
|
||||
annotationRegistration = str.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ int BaseGenerator::transformXmlToFile(const Document& doc, const char* filename)
|
||||
int BaseGenerator::writeToFile(const char* filename, const std::string& data) const
|
||||
{
|
||||
std::ofstream file(filename);
|
||||
if (file.bad())
|
||||
if (file.fail())
|
||||
{
|
||||
std::cerr << "Unable to write file " << filename << endl;
|
||||
return 1;
|
||||
|
||||
@@ -375,6 +375,9 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
|
||||
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
{
|
||||
if (propertySignature == "v")
|
||||
propertyArg = "{" + propertyArg + ", sdbus::embed_variant}";
|
||||
|
||||
const std::string realRetType = (asyncSet ? (futureSet ? "std::future<void>" : "sdbus::PendingAsyncCall") : "void");
|
||||
|
||||
propertySS << tab << realRetType << " " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl
|
||||
|
||||
@@ -51,6 +51,7 @@ void usage(std::ostream& output, const char* programName)
|
||||
" --adaptor=FILE Generate header file FILE with stub class (server)" << endl <<
|
||||
" -h, --help " << endl <<
|
||||
" --verbose Explain what is being done" << endl <<
|
||||
" -v, --version Prints out sdbus-c++ version used by the tool" << endl <<
|
||||
endl <<
|
||||
"The stub generator takes an XML file describing DBus interface and creates" << endl <<
|
||||
"C++ header files to be used by C++ code wanting to cumminicate through that" << endl <<
|
||||
@@ -104,6 +105,11 @@ int main(int argc, char **argv)
|
||||
usage(std::cout, programName);
|
||||
return 0;
|
||||
}
|
||||
else if (!strcmp(*argv, "--version") || !strcmp(*argv, "-v"))
|
||||
{
|
||||
std::cout << "Version: " << SDBUS_XML2CPP_VERSION << std::endl;
|
||||
return 0;
|
||||
}
|
||||
else if (!strcmp(*argv, "--verbose"))
|
||||
{
|
||||
verbose = true;
|
||||
@@ -186,7 +192,10 @@ int main(int argc, char **argv)
|
||||
std::cerr << "Generating proxy header " << proxy << endl;
|
||||
}
|
||||
ProxyGenerator pg;
|
||||
pg.transformXmlToFile(doc, proxy);
|
||||
if(pg.transformXmlToFile(doc, proxy)) {
|
||||
std::cerr << "Failed to generate proxy header" << endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (adaptor)
|
||||
@@ -196,7 +205,11 @@ int main(int argc, char **argv)
|
||||
std::cerr << "Generating adaptor header " << adaptor << endl;
|
||||
}
|
||||
AdaptorGenerator ag;
|
||||
ag.transformXmlToFile(doc, adaptor);
|
||||
if(ag.transformXmlToFile(doc, adaptor)) {
|
||||
std::cerr << "Failed to generate adaptor header" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user