mirror of
https://github.com/Kistler-Group/sdbus-cpp.git
synced 2026-08-28 16:02:37 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b9b15cbac | ||
|
|
b5c352700f | ||
|
|
ca073434d2 | ||
|
|
ca69493b20 | ||
|
|
fbe77629bc | ||
|
|
b042f4e13d | ||
|
|
eb05ed5d18 | ||
|
|
7363944751 | ||
|
|
c2bb343f8a | ||
|
|
9f3e89eb6a | ||
|
|
6f694e3fd3 | ||
|
|
44aa4f080f | ||
|
|
6715ef1fc3 | ||
|
|
28b78822cf | ||
|
|
70750e4557 | ||
|
|
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 | ||
|
|
b7d85f936d | ||
|
|
798eaf8626 | ||
|
|
2bc9d3ebb3 | ||
|
|
26c6da11be | ||
|
|
0c75ad2b71 | ||
|
|
84a6fbbf86 | ||
|
|
83ece48ab0 | ||
|
|
310161207a | ||
|
|
ef552ec089 | ||
|
|
7894cda577 | ||
|
|
d41a176c2a | ||
|
|
f15c260750 | ||
|
|
d856969051 | ||
|
|
32a97f214f | ||
|
|
42f0bd07c0 | ||
|
|
fe21ee9656 | ||
|
|
0dc0c87cc9 | ||
|
|
29e94c3b68 | ||
|
|
c433d26176 | ||
|
|
abc4d755f7 | ||
|
|
4c35e98668 | ||
|
|
87500ad9ad | ||
|
|
9a5616a87a | ||
|
|
4a90f80933 | ||
|
|
d177d2e365 | ||
|
|
c205178fc0 | ||
|
|
280fcfb067 | ||
|
|
0074c79e7f | ||
|
|
3de2812a60 | ||
|
|
914d133d10 | ||
|
|
4721060d76 | ||
|
|
1c72f1ce52 | ||
|
|
b65461c404 | ||
|
|
24776f2047 | ||
|
|
f1b9226491 | ||
|
|
dc0c2562b8 | ||
|
|
84932a6985 | ||
|
|
bdf313bc60 | ||
|
|
e76d38c317 | ||
|
|
c92f5722c7 | ||
|
|
20a13eeafb | ||
|
|
4fd09d4e81 | ||
|
|
35fd3ff5ce | ||
|
|
6f35f00fcf | ||
|
|
9412940d1e | ||
|
|
c6afa26541 | ||
|
|
b4d036c503 | ||
|
|
0bfda9ab92 | ||
|
|
64337e545d | ||
|
|
788168eded | ||
|
|
b9aa770f58 | ||
|
|
2efb6909b5 | ||
|
|
3e20fc639e | ||
|
|
7450515d0b | ||
|
|
c769637f3b | ||
|
|
334fcb8833 | ||
|
|
b9088cc801 | ||
|
|
a73eb9b8c1 | ||
|
|
700a011b80 | ||
|
|
8a9cfc1f19 |
+66
@@ -0,0 +1,66 @@
|
||||
---
|
||||
# TODO: enable -llvm-include-order in the end, after clang-format
|
||||
Checks: "*,\
|
||||
-llvmlibc-*,\
|
||||
-altera-*,\
|
||||
-fuchsia-*,
|
||||
-cert-err58-cpp,\
|
||||
-modernize-use-trailing-return-type,\
|
||||
-cppcoreguidelines-avoid-magic-numbers,\
|
||||
-readability-magic-numbers,\
|
||||
-readability-braces-around-statements,\
|
||||
-google-readability-braces-around-statements,\
|
||||
-hicpp-braces-around-statements,\
|
||||
-hicpp-signed-bitwise,\
|
||||
-llvm-include-order,\
|
||||
-llvm-header-guard,\
|
||||
-google-runtime-int,\
|
||||
-google-default-arguments,\
|
||||
-hicpp-named-parameter,\
|
||||
-readability-named-parameter,\
|
||||
-bugprone-macro-parentheses,\
|
||||
-google-readability-todo,\
|
||||
-google-build-using-namespace,\
|
||||
-cppcoreguidelines-pro-type-reinterpret-cast,\
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,\
|
||||
-hicpp-no-array-decay,\
|
||||
-cppcoreguidelines-avoid-c-arrays,\
|
||||
-hicpp-avoid-c-arrays,\
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,\
|
||||
-misc-non-private-member-variables-in-classes,\
|
||||
-modernize-avoid-c-arrays"
|
||||
|
||||
HeaderFilterRegex: '.*'
|
||||
FormatStyle: file
|
||||
WarningsAsErrors: "*"
|
||||
CheckOptions:
|
||||
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
|
||||
value: '1'
|
||||
- key: readability-implicit-bool-conversion.AllowPointerConditions
|
||||
value: '1'
|
||||
- key: readability-implicit-bool-conversion.AllowIntegerConditions
|
||||
value: '1'
|
||||
- key: readability-redundant-member-init.IgnoreBaseInCopyConstructors
|
||||
value: '1'
|
||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||
value: '1'
|
||||
- key: hicpp-special-member-functions.AllowSoleDefaultDtor
|
||||
value: '1'
|
||||
- key: readability-identifier-length.IgnoredVariableNames
|
||||
value: 'r|t|fd|id|ok|it|ts'
|
||||
- key: readability-identifier-length.IgnoredParameterNames
|
||||
value: 'fd|id|b'
|
||||
- key: performance-move-const-arg.CheckTriviallyCopyableMove
|
||||
value: '0'
|
||||
- key: hicpp-move-const-arg.CheckTriviallyCopyableMove
|
||||
value: '0'
|
||||
- key: misc-include-cleaner.IgnoreHeaders
|
||||
value: 'systemd/.*|sdbus-c\+\+/.*|gtest/.*|gmock/.*|bits/chrono.h|bits/basic_string.h|time.h|poll.h|stdlib.h|stdio.h'
|
||||
- key: readability-simplify-boolean-expr.IgnoreMacros
|
||||
value: '1'
|
||||
- key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams
|
||||
value: '1'
|
||||
# - key: bugprone-easily-swappable-parameters.MinimumLength
|
||||
# value: '3'
|
||||
# - key: readability-braces-around-statements.ShortStatementLines
|
||||
# value: '3'
|
||||
+52
-39
@@ -4,7 +4,9 @@ on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- master
|
||||
- release/v2.0
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -12,15 +14,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-22.04]
|
||||
os: [ubuntu-22.04, ubuntu-24.04]
|
||||
compiler: [g++, clang]
|
||||
build: [shared-libsystemd]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
compiler: g++
|
||||
build: embedded-static-libsystemd
|
||||
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: |
|
||||
@@ -34,61 +32,76 @@ 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++
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
|
||||
- name: install-googletest
|
||||
if: matrix.os == 'ubuntu-22.04' # On older ubuntus the libgmock-dev package is either unavailable or has faulty pkg-config file
|
||||
# We need to use libc++ with Clang because there is a bug in libstdc++ chrono headers that Clang has issues with
|
||||
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
|
||||
# run: |
|
||||
# sudo apt-get install -y libgmock-dev
|
||||
- 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: |
|
||||
sudo apt-get install -y libgmock-dev
|
||||
cmake -B _build -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" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
- name: configure-release
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
- name: configure-with-embedded-libsystemd
|
||||
cmake -B _build -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
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 ..
|
||||
cmake -B _build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="$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_BUILD_LIBSYSTEMD=ON -DSDBUSCPP_LIBSYSTEMD_VERSION=252 -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0
|
||||
- name: make
|
||||
run: |
|
||||
cd build
|
||||
cmake --build . -j4
|
||||
cmake --build _build -j4
|
||||
- name: verify
|
||||
run: |
|
||||
cd build
|
||||
sudo cmake --build . --target install
|
||||
ctest --output-on-failure
|
||||
sudo cmake --build _build --target install
|
||||
ctest --output-on-failure --test-dir _build
|
||||
- name: pack
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
|
||||
if: matrix.build == 'shared-libsystemd'
|
||||
run: |
|
||||
cd build
|
||||
cd _build
|
||||
cpack -G DEB
|
||||
- name: 'Upload Artifact'
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.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: |
|
||||
build/sdbus-c++*.deb
|
||||
build/sdbus-c++*.ddeb
|
||||
_build/sdbus-c++*.deb
|
||||
_build/sdbus-c++*.ddeb
|
||||
retention-days: 10
|
||||
static-analysis:
|
||||
name: static-analysis (ubuntu-24.04, clang-tidy, shared-libsystemd)
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install-deps
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y libsystemd-dev libgmock-dev clang
|
||||
sudo update-alternatives --remove-all cc
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
|
||||
sudo update-alternatives --remove-all c++
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
|
||||
- name: configure
|
||||
run: |
|
||||
cmake -B _build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-O0 -g" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_CLANG_TIDY=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_EXAMPLES=ON
|
||||
- name: make
|
||||
run: |
|
||||
cmake --build _build -j4
|
||||
freebsd-build:
|
||||
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:
|
||||
@@ -97,7 +110,7 @@ jobs:
|
||||
prepare: |
|
||||
pkg install -y cmake ninja pkgconf basu expat googletest
|
||||
run: |
|
||||
cmake -B _build -G Ninja -DBUILD_CODE_GEN=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON
|
||||
cmake -B _build -G Ninja -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON
|
||||
cmake --build _build
|
||||
cmake --install _build
|
||||
pkg install -y dbus
|
||||
|
||||
@@ -16,6 +16,9 @@ tests/run-test-on-device.sh
|
||||
.settings
|
||||
*.log
|
||||
|
||||
# vscode
|
||||
.vscode/
|
||||
|
||||
#autotools
|
||||
sdbus-cpp.pc
|
||||
*Makefile
|
||||
|
||||
+178
-95
@@ -2,59 +2,136 @@
|
||||
# PROJECT INFORMATION
|
||||
#-------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(sdbus-c++ VERSION 1.5.0 LANGUAGES C CXX)
|
||||
project(sdbus-c++ VERSION 2.3.1 LANGUAGES CXX C)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
# -------------------------------
|
||||
# CONFIGURATION OPTIONS
|
||||
# -------------------------------
|
||||
|
||||
option(SDBUSCPP_BUILD_LIBSYSTEMD "Fetch & build libsystemd static library and make it part of libsdbus-c++, instead of searching for it in the system" OFF)
|
||||
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 "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)
|
||||
option(SDBUSCPP_BUILD_TESTS "Build tests" OFF)
|
||||
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.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)
|
||||
option(SDBUSCPP_BUILD_EXAMPLES "Build example programs" OFF)
|
||||
option(SDBUSCPP_BUILD_DOCS "Build documentation for sdbus-c++" ON)
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
option(SDBUSCPP_BUILD_DOXYGEN_DOCS "Build doxygen documentation for sdbus-c++ API" OFF)
|
||||
endif()
|
||||
option(SDBUSCPP_CLANG_TIDY "Co-compile with clang-tidy static analyzer" OFF)
|
||||
#option(SDBUSCPP_COVERAGE "Build sdbus-c++ with code coverage instrumentation" OFF)
|
||||
# We promote the BUILD_SHARED_LIBS flag to a (global) option only if we are the main project
|
||||
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
|
||||
endif()
|
||||
|
||||
message(STATUS "")
|
||||
message(STATUS " *******************************************")
|
||||
message(STATUS " * SDBUS-C++ CONFIGURATION *")
|
||||
message(STATUS " *******************************************")
|
||||
message(STATUS "")
|
||||
message(STATUS " SDBUSCPP_BUILD_LIBSYSTEMD: ${SDBUSCPP_BUILD_LIBSYSTEMD}")
|
||||
if(SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
message(STATUS " SDBUSCPP_LIBSYSTEMD_VERSION: ${SDBUSCPP_LIBSYSTEMD_VERSION}")
|
||||
message(STATUS " SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS: ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}")
|
||||
endif()
|
||||
message(STATUS " SDBUSCPP_INSTALL: ${SDBUSCPP_INSTALL}")
|
||||
message(STATUS " SDBUSCPP_BUILD_TESTS: ${SDBUSCPP_BUILD_TESTS}")
|
||||
if(SDBUSCPP_BUILD_TESTS)
|
||||
message(STATUS " SDBUSCPP_BUILD_PERF_TESTS: ${SDBUSCPP_BUILD_PERF_TESTS}")
|
||||
message(STATUS " SDBUSCPP_BUILD_STRESS_TESTS: ${SDBUSCPP_BUILD_STRESS_TESTS}")
|
||||
message(STATUS " SDBUSCPP_TESTS_INSTALL_PATH: ${SDBUSCPP_TESTS_INSTALL_PATH}")
|
||||
message(STATUS " SDBUSCPP_GOOGLETEST_VERSION: ${SDBUSCPP_GOOGLETEST_VERSION}")
|
||||
message(STATUS " SDBUSCPP_GOOGLETEST_GIT_REPO: ${SDBUSCPP_GOOGLETEST_GIT_REPO}")
|
||||
endif()
|
||||
message(STATUS " SDBUSCPP_BUILD_CODEGEN: ${SDBUSCPP_BUILD_CODEGEN}")
|
||||
message(STATUS " SDBUSCPP_BUILD_EXAMPLES: ${SDBUSCPP_BUILD_EXAMPLES}")
|
||||
message(STATUS " SDBUSCPP_BUILD_DOCS: ${SDBUSCPP_BUILD_DOCS}")
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
message(STATUS " SDBUSCPP_BUILD_DOXYGEN_DOCS: ${SDBUSCPP_BUILD_DOXYGEN_DOCS}")
|
||||
endif()
|
||||
message(STATUS " BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}")
|
||||
message(STATUS "")
|
||||
|
||||
#-------------------------------
|
||||
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES
|
||||
#-------------------------------
|
||||
|
||||
set(LIBSYSTEMD_IMPL "systemd")
|
||||
set(LIBSYSTEMD_LIB "libsystemd")
|
||||
|
||||
option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into libsdbus-c++" OFF)
|
||||
|
||||
if(NOT BUILD_LIBSYSTEMD)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236)
|
||||
if(NOT TARGET PkgConfig::Systemd)
|
||||
message(WARNING "libsystemd not found, checking for libelogind instead")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=236)
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(LIBSYSTEMD_IMPL "elogind")
|
||||
set(LIBSYSTEMD_LIB "libelogind")
|
||||
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
|
||||
list(GET VERSION_LIST 0 Systemd_VERSION)
|
||||
else()
|
||||
message(WARNING "libelogind not found, checking for basu instead")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL basu)
|
||||
set(LIBSYSTEMD_IMPL "basu")
|
||||
set(LIBSYSTEMD_LIB "basu")
|
||||
# https://git.sr.ht/~emersion/basu/commit/d4d185d29a26
|
||||
set(Systemd_VERSION "240")
|
||||
endif()
|
||||
if(SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
# Build static libsystemd library as an external project
|
||||
include(cmake/LibsystemdExternalProject.cmake)
|
||||
set(SDBUS_IMPL "systemd")
|
||||
set(SDBUS_LIB "libsystemd")
|
||||
else()
|
||||
# Search for sd-bus implementations in the system as per user's configuration
|
||||
set(SDBUS_LIBS ${SDBUSCPP_SDBUS_LIB})
|
||||
if(SDBUSCPP_SDBUS_LIB STREQUAL "default")
|
||||
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>=${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>=${MINIMUM_SDBUS_VERSION})
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(SDBUS_IMPL "elogind")
|
||||
set(SDBUS_LIB "libelogind")
|
||||
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
|
||||
list(GET VERSION_LIST 0 Systemd_VERSION)
|
||||
break()
|
||||
endif()
|
||||
elseif(LIB STREQUAL "basu")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL basu)
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(SDBUS_IMPL "basu")
|
||||
set(SDBUS_LIB "basu")
|
||||
set(Systemd_VERSION "240") # https://git.sr.ht/~emersion/basu/commit/d4d185d29a26
|
||||
break()
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Unrecognized sd-bus implementation library ${LIB}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT TARGET PkgConfig::Systemd)
|
||||
message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found "
|
||||
"(if you have systemd in your OS, you may want to install package containing pkgconfig "
|
||||
" files for libsystemd library. On Ubuntu, that is libsystemd-dev. "
|
||||
" Alternatively, you may turn BUILD_LIBSYSTEMD on for sdbus-c++ to download, build "
|
||||
"and incorporate libsystemd as embedded library within sdbus-c++)")
|
||||
message(FATAL_ERROR "None of ${SDBUS_LIBS} libraries implementing sd-bus was found (Are their dev packages installed?)")
|
||||
endif()
|
||||
add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd)
|
||||
string(REGEX MATCHALL "([0-9]+)" SYSTEMD_VERSION_LIST "${Systemd_VERSION}")
|
||||
list(GET SYSTEMD_VERSION_LIST 0 LIBSYSTEMD_VERSION)
|
||||
message(STATUS "Building with libsystemd v${LIBSYSTEMD_VERSION}")
|
||||
else()
|
||||
# Build static libsystemd library as an external project
|
||||
include(cmake/LibsystemdExternalProject.cmake)
|
||||
list(GET SYSTEMD_VERSION_LIST 0 SDBUSCPP_LIBSYSTEMD_VERSION)
|
||||
message(STATUS "Building with libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
include(cmake/clang-tidy.cmake) # Static analysis with clang-tidy
|
||||
|
||||
#-------------------------------
|
||||
# SOURCE FILES CONFIGURATION
|
||||
#-------------------------------
|
||||
@@ -87,8 +164,11 @@ set(SDBUSCPP_HDR_SRCS
|
||||
${SDBUSCPP_SOURCE_DIR}/ISdBus.h)
|
||||
|
||||
set(SDBUSCPP_PUBLIC_HDRS
|
||||
${SDBUSCPP_INCLUDE_DIR}/Awaitable.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.inl
|
||||
${SDBUSCPP_INCLUDE_DIR}/VTableItems.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/VTableItems.inl
|
||||
${SDBUSCPP_INCLUDE_DIR}/Error.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/IConnection.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/AdaptorInterfaces.h
|
||||
@@ -109,7 +189,7 @@ set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HD
|
||||
# GENERAL COMPILER CONFIGURATION
|
||||
#-------------------------------
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
#----------------------------------
|
||||
# LIBRARY BUILD INFORMATION
|
||||
@@ -118,24 +198,19 @@ set(CMAKE_CXX_STANDARD 17)
|
||||
set(SDBUSCPP_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(SDBUSCPP_VERSION "${PROJECT_VERSION}")
|
||||
|
||||
# We promote the BUILD_SHARED_LIBS flag to a (global) option only if we are the main project
|
||||
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
|
||||
endif()
|
||||
|
||||
# Having an object target allows unit tests to reuse already built sources without re-building
|
||||
add_library(sdbus-c++-objlib OBJECT ${SDBUSCPP_SRCS})
|
||||
target_compile_definitions(sdbus-c++-objlib PRIVATE
|
||||
BUILD_LIB=1
|
||||
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
|
||||
SDBUS_${LIBSYSTEMD_IMPL}
|
||||
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
|
||||
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
SDBUS_${SDBUS_IMPL}
|
||||
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
|
||||
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
if(BUILD_LIBSYSTEMD)
|
||||
if(SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
|
||||
endif()
|
||||
target_link_libraries(sdbus-c++-objlib
|
||||
@@ -144,6 +219,9 @@ target_link_libraries(sdbus-c++-objlib
|
||||
Threads::Threads)
|
||||
|
||||
add_library(sdbus-c++)
|
||||
# Create alias to allow consistent target use regardless whether used in-source or found via find_package
|
||||
add_library(SDBusCpp::sdbus-c++ ALIAS sdbus-c++)
|
||||
|
||||
target_include_directories(sdbus-c++ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
set_target_properties(sdbus-c++
|
||||
@@ -153,28 +231,11 @@ set_target_properties(sdbus-c++
|
||||
OUTPUT_NAME "sdbus-c++")
|
||||
target_link_libraries(sdbus-c++ PRIVATE sdbus-c++-objlib)
|
||||
|
||||
#----------------------------------
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
set(EXPORT_SET sdbus-c++)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
list(APPEND EXPORT_SET "sdbus-c++-objlib")
|
||||
endif()
|
||||
|
||||
install(TARGETS ${EXPORT_SET}
|
||||
EXPORT sdbus-c++-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime NAMELINK_COMPONENT dev
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT dev)
|
||||
|
||||
#----------------------------------
|
||||
# TESTS
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_TESTS "Build tests (default OFF)" OFF)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
if(SDBUSCPP_BUILD_TESTS)
|
||||
message(STATUS "Building with tests")
|
||||
enable_testing()
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
|
||||
@@ -184,9 +245,7 @@ endif()
|
||||
# UTILS
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_CODE_GEN "Build and install interface stub code generator (default OFF)" OFF)
|
||||
|
||||
if(BUILD_CODE_GEN)
|
||||
if(SDBUSCPP_BUILD_CODEGEN)
|
||||
message(STATUS "Building with code generator tool")
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools")
|
||||
endif()
|
||||
@@ -195,9 +254,7 @@ endif()
|
||||
# EXAMPLES
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_EXAMPLES "Build example programs (default OFF)" OFF)
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
if(SDBUSCPP_BUILD_EXAMPLES)
|
||||
message(STATUS "Building with examples")
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
@@ -206,13 +263,9 @@ endif()
|
||||
# DOCUMENTATION
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_DOC "Build documentation for sdbus-c++" ON)
|
||||
|
||||
if(BUILD_DOC)
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
message(STATUS "Building with documentation")
|
||||
option(BUILD_DOXYGEN_DOC "Build doxygen documentation for sdbus-c++ API" OFF)
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/docs")
|
||||
install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc)
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
@@ -221,47 +274,77 @@ endif()
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
install(EXPORT sdbus-c++-targets
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
|
||||
NAMESPACE SDBusCpp::
|
||||
COMPONENT dev)
|
||||
|
||||
configure_package_config_file(cmake/sdbus-c++-config.cmake.in cmake/sdbus-c++-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++)
|
||||
write_basic_package_version_file(cmake/sdbus-c++-config-version.cmake COMPATIBILITY SameMajorVersion)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
|
||||
COMPONENT dev)
|
||||
|
||||
if(BUILD_SHARED_LIBS AND (BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
|
||||
if(BUILD_SHARED_LIBS AND (SDBUSCPP_BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
|
||||
set(PKGCONFIG_REQS ".private")
|
||||
else()
|
||||
set(PKGCONFIG_REQS "")
|
||||
endif()
|
||||
set(PKGCONFIG_DEPS ${LIBSYSTEMD_LIB})
|
||||
set(PKGCONFIG_DEPS ${SDBUS_LIB})
|
||||
configure_file(pkgconfig/sdbus-c++.pc.in pkgconfig/sdbus-c++.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
|
||||
|
||||
#----------------------------------
|
||||
# CPack
|
||||
# EXPORTING BUILD-TREE TARGETS
|
||||
#----------------------------------
|
||||
|
||||
set(EXPORT_SET sdbus-c++)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
list(APPEND EXPORT_SET "sdbus-c++-objlib")
|
||||
endif()
|
||||
|
||||
export(TARGETS ${EXPORT_SET}
|
||||
NAMESPACE SDBusCpp::
|
||||
FILE sdbus-c++-targets.cmake)
|
||||
|
||||
#----------------------------------
|
||||
# INSTALLATION & EXPORTING INSTALL-TREE TARGETS
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_INSTALL)
|
||||
install(TARGETS ${EXPORT_SET}
|
||||
EXPORT sdbus-c++-targets
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-runtime
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sdbus-c++-runtime NAMELINK_COMPONENT sdbus-c++-dev
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sdbus-c++-dev
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT sdbus-c++-dev)
|
||||
install(EXPORT sdbus-c++-targets
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
|
||||
NAMESPACE SDBusCpp::
|
||||
COMPONENT sdbus-c++-dev)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
|
||||
COMPONENT sdbus-c++-dev)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT sdbus-c++-dev)
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# CPACK
|
||||
#----------------------------------
|
||||
|
||||
set(CPACK_PACKAGE_VENDOR "Kistler")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "high-level C++ D-Bus library")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High-level C++ D-Bus library")
|
||||
set(CPACK_PACKAGE_CONTACT "info@kistler.com")
|
||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||
set(CPACK_COMPONENTS_ALL runtime dev doc)
|
||||
set(CPACK_COMPONENT_DEV_DEPENDS "runtime")
|
||||
|
||||
set(CPACK_COMPONENTS_ALL sdbus-c++-runtime sdbus-c++-dev sdbus-c++-doc)
|
||||
set(CPACK_COMPONENT_DEV_DEPENDS "sdbus-c++-runtime")
|
||||
# specific for DEB generator
|
||||
set(CPACK_DEB_COMPONENT_INSTALL ON)
|
||||
set(CPACK_DEBIAN_RUNTIME_DEBUGINFO_PACKAGE ON)
|
||||
set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME ${PROJECT_NAME})
|
||||
set(CPACK_DEBIAN_SDBUS-C++-RUNTIME_PACKAGE_NAME ${PROJECT_NAME})
|
||||
set(CPACK_DEBIAN_SDBUS-C++-DEV_PACKAGE_NAME "sdbus-c++-dev")
|
||||
set(CPACK_DEBIAN_SDBUS-C++-DOC_PACKAGE_NAME "sdbus-c++-doc")
|
||||
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)
|
||||
|
||||
@@ -248,3 +248,80 @@ v1.5.0
|
||||
- Add INSTALL_TESTS CMake option
|
||||
- Minor UnixFd cleanups
|
||||
- Additional little fixes and updates in code, build system, CI, and documentation
|
||||
|
||||
v1.6.0
|
||||
- Add support for enums in D-Bus serialization and signatures
|
||||
- Add support for std::variant as an alternative C++ type for D-Bus Variant
|
||||
- Add support for implicit conversions between std::variant and sdbus::Variant
|
||||
- Fix missing includes
|
||||
|
||||
v2.0.0
|
||||
- A new major version with revamped API, bringing numerous new features, simplifications, fixes and breaking changes improving not only API consistency, safety and expressiveness, but also performance
|
||||
- Add section 'Migrating to sdbus-c++ v2' to the 'Using sdbus-c++' document providing the complete list of breaking API/ABI/behavior changes and migration notes
|
||||
- Switch to C++20 standard (but the API is backwards-compatible with C++17, skipping C++20 features in such a case)
|
||||
- Add strong types to public API
|
||||
- Add support for string_view as a D-Bus type representation
|
||||
- Enable compile-time D-Bus signature generation
|
||||
- Redesign Object vtable registration API
|
||||
- Improve Proxy signal subscription API
|
||||
- Refactor object manager API for consistency
|
||||
- Introduce native sd-event integration
|
||||
- Let callbacks take message objects by value
|
||||
- Simplify async D-Bus connection handling
|
||||
- Simplify async call state flag
|
||||
- Make Variant constructor explicit
|
||||
- Use optional for passing potential call errors
|
||||
- Rename and re-organize CMake options
|
||||
- Rename connection creation methods
|
||||
- Have all async function names finish with *Async for consistency
|
||||
- Implement more flexible searching for sd-bus libs
|
||||
- Add new `SDBUSCPP_SDBUS_LIB` CMake configuration variable determining which sd-bus library shall be picked
|
||||
- Make Variant conversion operator explicit
|
||||
- Use sd-bus API to get the current message
|
||||
- Use sd_bus_match_signal() for signal registration
|
||||
- Provide also const char* overloads for convenience functions
|
||||
- Disable move in generated adaptor and proxy classes
|
||||
- Improve and make more efficient some Message API
|
||||
- Add Slot-returning overloads of async method calls
|
||||
- Remove floating_slot_t tag and use return_slot_t instead
|
||||
- Add [[nodiscard]] to many relevant API functions
|
||||
- Add proper fix for timeout handling
|
||||
- Fix for external event loops in which the event loop thread ID was not correctly initialized (now fixed and simplified by not needing the thread ID anymore)
|
||||
- Remove deprecated stuff
|
||||
- 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
|
||||
|
||||
v2.2.1
|
||||
- Fix component names in CPack configuration
|
||||
- Generate correct, expected DEB package names
|
||||
|
||||
v2.3.0
|
||||
- Add coroutine support for client-side async calls
|
||||
- Add support for dumping variant to string
|
||||
- Introduce clang-tidy for static analysis and fix issues
|
||||
- Add deduction guides for Struct from std::tuple
|
||||
- Other fixes and improvements
|
||||
|
||||
v2.3.1
|
||||
- Fix ABI compat issue by moving new virtual functions to the end of the class
|
||||
|
||||
@@ -26,70 +26,76 @@ $ sudo cmake --build . --target install
|
||||
|
||||
### CMake configuration flags for sdbus-c++
|
||||
|
||||
* `BUILD_CODE_GEN` [boolean]
|
||||
* `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 `-DBUILD_CODE_GEN=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.
|
||||
|
||||
* `BUILD_DOC` [boolean]
|
||||
* `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.
|
||||
|
||||
* `BUILD_TESTS` [boolean]
|
||||
* `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:
|
||||
|
||||
* `ENABLE_PERF_TESTS` [boolean]
|
||||
* `SDBUSCPP_BUILD_PERF_TESTS` [boolean]
|
||||
|
||||
Option for building sdbus-c++ performance tests. Default value: `OFF`.
|
||||
Build sdbus-c++ performance tests. Default value: `OFF`.
|
||||
|
||||
* `ENABLE_STRESS_TESTS` [boolean]
|
||||
* `SDBUSCPP_BUILD_STRESS_TESTS` [boolean]
|
||||
|
||||
Option for building sdbus-c++ stress tests. Default value: `OFF`.
|
||||
Build sdbus-c++ stress tests. Default value: `OFF`.
|
||||
|
||||
* `INSTALL_TESTS` [boolean]
|
||||
|
||||
Option for installing tests that were built. Default value: `OFF`.
|
||||
|
||||
* `TESTS_INSTALL_PATH` [string]
|
||||
* `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`).
|
||||
|
||||
* `BUILD_LIBSYSTEMD` [boolean]
|
||||
* `SDBUSCPP_BUILD_EXAMPLES` [boolean]
|
||||
|
||||
Option for building libsystemd dependency library automatically when sdbus-c++ is built, and making libsystemd an integral part of sdbus-c++ library. Default value: `OFF`. Might be very helpful in non-systemd environments where libsystemd shared library is unavailable (see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information). With this option turned on, you may also provide the following configuration flag:
|
||||
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`.
|
||||
|
||||
* `LIBSYSTEMD_VERSION` [string]
|
||||
* `SDBUSCPP_BUILD_LIBSYSTEMD` [boolean]
|
||||
|
||||
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`.
|
||||
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.
|
||||
|
||||
* `LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
|
||||
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).
|
||||
|
||||
With this option turned off, you may provide the following additional configuration flag:
|
||||
|
||||
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.
|
||||
* `SDBUSCPP_SDBUS_LIB` [string]
|
||||
|
||||
Defines which sd-bus implementation library to search for and use. Allowed values: `default`, `systemd`, `elogind`, `basu`. Default value: `default`, which means that sdbus-c++ will try to find any of `systemd`, `elogind`, `basu` in the order as listed here.
|
||||
|
||||
With this option turned on, you may provide the following additional configuration flag:
|
||||
|
||||
* `SDBUSCPP_LIBSYSTEMD_VERSION` [string]
|
||||
|
||||
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 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`.
|
||||
|
||||
* `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 a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-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 xml2cpp code generator (`BUILD_CODE_GEN` option is ON).
|
||||
* `expat` - necessary when building the xml2cpp binding code generator (`SDBUSCPP_BUILD_CODEGEN` option is `ON`).
|
||||
|
||||
Licensing
|
||||
---------
|
||||
|
||||
@@ -18,9 +18,6 @@ if (NOT CAP_FOUND)
|
||||
find_library(CAP_LIBRARIES cap) # Compat with Ubuntu 14.04 which ships libcap w/o .pc file
|
||||
endif()
|
||||
|
||||
set(LIBSYSTEMD_VERSION "242" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
|
||||
set(LIBSYSTEMD_EXTRA_CONFIG_OPTS "" CACHE STRING "Additional configuration options to be passed as-is to libsystemd build system")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(LIBSYSTEMD_BUILD_TYPE "plain")
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
@@ -29,24 +26,24 @@ else()
|
||||
set(LIBSYSTEMD_BUILD_TYPE "release")
|
||||
endif()
|
||||
|
||||
if(LIBSYSTEMD_VERSION LESS "239")
|
||||
if(SDBUSCPP_LIBSYSTEMD_VERSION LESS "239")
|
||||
message(FATAL_ERROR "Only libsystemd version >=239 can be built as static part of sdbus-c++")
|
||||
endif()
|
||||
if(LIBSYSTEMD_VERSION GREATER "240")
|
||||
if(SDBUSCPP_LIBSYSTEMD_VERSION GREATER "240")
|
||||
set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h)
|
||||
endif()
|
||||
|
||||
message(STATUS "Building with embedded libsystemd v${LIBSYSTEMD_VERSION}")
|
||||
message(STATUS "Building with embedded libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(LibsystemdBuildProject
|
||||
PREFIX libsystemd-v${LIBSYSTEMD_VERSION}
|
||||
PREFIX libsystemd-v${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
GIT_REPOSITORY https://github.com/systemd/systemd-stable.git
|
||||
GIT_TAG v${LIBSYSTEMD_VERSION}-stable
|
||||
GIT_TAG v${SDBUSCPP_LIBSYSTEMD_VERSION}-stable
|
||||
GIT_SHALLOW 1
|
||||
UPDATE_COMMAND ""
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
|
||||
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Drootprefix=<INSTALL_DIR> -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${LIBSYSTEMD_EXTRA_CONFIG_OPTS}
|
||||
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Drootprefix=<INSTALL_DIR> -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}
|
||||
BUILD_COMMAND ${BUILD_VERSION_H}
|
||||
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
|
||||
BUILD_ALWAYS 0
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#----------------------------------
|
||||
# STATIC ANALYSIS
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_CLANG_TIDY)
|
||||
message(STATUS "Building with static analysis")
|
||||
find_program(CLANG_TIDY NAMES clang-tidy)
|
||||
if(NOT CLANG_TIDY)
|
||||
message(WARNING "clang-tidy not found")
|
||||
else()
|
||||
message(STATUS "clang-tidy found: ${CLANG_TIDY}")
|
||||
set(DO_CLANG_TIDY "${CLANG_TIDY}")
|
||||
#set(DO_CLANG_TIDY "${CLANG_TIDY}" "-fix")
|
||||
set(CMAKE_CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
|
||||
endif()
|
||||
endif()
|
||||
+12
-8
@@ -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)
|
||||
|
||||
@@ -15,15 +15,19 @@ if(BUILD_DOXYGEN_DOC)
|
||||
# workaround bug https://github.com/doxygen/doxygen/pull/6787
|
||||
add_custom_command(TARGET doc POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/sdbus-c++-class-diagram.png ${CMAKE_CURRENT_BINARY_DIR}/html/.)
|
||||
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT doc)
|
||||
else()
|
||||
message(WARNING "Documentation enabled, but Doxygen cannot be found")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(FILES sdbus-c++-class-diagram.png
|
||||
sdbus-c++-class-diagram.uml
|
||||
systemd-dbus-config.md
|
||||
using-sdbus-c++.md
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc)
|
||||
if(SDBUSCPP_INSTALL)
|
||||
install(FILES sdbus-c++-class-diagram.png
|
||||
sdbus-c++-class-diagram.uml
|
||||
systemd-dbus-config.md
|
||||
using-sdbus-c++.md
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
|
||||
|
||||
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()
|
||||
|
||||
+438
-192
File diff suppressed because it is too large
Load Diff
+21
-3
@@ -1,6 +1,24 @@
|
||||
# Building examples
|
||||
|
||||
add_executable(obj-manager-server org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp)
|
||||
set(OBJECTMANAGER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.DBus.ObjectManager)
|
||||
set(OBJECTMANAGER_GENERATED_DIR ${OBJECTMANAGER_SOURCE_DIR}/dbus-api/gen-cpp)
|
||||
set(OBJECTMANAGER_SERVER_SRCS
|
||||
${OBJECTMANAGER_SOURCE_DIR}/obj-manager-server.cpp
|
||||
${OBJECTMANAGER_GENERATED_DIR}/examplemanager-planet1-server-glue.h)
|
||||
|
||||
add_executable(obj-manager-server ${OBJECTMANAGER_SERVER_SRCS})
|
||||
target_include_directories(obj-manager-server SYSTEM PRIVATE ${OBJECTMANAGER_GENERATED_DIR})
|
||||
target_link_libraries(obj-manager-server sdbus-c++)
|
||||
|
||||
add_executable(obj-manager-client org.freedesktop.DBus.ObjectManager/obj-manager-client.cpp)
|
||||
target_link_libraries(obj-manager-client sdbus-c++)
|
||||
set(OBJECTMANAGER_CLIENT_SRCS
|
||||
${OBJECTMANAGER_SOURCE_DIR}/obj-manager-client.cpp
|
||||
${OBJECTMANAGER_GENERATED_DIR}/examplemanager-planet1-client-glue.h)
|
||||
|
||||
add_executable(obj-manager-client ${OBJECTMANAGER_CLIENT_SRCS})
|
||||
target_include_directories(obj-manager-client SYSTEM PRIVATE ${OBJECTMANAGER_GENERATED_DIR})
|
||||
target_link_libraries(obj-manager-client sdbus-c++)
|
||||
|
||||
if(SDBUSCPP_INSTALL)
|
||||
install(TARGETS obj-manager-server DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-examples)
|
||||
install(TARGETS obj-manager-client DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-examples)
|
||||
endif()
|
||||
|
||||
+13
-4
@@ -21,28 +21,37 @@ public:
|
||||
|
||||
protected:
|
||||
Planet1_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
Planet1_proxy(const Planet1_proxy&) = delete;
|
||||
Planet1_proxy& operator=(const Planet1_proxy&) = delete;
|
||||
Planet1_proxy(Planet1_proxy&&) = delete;
|
||||
Planet1_proxy& operator=(Planet1_proxy&&) = delete;
|
||||
|
||||
~Planet1_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
uint64_t GetPopulation()
|
||||
{
|
||||
uint64_t result;
|
||||
proxy_.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
std::string Name()
|
||||
{
|
||||
return proxy_.getProperty("Name").onInterface(INTERFACE_NAME);
|
||||
return m_proxy.getProperty("Name").onInterface(INTERFACE_NAME).get<std::string>();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& proxy_;
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
+14
-4
@@ -21,14 +21,24 @@ public:
|
||||
|
||||
protected:
|
||||
Planet1_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
: m_object(object)
|
||||
{
|
||||
object_.registerMethod("GetPopulation").onInterface(INTERFACE_NAME).withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); });
|
||||
object_.registerProperty("Name").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Name(); });
|
||||
}
|
||||
|
||||
Planet1_adaptor(const Planet1_adaptor&) = delete;
|
||||
Planet1_adaptor& operator=(const Planet1_adaptor&) = delete;
|
||||
Planet1_adaptor(Planet1_adaptor&&) = delete;
|
||||
Planet1_adaptor& operator=(Planet1_adaptor&&) = delete;
|
||||
|
||||
~Planet1_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable( sdbus::registerMethod("GetPopulation").withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); })
|
||||
, sdbus::registerProperty("Name").withGetter([this](){ return this->Name(); })
|
||||
).forInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual uint64_t GetPopulation() = 0;
|
||||
|
||||
@@ -36,7 +46,7 @@ private:
|
||||
virtual std::string Name() = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
@@ -12,34 +12,48 @@
|
||||
#include "examplemanager-planet1-client-glue.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
class PlanetProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::ExampleManager::Planet1_proxy >
|
||||
{
|
||||
public:
|
||||
PlanetProxy(sdbus::IConnection& connection, std::string destination, std::string path)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(path))
|
||||
PlanetProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath path)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(path))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
PlanetProxy(const PlanetProxy&) = delete;
|
||||
PlanetProxy& operator=(const PlanetProxy&) = delete;
|
||||
PlanetProxy(PlanetProxy&&) = delete;
|
||||
PlanetProxy& operator=(PlanetProxy&&) = delete;
|
||||
|
||||
~PlanetProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
};
|
||||
|
||||
class ManagerProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
|
||||
class ManagerProxy final : public sdbus::ProxyInterfaces<sdbus::ObjectManager_proxy>
|
||||
{
|
||||
public:
|
||||
ManagerProxy(sdbus::IConnection& connection, const std::string& destination, std::string path)
|
||||
: ProxyInterfaces(connection, destination, std::move(path))
|
||||
, m_connection(connection)
|
||||
, m_destination(destination)
|
||||
ManagerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath path)
|
||||
: ProxyInterfaces(connection, destination, std::move(path))
|
||||
, m_connection(connection)
|
||||
, m_destination(std::move(destination))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
ManagerProxy(const ManagerProxy&) = delete;
|
||||
ManagerProxy& operator=(const ManagerProxy&) = delete;
|
||||
ManagerProxy(ManagerProxy&&) = delete;
|
||||
ManagerProxy& operator=(ManagerProxy&&) = delete;
|
||||
|
||||
~ManagerProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
@@ -47,8 +61,7 @@ public:
|
||||
|
||||
void handleExistingObjects()
|
||||
{
|
||||
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
|
||||
objectsInterfacesAndProperties = GetManagedObjects();
|
||||
auto objectsInterfacesAndProperties = GetManagedObjects();
|
||||
for (const auto& [object, interfacesAndProperties] : objectsInterfacesAndProperties) {
|
||||
onInterfacesAdded(object, interfacesAndProperties);
|
||||
}
|
||||
@@ -56,55 +69,57 @@ public:
|
||||
|
||||
private:
|
||||
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
|
||||
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties) override
|
||||
{
|
||||
std::cout << objectPath << " added:\t";
|
||||
for (const auto& [interface, _] : interfacesAndProperties) {
|
||||
std::cout << interface << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << '\n';
|
||||
|
||||
// Parse and print some more info
|
||||
auto planetInterface = interfacesAndProperties.find(org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME);
|
||||
auto planetInterface = interfacesAndProperties.find(sdbus::InterfaceName{org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME});
|
||||
if (planetInterface == interfacesAndProperties.end()) {
|
||||
return;
|
||||
}
|
||||
const auto& properties = planetInterface->second;
|
||||
// get a property which was passed as part of the signal.
|
||||
const auto& name = properties.at("Name").get<std::string>();
|
||||
const auto& name = properties.at(sdbus::PropertyName{"Name"}).get<std::string>();
|
||||
// or create a proxy instance to the newly added object.
|
||||
PlanetProxy planet(m_connection, m_destination, objectPath);
|
||||
std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << std::endl;
|
||||
std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << '\n';
|
||||
}
|
||||
|
||||
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces) override
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces) override
|
||||
{
|
||||
std::cout << objectPath << " removed:\t";
|
||||
for (const auto& interface : interfaces) {
|
||||
std::cout << interface << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
sdbus::IConnection& m_connection;
|
||||
std::string m_destination;
|
||||
sdbus::ServiceName m_destination;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
auto connection = sdbus::createSessionBusConnection();
|
||||
|
||||
auto managerProxy = std::make_unique<ManagerProxy>(*connection, "org.sdbuscpp.examplemanager", "/org/sdbuscpp/examplemanager");
|
||||
sdbus::ServiceName destination{"org.sdbuscpp.examplemanager"};
|
||||
sdbus::ObjectPath objectPath{"/org/sdbuscpp/examplemanager"};
|
||||
auto managerProxy = std::make_unique<ManagerProxy>(*connection, std::move(destination), std::move(objectPath));
|
||||
try {
|
||||
managerProxy->handleExistingObjects();
|
||||
}
|
||||
catch (const sdbus::Error& e) {
|
||||
if (e.getName() == "org.freedesktop.DBus.Error.ServiceUnknown") {
|
||||
std::cout << "Waiting for server to start ..." << std::endl;
|
||||
std::cout << "Waiting for server to start ..." << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
connection->enterEventLoop();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,39 +18,52 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
class ManagerAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
|
||||
class ManagerAdaptor : public sdbus::AdaptorInterfaces<sdbus::ObjectManager_adaptor>
|
||||
{
|
||||
public:
|
||||
ManagerAdaptor(sdbus::IConnection& connection, std::string path)
|
||||
: AdaptorInterfaces(connection, std::move(path))
|
||||
ManagerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path)
|
||||
: AdaptorInterfaces(connection, std::move(path))
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
ManagerAdaptor(const ManagerAdaptor&) = delete;
|
||||
ManagerAdaptor& operator=(const ManagerAdaptor&) = delete;
|
||||
ManagerAdaptor(ManagerAdaptor&&) = delete;
|
||||
ManagerAdaptor& operator=(ManagerAdaptor&&) = delete;
|
||||
|
||||
~ManagerAdaptor()
|
||||
{
|
||||
unregisterAdaptor();
|
||||
}
|
||||
};
|
||||
|
||||
class PlanetAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor,
|
||||
sdbus::ManagedObject_adaptor,
|
||||
sdbus::Properties_adaptor >
|
||||
class PlanetAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor
|
||||
, sdbus::ManagedObject_adaptor
|
||||
, sdbus::Properties_adaptor >
|
||||
{
|
||||
public:
|
||||
PlanetAdaptor(sdbus::IConnection& connection, std::string path, std::string name, uint64_t poulation)
|
||||
: AdaptorInterfaces(connection, std::move(path))
|
||||
, m_name(std::move(name))
|
||||
, m_population(poulation)
|
||||
PlanetAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path, std::string name, uint64_t population)
|
||||
: AdaptorInterfaces(connection, std::move(path))
|
||||
, m_name(std::move(name))
|
||||
, m_population(population)
|
||||
{
|
||||
registerAdaptor();
|
||||
emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
|
||||
emitInterfacesAddedSignal({sdbus::InterfaceName{Planet1_adaptor::INTERFACE_NAME}});
|
||||
}
|
||||
|
||||
PlanetAdaptor(const PlanetAdaptor&) = delete;
|
||||
PlanetAdaptor& operator=(const PlanetAdaptor&) = delete;
|
||||
PlanetAdaptor(PlanetAdaptor&&) = delete;
|
||||
PlanetAdaptor& operator=(PlanetAdaptor&&) = delete;
|
||||
|
||||
~PlanetAdaptor()
|
||||
{
|
||||
emitInterfacesRemovedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
|
||||
emitInterfacesRemovedSignal({sdbus::InterfaceName{Planet1_adaptor::INTERFACE_NAME}});
|
||||
unregisterAdaptor();
|
||||
}
|
||||
|
||||
@@ -76,24 +89,26 @@ void printCountDown(const std::string& message, int seconds)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
std::cout << i << " " << std::flush;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
auto connection = sdbus::createSessionBusConnection();
|
||||
connection->requestName("org.sdbuscpp.examplemanager");
|
||||
const sdbus::ServiceName serviceName{"org.sdbuscpp.examplemanager"};
|
||||
connection->requestName(serviceName);
|
||||
connection->enterEventLoopAsync();
|
||||
|
||||
auto manager = std::make_unique<ManagerAdaptor>(*connection, "/org/sdbuscpp/examplemanager");
|
||||
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
|
||||
auto manager = std::make_unique<ManagerAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager"});
|
||||
while (true)
|
||||
{
|
||||
printCountDown("Creating PlanetAdaptor in ", 5);
|
||||
auto earth = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Earth", "Earth", 7'874'965'825);
|
||||
auto earth = std::make_unique<PlanetAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Earth"}, "Earth", 7'874'965'825);
|
||||
printCountDown("Creating PlanetAdaptor in ", 5);
|
||||
auto trantor = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Trantor", "Trantor", 40'000'000'000);
|
||||
auto trantor = std::make_unique<PlanetAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Trantor"}, "Trantor", 40'000'000'000);
|
||||
printCountDown("Creating PlanetAdaptor in ", 5);
|
||||
auto laconia = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Laconia", "Laconia", 231'721);
|
||||
auto laconia = std::make_unique<PlanetAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Laconia"}, "Laconia", 231'721);
|
||||
printCountDown("Removing PlanetAdaptor in ", 5);
|
||||
earth.reset();
|
||||
printCountDown("Removing PlanetAdaptor in ", 5);
|
||||
@@ -102,7 +117,7 @@ int main()
|
||||
laconia.reset();
|
||||
}
|
||||
|
||||
connection->releaseName("org.sdbuscpp.examplemanager");
|
||||
connection->releaseName(serviceName);
|
||||
connection->leaveEventLoop();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file AdaptorInterfaces.h
|
||||
*
|
||||
@@ -35,7 +35,7 @@
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IConnection;
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@@ -50,18 +50,18 @@ namespace sdbus {
|
||||
class ObjectHolder
|
||||
{
|
||||
protected:
|
||||
ObjectHolder(std::unique_ptr<IObject>&& object)
|
||||
explicit ObjectHolder(std::unique_ptr<IObject>&& object)
|
||||
: object_(std::move(object))
|
||||
{
|
||||
}
|
||||
|
||||
const IObject& getObject() const
|
||||
[[nodiscard]] const IObject& getObject() const
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
}
|
||||
|
||||
IObject& getObject()
|
||||
[[nodiscard]] IObject& getObject()
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
@@ -82,17 +82,23 @@ namespace sdbus {
|
||||
* adaptor-side interface classes representing interfaces (with methods, signals and properties)
|
||||
* of the D-Bus object.
|
||||
*
|
||||
* In the final adaptor class inherited from AdaptorInterfaces, it is necessary to finish
|
||||
* adaptor registration in class constructor (finishRegistration();`), and, conversely,
|
||||
* unregister the adaptor in class destructor (`unregister();`).
|
||||
* In the final adaptor class inherited from AdaptorInterfaces, one needs to make sure:
|
||||
* 1. to call `registerAdaptor();` in the class constructor, and, conversely,
|
||||
* 2. to call `unregisterAdaptor();` in the class destructor,
|
||||
* so that the object API vtable is registered and unregistered at the proper time.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
template <typename... Interfaces>
|
||||
class AdaptorInterfaces
|
||||
: protected ObjectHolder
|
||||
, public _Interfaces...
|
||||
, public Interfaces...
|
||||
{
|
||||
public:
|
||||
AdaptorInterfaces(const AdaptorInterfaces&) = delete;
|
||||
AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete;
|
||||
AdaptorInterfaces(AdaptorInterfaces&&) = delete;
|
||||
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = delete;
|
||||
|
||||
/*!
|
||||
* @brief Creates object instance
|
||||
*
|
||||
@@ -101,26 +107,26 @@ namespace sdbus {
|
||||
*
|
||||
* For more information, consult @ref createObject(sdbus::IConnection&,std::string)
|
||||
*/
|
||||
AdaptorInterfaces(IConnection& connection, std::string objectPath)
|
||||
AdaptorInterfaces(IConnection& connection, ObjectPath objectPath)
|
||||
: ObjectHolder(createObject(connection, std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
, Interfaces(getObject())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Finishes adaptor API registration and publishes the adaptor on the bus
|
||||
* @brief Adds object vtable (i.e. D-Bus API) definitions for all interfaces it implements
|
||||
*
|
||||
* This function must be called in the constructor of the final adaptor class that implements AdaptorInterfaces.
|
||||
*
|
||||
* For more information, see underlying @ref IObject::finishRegistration()
|
||||
* See also @ref IObject::addVTable()
|
||||
*/
|
||||
void registerAdaptor()
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
(Interfaces::registerAdaptor(), ...);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Unregisters adaptors's API and removes it from the bus
|
||||
* @brief Unregisters adaptor's API and removes it from the bus
|
||||
*
|
||||
* This function must be called in the destructor of the final adaptor class that implements AdaptorInterfaces.
|
||||
*
|
||||
@@ -132,23 +138,16 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
* @brief Returns reference to the underlying IObject instance
|
||||
*/
|
||||
const std::string& getObjectPath() const
|
||||
{
|
||||
return getObject().getObjectPath();
|
||||
}
|
||||
using ObjectHolder::getObject;
|
||||
|
||||
protected:
|
||||
using base_type = AdaptorInterfaces;
|
||||
|
||||
AdaptorInterfaces(const AdaptorInterfaces&) = delete;
|
||||
AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete;
|
||||
AdaptorInterfaces(AdaptorInterfaces&&) = default;
|
||||
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = default;
|
||||
~AdaptorInterfaces() = default;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_ADAPTORINTERFACES_H_ */
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2026 - Alex Cani <alexcani109@gmail.com>
|
||||
*
|
||||
* @file Awaitable.h
|
||||
*
|
||||
* Created on: Feb 28, 2026
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CXX_AWAITABLE_H_
|
||||
#define SDBUS_CXX_AWAITABLE_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#if __has_include(<coroutine>)
|
||||
#include <coroutine>
|
||||
#endif
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// Forward declarations
|
||||
class AsyncMethodInvoker;
|
||||
namespace internal {
|
||||
class Proxy;
|
||||
} // namespace internal
|
||||
|
||||
/********************************************//**
|
||||
* @enum AwaitableState
|
||||
*
|
||||
* Represents the lifecycle state of an asynchronous
|
||||
* operation in the coroutine awaitable protocol.
|
||||
* Used for atomic coordination between the coroutine
|
||||
* and the D-Bus callback thread.
|
||||
*
|
||||
***********************************************/
|
||||
enum class AwaitableState : uint8_t
|
||||
{
|
||||
NotReady, // Initial state: callback hasn't fired yet
|
||||
Waiting, // Coroutine is suspended and waiting for callback
|
||||
Completed // Callback completed, result is ready
|
||||
};
|
||||
|
||||
// Shared data
|
||||
template <typename T>
|
||||
struct AwaitableData
|
||||
{
|
||||
using result_type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
|
||||
std::variant<result_type, std::exception_ptr> result;
|
||||
std::atomic<AwaitableState> status{AwaitableState::NotReady};
|
||||
#ifdef __cpp_lib_coroutine
|
||||
// Keep the handle as the last member to mainting ABI compatibility
|
||||
// with clients without coroutine support.
|
||||
std::coroutine_handle<> handle;
|
||||
#endif // __cpp_lib_coroutine
|
||||
|
||||
void resumeCoroutine()
|
||||
{
|
||||
#ifdef __cpp_lib_coroutine
|
||||
handle.resume();
|
||||
#endif // __cpp_lib_coroutine
|
||||
}
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Awaitable
|
||||
*
|
||||
* A C++20 coroutine awaitable that represents an asynchronous
|
||||
* operation. Allows suspending a coroutine until a D-Bus method
|
||||
* call completes, then resuming with the result or exception.
|
||||
*
|
||||
* This is not a full-fledged coroutine type, but a simple awaitable
|
||||
* that can be used with `co_await` to retrieve results of async D-Bus calls.
|
||||
* This is independent of any specific coroutine framework or scheduler,
|
||||
* as it relies on the D-Bus callback mechanism to resume the coroutine.
|
||||
*
|
||||
* You most likely don't need to use this class directly. Instead, use the
|
||||
* respective low-level or high-level API functions that return an Awaitable
|
||||
* instance, such as IProxy::callMethodAsync with with_awaitable_t tag,
|
||||
* or the .getResultAsAwaitable() methods of the high-level API.
|
||||
*
|
||||
* The class represents nothing, i.e. is a simple placeholder class, if the API
|
||||
* is used as C++17 or with a standard library not supporting coroutines.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename T>
|
||||
class Awaitable
|
||||
{
|
||||
#ifdef __cpp_lib_coroutine
|
||||
public:
|
||||
// Called when the coroutine is co_await'ed. Returns true if the coroutine should be suspended.
|
||||
[[nodiscard]] bool await_ready() const noexcept
|
||||
{
|
||||
return data_->status.load(std::memory_order_acquire) == AwaitableState::Completed;
|
||||
}
|
||||
|
||||
// Called when the coroutine is suspended, returning false here will immediately
|
||||
// resume the coroutine.
|
||||
bool await_suspend(std::coroutine_handle<> handle) noexcept
|
||||
{
|
||||
data_->handle = handle;
|
||||
|
||||
// Attempt transition from NotReady to Waiting.
|
||||
AwaitableState expected = AwaitableState::NotReady;
|
||||
return data_->status.compare_exchange_strong(expected, AwaitableState::Waiting, std::memory_order_acq_rel);
|
||||
}
|
||||
|
||||
// Called when the coroutine is resumed. Returns the result or throws the exception.
|
||||
[[nodiscard]] T await_resume() const
|
||||
{
|
||||
if (auto* exception = std::get_if<std::exception_ptr>(&data_->result); exception != nullptr)
|
||||
std::rethrow_exception(*exception);
|
||||
|
||||
if constexpr (std::is_void_v<T>)
|
||||
return;
|
||||
else
|
||||
return std::get<T>(std::move(data_->result));
|
||||
}
|
||||
#endif // __cpp_lib_coroutine
|
||||
|
||||
private:
|
||||
friend internal::Proxy;
|
||||
friend AsyncMethodInvoker;
|
||||
|
||||
explicit Awaitable(std::shared_ptr<AwaitableData<T>> data)
|
||||
: data_(std::move(data))
|
||||
{
|
||||
assert(data_ != nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<AwaitableData<T>> data_;
|
||||
};
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif // SDBUS_CXX_AWAITABLE_H_
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file ConvenienceApiClasses.h
|
||||
*
|
||||
@@ -27,16 +27,19 @@
|
||||
#ifndef SDBUS_CXX_CONVENIENCEAPICLASSES_H_
|
||||
#define SDBUS_CXX_CONVENIENCEAPICLASSES_H_
|
||||
|
||||
#include <sdbus-c++/Awaitable.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <sdbus-c++/VTableItems.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@@ -44,119 +47,47 @@ namespace sdbus {
|
||||
class IProxy;
|
||||
class Error;
|
||||
class PendingAsyncCall;
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
class MethodRegistrator
|
||||
class VTableAdder
|
||||
{
|
||||
public:
|
||||
MethodRegistrator(IObject& object, std::string methodName);
|
||||
MethodRegistrator(MethodRegistrator&& other) = default;
|
||||
~MethodRegistrator() noexcept(false);
|
||||
|
||||
MethodRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename _Function> MethodRegistrator& implementedAs(_Function&& callback);
|
||||
MethodRegistrator& withInputParamNames(std::vector<std::string> paramNames);
|
||||
template <typename... _String> MethodRegistrator& withInputParamNames(_String... paramNames);
|
||||
MethodRegistrator& withOutputParamNames(std::vector<std::string> paramNames);
|
||||
template <typename... _String> MethodRegistrator& withOutputParamNames(_String... paramNames);
|
||||
MethodRegistrator& markAsDeprecated();
|
||||
MethodRegistrator& markAsPrivileged();
|
||||
MethodRegistrator& withNoReply();
|
||||
void forInterface(InterfaceName interfaceName);
|
||||
void forInterface(std::string interfaceName);
|
||||
[[nodiscard]] Slot forInterface(InterfaceName interfaceName, return_slot_t);
|
||||
[[nodiscard]] Slot forInterface(std::string interfaceName, return_slot_t);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
std::string methodName_;
|
||||
std::string interfaceName_;
|
||||
std::string inputSignature_;
|
||||
std::vector<std::string> inputParamNames_;
|
||||
std::string outputSignature_;
|
||||
std::vector<std::string> outputParamNames_;
|
||||
method_callback methodCallback_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
|
||||
};
|
||||
friend IObject;
|
||||
VTableAdder(IObject& object, std::vector<VTableItem> vtable);
|
||||
|
||||
class SignalRegistrator
|
||||
{
|
||||
public:
|
||||
SignalRegistrator(IObject& object, std::string signalName);
|
||||
SignalRegistrator(SignalRegistrator&& other) = default;
|
||||
~SignalRegistrator() noexcept(false);
|
||||
|
||||
SignalRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename... _Args> SignalRegistrator& withParameters();
|
||||
template <typename... _Args> SignalRegistrator& withParameters(std::vector<std::string> paramNames);
|
||||
template <typename... _Args, typename... _String> SignalRegistrator& withParameters(_String... paramNames);
|
||||
SignalRegistrator& markAsDeprecated();
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
std::string signalName_;
|
||||
std::string interfaceName_;
|
||||
std::string signalSignature_;
|
||||
std::vector<std::string> paramNames_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
|
||||
};
|
||||
|
||||
class PropertyRegistrator
|
||||
{
|
||||
public:
|
||||
PropertyRegistrator(IObject& object, const std::string& propertyName);
|
||||
PropertyRegistrator(PropertyRegistrator&& other) = default;
|
||||
~PropertyRegistrator() noexcept(false);
|
||||
|
||||
PropertyRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename _Function> PropertyRegistrator& withGetter(_Function&& callback);
|
||||
template <typename _Function> PropertyRegistrator& withSetter(_Function&& callback);
|
||||
PropertyRegistrator& markAsDeprecated();
|
||||
PropertyRegistrator& markAsPrivileged();
|
||||
PropertyRegistrator& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& propertyName_;
|
||||
std::string interfaceName_;
|
||||
std::string propertySignature_;
|
||||
property_get_callback getter_;
|
||||
property_set_callback setter_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when PropertyRegistrator is constructed
|
||||
};
|
||||
|
||||
class InterfaceFlagsSetter
|
||||
{
|
||||
public:
|
||||
InterfaceFlagsSetter(IObject& object, const std::string& interfaceName);
|
||||
InterfaceFlagsSetter(InterfaceFlagsSetter&& other) = default;
|
||||
~InterfaceFlagsSetter() noexcept(false);
|
||||
|
||||
InterfaceFlagsSetter& markAsDeprecated();
|
||||
InterfaceFlagsSetter& markAsPrivileged();
|
||||
InterfaceFlagsSetter& withNoReplyMethods();
|
||||
InterfaceFlagsSetter& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& interfaceName_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when InterfaceFlagsSetter is constructed
|
||||
IObject& object_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
std::vector<VTableItem> vtable_;
|
||||
};
|
||||
|
||||
class SignalEmitter
|
||||
{
|
||||
public:
|
||||
SignalEmitter(IObject& object, const std::string& signalName);
|
||||
SignalEmitter(SignalEmitter&& other) = default;
|
||||
~SignalEmitter() noexcept(false);
|
||||
SignalEmitter& onInterface(const InterfaceName& interfaceName);
|
||||
SignalEmitter& onInterface(const std::string& interfaceName);
|
||||
template <typename... _Args> void withArguments(_Args&&... args);
|
||||
SignalEmitter& onInterface(const char* interfaceName);
|
||||
template <typename... Args> void withArguments(Args&&... args);
|
||||
|
||||
SignalEmitter(const SignalEmitter&) = delete;
|
||||
SignalEmitter& operator=(const SignalEmitter&) = delete;
|
||||
SignalEmitter(SignalEmitter&& other) = default;
|
||||
SignalEmitter& operator=(SignalEmitter&&) = delete;
|
||||
~SignalEmitter() noexcept(false);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& signalName_;
|
||||
friend IObject;
|
||||
SignalEmitter(IObject& object, const SignalName& signalName);
|
||||
SignalEmitter(IObject& object, const char* signalName);
|
||||
|
||||
IObject& object_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
const char* signalName_;
|
||||
Signal signal_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
||||
};
|
||||
@@ -164,22 +95,29 @@ namespace sdbus {
|
||||
class MethodInvoker
|
||||
{
|
||||
public:
|
||||
MethodInvoker(IProxy& proxy, const std::string& methodName);
|
||||
MethodInvoker(MethodInvoker&& other) = default;
|
||||
~MethodInvoker() noexcept(false);
|
||||
|
||||
MethodInvoker& onInterface(const InterfaceName& interfaceName);
|
||||
MethodInvoker& onInterface(const std::string& interfaceName);
|
||||
MethodInvoker& onInterface(const char* interfaceName);
|
||||
MethodInvoker& withTimeout(uint64_t usec);
|
||||
template <typename _Rep, typename _Period>
|
||||
MethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
|
||||
template <typename... _Args> void storeResultsTo(_Args&... args);
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
MethodInvoker& withTimeout(const std::chrono::duration<Rep, Period>& timeout);
|
||||
template <typename... Args> MethodInvoker& withArguments(Args&&... args);
|
||||
template <typename... Args> void storeResultsTo(Args&... args);
|
||||
void dontExpectReply();
|
||||
|
||||
MethodInvoker(const MethodInvoker&) = delete;
|
||||
MethodInvoker& operator=(const MethodInvoker&) = delete;
|
||||
MethodInvoker(MethodInvoker&& other) = default;
|
||||
MethodInvoker& operator=(MethodInvoker&&) = delete;
|
||||
~MethodInvoker() noexcept(false);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& methodName_;
|
||||
friend IProxy;
|
||||
MethodInvoker(IProxy& proxy, const MethodName& methodName);
|
||||
MethodInvoker(IProxy& proxy, const char* methodName);
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
const char* methodName_;
|
||||
uint64_t timeout_{};
|
||||
MethodCall method_;
|
||||
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
||||
@@ -189,21 +127,29 @@ namespace sdbus {
|
||||
class AsyncMethodInvoker
|
||||
{
|
||||
public:
|
||||
AsyncMethodInvoker(IProxy& proxy, const std::string& methodName);
|
||||
AsyncMethodInvoker& onInterface(const InterfaceName& interfaceName);
|
||||
AsyncMethodInvoker& onInterface(const std::string& interfaceName);
|
||||
AsyncMethodInvoker& onInterface(const char* interfaceName);
|
||||
AsyncMethodInvoker& withTimeout(uint64_t usec);
|
||||
template <typename _Rep, typename _Period>
|
||||
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
template <typename Rep, typename Period>
|
||||
AsyncMethodInvoker& withTimeout(const std::chrono::duration<Rep, Period>& timeout);
|
||||
template <typename... Args> AsyncMethodInvoker& withArguments(Args&&... args);
|
||||
template <typename Function> PendingAsyncCall uponReplyInvoke(Function&& callback);
|
||||
template <typename Function> [[nodiscard]] Slot uponReplyInvoke(Function&& callback, return_slot_t);
|
||||
// Returned future will be std::future<void> for no (void) D-Bus method return value
|
||||
// or std::future<T> for single D-Bus method return value
|
||||
// or std::future<std::tuple<...>> for multiple method return values
|
||||
template <typename... _Args> std::future<future_return_t<_Args...>> getResultAsFuture();
|
||||
template <typename... Args> std::future<future_return_t<Args...>> getResultAsFuture();
|
||||
template <typename... Args> Awaitable<awaitable_return_t<Args...>> getResultAsAwaitable();
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& methodName_;
|
||||
friend IProxy;
|
||||
AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName);
|
||||
AsyncMethodInvoker(IProxy& proxy, const char* methodName);
|
||||
template <typename Function> async_reply_handler makeAsyncReplyHandler(Function&& callback);
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
const char* methodName_;
|
||||
uint64_t timeout_{};
|
||||
MethodCall method_;
|
||||
};
|
||||
@@ -211,108 +157,134 @@ namespace sdbus {
|
||||
class SignalSubscriber
|
||||
{
|
||||
public:
|
||||
SignalSubscriber(IProxy& proxy, const std::string& signalName);
|
||||
SignalSubscriber& onInterface(std::string interfaceName);
|
||||
template <typename _Function> void call(_Function&& callback);
|
||||
SignalSubscriber& onInterface(const InterfaceName& interfaceName);
|
||||
SignalSubscriber& onInterface(const std::string& interfaceName);
|
||||
SignalSubscriber& onInterface(const char* interfaceName);
|
||||
template <typename Function> void call(Function&& callback);
|
||||
template <typename Function> [[nodiscard]] Slot call(Function&& callback, return_slot_t);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& signalName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
friend IProxy;
|
||||
SignalSubscriber(IProxy& proxy, const SignalName& signalName);
|
||||
SignalSubscriber(IProxy& proxy, const char* signalName);
|
||||
template <typename Function> signal_handler makeSignalHandler(Function&& callback);
|
||||
|
||||
class SignalUnsubscriber
|
||||
{
|
||||
public:
|
||||
SignalUnsubscriber(IProxy& proxy, const std::string& signalName);
|
||||
void onInterface(const std::string& interfaceName);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& signalName_;
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
const char* signalName_;
|
||||
const char* interfaceName_{};
|
||||
};
|
||||
|
||||
class PropertyGetter
|
||||
{
|
||||
public:
|
||||
PropertyGetter(IProxy& proxy, const std::string& propertyName);
|
||||
Variant onInterface(const std::string& interfaceName);
|
||||
Variant onInterface(std::string_view interfaceName);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& propertyName_;
|
||||
friend IProxy;
|
||||
PropertyGetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
std::string_view propertyName_;
|
||||
};
|
||||
|
||||
class AsyncPropertyGetter
|
||||
{
|
||||
public:
|
||||
AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName);
|
||||
AsyncPropertyGetter& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
AsyncPropertyGetter& onInterface(std::string_view interfaceName);
|
||||
template <typename Function> PendingAsyncCall uponReplyInvoke(Function&& callback);
|
||||
template <typename Function> [[nodiscard]] Slot uponReplyInvoke(Function&& callback, return_slot_t);
|
||||
std::future<Variant> getResultAsFuture();
|
||||
Awaitable<Variant> getResultAsAwaitable();
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& propertyName_;
|
||||
const std::string* interfaceName_{};
|
||||
friend IProxy;
|
||||
AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
std::string_view propertyName_;
|
||||
std::string_view interfaceName_;
|
||||
};
|
||||
|
||||
class PropertySetter
|
||||
{
|
||||
public:
|
||||
PropertySetter(IProxy& proxy, const std::string& propertyName);
|
||||
PropertySetter& onInterface(const std::string& interfaceName);
|
||||
template <typename _Value> void toValue(const _Value& value);
|
||||
template <typename _Value> void toValue(const _Value& value, dont_expect_reply_t);
|
||||
PropertySetter& onInterface(std::string_view interfaceName);
|
||||
template <typename Value> void toValue(const Value& value);
|
||||
template <typename Value> void toValue(const Value& value, dont_expect_reply_t);
|
||||
void toValue(const Variant& value);
|
||||
void toValue(const Variant& value, dont_expect_reply_t);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& propertyName_;
|
||||
const std::string* interfaceName_{};
|
||||
friend IProxy;
|
||||
PropertySetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
std::string_view propertyName_;
|
||||
std::string_view interfaceName_;
|
||||
};
|
||||
|
||||
class AsyncPropertySetter
|
||||
{
|
||||
public:
|
||||
AsyncPropertySetter(IProxy& proxy, const std::string& propertyName);
|
||||
AsyncPropertySetter& onInterface(const std::string& interfaceName);
|
||||
template <typename _Value> AsyncPropertySetter& toValue(_Value&& value);
|
||||
AsyncPropertySetter& onInterface(std::string_view interfaceName);
|
||||
template <typename Value> AsyncPropertySetter& toValue(Value&& value);
|
||||
AsyncPropertySetter& toValue(Variant value);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
template <typename Function> PendingAsyncCall uponReplyInvoke(Function&& callback);
|
||||
template <typename Function> [[nodiscard]] Slot uponReplyInvoke(Function&& callback, return_slot_t);
|
||||
std::future<void> getResultAsFuture();
|
||||
Awaitable<void> getResultAsAwaitable();
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& propertyName_;
|
||||
const std::string* interfaceName_{};
|
||||
friend IProxy;
|
||||
AsyncPropertySetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
std::string_view propertyName_;
|
||||
std::string_view interfaceName_;
|
||||
Variant value_;
|
||||
};
|
||||
|
||||
class AllPropertiesGetter
|
||||
{
|
||||
public:
|
||||
AllPropertiesGetter(IProxy& proxy);
|
||||
std::map<std::string, Variant> onInterface(const std::string& interfaceName);
|
||||
std::map<PropertyName, Variant> onInterface(std::string_view interfaceName);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
friend IProxy;
|
||||
explicit AllPropertiesGetter(IProxy& proxy);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
};
|
||||
|
||||
class AsyncAllPropertiesGetter
|
||||
{
|
||||
public:
|
||||
AsyncAllPropertiesGetter(IProxy& proxy);
|
||||
AsyncAllPropertiesGetter& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
std::future<std::map<std::string, Variant>> getResultAsFuture();
|
||||
AsyncAllPropertiesGetter& onInterface(std::string_view interfaceName);
|
||||
template <typename Function> PendingAsyncCall uponReplyInvoke(Function&& callback);
|
||||
template <typename Function> [[nodiscard]] Slot uponReplyInvoke(Function&& callback, return_slot_t);
|
||||
std::future<std::map<PropertyName, Variant>> getResultAsFuture();
|
||||
Awaitable<std::map<PropertyName, Variant>> getResultAsAwaitable();
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string* interfaceName_{};
|
||||
friend IProxy;
|
||||
explicit AsyncAllPropertiesGetter(IProxy& proxy);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
std::string_view interfaceName_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+32
-16
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Error.h
|
||||
*
|
||||
@@ -27,7 +27,7 @@
|
||||
#ifndef SDBUS_CXX_ERROR_H_
|
||||
#define SDBUS_CXX_ERROR_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <cerrno>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
@@ -43,47 +43,63 @@ namespace sdbus {
|
||||
: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit Error(const std::string& name, const char* message = nullptr)
|
||||
: Error(name, std::string(message ? message : ""))
|
||||
// Strong type representing the D-Bus error name
|
||||
class Name : public std::string
|
||||
{
|
||||
public:
|
||||
Name() = default;
|
||||
explicit Name(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit Name(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
explicit Error(Name name, const char* message = nullptr)
|
||||
: Error(std::move(name), std::string(message ? message : ""))
|
||||
{
|
||||
}
|
||||
|
||||
Error(const std::string& name, const std::string& message)
|
||||
: std::runtime_error("[" + name + "] " + message)
|
||||
, name_(name)
|
||||
, message_(message)
|
||||
Error(Name name, std::string message)
|
||||
: std::runtime_error(!message.empty() ? "[" + name + "] " + message : "[" + name + "]")
|
||||
, name_(std::move(name))
|
||||
, message_(std::move(message))
|
||||
{
|
||||
}
|
||||
|
||||
const std::string& getName() const
|
||||
[[nodiscard]] const Name& getName() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
const std::string& getMessage() const
|
||||
[[nodiscard]] const std::string& getMessage() const
|
||||
{
|
||||
return message_;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
[[nodiscard]] bool isValid() const
|
||||
{
|
||||
return !getName().empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
Name name_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
sdbus::Error createError(int errNo, const std::string& customMsg);
|
||||
Error createError(int errNo, std::string customMsg = {});
|
||||
|
||||
inline const char* SDBUSCPP_ERROR_NAME = "org.sdbuscpp.Error";
|
||||
}
|
||||
inline const Error::Name SDBUSCPP_ERROR_NAME{"org.sdbuscpp.Error"};
|
||||
} // namespace sdbus
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
|
||||
throw sdbus::createError((_ERRNO), (_MSG)) \
|
||||
/**/
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \
|
||||
if (!(_COND)) ; else SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \
|
||||
/**/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Flags.h
|
||||
*
|
||||
@@ -74,26 +74,26 @@ namespace sdbus {
|
||||
flags_.set(flag, value);
|
||||
}
|
||||
|
||||
bool test(GeneralFlags flag) const
|
||||
[[nodiscard]] bool test(GeneralFlags flag) const
|
||||
{
|
||||
return flags_.test(flag);
|
||||
}
|
||||
|
||||
bool test(PropertyUpdateBehaviorFlags flag) const
|
||||
[[nodiscard]] bool test(PropertyUpdateBehaviorFlags flag) const
|
||||
{
|
||||
return flags_.test(flag);
|
||||
}
|
||||
|
||||
uint64_t toSdBusInterfaceFlags() const;
|
||||
uint64_t toSdBusMethodFlags() const;
|
||||
uint64_t toSdBusSignalFlags() const;
|
||||
uint64_t toSdBusPropertyFlags() const;
|
||||
uint64_t toSdBusWritablePropertyFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusInterfaceFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusMethodFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusSignalFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusPropertyFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusWritablePropertyFlags() const;
|
||||
|
||||
private:
|
||||
std::bitset<FLAG_COUNT> flags_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_FLAGS_H_ */
|
||||
|
||||
+234
-222
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file IConnection.h
|
||||
*
|
||||
@@ -28,13 +28,22 @@
|
||||
#define SDBUS_CXX_ICONNECTION_H_
|
||||
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
// Forward declarations
|
||||
struct sd_bus;
|
||||
struct sd_event;
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class ObjectPath;
|
||||
class BusName;
|
||||
using ServiceName = BusName;
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@@ -51,90 +60,10 @@ namespace sdbus {
|
||||
class IConnection
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Poll Data for external event loop implementations.
|
||||
*
|
||||
* To integrate sdbus with your app's own custom event handling system
|
||||
* you can use this method to query which file descriptors, poll events
|
||||
* and timeouts you should add to your app's poll(2), or select(2)
|
||||
* call in your main event loop.
|
||||
*
|
||||
* If you are unsure what this all means then use
|
||||
* enterEventLoop() or enterEventLoopAsync() instead.
|
||||
*
|
||||
* See: getEventLoopPollData()
|
||||
*/
|
||||
struct PollData
|
||||
{
|
||||
/*!
|
||||
* The read fd to be monitored by the event loop.
|
||||
*/
|
||||
int fd;
|
||||
/*!
|
||||
* The events to use for poll(2) alongside fd.
|
||||
*/
|
||||
short int events;
|
||||
|
||||
/*!
|
||||
* Absolute timeout value in micro seconds and based of CLOCK_MONOTONIC.
|
||||
*/
|
||||
uint64_t timeout_usec;
|
||||
|
||||
/*!
|
||||
* Get the event poll timeout.
|
||||
*
|
||||
* The timeout is an absolute value based of CLOCK_MONOTONIC.
|
||||
*
|
||||
* @return a duration since the CLOCK_MONOTONIC epoch started.
|
||||
*/
|
||||
[[nodiscard]] std::chrono::microseconds getAbsoluteTimeout() const
|
||||
{
|
||||
return std::chrono::microseconds(timeout_usec);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the timeout as relative value from now
|
||||
*
|
||||
* @return std::nullopt if the timeout is indefinite. A duration otherwise.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::chrono::microseconds> getRelativeTimeout() const;
|
||||
|
||||
/*!
|
||||
* Get a converted, relative timeout which can be passed as argument 'timeout' to poll(2)
|
||||
*
|
||||
* @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block. An integer in milli
|
||||
* seconds otherwise.
|
||||
*/
|
||||
[[nodiscard]] int getPollTimeout() const;
|
||||
};
|
||||
struct PollData;
|
||||
|
||||
virtual ~IConnection() = default;
|
||||
|
||||
/*!
|
||||
* @brief Requests D-Bus name on the connection
|
||||
*
|
||||
* @param[in] name Name to request
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void requestName(const std::string& name) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Releases D-Bus name on the connection
|
||||
*
|
||||
* @param[in] name Name to release
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void releaseName(const std::string& name) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Retrieve the unique name of a connection. E.g. ":1.xx"
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual std::string getUniqueName() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Enters I/O event loop on this bus connection
|
||||
*
|
||||
@@ -163,61 +92,103 @@ namespace sdbus {
|
||||
virtual void leaveEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager at the specified D-Bus object path
|
||||
* @brief Attaches the bus connection to a sd-event event loop
|
||||
*
|
||||
* Creates an ObjectManager interface at the specified object path on
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
* to see what objects it has.
|
||||
*
|
||||
* This call creates a floating registration. The ObjectManager will
|
||||
* be there for the object path until the connection is destroyed.
|
||||
*
|
||||
* Another, recommended way to add object managers is directly through
|
||||
* IObject API.
|
||||
* @param[in] event sd-event event loop object
|
||||
* @param[in] priority Specified priority
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*
|
||||
* See `man sd_bus_attach_event'.
|
||||
*/
|
||||
[[deprecated("Use one of other addObjectManager overloads")]] virtual void addObjectManager(const std::string& objectPath) = 0;
|
||||
virtual void attachSdEventLoop(sd_event *event, int priority = 0) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns fd, I/O events and timeout data you can pass to poll
|
||||
*
|
||||
* To integrate sdbus with your app's own custom event handling system
|
||||
* (without the requirement of an extra thread), you can use this
|
||||
* method to query which file descriptors, poll events and timeouts you
|
||||
* should add to your app's poll call in your main event loop. If these
|
||||
* file descriptors signal, then you should call processPendingRequest
|
||||
* to process the event. This means that all of sdbus's callbacks will
|
||||
* arrive on your app's main event thread (opposed to on a thread created
|
||||
* by sdbus-c++). If you are unsure what this all means then use
|
||||
* enterEventLoop() or enterEventLoopAsync() instead.
|
||||
*
|
||||
* To integrate sdbus-c++ into a gtk app, pass the file descriptor returned
|
||||
* by this method to g_main_context_add_poll.
|
||||
* @brief Detaches the bus connection from a sd-event event loop
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual PollData getEventLoopPollData() const = 0;
|
||||
virtual void detachSdEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Process a pending request
|
||||
* @brief Gets current sd-event event loop for the bus connection
|
||||
*
|
||||
* @returns true if an event was processed, false if poll should be called
|
||||
* @return Pointer to event loop object if attached, nullptr otherwise
|
||||
*/
|
||||
virtual sd_event *getSdEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns fd's, I/O events and timeout data to be used in an external event loop
|
||||
*
|
||||
* Processes a single dbus event. All of sdbus-c++'s callbacks will be called
|
||||
* from within this method. This method should ONLY be used in conjuction
|
||||
* with getEventLoopPollData().
|
||||
* This method returns true if an I/O message was processed. This you can try
|
||||
* to call this method again before going to poll on I/O events. The method
|
||||
* returns false if no operations were pending, and the caller should then
|
||||
* poll for I/O events before calling this method again.
|
||||
* enterEventLoop() and enterEventLoopAsync() will call this method for you,
|
||||
* so there is no need to call it when using these. If you are unsure what
|
||||
* this all means then don't use this method.
|
||||
* This function is useful to hook up a bus connection object with an
|
||||
* external (like GMainLoop, boost::asio, etc.) or manual event loop
|
||||
* involving poll() or a similar I/O polling call.
|
||||
*
|
||||
* Before **each** invocation of the I/O polling call, this function
|
||||
* should be invoked. Returned PollData::fd file descriptor should
|
||||
* be polled for the events indicated by PollData::events, and the I/O
|
||||
* call should block for that up to the returned PollData::timeout.
|
||||
*
|
||||
* Additionally, returned PollData::eventFd should be polled for POLLIN
|
||||
* events.
|
||||
*
|
||||
* After each I/O polling call the bus connection needs to process
|
||||
* incoming or outgoing data, by invoking processPendingEvent().
|
||||
*
|
||||
* Note that the returned timeout should be considered only a maximum
|
||||
* sleeping time. It is permissible (and even expected) that shorter
|
||||
* timeouts are used by the calling program, in case other event sources
|
||||
* are polled in the same event loop. Note that the returned time-value
|
||||
* is absolute, based of CLOCK_MONOTONIC and specified in microseconds.
|
||||
* Use PollData::getPollTimeout() to have the timeout value converted
|
||||
* in a form that can be passed to poll(2).
|
||||
*
|
||||
* The bus connection conveniently integrates sd-event event loop.
|
||||
* To attach the bus connection to a sd-event event loop, use
|
||||
* attachSdEventLoop() function.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual bool processPendingRequest() = 0;
|
||||
[[nodiscard]] virtual PollData getEventLoopPollData() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Processes a pending event
|
||||
*
|
||||
* @returns True if an event was processed, false if no operations were pending
|
||||
*
|
||||
* This function drives the D-Bus connection. It processes pending I/O events.
|
||||
* Queued outgoing messages (or parts thereof) are sent out. Queued incoming
|
||||
* messages are dispatched to registered callbacks. Timeouts are recalculated.
|
||||
*
|
||||
* It returns false when no operations were pending and true if a message was
|
||||
* processed. When false is returned the caller should synchronously poll for
|
||||
* I/O events before calling into processPendingEvent() again.
|
||||
* Don't forget to call getEventLoopPollData() each time before the next poll.
|
||||
*
|
||||
* You don't need to directly call this method or getEventLoopPollData() method
|
||||
* when using convenient, internal bus connection event loops through
|
||||
* enterEventLoop() or enterEventLoopAsync() calls, or when the bus is
|
||||
* connected to a sd-event event loop through attachSdEventLoop().
|
||||
* It is invoked automatically when necessary.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual bool processPendingEvent() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides access to the currently processed D-Bus message
|
||||
*
|
||||
* This method provides access to the currently processed incoming D-Bus message.
|
||||
* "Currently processed" means that the registered callback handler(s) for that message
|
||||
* are being invoked. This method is meant to be called from within a callback handler
|
||||
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
|
||||
* guaranteed to return a valid D-Bus message instance for which the handler is called.
|
||||
* If called from other contexts/threads, it may return a valid or invalid message, depending
|
||||
* on whether a message was processed or not at the time of the call.
|
||||
*
|
||||
* @return Currently processed D-Bus message
|
||||
*/
|
||||
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Sets general method call timeout
|
||||
@@ -236,8 +207,8 @@ namespace sdbus {
|
||||
/*!
|
||||
* @copydoc IConnection::setMethodCallTimeout(uint64_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
void setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
template <typename Rep, typename Period>
|
||||
void setMethodCallTimeout(const std::chrono::duration<Rep, Period>& timeout);
|
||||
|
||||
/*!
|
||||
* @brief Gets general method call timeout
|
||||
@@ -248,10 +219,11 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual uint64_t getMethodCallTimeout() const = 0;
|
||||
[[nodiscard]] virtual uint64_t getMethodCallTimeout() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager at the specified D-Bus object path
|
||||
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
|
||||
*
|
||||
* Creates an ObjectManager interface at the specified object path on
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
@@ -260,12 +232,52 @@ namespace sdbus {
|
||||
* This call creates a floating registration. The ObjectManager will
|
||||
* be there for the object path until the connection is destroyed.
|
||||
*
|
||||
* Another, recommended way to add object managers is directly through
|
||||
* IObject API.
|
||||
* Another, recommended way to add object managers is directly through IObject API.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addObjectManager(const std::string& objectPath, floating_slot_t) = 0;
|
||||
virtual void addObjectManager(const ObjectPath& objectPath) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager at the specified D-Bus object path
|
||||
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
|
||||
* @return Slot handle owning the registration
|
||||
*
|
||||
* Creates an ObjectManager interface at the specified object path on
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
* to see what objects it has.
|
||||
*
|
||||
* This call returns an owning slot. The lifetime of the ObjectManager
|
||||
* interface is bound to the lifetime of the returned slot instance.
|
||||
*
|
||||
* Another, recommended way to add object managers is directly through IObject API.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Installs a floating match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
*
|
||||
* The method installs a match rule for messages received on the specified bus connection.
|
||||
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
|
||||
* The specified handler function callback is called for each incoming message matching the specified
|
||||
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
|
||||
* sends a control message requesting the match to be added to the broker and waits until the broker
|
||||
* confirms the match has been installed successfully.
|
||||
*
|
||||
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||
* is bound to the lifetime of the bus connection.
|
||||
*
|
||||
* For more information, consult `man sd_bus_add_match`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addMatch(const std::string& match, message_handler callback) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Installs a match rule for messages received on this bus connection
|
||||
@@ -278,34 +290,40 @@ namespace sdbus {
|
||||
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
|
||||
* The specified handler function callback is called for each incoming message matching the specified
|
||||
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
|
||||
* sends a control message requested the match to be added to the broker and waits until the broker
|
||||
* sends a control message requesting the match to be added to the broker and waits until the broker
|
||||
* confirms the match has been installed successfully.
|
||||
*
|
||||
* Simply let go of the slot instance to uninstall the match rule from the bus connection. The slot
|
||||
* must not outlive the connection for the slot is associated with it.
|
||||
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
|
||||
* the slot instance implies uninstalling of the match rule from the bus connection.
|
||||
*
|
||||
* For more information, consult `man sd_bus_add_match`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback) = 0;
|
||||
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback, return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Installs a floating match rule for messages received on this bus connection
|
||||
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
*
|
||||
* This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously,
|
||||
* in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response.
|
||||
* The `installCallback' callable is called when the response is later received, with the response message
|
||||
* from the broker as parameter. If it's an empty function object, a default implementation is used that
|
||||
* terminates the bus connection should installing the match fail.
|
||||
*
|
||||
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||
* is bound to the lifetime of the bus connection.
|
||||
*
|
||||
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation for more
|
||||
* information.
|
||||
* For more information, consult `man sd_bus_add_match_async`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addMatch(const std::string& match, message_handler callback, floating_slot_t) = 0;
|
||||
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Asynchronously installs a match rule for messages received on this bus connection
|
||||
@@ -321,106 +339,100 @@ namespace sdbus {
|
||||
* from the broker as parameter. If it's an empty function object, a default implementation is used that
|
||||
* terminates the bus connection should installing the match fail.
|
||||
*
|
||||
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation, and consult
|
||||
* `man sd_bus_add_match`, for more information.
|
||||
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
|
||||
* the slot instance implies the uninstalling of the match rule from the bus connection.
|
||||
*
|
||||
* For more information, consult `man sd_bus_add_match_async`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
|
||||
[[nodiscard]] virtual Slot addMatchAsync( const std::string& match
|
||||
, message_handler callback
|
||||
, message_handler installCallback
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
*
|
||||
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||
* is bound to the lifetime of the bus connection.
|
||||
*
|
||||
* Refer to the @c addMatch(const std::string& match, message_handler callback, message_handler installCallback)
|
||||
* documentation for more information.
|
||||
* @brief Retrieves the unique name of a connection. E.g. ":1.xx"
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) = 0;
|
||||
[[nodiscard]] virtual BusName getUniqueName() const = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::enterEventLoop()
|
||||
* @brief Requests a well-known D-Bus service name on a bus
|
||||
*
|
||||
* @deprecated This function has been replaced by enterEventLoop()
|
||||
* @param[in] name Name to request
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop();
|
||||
virtual void requestName(const ServiceName& name) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::enterEventLoopAsync()
|
||||
* @brief Releases an acquired well-known D-Bus service name on a bus
|
||||
*
|
||||
* @deprecated This function has been replaced by enterEventLoopAsync()
|
||||
* @param[in] name Name to release
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync();
|
||||
virtual void releaseName(const ServiceName& name) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::leaveEventLoop()
|
||||
* @struct PollData
|
||||
*
|
||||
* @deprecated This function has been replaced by leaveEventLoop()
|
||||
* Carries poll data needed for integration with external event loop implementations.
|
||||
*
|
||||
* See getEventLoopPollData() for more info.
|
||||
*/
|
||||
[[deprecated("This function has been replaced by leaveEventLoop()")]] void leaveProcessingLoop();
|
||||
struct PollData
|
||||
{
|
||||
/*!
|
||||
* The read fd to be monitored by the event loop.
|
||||
*/
|
||||
int fd;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::getEventLoopPollData()
|
||||
*
|
||||
* @deprecated This function has been replaced by getEventLoopPollData()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by getEventLoopPollData()")]] PollData getProcessLoopPollData() const;
|
||||
/*!
|
||||
* The events to use for poll(2) alongside fd.
|
||||
*/
|
||||
short int events;
|
||||
|
||||
/*!
|
||||
* Absolute timeout value in microseconds, based of CLOCK_MONOTONIC.
|
||||
*
|
||||
* Call getPollTimeout() to get timeout recalculated to relative timeout that can be passed to poll(2).
|
||||
*/
|
||||
std::chrono::microseconds timeout;
|
||||
|
||||
/*!
|
||||
* An additional event fd to be monitored by the event loop for POLLIN events.
|
||||
*/
|
||||
int eventFd;
|
||||
|
||||
/*!
|
||||
* Returns the timeout as relative value from now.
|
||||
*
|
||||
* Returned value is std::chrono::microseconds::max() if the timeout is indefinite.
|
||||
*
|
||||
* @return Relative timeout as a time duration
|
||||
*/
|
||||
[[nodiscard]] std::chrono::microseconds getRelativeTimeout() const;
|
||||
|
||||
/*!
|
||||
* Returns relative timeout in the form which can be passed as argument 'timeout' to poll(2)
|
||||
*
|
||||
* @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block.
|
||||
* An integer in milliseconds otherwise.
|
||||
*/
|
||||
[[nodiscard]] int getPollTimeout() const;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline void IConnection::setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
|
||||
template <typename Rep, typename Period>
|
||||
inline void IConnection::setMethodCallTimeout(const std::chrono::duration<Rep, Period>& timeout)
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
return setMethodCallTimeout(microsecs.count());
|
||||
}
|
||||
|
||||
inline void IConnection::enterProcessingLoop()
|
||||
{
|
||||
enterEventLoop();
|
||||
}
|
||||
|
||||
inline void IConnection::enterProcessingLoopAsync()
|
||||
{
|
||||
enterEventLoopAsync();
|
||||
}
|
||||
|
||||
inline void IConnection::leaveProcessingLoop()
|
||||
{
|
||||
leaveEventLoop();
|
||||
}
|
||||
|
||||
inline IConnection::PollData IConnection::getProcessLoopPollData() const
|
||||
{
|
||||
return getEventLoopPollData();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system bus connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system bus connection with a name
|
||||
*
|
||||
* @param[in] name Name to request on the connection after its opening
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session bus connection when in a user context, and a system bus connection, otherwise.
|
||||
*
|
||||
@@ -428,7 +440,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection();
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session bus connection with a name when in a user context, and a system bus connection with a name, otherwise.
|
||||
@@ -438,7 +450,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection(const std::string& name);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createBusConnection(const ServiceName& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system bus connection
|
||||
@@ -447,7 +459,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createSystemBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system bus connection with a name
|
||||
@@ -457,7 +469,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createSystemBusConnection(const ServiceName& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session bus connection
|
||||
@@ -466,7 +478,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createSessionBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session bus connection with a name
|
||||
@@ -476,7 +488,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createSessionBusConnection(const ServiceName& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session bus connection at a custom address
|
||||
@@ -488,7 +500,7 @@ namespace sdbus {
|
||||
*
|
||||
* Consult manual pages for `sd_bus_set_address` of the underlying sd-bus library for more information.
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnectionWithAddress(const std::string& address);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createSessionBusConnectionWithAddress(const std::string& address);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection on a remote host using ssh
|
||||
@@ -498,7 +510,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createRemoteSystemBusConnection(const std::string& host);
|
||||
|
||||
/*!
|
||||
* @brief Opens direct D-Bus connection at a custom address
|
||||
@@ -508,7 +520,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDirectBusConnection(const std::string& address);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createDirectBusConnection(const std::string& address);
|
||||
|
||||
/*!
|
||||
* @brief Opens direct D-Bus connection at the given file descriptor
|
||||
@@ -521,7 +533,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDirectBusConnection(int fd);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createDirectBusConnection(int fd);
|
||||
|
||||
/*!
|
||||
* @brief Opens direct D-Bus connection at fd as a server
|
||||
@@ -537,7 +549,7 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createServerBus(int fd);
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createServerBus(int fd);
|
||||
|
||||
/*!
|
||||
* @brief Creates sdbus-c++ bus connection representation out of underlying sd_bus instance
|
||||
@@ -566,7 +578,7 @@ namespace sdbus {
|
||||
* auto con = sdbus::createBusConnection(bus); // IConnection consumes sd_bus object
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createBusConnection(sd_bus *bus);
|
||||
}
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createBusConnection(sd_bus *bus);
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_ICONNECTION_H_ */
|
||||
|
||||
+277
-305
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file IObject.h
|
||||
*
|
||||
@@ -28,18 +28,21 @@
|
||||
#define SDBUS_CXX_IOBJECT_H_
|
||||
|
||||
#include <sdbus-c++/ConvenienceApiClasses.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/VTableItems.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Signal;
|
||||
class IConnection;
|
||||
}
|
||||
class ObjectPath;
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@@ -58,186 +61,108 @@ namespace sdbus {
|
||||
***********************************************/
|
||||
class IObject
|
||||
{
|
||||
public:
|
||||
public: // High-level, convenience API
|
||||
virtual ~IObject() = default;
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] methodCallback Callback that implements the body of the method
|
||||
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
|
||||
* @param[in] vtable Individual instances of VTable item structures stored in a vector
|
||||
* @return VTableAdder high-level helper class
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* Parameter `vtable' represents a vtable definition that may contain method declarations
|
||||
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
|
||||
* struct), signal declarations (using SignalVTableItem struct), or global interface
|
||||
* flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime.
|
||||
*
|
||||
* When called like `addVTable(vtable).forInterface(interface)`, then an internal registration
|
||||
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
|
||||
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
|
||||
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
|
||||
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
|
||||
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
|
||||
* can be added or removed by the user at runtime.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, std::string outputSignature
|
||||
, method_callback methodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
[[nodiscard]] VTableAdder addVTable(std::vector<VTableItem> vtable);
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] inputNames Names of input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] outputNames Names of output parameters
|
||||
* @param[in] methodCallback Callback that implements the body of the method
|
||||
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
|
||||
* @param[in] items Individual instances of VTable item structures
|
||||
* @return VTableAdder high-level helper class
|
||||
*
|
||||
* Provided names of input and output parameters will be included in the introspection
|
||||
* description (given that at least version 242 of underlying libsystemd library is
|
||||
* used; otherwise, names of parameters are ignored). This usually helps better describe
|
||||
* the API to the introspector.
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* Parameter pack contains vtable definition that may contain method declarations
|
||||
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
|
||||
* struct), signal declarations (using SignalVTableItem struct), or global interface
|
||||
* flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime.
|
||||
*
|
||||
* When called like `addVTable(items...).forInterface(interface)`, then an internal registration
|
||||
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
|
||||
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
|
||||
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
|
||||
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
|
||||
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
|
||||
* can be added or removed by the user at runtime.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, const std::vector<std::string>& inputNames
|
||||
, std::string outputSignature
|
||||
, const std::vector<std::string>& outputNames
|
||||
, method_callback methodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
template < typename... VTableItems
|
||||
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> > // NOLINT(modernize-use-constraints): We are C++17 compatible at the moment
|
||||
[[nodiscard]] VTableAdder addVTable(VTableItems&&... items);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will emit on D-Bus
|
||||
* @brief Emits signal on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal will fall under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signature D-Bus signature of signal parameters
|
||||
* @param[in] flags D-Bus signal flags (deprecated)
|
||||
* @return A helper object for convenient emission of signals
|
||||
*
|
||||
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
|
||||
* from the D-Bus message concept. Signal arguments are automatically serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int arg1 = ...;
|
||||
* double arg2 = ...;
|
||||
* SignalName fooSignal{"fooSignal"};
|
||||
* object_.emitSignal(fooSignal).onInterface("com.kistler.foo").withArguments(arg1, arg2);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, Flags flags = {} ) = 0;
|
||||
[[nodiscard]] SignalEmitter emitSignal(const SignalName& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will emit on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal will fall under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signature D-Bus signature of signal parameters
|
||||
* @param[in] paramNames Names of parameters of the signal
|
||||
* @param[in] flags D-Bus signal flags (deprecated)
|
||||
*
|
||||
* Provided names of signal output parameters will be included in the introspection
|
||||
* description (given that at least version 242 of underlying libsystemd library is
|
||||
* used; otherwise, names of parameters are ignored). This usually helps better describe
|
||||
* the API to the introspector.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
* @copydoc IObject::emitSignal(const SignalName&)
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, const std::vector<std::string>& paramNames
|
||||
, Flags flags = {} ) = 0;
|
||||
[[nodiscard]] SignalEmitter emitSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Registers read-only property that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the property will fall under
|
||||
* @param[in] propertyName Name of the property
|
||||
* @param[in] signature D-Bus signature of property parameters
|
||||
* @param[in] getCallback Callback that implements the body of the property getter
|
||||
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
* @copydoc IObject::emitSignal(const SignalName&)
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers read/write property that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the property will fall under
|
||||
* @param[in] propertyName Name of the property
|
||||
* @param[in] signature D-Bus signature of property parameters
|
||||
* @param[in] getCallback Callback that implements the body of the property getter
|
||||
* @param[in] setCallback Callback that implements the body of the property setter
|
||||
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Sets flags for a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface whose flags will be set
|
||||
* @param[in] flags Flags to be set
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void setInterfaceFlags(const std::string& interfaceName, Flags flags) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Finishes object API registration and publishes the object on the bus
|
||||
*
|
||||
* The method exports all up to now registered methods, signals and properties on D-Bus.
|
||||
* Must be called after all methods, signals and properties have been registered.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void finishRegistration() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Unregisters object's API and removes object from the bus
|
||||
*
|
||||
* This method unregisters the object, its interfaces, methods, signals and properties
|
||||
* from the bus. Unregistration is done automatically also in object's destructor. This
|
||||
* method makes sense if, in the process of object removal, we need to make sure that
|
||||
* callbacks are unregistered explicitly before the final destruction of the object instance.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void unregister() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Creates a signal message
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A signal message
|
||||
*
|
||||
* Serialize signal arguments into the returned message and emit the signal by passing
|
||||
* the message with serialized arguments to the @c emitSignal function.
|
||||
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual Signal createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits signal for this object path
|
||||
*
|
||||
* @param[in] message Signal message to be sent out
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitSignal(const sdbus::Signal& message) = 0;
|
||||
[[nodiscard]] SignalEmitter emitSignal(const char* signalName);
|
||||
|
||||
/*!
|
||||
* @brief Emits PropertyChanged signal for specified properties under a given interface of this object path
|
||||
@@ -247,7 +172,12 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) = 0;
|
||||
virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&,const std::vector<PropertyName>&)
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits PropertyChanged signal for all properties on a given interface of this object path
|
||||
@@ -256,7 +186,12 @@ namespace sdbus {
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const std::string& interfaceName) = 0;
|
||||
virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&)
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const char* interfaceName) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal on this object path
|
||||
@@ -276,14 +211,12 @@ namespace sdbus {
|
||||
* @brief Emits InterfacesAdded signal on this object path
|
||||
*
|
||||
* This emits an InterfacesAdded signal on this object path with explicitly provided list
|
||||
* of registered interfaces. As sdbus-c++ does currently not supported adding/removing
|
||||
* interfaces of an existing object at run time (an object has a fixed set of interfaces
|
||||
* registered by the time of invoking finishRegistration()), emitInterfacesAddedSignal(void)
|
||||
* is probably what you are looking for.
|
||||
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
|
||||
* object interfaces and their vtables, so this method now makes more sense.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) = 0;
|
||||
virtual void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesRemoved signal on this object path
|
||||
@@ -300,14 +233,12 @@ namespace sdbus {
|
||||
* @brief Emits InterfacesRemoved signal on this object path
|
||||
*
|
||||
* This emits an InterfacesRemoved signal on the given path with explicitly provided list
|
||||
* of registered interfaces. As sdbus-c++ does currently not supported adding/removing
|
||||
* interfaces of an existing object at run time (an object has a fixed set of interfaces
|
||||
* registered by the time of invoking finishRegistration()), emitInterfacesRemovedSignal(void)
|
||||
* is probably what you are looking for.
|
||||
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
|
||||
* object interfaces and their vtables, so this method now makes more sense.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) = 0;
|
||||
virtual void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager interface at the path of this D-Bus object
|
||||
@@ -316,173 +247,213 @@ namespace sdbus {
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
* to see what objects it has.
|
||||
*
|
||||
* This call creates a so-called floating registration. This means that
|
||||
* the ObjectManager interface stays there for the lifetime of the object.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addObjectManager() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Removes an ObjectManager interface from the path of this D-Bus object
|
||||
* @brief Adds an ObjectManager interface at the path of this D-Bus object
|
||||
*
|
||||
* @return Slot handle owning the registration
|
||||
*
|
||||
* Creates an ObjectManager interface at the specified object path on
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
* to see what objects it has.
|
||||
*
|
||||
* The lifetime of the ObjectManager interface is bound to the lifetime
|
||||
* of the returned slot instance.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void removeObjectManager() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Tests whether ObjectManager interface is added at the path of this D-Bus object
|
||||
* @return True if ObjectManager interface is there, false otherwise
|
||||
*/
|
||||
virtual bool hasObjectManager() const = 0;
|
||||
[[nodiscard]] virtual Slot addObjectManager(return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides D-Bus connection used by the object
|
||||
*
|
||||
* @return Reference to the D-Bus connection
|
||||
*/
|
||||
virtual sdbus::IConnection& getConnection() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A helper object for convenient registration of the method
|
||||
*
|
||||
* This is a high-level, convenience way of registering D-Bus methods that abstracts
|
||||
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the parameters and return type
|
||||
* of the provided native method implementation callback.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] MethodRegistrator registerMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient registration of the signal
|
||||
*
|
||||
* This is a high-level, convenience way of registering D-Bus signals that abstracts
|
||||
* from the D-Bus message concept. Signal arguments are automatically (de)serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native parameters.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object.registerSignal("paramChange").onInterface("com.kistler.foo").withParameters<std::map<int32_t, std::string>>();
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] SignalRegistrator registerSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Registers property that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient registration of the property
|
||||
*
|
||||
* This is a high-level, convenience way of registering D-Bus properties that abstracts
|
||||
* from the D-Bus message concept. Property arguments are automatically (de)serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native callbacks.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.registerProperty("state").onInterface("com.kistler.foo").withGetter([this](){ return this->state(); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] PropertyRegistrator registerProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets flags (annotations) for a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface whose flags will be set
|
||||
* @return A helper object for convenient setting of Interface flags
|
||||
*
|
||||
* This is a high-level, convenience alternative to the other setInterfaceFlags overload.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.setInterfaceFlags("com.kistler.foo").markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
|
||||
|
||||
/*!
|
||||
* @brief Emits signal on D-Bus
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient emission of signals
|
||||
*
|
||||
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
|
||||
* from the D-Bus message concept. Signal arguments are automatically serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int arg1 = ...;
|
||||
* double arg2 = ...;
|
||||
* object_.emitSignal("fooSignal").onInterface("com.kistler.foo").withArguments(arg1, arg2);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] SignalEmitter emitSignal(const std::string& signalName);
|
||||
[[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
virtual const std::string& getObjectPath() const = 0;
|
||||
[[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides currently processed D-Bus message
|
||||
* @brief Provides access to the currently processed D-Bus message
|
||||
*
|
||||
* This method provides immutable access to the currently processed incoming D-Bus message.
|
||||
* This method provides access to the currently processed incoming D-Bus message.
|
||||
* "Currently processed" means that the registered callback handler(s) for that message
|
||||
* are being invoked. This method is meant to be called from within a callback handler
|
||||
* (e.g. D-Bus method implementation handler). In such a case it is guaranteed to return
|
||||
* a valid pointer to the D-Bus message for which the handler is called. If called from other
|
||||
* contexts/threads, it may return a nonzero pointer or a nullptr, depending on whether a message
|
||||
* was processed at the time of call or not, but the value is nondereferencable, since the pointed-to
|
||||
* message may have gone in the meantime.
|
||||
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
|
||||
* guaranteed to return a valid D-Bus message instance for which the handler is called.
|
||||
* If called from other contexts/threads, it may return a valid or invalid message, depending
|
||||
* on whether a message was processed or not at the time of the call.
|
||||
*
|
||||
* @return A pointer to the currently processed D-Bus message
|
||||
* @return Currently processed D-Bus message
|
||||
*/
|
||||
virtual const Message* getCurrentlyProcessedMessage() const = 0;
|
||||
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Unregisters object's API and removes object from the bus
|
||||
*
|
||||
* This method unregisters the object, its interfaces, methods, signals and properties
|
||||
* from the bus. Unregistration is done automatically also in object's destructor. This
|
||||
* method makes sense if, in the process of object removal, we need to make sure that
|
||||
* callbacks are unregistered explicitly before the final destruction of the object instance.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void unregister() = 0;
|
||||
|
||||
// Lower-level, message-based API
|
||||
/*!
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] items Individual instances of VTable item structures
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* Parameter `items' represents a vtable definition that may contain method declarations
|
||||
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
|
||||
* struct), signal declarations (using SignalVTableItem struct), or global interface
|
||||
* flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime. For each vtable an internal
|
||||
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
template < typename... VTableItems
|
||||
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
|
||||
void addVTable(InterfaceName interfaceName, VTableItems&&... items);
|
||||
|
||||
/*!
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
|
||||
* property declarations (using PropertyVTableItem struct), signal declarations (using
|
||||
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime. For each vtable an internal
|
||||
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
|
||||
* property declarations (using PropertyVTableItem struct), signal declarations (using
|
||||
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime. For each vtable an internal
|
||||
* registration slot is created and is returned to the caller. The returned slot should be destroyed
|
||||
* when the vtable is not needed anymore. This allows for "dynamic" object API where vtables
|
||||
* can be added or removed by the user at runtime.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Creates a signal message
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A signal message
|
||||
*
|
||||
* Serialize signal arguments into the returned message and emit the signal by passing
|
||||
* the message with serialized arguments to the @c emitSignal function.
|
||||
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits signal for this object path
|
||||
*
|
||||
* @param[in] message Signal message to be sent out
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitSignal(const Signal& message) = 0;
|
||||
|
||||
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) const = 0;
|
||||
};
|
||||
|
||||
// Out-of-line member definitions
|
||||
|
||||
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
|
||||
inline SignalEmitter IObject::emitSignal(const SignalName& signalName)
|
||||
{
|
||||
return MethodRegistrator(*this, methodName);
|
||||
}
|
||||
|
||||
inline SignalRegistrator IObject::registerSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalRegistrator(*this, signalName);
|
||||
}
|
||||
|
||||
inline PropertyRegistrator IObject::registerProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertyRegistrator(*this, propertyName);
|
||||
}
|
||||
|
||||
inline InterfaceFlagsSetter IObject::setInterfaceFlags(const std::string& interfaceName)
|
||||
{
|
||||
return InterfaceFlagsSetter(*this, interfaceName);
|
||||
return {*this, signalName};
|
||||
}
|
||||
|
||||
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalEmitter(*this, signalName);
|
||||
return {*this, signalName.c_str()};
|
||||
}
|
||||
|
||||
inline SignalEmitter IObject::emitSignal(const char* signalName)
|
||||
{
|
||||
return {*this, signalName};
|
||||
}
|
||||
|
||||
template <typename... VTableItems, typename>
|
||||
void IObject::addVTable(InterfaceName interfaceName, VTableItems&&... items)
|
||||
{
|
||||
addVTable(std::move(interfaceName), {std::forward<VTableItems>(items)...});
|
||||
}
|
||||
|
||||
template <typename... VTableItems, typename>
|
||||
VTableAdder IObject::addVTable(VTableItems&&... items)
|
||||
{
|
||||
return addVTable(std::vector<VTableItem>{std::forward<VTableItems>(items)...});
|
||||
}
|
||||
|
||||
inline VTableAdder IObject::addVTable(std::vector<VTableItem> vtable)
|
||||
{
|
||||
return {*this, std::move(vtable)};
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -503,10 +474,11 @@ namespace sdbus {
|
||||
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
|
||||
[[nodiscard]] std::unique_ptr<IObject> createObject(IConnection& connection, ObjectPath objectPath);
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl> // NOLINT(misc-header-include-cycle)
|
||||
#include <sdbus-c++/VTableItems.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IOBJECT_H_ */
|
||||
|
||||
+564
-207
File diff suppressed because it is too large
Load Diff
+382
-222
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Message.h
|
||||
*
|
||||
@@ -27,36 +27,40 @@
|
||||
#ifndef SDBUS_CXX_MESSAGE_H_
|
||||
#define SDBUS_CXX_MESSAGE_H_
|
||||
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#if __cplusplus >= 202002L
|
||||
#include <span>
|
||||
#endif
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#ifdef __has_include
|
||||
# if __has_include(<span>)
|
||||
# include <span>
|
||||
# endif
|
||||
#endif
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <sys/types.h>
|
||||
#include <algorithm>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Variant;
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
template <typename... ValueTypes> class Struct;
|
||||
class UnixFd;
|
||||
class MethodReply;
|
||||
namespace internal {
|
||||
class ISdBus;
|
||||
class IConnection;
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@@ -69,13 +73,19 @@ namespace sdbus {
|
||||
* Serialization and deserialization functions are provided for types supported
|
||||
* by D-Bus.
|
||||
*
|
||||
* You don't need to work with this class directly if you use high-level APIs
|
||||
* of @c IObject and @c IProxy.
|
||||
* You mostly don't need to work with this class directly if you use high-level
|
||||
* APIs of @c IObject and @c IProxy.
|
||||
*
|
||||
***********************************************/
|
||||
class [[nodiscard]] Message
|
||||
{
|
||||
public:
|
||||
Message(const Message&) noexcept;
|
||||
Message& operator=(const Message&) noexcept;
|
||||
Message(Message&& other) noexcept;
|
||||
Message& operator=(Message&& other) noexcept;
|
||||
~Message();
|
||||
|
||||
Message& operator<<(bool item);
|
||||
Message& operator<<(int16_t item);
|
||||
Message& operator<<(int32_t item);
|
||||
@@ -87,27 +97,34 @@ namespace sdbus {
|
||||
Message& operator<<(double item);
|
||||
Message& operator<<(const char *item);
|
||||
Message& operator<<(const std::string &item);
|
||||
Message& operator<<(std::string_view item);
|
||||
Message& operator<<(const Variant &item);
|
||||
template <typename ...Elements>
|
||||
Message& operator<<(const std::variant<Elements...>& value);
|
||||
Message& operator<<(const ObjectPath &item);
|
||||
Message& operator<<(const Signature &item);
|
||||
Message& operator<<(const UnixFd &item);
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
Message& operator<<(const std::vector<_Element, _Allocator>& items);
|
||||
template <typename _Element, std::size_t _Size>
|
||||
Message& operator<<(const std::array<_Element, _Size>& items);
|
||||
#if __cplusplus >= 202002L
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
Message& operator<<(const std::span<_Element, _Extent>& items);
|
||||
template <typename Element, typename Allocator>
|
||||
Message& operator<<(const std::vector<Element, Allocator>& items);
|
||||
template <typename Element, std::size_t Size>
|
||||
Message& operator<<(const std::array<Element, Size>& items);
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename Element, std::size_t Extent>
|
||||
Message& operator<<(const std::span<Element, Extent>& items);
|
||||
#endif
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items);
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator<<(const Struct<_ValueTypes...>& item);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator<<(const std::tuple<_ValueTypes...>& item);
|
||||
// NOLINTNEXTLINE(modernize-use-constraints): Public API is C++17-compatible
|
||||
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>
|
||||
Message& operator<<(const std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items);
|
||||
template <typename... ValueTypes>
|
||||
Message& operator<<(const Struct<ValueTypes...>& item);
|
||||
template <typename... ValueTypes>
|
||||
Message& operator<<(const std::tuple<ValueTypes...>& item);
|
||||
|
||||
Message& operator>>(bool& item);
|
||||
Message& operator>>(int16_t& item);
|
||||
@@ -121,56 +138,88 @@ namespace sdbus {
|
||||
Message& operator>>(char*& item);
|
||||
Message& operator>>(std::string &item);
|
||||
Message& operator>>(Variant &item);
|
||||
template <typename ...Elements>
|
||||
Message& operator>>(std::variant<Elements...>& value);
|
||||
Message& operator>>(ObjectPath &item);
|
||||
Message& operator>>(Signature &item);
|
||||
Message& operator>>(UnixFd &item);
|
||||
template <typename _Element, typename _Allocator>
|
||||
Message& operator>>(std::vector<_Element, _Allocator>& items);
|
||||
template <typename _Element, std::size_t _Size>
|
||||
Message& operator>>(std::array<_Element, _Size>& items);
|
||||
#if __cplusplus >= 202002L
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
Message& operator>>(std::span<_Element, _Extent>& items);
|
||||
template <typename Element, typename Allocator>
|
||||
Message& operator>>(std::vector<Element, Allocator>& items);
|
||||
template <typename Element, std::size_t Size>
|
||||
Message& operator>>(std::array<Element, Size>& items);
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename Element, std::size_t Extent>
|
||||
Message& operator>>(std::span<Element, Extent>& items);
|
||||
#endif
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items);
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator>>(Struct<_ValueTypes...>& item);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator>>(std::tuple<_ValueTypes...>& item);
|
||||
// NOLINTNEXTLINE(modernize-use-constraints): Public API is C++17-compatible
|
||||
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>
|
||||
Message& operator>>(std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items);
|
||||
template <typename... ValueTypes>
|
||||
Message& operator>>(Struct<ValueTypes...>& item);
|
||||
template <typename... ValueTypes>
|
||||
Message& operator>>(std::tuple<ValueTypes...>& item);
|
||||
|
||||
Message& openContainer(const std::string& signature);
|
||||
template <typename ElementType>
|
||||
Message& openContainer();
|
||||
Message& openContainer(const char* signature);
|
||||
Message& closeContainer();
|
||||
Message& openDictEntry(const std::string& signature);
|
||||
template <typename KeyType, typename ValueType>
|
||||
Message& openDictEntry();
|
||||
Message& openDictEntry(const char* signature);
|
||||
Message& closeDictEntry();
|
||||
Message& openVariant(const std::string& signature);
|
||||
template <typename ValueType>
|
||||
Message& openVariant();
|
||||
Message& openVariant(const char* signature);
|
||||
Message& closeVariant();
|
||||
Message& openStruct(const std::string& signature);
|
||||
template <typename... ValueTypes>
|
||||
Message& openStruct();
|
||||
Message& openStruct(const char* signature);
|
||||
Message& closeStruct();
|
||||
|
||||
Message& enterContainer(const std::string& signature);
|
||||
template <typename ElementType>
|
||||
Message& enterContainer();
|
||||
Message& enterContainer(const char* signature);
|
||||
Message& exitContainer();
|
||||
Message& enterDictEntry(const std::string& signature);
|
||||
template <typename KeyType, typename ValueType>
|
||||
Message& enterDictEntry();
|
||||
Message& enterDictEntry(const char* signature);
|
||||
Message& exitDictEntry();
|
||||
Message& enterVariant(const std::string& signature);
|
||||
template <typename ValueType>
|
||||
Message& enterVariant();
|
||||
Message& enterVariant(const char* signature);
|
||||
Message& exitVariant();
|
||||
Message& enterStruct(const std::string& signature);
|
||||
template <typename... ValueTypes>
|
||||
Message& enterStruct();
|
||||
Message& enterStruct(const char* signature);
|
||||
Message& exitStruct();
|
||||
|
||||
Message& appendArray(char type, const void *ptr, size_t size);
|
||||
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();
|
||||
|
||||
std::string getInterfaceName() const;
|
||||
std::string getMemberName() const;
|
||||
std::string getSender() const;
|
||||
std::string getPath() const;
|
||||
std::string getDestination() const;
|
||||
void peekType(std::string& type, std::string& contents) const;
|
||||
const char* getInterfaceName() const;
|
||||
const char* getMemberName() const;
|
||||
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;
|
||||
bool isEmpty() const;
|
||||
bool isAtEnd(bool complete) const;
|
||||
@@ -179,6 +228,15 @@ namespace sdbus {
|
||||
void seal();
|
||||
void rewind(bool complete);
|
||||
|
||||
enum class DumpFlags : uint64_t // NOLINT(performance-enum-size): using size from sd-bus
|
||||
{
|
||||
Default = 0ULL,
|
||||
WithHeader = 1ULL << 0,
|
||||
SubtreeOnly = 1ULL << 1,
|
||||
SubtreeOnlyWithHeader = WithHeader | SubtreeOnly
|
||||
};
|
||||
[[nodiscard]] std::string dumpToString(DumpFlags flags) const;
|
||||
|
||||
pid_t getCredsPid() const;
|
||||
uid_t getCredsUid() const;
|
||||
uid_t getCredsEuid() const;
|
||||
@@ -190,42 +248,29 @@ namespace sdbus {
|
||||
class Factory;
|
||||
|
||||
private:
|
||||
template <typename _Array>
|
||||
void serializeArray(const _Array& items);
|
||||
template <typename _Array>
|
||||
void deserializeArray(_Array& items);
|
||||
template <typename _Array>
|
||||
void deserializeArrayFast(_Array& items);
|
||||
template <typename _Element, typename _Allocator>
|
||||
void deserializeArrayFast(std::vector<_Element, _Allocator>& items);
|
||||
template <typename _Array>
|
||||
void deserializeArraySlow(_Array& items);
|
||||
template <typename _Element, typename _Allocator>
|
||||
void deserializeArraySlow(std::vector<_Element, _Allocator>& items);
|
||||
|
||||
template <typename _Dictionary>
|
||||
void serializeDictionary(const _Dictionary& items);
|
||||
template <typename _Dictionary>
|
||||
void deserializeDictionary(_Dictionary& items);
|
||||
template <typename Array>
|
||||
void serializeArray(const Array& items);
|
||||
template <typename Array>
|
||||
void deserializeArray(Array& items);
|
||||
template <typename Array>
|
||||
void deserializeArrayFast(Array& items);
|
||||
template <typename Element, typename Allocator>
|
||||
void deserializeArrayFast(std::vector<Element, Allocator>& items);
|
||||
template <typename Array>
|
||||
void deserializeArraySlow(Array& items);
|
||||
template <typename Element, typename Allocator>
|
||||
void deserializeArraySlow(std::vector<Element, Allocator>& items);
|
||||
|
||||
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;
|
||||
|
||||
Message(const Message&) noexcept;
|
||||
Message& operator=(const Message&) noexcept;
|
||||
Message(Message&& other) noexcept;
|
||||
Message& operator=(Message&& other) noexcept;
|
||||
|
||||
~Message();
|
||||
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};
|
||||
};
|
||||
|
||||
@@ -238,23 +283,20 @@ namespace sdbus {
|
||||
MethodCall() = default;
|
||||
|
||||
MethodReply send(uint64_t timeout) const;
|
||||
[[deprecated("Use send overload with floating_slot instead")]] void send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const;
|
||||
void send(void* callback, void* userData, uint64_t timeout, floating_slot_t) const;
|
||||
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout) const;
|
||||
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout, return_slot_t) const;
|
||||
|
||||
MethodReply createReply() const;
|
||||
MethodReply createErrorReply(const sdbus::Error& error) const;
|
||||
MethodReply createErrorReply(const Error& error) const;
|
||||
|
||||
void dontExpectReply();
|
||||
bool doesntExpectReply() const;
|
||||
|
||||
protected:
|
||||
MethodCall(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t) noexcept;
|
||||
MethodCall(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
|
||||
|
||||
private:
|
||||
MethodReply sendWithReply(uint64_t timeout = 0) const;
|
||||
MethodReply sendWithNoReply() const;
|
||||
const internal::IConnection* connection_{};
|
||||
};
|
||||
|
||||
class MethodReply : public Message
|
||||
@@ -265,6 +307,7 @@ namespace sdbus {
|
||||
public:
|
||||
MethodReply() = default;
|
||||
void send() const;
|
||||
uint64_t getReplyCookie() const;
|
||||
};
|
||||
|
||||
class Signal : public Message
|
||||
@@ -275,6 +318,7 @@ namespace sdbus {
|
||||
public:
|
||||
Signal() = default;
|
||||
void setDestination(const std::string& destination);
|
||||
void setDestination(const char* destination);
|
||||
void send() const;
|
||||
};
|
||||
|
||||
@@ -306,25 +350,40 @@ namespace sdbus {
|
||||
PlainMessage() = default;
|
||||
};
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
|
||||
PlainMessage createPlainMessage();
|
||||
|
||||
template <typename ...Elements>
|
||||
inline Message& Message::operator<<(const std::variant<Elements...>& value)
|
||||
{
|
||||
std::visit([this](const auto& inner)
|
||||
{
|
||||
openVariant<decltype(inner)>();
|
||||
*this << inner;
|
||||
closeVariant();
|
||||
}, value);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Element, typename Allocator>
|
||||
inline Message& Message::operator<<(const std::vector<Element, Allocator>& items)
|
||||
{
|
||||
serializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Element, std::size_t _Size>
|
||||
inline Message& Message::operator<<(const std::array<_Element, _Size>& items)
|
||||
template <typename Element, std::size_t Size>
|
||||
inline Message& Message::operator<<(const std::array<Element, Size>& items)
|
||||
{
|
||||
serializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
inline Message& Message::operator<<(const std::span<_Element, _Extent>& items)
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename Element, std::size_t Extent>
|
||||
inline Message& Message::operator<<(const std::span<Element, Extent>& items)
|
||||
{
|
||||
serializeArray(items);
|
||||
|
||||
@@ -332,20 +391,27 @@ namespace sdbus {
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::serializeArray(const _Array& items)
|
||||
template <typename Enum, typename>
|
||||
inline Message& Message::operator<<(const Enum &item)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
return operator<<(static_cast<std::underlying_type_t<Enum>>(item));
|
||||
}
|
||||
|
||||
template <typename Array>
|
||||
inline void Message::serializeArray(const Array& items)
|
||||
{
|
||||
using ElementType = typename Array::value_type;
|
||||
|
||||
// Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool,
|
||||
// otherwise use step-by-step serialization of individual elements.
|
||||
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
|
||||
{
|
||||
appendArray(*signature_of<ElementType>::str().c_str(), items.data(), items.size() * sizeof(ElementType));
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
|
||||
appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType));
|
||||
}
|
||||
else
|
||||
{
|
||||
openContainer(signature_of<ElementType>::str());
|
||||
openContainer<ElementType>();
|
||||
|
||||
for (const auto& item : items)
|
||||
*this << item;
|
||||
@@ -354,102 +420,131 @@ namespace sdbus {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items)
|
||||
template <typename Key, typename Value>
|
||||
inline Message& Message::operator<<(const DictEntry<Key, Value>& value)
|
||||
{
|
||||
serializeDictionary(items);
|
||||
openDictEntry<Key, Value>();
|
||||
*this << value.first;
|
||||
*this << value.second;
|
||||
closeDictEntry();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename _Dictionary>
|
||||
inline void Message::serializeDictionary(const _Dictionary& items)
|
||||
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)
|
||||
{
|
||||
using KeyType = typename _Dictionary::key_type;
|
||||
using ValueType = typename _Dictionary::mapped_type;
|
||||
serializeDictionary<Key, Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
|
||||
|
||||
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
|
||||
const std::string arraySignature = "{" + dictEntrySignature + "}";
|
||||
return *this;
|
||||
}
|
||||
|
||||
openContainer(arraySignature);
|
||||
template <typename Key, typename Value>
|
||||
inline Message& Message::serializeDictionary(const std::initializer_list<DictEntry<Key, Value>>& dictEntries)
|
||||
{
|
||||
serializeDictionary<Key, Value>([&](Message& msg){ for (const auto& entry : dictEntries) msg << entry; });
|
||||
|
||||
for (const auto& item : items)
|
||||
{
|
||||
openDictEntry(dictEntrySignature);
|
||||
*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
|
||||
{
|
||||
template <typename... _Args>
|
||||
void serialize_pack(Message& msg, _Args&&... args)
|
||||
template <typename... Args>
|
||||
void serialize_pack(Message& msg, Args&&... args)
|
||||
{
|
||||
(void)(msg << ... << args);
|
||||
(void)(msg << ... << std::forward<Args>(args));
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
template <class Tuple, std::size_t... Is>
|
||||
void serialize_tuple( Message& msg
|
||||
, const _Tuple& t
|
||||
, std::index_sequence<_Is...>)
|
||||
, const Tuple& tuple
|
||||
, std::index_sequence<Is...>)
|
||||
{
|
||||
serialize_pack(msg, std::get<_Is>(t)...);
|
||||
serialize_pack(msg, std::get<Is>(tuple)...);
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& Message::operator<<(const Struct<_ValueTypes...>& item)
|
||||
template <typename... ValueTypes>
|
||||
inline Message& Message::operator<<(const Struct<ValueTypes...>& item)
|
||||
{
|
||||
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
|
||||
assert(structSignature.size() > 2);
|
||||
// Remove opening and closing parenthesis from the struct signature to get contents signature
|
||||
auto structContentSignature = structSignature.substr(1, structSignature.size() - 2);
|
||||
|
||||
openStruct(structContentSignature);
|
||||
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
openStruct<ValueTypes...>();
|
||||
detail::serialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
|
||||
closeStruct();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& Message::operator<<(const std::tuple<_ValueTypes...>& item)
|
||||
template <typename... ValueTypes>
|
||||
inline Message& Message::operator<<(const std::tuple<ValueTypes...>& item)
|
||||
{
|
||||
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
detail::serialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
|
||||
namespace detail
|
||||
{
|
||||
template <typename Element, typename... Elements>
|
||||
bool deserialize_variant(Message& msg, std::variant<Elements...>& value, const char* signature)
|
||||
{
|
||||
constexpr auto elemSignature = as_null_terminated(signature_of_v<Element>);
|
||||
if (std::strcmp(signature, elemSignature.data()) != 0)
|
||||
return false;
|
||||
|
||||
Element temp;
|
||||
msg.enterVariant(signature);
|
||||
msg >> temp;
|
||||
msg.exitVariant();
|
||||
value = std::move(temp);
|
||||
return true;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Elements>
|
||||
inline Message& Message::operator>>(std::variant<Elements...>& value)
|
||||
{
|
||||
auto [type, contents] = peekType();
|
||||
const bool result = (detail::deserialize_variant<Elements>(*this, value, contents) || ...);
|
||||
SDBUS_THROW_ERROR_IF(!result, "Failed to deserialize variant: signature did not match any of the variant types", EINVAL);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Element, typename Allocator>
|
||||
inline Message& Message::operator>>(std::vector<Element, Allocator>& items)
|
||||
{
|
||||
deserializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Element, std::size_t _Size>
|
||||
inline Message& Message::operator>>(std::array<_Element, _Size>& items)
|
||||
template <typename Element, std::size_t Size>
|
||||
inline Message& Message::operator>>(std::array<Element, Size>& items)
|
||||
{
|
||||
deserializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
inline Message& Message::operator>>(std::span<_Element, _Extent>& items)
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename Element, std::size_t Extent>
|
||||
inline Message& Message::operator>>(std::span<Element, Extent>& items)
|
||||
{
|
||||
deserializeArray(items);
|
||||
|
||||
@@ -457,10 +552,19 @@ namespace sdbus {
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::deserializeArray(_Array& items)
|
||||
template <typename Enum, typename>
|
||||
inline Message& Message::operator>>(Enum& item)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
std::underlying_type_t<Enum> val;
|
||||
*this >> val;
|
||||
item = static_cast<Enum>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Array>
|
||||
inline void Message::deserializeArray(Array& items)
|
||||
{
|
||||
using ElementType = typename Array::value_type;
|
||||
|
||||
// Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool,
|
||||
// otherwise use step-by-step deserialization of individual elements.
|
||||
@@ -474,40 +578,43 @@ namespace sdbus {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::deserializeArrayFast(_Array& items)
|
||||
template <typename Array>
|
||||
inline void Message::deserializeArrayFast(Array& items)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
using ElementType = typename Array::value_type;
|
||||
|
||||
size_t arraySize{};
|
||||
const ElementType* arrayPtr{};
|
||||
|
||||
readArray(*signature_of<ElementType>::str().c_str(), (const void**)&arrayPtr, &arraySize);
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
|
||||
readArray(*signature.data(), reinterpret_cast<const void**>(&arrayPtr), &arraySize);
|
||||
|
||||
size_t elementsInMsg = arraySize / sizeof(ElementType);
|
||||
bool notEnoughSpace = items.size() < elementsInMsg;
|
||||
const size_t elementsInMsg = arraySize / sizeof(ElementType);
|
||||
const bool notEnoughSpace = items.size() < elementsInMsg;
|
||||
SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL);
|
||||
|
||||
std::copy_n(arrayPtr, elementsInMsg, items.begin());
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
void Message::deserializeArrayFast(std::vector<_Element, _Allocator>& items)
|
||||
template <typename Element, typename Allocator>
|
||||
void Message::deserializeArrayFast(std::vector<Element, Allocator>& items)
|
||||
{
|
||||
size_t arraySize{};
|
||||
const _Element* arrayPtr{};
|
||||
const Element* arrayPtr{};
|
||||
|
||||
readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<Element>);
|
||||
readArray(*signature.data(), reinterpret_cast<const void**>(&arrayPtr), &arraySize);
|
||||
|
||||
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(Element)));
|
||||
}
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::deserializeArraySlow(_Array& items)
|
||||
template <typename Array>
|
||||
inline void Message::deserializeArraySlow(Array& items)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
using ElementType = typename Array::value_type;
|
||||
|
||||
if(!enterContainer(signature_of<ElementType>::str()))
|
||||
if(!enterContainer<ElementType>())
|
||||
return;
|
||||
|
||||
for (auto& elem : items)
|
||||
@@ -521,15 +628,15 @@ namespace sdbus {
|
||||
exitContainer();
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
|
||||
template <typename Element, typename Allocator>
|
||||
void Message::deserializeArraySlow(std::vector<Element, Allocator>& items)
|
||||
{
|
||||
if(!enterContainer(signature_of<_Element>::str()))
|
||||
if(!enterContainer<Element>())
|
||||
return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
_Element elem;
|
||||
Element elem;
|
||||
if (*this >> elem)
|
||||
items.emplace_back(std::move(elem));
|
||||
else
|
||||
@@ -541,94 +648,147 @@ namespace sdbus {
|
||||
exitContainer();
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items)
|
||||
template <typename Key, typename Value>
|
||||
inline Message& Message::operator>>(DictEntry<Key, Value>& value)
|
||||
{
|
||||
deserializeDictionary(items);
|
||||
if (!enterDictEntry<Key, Value>())
|
||||
return *this;
|
||||
*this >> value.first >> value.second;
|
||||
exitDictEntry();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename _Dictionary>
|
||||
inline void Message::deserializeDictionary(_Dictionary& items)
|
||||
template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
|
||||
inline Message& Message::operator>>(std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items)
|
||||
{
|
||||
using KeyType = typename _Dictionary::key_type;
|
||||
using ValueType = typename _Dictionary::mapped_type;
|
||||
deserializeDictionary<Key, Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
|
||||
|
||||
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
|
||||
const std::string arraySignature = "{" + dictEntrySignature + "}";
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (!enterContainer(arraySignature))
|
||||
return;
|
||||
template <typename Key, typename Value, typename Callback>
|
||||
inline Message& Message::deserializeDictionary(const Callback& callback)
|
||||
{
|
||||
if (!enterContainer<DictEntry<Key, Value>>())
|
||||
return *this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!enterDictEntry(dictEntrySignature))
|
||||
DictEntry<Key, Value> dictEntry;
|
||||
*this >> dictEntry;
|
||||
if (!*this)
|
||||
break;
|
||||
|
||||
KeyType key;
|
||||
ValueType value;
|
||||
*this >> key >> value;
|
||||
|
||||
items.emplace(std::move(key), std::move(value));
|
||||
|
||||
exitDictEntry();
|
||||
callback(std::move(dictEntry));
|
||||
}
|
||||
|
||||
clearFlags();
|
||||
|
||||
exitContainer();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename... _Args>
|
||||
void deserialize_pack(Message& msg, _Args&... args)
|
||||
template <typename... Args>
|
||||
void deserialize_pack(Message& msg, Args&... args)
|
||||
{
|
||||
(void)(msg >> ... >> args);
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
template <class Tuple, std::size_t... Is>
|
||||
void deserialize_tuple( Message& msg
|
||||
, _Tuple& t
|
||||
, std::index_sequence<_Is...> )
|
||||
, Tuple& tuple
|
||||
, std::index_sequence<Is...> )
|
||||
{
|
||||
deserialize_pack(msg, std::get<_Is>(t)...);
|
||||
deserialize_pack(msg, std::get<Is>(tuple)...);
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& Message::operator>>(Struct<_ValueTypes...>& item)
|
||||
template <typename... ValueTypes>
|
||||
inline Message& Message::operator>>(Struct<ValueTypes...>& item)
|
||||
{
|
||||
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
|
||||
// Remove opening and closing parenthesis from the struct signature to get contents signature
|
||||
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
|
||||
|
||||
if (!enterStruct(structContentSignature))
|
||||
if (!enterStruct<ValueTypes...>())
|
||||
return *this;
|
||||
|
||||
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
detail::deserialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
|
||||
|
||||
exitStruct();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& Message::operator>>(std::tuple<_ValueTypes...>& item)
|
||||
template <typename... ValueTypes>
|
||||
inline Message& Message::operator>>(std::tuple<ValueTypes...>& item)
|
||||
{
|
||||
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
detail::deserialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
||||
template <typename ElementType>
|
||||
inline Message& Message::openContainer()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
|
||||
return openContainer(signature.data());
|
||||
}
|
||||
|
||||
template <typename KeyType, typename ValueType>
|
||||
inline Message& Message::openDictEntry()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<KeyType, ValueType>>);
|
||||
return openDictEntry(signature.data());
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
inline Message& Message::openVariant()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<ValueType>);
|
||||
return openVariant(signature.data());
|
||||
}
|
||||
|
||||
template <typename... ValueTypes>
|
||||
inline Message& Message::openStruct()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<ValueTypes...>>);
|
||||
return openStruct(signature.data());
|
||||
}
|
||||
|
||||
template <typename ElementType>
|
||||
inline Message& Message::enterContainer()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
|
||||
return enterContainer(signature.data());
|
||||
}
|
||||
|
||||
template <typename KeyType, typename ValueType>
|
||||
inline Message& Message::enterDictEntry()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<KeyType, ValueType>>);
|
||||
return enterDictEntry(signature.data());
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
inline Message& Message::enterVariant()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<ValueType>);
|
||||
return enterVariant(signature.data());
|
||||
}
|
||||
|
||||
template <typename... ValueTypes>
|
||||
inline Message& Message::enterStruct()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<ValueTypes...>>);
|
||||
return enterStruct(signature.data());
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_MESSAGE_H_ */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file MethodResult.h
|
||||
*
|
||||
@@ -28,12 +28,13 @@
|
||||
#define SDBUS_CXX_METHODRESULT_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// Forward declaration
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Error;
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@@ -45,12 +46,12 @@ namespace sdbus {
|
||||
* by the method to either method return value or an error.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Results>
|
||||
template <typename... Results>
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
Result() = default;
|
||||
Result(MethodCall call);
|
||||
explicit Result(MethodCall call);
|
||||
|
||||
Result(const Result&) = delete;
|
||||
Result& operator=(const Result&) = delete;
|
||||
@@ -58,21 +59,23 @@ namespace sdbus {
|
||||
Result(Result&& other) = default;
|
||||
Result& operator=(Result&& other) = default;
|
||||
|
||||
void returnResults(const _Results&... results) const;
|
||||
~Result() = default;
|
||||
|
||||
void returnResults(const Results&... results) const;
|
||||
void returnError(const Error& error) const;
|
||||
|
||||
private:
|
||||
MethodCall call_;
|
||||
};
|
||||
|
||||
template <typename... _Results>
|
||||
inline Result<_Results...>::Result(MethodCall call)
|
||||
template <typename... Results>
|
||||
inline Result<Results...>::Result(MethodCall call)
|
||||
: call_(std::move(call))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::returnResults(const _Results&... results) const
|
||||
template <typename... Results>
|
||||
inline void Result<Results...>::returnResults(const Results&... results) const
|
||||
{
|
||||
assert(call_.isValid());
|
||||
auto reply = call_.createReply();
|
||||
@@ -80,13 +83,13 @@ namespace sdbus {
|
||||
reply.send();
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::returnError(const Error& error) const
|
||||
template <typename... Results>
|
||||
inline void Result<Results...>::returnError(const Error& error) const
|
||||
{
|
||||
auto reply = call_.createErrorReply(error);
|
||||
reply.send();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_METHODRESULT_H_ */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file ProxyInterfaces.h
|
||||
*
|
||||
@@ -29,13 +29,12 @@
|
||||
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IConnection;
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@@ -51,19 +50,19 @@ namespace sdbus {
|
||||
class ProxyObjectHolder
|
||||
{
|
||||
protected:
|
||||
ProxyObjectHolder(std::unique_ptr<IProxy>&& proxy)
|
||||
explicit ProxyObjectHolder(std::unique_ptr<IProxy>&& proxy)
|
||||
: proxy_(std::move(proxy))
|
||||
{
|
||||
assert(proxy_ != nullptr);
|
||||
}
|
||||
|
||||
const IProxy& getProxy() const
|
||||
[[nodiscard]] const IProxy& getProxy() const
|
||||
{
|
||||
assert(proxy_ != nullptr);
|
||||
return *proxy_;
|
||||
}
|
||||
|
||||
IProxy& getProxy()
|
||||
[[nodiscard]] IProxy& getProxy()
|
||||
{
|
||||
assert(proxy_ != nullptr);
|
||||
return *proxy_;
|
||||
@@ -83,17 +82,23 @@ namespace sdbus {
|
||||
* methods. So the _Interfaces template parameter is a list of sdbus-c++-xml2cpp-generated
|
||||
* proxy-side interface classes representing interfaces of the corresponding remote D-Bus object.
|
||||
*
|
||||
* In the final proxy class inherited from ProxyInterfaces, it is necessary to finish proxy
|
||||
* registration in class constructor (`finishRegistration();`), and, conversely, unregister
|
||||
* the proxy in class destructor (`unregister();`).
|
||||
* In the final adaptor class inherited from ProxyInterfaces, one needs to make sure:
|
||||
* 1. to call `registerProxy();` in the class constructor, and, conversely,
|
||||
* 2. to call `unregisterProxy();` in the class destructor,
|
||||
* so that the signals are subscribed to and unsubscribed from at a proper time.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
template <typename... Interfaces>
|
||||
class ProxyInterfaces
|
||||
: protected ProxyObjectHolder
|
||||
, public _Interfaces...
|
||||
, public Interfaces...
|
||||
{
|
||||
public:
|
||||
ProxyInterfaces(const ProxyInterfaces&) = delete;
|
||||
ProxyInterfaces& operator=(const ProxyInterfaces&) = delete;
|
||||
ProxyInterfaces(ProxyInterfaces&&) = delete;
|
||||
ProxyInterfaces& operator=(ProxyInterfaces&&) = delete;
|
||||
|
||||
/*!
|
||||
* @brief Creates native-like proxy object instance
|
||||
*
|
||||
@@ -103,9 +108,9 @@ namespace sdbus {
|
||||
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
|
||||
* For more information on its behavior, consult @ref createProxy(std::string,std::string)
|
||||
*/
|
||||
ProxyInterfaces(std::string destination, std::string objectPath)
|
||||
ProxyInterfaces(ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getProxy())...
|
||||
, Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
@@ -118,9 +123,9 @@ namespace sdbus {
|
||||
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
|
||||
* For more information on its behavior, consult @ref createProxy(std::string,std::string,sdbus::dont_run_event_loop_thread_t)
|
||||
*/
|
||||
ProxyInterfaces(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
|
||||
ProxyInterfaces(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
|
||||
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
|
||||
, _Interfaces(getProxy())...
|
||||
, Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
@@ -134,9 +139,9 @@ namespace sdbus {
|
||||
* The proxy created this way just references a D-Bus connection owned and managed by the user.
|
||||
* For more information on its behavior, consult @ref createProxy(IConnection&,std::string,std::string)
|
||||
*/
|
||||
ProxyInterfaces(IConnection& connection, std::string destination, std::string objectPath)
|
||||
ProxyInterfaces(IConnection& connection, ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyObjectHolder(createProxy(connection, std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getProxy())...
|
||||
, Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
@@ -150,9 +155,9 @@ namespace sdbus {
|
||||
* The proxy created this way becomes an owner of the connection.
|
||||
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string)
|
||||
*/
|
||||
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath)
|
||||
ProxyInterfaces(std::unique_ptr<IConnection>&& connection, ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getProxy())...
|
||||
, Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
@@ -166,22 +171,22 @@ namespace sdbus {
|
||||
* The proxy created this way becomes an owner of the connection.
|
||||
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string,sdbus::dont_run_event_loop_thread_t)
|
||||
*/
|
||||
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
|
||||
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
|
||||
, _Interfaces(getProxy())...
|
||||
ProxyInterfaces(std::unique_ptr<IConnection>&& connection, ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
|
||||
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
|
||||
, Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Finishes proxy registration and makes the proxy ready for use
|
||||
* @brief Registers handlers for D-Bus signals of the remote object
|
||||
*
|
||||
* This function must be called in the constructor of the final proxy class that implements ProxyInterfaces.
|
||||
*
|
||||
* For more information, see underlying @ref IProxy::finishRegistration()
|
||||
* See also @ref IProxy::registerSignalHandler()
|
||||
*/
|
||||
void registerProxy()
|
||||
{
|
||||
getProxy().finishRegistration();
|
||||
(Interfaces::registerProxy(), ...);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -197,23 +202,16 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
* @brief Returns reference to the underlying IProxy instance
|
||||
*/
|
||||
const std::string& getObjectPath() const
|
||||
{
|
||||
return getProxy().getObjectPath();
|
||||
}
|
||||
using ProxyObjectHolder::getProxy;
|
||||
|
||||
protected:
|
||||
using base_type = ProxyInterfaces;
|
||||
|
||||
ProxyInterfaces(const ProxyInterfaces&) = delete;
|
||||
ProxyInterfaces& operator=(const ProxyInterfaces&) = delete;
|
||||
ProxyInterfaces(ProxyInterfaces&&) = default;
|
||||
ProxyInterfaces& operator=(ProxyInterfaces&&) = default;
|
||||
~ProxyInterfaces() = default;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_INTERFACES_H_ */
|
||||
#endif /* SDBUS_CXX_PROXYINTERFACES_H_ */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file StandardInterfaces.h
|
||||
*
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
@@ -39,206 +40,364 @@ namespace sdbus {
|
||||
// Proxy for peer
|
||||
class Peer_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
|
||||
|
||||
protected:
|
||||
Peer_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
explicit Peer_proxy(IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
Peer_proxy(const Peer_proxy&) = delete;
|
||||
Peer_proxy& operator=(const Peer_proxy&) = delete;
|
||||
Peer_proxy(Peer_proxy&&) = default;
|
||||
Peer_proxy& operator=(Peer_proxy&&) = default;
|
||||
|
||||
~Peer_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Peer_proxy(const Peer_proxy&) = delete;
|
||||
Peer_proxy& operator=(const Peer_proxy&) = delete;
|
||||
Peer_proxy(Peer_proxy&&) = delete;
|
||||
Peer_proxy& operator=(Peer_proxy&&) = delete;
|
||||
|
||||
void Ping()
|
||||
{
|
||||
proxy_->callMethod("Ping").onInterface(INTERFACE_NAME);
|
||||
m_proxy.callMethod("Ping").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
std::string GetMachineId()
|
||||
{
|
||||
std::string machineUUID;
|
||||
proxy_->callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
|
||||
m_proxy.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
|
||||
return machineUUID;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// Proxy for introspection
|
||||
class Introspectable_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
|
||||
|
||||
protected:
|
||||
Introspectable_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
explicit Introspectable_proxy(IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
Introspectable_proxy(const Introspectable_proxy&) = delete;
|
||||
Introspectable_proxy& operator=(const Introspectable_proxy&) = delete;
|
||||
Introspectable_proxy(Introspectable_proxy&&) = default;
|
||||
Introspectable_proxy& operator=(Introspectable_proxy&&) = default;
|
||||
|
||||
~Introspectable_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
Introspectable_proxy(const Introspectable_proxy&) = delete;
|
||||
Introspectable_proxy& operator=(const Introspectable_proxy&) = delete;
|
||||
Introspectable_proxy(Introspectable_proxy&&) = delete;
|
||||
Introspectable_proxy& operator=(Introspectable_proxy&&) = delete;
|
||||
|
||||
std::string Introspect()
|
||||
{
|
||||
std::string xml;
|
||||
proxy_->callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
|
||||
m_proxy.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
|
||||
return xml;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// Proxy for properties
|
||||
class Properties_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
explicit Properties_proxy(IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
proxy_
|
||||
->uponSignal("PropertiesChanged")
|
||||
}
|
||||
|
||||
~Properties_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
m_proxy
|
||||
.uponSignal("PropertiesChanged")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const std::string& interfaceName
|
||||
, const std::map<std::string, sdbus::Variant>& changedProperties
|
||||
, const std::vector<std::string>& invalidatedProperties )
|
||||
.call([this]( const InterfaceName& interfaceName
|
||||
, const std::map<PropertyName, Variant>& changedProperties
|
||||
, const std::vector<PropertyName>& invalidatedProperties )
|
||||
{
|
||||
this->onPropertiesChanged(interfaceName, changedProperties, invalidatedProperties);
|
||||
});
|
||||
}
|
||||
|
||||
Properties_proxy(const Properties_proxy&) = delete;
|
||||
Properties_proxy& operator=(const Properties_proxy&) = delete;
|
||||
Properties_proxy(Properties_proxy&&) = default;
|
||||
Properties_proxy& operator=(Properties_proxy&&) = default;
|
||||
|
||||
~Properties_proxy() = default;
|
||||
|
||||
virtual void onPropertiesChanged( const std::string& interfaceName
|
||||
, const std::map<std::string, sdbus::Variant>& changedProperties
|
||||
, const std::vector<std::string>& invalidatedProperties ) = 0;
|
||||
virtual void onPropertiesChanged( const InterfaceName& interfaceName
|
||||
, const std::map<PropertyName, Variant>& changedProperties
|
||||
, const std::vector<PropertyName>& invalidatedProperties ) = 0;
|
||||
|
||||
public:
|
||||
sdbus::Variant Get(const std::string& interfaceName, const std::string& propertyName)
|
||||
Properties_proxy(const Properties_proxy&) = delete;
|
||||
Properties_proxy& operator=(const Properties_proxy&) = delete;
|
||||
Properties_proxy(Properties_proxy&&) = delete;
|
||||
Properties_proxy& operator=(Properties_proxy&&) = delete;
|
||||
|
||||
Variant Get(const InterfaceName& interfaceName, const PropertyName& propertyName)
|
||||
{
|
||||
return proxy_->getProperty(propertyName).onInterface(interfaceName);
|
||||
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAsync(const std::string& interfaceName, const std::string& propertyName, _Function&& callback)
|
||||
Variant Get(std::string_view interfaceName, std::string_view propertyName)
|
||||
{
|
||||
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
|
||||
}
|
||||
|
||||
std::future<sdbus::Variant> GetAsync(const std::string& interfaceName, const std::string& propertyName, with_future_t)
|
||||
template <typename Function>
|
||||
PendingAsyncCall GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, Function&& callback)
|
||||
{
|
||||
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
|
||||
}
|
||||
|
||||
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
|
||||
template <typename Function>
|
||||
[[nodiscard]] Slot GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, Function&& callback, return_slot_t)
|
||||
{
|
||||
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, dont_expect_reply_t)
|
||||
std::future<Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t)
|
||||
{
|
||||
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, _Function&& callback)
|
||||
Awaitable<Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_awaitable_t)
|
||||
{
|
||||
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsAwaitable();
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, with_future_t)
|
||||
template <typename Function>
|
||||
PendingAsyncCall GetAsync(std::string_view interfaceName, std::string_view propertyName, Function&& callback)
|
||||
{
|
||||
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
|
||||
}
|
||||
|
||||
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
|
||||
template <typename Function>
|
||||
[[nodiscard]] Slot GetAsync(std::string_view interfaceName, std::string_view propertyName, Function&& callback, return_slot_t)
|
||||
{
|
||||
return proxy_->getAllProperties().onInterface(interfaceName);
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(const std::string& interfaceName, _Function&& callback)
|
||||
std::future<Variant> GetAsync(std::string_view interfaceName, std::string_view propertyName, with_future_t)
|
||||
{
|
||||
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
std::future<std::map<std::string, sdbus::Variant>> GetAllAsync(const std::string& interfaceName, with_future_t)
|
||||
Awaitable<Variant> GetAsync(std::string_view interfaceName, std::string_view propertyName, with_awaitable_t)
|
||||
{
|
||||
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsAwaitable();
|
||||
}
|
||||
|
||||
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const Variant& value)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
||||
}
|
||||
|
||||
void Set(std::string_view interfaceName, const std::string_view propertyName, const Variant& value)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
||||
}
|
||||
|
||||
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const Variant& value, dont_expect_reply_t)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
|
||||
}
|
||||
|
||||
void Set(std::string_view interfaceName, const std::string_view propertyName, const Variant& value, dont_expect_reply_t)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
PendingAsyncCall SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const Variant& value, Function&& callback)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<Function>(callback));
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
[[nodiscard]] Slot SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const 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 Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
}
|
||||
|
||||
Awaitable<void> SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const Variant& value, with_awaitable_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsAwaitable();
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const Variant& value, Function&& callback)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<Function>(callback));
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
[[nodiscard]] Slot SetAsync(std::string_view interfaceName, std::string_view propertyName, const 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(std::string_view interfaceName, std::string_view propertyName, const Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
}
|
||||
|
||||
Awaitable<void> SetAsync(std::string_view interfaceName, std::string_view propertyName, const Variant& value, with_awaitable_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsAwaitable();
|
||||
}
|
||||
|
||||
std::map<PropertyName, Variant> GetAll(const InterfaceName& interfaceName)
|
||||
{
|
||||
return m_proxy.getAllProperties().onInterface(interfaceName);
|
||||
}
|
||||
|
||||
std::map<PropertyName, Variant> GetAll(std::string_view interfaceName)
|
||||
{
|
||||
return m_proxy.getAllProperties().onInterface(interfaceName);
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, Function&& callback)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
[[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, Variant>> GetAllAsync(const InterfaceName& interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
Awaitable<std::map<PropertyName, Variant>> GetAllAsync(const InterfaceName& interfaceName, with_awaitable_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsAwaitable();
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
PendingAsyncCall GetAllAsync(std::string_view interfaceName, Function&& callback)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
[[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, Variant>> GetAllAsync(std::string_view interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
Awaitable<std::map<PropertyName, Variant>> GetAllAsync(std::string_view interfaceName, with_awaitable_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsAwaitable();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// Proxy for object manager
|
||||
class ObjectManager_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
|
||||
protected:
|
||||
ObjectManager_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
explicit ObjectManager_proxy(IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
proxy_
|
||||
->uponSignal("InterfacesAdded")
|
||||
}
|
||||
|
||||
~ObjectManager_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
m_proxy
|
||||
.uponSignal("InterfacesAdded")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
|
||||
.call([this]( const ObjectPath& objectPath
|
||||
, const std::map<InterfaceName, std::map<PropertyName, Variant>>& interfacesAndProperties )
|
||||
{
|
||||
this->onInterfacesAdded(objectPath, interfacesAndProperties);
|
||||
});
|
||||
|
||||
proxy_->uponSignal("InterfacesRemoved")
|
||||
m_proxy
|
||||
.uponSignal("InterfacesRemoved")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
.call([this]( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces )
|
||||
{
|
||||
this->onInterfacesRemoved(objectPath, interfaces);
|
||||
});
|
||||
}
|
||||
|
||||
ObjectManager_proxy(const ObjectManager_proxy&) = delete;
|
||||
ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete;
|
||||
ObjectManager_proxy(ObjectManager_proxy&&) = default;
|
||||
ObjectManager_proxy& operator=(ObjectManager_proxy&&) = default;
|
||||
|
||||
~ObjectManager_proxy() = default;
|
||||
|
||||
virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) = 0;
|
||||
virtual void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces) = 0;
|
||||
virtual void onInterfacesAdded( const ObjectPath& objectPath
|
||||
, const std::map<InterfaceName, std::map<PropertyName, Variant>>& interfacesAndProperties) = 0;
|
||||
virtual void onInterfacesRemoved( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces) = 0;
|
||||
|
||||
public:
|
||||
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects()
|
||||
ObjectManager_proxy(const ObjectManager_proxy&) = delete;
|
||||
ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete;
|
||||
ObjectManager_proxy(ObjectManager_proxy&&) = delete;
|
||||
ObjectManager_proxy& operator=(ObjectManager_proxy&&) = delete;
|
||||
|
||||
std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>> GetManagedObjects()
|
||||
{
|
||||
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
|
||||
proxy_->callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
|
||||
std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>> objectsInterfacesAndProperties;
|
||||
m_proxy.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
|
||||
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<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>> GetManagedObjectsAsync(with_future_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsFuture<std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>>();
|
||||
}
|
||||
|
||||
Awaitable<std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>> GetManagedObjectsAsync(with_awaitable_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsAwaitable<std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>>();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
|
||||
@@ -248,40 +407,53 @@ namespace sdbus {
|
||||
// Adaptor for properties
|
||||
class Properties_adaptor
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
explicit Properties_adaptor(IObject& object) : m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
Properties_adaptor(const Properties_adaptor&) = delete;
|
||||
Properties_adaptor& operator=(const Properties_adaptor&) = delete;
|
||||
Properties_adaptor(Properties_adaptor&&) = default;
|
||||
Properties_adaptor& operator=(Properties_adaptor&&) = default;
|
||||
|
||||
~Properties_adaptor() = default;
|
||||
|
||||
public:
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
|
||||
void registerAdaptor()
|
||||
{
|
||||
object_->emitPropertiesChangedSignal(interfaceName, properties);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName)
|
||||
public:
|
||||
Properties_adaptor(const Properties_adaptor&) = delete;
|
||||
Properties_adaptor& operator=(const Properties_adaptor&) = delete;
|
||||
Properties_adaptor(Properties_adaptor&&) = delete;
|
||||
Properties_adaptor& operator=(Properties_adaptor&&) = delete;
|
||||
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& properties)
|
||||
{
|
||||
object_->emitPropertiesChangedSignal(interfaceName);
|
||||
m_object.emitPropertiesChangedSignal(interfaceName, properties);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& properties)
|
||||
{
|
||||
m_object.emitPropertiesChangedSignal(interfaceName, properties);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName)
|
||||
{
|
||||
m_object.emitPropertiesChangedSignal(interfaceName);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const char* interfaceName)
|
||||
{
|
||||
m_object.emitPropertiesChangedSignal(interfaceName);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
IObject& m_object;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Object Manager Convenience Adaptor
|
||||
*
|
||||
* Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces
|
||||
* Adding this class as Interfaces.. template parameter of class AdaptorInterfaces
|
||||
* implements the *GetManagedObjects()* method of the [org.freedesktop.DBus.ObjectManager.GetManagedObjects](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager)
|
||||
* interface.
|
||||
*
|
||||
@@ -290,53 +462,61 @@ namespace sdbus {
|
||||
*/
|
||||
class ObjectManager_adaptor
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
|
||||
protected:
|
||||
explicit ObjectManager_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
explicit ObjectManager_adaptor(IObject& object) : m_object(object)
|
||||
{
|
||||
object_->addObjectManager();
|
||||
}
|
||||
|
||||
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
|
||||
ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete;
|
||||
ObjectManager_adaptor(ObjectManager_adaptor&&) = default;
|
||||
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = default;
|
||||
|
||||
~ObjectManager_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addObjectManager();
|
||||
}
|
||||
|
||||
public:
|
||||
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
|
||||
ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete;
|
||||
ObjectManager_adaptor(ObjectManager_adaptor&&) = delete;
|
||||
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = delete;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
IObject& m_object;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Managed Object Convenience Adaptor
|
||||
*
|
||||
* Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces
|
||||
* Adding this class as Interfaces.. template parameter of class AdaptorInterfaces
|
||||
* will extend the resulting object adaptor with emitInterfacesAddedSignal()/emitInterfacesRemovedSignal()
|
||||
* according to org.freedesktop.DBus.ObjectManager.InterfacesAdded/.InterfacesRemoved.
|
||||
*
|
||||
* Note that objects which implement this adaptor require an object manager (e.g via ObjectManager_adaptor) to be
|
||||
* instantiated on one of it's parent object paths or the same path. InterfacesAdded/InterfacesRemoved
|
||||
* Note that objects which implement this adaptor require an object manager (e.g., via ObjectManager_adaptor) to be
|
||||
* instantiated on one of its parent object paths or the same path. InterfacesAdded/InterfacesRemoved
|
||||
* signals are sent from the closest object manager at either the same path or the closest parent path of an object.
|
||||
*/
|
||||
class ManagedObject_adaptor
|
||||
{
|
||||
protected:
|
||||
explicit ManagedObject_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
explicit ManagedObject_adaptor(IObject& object)
|
||||
: m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
ManagedObject_adaptor(const ManagedObject_adaptor&) = delete;
|
||||
ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete;
|
||||
ManagedObject_adaptor(ManagedObject_adaptor&&) = default;
|
||||
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = default;
|
||||
|
||||
~ManagedObject_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
ManagedObject_adaptor(const ManagedObject_adaptor&) = delete;
|
||||
ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete;
|
||||
ManagedObject_adaptor(ManagedObject_adaptor&&) = delete;
|
||||
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = delete;
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal for this object path
|
||||
*
|
||||
@@ -344,7 +524,7 @@ namespace sdbus {
|
||||
*/
|
||||
void emitInterfacesAddedSignal()
|
||||
{
|
||||
object_->emitInterfacesAddedSignal();
|
||||
m_object.emitInterfacesAddedSignal();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -352,9 +532,9 @@ namespace sdbus {
|
||||
*
|
||||
* See IObject::emitInterfacesAddedSignal().
|
||||
*/
|
||||
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
|
||||
void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces)
|
||||
{
|
||||
object_->emitInterfacesAddedSignal(interfaces);
|
||||
m_object.emitInterfacesAddedSignal(interfaces);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -364,7 +544,7 @@ namespace sdbus {
|
||||
*/
|
||||
void emitInterfacesRemovedSignal()
|
||||
{
|
||||
object_->emitInterfacesRemovedSignal();
|
||||
m_object.emitInterfacesRemovedSignal();
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -372,15 +552,15 @@ namespace sdbus {
|
||||
*
|
||||
* See IObject::emitInterfacesRemovedSignal().
|
||||
*/
|
||||
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
|
||||
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
|
||||
{
|
||||
object_->emitInterfacesRemovedSignal(interfaces);
|
||||
m_object.emitInterfacesRemovedSignal(interfaces);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
IObject& m_object;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_STANDARDINTERFACES_H_ */
|
||||
|
||||
+367
-358
File diff suppressed because it is too large
Load Diff
+380
-72
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Types.h
|
||||
*
|
||||
@@ -29,11 +29,13 @@
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus {
|
||||
@@ -47,7 +49,7 @@ namespace sdbus {
|
||||
* Some const methods are conceptually const, but not physically const,
|
||||
* thus are not thread-safe. This is by design: normally, clients
|
||||
* should process a single Variant object in a single thread at a time.
|
||||
* Otherwise they need to take care of synchronization by themselves.
|
||||
* Otherwise, they need to take care of synchronization by themselves.
|
||||
*
|
||||
***********************************************/
|
||||
class Variant
|
||||
@@ -55,48 +57,91 @@ namespace sdbus {
|
||||
public:
|
||||
Variant();
|
||||
|
||||
template <typename _ValueType>
|
||||
Variant(const _ValueType& value)
|
||||
: Variant()
|
||||
template <typename ValueType>
|
||||
explicit Variant(const ValueType& value) : Variant()
|
||||
{
|
||||
msg_.openVariant(signature_of<_ValueType>::str());
|
||||
msg_.openVariant<ValueType>();
|
||||
msg_ << value;
|
||||
msg_.closeVariant();
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename _ValueType>
|
||||
_ValueType get() const
|
||||
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) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions): implicit conversion intentional
|
||||
: Variant()
|
||||
{
|
||||
msg_ << value;
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
ValueType get() const
|
||||
{
|
||||
_ValueType val;
|
||||
msg_.rewind(false);
|
||||
msg_.enterVariant(signature_of<_ValueType>::str());
|
||||
|
||||
msg_.enterVariant<ValueType>();
|
||||
ValueType val;
|
||||
msg_ >> val;
|
||||
msg_.exitVariant();
|
||||
return val;
|
||||
}
|
||||
|
||||
// Only allow conversion operator for true D-Bus type representations in C++
|
||||
template <typename _ValueType, typename = std::enable_if_t<signature_of<_ValueType>::is_valid>>
|
||||
operator _ValueType() const
|
||||
[[nodiscard]] std::string dumpToString() const
|
||||
{
|
||||
return get<_ValueType>();
|
||||
msg_.rewind(false);
|
||||
|
||||
return msg_.dumpToString(Message::DumpFlags::SubtreeOnly);
|
||||
}
|
||||
|
||||
template <typename _Type>
|
||||
// Only allow conversion operator for true D-Bus type representations in C++
|
||||
// NOLINTNEXTLINE(modernize-use-constraints): TODO for future: Use `requires signature_of<_ValueType>::is_valid` (when we stop supporting C++17 in public API)
|
||||
template <typename ValueType, typename = std::enable_if_t<signature_of<ValueType>::is_valid>>
|
||||
explicit operator ValueType() const
|
||||
{
|
||||
return get<ValueType>();
|
||||
}
|
||||
|
||||
template <typename... Elements>
|
||||
operator std::variant<Elements...>() const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions): implicit conversion intentional
|
||||
{
|
||||
std::variant<Elements...> result;
|
||||
msg_.rewind(false);
|
||||
msg_ >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
bool containsValueOfType() const
|
||||
{
|
||||
return signature_of<_Type>::str() == peekValueType();
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<Type>);
|
||||
return std::strcmp(signature.data(), peekValueType()) == 0;
|
||||
}
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
void serializeTo(Message& msg) const;
|
||||
void deserializeFrom(Message& msg);
|
||||
std::string peekValueType() const;
|
||||
const char* peekValueType() const;
|
||||
|
||||
private:
|
||||
mutable PlainMessage msg_{};
|
||||
mutable PlainMessage msg_;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
@@ -109,86 +154,154 @@ namespace sdbus {
|
||||
* std::tuple_size and in structured bindings.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _ValueTypes>
|
||||
template <typename... ValueTypes>
|
||||
class Struct
|
||||
: public std::tuple<_ValueTypes...>
|
||||
: public std::tuple<ValueTypes...>
|
||||
{
|
||||
public:
|
||||
using std::tuple<_ValueTypes...>::tuple;
|
||||
using std::tuple<ValueTypes...>::tuple;
|
||||
|
||||
// Disable constructor if an older then 7.1.0 version of GCC is used
|
||||
#if !((defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !(__GNUC__ > 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 0)))))
|
||||
Struct() = default;
|
||||
|
||||
explicit Struct(const std::tuple<_ValueTypes...>& t)
|
||||
: std::tuple<_ValueTypes...>(t)
|
||||
explicit Struct(const std::tuple<ValueTypes...>& tuple)
|
||||
: std::tuple<ValueTypes...>(tuple)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template <std::size_t _I>
|
||||
auto& get()
|
||||
template <std::size_t I>
|
||||
[[nodiscard]] auto& get()
|
||||
{
|
||||
return std::get<_I>(*this);
|
||||
return std::get<I>(*this);
|
||||
}
|
||||
|
||||
template <std::size_t _I>
|
||||
const auto& get() const
|
||||
template <std::size_t I>
|
||||
[[nodiscard]] const auto& get() const
|
||||
{
|
||||
return std::get<_I>(*this);
|
||||
return std::get<I>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... _Elements>
|
||||
Struct(_Elements...) -> Struct<_Elements...>;
|
||||
template <typename... Elements>
|
||||
Struct(Elements...) -> Struct<Elements...>;
|
||||
|
||||
template<typename... _Elements>
|
||||
constexpr Struct<std::decay_t<_Elements>...>
|
||||
make_struct(_Elements&&... args)
|
||||
template <typename... Elements>
|
||||
Struct(const std::tuple<Elements...>&) -> Struct<Elements...>;
|
||||
|
||||
template <typename... Elements>
|
||||
Struct(std::tuple<Elements...>&&) -> Struct<Elements...>;
|
||||
|
||||
template<typename... Elements>
|
||||
constexpr Struct<std::decay_t<Elements>...>
|
||||
make_struct(Elements&&... args)
|
||||
{
|
||||
typedef Struct<std::decay_t<_Elements>...> result_type;
|
||||
return result_type(std::forward<_Elements>(args)...);
|
||||
using result_type = Struct<std::decay_t<Elements>...>;
|
||||
return result_type(std::forward<Elements>(args)...);
|
||||
}
|
||||
|
||||
/********************************************//**
|
||||
* @class ObjectPath
|
||||
*
|
||||
* Representation of object path D-Bus type
|
||||
* Strong type representing the D-Bus object path
|
||||
*
|
||||
***********************************************/
|
||||
class ObjectPath : public std::string
|
||||
{
|
||||
public:
|
||||
using std::string::string;
|
||||
ObjectPath() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
|
||||
ObjectPath(const ObjectPath&) = default; // Fixes gcc 8.3 error (deleted copy constructor)
|
||||
ObjectPath(ObjectPath&&) = default; // Enable move - user-declared copy ctor prevents implicit creation
|
||||
ObjectPath& operator = (const ObjectPath&) = default; // Fixes gcc 8.3 error (deleted copy assignment)
|
||||
ObjectPath& operator = (ObjectPath&&) = default; // Enable move - user-declared copy assign prevents implicit creation
|
||||
ObjectPath(std::string path)
|
||||
: std::string(std::move(path))
|
||||
ObjectPath() = default;
|
||||
explicit ObjectPath(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit ObjectPath(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class BusName
|
||||
*
|
||||
* Strong type representing the D-Bus bus/service/connection name
|
||||
*
|
||||
***********************************************/
|
||||
class BusName : public std::string
|
||||
{
|
||||
public:
|
||||
BusName() = default;
|
||||
explicit BusName(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit BusName(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
using ServiceName = BusName;
|
||||
using ConnectionName = BusName;
|
||||
|
||||
/********************************************//**
|
||||
* @class InterfaceName
|
||||
*
|
||||
* Strong type representing the D-Bus interface name
|
||||
*
|
||||
***********************************************/
|
||||
class InterfaceName : public std::string
|
||||
{
|
||||
public:
|
||||
InterfaceName() = default;
|
||||
explicit InterfaceName(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit InterfaceName(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class MemberName
|
||||
*
|
||||
* Strong type representing the D-Bus member name
|
||||
*
|
||||
***********************************************/
|
||||
class MemberName : public std::string
|
||||
{
|
||||
public:
|
||||
MemberName() = default;
|
||||
explicit MemberName(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit MemberName(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
using MethodName = MemberName;
|
||||
using SignalName = MemberName;
|
||||
using PropertyName = MemberName;
|
||||
|
||||
/********************************************//**
|
||||
* @class Signature
|
||||
*
|
||||
* Representation of Signature D-Bus type
|
||||
* Strong type representing the D-Bus object path
|
||||
*
|
||||
***********************************************/
|
||||
class Signature : public std::string
|
||||
{
|
||||
public:
|
||||
using std::string::string;
|
||||
Signature() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
|
||||
Signature(const Signature&) = default; // Fixes gcc 8.3 error (deleted copy constructor)
|
||||
Signature(Signature&&) = default; // Enable move - user-declared copy ctor prevents implicit creation
|
||||
Signature& operator = (const Signature&) = default; // Fixes gcc 8.3 error (deleted copy assignment)
|
||||
Signature& operator = (Signature&&) = default; // Enable move - user-declared copy assign prevents implicit creation
|
||||
Signature(std::string path)
|
||||
: std::string(std::move(path))
|
||||
Signature() = default;
|
||||
explicit Signature(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit Signature(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
@@ -234,12 +347,12 @@ namespace sdbus {
|
||||
return *this;
|
||||
}
|
||||
|
||||
UnixFd(UnixFd&& other)
|
||||
UnixFd(UnixFd&& other) noexcept
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
UnixFd& operator=(UnixFd&& other)
|
||||
UnixFd& operator=(UnixFd&& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
@@ -255,7 +368,7 @@ namespace sdbus {
|
||||
close();
|
||||
}
|
||||
|
||||
int get() const
|
||||
[[nodiscard]] int get() const
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
@@ -275,7 +388,7 @@ namespace sdbus {
|
||||
return std::exchange(fd_, -1);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
[[nodiscard]] bool isValid() const
|
||||
{
|
||||
return fd_ >= 0;
|
||||
}
|
||||
@@ -291,16 +404,211 @@ namespace sdbus {
|
||||
int fd_ = -1;
|
||||
};
|
||||
|
||||
}
|
||||
/********************************************//**
|
||||
* @typedef DictEntry
|
||||
*
|
||||
* DictEntry is implemented as std::pair, a standard
|
||||
* value_type in STL(-like) associative containers.
|
||||
*
|
||||
***********************************************/
|
||||
template<typename T1, typename T2>
|
||||
using DictEntry = std::pair<T1, T2>;
|
||||
|
||||
template <size_t _I, typename... _ValueTypes>
|
||||
struct std::tuple_element<_I, sdbus::Struct<_ValueTypes...>>
|
||||
: std::tuple_element<_I, std::tuple<_ValueTypes...>>
|
||||
} // 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...>> // NOLINT(cert-dcl58-cpp): specialization in std namespace allowed in this case
|
||||
: std::tuple_element<I, std::tuple<ValueTypes...>>
|
||||
{};
|
||||
template <typename... ValueTypes>
|
||||
struct std::tuple_size<sdbus::Struct<ValueTypes...>> // NOLINT(cert-dcl58-cpp): specialization in std namespace allowed in this case
|
||||
: std::tuple_size<std::tuple<ValueTypes...>>
|
||||
{};
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
struct std::tuple_size<sdbus::Struct<_ValueTypes...>>
|
||||
: std::tuple_size<std::tuple<_ValueTypes...>>
|
||||
{};
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
/********************************************//**
|
||||
* @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<Struct<SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, __VA_ARGS__)>> \
|
||||
{}; \
|
||||
\
|
||||
inline auto as_dictionary_if_struct(const STRUCT& object) \
|
||||
{ \
|
||||
return as_dictionary<STRUCT>(object); \
|
||||
} \
|
||||
\
|
||||
inline Message& operator<<(Message& msg, const STRUCT& items) \
|
||||
{ \
|
||||
return msg << 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
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
#endif /* SDBUS_CXX_TYPES_H_ */
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableItems.h
|
||||
*
|
||||
* Created on: Dec 14, 2023
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CXX_VTABLEITEMS_H_
|
||||
#define SDBUS_CXX_VTABLEITEMS_H_
|
||||
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
struct MethodVTableItem
|
||||
{
|
||||
template <typename Function> MethodVTableItem& implementedAs(Function&& callback);
|
||||
MethodVTableItem& withInputParamNames(std::vector<std::string> names);
|
||||
template <typename... String> MethodVTableItem& withInputParamNames(String... names);
|
||||
MethodVTableItem& withOutputParamNames(std::vector<std::string> names);
|
||||
template <typename... String> MethodVTableItem& withOutputParamNames(String... names);
|
||||
MethodVTableItem& markAsDeprecated();
|
||||
MethodVTableItem& markAsPrivileged();
|
||||
MethodVTableItem& withNoReply();
|
||||
|
||||
MethodName name;
|
||||
Signature inputSignature;
|
||||
std::vector<std::string> inputParamNames;
|
||||
Signature outputSignature;
|
||||
std::vector<std::string> outputParamNames;
|
||||
method_callback callbackHandler;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
MethodVTableItem registerMethod(MethodName methodName);
|
||||
MethodVTableItem registerMethod(std::string methodName);
|
||||
|
||||
struct SignalVTableItem
|
||||
{
|
||||
template <typename... Args> SignalVTableItem& withParameters();
|
||||
template <typename... Args> SignalVTableItem& withParameters(std::vector<std::string> names);
|
||||
template <typename... Args, typename... String> SignalVTableItem& withParameters(String... names);
|
||||
SignalVTableItem& markAsDeprecated();
|
||||
|
||||
SignalName name;
|
||||
Signature signature;
|
||||
std::vector<std::string> paramNames;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
SignalVTableItem registerSignal(SignalName signalName);
|
||||
SignalVTableItem registerSignal(std::string signalName);
|
||||
|
||||
struct PropertyVTableItem
|
||||
{
|
||||
template <typename Function> PropertyVTableItem& withGetter(Function&& callback);
|
||||
template <typename Function> PropertyVTableItem& withSetter(Function&& callback);
|
||||
PropertyVTableItem& markAsDeprecated();
|
||||
PropertyVTableItem& markAsPrivileged();
|
||||
PropertyVTableItem& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
|
||||
|
||||
PropertyName name;
|
||||
Signature signature;
|
||||
property_get_callback getter;
|
||||
property_set_callback setter;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
PropertyVTableItem registerProperty(PropertyName propertyName);
|
||||
PropertyVTableItem registerProperty(std::string propertyName);
|
||||
|
||||
struct InterfaceFlagsVTableItem
|
||||
{
|
||||
InterfaceFlagsVTableItem& markAsDeprecated();
|
||||
InterfaceFlagsVTableItem& markAsPrivileged();
|
||||
InterfaceFlagsVTableItem& withNoReplyMethods();
|
||||
InterfaceFlagsVTableItem& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
|
||||
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
InterfaceFlagsVTableItem setInterfaceFlags();
|
||||
|
||||
using VTableItem = std::variant<MethodVTableItem, SignalVTableItem, PropertyVTableItem, InterfaceFlagsVTableItem>;
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_VTABLEITEMS_H_ */
|
||||
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableItems.inl
|
||||
*
|
||||
* Created on: Dec 14, 2023
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_VTABLEITEMS_INL_
|
||||
#define SDBUS_CPP_VTABLEITEMS_INL_
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** Method VTable Item ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
template <typename Function>
|
||||
MethodVTableItem& MethodVTableItem::implementedAs(Function&& callback)
|
||||
{
|
||||
inputSignature = signature_of_function_input_arguments_v<Function>;
|
||||
outputSignature = signature_of_function_output_arguments_v<Function>;
|
||||
callbackHandler = [callback = std::forward<Function>(callback)](MethodCall call)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<Function> inputArgs;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple.
|
||||
call >> inputArgs;
|
||||
|
||||
if constexpr (!is_async_method_v<Function>)
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
auto ret = sdbus::apply(callback, inputArgs);
|
||||
|
||||
// Store output arguments to the reply message and send it back.
|
||||
auto reply = call.createReply();
|
||||
reply << ret;
|
||||
reply.send();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple and with result object to be set later
|
||||
using AsyncResult = typename function_traits<Function>::async_result_t;
|
||||
sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
|
||||
}
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::withInputParamNames(std::vector<std::string> names)
|
||||
{
|
||||
inputParamNames = std::move(names);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... String>
|
||||
inline MethodVTableItem& MethodVTableItem::withInputParamNames(String... names)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withInputParamNames({names...});
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::withOutputParamNames(std::vector<std::string> names)
|
||||
{
|
||||
outputParamNames = std::move(names);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... String>
|
||||
inline MethodVTableItem& MethodVTableItem::withOutputParamNames(String... names)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withOutputParamNames({names...});
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::markAsPrivileged()
|
||||
{
|
||||
flags.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::withNoReply()
|
||||
{
|
||||
flags.set(Flags::METHOD_NO_REPLY);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem registerMethod(MethodName methodName)
|
||||
{
|
||||
return {std::move(methodName), {}, {}, {}, {}, {}, {}};
|
||||
}
|
||||
|
||||
inline MethodVTableItem registerMethod(std::string methodName)
|
||||
{
|
||||
return registerMethod(MethodName{std::move(methodName)});
|
||||
}
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** Signal VTable Item ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
template <typename... Args>
|
||||
inline SignalVTableItem& SignalVTableItem::withParameters()
|
||||
{
|
||||
signature = signature_of_function_input_arguments_v<void(Args...)>;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline SignalVTableItem& SignalVTableItem::withParameters(std::vector<std::string> names)
|
||||
{
|
||||
paramNames = std::move(names);
|
||||
|
||||
return withParameters<Args...>();
|
||||
}
|
||||
|
||||
template <typename... Args, typename... String>
|
||||
inline SignalVTableItem& SignalVTableItem::withParameters(String... names)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
static_assert(sizeof...(Args) == sizeof...(String), "Numbers of signal parameters and their names don't match");
|
||||
|
||||
return withParameters<Args...>({names...});
|
||||
}
|
||||
|
||||
inline SignalVTableItem& SignalVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline SignalVTableItem registerSignal(SignalName signalName)
|
||||
{
|
||||
return {std::move(signalName), {}, {}, {}};
|
||||
}
|
||||
|
||||
inline SignalVTableItem registerSignal(std::string signalName)
|
||||
{
|
||||
return registerSignal(SignalName{std::move(signalName)});
|
||||
}
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** Property VTable Item ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
template <typename Function>
|
||||
inline PropertyVTableItem& PropertyVTableItem::withGetter(Function&& callback)
|
||||
{
|
||||
static_assert(function_argument_count_v<Function> == 0, "Property getter function must not take any arguments");
|
||||
static_assert(!std::is_void_v<function_result_t<Function>>, "Property getter function must return property value");
|
||||
|
||||
if (signature.empty())
|
||||
signature = signature_of_function_output_arguments_v<Function>;
|
||||
|
||||
getter = [callback = std::forward<Function>(callback)](PropertyGetReply& reply)
|
||||
{
|
||||
// Get the propety value and serialize it into the pre-constructed reply message
|
||||
reply << callback();
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
inline PropertyVTableItem& PropertyVTableItem::withSetter(Function&& callback)
|
||||
{
|
||||
static_assert(function_argument_count_v<Function> == 1, "Property setter function must take one parameter - the property value");
|
||||
static_assert(std::is_void_v<function_result_t<Function>>, "Property setter function must not return any value");
|
||||
|
||||
if (signature.empty())
|
||||
signature = signature_of_function_input_arguments_v<Function>;
|
||||
|
||||
setter = [callback = std::forward<Function>(callback)](PropertySetCall call)
|
||||
{
|
||||
// Default-construct property value
|
||||
using property_type = function_argument_t<Function, 0>;
|
||||
std::decay_t<property_type> property;
|
||||
|
||||
// Deserialize property value from the incoming call message
|
||||
call >> property;
|
||||
|
||||
// Invoke setter with the value
|
||||
callback(property);
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem& PropertyVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem& PropertyVTableItem::markAsPrivileged()
|
||||
{
|
||||
flags.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem& PropertyVTableItem::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
|
||||
{
|
||||
flags.set(behavior);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem registerProperty(PropertyName propertyName)
|
||||
{
|
||||
return {std::move(propertyName), {}, {}, {}, {}};
|
||||
}
|
||||
|
||||
inline PropertyVTableItem registerProperty(std::string propertyName)
|
||||
{
|
||||
return registerProperty(PropertyName{std::move(propertyName)});
|
||||
}
|
||||
|
||||
/*** --------------------------- ***/
|
||||
/*** Interface Flags VTable Item ***/
|
||||
/*** --------------------------- ***/
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsPrivileged()
|
||||
{
|
||||
flags.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withNoReplyMethods()
|
||||
{
|
||||
flags.set(Flags::METHOD_NO_REPLY);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
|
||||
{
|
||||
flags.set(behavior);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem setInterfaceFlags()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CPP_VTABLEITEMS_INL_ */
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file sdbus-c++.h
|
||||
*
|
||||
@@ -24,6 +24,7 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// IWYU pragma: begin_exports
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
@@ -36,3 +37,4 @@
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
+613
-302
File diff suppressed because it is too large
Load Diff
+150
-80
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Connection.h
|
||||
*
|
||||
@@ -27,44 +27,57 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
|
||||
#include "sdbus-c++/Message.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include "ISdBus.h"
|
||||
#include SDBUS_HEADER
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include SDBUS_HEADER
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
// Forward declarations
|
||||
struct sd_event_source;
|
||||
namespace sdbus {
|
||||
class ObjectPath;
|
||||
class InterfaceName;
|
||||
class BusName;
|
||||
using ServiceName = BusName;
|
||||
class MemberName;
|
||||
using MethodName = MemberName;
|
||||
using SignalName = MemberName;
|
||||
using PropertyName = MemberName;
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Connection final
|
||||
: public sdbus::internal::IConnection
|
||||
: public IConnection
|
||||
{
|
||||
public:
|
||||
// Bus type tags
|
||||
struct default_bus_t{};
|
||||
inline static constexpr default_bus_t default_bus{};
|
||||
static constexpr default_bus_t default_bus{};
|
||||
struct system_bus_t{};
|
||||
inline static constexpr system_bus_t system_bus{};
|
||||
static constexpr system_bus_t system_bus{};
|
||||
struct session_bus_t{};
|
||||
inline static constexpr session_bus_t session_bus{};
|
||||
static constexpr session_bus_t session_bus{};
|
||||
struct custom_session_bus_t{};
|
||||
inline static constexpr custom_session_bus_t custom_session_bus{};
|
||||
static constexpr custom_session_bus_t custom_session_bus{};
|
||||
struct remote_system_bus_t{};
|
||||
inline static constexpr remote_system_bus_t remote_system_bus{};
|
||||
static constexpr remote_system_bus_t remote_system_bus{};
|
||||
struct private_bus_t{};
|
||||
inline static constexpr private_bus_t private_bus{};
|
||||
static constexpr private_bus_t private_bus{};
|
||||
struct server_bus_t{};
|
||||
inline static constexpr server_bus_t server_bus{};
|
||||
static constexpr server_bus_t server_bus{};
|
||||
struct sdbus_bus_t{}; // A bus connection created directly from existing sd_bus instance
|
||||
inline static constexpr sdbus_bus_t sdbus_bus{};
|
||||
static constexpr sdbus_bus_t sdbus_bus{};
|
||||
struct pseudo_bus_t{}; // A bus connection that is not really established with D-Bus daemon
|
||||
inline static constexpr pseudo_bus_t pseudo_bus{};
|
||||
static constexpr pseudo_bus_t pseudo_bus{};
|
||||
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, default_bus_t);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t);
|
||||
@@ -76,64 +89,96 @@ namespace sdbus::internal {
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, server_bus_t, int fd);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, sdbus_bus_t, sd_bus *bus);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t);
|
||||
Connection(const Connection&) = delete;
|
||||
Connection& operator=(const Connection&) = delete;
|
||||
Connection(Connection&&) = delete;
|
||||
Connection& operator=(Connection&&) = delete;
|
||||
~Connection() override;
|
||||
|
||||
void requestName(const std::string& name) override;
|
||||
void releaseName(const std::string& name) override;
|
||||
std::string getUniqueName() const override;
|
||||
void requestName(const ServiceName & name) override;
|
||||
void releaseName(const ServiceName& name) override;
|
||||
[[nodiscard]] BusName getUniqueName() const override;
|
||||
void enterEventLoop() override;
|
||||
void enterEventLoopAsync() override;
|
||||
void leaveEventLoop() override;
|
||||
PollData getEventLoopPollData() const override;
|
||||
bool processPendingRequest() override;
|
||||
[[nodiscard]] PollData getEventLoopPollData() const override;
|
||||
bool processPendingEvent() override;
|
||||
Message getCurrentlyProcessedMessage() const override;
|
||||
|
||||
void addObjectManager(const std::string& objectPath) override;
|
||||
void addObjectManager(const std::string& objectPath, floating_slot_t) override;
|
||||
Slot addObjectManager(const std::string& objectPath, request_slot_t) override;
|
||||
void addObjectManager(const ObjectPath& objectPath) override;
|
||||
Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) override;
|
||||
|
||||
void setMethodCallTimeout(uint64_t timeout) override;
|
||||
uint64_t getMethodCallTimeout() const override;
|
||||
[[nodiscard]] uint64_t getMethodCallTimeout() const override;
|
||||
|
||||
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback) override;
|
||||
void addMatch(const std::string& match, message_handler callback, floating_slot_t) override;
|
||||
[[nodiscard]] Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override;
|
||||
void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) override;
|
||||
void addMatch(const std::string& match, message_handler callback) override;
|
||||
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback, return_slot_t) override;
|
||||
void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override;
|
||||
[[nodiscard]] Slot addMatchAsync( const std::string& match
|
||||
, message_handler callback
|
||||
, message_handler installCallback
|
||||
, return_slot_t ) override;
|
||||
|
||||
const ISdBus& getSdBusInterface() const override;
|
||||
ISdBus& getSdBusInterface() override;
|
||||
void attachSdEventLoop(sd_event *event, int priority) override;
|
||||
void detachSdEventLoop() override;
|
||||
sd_event *getSdEventLoop() override;
|
||||
|
||||
Slot addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
Slot addObjectVTable( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData ) override;
|
||||
, void* userData
|
||||
, return_slot_t ) override;
|
||||
|
||||
PlainMessage createPlainMessage() const override;
|
||||
MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const override;
|
||||
Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const override;
|
||||
[[nodiscard]] PlainMessage createPlainMessage() const override;
|
||||
[[nodiscard]] MethodCall createMethodCall( const ServiceName& destination
|
||||
, const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const MethodName& methodName ) const override;
|
||||
[[nodiscard]] MethodCall createMethodCall( const char* destination
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* methodName ) const override;
|
||||
[[nodiscard]] Signal createSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const SignalName& signalName ) const override;
|
||||
[[nodiscard]] Signal createSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName ) const override;
|
||||
|
||||
void emitPropertiesChangedSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::vector<std::string>& propNames ) override;
|
||||
void emitInterfacesAddedSignal(const std::string& objectPath) override;
|
||||
void emitInterfacesAddedSignal( const std::string& objectPath
|
||||
, const std::vector<std::string>& interfaces ) override;
|
||||
void emitInterfacesRemovedSignal(const std::string& objectPath) override;
|
||||
void emitInterfacesRemovedSignal( const std::string& objectPath
|
||||
, const std::vector<std::string>& interfaces ) override;
|
||||
void emitPropertiesChangedSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) override;
|
||||
void emitPropertiesChangedSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) override;
|
||||
void emitInterfacesAddedSignal(const ObjectPath& objectPath) override;
|
||||
void emitInterfacesAddedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) override;
|
||||
void emitInterfacesRemovedSignal(const ObjectPath& objectPath) override;
|
||||
void emitInterfacesRemovedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) override;
|
||||
|
||||
Slot registerSignalHandler( const std::string& sender
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
Slot registerSignalHandler( const char* sender
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) override;
|
||||
, void* userData
|
||||
, return_slot_t ) override;
|
||||
|
||||
MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) 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**)>;
|
||||
@@ -143,27 +188,46 @@ namespace sdbus::internal {
|
||||
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
|
||||
BusPtr openPseudoBus();
|
||||
void finishHandshake(sd_bus* bus);
|
||||
bool waitForNextRequest();
|
||||
static std::string composeSignalMatchFilter( const std::string &sender
|
||||
, const std::string &objectPath
|
||||
, const std::string &interfaceName
|
||||
, const std::string &signalName);
|
||||
void notifyEventLoop(int fd) const;
|
||||
void notifyEventLoopToExit() const;
|
||||
void clearEventLoopNotification(int fd) const;
|
||||
void notifyEventLoopNewTimeout() const override;
|
||||
bool waitForNextEvent();
|
||||
|
||||
[[nodiscard]] bool arePendingMessagesInQueues() const;
|
||||
|
||||
void notifyEventLoopToExit();
|
||||
void notifyEventLoopToWakeUpFromPoll();
|
||||
void wakeUpEventLoopIfMessagesInQueue();
|
||||
void joinWithEventLoop();
|
||||
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
|
||||
|
||||
template <typename StringBasedType>
|
||||
static std::vector</*const */char*> to_strv(const std::vector<StringBasedType>& strings);
|
||||
|
||||
static int sdbus_match_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_match_install_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
|
||||
#ifndef SDBUS_basu // sd_event integration is not supported if instead of libsystemd we are based on basu
|
||||
static Slot createSdEventSlot(sd_event *event);
|
||||
Slot createSdTimeEventSourceSlot(sd_event *event, int priority);
|
||||
Slot createSdIoEventSourceSlot(sd_event *event, int fd, int priority);
|
||||
Slot createSdInternalEventSourceSlot(sd_event *event, int fd, int priority);
|
||||
static void deleteSdEventSource(sd_event_source *source);
|
||||
|
||||
static int onSdTimerEvent(sd_event_source *source, uint64_t usec, void *userdata);
|
||||
static int onSdIoEvent(sd_event_source *source, int fd, uint32_t revents, void *userdata);
|
||||
static int onSdInternalEvent(sd_event_source *source, int fd, uint32_t revents, void *userdata);
|
||||
static int onSdEventPrepare(sd_event_source *source, void *userdata);
|
||||
#endif
|
||||
|
||||
struct EventFd
|
||||
{
|
||||
EventFd();
|
||||
EventFd(const EventFd&) = delete;
|
||||
EventFd& operator=(const EventFd&) = delete;
|
||||
EventFd(EventFd&&) = delete;
|
||||
EventFd& operator=(EventFd&&) = delete;
|
||||
~EventFd();
|
||||
void notify();
|
||||
bool clear();
|
||||
|
||||
int fd{-1};
|
||||
};
|
||||
|
||||
@@ -171,22 +235,28 @@ namespace sdbus::internal {
|
||||
{
|
||||
message_handler callback;
|
||||
message_handler installCallback;
|
||||
Connection& connection;
|
||||
sd_bus_slot *slot;
|
||||
Connection& connection; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
Slot slot;
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<ISdBus> iface_;
|
||||
// sd-event integration
|
||||
struct SdEvent
|
||||
{
|
||||
Slot sdEvent;
|
||||
Slot sdTimeEventSource;
|
||||
Slot sdIoEventSource;
|
||||
Slot sdInternalEventSource;
|
||||
};
|
||||
|
||||
std::unique_ptr<ISdBus> sdbus_;
|
||||
BusPtr bus_;
|
||||
std::thread asyncLoopThread_;
|
||||
std::atomic<std::thread::id> loopThreadId_;
|
||||
std::mutex loopMutex_;
|
||||
EventFd loopExitFd_;
|
||||
EventFd eventFd_;
|
||||
std::atomic<uint64_t> activeTimeout_{};
|
||||
EventFd loopExitFd_; // To wake up event loop I/O polling to exit
|
||||
EventFd eventFd_; // To wake up event loop I/O polling to re-enter poll with fresh PollData values
|
||||
std::vector<Slot> floatingMatchRules_;
|
||||
std::unique_ptr<SdEvent> sdEvent_; // Integration of systemd sd-event event loop implementation
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_CONNECTION_H_ */
|
||||
|
||||
+23
-8
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Error.cpp
|
||||
*
|
||||
@@ -24,20 +24,35 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include SDBUS_HEADER
|
||||
#include "sdbus-c++/Error.h"
|
||||
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
#include SDBUS_HEADER
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
sdbus::Error createError(int errNo, const 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); };
|
||||
|
||||
std::string name(sdbusError.name);
|
||||
std::string message(customMsg + " (" + sdbusError.message + ")");
|
||||
return sdbus::Error(name, message);
|
||||
Error::Name name(sd_bus_error_is_set(&sdbusError) != 0 ? sdbusError.name : "");
|
||||
std::string message(std::move(customMsg));
|
||||
if (!message.empty() && sdbusError.message != nullptr)
|
||||
{
|
||||
message.append(" (");
|
||||
message.append(sdbusError.message);
|
||||
message.append(")");
|
||||
}
|
||||
else if (sdbusError.message != nullptr)
|
||||
{
|
||||
message = sdbusError.message;
|
||||
}
|
||||
|
||||
return {std::move(name), std::move(message)};
|
||||
}
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
+3
-7
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Flags.cpp
|
||||
*
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include SDBUS_HEADER
|
||||
#include <cstdint>
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
@@ -33,7 +34,6 @@ namespace sdbus
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
if (!flags_.test(Flags::PRIVILEGED))
|
||||
@@ -55,7 +55,6 @@ namespace sdbus
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
if (!flags_.test(Flags::PRIVILEGED))
|
||||
@@ -70,7 +69,6 @@ namespace sdbus
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
|
||||
@@ -81,7 +79,6 @@ namespace sdbus
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
//if (!flags_.test(Flags::PRIVILEGED))
|
||||
@@ -103,10 +100,9 @@ namespace sdbus
|
||||
{
|
||||
auto sdbusFlags = toSdBusPropertyFlags();
|
||||
|
||||
using namespace sdbus;
|
||||
if (!flags_.test(Flags::PRIVILEGED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_UNPRIVILEGED;
|
||||
|
||||
return sdbusFlags;
|
||||
}
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
+75
-44
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file IConnection.h
|
||||
*
|
||||
@@ -27,23 +27,33 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_ICONNECTION_H_
|
||||
#define SDBUS_CXX_INTERNAL_ICONNECTION_H_
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include SDBUS_HEADER
|
||||
#include <string>
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
|
||||
#include "sdbus-c++/TypeTraits.h"
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include SDBUS_HEADER
|
||||
#include <vector>
|
||||
|
||||
// Forward declaration
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
class PlainMessage;
|
||||
class ObjectPath;
|
||||
class InterfaceName;
|
||||
class BusName;
|
||||
using ServiceName = BusName;
|
||||
class MemberName;
|
||||
using MethodName = MemberName;
|
||||
using SignalName = MemberName;
|
||||
using PropertyName = MemberName;
|
||||
class Error;
|
||||
namespace internal {
|
||||
class ISdBus;
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
@@ -53,50 +63,71 @@ namespace sdbus::internal {
|
||||
public:
|
||||
~IConnection() override = default;
|
||||
|
||||
virtual const ISdBus& getSdBusInterface() const = 0;
|
||||
virtual ISdBus& getSdBusInterface() = 0;
|
||||
|
||||
[[nodiscard]] virtual Slot addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
[[nodiscard]] virtual Slot addObjectVTable( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData ) = 0;
|
||||
, void* userData
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
virtual PlainMessage createPlainMessage() const = 0;
|
||||
virtual MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
virtual Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
[[nodiscard]] virtual PlainMessage createPlainMessage() const = 0;
|
||||
[[nodiscard]] virtual MethodCall createMethodCall( const ServiceName& destination
|
||||
, const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const MethodName& methodName ) const = 0;
|
||||
[[nodiscard]] virtual MethodCall createMethodCall( const char* destination
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* methodName ) const = 0;
|
||||
[[nodiscard]] virtual Signal createSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const SignalName& signalName ) const = 0;
|
||||
[[nodiscard]] virtual Signal createSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName ) const = 0;
|
||||
|
||||
virtual void emitPropertiesChangedSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::vector<std::string>& propNames ) = 0;
|
||||
virtual void emitInterfacesAddedSignal(const std::string& objectPath) = 0;
|
||||
virtual void emitInterfacesAddedSignal( const std::string& objectPath
|
||||
, const std::vector<std::string>& interfaces ) = 0;
|
||||
virtual void emitInterfacesRemovedSignal(const std::string& objectPath) = 0;
|
||||
virtual void emitInterfacesRemovedSignal( const std::string& objectPath
|
||||
, const std::vector<std::string>& interfaces ) = 0;
|
||||
virtual void emitPropertiesChangedSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) = 0;
|
||||
virtual void emitPropertiesChangedSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) = 0;
|
||||
virtual void emitInterfacesAddedSignal(const ObjectPath& objectPath) = 0;
|
||||
virtual void emitInterfacesAddedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) = 0;
|
||||
virtual void emitInterfacesRemovedSignal(const ObjectPath& objectPath) = 0;
|
||||
virtual void emitInterfacesRemovedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) = 0;
|
||||
|
||||
using sdbus::IConnection::addObjectManager;
|
||||
[[nodiscard]] virtual Slot addObjectManager(const std::string& objectPath, request_slot_t) = 0;
|
||||
|
||||
[[nodiscard]] virtual Slot registerSignalHandler( const std::string& sender
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
[[nodiscard]] virtual Slot registerSignalHandler( const char* sender
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) = 0;
|
||||
, void* userData
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
virtual void notifyEventLoopNewTimeout() const = 0;
|
||||
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 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> createConnection();
|
||||
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createPseudoConnection();
|
||||
[[nodiscard]] std::unique_ptr<IConnection> createPseudoConnection();
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_ICONNECTION_H_ */
|
||||
|
||||
+27
-24
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file ISdBus.h
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
@@ -44,18 +44,18 @@ namespace sdbus::internal {
|
||||
|
||||
virtual ~ISdBus() = default;
|
||||
|
||||
virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) = 0;
|
||||
virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) = 0;
|
||||
virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *msg) = 0;
|
||||
virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *msg) = 0;
|
||||
|
||||
virtual int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) = 0;
|
||||
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) = 0;
|
||||
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) = 0;
|
||||
virtual int sd_bus_send(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie) = 0;
|
||||
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) = 0;
|
||||
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) = 0;
|
||||
|
||||
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type) = 0;
|
||||
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) = 0;
|
||||
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) = 0;
|
||||
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) = 0;
|
||||
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) = 0;
|
||||
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **msg, uint8_t type) = 0;
|
||||
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member) = 0;
|
||||
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member) = 0;
|
||||
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **msg) = 0;
|
||||
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err) = 0;
|
||||
|
||||
virtual int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) = 0;
|
||||
virtual int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) = 0;
|
||||
@@ -81,32 +81,35 @@ namespace sdbus::internal {
|
||||
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) = 0;
|
||||
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0;
|
||||
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) = 0;
|
||||
virtual int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata) = 0;
|
||||
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
|
||||
|
||||
virtual int sd_bus_new(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_start(sd_bus *bus) = 0;
|
||||
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0;
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **ret) = 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(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;
|
||||
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) = 0;
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *msg, 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_unref(sd_bus_creds *c) = 0;
|
||||
virtual int sd_bus_query_sender_creds(sd_bus_message *msg, uint64_t mask, sd_bus_creds **creds) = 0;
|
||||
virtual sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *creds) = 0;
|
||||
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *creds) = 0;
|
||||
|
||||
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) = 0;
|
||||
virtual int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) = 0;
|
||||
virtual int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *uid) = 0;
|
||||
virtual int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) = 0;
|
||||
virtual int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) = 0;
|
||||
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) = 0;
|
||||
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) = 0;
|
||||
virtual int sd_bus_creds_get_pid(sd_bus_creds *creds, pid_t *pid) = 0;
|
||||
virtual int sd_bus_creds_get_uid(sd_bus_creds *creds, uid_t *uid) = 0;
|
||||
virtual int sd_bus_creds_get_euid(sd_bus_creds *creds, uid_t *uid) = 0;
|
||||
virtual int sd_bus_creds_get_gid(sd_bus_creds *creds, gid_t *gid) = 0;
|
||||
virtual int sd_bus_creds_get_egid(sd_bus_creds *creds, gid_t *egid) = 0;
|
||||
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *creds, const gid_t **gids) = 0;
|
||||
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *creds, const char **label) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#endif //SDBUS_CXX_ISDBUS_H
|
||||
|
||||
+243
-214
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Message.cpp
|
||||
*
|
||||
@@ -24,39 +24,54 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "MessageUtils.h"
|
||||
#include "ISdBus.h"
|
||||
#include "sdbus-c++/Message.h"
|
||||
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/Types.h"
|
||||
#include "sdbus-c++/TypeTraits.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include SDBUS_HEADER
|
||||
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdint> // int16_t, uint64_t, ...
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // atexit
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/types.h> // pid_t, gid_t, ...
|
||||
#include <tuple> // std::ignore
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <utility> // std::move
|
||||
#include <vector>
|
||||
#include SDBUS_HEADER
|
||||
|
||||
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(static_cast<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
|
||||
@@ -66,14 +81,17 @@ Message::Message(const Message& other) noexcept
|
||||
|
||||
Message& Message::operator=(const Message& other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
if (msg_)
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount(static_cast<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(static_cast<sd_bus_message*>(msg_));
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -86,12 +104,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(static_cast<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;
|
||||
|
||||
@@ -101,14 +119,18 @@ Message& Message::operator=(Message&& other) noexcept
|
||||
Message::~Message()
|
||||
{
|
||||
if (msg_)
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
Message& Message::operator<<(bool item)
|
||||
{
|
||||
int intItem = item;
|
||||
int itemAsInt = static_cast<int>(item);
|
||||
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
|
||||
// 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(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BOOLEAN, &itemAsInt);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a bool value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -116,7 +138,7 @@ Message& Message::operator<<(bool item)
|
||||
|
||||
Message& Message::operator<<(int16_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT16, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT16, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int16_t value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -124,7 +146,7 @@ Message& Message::operator<<(int16_t item)
|
||||
|
||||
Message& Message::operator<<(int32_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT32, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT32, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int32_t value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -132,7 +154,7 @@ Message& Message::operator<<(int32_t item)
|
||||
|
||||
Message& Message::operator<<(int64_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT64, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT64, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int64_t value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -140,7 +162,7 @@ Message& Message::operator<<(int64_t item)
|
||||
|
||||
Message& Message::operator<<(uint8_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BYTE, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BYTE, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a byte value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -148,7 +170,7 @@ Message& Message::operator<<(uint8_t item)
|
||||
|
||||
Message& Message::operator<<(uint16_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT16, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT16, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint16_t value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -156,7 +178,7 @@ Message& Message::operator<<(uint16_t item)
|
||||
|
||||
Message& Message::operator<<(uint32_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT32, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT32, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint32_t value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -164,7 +186,7 @@ Message& Message::operator<<(uint32_t item)
|
||||
|
||||
Message& Message::operator<<(uint64_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT64, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT64, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint64_t value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -172,7 +194,7 @@ Message& Message::operator<<(uint64_t item)
|
||||
|
||||
Message& Message::operator<<(double item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DOUBLE, &item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a double value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -180,7 +202,7 @@ Message& Message::operator<<(double item)
|
||||
|
||||
Message& Message::operator<<(const char* item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, item);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRING, item);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a C-string value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -188,12 +210,23 @@ Message& Message::operator<<(const char* item)
|
||||
|
||||
Message& Message::operator<<(const std::string& item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, item.c_str());
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRING, item.c_str());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a string value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator<<(std::string_view item)
|
||||
{
|
||||
char* destPtr{};
|
||||
auto r = sd_bus_message_append_string_space(static_cast<sd_bus_message*>(msg_), item.length(), &destPtr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a string_view value", -r);
|
||||
|
||||
std::memcpy(destPtr, item.data(), item.length());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator<<(const Variant &item)
|
||||
{
|
||||
item.serializeTo(*this);
|
||||
@@ -203,7 +236,7 @@ Message& Message::operator<<(const Variant &item)
|
||||
|
||||
Message& Message::operator<<(const ObjectPath &item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_OBJECT_PATH, item.c_str());
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_OBJECT_PATH, item.c_str());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an ObjectPath value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -211,7 +244,7 @@ Message& Message::operator<<(const ObjectPath &item)
|
||||
|
||||
Message& Message::operator<<(const Signature &item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, item.c_str());
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_SIGNATURE, item.c_str());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a Signature value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -220,7 +253,7 @@ Message& Message::operator<<(const Signature &item)
|
||||
Message& Message::operator<<(const UnixFd &item)
|
||||
{
|
||||
auto fd = item.get();
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
|
||||
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UNIX_FD, &fd);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a UnixFd value", -r);
|
||||
|
||||
return *this;
|
||||
@@ -228,7 +261,7 @@ Message& Message::operator<<(const UnixFd &item)
|
||||
|
||||
Message& Message::appendArray(char type, const void *ptr, size_t size)
|
||||
{
|
||||
auto r = sd_bus_message_append_array((sd_bus_message*)msg_, type, ptr, size);
|
||||
auto r = sd_bus_message_append_array(static_cast<sd_bus_message*>(msg_), type, ptr, size);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an array", -r);
|
||||
|
||||
return *this;
|
||||
@@ -236,8 +269,8 @@ Message& Message::appendArray(char type, const void *ptr, size_t size)
|
||||
|
||||
Message& Message::operator>>(bool& item)
|
||||
{
|
||||
int intItem;
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
|
||||
int intItem{};
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BOOLEAN, &intItem);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -250,7 +283,7 @@ Message& Message::operator>>(bool& item)
|
||||
|
||||
Message& Message::operator>>(int16_t& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT16, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT16, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -261,7 +294,7 @@ Message& Message::operator>>(int16_t& item)
|
||||
|
||||
Message& Message::operator>>(int32_t& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT32, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT32, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -272,7 +305,7 @@ Message& Message::operator>>(int32_t& item)
|
||||
|
||||
Message& Message::operator>>(int64_t& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT64, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT64, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -283,7 +316,7 @@ Message& Message::operator>>(int64_t& item)
|
||||
|
||||
Message& Message::operator>>(uint8_t& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BYTE, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BYTE, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -294,7 +327,7 @@ Message& Message::operator>>(uint8_t& item)
|
||||
|
||||
Message& Message::operator>>(uint16_t& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT16, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT16, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -305,7 +338,7 @@ Message& Message::operator>>(uint16_t& item)
|
||||
|
||||
Message& Message::operator>>(uint32_t& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT32, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT32, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -316,7 +349,7 @@ Message& Message::operator>>(uint32_t& item)
|
||||
|
||||
Message& Message::operator>>(uint64_t& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT64, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT64, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -327,7 +360,7 @@ Message& Message::operator>>(uint64_t& item)
|
||||
|
||||
Message& Message::readArray(char type, const void **ptr, size_t *size)
|
||||
{
|
||||
auto r = sd_bus_message_read_array((sd_bus_message*)msg_, type, ptr, size);
|
||||
auto r = sd_bus_message_read_array(static_cast<sd_bus_message*>(msg_), type, ptr, size);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -338,7 +371,7 @@ Message& Message::readArray(char type, const void **ptr, size_t *size)
|
||||
|
||||
Message& Message::operator>>(double& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DOUBLE, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -349,7 +382,7 @@ Message& Message::operator>>(double& item)
|
||||
|
||||
Message& Message::operator>>(char*& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, &item);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRING, reinterpret_cast<void*>(&item));
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -385,7 +418,7 @@ Message& Message::operator>>(Variant &item)
|
||||
Message& Message::operator>>(ObjectPath &item)
|
||||
{
|
||||
char* str{};
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_OBJECT_PATH, &str);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_OBJECT_PATH, reinterpret_cast<void*>(&str));
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -400,7 +433,7 @@ Message& Message::operator>>(ObjectPath &item)
|
||||
Message& Message::operator>>(Signature &item)
|
||||
{
|
||||
char* str{};
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, &str);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_SIGNATURE, reinterpret_cast<void*>(&str));
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -415,7 +448,7 @@ Message& Message::operator>>(Signature &item)
|
||||
Message& Message::operator>>(UnixFd &item)
|
||||
{
|
||||
int fd = -1;
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
|
||||
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UNIX_FD, &fd);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -426,10 +459,9 @@ Message& Message::operator>>(UnixFd &item)
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Message& Message::openContainer(const std::string& signature)
|
||||
Message& Message::openContainer(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
|
||||
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_ARRAY, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a container", -r);
|
||||
|
||||
return *this;
|
||||
@@ -437,15 +469,15 @@ Message& Message::openContainer(const std::string& signature)
|
||||
|
||||
Message& Message::closeContainer()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a container", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::openDictEntry(const std::string& signature)
|
||||
Message& Message::openDictEntry(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
|
||||
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DICT_ENTRY, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
@@ -453,15 +485,15 @@ Message& Message::openDictEntry(const std::string& signature)
|
||||
|
||||
Message& Message::closeDictEntry()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::openVariant(const std::string& signature)
|
||||
Message& Message::openVariant(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
|
||||
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_VARIANT, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a variant", -r);
|
||||
|
||||
return *this;
|
||||
@@ -469,15 +501,15 @@ Message& Message::openVariant(const std::string& signature)
|
||||
|
||||
Message& Message::closeVariant()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a variant", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::openStruct(const std::string& signature)
|
||||
Message& Message::openStruct(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
|
||||
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRUCT, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a struct", -r);
|
||||
|
||||
return *this;
|
||||
@@ -485,16 +517,15 @@ Message& Message::openStruct(const std::string& signature)
|
||||
|
||||
Message& Message::closeStruct()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a struct", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Message& Message::enterContainer(const std::string& signature)
|
||||
Message& Message::enterContainer(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_ARRAY, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -505,15 +536,15 @@ Message& Message::enterContainer(const std::string& signature)
|
||||
|
||||
Message& Message::exitContainer()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a container", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::enterDictEntry(const std::string& signature)
|
||||
Message& Message::enterDictEntry(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DICT_ENTRY, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -524,15 +555,15 @@ Message& Message::enterDictEntry(const std::string& signature)
|
||||
|
||||
Message& Message::exitDictEntry()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::enterVariant(const std::string& signature)
|
||||
Message& Message::enterVariant(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_VARIANT, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -543,15 +574,15 @@ Message& Message::enterVariant(const std::string& signature)
|
||||
|
||||
Message& Message::exitVariant()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a variant", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::enterStruct(const std::string& signature)
|
||||
Message& Message::enterStruct(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRUCT, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@@ -562,7 +593,7 @@ Message& Message::enterStruct(const std::string& signature)
|
||||
|
||||
Message& Message::exitStruct()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a struct", -r);
|
||||
|
||||
return *this;
|
||||
@@ -581,7 +612,7 @@ void Message::clearFlags()
|
||||
|
||||
void Message::copyTo(Message& destination, bool complete) const
|
||||
{
|
||||
auto r = sd_bus_message_copy((sd_bus_message*)destination.msg_, (sd_bus_message*)msg_, complete);
|
||||
auto r = sd_bus_message_copy(static_cast<sd_bus_message*>(destination.msg_), static_cast<sd_bus_message*>(msg_), complete); // NOLINT(readability-implicit-bool-conversion)
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to copy the message", -r);
|
||||
}
|
||||
|
||||
@@ -589,158 +620,186 @@ void Message::seal()
|
||||
{
|
||||
const auto messageCookie = 1;
|
||||
const auto sealTimeout = 0;
|
||||
auto r = sd_bus_message_seal((sd_bus_message*)msg_, messageCookie, sealTimeout);
|
||||
auto r = sd_bus_message_seal(static_cast<sd_bus_message*>(msg_), messageCookie, sealTimeout);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to seal the message", -r);
|
||||
}
|
||||
|
||||
void Message::rewind(bool complete)
|
||||
{
|
||||
auto r = sd_bus_message_rewind((sd_bus_message*)msg_, complete);
|
||||
auto r = sd_bus_message_rewind(static_cast<sd_bus_message*>(msg_), complete); // NOLINT(readability-implicit-bool-conversion)
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
|
||||
}
|
||||
|
||||
std::string Message::getInterfaceName() const
|
||||
std::string Message::dumpToString(DumpFlags flags) const
|
||||
{
|
||||
auto interface = sd_bus_message_get_interface((sd_bus_message*)msg_);
|
||||
return interface != nullptr ? interface : "";
|
||||
#if LIBSYSTEMD_VERSION>=245 && !defined(SDBUS_basu)
|
||||
char* buffer{};
|
||||
SCOPE_EXIT{ free(buffer); }; // NOLINT(cppcoreguidelines-no-malloc,hicpp-no-malloc,cppcoreguidelines-owning-memory)
|
||||
size_t size{};
|
||||
|
||||
const std::unique_ptr<FILE, int(*)(FILE*)> stream{open_memstream(&buffer, &size), fclose};
|
||||
SDBUS_THROW_ERROR_IF(!stream, "Failed to open memory stream", errno);
|
||||
|
||||
auto r = sd_bus_message_dump(static_cast<sd_bus_message*>(msg_), stream.get(), static_cast<uint64_t>(flags));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to dump the message", -r);
|
||||
|
||||
(void)fflush(stream.get());
|
||||
|
||||
return {buffer, size};
|
||||
#else
|
||||
(void)flags;
|
||||
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Dumping sd-bus message not supported by underlying version of libsystemd");
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Message::getMemberName() const
|
||||
const char* Message::getInterfaceName() const
|
||||
{
|
||||
auto member = sd_bus_message_get_member((sd_bus_message*)msg_);
|
||||
return member != nullptr ? member : "";
|
||||
return sd_bus_message_get_interface(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
std::string Message::getSender() const
|
||||
const char* Message::getMemberName() const
|
||||
{
|
||||
return sd_bus_message_get_sender((sd_bus_message*)msg_);
|
||||
return sd_bus_message_get_member(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
std::string Message::getPath() const
|
||||
const char* Message::getSender() const
|
||||
{
|
||||
auto path = sd_bus_message_get_path((sd_bus_message*)msg_);
|
||||
return path != nullptr ? path : "";
|
||||
return sd_bus_message_get_sender(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
std::string Message::getDestination() const
|
||||
const char* Message::getPath() const
|
||||
{
|
||||
auto destination = sd_bus_message_get_destination((sd_bus_message*)msg_);
|
||||
return destination != nullptr ? destination : "";
|
||||
return sd_bus_message_get_path(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
void Message::peekType(std::string& type, std::string& contents) const
|
||||
const char* Message::getDestination() const
|
||||
{
|
||||
char typeSig;
|
||||
const char* contentsSig;
|
||||
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig);
|
||||
return sd_bus_message_get_destination(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
uint64_t Message::getCookie() const
|
||||
{
|
||||
uint64_t cookie{};
|
||||
auto r = sd_bus_message_get_cookie(static_cast<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{};
|
||||
const char* contentsSignature{};
|
||||
auto r = sd_bus_message_peek_type(static_cast<sd_bus_message*>(msg_), &typeSignature, &contentsSignature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r);
|
||||
type = typeSig;
|
||||
contents = contentsSig ? contentsSig : "";
|
||||
return {typeSignature, contentsSignature};
|
||||
}
|
||||
|
||||
bool Message::isValid() const
|
||||
{
|
||||
return msg_ != nullptr && sdbus_ != nullptr;
|
||||
return msg_ != nullptr && connection_ != nullptr;
|
||||
}
|
||||
|
||||
bool Message::isEmpty() const
|
||||
{
|
||||
return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0;
|
||||
return sd_bus_message_is_empty(static_cast<sd_bus_message*>(msg_)) != 0;
|
||||
}
|
||||
|
||||
bool Message::isAtEnd(bool complete) const
|
||||
{
|
||||
return sd_bus_message_at_end((sd_bus_message*)msg_, complete) > 0;
|
||||
return sd_bus_message_at_end(static_cast<sd_bus_message*>(msg_), complete) > 0; // NOLINT(readability-implicit-bool-conversion)
|
||||
}
|
||||
|
||||
// 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;
|
||||
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(static_cast<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;
|
||||
}
|
||||
|
||||
uid_t Message::getCredsUid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_UID | SD_BUS_CREDS_AUGMENT;
|
||||
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(static_cast<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);
|
||||
auto uid = static_cast<uid_t>(-1);
|
||||
r = sd_bus_creds_get_uid(creds, &uid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred uid", -r);
|
||||
return uid;
|
||||
}
|
||||
|
||||
uid_t Message::getCredsEuid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_EUID | SD_BUS_CREDS_AUGMENT;
|
||||
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(static_cast<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);
|
||||
auto euid = static_cast<uid_t>(-1);
|
||||
r = sd_bus_creds_get_euid(creds, &euid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred euid", -r);
|
||||
return euid;
|
||||
}
|
||||
|
||||
gid_t Message::getCredsGid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_GID | SD_BUS_CREDS_AUGMENT;
|
||||
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(static_cast<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);
|
||||
auto gid = static_cast<gid_t>(-1);
|
||||
r = sd_bus_creds_get_gid(creds, &gid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred gid", -r);
|
||||
return gid;
|
||||
}
|
||||
|
||||
gid_t Message::getCredsEgid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_EGID | SD_BUS_CREDS_AUGMENT;
|
||||
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(static_cast<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);
|
||||
auto egid = static_cast<gid_t>(-1);
|
||||
r = sd_bus_creds_get_egid(creds, &egid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred egid", -r);
|
||||
return egid;
|
||||
}
|
||||
|
||||
std::vector<gid_t> Message::getCredsSupplementaryGids() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_SUPPLEMENTARY_GIDS | SD_BUS_CREDS_AUGMENT;
|
||||
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(static_cast<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{};
|
||||
if (cGids != nullptr)
|
||||
{
|
||||
for (int i = 0; i < r; i++)
|
||||
gids.push_back(cGids[i]);
|
||||
gids.push_back(cGids[i]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
|
||||
return gids;
|
||||
@@ -748,38 +807,35 @@ std::vector<gid_t> Message::getCredsSupplementaryGids() const
|
||||
|
||||
std::string Message::getSELinuxContext() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_AUGMENT | SD_BUS_CREDS_SELINUX_CONTEXT;
|
||||
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(static_cast<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
|
||||
, const internal::IConnection *connection
|
||||
, internal::IConnection *connection
|
||||
, adopt_message_t) noexcept
|
||||
: Message(msg, sdbus, adopt_message)
|
||||
, connection_(connection)
|
||||
: Message(msg, connection, adopt_message)
|
||||
{
|
||||
assert(connection_ != nullptr);
|
||||
}
|
||||
|
||||
void MethodCall::dontExpectReply()
|
||||
{
|
||||
auto r = sd_bus_message_set_expect_reply((sd_bus_message*)msg_, 0);
|
||||
auto r = sd_bus_message_set_expect_reply(static_cast<sd_bus_message*>(msg_), 0);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set the dont-expect-reply flag", -r);
|
||||
}
|
||||
|
||||
bool MethodCall::doesntExpectReply() const
|
||||
{
|
||||
auto r = sd_bus_message_get_expect_reply((sd_bus_message*)msg_);
|
||||
auto r = sd_bus_message_get_expect_reply(static_cast<sd_bus_message*>(msg_));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get the dont-expect-reply flag", -r);
|
||||
return r == 0;
|
||||
}
|
||||
@@ -788,100 +844,69 @@ MethodReply MethodCall::send(uint64_t timeout) const
|
||||
{
|
||||
if (!doesntExpectReply())
|
||||
return sendWithReply(timeout);
|
||||
else
|
||||
return sendWithNoReply();
|
||||
|
||||
return sendWithNoReply();
|
||||
}
|
||||
|
||||
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(static_cast<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 sdbus::Error(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(static_cast<sd_bus_message*>(msg_));
|
||||
|
||||
return Factory::create<MethodReply>(); // No reply
|
||||
}
|
||||
|
||||
void MethodCall::send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const
|
||||
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout, return_slot_t) const // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
MethodCall::send(callback, userData, timeout, floating_slot);
|
||||
}
|
||||
|
||||
void MethodCall::send(void* callback, void* userData, uint64_t timeout, floating_slot_t) const
|
||||
{
|
||||
auto r = sdbus_->sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
|
||||
|
||||
// Force event loop to re-enter polling with the async call timeout if that is less than the one used in current poll
|
||||
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
|
||||
connection_->notifyEventLoopNewTimeout();
|
||||
}
|
||||
|
||||
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout) 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);
|
||||
|
||||
// Force event loop to re-enter polling with the async call timeout if that is less than the one used in current poll
|
||||
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
|
||||
connection_->notifyEventLoopNewTimeout();
|
||||
|
||||
return {slot, [sdbus_ = sdbus_](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
return connection_->callMethodAsync(static_cast<sd_bus_message*>(msg_), reinterpret_cast<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(static_cast<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(static_cast<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(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
uint64_t MethodReply::getReplyCookie() const
|
||||
{
|
||||
uint64_t cookie{};
|
||||
auto r = sd_bus_message_get_reply_cookie(static_cast<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(static_cast<sd_bus_message*>(msg_));
|
||||
}
|
||||
|
||||
void Signal::setDestination(const std::string& destination)
|
||||
{
|
||||
auto r = sdbus_->sd_bus_message_set_destination((sd_bus_message*)msg_, destination.c_str());
|
||||
setDestination(destination.c_str());
|
||||
}
|
||||
|
||||
void Signal::setDestination(const char* destination)
|
||||
{
|
||||
auto r = sd_bus_message_set_destination(static_cast<sd_bus_message*>(msg_), destination);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
|
||||
}
|
||||
|
||||
@@ -897,27 +922,31 @@ namespace {
|
||||
// Please note that the solution is NOT thread-safe.
|
||||
// Another common solution is global sdbus-c++ startup/shutdown functions, but that would be an intrusive change.
|
||||
|
||||
/*constinit (C++20 keyword) */ static bool pseudoConnectionDestroyed{};
|
||||
#ifdef __cpp_constinit
|
||||
constinit bool pseudoConnectionDestroyed{}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
#else
|
||||
bool pseudoConnectionDestroyed{}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
#endif
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection, void(*)(sdbus::internal::IConnection*)> createPseudoConnection()
|
||||
std::unique_ptr<internal::IConnection, void(*)(internal::IConnection*)> createPseudoConnection()
|
||||
{
|
||||
auto deleter = [](sdbus::internal::IConnection* con)
|
||||
auto deleter = [](internal::IConnection* con)
|
||||
{
|
||||
delete con;
|
||||
delete con; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
pseudoConnectionDestroyed = true;
|
||||
};
|
||||
|
||||
return {internal::createPseudoConnection().release(), std::move(deleter)};
|
||||
}
|
||||
|
||||
sdbus::internal::IConnection& getPseudoConnectionInstance()
|
||||
internal::IConnection& getPseudoConnectionInstance()
|
||||
{
|
||||
static auto connection = createPseudoConnection();
|
||||
|
||||
if (pseudoConnectionDestroyed)
|
||||
{
|
||||
connection = createPseudoConnection(); // Phoenix rising from the ashes
|
||||
atexit([](){ connection.~unique_ptr(); }); // We have to manually take care of deleting the phoenix
|
||||
std::ignore = atexit([](){ connection.~unique_ptr(); }); // We have to manually take care of deleting the phoenix
|
||||
pseudoConnectionDestroyed = false;
|
||||
}
|
||||
|
||||
@@ -926,7 +955,7 @@ sdbus::internal::IConnection& getPseudoConnectionInstance()
|
||||
return *connection;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
PlainMessage createPlainMessage()
|
||||
{
|
||||
@@ -934,8 +963,8 @@ 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();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
+14
-22
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file MessageUtils.h
|
||||
*
|
||||
@@ -34,38 +34,30 @@ namespace sdbus
|
||||
class Message::Factory
|
||||
{
|
||||
public:
|
||||
template<typename _Msg>
|
||||
static _Msg create()
|
||||
template<typename Msg>
|
||||
static Msg create()
|
||||
{
|
||||
return _Msg{};
|
||||
return Msg{};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg)
|
||||
template<typename Msg>
|
||||
static Msg create(void *msg)
|
||||
{
|
||||
return _Msg{msg};
|
||||
return Msg{msg};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg, internal::ISdBus* sdbus)
|
||||
template<typename Msg>
|
||||
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)
|
||||
template<typename Msg>
|
||||
static Msg create(void *msg, internal::IConnection* connection, adopt_message_t)
|
||||
{
|
||||
return _Msg{msg, sdbus, adopt_message};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t)
|
||||
{
|
||||
return _Msg{msg, sdbus, connection, adopt_message};
|
||||
return Msg{msg, connection, adopt_message};
|
||||
}
|
||||
};
|
||||
|
||||
PlainMessage createPlainMessage();
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */
|
||||
|
||||
+230
-240
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Object.cpp
|
||||
*
|
||||
@@ -25,172 +25,105 @@
|
||||
*/
|
||||
|
||||
#include "Object.h"
|
||||
#include "MessageUtils.h"
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/Flags.h"
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
#include "sdbus-c++/IObject.h"
|
||||
#include "sdbus-c++/Message.h"
|
||||
#include "sdbus-c++/TypeTraits.h"
|
||||
#include "sdbus-c++/VTableItems.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "Utils.h"
|
||||
#include "VTableUtils.h"
|
||||
#include SDBUS_HEADER
|
||||
#include <utility>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <memory>
|
||||
#include SDBUS_HEADER
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
|
||||
Object::Object(IConnection& connection, ObjectPath objectPath)
|
||||
: connection_(connection), objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_);
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, std::string outputSignature
|
||||
, method_callback methodCallback
|
||||
, Flags flags )
|
||||
void Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
|
||||
{
|
||||
registerMethod( interfaceName
|
||||
, std::move(methodName)
|
||||
, std::move(inputSignature)
|
||||
, {}
|
||||
, std::move(outputSignature)
|
||||
, {}
|
||||
, std::move(methodCallback)
|
||||
, std::move(flags) );
|
||||
auto slot = Object::addVTable(std::move(interfaceName), std::move(vtable), return_slot);
|
||||
|
||||
vtables_.push_back(std::move(slot));
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, const std::vector<std::string>& inputNames
|
||||
, std::string outputSignature
|
||||
, const std::vector<std::string>& outputNames
|
||||
, method_callback methodCallback
|
||||
, Flags flags )
|
||||
Slot Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t)
|
||||
{
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
|
||||
SDBUS_CHECK_MEMBER_NAME(methodName);
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName.c_str());
|
||||
|
||||
auto& interface = getInterface(interfaceName);
|
||||
InterfaceData::MethodData methodData{ std::move(inputSignature)
|
||||
, std::move(outputSignature)
|
||||
, paramNamesToString(inputNames) + paramNamesToString(outputNames)
|
||||
, std::move(methodCallback)
|
||||
, std::move(flags) };
|
||||
auto inserted = interface.methods.emplace(std::move(methodName), std::move(methodData)).second;
|
||||
// 1st pass -- create vtable structure for internal sdbus-c++ purposes
|
||||
auto internalVTable = std::make_unique<VTable>(createInternalVTable(std::move(interfaceName), std::move(vtable)));
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
}
|
||||
// 2nd pass -- from internal sdbus-c++ vtable, create vtable structure in format expected by underlying sd-bus library
|
||||
internalVTable->sdbusVTable = createInternalSdBusVTable(*internalVTable);
|
||||
|
||||
void Object::registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, Flags flags )
|
||||
{
|
||||
registerSignal(interfaceName, std::move(signalName), std::move(signature), {}, std::move(flags));
|
||||
}
|
||||
// 3rd step -- register the vtable with sd-bus
|
||||
internalVTable->slot = connection_.addObjectVTable( objectPath_
|
||||
, internalVTable->interfaceName
|
||||
, internalVTable->sdbusVTable.data()
|
||||
, internalVTable.get()
|
||||
, return_slot );
|
||||
|
||||
void Object::registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, const std::vector<std::string>& paramNames
|
||||
, Flags flags )
|
||||
{
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
|
||||
SDBUS_CHECK_MEMBER_NAME(signalName);
|
||||
|
||||
auto& interface = getInterface(interfaceName);
|
||||
|
||||
InterfaceData::SignalData signalData{std::move(signature), paramNamesToString(paramNames), std::move(flags)};
|
||||
auto inserted = interface.signals.emplace(std::move(signalName), std::move(signalData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, Flags flags )
|
||||
{
|
||||
registerProperty( interfaceName
|
||||
, std::move(propertyName)
|
||||
, std::move(signature)
|
||||
, std::move(getCallback)
|
||||
, {}
|
||||
, std::move(flags) );
|
||||
}
|
||||
|
||||
void Object::registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback
|
||||
, Flags flags )
|
||||
{
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
|
||||
SDBUS_CHECK_MEMBER_NAME(propertyName);
|
||||
SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL);
|
||||
|
||||
auto& interface = getInterface(interfaceName);
|
||||
|
||||
InterfaceData::PropertyData propertyData{ std::move(signature)
|
||||
, std::move(getCallback)
|
||||
, std::move(setCallback)
|
||||
, std::move(flags) };
|
||||
auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
|
||||
{
|
||||
auto& interface = getInterface(interfaceName);
|
||||
interface.flags = flags;
|
||||
}
|
||||
|
||||
void Object::finishRegistration()
|
||||
{
|
||||
for (auto& item : interfaces_)
|
||||
{
|
||||
const auto& interfaceName = item.first;
|
||||
auto& interfaceData = item.second;
|
||||
|
||||
const auto& vtable = createInterfaceVTable(interfaceData);
|
||||
activateInterfaceVTable(interfaceName, interfaceData, vtable);
|
||||
}
|
||||
// Return vtable wrapped in a Slot object
|
||||
return {internalVTable.release(), [](void *ptr){ delete static_cast<VTable*>(ptr); }}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
void Object::unregister()
|
||||
{
|
||||
interfaces_.clear();
|
||||
removeObjectManager();
|
||||
vtables_.clear();
|
||||
objectManagerSlot_.reset();
|
||||
}
|
||||
|
||||
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
|
||||
Signal Object::createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const
|
||||
{
|
||||
return connection_.createSignal(objectPath_, interfaceName, signalName);
|
||||
}
|
||||
|
||||
void Object::emitSignal(const sdbus::Signal& message)
|
||||
Signal Object::createSignal(const char* interfaceName, const char* signalName) const
|
||||
{
|
||||
return connection_.createSignal(objectPath_.c_str(), interfaceName, signalName);
|
||||
}
|
||||
|
||||
void Object::emitSignal(const Signal& message)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
|
||||
|
||||
message.send();
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames)
|
||||
void Object::emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames)
|
||||
{
|
||||
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const std::string& interfaceName)
|
||||
void Object::emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames)
|
||||
{
|
||||
connection_.emitPropertiesChangedSignal(objectPath_.c_str(), interfaceName, propNames);
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const InterfaceName& interfaceName)
|
||||
{
|
||||
Object::emitPropertiesChangedSignal(interfaceName, {});
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const char* interfaceName)
|
||||
{
|
||||
Object::emitPropertiesChangedSignal(interfaceName, {});
|
||||
}
|
||||
@@ -200,7 +133,7 @@ void Object::emitInterfacesAddedSignal()
|
||||
connection_.emitInterfacesAddedSignal(objectPath_);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
|
||||
void Object::emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces)
|
||||
{
|
||||
connection_.emitInterfacesAddedSignal(objectPath_, interfaces);
|
||||
}
|
||||
@@ -210,24 +143,19 @@ void Object::emitInterfacesRemovedSignal()
|
||||
connection_.emitInterfacesRemovedSignal(objectPath_);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
|
||||
void Object::emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
|
||||
{
|
||||
connection_.emitInterfacesRemovedSignal(objectPath_, interfaces);
|
||||
}
|
||||
|
||||
void Object::addObjectManager()
|
||||
{
|
||||
objectManagerSlot_ = connection_.addObjectManager(objectPath_, request_slot);
|
||||
objectManagerSlot_ = connection_.addObjectManager(objectPath_, return_slot);
|
||||
}
|
||||
|
||||
void Object::removeObjectManager()
|
||||
Slot Object::addObjectManager(return_slot_t)
|
||||
{
|
||||
objectManagerSlot_.reset();
|
||||
}
|
||||
|
||||
bool Object::hasObjectManager() const
|
||||
{
|
||||
return objectManagerSlot_ != nullptr;
|
||||
return connection_.addObjectManager(objectPath_, return_slot);
|
||||
}
|
||||
|
||||
sdbus::IConnection& Object::getConnection() const
|
||||
@@ -235,91 +163,161 @@ sdbus::IConnection& Object::getConnection() const
|
||||
return connection_;
|
||||
}
|
||||
|
||||
const std::string& Object::getObjectPath() const
|
||||
const ObjectPath& Object::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
const Message* Object::getCurrentlyProcessedMessage() const
|
||||
Message Object::getCurrentlyProcessedMessage() const
|
||||
{
|
||||
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
|
||||
return connection_.getCurrentlyProcessedMessage();
|
||||
}
|
||||
|
||||
Object::InterfaceData& Object::getInterface(const std::string& interfaceName)
|
||||
Object::VTable Object::createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
|
||||
{
|
||||
return interfaces_.emplace(interfaceName, *this).first->second;
|
||||
}
|
||||
VTable internalVTable;
|
||||
|
||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||
{
|
||||
auto& vtable = interfaceData.vtable;
|
||||
assert(vtable.empty());
|
||||
internalVTable.interfaceName = std::move(interfaceName);
|
||||
|
||||
vtable.push_back(createVTableStartItem(interfaceData.flags.toSdBusInterfaceFlags()));
|
||||
registerMethodsToVTable(interfaceData, vtable);
|
||||
registerSignalsToVTable(interfaceData, vtable);
|
||||
registerPropertiesToVTable(interfaceData, vtable);
|
||||
vtable.push_back(createVTableEndItem());
|
||||
|
||||
return vtable;
|
||||
}
|
||||
|
||||
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.methods)
|
||||
for (auto& vtableItem : vtable)
|
||||
{
|
||||
const auto& methodName = item.first;
|
||||
const auto& methodData = item.second;
|
||||
|
||||
vtable.push_back(createVTableMethodItem( methodName.c_str()
|
||||
, methodData.inputArgs.c_str()
|
||||
, methodData.outputArgs.c_str()
|
||||
, methodData.paramNames.c_str()
|
||||
, &Object::sdbus_method_callback
|
||||
, methodData.flags.toSdBusMethodFlags() ));
|
||||
std::visit( overload{ [&](InterfaceFlagsVTableItem&& interfaceFlags){ writeInterfaceFlagsToVTable(std::move(interfaceFlags), internalVTable); }
|
||||
, [&](MethodVTableItem&& method){ writeMethodRecordToVTable(std::move(method), internalVTable); }
|
||||
, [&](SignalVTableItem&& signal){ writeSignalRecordToVTable(std::move(signal), internalVTable); }
|
||||
, [&](PropertyVTableItem&& property){ writePropertyRecordToVTable(std::move(property), internalVTable); } }
|
||||
, std::move(vtableItem) );
|
||||
}
|
||||
|
||||
// Sort arrays so we can do fast searching for an item in sd-bus callback handlers
|
||||
std::sort(internalVTable.methods.begin(), internalVTable.methods.end(), [](const auto& lhs, const auto& rhs){ return lhs.name < rhs.name; });
|
||||
std::sort(internalVTable.signals.begin(), internalVTable.signals.end(), [](const auto& lhs, const auto& rhs){ return lhs.name < rhs.name; });
|
||||
std::sort(internalVTable.properties.begin(), internalVTable.properties.end(), [](const auto& lhs, const auto& rhs){ return lhs.name < rhs.name; });
|
||||
|
||||
internalVTable.object = this;
|
||||
|
||||
return internalVTable;
|
||||
}
|
||||
|
||||
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
void Object::writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.signals)
|
||||
vtable.interfaceFlags = std::move(flags.flags);
|
||||
}
|
||||
|
||||
void Object::writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable)
|
||||
{
|
||||
SDBUS_CHECK_MEMBER_NAME(method.name.c_str());
|
||||
SDBUS_THROW_ERROR_IF(!method.callbackHandler, "Invalid method callback provided", EINVAL);
|
||||
|
||||
vtable.methods.push_back({ std::move(method.name)
|
||||
, std::move(method.inputSignature)
|
||||
, std::move(method.outputSignature)
|
||||
, paramNamesToString(method.inputParamNames) + paramNamesToString(method.outputParamNames)
|
||||
, std::move(method.callbackHandler)
|
||||
, std::move(method.flags) });
|
||||
}
|
||||
|
||||
void Object::writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable)
|
||||
{
|
||||
SDBUS_CHECK_MEMBER_NAME(signal.name.c_str());
|
||||
|
||||
vtable.signals.push_back({ std::move(signal.name)
|
||||
, std::move(signal.signature)
|
||||
, paramNamesToString(signal.paramNames)
|
||||
, std::move(signal.flags) });
|
||||
}
|
||||
|
||||
void Object::writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable)
|
||||
{
|
||||
SDBUS_CHECK_MEMBER_NAME(property.name.c_str());
|
||||
SDBUS_THROW_ERROR_IF(!property.getter && !property.setter, "Invalid property callbacks provided", EINVAL);
|
||||
|
||||
vtable.properties.push_back({ std::move(property.name)
|
||||
, std::move(property.signature)
|
||||
, std::move(property.getter)
|
||||
, std::move(property.setter)
|
||||
, std::move(property.flags) });
|
||||
}
|
||||
|
||||
std::vector<sd_bus_vtable> Object::createInternalSdBusVTable(const VTable& vtable)
|
||||
{
|
||||
std::vector<sd_bus_vtable> sdbusVTable;
|
||||
|
||||
startSdBusVTable(vtable.interfaceFlags, sdbusVTable);
|
||||
for (const auto& methodItem : vtable.methods)
|
||||
writeMethodRecordToSdBusVTable(methodItem, sdbusVTable);
|
||||
for (const auto& signalItem : vtable.signals)
|
||||
writeSignalRecordToSdBusVTable(signalItem, sdbusVTable);
|
||||
for (const auto& propertyItem : vtable.properties)
|
||||
writePropertyRecordToSdBusVTable(propertyItem, sdbusVTable);
|
||||
finalizeSdBusVTable(sdbusVTable);
|
||||
|
||||
return sdbusVTable;
|
||||
}
|
||||
|
||||
void Object::startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = createSdBusVTableStartItem(interfaceFlags.toSdBusInterfaceFlags());
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = createSdBusVTableMethodItem( method.name.c_str()
|
||||
, method.inputSignature.c_str()
|
||||
, method.outputSignature.c_str()
|
||||
, method.paramNames.c_str()
|
||||
, &Object::sdbus_method_callback
|
||||
, method.flags.toSdBusMethodFlags() );
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = createSdBusVTableSignalItem( signal.name.c_str()
|
||||
, signal.signature.c_str()
|
||||
, signal.paramNames.c_str()
|
||||
, signal.flags.toSdBusSignalFlags() );
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = !property.setCallback
|
||||
? createSdBusVTableReadOnlyPropertyItem( property.name.c_str()
|
||||
, property.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, property.flags.toSdBusPropertyFlags() )
|
||||
: createSdBusVTableWritablePropertyItem( property.name.c_str()
|
||||
, property.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, &Object::sdbus_property_set_callback
|
||||
, property.flags.toSdBusWritablePropertyFlags() );
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
vtable.push_back(createSdBusVTableEndItem());
|
||||
}
|
||||
|
||||
const Object::VTable::MethodItem* Object::findMethod(const VTable& vtable, std::string_view methodName)
|
||||
{
|
||||
auto it = std::lower_bound(vtable.methods.begin(), vtable.methods.end(), methodName, [](const auto& methodItem, const auto& methodName)
|
||||
{
|
||||
const auto& signalName = item.first;
|
||||
const auto& signalData = item.second;
|
||||
return methodItem.name < methodName;
|
||||
});
|
||||
|
||||
vtable.push_back(createVTableSignalItem( signalName.c_str()
|
||||
, signalData.signature.c_str()
|
||||
, signalData.paramNames.c_str()
|
||||
, signalData.flags.toSdBusSignalFlags() ));
|
||||
}
|
||||
return it != vtable.methods.end() && it->name == methodName ? &*it : nullptr;
|
||||
}
|
||||
|
||||
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
const Object::VTable::PropertyItem* Object::findProperty(const VTable& vtable, std::string_view propertyName)
|
||||
{
|
||||
for (const auto& item : interfaceData.properties)
|
||||
auto it = std::lower_bound(vtable.properties.begin(), vtable.properties.end(), propertyName, [](const auto& propertyItem, const auto& propertyName)
|
||||
{
|
||||
const auto& propertyName = item.first;
|
||||
const auto& propertyData = item.second;
|
||||
return propertyItem.name < propertyName;
|
||||
});
|
||||
|
||||
if (!propertyData.setCallback)
|
||||
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, propertyData.flags.toSdBusPropertyFlags() ));
|
||||
else
|
||||
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, &Object::sdbus_property_set_callback
|
||||
, propertyData.flags.toSdBusWritablePropertyFlags() ));
|
||||
}
|
||||
}
|
||||
|
||||
void Object::activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable )
|
||||
{
|
||||
interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], &interfaceData);
|
||||
return it != vtable.properties.end() && it->name == propertyName ? &*it : nullptr;
|
||||
}
|
||||
|
||||
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
|
||||
@@ -332,22 +330,17 @@ std::string Object::paramNamesToString(const std::vector<std::string>& paramName
|
||||
|
||||
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* interfaceData = static_cast<InterfaceData*>(userData);
|
||||
assert(interfaceData != nullptr);
|
||||
auto& object = interfaceData->object;
|
||||
auto* vtable = static_cast<VTable*>(userData);
|
||||
assert(vtable != nullptr);
|
||||
assert(vtable->object != nullptr);
|
||||
|
||||
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object.connection_.getSdBusInterface());
|
||||
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &vtable->object->connection_);
|
||||
|
||||
object.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
const auto* methodItem = findMethod(*vtable, message.getMemberName());
|
||||
assert(methodItem != nullptr);
|
||||
assert(methodItem->callback);
|
||||
|
||||
auto& callback = interfaceData->methods[message.getMemberName()].callback;
|
||||
assert(callback);
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ callback(message); }, retError);
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ methodItem->callback(std::move(message)); }, retError);
|
||||
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
@@ -360,21 +353,23 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
auto* interfaceData = static_cast<InterfaceData*>(userData);
|
||||
assert(interfaceData != nullptr);
|
||||
auto& object = interfaceData->object;
|
||||
auto* vtable = static_cast<VTable*>(userData);
|
||||
assert(vtable != nullptr);
|
||||
assert(vtable->object != nullptr);
|
||||
|
||||
auto& callback = interfaceData->properties[property].getCallback;
|
||||
// Getter can be empty - the case of "write-only" property
|
||||
if (!callback)
|
||||
const auto* propertyItem = findProperty(*vtable, property);
|
||||
assert(propertyItem != nullptr);
|
||||
|
||||
// Getter may be empty - the case of "write-only" property
|
||||
if (!propertyItem->getCallback)
|
||||
{
|
||||
sd_bus_error_set(retError, "org.freedesktop.DBus.Error.Failed", "Cannot read property as it is write-only");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object.connection_.getSdBusInterface());
|
||||
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &vtable->object->connection_);
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ callback(reply); }, retError);
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->getCallback(reply); }, retError);
|
||||
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
@@ -387,36 +382,31 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
auto* interfaceData = static_cast<InterfaceData*>(userData);
|
||||
assert(interfaceData != nullptr);
|
||||
auto& object = interfaceData->object;
|
||||
auto* vtable = static_cast<VTable*>(userData);
|
||||
assert(vtable != nullptr);
|
||||
assert(vtable->object != nullptr);
|
||||
|
||||
auto& callback = interfaceData->properties[property].setCallback;
|
||||
assert(callback);
|
||||
const auto* propertyItem = findProperty(*vtable, property);
|
||||
assert(propertyItem != nullptr);
|
||||
assert(propertyItem->setCallback);
|
||||
|
||||
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object.connection_.getSdBusInterface());
|
||||
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &vtable->object->connection_);
|
||||
|
||||
object.m_CurrentlyProcessedMessage.store(&value, std::memory_order_relaxed);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ callback(value); }, retError);
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->setCallback(std::move(value)); }, retError);
|
||||
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath)
|
||||
std::unique_ptr<IObject> createObject(IConnection& connection, ObjectPath objectPath)
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
|
||||
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(&connection);
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
return std::make_unique<sdbus::internal::Object>(*sdbusConnection, std::move(objectPath));
|
||||
return std::make_unique<internal::Object>(*sdbusConnection, std::move(objectPath));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
+88
-97
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Object.h
|
||||
*
|
||||
@@ -27,16 +27,26 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_OBJECT_H_
|
||||
#define SDBUS_CXX_INTERNAL_OBJECT_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include "IConnection.h"
|
||||
#include SDBUS_HEADER
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include "sdbus-c++/IObject.h"
|
||||
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include SDBUS_HEADER
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IConnection;
|
||||
namespace internal {
|
||||
class IConnection;
|
||||
} // namespace internal
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
@@ -44,117 +54,100 @@ namespace sdbus::internal {
|
||||
: public IObject
|
||||
{
|
||||
public:
|
||||
Object(sdbus::internal::IConnection& connection, std::string objectPath);
|
||||
Object(IConnection& connection, ObjectPath objectPath);
|
||||
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, std::string outputSignature
|
||||
, method_callback methodCallback
|
||||
, Flags flags ) override;
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, const std::vector<std::string>& inputNames
|
||||
, std::string outputSignature
|
||||
, const std::vector<std::string>& outputNames
|
||||
, method_callback methodCallback
|
||||
, Flags flags ) override;
|
||||
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, Flags flags ) override;
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, const std::vector<std::string>& paramNames
|
||||
, Flags flags ) override;
|
||||
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, Flags flags ) override;
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback
|
||||
, Flags flags ) override;
|
||||
|
||||
void setInterfaceFlags(const std::string& interfaceName, Flags flags) override;
|
||||
|
||||
void finishRegistration() override;
|
||||
void addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) override;
|
||||
Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) override;
|
||||
void unregister() override;
|
||||
|
||||
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Signal& message) override;
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) override;
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName) override;
|
||||
[[nodiscard]] Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const override;
|
||||
Signal createSignal(const char* interfaceName, const char* signalName) const override;
|
||||
void emitSignal(const 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;
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName) override;
|
||||
void emitPropertiesChangedSignal(const char* interfaceName) override;
|
||||
void emitInterfacesAddedSignal() override;
|
||||
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) override;
|
||||
void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces) override;
|
||||
void emitInterfacesRemovedSignal() override;
|
||||
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) override;
|
||||
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces) override;
|
||||
|
||||
void addObjectManager() override;
|
||||
void removeObjectManager() override;
|
||||
bool hasObjectManager() const override;
|
||||
[[nodiscard]] Slot addObjectManager(return_slot_t) override;
|
||||
|
||||
sdbus::IConnection& getConnection() const override;
|
||||
const std::string& getObjectPath() const override;
|
||||
const Message* getCurrentlyProcessedMessage() const override;
|
||||
[[nodiscard]] sdbus::IConnection& getConnection() const override;
|
||||
[[nodiscard]] const ObjectPath& getObjectPath() const override;
|
||||
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
|
||||
|
||||
private:
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
// A vtable record comprising methods, signals, properties, flags.
|
||||
// Once created, it cannot be modified. Only new vtables records can be added.
|
||||
// An interface can have any number of vtables attached to it, not only one.
|
||||
struct VTable
|
||||
{
|
||||
InterfaceData(Object& object) : object(object) {}
|
||||
InterfaceName interfaceName;
|
||||
Flags interfaceFlags;
|
||||
|
||||
using MethodName = std::string;
|
||||
struct MethodData
|
||||
struct MethodItem
|
||||
{
|
||||
const std::string inputArgs;
|
||||
const std::string outputArgs;
|
||||
const std::string paramNames;
|
||||
MethodName name;
|
||||
Signature inputSignature;
|
||||
Signature outputSignature;
|
||||
std::string paramNames;
|
||||
method_callback callback;
|
||||
Flags flags;
|
||||
};
|
||||
std::map<MethodName, MethodData> methods;
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
// Array of method records sorted by method name
|
||||
std::vector<MethodItem> methods;
|
||||
|
||||
struct SignalItem
|
||||
{
|
||||
const std::string signature;
|
||||
const std::string paramNames;
|
||||
SignalName name;
|
||||
Signature signature;
|
||||
std::string paramNames;
|
||||
Flags flags;
|
||||
};
|
||||
std::map<SignalName, SignalData> signals;
|
||||
using PropertyName = std::string;
|
||||
struct PropertyData
|
||||
// Array of signal records sorted by signal name
|
||||
std::vector<SignalItem> signals;
|
||||
|
||||
struct PropertyItem
|
||||
{
|
||||
const std::string signature;
|
||||
PropertyName name;
|
||||
Signature signature;
|
||||
property_get_callback getCallback;
|
||||
property_set_callback setCallback;
|
||||
Flags flags;
|
||||
};
|
||||
std::map<PropertyName, PropertyData> properties;
|
||||
std::vector<sd_bus_vtable> vtable;
|
||||
Flags flags;
|
||||
Object& object;
|
||||
// Array of signal records sorted by signal name
|
||||
std::vector<PropertyItem> properties;
|
||||
|
||||
// VTable structure in format required by sd-bus API
|
||||
std::vector<sd_bus_vtable> sdbusVTable;
|
||||
|
||||
// Back-reference to the owning object from sd-bus callback handlers
|
||||
Object* object{};
|
||||
|
||||
// This is intentionally the last member, because it must be destructed first,
|
||||
// releasing callbacks above before the callbacks themselves are destructed.
|
||||
Slot slot;
|
||||
};
|
||||
|
||||
InterfaceData& getInterface(const std::string& interfaceName);
|
||||
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData);
|
||||
static void registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
static void registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
static void registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
void activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable );
|
||||
VTable createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable);
|
||||
static void writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable);
|
||||
static void writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable);
|
||||
static void writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable);
|
||||
static void writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable);
|
||||
|
||||
static std::vector<sd_bus_vtable> createInternalSdBusVTable(const VTable& vtable);
|
||||
static void startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable);
|
||||
static void writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable);
|
||||
static void writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable);
|
||||
static void writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable);
|
||||
static void finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable);
|
||||
|
||||
static const VTable::MethodItem* findMethod(const VTable& vtable, std::string_view methodName);
|
||||
static const VTable::PropertyItem* findProperty(const VTable& vtable, std::string_view propertyName);
|
||||
|
||||
static std::string paramNamesToString(const std::vector<std::string>& paramNames);
|
||||
|
||||
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
@@ -173,14 +166,12 @@ namespace sdbus::internal {
|
||||
, void *userData
|
||||
, sd_bus_error *retError );
|
||||
|
||||
private:
|
||||
sdbus::internal::IConnection& connection_;
|
||||
std::string objectPath_;
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
IConnection& connection_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
ObjectPath objectPath_;
|
||||
std::vector<Slot> vtables_;
|
||||
Slot objectManagerSlot_;
|
||||
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_OBJECT_H_ */
|
||||
|
||||
+264
-221
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Proxy.cpp
|
||||
*
|
||||
@@ -25,233 +25,238 @@
|
||||
*/
|
||||
|
||||
#include "Proxy.h"
|
||||
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
#include "sdbus-c++/IProxy.h"
|
||||
#include "sdbus-c++/Message.h"
|
||||
#include "sdbus-c++/TypeTraits.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "Utils.h"
|
||||
#include "sdbus-c++/Message.h"
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include SDBUS_HEADER
|
||||
#include "Utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include SDBUS_HEADER
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
|
||||
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
|
||||
Proxy::Proxy(IConnection& connection, ServiceName destination, ObjectPath objectPath)
|
||||
: connection_(&connection, [](IConnection *){ /* Intentionally left empty */ })
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_);
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_);
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
|
||||
// The connection is not ours only, it is owned and managed by the user and we just reference
|
||||
// it here, so we expect the client to manage the event loop upon this connection themselves.
|
||||
}
|
||||
|
||||
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
Proxy::Proxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
: connection_(std::move(connection))
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_);
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_);
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
|
||||
// The connection is ours only, i.e. it's us who has to manage the event loop upon this connection,
|
||||
// in order that we get and process signals, async call replies, and other messages from D-Bus.
|
||||
connection_->enterEventLoopAsync();
|
||||
}
|
||||
|
||||
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath
|
||||
Proxy::Proxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t )
|
||||
: connection_(std::move(connection))
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_);
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_);
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
|
||||
// Even though the connection is ours only, we don't start an event loop thread.
|
||||
// This proxy is meant to be created, used for simple synchronous D-Bus call(s) and then dismissed.
|
||||
}
|
||||
|
||||
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
|
||||
MethodCall Proxy::createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const
|
||||
{
|
||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||
}
|
||||
|
||||
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
|
||||
MethodCall Proxy::createMethodCall(const char* interfaceName, const char* methodName) const
|
||||
{
|
||||
// Sending method call synchronously is the only operation that blocks, waiting for the method
|
||||
// reply message among the incoming messages on the sd-bus connection socket. But typically there
|
||||
// already is somebody that generally handles incoming D-Bus messages -- the connection event loop
|
||||
// running typically in its own thread. We have to avoid polling on socket from several threads.
|
||||
// So we have to branch here: either we are within the context of the event loop thread, then we
|
||||
// can send the message simply via sd_bus_call, which blocks. Or we are in another thread, then
|
||||
// we can perform the send operation of the method call message from here (because that is thread-
|
||||
// safe like other sd-bus API accesses), but the incoming reply we have to get through the event
|
||||
// loop thread, because this is the only rightful listener on the sd-bus connection socket.
|
||||
// So, technically, we use async means to wait here for reply received by the event loop thread.
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
|
||||
|
||||
// If we don't need to wait for any reply, we can send the message now irrespective of the context
|
||||
if (message.doesntExpectReply())
|
||||
return message.send(timeout);
|
||||
|
||||
// If we are in the context of event loop thread, we can send the D-Bus call synchronously
|
||||
// and wait blockingly for the reply, because we are the exclusive listeners on the socket
|
||||
auto reply = connection_->tryCallMethodSynchronously(message, timeout);
|
||||
if (reply.isValid())
|
||||
return reply;
|
||||
|
||||
// Otherwise we send the call asynchronously and do blocking wait for the reply from the event loop thread
|
||||
return sendMethodCallMessageAndWaitForReply(message, timeout);
|
||||
return connection_->createMethodCall(destination_.c_str(), objectPath_.c_str(), interfaceName, methodName);
|
||||
}
|
||||
|
||||
PendingAsyncCall Proxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
|
||||
MethodReply Proxy::callMethod(const MethodCall& message)
|
||||
{
|
||||
return Proxy::callMethod(message, /*timeout*/ 0);
|
||||
}
|
||||
|
||||
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
|
||||
|
||||
return message.send(timeout);
|
||||
}
|
||||
|
||||
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback)
|
||||
{
|
||||
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0);
|
||||
}
|
||||
|
||||
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, return_slot_t)
|
||||
{
|
||||
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0, return_slot);
|
||||
}
|
||||
|
||||
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
|
||||
|
||||
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
|
||||
auto callData = std::make_shared<AsyncCalls::CallData>(AsyncCalls::CallData{*this, std::move(asyncReplyCallback), {}, AsyncCalls::CallData::State::RUNNING});
|
||||
auto weakData = std::weak_ptr<AsyncCalls::CallData>{callData};
|
||||
auto asyncCallInfo = std::make_shared<AsyncCallInfo>(AsyncCallInfo{ .callback = std::move(asyncReplyCallback)
|
||||
, .proxy = *this
|
||||
, .slot = {}
|
||||
, .floating = false });
|
||||
|
||||
callData->slot = message.send(callback, callData.get(), timeout);
|
||||
asyncCallInfo->slot = message.send(reinterpret_cast<void*>(&Proxy::sdbus_async_reply_handler), asyncCallInfo.get(), timeout, return_slot);
|
||||
|
||||
pendingAsyncCalls_.addCall(std::move(callData));
|
||||
auto asyncCallInfoWeakPtr = std::weak_ptr{asyncCallInfo};
|
||||
|
||||
return {weakData};
|
||||
floatingAsyncCallSlots_.push_back(std::move(asyncCallInfo));
|
||||
|
||||
return PendingAsyncCall{asyncCallInfoWeakPtr};
|
||||
}
|
||||
|
||||
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, with_future_t)
|
||||
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout, return_slot_t)
|
||||
{
|
||||
return Proxy::callMethod(message, {}, with_future);
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
|
||||
|
||||
auto asyncCallInfo = std::make_unique<AsyncCallInfo>(AsyncCallInfo{ .callback = std::move(asyncReplyCallback)
|
||||
, .proxy = *this
|
||||
, .slot = {}
|
||||
, .floating = true });
|
||||
|
||||
asyncCallInfo->slot = message.send(reinterpret_cast<void*>(&Proxy::sdbus_async_reply_handler), asyncCallInfo.get(), timeout, return_slot);
|
||||
|
||||
return {asyncCallInfo.release(), [](void *ptr){ delete static_cast<AsyncCallInfo*>(ptr); }}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, uint64_t timeout, with_future_t)
|
||||
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, with_future_t)
|
||||
{
|
||||
return Proxy::callMethodAsync(message, {}, with_future);
|
||||
}
|
||||
|
||||
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t)
|
||||
{
|
||||
auto promise = std::make_shared<std::promise<MethodReply>>();
|
||||
auto future = promise->get_future();
|
||||
|
||||
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply& reply, const Error* error) noexcept
|
||||
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, std::optional<Error> error) noexcept
|
||||
{
|
||||
if (error == nullptr)
|
||||
promise->set_value(reply);
|
||||
if (!error)
|
||||
promise->set_value(std::move(reply));
|
||||
else
|
||||
promise->set_exception(std::make_exception_ptr(*error));
|
||||
promise->set_exception(std::make_exception_ptr(*std::move(error)));
|
||||
};
|
||||
|
||||
(void)Proxy::callMethod(message, std::move(asyncReplyCallback), timeout);
|
||||
(void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout)
|
||||
Awaitable<MethodReply> Proxy::callMethodAsync(const MethodCall& message, with_awaitable_t)
|
||||
{
|
||||
/*thread_local*/ SyncCallReplyData syncCallReplyData;
|
||||
return Proxy::callMethodAsync(message, /*timeout*/ 0, with_awaitable);
|
||||
}
|
||||
|
||||
async_reply_handler asyncReplyCallback = [&syncCallReplyData](MethodReply& reply, const Error* error)
|
||||
Awaitable<MethodReply> Proxy::callMethodAsync(const MethodCall& message, uint64_t timeout, with_awaitable_t)
|
||||
{
|
||||
auto data = std::make_shared<AwaitableData<MethodReply>>();
|
||||
async_reply_handler asyncReplyCallback = [data](MethodReply reply, std::optional<Error> error) noexcept
|
||||
{
|
||||
syncCallReplyData.sendMethodReplyToWaitingThread(reply, error);
|
||||
if (!error)
|
||||
data->result = std::move(reply);
|
||||
else
|
||||
data->result = std::make_exception_ptr(*std::move(error));
|
||||
|
||||
auto previous = data->status.exchange(AwaitableState::Completed, std::memory_order_acq_rel);
|
||||
if (previous == AwaitableState::Waiting)
|
||||
data->resumeCoroutine();
|
||||
};
|
||||
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
|
||||
AsyncCalls::CallData callData{*this, std::move(asyncReplyCallback), {}, AsyncCalls::CallData::State::NOT_ASYNC};
|
||||
|
||||
message.send(callback, &callData, timeout, floating_slot);
|
||||
(void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout);
|
||||
|
||||
return syncCallReplyData.waitForMethodReply();
|
||||
return Awaitable{data};
|
||||
}
|
||||
|
||||
void Proxy::SyncCallReplyData::sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error)
|
||||
{
|
||||
std::unique_lock lock{mutex_};
|
||||
SCOPE_EXIT{ cond_.notify_one(); }; // This must happen before unlocking the mutex to avoid potential data race on spurious wakeup in the waiting thread
|
||||
SCOPE_EXIT{ arrived_ = true; };
|
||||
|
||||
//error_ = nullptr; // Necessary if SyncCallReplyData instance is thread_local
|
||||
|
||||
if (error == nullptr)
|
||||
reply_ = std::move(reply);
|
||||
else
|
||||
error_ = std::make_unique<Error>(*error);
|
||||
}
|
||||
|
||||
MethodReply Proxy::SyncCallReplyData::waitForMethodReply()
|
||||
{
|
||||
std::unique_lock lock{mutex_};
|
||||
cond_.wait(lock, [this](){ return arrived_; });
|
||||
|
||||
//arrived_ = false; // Necessary if SyncCallReplyData instance is thread_local
|
||||
|
||||
if (error_)
|
||||
throw *error_;
|
||||
|
||||
return std::move(reply_);
|
||||
}
|
||||
|
||||
void Proxy::registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
void Proxy::registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler )
|
||||
{
|
||||
Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler));
|
||||
}
|
||||
|
||||
void Proxy::registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler )
|
||||
{
|
||||
auto slot = Proxy::registerSignalHandler(interfaceName, signalName, std::move(signalHandler), return_slot);
|
||||
|
||||
floatingSignalSlots_.push_back(std::move(slot));
|
||||
}
|
||||
|
||||
Slot Proxy::registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t )
|
||||
{
|
||||
return Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler), return_slot);
|
||||
}
|
||||
|
||||
Slot Proxy::registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t )
|
||||
{
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
|
||||
SDBUS_CHECK_MEMBER_NAME(signalName);
|
||||
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
auto signalInfo = std::make_unique<SignalInfo>(SignalInfo{std::move(signalHandler), *this, {}});
|
||||
|
||||
auto signalData = std::make_unique<InterfaceData::SignalData>(*this, std::move(signalHandler), nullptr);
|
||||
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
|
||||
signalInfo->slot = connection_->registerSignalHandler( destination_.c_str()
|
||||
, objectPath_.c_str()
|
||||
, interfaceName
|
||||
, signalName
|
||||
, &Proxy::sdbus_signal_handler
|
||||
, signalInfo.get()
|
||||
, return_slot );
|
||||
|
||||
auto inserted = insertionResult.second;
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Proxy::unregisterSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName )
|
||||
{
|
||||
auto it = interfaces_.find(interfaceName);
|
||||
|
||||
if (it != interfaces_.end())
|
||||
it->second.signals_.erase(signalName);
|
||||
}
|
||||
|
||||
void Proxy::finishRegistration()
|
||||
{
|
||||
registerSignalHandlers(*connection_);
|
||||
}
|
||||
|
||||
void Proxy::registerSignalHandlers(sdbus::internal::IConnection& connection)
|
||||
{
|
||||
for (auto& interfaceItem : interfaces_)
|
||||
{
|
||||
const auto& interfaceName = interfaceItem.first;
|
||||
auto& signalsOnInterface = interfaceItem.second.signals_;
|
||||
|
||||
for (auto& signalItem : signalsOnInterface)
|
||||
{
|
||||
const auto& signalName = signalItem.first;
|
||||
auto* signalData = signalItem.second.get();
|
||||
signalData->slot = connection.registerSignalHandler( destination_
|
||||
, objectPath_
|
||||
, interfaceName
|
||||
, signalName
|
||||
, &Proxy::sdbus_signal_handler
|
||||
, signalData);
|
||||
}
|
||||
}
|
||||
return {signalInfo.release(), [](void *ptr){ delete static_cast<SignalInfo*>(ptr); }}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
void Proxy::unregister()
|
||||
{
|
||||
pendingAsyncCalls_.clear();
|
||||
interfaces_.clear();
|
||||
floatingAsyncCallSlots_.clear();
|
||||
floatingSignalSlots_.clear();
|
||||
}
|
||||
|
||||
sdbus::IConnection& Proxy::getConnection() const
|
||||
@@ -259,53 +264,44 @@ sdbus::IConnection& Proxy::getConnection() const
|
||||
return *connection_;
|
||||
}
|
||||
|
||||
const std::string& Proxy::getObjectPath() const
|
||||
const ObjectPath& Proxy::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
const Message* Proxy::getCurrentlyProcessedMessage() const
|
||||
Message Proxy::getCurrentlyProcessedMessage() const
|
||||
{
|
||||
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
|
||||
return connection_->getCurrentlyProcessedMessage();
|
||||
}
|
||||
|
||||
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* asyncCallData = static_cast<AsyncCalls::CallData*>(userData);
|
||||
assert(asyncCallData != nullptr);
|
||||
assert(asyncCallData->callback);
|
||||
auto& proxy = asyncCallData->proxy;
|
||||
auto state = asyncCallData->state;
|
||||
auto* asyncCallInfo = static_cast<AsyncCallInfo*>(userData);
|
||||
assert(asyncCallInfo != nullptr);
|
||||
assert(asyncCallInfo->callback);
|
||||
auto& proxy = asyncCallInfo->proxy;
|
||||
|
||||
// We are removing the CallData item at the complete scope exit, after the callback has been invoked.
|
||||
// We can't do it earlier (before callback invocation for example), because CallBack data (slot release)
|
||||
// is the synchronization point between callback invocation and Proxy::unregister.
|
||||
SCOPE_EXIT
|
||||
{
|
||||
// Remove call meta-data if it's a real async call (a sync call done in terms of async has STATE_NOT_ASYNC)
|
||||
if (state != AsyncCalls::CallData::State::NOT_ASYNC)
|
||||
proxy.pendingAsyncCalls_.removeCall(asyncCallData);
|
||||
proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
|
||||
};
|
||||
|
||||
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
|
||||
|
||||
proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
auto message = Message::Factory::create<MethodReply>(sdbusMessage, proxy.connection_.get());
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&]
|
||||
{
|
||||
const auto* error = sd_bus_message_get_error(sdbusMessage);
|
||||
if (error == nullptr)
|
||||
{
|
||||
asyncCallData->callback(message, nullptr);
|
||||
asyncCallInfo->callback(std::move(message), {});
|
||||
}
|
||||
else
|
||||
{
|
||||
Error exception(error->name, error->message);
|
||||
asyncCallData->callback(message, &exception);
|
||||
Error exception(Error::Name{error->name}, error->message);
|
||||
asyncCallInfo->callback(std::move(message), std::move(exception));
|
||||
}
|
||||
}, retError);
|
||||
|
||||
@@ -314,38 +310,75 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
|
||||
|
||||
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* signalData = static_cast<InterfaceData::SignalData*>(userData);
|
||||
assert(signalData != nullptr);
|
||||
assert(signalData->callback);
|
||||
auto* signalInfo = static_cast<SignalInfo*>(userData);
|
||||
assert(signalInfo != nullptr);
|
||||
assert(signalInfo->callback);
|
||||
|
||||
auto message = Message::Factory::create<Signal>(sdbusMessage, &signalData->proxy.connection_->getSdBusInterface());
|
||||
auto message = Message::Factory::create<Signal>(sdbusMessage, signalInfo->proxy.connection_.get());
|
||||
|
||||
signalData->proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
signalData->proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ signalData->callback(message); }, retError);
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ signalInfo->callback(std::move(message)); }, retError);
|
||||
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
Proxy::FloatingAsyncCallSlots::~FloatingAsyncCallSlots()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void Proxy::FloatingAsyncCallSlots::push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo)
|
||||
{
|
||||
const std::lock_guard lock(mutex_);
|
||||
if (!asyncCallInfo->finished) // The call may have finished in the meantime
|
||||
slots_.emplace_back(std::move(asyncCallInfo));
|
||||
}
|
||||
|
||||
void Proxy::FloatingAsyncCallSlots::erase(AsyncCallInfo* info)
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
info->finished = true;
|
||||
auto it = std::find_if(slots_.begin(), slots_.end(), [info](auto const& entry){ return entry.get() == info; });
|
||||
if (it != slots_.end())
|
||||
{
|
||||
auto callInfo = std::move(*it);
|
||||
slots_.erase(it);
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `removeCall` is called by some
|
||||
// thread and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
}
|
||||
|
||||
void Proxy::FloatingAsyncCallSlots::clear()
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
auto asyncCallSlots = std::move(slots_);
|
||||
slots_ = {};
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `clear` is called by some thread
|
||||
// and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
|
||||
} // namespace sdbus::internal
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callData)
|
||||
: callData_(std::move(callData))
|
||||
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callInfo)
|
||||
: callInfo_(std::move(callInfo))
|
||||
{
|
||||
}
|
||||
|
||||
void PendingAsyncCall::cancel()
|
||||
{
|
||||
if (auto ptr = callData_.lock(); ptr != nullptr)
|
||||
if (auto ptr = callInfo_.lock(); ptr != nullptr)
|
||||
{
|
||||
auto* callData = static_cast<internal::Proxy::AsyncCalls::CallData*>(ptr.get());
|
||||
callData->proxy.pendingAsyncCalls_.removeCall(callData);
|
||||
auto* asyncCallInfo = static_cast<internal::Proxy::AsyncCallInfo*>(ptr.get());
|
||||
asyncCallInfo->proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
|
||||
|
||||
// At this point, the callData item is being deleted, leading to the release of the
|
||||
// sd-bus slot pointer. This release locks the global sd-bus mutex. If the async
|
||||
@@ -356,81 +389,91 @@ void PendingAsyncCall::cancel()
|
||||
|
||||
bool PendingAsyncCall::isPending() const
|
||||
{
|
||||
return !callData_.expired();
|
||||
return !callInfo_.expired();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
std::unique_ptr<IProxy> createProxy( IConnection& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
|
||||
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(&connection);
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( *sdbusConnection
|
||||
return std::make_unique<internal::Proxy>( *sdbusConnection
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved): connection is moved but cast to an internal type
|
||||
std::unique_ptr<IProxy> createProxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(connection.release());
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
connection.release();
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
return std::make_unique<internal::Proxy>( std::unique_ptr<internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath
|
||||
, dont_run_event_loop_thread_t )
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved): connection is moved cast to an internal type
|
||||
std::unique_ptr<IProxy> createProxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(connection.release());
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
connection.release();
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath)
|
||||
, dont_run_event_loop_thread );
|
||||
return std::make_unique<internal::Proxy>( std::unique_ptr<internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath)
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
|
||||
, std::string objectPath )
|
||||
std::unique_ptr<IProxy> createLightWeightProxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
return createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
|
||||
std::unique_ptr<IProxy> createProxy( ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
auto connection = createBusConnection();
|
||||
|
||||
auto sdbusConnection = std::unique_ptr<internal::IConnection>(dynamic_cast<internal::IConnection*>(connection.release()));
|
||||
assert(sdbusConnection != nullptr);
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
return std::make_unique<internal::Proxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
|
||||
, std::string objectPath
|
||||
std::unique_ptr<IProxy> createProxy( ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t )
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
auto connection = createBusConnection();
|
||||
|
||||
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
|
||||
auto sdbusConnection = std::unique_ptr<internal::IConnection>(dynamic_cast<internal::IConnection*>(connection.release()));
|
||||
assert(sdbusConnection != nullptr);
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath)
|
||||
, dont_run_event_loop_thread );
|
||||
return std::make_unique<internal::Proxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath)
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath)
|
||||
{
|
||||
return createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
+88
-136
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Proxy.h
|
||||
*
|
||||
@@ -27,16 +27,17 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_PROXY_H_
|
||||
#define SDBUS_CXX_INTERNAL_PROXY_H_
|
||||
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include "sdbus-c++/IProxy.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include SDBUS_HEADER
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <string>
|
||||
#include SDBUS_HEADER
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
@@ -44,159 +45,110 @@ namespace sdbus::internal {
|
||||
: public IProxy
|
||||
{
|
||||
public:
|
||||
Proxy( sdbus::internal::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath
|
||||
Proxy( IConnection& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
Proxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
Proxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
[[nodiscard]] MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const override;
|
||||
[[nodiscard]] 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 callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
|
||||
std::future<MethodReply> callMethod(const MethodCall& message, with_future_t) override;
|
||||
std::future<MethodReply> callMethod(const MethodCall& message, uint64_t timeout, with_future_t) override;
|
||||
PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) override;
|
||||
Slot callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, return_slot_t ) override;
|
||||
PendingAsyncCall callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, uint64_t timeout ) override;
|
||||
Slot callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, uint64_t timeout
|
||||
, return_slot_t ) override;
|
||||
std::future<MethodReply> callMethodAsync(const MethodCall& message, with_future_t) override;
|
||||
std::future<MethodReply> callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t) override;
|
||||
Awaitable<MethodReply> callMethodAsync(const MethodCall& message, with_awaitable_t) override;
|
||||
Awaitable<MethodReply> callMethodAsync( const MethodCall& message
|
||||
, uint64_t timeout
|
||||
, with_awaitable_t ) override;
|
||||
|
||||
void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
void registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler ) override;
|
||||
void unregisterSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName ) override;
|
||||
|
||||
void finishRegistration() override;
|
||||
void registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler ) override;
|
||||
Slot registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t ) override;
|
||||
Slot registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t ) override;
|
||||
void unregister() override;
|
||||
|
||||
sdbus::IConnection& getConnection() const override;
|
||||
const std::string& getObjectPath() const override;
|
||||
const Message* getCurrentlyProcessedMessage() const override;
|
||||
[[nodiscard]] sdbus::IConnection& getConnection() const override;
|
||||
[[nodiscard]] const ObjectPath& getObjectPath() const override;
|
||||
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
|
||||
|
||||
private:
|
||||
class SyncCallReplyData
|
||||
{
|
||||
public:
|
||||
void sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error);
|
||||
MethodReply waitForMethodReply();
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cond_;
|
||||
bool arrived_{};
|
||||
MethodReply reply_;
|
||||
std::unique_ptr<Error> error_;
|
||||
};
|
||||
|
||||
MethodReply sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout);
|
||||
void registerSignalHandlers(sdbus::internal::IConnection& connection);
|
||||
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
friend PendingAsyncCall;
|
||||
|
||||
std::unique_ptr< sdbus::internal::IConnection
|
||||
, std::function<void(sdbus::internal::IConnection*)>
|
||||
> connection_;
|
||||
std::string destination_;
|
||||
std::string objectPath_;
|
||||
std::unique_ptr<IConnection, std::function<void(IConnection*)>> connection_;
|
||||
ServiceName destination_;
|
||||
ObjectPath objectPath_;
|
||||
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
std::vector<Slot> floatingSignalSlots_;
|
||||
|
||||
struct SignalInfo
|
||||
{
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
{
|
||||
SignalData(Proxy& proxy, signal_handler callback, Slot slot)
|
||||
: proxy(proxy)
|
||||
, callback(std::move(callback))
|
||||
, slot(std::move(slot))
|
||||
{}
|
||||
Proxy& proxy;
|
||||
signal_handler callback;
|
||||
// slot must be listed after callback to ensure that slot is destructed first.
|
||||
// Destructing the slot will sd_bus_slot_unref() the callback.
|
||||
// Only after sd_bus_slot_unref(), we can safely delete the callback. The bus mutex (SdBus::sdbusMutex_)
|
||||
// ensures that sd_bus_slot_unref() and the callback execute sequentially.
|
||||
Slot slot;
|
||||
};
|
||||
std::map<SignalName, std::unique_ptr<SignalData>> signals_;
|
||||
signal_handler callback;
|
||||
Proxy& proxy; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
Slot slot;
|
||||
};
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
|
||||
// We need to keep track of pending async calls. When the proxy is being destructed, we must
|
||||
// remove all slots of these pending calls, otherwise in case when the connection outlives
|
||||
// the proxy, we might get async reply handlers invoked for pending async calls after the proxy
|
||||
// has been destroyed, which is a free ticket into the realm of undefined behavior.
|
||||
class AsyncCalls
|
||||
struct AsyncCallInfo
|
||||
{
|
||||
async_reply_handler callback;
|
||||
Proxy& proxy; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
||||
Slot slot;
|
||||
bool finished{false};
|
||||
bool floating;
|
||||
};
|
||||
|
||||
// Container keeping track of pending async calls
|
||||
class FloatingAsyncCallSlots
|
||||
{
|
||||
public:
|
||||
struct CallData
|
||||
{
|
||||
enum class State
|
||||
{ NOT_ASYNC
|
||||
, RUNNING
|
||||
, FINISHED
|
||||
};
|
||||
FloatingAsyncCallSlots() = default;
|
||||
FloatingAsyncCallSlots(const FloatingAsyncCallSlots&) = delete;
|
||||
FloatingAsyncCallSlots& operator=(const FloatingAsyncCallSlots&) = delete;
|
||||
FloatingAsyncCallSlots(FloatingAsyncCallSlots&& other) = delete;
|
||||
FloatingAsyncCallSlots& operator=(FloatingAsyncCallSlots&&) = delete;
|
||||
~FloatingAsyncCallSlots();
|
||||
|
||||
Proxy& proxy;
|
||||
async_reply_handler callback;
|
||||
Slot slot;
|
||||
State state;
|
||||
};
|
||||
|
||||
~AsyncCalls()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void addCall(std::shared_ptr<CallData> asyncCallData)
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
if (asyncCallData->state != CallData::State::FINISHED) // The call may have finished in the meantime
|
||||
calls_.emplace_back(std::move(asyncCallData));
|
||||
}
|
||||
|
||||
void removeCall(CallData* data)
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
data->state = CallData::State::FINISHED;
|
||||
if (auto it = std::find_if(calls_.begin(), calls_.end(), [data](auto const& entry){ return entry.get() == data; }); it != calls_.end())
|
||||
{
|
||||
auto callData = std::move(*it);
|
||||
calls_.erase(it);
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `removeCall` is called by some
|
||||
// thread and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
auto asyncCallSlots = std::move(calls_);
|
||||
calls_ = {};
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `clear` is called by some thread
|
||||
// and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
void push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo);
|
||||
void erase(AsyncCallInfo* info);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::deque<std::shared_ptr<CallData>> calls_;
|
||||
} pendingAsyncCalls_;
|
||||
std::deque<std::shared_ptr<AsyncCallInfo>> slots_;
|
||||
};
|
||||
|
||||
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
|
||||
FloatingAsyncCallSlots floatingAsyncCallSlots_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_PROXY_H_ */
|
||||
|
||||
+22
-16
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file ScopeGuard.h
|
||||
*
|
||||
@@ -27,8 +27,11 @@
|
||||
#ifndef SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
|
||||
#define SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
|
||||
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
// Straightforward, modern, easy-to-use RAII utility to perform work on scope exit in an exception-safe manner.
|
||||
//
|
||||
// The utility helps providing basic exception safety guarantee by ensuring that the resources are always
|
||||
@@ -104,21 +107,22 @@ namespace sdbus::internal {
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Fun, typename _Tag>
|
||||
template <class Fun, typename Tag>
|
||||
class ScopeGuard
|
||||
{
|
||||
public:
|
||||
ScopeGuard(_Fun f) : fnc_(std::move(f))
|
||||
explicit ScopeGuard(Fun fun) : fnc_(std::move(fun))
|
||||
{
|
||||
}
|
||||
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
ScopeGuard(ScopeGuard&& rhs) : fnc_(std::move(rhs.fnc_)), active_(rhs.active_), exceptions_(rhs.exceptions_)
|
||||
ScopeGuard(ScopeGuard&& rhs) noexcept : fnc_(std::move(rhs.fnc_)), exceptions_(rhs.exceptions_), active_(rhs.active_)
|
||||
{
|
||||
rhs.dismiss();
|
||||
}
|
||||
ScopeGuard& operator=(ScopeGuard&&) = delete;
|
||||
|
||||
void dismiss()
|
||||
{
|
||||
@@ -127,35 +131,35 @@ namespace sdbus::internal {
|
||||
|
||||
~ScopeGuard()
|
||||
{
|
||||
if (active_ && _Tag::holds(exceptions_))
|
||||
if (active_ && Tag::holds(exceptions_))
|
||||
fnc_();
|
||||
}
|
||||
|
||||
private:
|
||||
_Fun fnc_;
|
||||
Fun fnc_;
|
||||
int exceptions_{std::uncaught_exceptions()};
|
||||
bool active_{true};
|
||||
};
|
||||
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitTag> operator+(ScopeGuardOnExitTag, _Fun&& fnc)
|
||||
template <typename Fun>
|
||||
ScopeGuard<Fun, ScopeGuardOnExitTag> operator+(ScopeGuardOnExitTag, Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitTag>(std::forward<_Fun>(fnc));
|
||||
return ScopeGuard<Fun, ScopeGuardOnExitTag>(std::forward<Fun>(fnc));
|
||||
}
|
||||
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag> operator+(ScopeGuardOnExitSuccessTag, _Fun&& fnc)
|
||||
template <typename Fun>
|
||||
ScopeGuard<Fun, ScopeGuardOnExitSuccessTag> operator+(ScopeGuardOnExitSuccessTag, Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag>(std::forward<_Fun>(fnc));
|
||||
return ScopeGuard<Fun, ScopeGuardOnExitSuccessTag>(std::forward<Fun>(fnc));
|
||||
}
|
||||
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitFailureTag> operator+(ScopeGuardOnExitFailureTag, _Fun&& fnc)
|
||||
template <typename Fun>
|
||||
ScopeGuard<Fun, ScopeGuardOnExitFailureTag> operator+(ScopeGuardOnExitFailureTag, Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitFailureTag>(std::forward<_Fun>(fnc));
|
||||
return ScopeGuard<Fun, ScopeGuardOnExitFailureTag>(std::forward<Fun>(fnc));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#define CONCATENATE_IMPL(s1, s2) s1##s2
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
@@ -166,4 +170,6 @@ namespace sdbus::internal {
|
||||
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
|
||||
#endif
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
#endif /* SDBUS_CPP_INTERNAL_SCOPEGUARD_H_ */
|
||||
|
||||
+126
-95
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file SdBus.cpp
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
@@ -26,151 +26,150 @@
|
||||
*/
|
||||
|
||||
#include "SdBus.h"
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "sdbus-c++/Error.h" // NOLINT(misc-include-cleaner)
|
||||
#include SDBUS_HEADER
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *m)
|
||||
sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *msg)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_ref(m);
|
||||
return ::sd_bus_message_ref(msg);
|
||||
}
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m)
|
||||
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *msg)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_unref(m);
|
||||
return ::sd_bus_message_unref(msg);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
|
||||
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
auto r = ::sd_bus_send(bus, m, cookie);
|
||||
auto r = ::sd_bus_send(bus, msg, cookie);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// Make sure long messages are not only stored in outgoing queues but also really sent out
|
||||
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
|
||||
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_call(bus, m, usec, ret_error, reply);
|
||||
return ::sd_bus_call(bus, msg, usec, ret_error, reply);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)
|
||||
int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
auto r = ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
|
||||
auto r = ::sd_bus_call_async(bus, slot, msg, callback, userdata, usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// Make sure long messages are not only stored in outgoing queues but also really sent out
|
||||
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type)
|
||||
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **msg, uint8_t type)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new(bus, m, type);
|
||||
return ::sd_bus_message_new(bus, msg, type);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)
|
||||
int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member);
|
||||
return ::sd_bus_message_new_method_call(bus, msg, destination, path, interface, member);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)
|
||||
int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_signal(bus, m, path, interface, member);
|
||||
return ::sd_bus_message_new_signal(bus, msg, path, interface, member);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m)
|
||||
int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **msg)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_return(call, m);
|
||||
return ::sd_bus_message_new_method_return(call, msg);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e)
|
||||
int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_error(call, m, e);
|
||||
return ::sd_bus_message_new_method_error(call, msg, err);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_set_method_call_timeout(bus, 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
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_get_method_call_timeout(bus, 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
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_properties_changed_strv(bus, path, interface, names);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_object_added(sd_bus *bus, const char *path)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_object_added(bus, path);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_object_removed(sd_bus *bus, const char *path)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_object_removed(bus, path);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
|
||||
}
|
||||
@@ -202,14 +201,14 @@ int SdBus::sd_bus_open_user_with_address(sd_bus **ret, const char* address)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_bus_client(bus, true);
|
||||
r = ::sd_bus_set_bus_client(bus, true); // NOLINT(readability-implicit-bool-conversion)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// Copying behavior from
|
||||
// https://github.com/systemd/systemd/blob/fee6441601c979165ebcbb35472036439f8dad5f/src/libsystemd/sd-bus/sd-bus.c#L1381
|
||||
// Here, we make the bus as trusted
|
||||
r = ::sd_bus_set_trusted(bus, true);
|
||||
r = ::sd_bus_set_trusted(bus, true); // NOLINT(readability-implicit-bool-conversion)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -281,7 +280,7 @@ int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_server(bus, true, id);
|
||||
r = ::sd_bus_set_server(bus, true, id); // NOLINT(readability-implicit-bool-conversion)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -296,65 +295,75 @@ int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
|
||||
|
||||
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
|
||||
{
|
||||
#ifdef SDBUS_basu
|
||||
#ifndef SDBUS_basu
|
||||
return ::sd_bus_open_system_remote(ret, host);
|
||||
#else
|
||||
(void)ret;
|
||||
(void)host;
|
||||
// https://git.sr.ht/~emersion/basu/commit/01d33b244eb6
|
||||
return -EOPNOTSUPP;
|
||||
#else
|
||||
return ::sd_bus_open_system_remote(ret, host);
|
||||
#endif
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_request_name(bus, name, flags);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_release_name(sd_bus *bus, const char *name)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_release_name(bus, name);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_unique_name(sd_bus *bus, const char **name)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_get_unique_name(bus, name);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_object_vtable(bus, slot, path, interface, vtable, userdata);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_object_manager(bus, slot, path);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_match(bus, slot, match, callback, userdata);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_match_async(bus, slot, match, callback, install_callback, userdata);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata)
|
||||
{
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_match_signal(bus, ret, sender, path, interface, member, callback, userdata);
|
||||
}
|
||||
|
||||
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_slot_unref(slot);
|
||||
}
|
||||
@@ -369,16 +378,21 @@ int SdBus::sd_bus_start(sd_bus *bus)
|
||||
return ::sd_bus_start(bus);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
|
||||
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **msg)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_process(bus, r);
|
||||
return ::sd_bus_process(bus, msg);
|
||||
}
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_get_current_message(sd_bus *bus)
|
||||
{
|
||||
return ::sd_bus_get_current_message(bus);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
auto r = ::sd_bus_get_fd(bus);
|
||||
if (r < 0)
|
||||
@@ -395,6 +409,16 @@ int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
|
||||
return r;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
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)
|
||||
{
|
||||
return ::sd_bus_flush(bus);
|
||||
@@ -415,74 +439,81 @@ sd_bus* SdBus::sd_bus_close_unref(sd_bus *bus)
|
||||
#endif
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_set_destination(sd_bus_message *m, const char *destination)
|
||||
int SdBus::sd_bus_message_set_destination(sd_bus_message *msg, const char *destination)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_set_destination(m, destination);
|
||||
return ::sd_bus_message_set_destination(msg, destination);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c)
|
||||
int SdBus::sd_bus_query_sender_creds(sd_bus_message *msg, uint64_t mask, sd_bus_creds **creds)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_query_sender_creds(m, mask, c);
|
||||
return ::sd_bus_query_sender_creds(msg, mask, creds);
|
||||
}
|
||||
|
||||
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *c)
|
||||
sd_bus_creds* SdBus::sd_bus_creds_ref(sd_bus_creds *creds)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_unref(c);
|
||||
return ::sd_bus_creds_ref(creds);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid)
|
||||
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *creds)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_pid(c, pid);
|
||||
return ::sd_bus_creds_unref(creds);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid)
|
||||
int SdBus::sd_bus_creds_get_pid(sd_bus_creds *creds, pid_t *pid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_uid(c, uid);
|
||||
return ::sd_bus_creds_get_pid(creds, pid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid)
|
||||
int SdBus::sd_bus_creds_get_uid(sd_bus_creds *creds, uid_t *uid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_euid(c, euid);
|
||||
return ::sd_bus_creds_get_uid(creds, uid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid)
|
||||
int SdBus::sd_bus_creds_get_euid(sd_bus_creds *creds, uid_t *euid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_gid(c, gid);
|
||||
return ::sd_bus_creds_get_euid(creds, euid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_egid(sd_bus_creds *c, uid_t *egid)
|
||||
int SdBus::sd_bus_creds_get_gid(sd_bus_creds *creds, gid_t *gid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_egid(c, egid);
|
||||
return ::sd_bus_creds_get_gid(creds, gid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids)
|
||||
int SdBus::sd_bus_creds_get_egid(sd_bus_creds *creds, uid_t *egid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_supplementary_gids(c, gids);
|
||||
return ::sd_bus_creds_get_egid(creds, egid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label)
|
||||
int SdBus::sd_bus_creds_get_supplementary_gids(sd_bus_creds *creds, const gid_t **gids)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_selinux_context(c, label);
|
||||
return ::sd_bus_creds_get_supplementary_gids(creds, gids);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_selinux_context(sd_bus_creds *creds, const char **label)
|
||||
{
|
||||
const std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_selinux_context(creds, label);
|
||||
}
|
||||
|
||||
} // namespace sdbus::internal
|
||||
|
||||
+57
-54
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file SdBus.h
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
@@ -36,72 +36,75 @@ namespace sdbus::internal {
|
||||
class SdBus final : public ISdBus
|
||||
{
|
||||
public:
|
||||
virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) override;
|
||||
virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) override;
|
||||
sd_bus_message* sd_bus_message_ref(sd_bus_message *msg) override;
|
||||
sd_bus_message* sd_bus_message_unref(sd_bus_message *msg) override;
|
||||
|
||||
virtual int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) override;
|
||||
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) override;
|
||||
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) override;
|
||||
int sd_bus_send(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie) override;
|
||||
int sd_bus_call(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) override;
|
||||
int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) override;
|
||||
|
||||
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type) override;
|
||||
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) override;
|
||||
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) override;
|
||||
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) override;
|
||||
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) override;
|
||||
int sd_bus_message_new(sd_bus *bus, sd_bus_message **msg, uint8_t type) override;
|
||||
int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member) override;
|
||||
int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member) override;
|
||||
int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **msg) override;
|
||||
int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err) override;
|
||||
|
||||
virtual int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) override;
|
||||
virtual int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) override;
|
||||
int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) override;
|
||||
int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) override;
|
||||
|
||||
virtual int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) override;
|
||||
virtual int sd_bus_emit_object_added(sd_bus *bus, const char *path) override;
|
||||
virtual int sd_bus_emit_object_removed(sd_bus *bus, const char *path) override;
|
||||
virtual int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) override;
|
||||
virtual int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) override;
|
||||
int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) override;
|
||||
int sd_bus_emit_object_added(sd_bus *bus, const char *path) override;
|
||||
int sd_bus_emit_object_removed(sd_bus *bus, const char *path) override;
|
||||
int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) override;
|
||||
int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) override;
|
||||
|
||||
virtual int sd_bus_open(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_system(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_user(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
|
||||
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override;
|
||||
virtual int sd_bus_open_direct(sd_bus **ret, const char* address) override;
|
||||
virtual int sd_bus_open_direct(sd_bus **ret, int fd) override;
|
||||
virtual int sd_bus_open_server(sd_bus **ret, int fd) override;
|
||||
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
|
||||
virtual int sd_bus_release_name(sd_bus *bus, const char *name) override;
|
||||
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
|
||||
virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
|
||||
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
|
||||
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
|
||||
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) override;
|
||||
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;
|
||||
int sd_bus_open(sd_bus **ret) override;
|
||||
int sd_bus_open_system(sd_bus **ret) override;
|
||||
int sd_bus_open_user(sd_bus **ret) override;
|
||||
int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
|
||||
int sd_bus_open_system_remote(sd_bus **ret, const char* host) override;
|
||||
int sd_bus_open_direct(sd_bus **ret, const char* address) override;
|
||||
int sd_bus_open_direct(sd_bus **ret, int fd) override;
|
||||
int sd_bus_open_server(sd_bus **ret, int fd) override;
|
||||
int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
|
||||
int sd_bus_release_name(sd_bus *bus, const char *name) override;
|
||||
int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
|
||||
int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
|
||||
int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
|
||||
int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
|
||||
int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) override;
|
||||
int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata) override;
|
||||
sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;
|
||||
|
||||
virtual int sd_bus_new(sd_bus **ret) override;
|
||||
virtual int sd_bus_start(sd_bus *bus) override;
|
||||
int sd_bus_new(sd_bus **ret) override;
|
||||
int sd_bus_start(sd_bus *bus) override;
|
||||
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override;
|
||||
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
|
||||
int sd_bus_process(sd_bus *bus, sd_bus_message **msg) override;
|
||||
sd_bus_message* sd_bus_get_current_message(sd_bus *bus) override;
|
||||
int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
|
||||
int sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) override;
|
||||
int sd_bus_flush(sd_bus *bus) override;
|
||||
sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override;
|
||||
sd_bus *sd_bus_close_unref(sd_bus *bus) 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;
|
||||
int sd_bus_message_set_destination(sd_bus_message *msg, const char *destination) override;
|
||||
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) override;
|
||||
int sd_bus_query_sender_creds(sd_bus_message *msg, uint64_t mask, sd_bus_creds **creds) override;
|
||||
sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *creds) override;
|
||||
sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *creds) 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_unref(sd_bus_creds *c) override;
|
||||
|
||||
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) override;
|
||||
virtual int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) override;
|
||||
virtual int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid) override;
|
||||
virtual int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) override;
|
||||
virtual int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) override;
|
||||
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) override;
|
||||
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) override;
|
||||
int sd_bus_creds_get_pid(sd_bus_creds *creds, pid_t *pid) override;
|
||||
int sd_bus_creds_get_uid(sd_bus_creds *creds, uid_t *uid) override;
|
||||
int sd_bus_creds_get_euid(sd_bus_creds *creds, uid_t *euid) override;
|
||||
int sd_bus_creds_get_gid(sd_bus_creds *creds, gid_t *gid) override;
|
||||
int sd_bus_creds_get_egid(sd_bus_creds *creds, gid_t *egid) override;
|
||||
int sd_bus_creds_get_supplementary_gids(sd_bus_creds *creds, const gid_t **gids) override;
|
||||
int sd_bus_creds_get_selinux_context(sd_bus_creds *creds, const char **label) override;
|
||||
|
||||
private:
|
||||
std::recursive_mutex sdbusMutex_;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#endif //SDBUS_C_SDBUS_H
|
||||
#endif // SDBUS_CXX_SDBUS_H
|
||||
|
||||
+11
-13
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Types.cpp
|
||||
*
|
||||
@@ -24,13 +24,13 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "MessageUtils.h"
|
||||
#include SDBUS_HEADER
|
||||
#include <cassert>
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/Message.h"
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <system_error>
|
||||
#include SDBUS_HEADER
|
||||
#include <unistd.h>
|
||||
|
||||
namespace sdbus {
|
||||
@@ -53,12 +53,10 @@ void Variant::deserializeFrom(Message& msg)
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
std::string Variant::peekValueType() const
|
||||
const char* Variant::peekValueType() const
|
||||
{
|
||||
msg_.rewind(false);
|
||||
std::string type;
|
||||
std::string contents;
|
||||
msg_.peekType(type, contents);
|
||||
auto [type, contents] = msg_.peekType();
|
||||
return contents;
|
||||
}
|
||||
|
||||
@@ -67,7 +65,7 @@ bool Variant::isEmpty() const
|
||||
return msg_.isEmpty();
|
||||
}
|
||||
|
||||
void UnixFd::close()
|
||||
void UnixFd::close() // NOLINT(readability-make-member-function-const)
|
||||
{
|
||||
if (fd_ >= 0)
|
||||
{
|
||||
@@ -82,7 +80,7 @@ int UnixFd::checkedDup(int fd)
|
||||
return fd;
|
||||
}
|
||||
|
||||
int ret = ::dup(fd);
|
||||
const int ret = ::dup(fd); // NOLINT(android-cloexec-dup) // TODO: verify
|
||||
if (ret < 0)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "dup failed");
|
||||
@@ -90,4 +88,4 @@ int UnixFd::checkedDup(int fd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
+29
-14
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Utils.h
|
||||
*
|
||||
@@ -30,18 +30,20 @@
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include SDBUS_HEADER
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#if LIBSYSTEMD_VERSION>=246
|
||||
#define SDBUS_CHECK_OBJECT_PATH(_PATH) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_object_path_is_valid(_PATH.c_str()), "Invalid object path '" + _PATH + "' provided", EINVAL) \
|
||||
#define SDBUS_CHECK_OBJECT_PATH(_PATH) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_object_path_is_valid(_PATH), std::string("Invalid object path '") + _PATH + "' provided", EINVAL) \
|
||||
/**/
|
||||
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME.c_str()), "Invalid interface name '" + _NAME + "' provided", EINVAL) \
|
||||
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME), std::string("Invalid interface name '") + _NAME + "' provided", EINVAL) \
|
||||
/**/
|
||||
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!_NAME.empty() && !sd_bus_service_name_is_valid(_NAME.c_str()), "Invalid service name '" + _NAME + "' provided", EINVAL) \
|
||||
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(*_NAME && !sd_bus_service_name_is_valid(_NAME), std::string("Invalid service name '") + _NAME + "' provided", EINVAL) \
|
||||
/**/
|
||||
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME.c_str()), "Invalid member name '" + _NAME + "' provided", EINVAL) \
|
||||
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME), std::string("Invalid member name '") + _NAME + "' provided", EINVAL) \
|
||||
/**/
|
||||
#else
|
||||
#define SDBUS_CHECK_OBJECT_PATH(_PATH)
|
||||
@@ -50,10 +52,12 @@
|
||||
#define SDBUS_CHECK_MEMBER_NAME(_NAME)
|
||||
#endif
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
template <typename _Callable>
|
||||
bool invokeHandlerAndCatchErrors(_Callable callable, sd_bus_error *retError)
|
||||
template <typename Callable>
|
||||
bool invokeHandlerAndCatchErrors(Callable callable, sd_bus_error *retError)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -66,22 +70,33 @@ namespace sdbus::internal {
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, e.what());
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, "Unknown error occurred");
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), "Unknown error occurred");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns time since epoch based of POSIX CLOCK_MONOTONIC,
|
||||
// so we use the very same clock as underlying sd-bus library.
|
||||
[[nodiscard]] inline auto now()
|
||||
{
|
||||
struct timespec ts{};
|
||||
auto r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "clock_gettime failed: ", -errno);
|
||||
|
||||
return std::chrono::nanoseconds(ts.tv_nsec) + std::chrono::seconds(ts.tv_sec);
|
||||
}
|
||||
|
||||
// Implementation of the overload pattern for variant visitation
|
||||
template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
|
||||
template <class... Ts> overload(Ts...) -> overload<Ts...>;
|
||||
|
||||
}
|
||||
} // namespace sdbus::internal
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_UTILS_H_ */
|
||||
|
||||
+22
-22
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableUtils.c
|
||||
*
|
||||
@@ -27,18 +27,18 @@
|
||||
#include "VTableUtils.h"
|
||||
#include SDBUS_HEADER
|
||||
|
||||
sd_bus_vtable createVTableStartItem(uint64_t flags)
|
||||
sd_bus_vtable createSdBusVTableStartItem(uint64_t flags)
|
||||
{
|
||||
struct sd_bus_vtable vtableStart = SD_BUS_VTABLE_START(flags);
|
||||
return vtableStart;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags )
|
||||
sd_bus_vtable createSdBusVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags )
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
// We have to expand macro SD_BUS_METHOD_WITH_NAMES manually here, because the macro expects literal char strings
|
||||
@@ -65,10 +65,10 @@ sd_bus_vtable createVTableMethodItem( const char *member
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableSignalItem( const char *member
|
||||
, const char *signature
|
||||
, const char *outnames
|
||||
, uint64_t flags )
|
||||
sd_bus_vtable createSdBusVTableSignalItem( const char *member
|
||||
, const char *signature
|
||||
, const char *outnames
|
||||
, uint64_t flags )
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL_WITH_NAMES(member, signature, outnames, flags);
|
||||
@@ -79,26 +79,26 @@ sd_bus_vtable createVTableSignalItem( const char *member
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, uint64_t flags )
|
||||
sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, flags);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags )
|
||||
sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, flags);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableEndItem()
|
||||
sd_bus_vtable createSdBusVTableEndItem()
|
||||
{
|
||||
struct sd_bus_vtable vtableEnd = SD_BUS_VTABLE_END;
|
||||
return vtableEnd;
|
||||
|
||||
+23
-23
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableUtils.h
|
||||
*
|
||||
@@ -27,34 +27,34 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
|
||||
#define SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
|
||||
|
||||
#include SDBUS_HEADER
|
||||
#include <stdbool.h>
|
||||
#include SDBUS_HEADER
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
sd_bus_vtable createVTableStartItem(uint64_t flags);
|
||||
sd_bus_vtable createVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createVTableSignalItem( const char *member
|
||||
, const char *signature
|
||||
, const char *outnames
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createVTablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createVTableEndItem();
|
||||
sd_bus_vtable createSdBusVTableStartItem(uint64_t flags);
|
||||
sd_bus_vtable createSdBusVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableSignalItem( const char *member
|
||||
, const char *signature
|
||||
, const char *outnames
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableEndItem();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+75
-70
@@ -2,15 +2,12 @@
|
||||
# DOWNLOAD AND BUILD OF GOOGLETEST
|
||||
#-------------------------------
|
||||
|
||||
set(GOOGLETEST_VERSION 1.10.0 CACHE STRING "Version of gmock to use")
|
||||
set(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")
|
||||
|
||||
find_package(GTest ${GOOGLETEST_VERSION} CONFIG)
|
||||
find_package(GTest ${SDBUSCPP_GOOGLETEST_VERSION} CONFIG)
|
||||
if (NOT TARGET GTest::gmock)
|
||||
# Try pkg-config if GTest was not found through CMake config
|
||||
find_package(PkgConfig)
|
||||
if (PkgConfig_FOUND)
|
||||
pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${GOOGLETEST_VERSION})
|
||||
pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${SDBUSCPP_GOOGLETEST_VERSION})
|
||||
if(TARGET PkgConfig::GMock)
|
||||
add_library(GTest::gmock ALIAS PkgConfig::GMock)
|
||||
endif()
|
||||
@@ -19,26 +16,26 @@ if (NOT TARGET GTest::gmock)
|
||||
if (NOT TARGET GTest::gmock)
|
||||
include(FetchContent)
|
||||
|
||||
message("Fetching googletest v${GOOGLETEST_VERSION}...")
|
||||
if (SDBUSCPP_GOOGLETEST_VERSION VERSION_GREATER_EQUAL 1.13.0)
|
||||
set(GOOGLETEST_TAG "v${SDBUSCPP_GOOGLETEST_VERSION}")
|
||||
else()
|
||||
set(GOOGLETEST_TAG "release-${SDBUSCPP_GOOGLETEST_VERSION}")
|
||||
endif()
|
||||
|
||||
message("Manually fetching & building googletest ${GOOGLETEST_TAG}...")
|
||||
FetchContent_Declare(googletest
|
||||
GIT_REPOSITORY ${GOOGLETEST_GIT_REPO}
|
||||
GIT_TAG release-${GOOGLETEST_VERSION}
|
||||
GIT_REPOSITORY ${SDBUSCPP_GOOGLETEST_GIT_REPO}
|
||||
GIT_TAG ${GOOGLETEST_TAG}
|
||||
GIT_SHALLOW 1
|
||||
UPDATE_COMMAND "")
|
||||
|
||||
#FetchContent_MakeAvailable(googletest) # Not available in CMake 3.13 :-( Let's do it manually:
|
||||
FetchContent_GetProperties(googletest)
|
||||
if(NOT googletest_POPULATED)
|
||||
FetchContent_Populate(googletest)
|
||||
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
|
||||
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
|
||||
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
|
||||
add_library(GTest::gmock ALIAS gmock)
|
||||
endif()
|
||||
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
|
||||
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
|
||||
add_library(GTest::gmock ALIAS gmock)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -50,54 +47,53 @@ set(UNITTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/unittests)
|
||||
set(UNITTESTS_SRCS
|
||||
${UNITTESTS_SOURCE_DIR}/sdbus-c++-unit-tests.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/Message_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/PollData_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/Types_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/TypeTraits_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/Connection_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/mocks/SdBusMock.h)
|
||||
|
||||
set(INTEGRATIONTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/integrationtests)
|
||||
set(INTEGRATIONTESTS_GENERATED_DIR ${INTEGRATIONTESTS_SOURCE_DIR}/dbus-api/gen-cpp)
|
||||
set(INTEGRATIONTESTS_SRCS
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusConnectionTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusGeneralTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusMethodsTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusAsyncMethodsTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusAwaitableMethodsTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusSignalsTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusPropertiesTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusStandardInterfacesTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/Defs.h
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/integrationtests-adaptor.h
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/integrationtests-proxy.h
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/TestFixture.h
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/TestFixture.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/TestAdaptor.h
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/TestAdaptor.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/TestProxy.h
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/TestProxy.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/sdbus-c++-integration-tests.cpp)
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/sdbus-c++-integration-tests.cpp
|
||||
${INTEGRATIONTESTS_GENERATED_DIR}/integrationtests-adaptor.h
|
||||
${INTEGRATIONTESTS_GENERATED_DIR}/integrationtests-proxy.h)
|
||||
|
||||
set(PERFTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/perftests)
|
||||
set(STRESSTESTS_CLIENT_SRCS
|
||||
set(PERFTESTS_GENERATED_DIR ${PERFTESTS_SOURCE_DIR}/dbus-api/gen-cpp)
|
||||
set(PERFTESTS_CLIENT_SRCS
|
||||
${PERFTESTS_SOURCE_DIR}/client.cpp
|
||||
${PERFTESTS_SOURCE_DIR}/perftests-proxy.h)
|
||||
set(STRESSTESTS_SERVER_SRCS
|
||||
${PERFTESTS_GENERATED_DIR}/perftests-proxy.h)
|
||||
set(PERFTESTS_SERVER_SRCS
|
||||
${PERFTESTS_SOURCE_DIR}/server.cpp
|
||||
${PERFTESTS_SOURCE_DIR}/perftests-adaptor.h)
|
||||
${PERFTESTS_GENERATED_DIR}/perftests-adaptor.h)
|
||||
|
||||
set(STRESSTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stresstests)
|
||||
set(STRESSTESTS_GENERATED_DIR ${STRESSTESTS_SOURCE_DIR}/dbus-api/gen-cpp)
|
||||
set(STRESSTESTS_SRCS
|
||||
${STRESSTESTS_SOURCE_DIR}/sdbus-c++-stress-tests.cpp
|
||||
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-adaptor.h
|
||||
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-proxy.h
|
||||
${STRESSTESTS_SOURCE_DIR}/celsius-thermometer-adaptor.h
|
||||
${STRESSTESTS_SOURCE_DIR}/celsius-thermometer-proxy.h
|
||||
${STRESSTESTS_SOURCE_DIR}/concatenator-adaptor.h
|
||||
${STRESSTESTS_SOURCE_DIR}/concatenator-proxy.h)
|
||||
|
||||
#-------------------------------
|
||||
# GENERAL COMPILER CONFIGURATION
|
||||
#-------------------------------
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
${STRESSTESTS_GENERATED_DIR}/fahrenheit-thermometer-adaptor.h
|
||||
${STRESSTESTS_GENERATED_DIR}/fahrenheit-thermometer-proxy.h
|
||||
${STRESSTESTS_GENERATED_DIR}/celsius-thermometer-adaptor.h
|
||||
${STRESSTESTS_GENERATED_DIR}/celsius-thermometer-proxy.h
|
||||
${STRESSTESTS_GENERATED_DIR}/concatenator-adaptor.h
|
||||
${STRESSTESTS_GENERATED_DIR}/concatenator-proxy.h)
|
||||
|
||||
#----------------------------------
|
||||
# BUILD INFORMATION
|
||||
@@ -105,33 +101,42 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
|
||||
target_compile_definitions(sdbus-c++-unit-tests PRIVATE
|
||||
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
|
||||
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
|
||||
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
SDBUS_${SDBUS_IMPL}
|
||||
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
|
||||
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib GTest::gmock)
|
||||
|
||||
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
|
||||
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
|
||||
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
|
||||
target_compile_definitions(sdbus-c++-integration-tests PRIVATE
|
||||
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
SDBUS_${SDBUS_IMPL})
|
||||
target_include_directories(sdbus-c++-integration-tests SYSTEM PRIVATE ${INTEGRATIONTESTS_GENERATED_DIR})
|
||||
if(NOT SDBUS_IMPL STREQUAL "basu")
|
||||
# Systemd::Libsystemd is included because integration tests use sd-event. Otherwise sdbus-c++ encapsulates and hides libsystemd.
|
||||
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ Systemd::Libsystemd GTest::gmock)
|
||||
else()
|
||||
# sd-event implementation is not part of basu, so its integration tests will be skipped
|
||||
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
|
||||
endif()
|
||||
|
||||
# Manual performance and stress tests
|
||||
option(ENABLE_PERF_TESTS "Build and install manual performance tests (default OFF)" OFF)
|
||||
option(ENABLE_STRESS_TESTS "Build and install manual stress tests (default OFF)" OFF)
|
||||
|
||||
if(ENABLE_PERF_TESTS OR ENABLE_STRESS_TESTS)
|
||||
if(SDBUSCPP_BUILD_PERF_TESTS OR SDBUSCPP_BUILD_STRESS_TESTS)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if(ENABLE_PERF_TESTS)
|
||||
if(SDBUSCPP_BUILD_PERF_TESTS)
|
||||
message(STATUS "Building with performance tests")
|
||||
add_executable(sdbus-c++-perf-tests-client ${STRESSTESTS_CLIENT_SRCS})
|
||||
add_executable(sdbus-c++-perf-tests-client ${PERFTESTS_CLIENT_SRCS})
|
||||
target_include_directories(sdbus-c++-perf-tests-client SYSTEM PRIVATE ${PERFTESTS_GENERATED_DIR})
|
||||
target_link_libraries(sdbus-c++-perf-tests-client sdbus-c++ Threads::Threads)
|
||||
add_executable(sdbus-c++-perf-tests-server ${STRESSTESTS_SERVER_SRCS})
|
||||
add_executable(sdbus-c++-perf-tests-server ${PERFTESTS_SERVER_SRCS})
|
||||
target_include_directories(sdbus-c++-perf-tests-server SYSTEM PRIVATE ${PERFTESTS_GENERATED_DIR})
|
||||
target_link_libraries(sdbus-c++-perf-tests-server sdbus-c++ Threads::Threads)
|
||||
endif()
|
||||
|
||||
if(ENABLE_STRESS_TESTS)
|
||||
if(SDBUSCPP_BUILD_STRESS_TESTS)
|
||||
message(STATUS "Building with stress tests")
|
||||
add_executable(sdbus-c++-stress-tests ${STRESSTESTS_SRCS})
|
||||
target_include_directories(sdbus-c++-stress-tests SYSTEM PRIVATE ${STRESSTESTS_GENERATED_DIR})
|
||||
target_link_libraries(sdbus-c++-stress-tests sdbus-c++ Threads::Threads)
|
||||
endif()
|
||||
endif()
|
||||
@@ -140,25 +145,25 @@ endif()
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
|
||||
option(INSTALL_TESTS "Install tests (default OFF)" OFF)
|
||||
|
||||
if(INSTALL_TESTS)
|
||||
if(SDBUSCPP_INSTALL)
|
||||
include(GNUInstallDirs)
|
||||
set(TESTS_INSTALL_PATH "tests/${PROJECT_NAME}" CACHE STRING "Specifies where the test binaries will be installed")
|
||||
|
||||
install(TARGETS sdbus-c++-unit-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(TARGETS sdbus-c++-integration-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
|
||||
if(ENABLE_PERF_TESTS)
|
||||
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
install(TARGETS sdbus-c++-unit-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(TARGETS sdbus-c++-integration-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
|
||||
COMPONENT sdbus-c++-test)
|
||||
if(SDBUSCPP_BUILD_PERF_TESTS)
|
||||
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
|
||||
COMPONENT sdbus-c++-test)
|
||||
endif()
|
||||
|
||||
if(ENABLE_STRESS_TESTS)
|
||||
install(TARGETS sdbus-c++-stress-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
|
||||
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
|
||||
if(SDBUSCPP_BUILD_STRESS_TESTS)
|
||||
install(TARGETS sdbus-c++-stress-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
|
||||
COMPONENT sdbus-c++-test)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file DBusAsyncMethodsTests.cpp
|
||||
*
|
||||
@@ -25,53 +25,54 @@
|
||||
*/
|
||||
|
||||
#include "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
#include "Defs.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <optional>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::DoubleEq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::Le;
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::SizeIs;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
using SdbusTestObject = TestFixture;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
|
||||
TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::steady_clock> start;
|
||||
try
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
m_proxy->doOperationClientSideAsyncWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
|
||||
this->m_proxy->doOperationClientSideAsyncWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
|
||||
future.get();
|
||||
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
@@ -89,39 +90,39 @@ TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
|
||||
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchronousMethodAsynchronously)
|
||||
{
|
||||
// Yeah, this is kinda timing-dependent test, but times should be safe...
|
||||
std::mutex mtx;
|
||||
std::vector<uint32_t> results;
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
std::atomic invoke{false};
|
||||
std::atomic startedCount{0};
|
||||
auto call = [&](uint32_t param)
|
||||
{
|
||||
TestProxy proxy{BUS_NAME, OBJECT_PATH};
|
||||
TestProxy proxy{SERVICE_NAME, OBJECT_PATH};
|
||||
++startedCount;
|
||||
while (!invoke) ;
|
||||
auto result = proxy.doOperationAsync(param);
|
||||
std::lock_guard<std::mutex> guard(mtx);
|
||||
std::lock_guard const guard(mtx);
|
||||
results.push_back(result);
|
||||
};
|
||||
|
||||
std::thread invocations[]{std::thread{call, 1500}, std::thread{call, 1000}, std::thread{call, 500}};
|
||||
while (startedCount != 3) ;
|
||||
invoke = true;
|
||||
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
|
||||
std::for_each(std::begin(invocations), std::end(invocations), [](auto& thread){ thread.join(); });
|
||||
|
||||
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
TYPED_TEST(AsyncSdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
{
|
||||
std::atomic<size_t> resultCount{};
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
std::atomic invoke{false};
|
||||
std::atomic startedCount{0};
|
||||
auto call = [&]()
|
||||
{
|
||||
TestProxy proxy{BUS_NAME, OBJECT_PATH};
|
||||
TestProxy proxy{SERVICE_NAME, OBJECT_PATH};
|
||||
++startedCount;
|
||||
while (!invoke) ;
|
||||
|
||||
@@ -139,38 +140,51 @@ TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
std::thread invocations[]{std::thread{call}, std::thread{call}, std::thread{call}};
|
||||
while (startedCount != 3) ;
|
||||
invoke = true;
|
||||
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
|
||||
std::for_each(std::begin(invocations), std::end(invocations), [](auto& thread){ thread.join(); });
|
||||
|
||||
ASSERT_THAT(resultCount, Eq(1500));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
|
||||
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;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(std::move(err)));
|
||||
});
|
||||
|
||||
m_proxy->doOperationClientSideAsync(100);
|
||||
this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(100));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
|
||||
{
|
||||
auto future = m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
|
||||
auto future = this->m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(100));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
|
||||
{
|
||||
auto future = m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
|
||||
auto future = this->m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
|
||||
|
||||
auto methodReply = future.get();
|
||||
uint32_t returnValue{};
|
||||
@@ -179,87 +193,111 @@ TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasic
|
||||
ASSERT_THAT(returnValue, Eq(100));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodWithLargeDataAsynchronouslyOnClientSideWithFuture)
|
||||
{
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
|
||||
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 call = m_proxy->doOperationClientSideAsync(100);
|
||||
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*/, const std::optional<sdbus::Error>& /*err*/){});
|
||||
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_TRUE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CancelsPendingAsyncCallOnClientSide)
|
||||
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
auto call = m_proxy->doOperationClientSideAsync(100);
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){ promise.set_value(1); });
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
|
||||
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSideByDestroyingOwningSlot)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
auto call = m_proxy->doOperationClientSideAsync(100);
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){ promise.set_value(1); });
|
||||
|
||||
{
|
||||
auto slot = this->m_proxy->doOperationClientSideAsync(100, 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(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){ promise.set_value(1); });
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_FALSE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){ promise.set_value(1); });
|
||||
|
||||
auto call = m_proxy->doOperationClientSideAsync(0);
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(0);
|
||||
(void) future.get(); // Wait for the call to finish
|
||||
|
||||
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
|
||||
{
|
||||
sdbus::PendingAsyncCall call;
|
||||
sdbus::PendingAsyncCall const call;
|
||||
|
||||
ASSERT_FALSE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, SupportsAsyncCallCopyAssignment)
|
||||
TYPED_TEST(AsyncSdbusTestObject, SupportsAsyncCallCopyAssignment)
|
||||
{
|
||||
sdbus::PendingAsyncCall call;
|
||||
|
||||
call = m_proxy->doOperationClientSideAsync(100);
|
||||
call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_TRUE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
|
||||
TYPED_TEST(AsyncSdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
m_proxy->doErroneousOperationClientSideAsync();
|
||||
this->m_proxy->doErroneousOperationClientSideAsync();
|
||||
|
||||
ASSERT_THROW(future.get(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
|
||||
TYPED_TEST(AsyncSdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
|
||||
{
|
||||
auto future = m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
|
||||
auto future = this->m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
|
||||
|
||||
ASSERT_THROW(future.get(), sdbus::Error);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2026 - Alex Cani <alexcani109@gmail.com>
|
||||
*
|
||||
* @file DBusAwaitableMethodsTests.cpp
|
||||
*
|
||||
* Created on: Mar 1, 2026
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <coroutine>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "TestFixture.h"
|
||||
#include "TestProxy.h"
|
||||
|
||||
using ::testing::Eq;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
// Simple coroutine task type for testing purposes
|
||||
// Uses a promise/future pair to communicate results and exceptions
|
||||
// between the coroutine and the test code. Do not mistake this for
|
||||
// the std::future-based async API of sdbus-c++.
|
||||
template<typename T>
|
||||
struct Task {
|
||||
struct promise_type {
|
||||
T value;
|
||||
std::exception_ptr exception;
|
||||
std::promise<T> completion;
|
||||
std::future<T> future;
|
||||
|
||||
promise_type() : future(completion.get_future()) {}
|
||||
|
||||
Task get_return_object() {
|
||||
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
|
||||
}
|
||||
|
||||
// Lazy coroutine
|
||||
std::suspend_always initial_suspend() noexcept { return {}; }
|
||||
|
||||
// final_suspend suspends so that the test code can retrieve the result
|
||||
// or exception from the promise before the coroutine is destroyed
|
||||
std::suspend_always final_suspend() noexcept
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
completion.set_exception(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
completion.set_value(std::move(value));
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void return_value(T val) { value = std::move(val); }
|
||||
void unhandled_exception() { exception = std::current_exception(); }
|
||||
};
|
||||
|
||||
std::coroutine_handle<promise_type> handle;
|
||||
|
||||
// Ctor and rule of 5 for proper handle management
|
||||
explicit Task(std::coroutine_handle<promise_type> hnd) : handle(hnd) {}
|
||||
Task(Task&& other) noexcept : handle(std::exchange(other.handle, {})) {}
|
||||
Task& operator=(Task&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (handle) handle.destroy();
|
||||
handle = std::exchange(other.handle, {});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Task(const Task&) = delete;
|
||||
Task& operator=(const Task&) = delete;
|
||||
~Task() { if (handle) handle.destroy(); }
|
||||
|
||||
// "User API" for the test code, allows starting the task and retrieving the result or exception
|
||||
void resume() { if (handle && !handle.done()) handle.resume(); }
|
||||
T get() { return handle.promise().future.get(); }
|
||||
};
|
||||
|
||||
// Specialization for void
|
||||
template<>
|
||||
struct Task<void> {
|
||||
struct promise_type {
|
||||
std::exception_ptr exception;
|
||||
std::promise<void> completion;
|
||||
std::future<void> future;
|
||||
|
||||
promise_type() : future(completion.get_future()) {}
|
||||
|
||||
Task get_return_object() {
|
||||
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
|
||||
}
|
||||
|
||||
std::suspend_always initial_suspend() noexcept { return {}; } // NOLINT(readability-convert-member-functions-to-static)
|
||||
|
||||
std::suspend_always final_suspend() noexcept
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
completion.set_exception(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
completion.set_value();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void return_void() {}
|
||||
void unhandled_exception() { exception = std::current_exception(); }
|
||||
};
|
||||
|
||||
std::coroutine_handle<promise_type> handle;
|
||||
|
||||
explicit Task(std::coroutine_handle<promise_type> hnd) : handle(hnd) {}
|
||||
Task(Task&& other) noexcept : handle(std::exchange(other.handle, {})) {}
|
||||
Task& operator=(Task&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (handle) handle.destroy();
|
||||
handle = std::exchange(other.handle, {});
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Task(const Task&) = delete;
|
||||
Task& operator=(const Task&) = delete;
|
||||
|
||||
~Task() { if (handle) handle.destroy(); }
|
||||
|
||||
void resume() { if (handle && !handle.done()) handle.resume(); } // NOLINT(readability-make-member-function-const)
|
||||
void get() { handle.promise().future.get(); } // NOLINT(readability-make-member-function-const)
|
||||
};
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithAwaitable)
|
||||
{
|
||||
auto task = [](TestProxy* proxy) -> Task<uint32_t> {
|
||||
co_return co_await proxy->doOperationClientSideAsync(100, sdbus::with_awaitable);
|
||||
}(this->m_proxy.get());
|
||||
|
||||
task.resume();
|
||||
|
||||
ASSERT_THAT(task.get(), Eq(100));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithAwaitableOnBasicAPILevel)
|
||||
{
|
||||
auto task = [](TestProxy* proxy) -> Task<uint32_t> {
|
||||
auto methodReply = co_await proxy->doOperationClientSideAsyncOnBasicAPILevel(100, sdbus::with_awaitable);
|
||||
uint32_t returnValue{};
|
||||
methodReply >> returnValue;
|
||||
co_return returnValue;
|
||||
}(this->m_proxy.get());
|
||||
|
||||
task.resume();
|
||||
|
||||
ASSERT_THAT(task.get(), Eq(100));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodWithLargeDataAsynchronouslyOnClientSideWithAwaitable)
|
||||
{
|
||||
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));
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) -- lambda closure has guaranteed lifetime
|
||||
auto lambda = [&largeMap, this]() -> Task<std::map<int32_t, std::string>> {
|
||||
co_return co_await this->m_proxy->doOperationWithLargeDataClientSideAsync(largeMap, sdbus::with_awaitable);
|
||||
};
|
||||
auto task = lambda();
|
||||
|
||||
task.resume();
|
||||
|
||||
ASSERT_THAT(task.get(), Eq(largeMap));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithAwaitableFails)
|
||||
{
|
||||
auto task = [](TestProxy* proxy) -> Task<void> {
|
||||
co_await proxy->doErroneousOperationClientSideAsync(sdbus::with_awaitable);
|
||||
}(this->m_proxy.get());
|
||||
|
||||
task.resume();
|
||||
|
||||
ASSERT_THROW(task.get(), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AwaitableSupportsMultipleSequentialCalls)
|
||||
{
|
||||
auto task = [](TestProxy* proxy) -> Task<uint32_t> {
|
||||
auto result1 = co_await proxy->doOperationClientSideAsync(10, sdbus::with_awaitable);
|
||||
auto result2 = co_await proxy->doOperationClientSideAsync(20, sdbus::with_awaitable);
|
||||
auto result3 = co_await proxy->doOperationClientSideAsync(30, sdbus::with_awaitable);
|
||||
co_return result1 + result2 + result3;
|
||||
}(this->m_proxy.get());
|
||||
|
||||
task.resume();
|
||||
|
||||
ASSERT_THAT(task.get(), Eq(60));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AwaitablePropagatesExceptionsCorrectly)
|
||||
{
|
||||
auto task = [](TestProxy* proxy) -> Task<std::string> {
|
||||
try {
|
||||
co_await proxy->doErroneousOperationClientSideAsync(sdbus::with_awaitable);
|
||||
co_return "FAILED";
|
||||
} catch (const sdbus::Error& e) {
|
||||
// Verify we can inspect the exception
|
||||
co_return std::string(e.getName());
|
||||
}
|
||||
}(this->m_proxy.get());
|
||||
|
||||
task.resume();
|
||||
|
||||
ASSERT_THAT(task.get(), ::testing::HasSubstr("Error"));
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file DBusConnectionTests.cpp
|
||||
*
|
||||
@@ -33,15 +33,11 @@
|
||||
|
||||
// gmock
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
// STL
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
using ::testing::Eq;
|
||||
using namespace sdbus::test;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
@@ -49,85 +45,49 @@ using namespace std::chrono_literals;
|
||||
|
||||
TEST(Connection, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(auto con = sdbus::createConnection());
|
||||
ASSERT_NO_THROW(auto con = sdbus::createBusConnection());
|
||||
}
|
||||
|
||||
TEST(Connection, CanRequestRegisteredDbusName)
|
||||
TEST(Connection, CanRequestName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
auto connection = sdbus::createBusConnection();
|
||||
|
||||
ASSERT_NO_THROW(connection->requestName(BUS_NAME))
|
||||
// In case of system bus connection, requesting may throw as we need to allow that first through a config file in /etc/dbus-1/system.d
|
||||
ASSERT_NO_THROW(connection->requestName(SERVICE_NAME))
|
||||
<< "Perhaps you've forgotten to copy `org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory before running the tests?";
|
||||
}
|
||||
|
||||
TEST(Connection, CannotRequestNonregisteredDbusName)
|
||||
TEST(SystemBusConnection, CannotRequestNonregisteredDbusName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->requestName("some.random.not.supported.dbus.name"), sdbus::Error);
|
||||
auto connection = sdbus::createSystemBusConnection();
|
||||
sdbus::ServiceName const notSupportedBusName{"some.random.not.supported.dbus.name"};
|
||||
|
||||
ASSERT_THROW(connection->requestName(notSupportedBusName), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanReleasedRequestedName)
|
||||
TEST(Connection, CanReleaseRequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
auto connection = sdbus::createBusConnection();
|
||||
connection->requestName(SERVICE_NAME);
|
||||
|
||||
connection->requestName(BUS_NAME);
|
||||
ASSERT_NO_THROW(connection->releaseName(BUS_NAME));
|
||||
ASSERT_NO_THROW(connection->releaseName(SERVICE_NAME));
|
||||
}
|
||||
|
||||
TEST(Connection, CannotReleaseNonrequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
|
||||
auto connection = sdbus::createBusConnection();
|
||||
sdbus::ServiceName const notAcquiredBusName{"some.random.unacquired.name"};
|
||||
|
||||
ASSERT_THROW(connection->releaseName(notAcquiredBusName), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanEnterAndLeaveEventLoop)
|
||||
TEST(Connection, CanEnterAndLeaveInternalEventLoop)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(BUS_NAME);
|
||||
auto connection = sdbus::createBusConnection();
|
||||
connection->requestName(SERVICE_NAME);
|
||||
|
||||
std::thread t([&](){ connection->enterEventLoop(); });
|
||||
connection->leaveEventLoop();
|
||||
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(Connection, PollDataGetZeroTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pd{};
|
||||
pd.timeout_usec = 0;
|
||||
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
|
||||
EXPECT_THAT(pd.getRelativeTimeout().value(), Eq(std::chrono::microseconds::zero()));
|
||||
EXPECT_THAT(pd.getPollTimeout(), Eq(0));
|
||||
}
|
||||
|
||||
TEST(Connection, PollDataGetInfiniteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pd{};
|
||||
pd.timeout_usec = UINT64_MAX;
|
||||
ASSERT_FALSE(pd.getRelativeTimeout().has_value());
|
||||
EXPECT_THAT(pd.getPollTimeout(), Eq(-1));
|
||||
}
|
||||
|
||||
TEST(Connection, PollDataGetZeroRelativeTimeoutForPast)
|
||||
{
|
||||
sdbus::IConnection::PollData pd{};
|
||||
auto past = std::chrono::steady_clock::now() - 10s;
|
||||
pd.timeout_usec = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch()).count();
|
||||
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
|
||||
EXPECT_THAT(pd.getRelativeTimeout().value(), Eq(0us));
|
||||
EXPECT_THAT(pd.getPollTimeout(), Eq(0));
|
||||
}
|
||||
|
||||
TEST(Connection, PollDataGetRelativeTimeoutInTolerance)
|
||||
{
|
||||
sdbus::IConnection::PollData pd{};
|
||||
constexpr auto TIMEOUT = 1s;
|
||||
constexpr auto TOLERANCE = 100ms;
|
||||
auto future = std::chrono::steady_clock::now() + TIMEOUT;
|
||||
pd.timeout_usec = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch()).count();
|
||||
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
|
||||
EXPECT_GE(pd.getRelativeTimeout().value(), TIMEOUT - TOLERANCE);
|
||||
EXPECT_LE(pd.getRelativeTimeout().value(), TIMEOUT + TOLERANCE);
|
||||
EXPECT_GE(pd.getPollTimeout(), 900);
|
||||
EXPECT_LE(pd.getPollTimeout(), 1100);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file DBusGeneralTests.cpp
|
||||
*
|
||||
@@ -27,143 +27,144 @@
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "TestFixture.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
#include "Defs.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <string_view>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
#include <variant>
|
||||
#include <type_traits>
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
using AConnection = TestFixture;
|
||||
using ADirectConnection = TestFixtureWithDirectConnection;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccessfully)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(BUS_NAME);
|
||||
auto connection = sdbus::createBusConnection();
|
||||
connection->requestName(SERVICE_NAME);
|
||||
|
||||
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
|
||||
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
|
||||
ASSERT_NO_THROW(const TestAdaptor adaptor(*connection, OBJECT_PATH));
|
||||
ASSERT_NO_THROW(const TestProxy proxy(SERVICE_NAME, OBJECT_PATH));
|
||||
|
||||
connection->releaseName(SERVICE_NAME);
|
||||
}
|
||||
|
||||
TEST(AProxy, SupportsMoveSemantics)
|
||||
TEST(AProxy, DoesNotSupportMoveSemantics)
|
||||
{
|
||||
static_assert(std::is_move_constructible_v<DummyTestProxy>);
|
||||
static_assert(std::is_move_assignable_v<DummyTestProxy>);
|
||||
static_assert(!std::is_move_constructible_v<DummyTestProxy>);
|
||||
static_assert(!std::is_move_assignable_v<DummyTestProxy>);
|
||||
}
|
||||
|
||||
TEST(AnAdaptor, SupportsMoveSemantics)
|
||||
TEST(AnAdaptor, DoesNotSupportMoveSemantics)
|
||||
{
|
||||
static_assert(std::is_move_constructible_v<DummyTestAdaptor>);
|
||||
static_assert(std::is_move_assignable_v<DummyTestAdaptor>);
|
||||
static_assert(!std::is_move_constructible_v<DummyTestAdaptor>);
|
||||
static_assert(!std::is_move_assignable_v<DummyTestAdaptor>);
|
||||
}
|
||||
|
||||
TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
|
||||
TYPED_TEST(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
|
||||
{
|
||||
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic matchingMessageReceived{false};
|
||||
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](const sdbus::Message& msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
});
|
||||
}, sdbus::return_slot);
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(matchingMessageReceived));
|
||||
}
|
||||
|
||||
TEST_F(AConnection, CanInstallMatchRuleAsynchronously)
|
||||
TYPED_TEST(AConnection, CanInstallMatchRuleAsynchronously)
|
||||
{
|
||||
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
std::atomic<bool> matchRuleInstalled{false};
|
||||
auto slot = s_proxyConnection->addMatchAsync( matchRule
|
||||
, [&](sdbus::Message& msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
}
|
||||
, [&](sdbus::Message& /*msg*/)
|
||||
{
|
||||
matchRuleInstalled = true;
|
||||
} );
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic matchingMessageReceived{false};
|
||||
std::atomic matchRuleInstalled{false};
|
||||
auto slot = this->s_proxyConnection->addMatchAsync( matchRule
|
||||
, [&](const sdbus::Message& msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
}
|
||||
, [&](const sdbus::Message& /*msg*/)
|
||||
{
|
||||
matchRuleInstalled = true;
|
||||
}
|
||||
, sdbus::return_slot );
|
||||
|
||||
EXPECT_TRUE(waitUntil(matchRuleInstalled));
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(matchingMessageReceived));
|
||||
}
|
||||
|
||||
TEST_F(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
|
||||
TYPED_TEST(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
|
||||
{
|
||||
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic matchingMessageReceived{false};
|
||||
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](const sdbus::Message& msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
});
|
||||
}, sdbus::return_slot);
|
||||
slot.reset();
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
|
||||
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
|
||||
}
|
||||
|
||||
TEST_F(AConnection, CanAddFloatingMatchRule)
|
||||
TYPED_TEST(AConnection, CanAddFloatingMatchRule)
|
||||
{
|
||||
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
auto con = sdbus::createSystemBusConnection();
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic matchingMessageReceived{false};
|
||||
auto con = sdbus::createBusConnection();
|
||||
con->enterEventLoopAsync();
|
||||
auto callback = [&](sdbus::Message& msg)
|
||||
auto callback = [&](const sdbus::Message& msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
};
|
||||
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
|
||||
m_adaptor->emitSimpleSignal();
|
||||
con->addMatch(matchRule, std::move(callback));
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
[[maybe_unused]] auto gotMessage = waitUntil(matchingMessageReceived, 2s);
|
||||
assert(gotMessage);
|
||||
matchingMessageReceived = false;
|
||||
|
||||
con.reset();
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
|
||||
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
|
||||
}
|
||||
|
||||
TEST_F(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
|
||||
TYPED_TEST(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
|
||||
{
|
||||
auto matchRule = "type='signal',interface='" + INTERFACE_NAME + "',member='simpleSignal'";
|
||||
std::atomic<size_t> numberOfMatchingMessages{};
|
||||
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
|
||||
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](const sdbus::Message& msg)
|
||||
{
|
||||
if(msg.getMemberName() == "simpleSignal")
|
||||
if(msg.getMemberName() == "simpleSignal"sv)
|
||||
numberOfMatchingMessages++;
|
||||
});
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
|
||||
}, sdbus::return_slot);
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
m_adaptor->emitSignalWithMap({});
|
||||
this->m_adaptor->emitSignalWithMap({});
|
||||
adaptor2->emitSimpleSignal();
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
|
||||
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file DBusMethodsTests.cpp
|
||||
*
|
||||
@@ -25,156 +25,171 @@
|
||||
*/
|
||||
|
||||
#include "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
#include "Defs.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::DoubleEq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::Le;
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::NotNull;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
using SdbusTestObject = TestFixture;
|
||||
namespace my {
|
||||
struct Struct
|
||||
{
|
||||
int i{};
|
||||
std::string s;
|
||||
std::vector<double> l;
|
||||
|
||||
friend bool operator==(const Struct &lhs, const Struct &rhs) = default;
|
||||
};
|
||||
} // namespace my
|
||||
|
||||
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l); // NOLINT(readability-identifier-length)
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsEmptyMethodSuccessfully)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
|
||||
ASSERT_NO_THROW(this->m_proxy->noArgNoReturn());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccessfully)
|
||||
{
|
||||
auto resInt = m_proxy->getInt();
|
||||
auto resInt = this->m_proxy->getInt();
|
||||
ASSERT_THAT(resInt, Eq(INT32_VALUE));
|
||||
|
||||
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
auto multiplyRes = this->m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithTuplesSuccessfully)
|
||||
{
|
||||
auto resTuple = m_proxy->getTuple();
|
||||
auto resTuple = this->m_proxy->getTuple();
|
||||
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
|
||||
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithStructSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccessfully)
|
||||
{
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
|
||||
auto vectorRes = m_proxy->getInts16FromStruct(a);
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> const strctA{};
|
||||
auto vectorRes = this->m_proxy->getInts16FromStruct(strctA);
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{0})); // because second item is by default initialized to 0
|
||||
|
||||
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> b{
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> const strctB{
|
||||
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
|
||||
};
|
||||
vectorRes = m_proxy->getInts16FromStruct(b);
|
||||
vectorRes = this->m_proxy->getInts16FromStruct(strctB);
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithVariantSuccessfully)
|
||||
{
|
||||
sdbus::Variant v{DOUBLE_VALUE};
|
||||
auto variantRes = m_proxy->processVariant(v);
|
||||
sdbus::Variant const var{DOUBLE_VALUE};
|
||||
sdbus::Variant const variantRes = this->m_proxy->processVariant(var);
|
||||
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStdVariantSuccessfully)
|
||||
{
|
||||
std::vector<int32_t> x{-2, 0, 2};
|
||||
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
|
||||
auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
|
||||
decltype(mapOfVariants) res{ {sdbus::Variant{-2}, sdbus::Variant{false}}
|
||||
, {sdbus::Variant{0}, sdbus::Variant{false}}
|
||||
, {sdbus::Variant{2}, sdbus::Variant{true}}};
|
||||
std::variant<int32_t, double, std::string> const var{DOUBLE_VALUE};
|
||||
auto variantRes = this->m_proxy->processVariant(var);
|
||||
ASSERT_THAT(std::get<int32_t>(variantRes), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccessfully)
|
||||
{
|
||||
std::vector const vec{-2, 0, 2};
|
||||
sdbus::Struct<sdbus::Variant, sdbus::Variant> const strct{false, true};
|
||||
std::map<int32_t, sdbus::Variant> mapOfVariants = this->m_proxy->getMapOfVariants(vec, strct);
|
||||
decltype(mapOfVariants) res{ {-2, sdbus::Variant{false}}
|
||||
, {0, sdbus::Variant{false}}
|
||||
, {2, sdbus::Variant{true}}};
|
||||
|
||||
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
|
||||
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
|
||||
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructInStructSuccessfully)
|
||||
{
|
||||
auto val = m_proxy->getStructInStruct();
|
||||
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
|
||||
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));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoStructsSuccessfully)
|
||||
{
|
||||
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
|
||||
auto val = this->m_proxy->sumStructItems({1, 2}, {3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoVectorsSuccessfully)
|
||||
{
|
||||
auto val = m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
|
||||
auto val = this->m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithSignatureSuccessfully)
|
||||
{
|
||||
auto resSignature = m_proxy->getSignature();
|
||||
auto resSignature = this->m_proxy->getSignature();
|
||||
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithObjectPathSuccessfully)
|
||||
{
|
||||
auto resObjectPath = m_proxy->getObjPath();
|
||||
auto resObjectPath = this->m_proxy->getObjPath();
|
||||
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithUnixFdSuccessfully)
|
||||
{
|
||||
auto resUnixFd = m_proxy->getUnixFd();
|
||||
auto resUnixFd = this->m_proxy->getUnixFd();
|
||||
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithComplexTypeSuccessfully)
|
||||
{
|
||||
auto resComplex = m_proxy->getComplex();
|
||||
auto resComplex = this->m_proxy->getComplex();
|
||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
|
||||
TYPED_TEST(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
|
||||
{
|
||||
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
|
||||
this->m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_adaptor->m_wasMultiplyCalled));
|
||||
ASSERT_THAT(m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasMultiplyCalled));
|
||||
ASSERT_THAT(this->m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
|
||||
{
|
||||
auto res = m_proxy->doOperationWithTimeout(500ms, 20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
|
||||
auto res = this->m_proxy->doOperationWithTimeout(500ms, (20ms).count()); // The operation will take 20ms, but the timeout is 500ms, so we are fine
|
||||
ASSERT_THAT(res, Eq(20));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
|
||||
TYPED_TEST(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
try
|
||||
{
|
||||
m_proxy->doOperationWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 1us, so we should time out
|
||||
this->m_proxy->doOperationWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
@@ -190,11 +205,11 @@ TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodThatThrowsError)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_proxy->throwError();
|
||||
this->m_proxy->throwError();
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
@@ -208,82 +223,158 @@ TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
|
||||
TYPED_TEST(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
|
||||
ASSERT_NO_THROW(this->m_proxy->throwErrorWithNoReply());
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
|
||||
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasThrowErrorCalled));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||
ASSERT_THROW(this->m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
|
||||
ASSERT_THROW(this->m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
|
||||
{
|
||||
TestProxy proxy("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
|
||||
TestProxy proxy(sdbus::ServiceName{"sdbuscpp.destination.that.does.not.exist"}, OBJECT_PATH);
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
|
||||
{
|
||||
TestProxy proxy(BUS_NAME, "/sdbuscpp/path/that/does/not/exist");
|
||||
TestProxy proxy(SERVICE_NAME, sdbus::ObjectPath{"/sdbuscpp/path/that/does/not/exist"});
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
|
||||
TYPED_TEST(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
|
||||
{
|
||||
m_proxy->emitTwoSimpleSignals();
|
||||
this->m_proxy->emitTwoSimpleSignals();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
|
||||
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
|
||||
TYPED_TEST(SdbusTestObject, CanSendAndReceiveDictionariesAsCustomStructsImplicitly)
|
||||
{
|
||||
m_proxy->doOperation(10); // This will save pointer to method call message on server side
|
||||
// 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;
|
||||
|
||||
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
|
||||
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperation"));
|
||||
this->m_proxy->getProxy().callMethod("returnDictionary")
|
||||
.onInterface("org.sdbuscpp.integrationtests")
|
||||
.withArguments(sdbus::as_dictionary(structSent))
|
||||
.storeResultsTo(structReceived);
|
||||
|
||||
ASSERT_THAT(structReceived, Eq(structSent));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
|
||||
{
|
||||
m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
|
||||
this->m_proxy->doOperation(10); // This will save pointer to method call message on server side
|
||||
|
||||
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
|
||||
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperationAsync"));
|
||||
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
|
||||
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
|
||||
|
||||
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
|
||||
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
|
||||
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
|
||||
TYPED_TEST(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
|
||||
{
|
||||
s_adaptorConnection->setMethodCallTimeout(5000000);
|
||||
ASSERT_THAT(s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
|
||||
this->s_adaptorConnection->setMethodCallTimeout(5000000);
|
||||
ASSERT_THAT(this->s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
|
||||
}
|
||||
#else
|
||||
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
|
||||
TYPED_TEST(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
|
||||
{
|
||||
ASSERT_THROW(s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
|
||||
ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
|
||||
ASSERT_THROW(this->s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
|
||||
ASSERT_THROW(this->s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
|
||||
TYPED_TEST(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
|
||||
{
|
||||
#if defined(__clang__) && defined(__FreeBSD__)
|
||||
GTEST_SKIP() << "https://github.com/Kistler-Group/sdbus-cpp/issues/359";
|
||||
#endif
|
||||
|
||||
auto proxy = std::make_unique<TestProxy>(BUS_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
|
||||
auto proxy = std::make_unique<TestProxy>(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
|
||||
|
||||
auto multiplyRes = proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime)
|
||||
{
|
||||
auto& object = this->m_adaptor->getObject();
|
||||
sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"};
|
||||
auto vtableSlot = object.addVTable( interfaceName
|
||||
, { sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })
|
||||
, sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) }
|
||||
, 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::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
int result{};
|
||||
proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2).storeResultsTo(result);
|
||||
|
||||
ASSERT_THAT(result, Eq(8));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime)
|
||||
{
|
||||
auto& object = this->m_adaptor->getObject();
|
||||
sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"};
|
||||
|
||||
auto vtableSlot = object.addVTable( interfaceName
|
||||
, { sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })
|
||||
, sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) }
|
||||
, sdbus::return_slot );
|
||||
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::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
ASSERT_THROW(proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2), sdbus::Error);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file DBusPropertiesTests.cpp
|
||||
*
|
||||
@@ -25,61 +25,57 @@
|
||||
*/
|
||||
|
||||
#include "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
#include "Defs.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::DoubleEq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::Not;
|
||||
using ::testing::IsEmpty;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
using SdbusTestObject = TestFixture;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST_F(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, ReadsReadOnlyPropertySuccessfully)
|
||||
{
|
||||
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
|
||||
ASSERT_THAT(this->m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
|
||||
TYPED_TEST(SdbusTestObject, FailsWritingToReadOnlyProperty)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->setStateProperty("new_value"), sdbus::Error);
|
||||
ASSERT_THROW(this->m_proxy->setStateProperty("new_value"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWritePropertySuccessfully)
|
||||
{
|
||||
uint32_t newActionValue = 5678;
|
||||
uint32_t const newActionValue = 5678;
|
||||
|
||||
m_proxy->action(newActionValue);
|
||||
this->m_proxy->action(newActionValue);
|
||||
|
||||
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
|
||||
{
|
||||
m_proxy->blocking(true); // This will save pointer to property get message on server side
|
||||
this->m_proxy->blocking(true); // This will save pointer to property get message on server side
|
||||
|
||||
ASSERT_THAT(m_adaptor->m_propertySetMsg, NotNull());
|
||||
ASSERT_THAT(m_adaptor->m_propertySetSender, Not(IsEmpty()));
|
||||
ASSERT_THAT(this->m_adaptor->m_propertySetMsg, NotNull());
|
||||
ASSERT_THAT(this->m_adaptor->m_propertySetSender, Not(IsEmpty()));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWriteVariantPropertySuccessfully)
|
||||
{
|
||||
sdbus::Variant const newActionValue{5678};
|
||||
|
||||
this->m_proxy->actionVariant(newActionValue);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->actionVariant().template get<int>(), Eq(5678));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file AdaptorAndProxy_test.cpp
|
||||
*
|
||||
@@ -27,143 +27,147 @@
|
||||
#include "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
#include "Defs.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::DoubleEq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::NotNull;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
using SdbusTestObject = TestFixture;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalSuccessfully)
|
||||
{
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccessfully)
|
||||
{
|
||||
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
|
||||
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
|
||||
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);
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
|
||||
TYPED_TEST(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
|
||||
{
|
||||
auto otherBusName = BUS_NAME + "2";
|
||||
auto connection2 = sdbus::createConnection(otherBusName);
|
||||
sdbus::ServiceName const otherBusName{SERVICE_NAME + "2"};
|
||||
auto connection2 = sdbus::createBusConnection(otherBusName);
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*connection2, OBJECT_PATH);
|
||||
|
||||
adaptor2->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccessfully)
|
||||
{
|
||||
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
|
||||
this->m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
|
||||
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("zero"));
|
||||
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("one"));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("zero"));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("one"));
|
||||
}
|
||||
|
||||
TEST_F(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));
|
||||
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(m_proxy->m_gotSignalWithMap));
|
||||
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
|
||||
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
|
||||
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"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccessfully)
|
||||
{
|
||||
double d = 3.14;
|
||||
m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
|
||||
double const val = 3.14;
|
||||
this->m_adaptor->emitSignalWithVariant(sdbus::Variant{val});
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
|
||||
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithVariant));
|
||||
ASSERT_THAT(this->m_proxy->m_variantFromSignal, DoubleEq(val));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccessfully)
|
||||
{
|
||||
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
|
||||
this->m_adaptor->emitSignalWithoutRegistration({"platform", sdbus::Signature{"av"}});
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
|
||||
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithSignature));
|
||||
ASSERT_THAT(this->m_proxy->m_signatureFromSignal["platform"], Eq(sdbus::Signature{"av"}));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithErrorAndTypeMismatchSuccessfully)
|
||||
{
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSignalWithErrorAndTypeMismatch();
|
||||
|
||||
waitUntil(m_proxy->m_gotSimpleSignal);
|
||||
|
||||
ASSERT_THAT(m_proxy->m_signalMsg, NotNull());
|
||||
ASSERT_THAT(m_proxy->m_signalMemberName, Eq("simpleSignal"));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithTypeMismatch));
|
||||
ASSERT_TRUE(this->m_proxy->m_errorFromSignal.has_value());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, UnregistersSignalHandler)
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
waitUntil(this->m_proxy->m_gotSimpleSignal);
|
||||
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
ASSERT_THAT(this->m_proxy->m_signalMsg, NotNull());
|
||||
ASSERT_THAT(this->m_proxy->m_signalName, Eq(std::string("simpleSignal")));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
|
||||
TYPED_TEST(SdbusTestObject, UnregistersSignalHandler)
|
||||
{
|
||||
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
|
||||
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
|
||||
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
|
||||
{
|
||||
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);
|
||||
|
||||
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, ReRegistersSignalHandler)
|
||||
TYPED_TEST(SdbusTestObject, ReRegistersSignalHandler)
|
||||
{
|
||||
// unregister simple-signal handler
|
||||
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
|
||||
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
|
||||
// re-register simple-signal handler
|
||||
ASSERT_NO_THROW(m_proxy->reRegisterSimpleSignalHandler());
|
||||
ASSERT_NO_THROW(this->m_proxy->reRegisterSimpleSignalHandler());
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file DBusStandardInterfacesTests.cpp
|
||||
*
|
||||
@@ -26,259 +26,321 @@
|
||||
|
||||
#include "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
#include "Defs.h"
|
||||
#include "integrationtests-adaptor.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include <exception>
|
||||
#include <cstdint>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <optional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::DoubleEq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::AnyOf;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::SizeIs;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
using SdbusTestObject = TestFixture;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST_F(SdbusTestObject, PingsViaPeerInterface)
|
||||
TYPED_TEST(SdbusTestObject, PingsViaPeerInterface)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->Ping());
|
||||
ASSERT_NO_THROW(this->m_proxy->Ping());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
|
||||
TYPED_TEST(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
|
||||
{
|
||||
if (::access("/etc/machine-id", F_OK) == -1 &&
|
||||
::access("/var/lib/dbus/machine-id", F_OK) == -1)
|
||||
GTEST_SKIP() << "/etc/machine-id and /var/lib/dbus/machine-id files do not exist, GetMachineId() will not work";
|
||||
|
||||
ASSERT_NO_THROW(m_proxy->GetMachineId());
|
||||
ASSERT_NO_THROW(this->m_proxy->GetMachineId());
|
||||
}
|
||||
|
||||
// TODO: Adjust expected xml and uncomment this test
|
||||
//TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
|
||||
//TYPED_TEST(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
|
||||
//{
|
||||
// ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
|
||||
// ASSERT_THAT(this->m_proxy->Introspect(), Eq(this->m_adaptor->getExpectedXmlApiDescription()));
|
||||
//}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
|
||||
TYPED_TEST(SdbusTestObject, GetsPropertyViaPropertiesInterface)
|
||||
{
|
||||
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
ASSERT_THAT(this->m_proxy->Get(INTERFACE_NAME, "state").template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
{
|
||||
std::promise<std::string> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
m_proxy->GetAsync(INTERFACE_NAME, "state", [&](const sdbus::Error* err, sdbus::Variant value)
|
||||
this->m_proxy->GetAsync(INTERFACE_NAME, "state", [&](std::optional<sdbus::Error> err, const sdbus::Variant& value)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value(value.get<std::string>());
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
{
|
||||
auto future = m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
|
||||
auto future = this->m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get().get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
ASSERT_THAT(future.get().template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
|
||||
TYPED_TEST(SdbusTestObject, SetsPropertyViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2345;
|
||||
uint32_t const newActionValue = 2345;
|
||||
|
||||
m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
|
||||
this->m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
|
||||
|
||||
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2346;
|
||||
uint32_t const newActionValue = 2346;
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](const sdbus::Error* err)
|
||||
this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
if (!err)
|
||||
promise.set_value();
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW(future.get());
|
||||
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
TYPED_TEST(SdbusTestObject, CancelsAsynchronousPropertySettingViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2347;
|
||||
|
||||
auto future = m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
|
||||
|
||||
ASSERT_NO_THROW(future.get());
|
||||
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
||||
{
|
||||
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(3));
|
||||
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
|
||||
{
|
||||
std::promise<std::map<std::string, sdbus::Variant>> promise;
|
||||
uint32_t const newActionValue = 2346;
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map<std::string, sdbus::Variant> value)
|
||||
{
|
||||
if (err == nullptr)
|
||||
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 const newActionValue = 2347;
|
||||
|
||||
auto future = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
|
||||
|
||||
ASSERT_NO_THROW(future.get());
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
||||
{
|
||||
const auto properties = this->m_proxy->GetAll(INTERFACE_NAME);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
|
||||
{
|
||||
std::promise<std::map<sdbus::PropertyName, sdbus::Variant>> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](std::optional<sdbus::Error> err, std::map<sdbus::PropertyName, sdbus::Variant> value)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value(std::move(value));
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
const auto properties = future.get();
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(3));
|
||||
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
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));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
{
|
||||
auto future = m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
|
||||
auto future = this->m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
|
||||
|
||||
auto properties = future.get();
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(3));
|
||||
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
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));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
|
||||
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const std::string& interfaceName
|
||||
, const std::map<std::string, sdbus::Variant>& changedProperties
|
||||
, const std::vector<std::string>& /*invalidatedProperties*/ )
|
||||
std::atomic signalReceived{false};
|
||||
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
|
||||
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<sdbus::PropertyName>& /*invalidatedProperties*/ )
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
|
||||
EXPECT_THAT(changedProperties.at(BLOCKING_PROPERTY).get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
|
||||
m_proxy->action(DEFAULT_ACTION_VALUE*2);
|
||||
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
|
||||
this->m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
|
||||
this->m_proxy->action(DEFAULT_ACTION_VALUE*2);
|
||||
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {BLOCKING_PROPERTY});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
|
||||
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const std::string& interfaceName
|
||||
, const std::map<std::string, sdbus::Variant>& changedProperties
|
||||
, const std::vector<std::string>& invalidatedProperties )
|
||||
std::atomic signalReceived{false};
|
||||
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
|
||||
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<sdbus::PropertyName>& invalidatedProperties )
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
EXPECT_THAT(changedProperties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
ASSERT_THAT(invalidatedProperties, SizeIs(1));
|
||||
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
|
||||
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
|
||||
TYPED_TEST(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
|
||||
{
|
||||
m_adaptor.reset();
|
||||
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
|
||||
this->m_adaptor.reset();
|
||||
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
|
||||
|
||||
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsSuccessfully)
|
||||
{
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
|
||||
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
|
||||
|
||||
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
|
||||
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH)
|
||||
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
|
||||
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
|
||||
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2)
|
||||
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
|
||||
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
|
||||
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronously)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
|
||||
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([&](const 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([&](const 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) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
std::atomic signalReceived{false};
|
||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
|
||||
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_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
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_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
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;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
|
||||
this->m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces) // NOLINT(readability-function-cognitive-complexity)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
|
||||
std::atomic signalReceived{false};
|
||||
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
#if LIBSYSTEMD_VERSION<=250
|
||||
@@ -290,31 +352,33 @@ TEST_F(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_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
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_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
|
||||
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;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesAddedSignal();
|
||||
this->m_adaptor->emitInterfacesAddedSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
std::atomic signalReceived{false};
|
||||
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
ASSERT_THAT(interfaces, SizeIs(1));
|
||||
@@ -322,16 +386,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
|
||||
this->m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
std::atomic signalReceived{false};
|
||||
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
#if LIBSYSTEMD_VERSION<=250
|
||||
@@ -344,7 +408,7 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesRemovedSignal();
|
||||
this->m_adaptor->emitInterfacesRemovedSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Defs.h
|
||||
*
|
||||
@@ -32,14 +32,18 @@
|
||||
#include <ostream>
|
||||
#include <filesystem>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
namespace sdbus::test {
|
||||
|
||||
const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
|
||||
const std::string BUS_NAME = INTERFACE_NAME;
|
||||
const std::string EMPTY_DESTINATION;
|
||||
const std::string MANAGER_PATH {"/org/sdbuscpp/integrationtests"};
|
||||
const std::string OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
|
||||
const std::string OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
|
||||
const InterfaceName INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
|
||||
const ServiceName SERVICE_NAME{"org.sdbuscpp.integrationtests"};
|
||||
const ServiceName EMPTY_DESTINATION;
|
||||
const ObjectPath MANAGER_PATH {"/org/sdbuscpp/integrationtests"};
|
||||
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"};
|
||||
|
||||
constexpr const uint8_t UINT8_VALUE{1};
|
||||
@@ -49,28 +53,30 @@ constexpr const int32_t INT32_VALUE{-42};
|
||||
constexpr const int32_t INT64_VALUE{-1024};
|
||||
|
||||
const std::string STRING_VALUE{"sdbus-c++-testing"};
|
||||
const sdbus::Signature SIGNATURE_VALUE{"a{is}"};
|
||||
const sdbus::ObjectPath OBJECT_PATH_VALUE{"/"};
|
||||
const Signature SIGNATURE_VALUE{"a{is}"};
|
||||
const ObjectPath OBJECT_PATH_VALUE{"/"};
|
||||
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};
|
||||
|
||||
}}
|
||||
} // namespace sdbus::test
|
||||
|
||||
namespace testing::internal {
|
||||
|
||||
// Printer for std::chrono::duration types.
|
||||
// This is a workaround, since it's not a good thing to add this to std namespace.
|
||||
template< class Rep, class Period >
|
||||
void PrintTo(const ::std::chrono::duration<Rep, Period>& d, ::std::ostream* os) {
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(d);
|
||||
*os << seconds.count() << "s";
|
||||
template<class Rep, class Period>
|
||||
void PrintTo(const ::std::chrono::duration<Rep, Period>& duration, ::std::ostream* stream)
|
||||
{
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(duration);
|
||||
*stream << seconds.count() << "s";
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace testing::internal
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TestAdaptor.cpp
|
||||
*
|
||||
@@ -25,14 +25,31 @@
|
||||
*/
|
||||
|
||||
#include "TestAdaptor.h"
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
#include "sdbus-c++/Types.h"
|
||||
#include "sdbus-c++/AdaptorInterfaces.h"
|
||||
#include <cstdint>
|
||||
#include "Defs.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include "sdbus-c++/Message.h"
|
||||
#include "sdbus-c++/MethodResult.h"
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <variant>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
namespace sdbus::test {
|
||||
|
||||
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) :
|
||||
AdaptorInterfaces(connection, path)
|
||||
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path) :
|
||||
AdaptorInterfaces(connection, std::move(path))
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
@@ -56,37 +73,37 @@ std::tuple<uint32_t, std::string> TestAdaptor::getTuple()
|
||||
return std::make_tuple(UINT32_VALUE, STRING_VALUE);
|
||||
}
|
||||
|
||||
double TestAdaptor::multiply(const int64_t& a, const double& b)
|
||||
double TestAdaptor::multiply(const int64_t& lhs, const double& rhs)
|
||||
{
|
||||
return a * b;
|
||||
return lhs * rhs; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
|
||||
}
|
||||
|
||||
void TestAdaptor::multiplyWithNoReply(const int64_t& a, const double& b)
|
||||
void TestAdaptor::multiplyWithNoReply(const int64_t& lhs, const double& rhs)
|
||||
{
|
||||
m_multiplyResult = a * b;
|
||||
m_multiplyResult = lhs * rhs; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
|
||||
m_wasMultiplyCalled = true;
|
||||
}
|
||||
|
||||
std::vector<int16_t> TestAdaptor::getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x)
|
||||
std::vector<int16_t> TestAdaptor::getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& strct)
|
||||
{
|
||||
std::vector<int16_t> res{x.get<1>()};
|
||||
auto y = std::get<std::vector<int16_t>>(x);
|
||||
res.insert(res.end(), y.begin(), y.end());
|
||||
std::vector res{strct.get<1>()};
|
||||
auto vec = std::get<std::vector<int16_t>>(strct);
|
||||
res.insert(res.end(), vec.begin(), vec.end());
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Variant TestAdaptor::processVariant(const sdbus::Variant& v)
|
||||
sdbus::Variant TestAdaptor::processVariant(const std::variant<int32_t, double, std::string>& var)
|
||||
{
|
||||
sdbus::Variant res{static_cast<int32_t>(v.get<double>())};
|
||||
sdbus::Variant res{static_cast<int32_t>(std::get<double>(var))};
|
||||
return res;
|
||||
}
|
||||
|
||||
std::map<int32_t, sdbus::Variant> TestAdaptor::getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
|
||||
std::map<int32_t, sdbus::Variant> TestAdaptor::getMapOfVariants(const std::vector<int32_t>& vec, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& strct)
|
||||
{
|
||||
std::map<int32_t, sdbus::Variant> res;
|
||||
for (auto item : x)
|
||||
for (auto item : vec)
|
||||
{
|
||||
res[item] = (item <= 0) ? std::get<0>(y) : std::get<1>(y);
|
||||
res[item] = (item <= 0) ? std::get<0>(strct) : std::get<1>(strct);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -96,24 +113,24 @@ sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> TestAdapto
|
||||
return sdbus::Struct{STRING_VALUE, sdbus::Struct{std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}}};
|
||||
}
|
||||
|
||||
int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& strctA, const sdbus::Struct<int32_t, int64_t>& strctB)
|
||||
{
|
||||
int32_t res{0};
|
||||
res += std::get<0>(a) + std::get<1>(a);
|
||||
res += std::get<0>(b) + std::get<1>(b);
|
||||
res += std::get<0>(strctA) + std::get<1>(strctA);
|
||||
res += std::get<0>(strctB) + std::get<1>(strctB); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t TestAdaptor::sumArrayItems(const std::vector<uint16_t>& a, const std::array<uint64_t, 3>& b)
|
||||
uint32_t TestAdaptor::sumArrayItems(const std::vector<uint16_t>& vec, const std::array<uint64_t, 3>& arr)
|
||||
{
|
||||
uint32_t res{0};
|
||||
for (auto x : a)
|
||||
for (auto elem : vec)
|
||||
{
|
||||
res += x;
|
||||
res += elem;
|
||||
}
|
||||
for (auto x : b)
|
||||
for (auto elem : arr)
|
||||
{
|
||||
res += x;
|
||||
res += elem;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -122,16 +139,24 @@ uint32_t TestAdaptor::doOperation(const uint32_t& param)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||
|
||||
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
|
||||
m_methodCallMemberName = m_methodCallMsg->getMemberName();
|
||||
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
|
||||
m_methodName = m_methodCallMsg->getMemberName();
|
||||
|
||||
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 = getObject().getCurrentlyProcessedMessage();
|
||||
m_methodCallMemberName = m_methodCallMsg->getMemberName();
|
||||
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
|
||||
m_methodName = m_methodCallMsg->getMemberName();
|
||||
|
||||
if (param == 0)
|
||||
{
|
||||
@@ -149,6 +174,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;
|
||||
@@ -173,7 +219,7 @@ std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::
|
||||
23, // uint8_t
|
||||
{ // vector
|
||||
{ // struct
|
||||
"/object/path", // object path
|
||||
sdbus::ObjectPath{"/object/path"}, // object path
|
||||
false,
|
||||
Variant{3.14},
|
||||
{ // map
|
||||
@@ -183,7 +229,7 @@ std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::
|
||||
}
|
||||
}
|
||||
},
|
||||
"a{t(a{ya(obva{is})}gs)}", // signature
|
||||
sdbus::Signature{"a{t(a{ya(obva{is})}gs)}"}, // signature
|
||||
std::string{}
|
||||
}
|
||||
}
|
||||
@@ -212,6 +258,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 +283,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;
|
||||
@@ -234,18 +300,23 @@ bool TestAdaptor::blocking()
|
||||
|
||||
void TestAdaptor::blocking(const bool& value)
|
||||
{
|
||||
m_propertySetMsg = getObject().getCurrentlyProcessedMessage();
|
||||
m_propertySetMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
|
||||
m_propertySetSender = m_propertySetMsg->getSender();
|
||||
|
||||
m_blocking = value;
|
||||
}
|
||||
|
||||
void TestAdaptor::emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
void TestAdaptor::emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct)
|
||||
{
|
||||
getObject().emitSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).withArguments(s);
|
||||
getObject().emitSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).withArguments(strct);
|
||||
}
|
||||
|
||||
std::string TestAdaptor::getExpectedXmlApiDescription() const
|
||||
void TestAdaptor::emitSignalWithErrorAndTypeMismatch()
|
||||
{
|
||||
getObject().emitSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME);
|
||||
}
|
||||
|
||||
std::string TestAdaptor::getExpectedXmlApiDescription()
|
||||
{
|
||||
return
|
||||
R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
@@ -423,4 +494,4 @@ R"delimiter(
|
||||
)delimiter";
|
||||
}
|
||||
|
||||
}}
|
||||
} // namespace sdbus::test
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TestAdaptor.h
|
||||
*
|
||||
@@ -33,18 +33,24 @@
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
namespace sdbus::test {
|
||||
|
||||
class ObjectManagerTestAdaptor final : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
|
||||
{
|
||||
public:
|
||||
ObjectManagerTestAdaptor(sdbus::IConnection& connection, std::string path) :
|
||||
ObjectManagerTestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path) :
|
||||
AdaptorInterfaces(connection, std::move(path))
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
ObjectManagerTestAdaptor(const ObjectManagerTestAdaptor&) = delete;
|
||||
ObjectManagerTestAdaptor& operator=(const ObjectManagerTestAdaptor&) = delete;
|
||||
ObjectManagerTestAdaptor(ObjectManagerTestAdaptor&&) = delete;
|
||||
ObjectManagerTestAdaptor& operator=(ObjectManagerTestAdaptor&&) = delete;
|
||||
|
||||
~ObjectManagerTestAdaptor()
|
||||
{
|
||||
unregisterAdaptor();
|
||||
@@ -56,23 +62,29 @@ class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integr
|
||||
, sdbus::ManagedObject_adaptor >
|
||||
{
|
||||
public:
|
||||
TestAdaptor(sdbus::IConnection& connection, const std::string& path);
|
||||
TestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path);
|
||||
TestAdaptor(const TestAdaptor&) = delete;
|
||||
TestAdaptor& operator=(const TestAdaptor&) = delete;
|
||||
TestAdaptor(TestAdaptor&&) = delete;
|
||||
TestAdaptor& operator=(TestAdaptor&&) = delete;
|
||||
~TestAdaptor();
|
||||
|
||||
protected:
|
||||
void noArgNoReturn() override;
|
||||
int32_t getInt() override;
|
||||
std::tuple<uint32_t, std::string> getTuple() override;
|
||||
double multiply(const int64_t& a, const double& b) override;
|
||||
void multiplyWithNoReply(const int64_t& a, const double& b) override;
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) override;
|
||||
sdbus::Variant processVariant(const sdbus::Variant& variant) override;
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) override;
|
||||
double multiply(const int64_t& lhs, const double& rhs) override;
|
||||
void multiplyWithNoReply(const int64_t& lhs, const double& rhs) override;
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& strct) override;
|
||||
sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>& variant) override;
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& vec, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& strct) override;
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override;
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) override;
|
||||
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;
|
||||
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override;
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& strctA, const sdbus::Struct<int32_t, int64_t>& strctB) override;
|
||||
uint32_t sumArrayItems(const std::vector<uint16_t>& vec, const std::array<uint64_t, 3>& arr) override;
|
||||
uint32_t doOperation(const uint32_t& param) 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 param) override;
|
||||
void doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t param, const std::map<int32_t, std::string>& largeMap) override;
|
||||
sdbus::Signature getSignature() override;
|
||||
sdbus::ObjectPath getObjPath() override;
|
||||
sdbus::UnixFd getUnixFd() override;
|
||||
@@ -81,21 +93,27 @@ 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;
|
||||
|
||||
public:
|
||||
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
|
||||
std::string getExpectedXmlApiDescription() const;
|
||||
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct);
|
||||
void emitSignalWithErrorAndTypeMismatch();
|
||||
static std::string getExpectedXmlApiDescription() ;
|
||||
|
||||
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
|
||||
@@ -103,9 +121,9 @@ public: // for tests
|
||||
mutable double m_multiplyResult{};
|
||||
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
|
||||
|
||||
const Message* m_methodCallMsg{};
|
||||
std::string m_methodCallMemberName;
|
||||
const Message* m_propertySetMsg{};
|
||||
std::unique_ptr<const Message> m_methodCallMsg;
|
||||
MethodName m_methodName;
|
||||
std::unique_ptr<const Message> m_propertySetMsg;
|
||||
std::string m_propertySetSender;
|
||||
};
|
||||
|
||||
@@ -114,7 +132,9 @@ class DummyTestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::i
|
||||
, sdbus::ManagedObject_adaptor >
|
||||
{
|
||||
public:
|
||||
DummyTestAdaptor(sdbus::IConnection& connection, const std::string& path) : AdaptorInterfaces(connection, path) {}
|
||||
DummyTestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path)
|
||||
: AdaptorInterfaces(connection, std::move(path))
|
||||
{}
|
||||
|
||||
protected:
|
||||
void noArgNoReturn() override {}
|
||||
@@ -123,13 +143,15 @@ protected:
|
||||
double multiply(const int64_t&, const double&) override { return {}; }
|
||||
void multiplyWithNoReply(const int64_t&, const double&) override {}
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>&) override { return {}; }
|
||||
sdbus::Variant processVariant(const sdbus::Variant&) override { return {}; }
|
||||
sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>&) override { return {}; }
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>&, const sdbus::Struct<sdbus::Variant, sdbus::Variant>&) override { return {}; }
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override { return {}; }
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>&, const sdbus::Struct<int32_t, int64_t>&) override { return {}; }
|
||||
uint32_t sumArrayItems(const std::vector<uint16_t>&, const std::array<uint64_t, 3>&) override { return {}; }
|
||||
uint32_t doOperation(const uint32_t&) override { return {}; }
|
||||
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 {}; }
|
||||
@@ -138,14 +160,18 @@ 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 {}; }
|
||||
};
|
||||
|
||||
}}
|
||||
} // namespace sdbus::test
|
||||
|
||||
#endif /* INTEGRATIONTESTS_TESTADAPTOR_H_ */
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TestAdaptor.cpp
|
||||
* @file TestFixture.cpp
|
||||
*
|
||||
* Created on: May 23, 2020
|
||||
* Project: sdbus-c++
|
||||
@@ -25,10 +25,23 @@
|
||||
*/
|
||||
|
||||
#include "TestFixture.h"
|
||||
#include <memory>
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
#include <thread>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
namespace sdbus::test {
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
|
||||
std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection();
|
||||
std::unique_ptr<sdbus::IConnection> BaseTestFixture::s_adaptorConnection = sdbus::createBusConnection();
|
||||
std::unique_ptr<sdbus::IConnection> BaseTestFixture::s_proxyConnection = sdbus::createBusConnection();
|
||||
|
||||
}}
|
||||
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
|
||||
|
||||
std::thread TestFixture<SdEventLoop>::s_adaptorEventLoopThread{};
|
||||
std::thread TestFixture<SdEventLoop>::s_proxyEventLoopThread{};
|
||||
sd_event *TestFixture<SdEventLoop>::s_adaptorSdEvent{};
|
||||
sd_event *TestFixture<SdEventLoop>::s_proxySdEvent{};
|
||||
int TestFixture<SdEventLoop>::s_eventExitFd{-1};
|
||||
|
||||
#endif // SDBUS_basu
|
||||
|
||||
} // namespace sdbus::test
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TestAdaptor.h
|
||||
* @file TestFixture.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
@@ -33,42 +33,42 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
|
||||
#include <systemd/sd-event.h>
|
||||
#endif // SDBUS_basu
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
namespace sdbus::test {
|
||||
|
||||
class TestFixture : public ::testing::Test
|
||||
inline const uint32_t ANY_UNSIGNED_NUMBER{123};
|
||||
|
||||
class BaseTestFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
s_proxyConnection->enterEventLoopAsync();
|
||||
s_adaptorConnection->requestName(BUS_NAME);
|
||||
s_adaptorConnection->enterEventLoopAsync();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
|
||||
s_adaptorConnection->requestName(SERVICE_NAME);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
static void TearDownTestSuite()
|
||||
{
|
||||
s_adaptorConnection->releaseName(BUS_NAME);
|
||||
s_adaptorConnection->leaveEventLoop();
|
||||
s_proxyConnection->leaveEventLoop();
|
||||
s_adaptorConnection->releaseName(SERVICE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetUp() override
|
||||
{
|
||||
m_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, BUS_NAME, MANAGER_PATH);
|
||||
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, BUS_NAME, OBJECT_PATH);
|
||||
m_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, SERVICE_NAME, MANAGER_PATH);
|
||||
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, SERVICE_NAME, OBJECT_PATH);
|
||||
|
||||
m_objectManagerAdaptor = std::make_unique<ObjectManagerTestAdaptor>(*s_adaptorConnection, MANAGER_PATH);
|
||||
m_adaptor = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH);
|
||||
@@ -89,12 +89,114 @@ public:
|
||||
std::unique_ptr<TestProxy> m_proxy;
|
||||
};
|
||||
|
||||
struct SdBusCppLoop{};
|
||||
struct SdEventLoop{};
|
||||
|
||||
template <typename EventLoop>
|
||||
class TestFixture : public BaseTestFixture{};
|
||||
|
||||
// Fixture working upon internal sdbus-c++ event loop
|
||||
template <>
|
||||
class TestFixture<SdBusCppLoop> : public BaseTestFixture
|
||||
{
|
||||
public:
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
BaseTestFixture::SetUpTestSuite();
|
||||
s_proxyConnection->enterEventLoopAsync();
|
||||
s_adaptorConnection->enterEventLoopAsync();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
|
||||
}
|
||||
|
||||
static void TearDownTestSuite()
|
||||
{
|
||||
BaseTestFixture::TearDownTestSuite();
|
||||
s_adaptorConnection->leaveEventLoop();
|
||||
s_proxyConnection->leaveEventLoop();
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
|
||||
|
||||
// Fixture working upon attached external sd-event loop
|
||||
template <>
|
||||
class TestFixture<SdEventLoop> : public BaseTestFixture
|
||||
{
|
||||
public:
|
||||
static void SetUpTestSuite()
|
||||
{
|
||||
sd_event_new(&s_adaptorSdEvent);
|
||||
sd_event_new(&s_proxySdEvent);
|
||||
|
||||
s_adaptorConnection->attachSdEventLoop(s_adaptorSdEvent);
|
||||
s_proxyConnection->attachSdEventLoop(s_proxySdEvent);
|
||||
|
||||
s_eventExitFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
auto exitHandler = [](sd_event_source *src, auto...){ return sd_event_exit(sd_event_source_get_event(src), 0); };
|
||||
sd_event_add_io(s_adaptorSdEvent, nullptr, s_eventExitFd, EPOLLIN, exitHandler, nullptr);
|
||||
sd_event_add_io(s_proxySdEvent, nullptr, s_eventExitFd, EPOLLIN, exitHandler, nullptr);
|
||||
|
||||
s_adaptorEventLoopThread = std::thread([]()
|
||||
{
|
||||
sd_event_loop(s_adaptorSdEvent);
|
||||
});
|
||||
s_proxyEventLoopThread = std::thread([]()
|
||||
{
|
||||
sd_event_loop(s_proxySdEvent);
|
||||
});
|
||||
|
||||
BaseTestFixture::SetUpTestSuite();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
|
||||
}
|
||||
|
||||
static void TearDownTestSuite()
|
||||
{
|
||||
(void)eventfd_write(s_eventExitFd, 1);
|
||||
|
||||
s_adaptorEventLoopThread.join();
|
||||
s_proxyEventLoopThread.join();
|
||||
|
||||
sd_event_unref(s_adaptorSdEvent);
|
||||
sd_event_unref(s_proxySdEvent);
|
||||
close(s_eventExitFd);
|
||||
|
||||
BaseTestFixture::TearDownTestSuite();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::thread s_adaptorEventLoopThread;
|
||||
static std::thread s_proxyEventLoopThread;
|
||||
static sd_event *s_adaptorSdEvent;
|
||||
static sd_event *s_proxySdEvent;
|
||||
static int s_eventExitFd;
|
||||
};
|
||||
|
||||
using EventLoopTags = ::testing::Types<SdBusCppLoop, SdEventLoop>;
|
||||
|
||||
#else // SDBUS_basu
|
||||
using EventLoopTags = ::testing::Types<SdBusCppLoop>;
|
||||
#endif // SDBUS_basu
|
||||
|
||||
TYPED_TEST_SUITE(TestFixture, EventLoopTags);
|
||||
|
||||
template <typename EventLoop>
|
||||
using SdbusTestObject = TestFixture<EventLoop>;
|
||||
TYPED_TEST_SUITE(SdbusTestObject, EventLoopTags);
|
||||
|
||||
template <typename EventLoop>
|
||||
using AsyncSdbusTestObject = TestFixture<EventLoop>;
|
||||
TYPED_TEST_SUITE(AsyncSdbusTestObject, EventLoopTags);
|
||||
|
||||
template <typename EventLoop>
|
||||
using AConnection = TestFixture<EventLoop>;
|
||||
TYPED_TEST_SUITE(AConnection, EventLoopTags);
|
||||
|
||||
class TestFixtureWithDirectConnection : public ::testing::Test
|
||||
{
|
||||
private:
|
||||
void SetUp() override
|
||||
{
|
||||
int sock = openUnixSocket();
|
||||
const int sock = openUnixSocket();
|
||||
createClientAndServerConnections(sock);
|
||||
createAdaptorAndProxyObjects();
|
||||
}
|
||||
@@ -109,18 +211,19 @@ private:
|
||||
|
||||
static int openUnixSocket()
|
||||
{
|
||||
int sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||
const int sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
|
||||
assert(sock >= 0);
|
||||
|
||||
sockaddr_un sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", DIRECT_CONNECTION_SOCKET_PATH.c_str());
|
||||
sockaddr_un saddr{};
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sun_family = AF_UNIX;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
(void)snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s", DIRECT_CONNECTION_SOCKET_PATH.c_str());
|
||||
|
||||
unlink(DIRECT_CONNECTION_SOCKET_PATH.c_str());
|
||||
|
||||
umask(0000);
|
||||
[[maybe_unused]] int r = bind(sock, (const sockaddr*) &sa, sizeof(sa.sun_path));
|
||||
[[maybe_unused]] int r = bind(sock, reinterpret_cast<const sockaddr*>(&saddr), sizeof(saddr.sun_path));
|
||||
assert(r >= 0);
|
||||
|
||||
r = listen(sock, 5);
|
||||
@@ -133,7 +236,7 @@ private:
|
||||
{
|
||||
std::thread t([&]()
|
||||
{
|
||||
auto fd = accept4(sock, NULL, NULL, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC);
|
||||
auto fd = accept4(sock, nullptr, nullptr, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC);
|
||||
m_adaptorConnection = sdbus::createServerBus(fd);
|
||||
// This is necessary so that createDirectBusConnection() below does not block
|
||||
m_adaptorConnection->enterEventLoopAsync();
|
||||
@@ -162,14 +265,14 @@ public:
|
||||
std::unique_ptr<TestProxy> m_proxy;
|
||||
};
|
||||
|
||||
template <typename _Fnc>
|
||||
inline bool waitUntil(_Fnc&& fnc, std::chrono::milliseconds timeout = std::chrono::seconds(5))
|
||||
template <typename Fnc>
|
||||
inline bool waitUntil(const Fnc& fnc, std::chrono::milliseconds timeout = std::chrono::seconds(5))
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
std::chrono::milliseconds elapsed{};
|
||||
std::chrono::milliseconds step{5ms};
|
||||
do {
|
||||
const std::chrono::milliseconds step{5ms};
|
||||
do { // NOLINT(cppcoreguidelines-avoid-do-while)
|
||||
std::this_thread::sleep_for(step);
|
||||
elapsed += step;
|
||||
if (elapsed > timeout)
|
||||
@@ -184,6 +287,6 @@ inline bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout
|
||||
return waitUntil([&flag]() -> bool { return flag; }, timeout);
|
||||
}
|
||||
|
||||
}}
|
||||
} // namespace sdbus::test
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_ */
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TestAdaptor.cpp
|
||||
* @file TestProxy.cpp
|
||||
*
|
||||
* Created on: May 23, 2020
|
||||
* Project: sdbus-c++
|
||||
@@ -25,31 +25,43 @@
|
||||
*/
|
||||
|
||||
#include "TestProxy.h"
|
||||
#include <thread>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include "Defs.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
namespace sdbus::test {
|
||||
|
||||
TestProxy::TestProxy(std::string destination, std::string objectPath)
|
||||
TestProxy::TestProxy(ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyInterfaces(std::move(destination), std::move(objectPath))
|
||||
{
|
||||
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s){ this->onSignalWithoutRegistration(s); });
|
||||
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct){ this->onSignalWithoutRegistration(strct); });
|
||||
getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional<sdbus::Error> err, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(std::move(err), wrongParameter); });
|
||||
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
TestProxy::TestProxy(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
|
||||
TestProxy::TestProxy(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
|
||||
: ProxyInterfaces(std::move(destination), std::move(objectPath), dont_run_event_loop_thread)
|
||||
{
|
||||
// It doesn't make sense to register any signals here since proxy upon a D-Bus connection with no event loop thread
|
||||
// will not receive any incoming messages except replies to synchronous D-Bus calls.
|
||||
}
|
||||
|
||||
TestProxy::TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
|
||||
TestProxy::TestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
|
||||
{
|
||||
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s){ this->onSignalWithoutRegistration(s); });
|
||||
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct){ this->onSignalWithoutRegistration(strct); });
|
||||
getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional<sdbus::Error> err, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(std::move(err), wrongParameter); });
|
||||
|
||||
registerProxy();
|
||||
}
|
||||
@@ -61,8 +73,8 @@ TestProxy::~TestProxy()
|
||||
|
||||
void TestProxy::onSimpleSignal()
|
||||
{
|
||||
m_signalMsg = getProxy().getCurrentlyProcessedMessage();
|
||||
m_signalMemberName = m_signalMsg->getMemberName();
|
||||
m_signalMsg = std::make_unique<sdbus::Message>(getProxy().getCurrentlyProcessedMessage());
|
||||
m_signalName = m_signalMsg->getMemberName();
|
||||
|
||||
m_gotSimpleSignal = true;
|
||||
}
|
||||
@@ -79,27 +91,34 @@ void TestProxy::onSignalWithVariant(const sdbus::Variant& aVariant)
|
||||
m_gotSignalWithVariant = true;
|
||||
}
|
||||
|
||||
void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct)
|
||||
{
|
||||
m_signatureFromSignal[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
|
||||
// Static cast to std::string is a workaround for gcc 11.4 false positive warning (which later gcc versions nor Clang emit)
|
||||
m_signatureFromSignal[std::get<0>(strct)] = static_cast<std::string>(std::get<0>(std::get<1>(strct)));
|
||||
m_gotSignalWithSignature = true;
|
||||
}
|
||||
|
||||
void TestProxy::onDoOperationReply(uint32_t returnValue, const sdbus::Error* error)
|
||||
void TestProxy::onSignalWithErrorAndTypeMismatch(std::optional<sdbus::Error> err, [[maybe_unused]] int wrongParameter)
|
||||
{
|
||||
if (m_DoOperationClientSideAsyncReplyHandler)
|
||||
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
|
||||
m_errorFromSignal = std::move(err);
|
||||
m_gotSignalWithTypeMismatch = true;
|
||||
}
|
||||
|
||||
void TestProxy::onPropertiesChanged( const std::string& interfaceName
|
||||
, const std::map<std::string, sdbus::Variant>& changedProperties
|
||||
, const std::vector<std::string>& invalidatedProperties )
|
||||
void TestProxy::onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error) const
|
||||
{
|
||||
if (m_DoOperationClientSideAsyncReplyHandler)
|
||||
m_DoOperationClientSideAsyncReplyHandler(returnValue, std::move(error));
|
||||
}
|
||||
|
||||
void TestProxy::onPropertiesChanged( const sdbus::InterfaceName& interfaceName
|
||||
, const std::map<PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<PropertyName>& invalidatedProperties )
|
||||
{
|
||||
if (m_onPropertiesChangedHandler)
|
||||
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
|
||||
}
|
||||
|
||||
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
|
||||
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler)
|
||||
{
|
||||
m_DoOperationClientSideAsyncReplyHandler = std::move(handler);
|
||||
}
|
||||
@@ -107,22 +126,43 @@ void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void
|
||||
uint32_t TestProxy::doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
uint32_t result;
|
||||
uint32_t result = 0;
|
||||
getProxy().callMethod("doOperation").onInterface(sdbus::test::INTERFACE_NAME).withTimeout(timeout).withArguments(param).storeResultsTo(result);
|
||||
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")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
|
||||
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
this->onDoOperationReply(returnValue, std::move(error));
|
||||
});
|
||||
}
|
||||
|
||||
Slot TestProxy::doOperationClientSideAsync(uint32_t param, sdbus::return_slot_t)
|
||||
{
|
||||
return getProxy().callMethodAsync("doOperation")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, std::move(error));
|
||||
}, sdbus::return_slot);
|
||||
}
|
||||
|
||||
std::future<uint32_t> TestProxy::doOperationClientSideAsync(uint32_t param, with_future_t)
|
||||
{
|
||||
return getProxy().callMethodAsync("doOperation")
|
||||
@@ -133,19 +173,27 @@ std::future<uint32_t> TestProxy::doOperationClientSideAsync(uint32_t param, with
|
||||
|
||||
std::future<MethodReply> TestProxy::doOperationClientSideAsyncOnBasicAPILevel(uint32_t param)
|
||||
{
|
||||
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, "doOperation");
|
||||
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, sdbus::MethodName{"doOperation"});
|
||||
methodCall << param;
|
||||
|
||||
return getProxy().callMethod(methodCall, sdbus::with_future);
|
||||
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")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error)
|
||||
.uponReplyInvoke([this](std::optional<sdbus::Error> error)
|
||||
{
|
||||
this->onDoOperationReply(0, error);
|
||||
this->onDoOperationReply(0, std::move(error));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -156,6 +204,38 @@ std::future<void> TestProxy::doErroneousOperationClientSideAsync(with_future_t)
|
||||
.getResultAsFuture<>();
|
||||
}
|
||||
|
||||
sdbus::Awaitable<uint32_t> TestProxy::doOperationClientSideAsync(uint32_t param, sdbus::with_awaitable_t)
|
||||
{
|
||||
return getProxy().callMethodAsync("doOperation")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withArguments(param)
|
||||
.getResultAsAwaitable<uint32_t>();
|
||||
}
|
||||
|
||||
sdbus::Awaitable<std::map<int32_t, std::string>> TestProxy::doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, sdbus::with_awaitable_t)
|
||||
{
|
||||
return getProxy().callMethodAsync("doOperationWithLargeData")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withArguments(largeParam)
|
||||
.getResultAsAwaitable<std::map<int32_t, std::string>>();
|
||||
}
|
||||
|
||||
sdbus::Awaitable<MethodReply> TestProxy::doOperationClientSideAsyncOnBasicAPILevel(uint32_t param, sdbus::with_awaitable_t)
|
||||
{
|
||||
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, sdbus::MethodName{"doOperation"});
|
||||
methodCall << param;
|
||||
|
||||
return getProxy().callMethodAsync(methodCall, sdbus::with_awaitable);
|
||||
}
|
||||
|
||||
|
||||
sdbus::Awaitable<void> TestProxy::doErroneousOperationClientSideAsync(sdbus::with_awaitable_t)
|
||||
{
|
||||
return getProxy().callMethodAsync("throwError")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.getResultAsAwaitable<>();
|
||||
}
|
||||
|
||||
void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
@@ -163,23 +243,24 @@ void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microse
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withTimeout(timeout)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
|
||||
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
this->onDoOperationReply(returnValue, std::move(error));
|
||||
});
|
||||
}
|
||||
|
||||
int32_t TestProxy::callNonexistentMethod()
|
||||
{
|
||||
int32_t result;
|
||||
int32_t result = 0;
|
||||
getProxy().callMethod("callNonexistentMethod").onInterface(sdbus::test::INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t TestProxy::callMethodOnNonexistentInterface()
|
||||
{
|
||||
int32_t result;
|
||||
getProxy().callMethod("someMethod").onInterface("sdbuscpp.interface.that.does.not.exist").storeResultsTo(result);
|
||||
sdbus::InterfaceName const nonexistentInterfaceName{"sdbuscpp.interface.that.does.not.exist"};
|
||||
int32_t result = 0;
|
||||
getProxy().callMethod("someMethod").onInterface(nonexistentInterfaceName).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -188,4 +269,4 @@ void TestProxy::setStateProperty(const std::string& value)
|
||||
getProxy().setProperty("state").onInterface(sdbus::test::INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
}}
|
||||
} // namespace sdbus::test
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TestAdaptor.h
|
||||
* @file TestProxy.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
@@ -33,38 +33,44 @@
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
namespace sdbus::test {
|
||||
|
||||
class ObjectManagerTestProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
|
||||
{
|
||||
public:
|
||||
ObjectManagerTestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
|
||||
ObjectManagerTestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
ObjectManagerTestProxy(const ObjectManagerTestProxy&) = delete;
|
||||
ObjectManagerTestProxy& operator=(const ObjectManagerTestProxy&) = delete;
|
||||
ObjectManagerTestProxy(ObjectManagerTestProxy&&) = delete;
|
||||
ObjectManagerTestProxy& operator=(ObjectManagerTestProxy&&) = delete;
|
||||
|
||||
~ObjectManagerTestProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
protected:
|
||||
void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
|
||||
void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>& interfacesAndProperties) override
|
||||
{
|
||||
if (m_onInterfacesAddedHandler)
|
||||
m_onInterfacesAddedHandler(objectPath, interfacesAndProperties);
|
||||
}
|
||||
|
||||
void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector<std::string>& interfaces) override
|
||||
void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector<sdbus::InterfaceName>& interfaces) override
|
||||
{
|
||||
if (m_onInterfacesRemovedHandler)
|
||||
m_onInterfacesRemovedHandler(objectPath, interfaces);
|
||||
}
|
||||
|
||||
public: // for tests
|
||||
std::function<void(const sdbus::ObjectPath&, const std::map<std::string, std::map<std::string, sdbus::Variant>>&)> m_onInterfacesAddedHandler;
|
||||
std::function<void(const sdbus::ObjectPath&, const std::vector<std::string>&)> m_onInterfacesRemovedHandler;
|
||||
std::function<void(const sdbus::ObjectPath&, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>&)> m_onInterfacesAddedHandler;
|
||||
std::function<void(const sdbus::ObjectPath&, const std::vector<sdbus::InterfaceName>&)> m_onInterfacesRemovedHandler;
|
||||
};
|
||||
|
||||
class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
|
||||
@@ -73,9 +79,13 @@ class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integratio
|
||||
, sdbus::Properties_proxy >
|
||||
{
|
||||
public:
|
||||
TestProxy(std::string destination, std::string objectPath);
|
||||
TestProxy(std::string destination, std::string objectPath, dont_run_event_loop_thread_t);
|
||||
TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath);
|
||||
TestProxy(ServiceName destination, ObjectPath objectPath);
|
||||
TestProxy(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t);
|
||||
TestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath);
|
||||
TestProxy(const TestProxy&) = delete;
|
||||
TestProxy& operator=(const TestProxy&) = delete;
|
||||
TestProxy(TestProxy&&) = delete;
|
||||
TestProxy& operator=(TestProxy&&) = delete;
|
||||
~TestProxy();
|
||||
|
||||
protected:
|
||||
@@ -83,43 +93,53 @@ protected:
|
||||
void onSignalWithMap(const std::map<int32_t, std::string>& aMap) override;
|
||||
void onSignalWithVariant(const sdbus::Variant& aVariant) override;
|
||||
|
||||
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
|
||||
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error);
|
||||
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct);
|
||||
void onSignalWithErrorAndTypeMismatch(std::optional<sdbus::Error> err, int wrongParameter);
|
||||
void onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error) const;
|
||||
|
||||
// Signals of standard D-Bus interfaces
|
||||
void onPropertiesChanged( const std::string& interfaceName
|
||||
, const std::map<std::string, sdbus::Variant>& changedProperties
|
||||
, const std::vector<std::string>& invalidatedProperties ) override;
|
||||
void onPropertiesChanged( const sdbus::InterfaceName& interfaceName
|
||||
, const std::map<PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<PropertyName>& invalidatedProperties ) override;
|
||||
|
||||
public:
|
||||
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler);
|
||||
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);
|
||||
sdbus::Awaitable<uint32_t> doOperationClientSideAsync(uint32_t param, sdbus::with_awaitable_t);
|
||||
sdbus::Awaitable<std::map<int32_t, std::string>> doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, sdbus::with_awaitable_t);
|
||||
sdbus::Awaitable<MethodReply> doOperationClientSideAsyncOnBasicAPILevel(uint32_t param, sdbus::with_awaitable_t);
|
||||
sdbus::Awaitable<void> doErroneousOperationClientSideAsync(sdbus::with_awaitable_t);
|
||||
void doErroneousOperationClientSideAsync();
|
||||
void doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
|
||||
int32_t callNonexistentMethod();
|
||||
int32_t callMethodOnNonexistentInterface();
|
||||
void setStateProperty(const std::string& value);
|
||||
|
||||
//private:
|
||||
public: // for tests
|
||||
//private: (Kept public for tests)
|
||||
int m_SimpleSignals = 0;
|
||||
std::atomic<bool> m_gotSimpleSignal{false};
|
||||
std::atomic<bool> m_gotSignalWithMap{false};
|
||||
std::map<int32_t, std::string> m_mapFromSignal;
|
||||
std::atomic<bool> m_gotSignalWithVariant{false};
|
||||
double m_variantFromSignal;
|
||||
double m_variantFromSignal{};
|
||||
std::atomic<bool> m_gotSignalWithSignature{false};
|
||||
std::map<std::string, std::string> m_signatureFromSignal;
|
||||
std::map<std::string, Signature> m_signatureFromSignal;
|
||||
std::atomic<bool> m_gotSignalWithTypeMismatch{false};
|
||||
std::optional<sdbus::Error> m_errorFromSignal;
|
||||
|
||||
std::function<void(uint32_t res, const sdbus::Error* err)> m_DoOperationClientSideAsyncReplyHandler;
|
||||
std::function<void(const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>&)> m_onPropertiesChangedHandler;
|
||||
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;
|
||||
|
||||
const Message* m_signalMsg{};
|
||||
std::string m_signalMemberName;
|
||||
std::unique_ptr<const MethodCall> m_methodCallMsg;
|
||||
std::unique_ptr<const Message> m_signalMsg;
|
||||
SignalName m_signalName;
|
||||
};
|
||||
|
||||
class DummyTestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
|
||||
@@ -128,8 +148,8 @@ class DummyTestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integ
|
||||
, sdbus::Properties_proxy >
|
||||
{
|
||||
public:
|
||||
DummyTestProxy(std::string destination, std::string objectPath)
|
||||
: ProxyInterfaces(destination, objectPath)
|
||||
DummyTestProxy(ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyInterfaces(std::move(destination), std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -139,12 +159,12 @@ protected:
|
||||
void onSignalWithVariant(const sdbus::Variant&) override {}
|
||||
|
||||
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>&) {}
|
||||
void onDoOperationReply(uint32_t, const sdbus::Error*) {}
|
||||
void onDoOperationReply(uint32_t, const std::optional<sdbus::Error>&) {}
|
||||
|
||||
// Signals of standard D-Bus interfaces
|
||||
void onPropertiesChanged( const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>& ) override {}
|
||||
void onPropertiesChanged(const InterfaceName&, const std::map<PropertyName, sdbus::Variant>&, const std::vector<PropertyName>&) override {}
|
||||
};
|
||||
|
||||
}}
|
||||
} // namespace sdbus::test
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_ */
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
|
||||
#define __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
|
||||
class integrationtests_adaptor
|
||||
{
|
||||
public:
|
||||
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.integrationtests";
|
||||
|
||||
protected:
|
||||
integrationtests_adaptor(sdbus::IObject& object)
|
||||
: m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
integrationtests_adaptor(const integrationtests_adaptor&) = delete;
|
||||
integrationtests_adaptor& operator=(const integrationtests_adaptor&) = delete;
|
||||
integrationtests_adaptor(integrationtests_adaptor&&) = delete;
|
||||
integrationtests_adaptor& operator=(integrationtests_adaptor&&) = delete;
|
||||
|
||||
~integrationtests_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable( sdbus::setInterfaceFlags().markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL)
|
||||
, sdbus::registerMethod("noArgNoReturn").implementedAs([this](){ return this->noArgNoReturn(); })
|
||||
, sdbus::registerMethod("getInt").withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); })
|
||||
, sdbus::registerMethod("getTuple").withOutputParamNames("arg0", "arg1").implementedAs([this](){ return this->getTuple(); })
|
||||
, sdbus::registerMethod("multiply").withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); })
|
||||
, sdbus::registerMethod("multiplyWithNoReply").withInputParamNames("a", "b").implementedAs([this](const int64_t& a, const double& b){ return this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply()
|
||||
, sdbus::registerMethod("getInts16FromStruct").withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0){ return this->getInts16FromStruct(arg0); })
|
||||
, sdbus::registerMethod("processVariant").withInputParamNames("variant").withOutputParamNames("result").implementedAs([this](const std::variant<int32_t, double, std::string>& variant){ return this->processVariant(variant); })
|
||||
, sdbus::registerMethod("getMapOfVariants").withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x, y); })
|
||||
, sdbus::registerMethod("getStructInStruct").withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); })
|
||||
, 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(); })
|
||||
, sdbus::registerMethod("getComplex").withOutputParamNames("arg0").implementedAs([this](){ return this->getComplex(); }).markAsDeprecated()
|
||||
, sdbus::registerMethod("throwError").implementedAs([this](){ return this->throwError(); })
|
||||
, 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);
|
||||
}
|
||||
|
||||
public:
|
||||
void emitSimpleSignal()
|
||||
{
|
||||
m_object.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void emitSignalWithMap(const std::map<int32_t, std::string>& aMap)
|
||||
{
|
||||
m_object.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
|
||||
}
|
||||
|
||||
void emitSignalWithVariant(const sdbus::Variant& aVariant)
|
||||
{
|
||||
m_object.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void noArgNoReturn() = 0;
|
||||
virtual int32_t getInt() = 0;
|
||||
virtual std::tuple<uint32_t, std::string> getTuple() = 0;
|
||||
virtual double multiply(const int64_t& a, const double& b) = 0;
|
||||
virtual void multiplyWithNoReply(const int64_t& a, const double& b) = 0;
|
||||
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) = 0;
|
||||
virtual sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>& variant) = 0;
|
||||
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) = 0;
|
||||
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() = 0;
|
||||
virtual 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;
|
||||
virtual std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
|
||||
virtual void throwError() = 0;
|
||||
virtual void 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;
|
||||
|
||||
private:
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
|
||||
#endif
|
||||
+76
-36
@@ -20,20 +20,24 @@ public:
|
||||
|
||||
protected:
|
||||
integrationtests_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
proxy_->uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
|
||||
proxy_->uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& aMap){ this->onSignalWithMap(aMap); });
|
||||
proxy_->uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& aVariant){ this->onSignalWithVariant(aVariant); });
|
||||
}
|
||||
|
||||
integrationtests_proxy(const integrationtests_proxy&) = delete;
|
||||
integrationtests_proxy& operator=(const integrationtests_proxy&) = delete;
|
||||
integrationtests_proxy(integrationtests_proxy&&) = default;
|
||||
integrationtests_proxy& operator=(integrationtests_proxy&&) = default;
|
||||
integrationtests_proxy(integrationtests_proxy&&) = delete;
|
||||
integrationtests_proxy& operator=(integrationtests_proxy&&) = delete;
|
||||
|
||||
~integrationtests_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
simpleSignalSlot_ = m_proxy.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }, sdbus::return_slot);
|
||||
m_proxy.uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& aMap){ this->onSignalWithMap(aMap); });
|
||||
m_proxy.uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& aVariant){ this->onSignalWithVariant(aVariant); });
|
||||
}
|
||||
|
||||
virtual void onSimpleSignal() = 0;
|
||||
virtual void onSignalWithMap(const std::map<int32_t, std::string>& aMap) = 0;
|
||||
virtual void onSignalWithVariant(const sdbus::Variant& aVariant) = 0;
|
||||
@@ -41,178 +45,214 @@ protected:
|
||||
public:
|
||||
void noArgNoReturn()
|
||||
{
|
||||
proxy_->callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
|
||||
m_proxy.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
int32_t getInt()
|
||||
{
|
||||
int32_t result;
|
||||
proxy_->callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<uint32_t, std::string> getTuple()
|
||||
{
|
||||
std::tuple<uint32_t, std::string> result;
|
||||
proxy_->callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
double multiply(const int64_t& a, const double& b)
|
||||
{
|
||||
double result;
|
||||
proxy_->callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
m_proxy.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void multiplyWithNoReply(const int64_t& a, const double& b)
|
||||
{
|
||||
proxy_->callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
|
||||
m_proxy.callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
|
||||
}
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0)
|
||||
{
|
||||
std::vector<int16_t> result;
|
||||
proxy_->callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
|
||||
m_proxy.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Variant processVariant(const sdbus::Variant& variant)
|
||||
{
|
||||
sdbus::Variant result;
|
||||
proxy_->callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
|
||||
m_proxy.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::variant<int32_t, double, std::string> processVariant(const std::variant<int32_t, double, std::string>& variant)
|
||||
{
|
||||
std::variant<int32_t, double, std::string> result;
|
||||
m_proxy.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
|
||||
{
|
||||
std::map<int32_t, sdbus::Variant> result;
|
||||
proxy_->callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
|
||||
m_proxy.callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct()
|
||||
{
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> result;
|
||||
proxy_->callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1)
|
||||
{
|
||||
int32_t result;
|
||||
proxy_->callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
|
||||
m_proxy.callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1)
|
||||
{
|
||||
uint32_t result;
|
||||
proxy_->callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
|
||||
m_proxy.callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t doOperation(const uint32_t& arg0)
|
||||
{
|
||||
uint32_t result;
|
||||
proxy_->callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
|
||||
m_proxy.callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
|
||||
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;
|
||||
proxy_->callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
|
||||
m_proxy.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
|
||||
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;
|
||||
proxy_->callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::ObjectPath getObjPath()
|
||||
{
|
||||
sdbus::ObjectPath result;
|
||||
proxy_->callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::UnixFd getUnixFd()
|
||||
{
|
||||
sdbus::UnixFd result;
|
||||
proxy_->callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex()
|
||||
{
|
||||
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> result;
|
||||
proxy_->callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void throwError()
|
||||
{
|
||||
proxy_->callMethod("throwError").onInterface(INTERFACE_NAME);
|
||||
m_proxy.callMethod("throwError").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void throwErrorWithNoReply()
|
||||
{
|
||||
proxy_->callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
|
||||
m_proxy.callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
|
||||
}
|
||||
|
||||
void doPrivilegedStuff()
|
||||
{
|
||||
proxy_->callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
|
||||
m_proxy.callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void emitTwoSimpleSignals()
|
||||
{
|
||||
proxy_->callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
|
||||
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()
|
||||
{
|
||||
proxy_->muteSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
simpleSignalSlot_.reset();
|
||||
}
|
||||
|
||||
void reRegisterSimpleSignalHandler()
|
||||
{
|
||||
proxy_->uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
|
||||
proxy_->finishRegistration();
|
||||
simpleSignalSlot_ = m_proxy.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }, sdbus::return_slot);
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t action()
|
||||
{
|
||||
return proxy_->getProperty("action").onInterface(INTERFACE_NAME);
|
||||
return m_proxy.getProperty("action").onInterface(INTERFACE_NAME).get<uint32_t>();
|
||||
}
|
||||
|
||||
void action(const uint32_t& value)
|
||||
{
|
||||
proxy_->setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
|
||||
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 proxy_->getProperty("blocking").onInterface(INTERFACE_NAME);
|
||||
return m_proxy.getProperty("blocking").onInterface(INTERFACE_NAME).get<bool>();
|
||||
}
|
||||
|
||||
void blocking(const bool& value)
|
||||
{
|
||||
proxy_->setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
|
||||
m_proxy.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
std::string state()
|
||||
{
|
||||
return proxy_->getProperty("state").onInterface(INTERFACE_NAME);
|
||||
return m_proxy.getProperty("state").onInterface(INTERFACE_NAME).get<std::string>();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
sdbus::IProxy& m_proxy;
|
||||
sdbus::Slot simpleSignalSlot_;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
@@ -1,114 +0,0 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
|
||||
#define __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
|
||||
class integrationtests_adaptor
|
||||
{
|
||||
public:
|
||||
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.integrationtests";
|
||||
|
||||
protected:
|
||||
integrationtests_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
{
|
||||
object_->setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
|
||||
object_->registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
|
||||
object_->registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); });
|
||||
object_->registerMethod("getTuple").onInterface(INTERFACE_NAME).withOutputParamNames("arg0", "arg1").implementedAs([this](){ return this->getTuple(); });
|
||||
object_->registerMethod("multiply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
|
||||
object_->registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").implementedAs([this](const int64_t& a, const double& b){ return this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
|
||||
object_->registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0){ return this->getInts16FromStruct(arg0); });
|
||||
object_->registerMethod("processVariant").onInterface(INTERFACE_NAME).withInputParamNames("variant").withOutputParamNames("result").implementedAs([this](const sdbus::Variant& variant){ return this->processVariant(variant); });
|
||||
object_->registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x, y); });
|
||||
object_->registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); });
|
||||
object_->registerMethod("sumStructItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1){ return this->sumStructItems(arg0, arg1); });
|
||||
object_->registerMethod("sumArrayItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1){ return this->sumArrayItems(arg0, arg1); });
|
||||
object_->registerMethod("doOperation").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); });
|
||||
object_->registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result<uint32_t>&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); });
|
||||
object_->registerMethod("getSignature").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); });
|
||||
object_->registerMethod("getObjPath").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getObjPath(); });
|
||||
object_->registerMethod("getUnixFd").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getUnixFd(); });
|
||||
object_->registerMethod("getComplex").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
|
||||
object_->registerMethod("throwError").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwError(); });
|
||||
object_->registerMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwErrorWithNoReply(); }).withNoReply();
|
||||
object_->registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged();
|
||||
object_->registerMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->emitTwoSimpleSignals(); });
|
||||
object_->registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
|
||||
object_->registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>("aMap");
|
||||
object_->registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>("aVariant");
|
||||
object_->registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL);
|
||||
object_->registerProperty("blocking").onInterface(INTERFACE_NAME).withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); });
|
||||
object_->registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
|
||||
}
|
||||
|
||||
integrationtests_adaptor(const integrationtests_adaptor&) = delete;
|
||||
integrationtests_adaptor& operator=(const integrationtests_adaptor&) = delete;
|
||||
integrationtests_adaptor(integrationtests_adaptor&&) = default;
|
||||
integrationtests_adaptor& operator=(integrationtests_adaptor&&) = default;
|
||||
|
||||
~integrationtests_adaptor() = default;
|
||||
|
||||
public:
|
||||
void emitSimpleSignal()
|
||||
{
|
||||
object_->emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void emitSignalWithMap(const std::map<int32_t, std::string>& aMap)
|
||||
{
|
||||
object_->emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
|
||||
}
|
||||
|
||||
void emitSignalWithVariant(const sdbus::Variant& aVariant)
|
||||
{
|
||||
object_->emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void noArgNoReturn() = 0;
|
||||
virtual int32_t getInt() = 0;
|
||||
virtual std::tuple<uint32_t, std::string> getTuple() = 0;
|
||||
virtual double multiply(const int64_t& a, const double& b) = 0;
|
||||
virtual void multiplyWithNoReply(const int64_t& a, const double& b) = 0;
|
||||
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) = 0;
|
||||
virtual sdbus::Variant processVariant(const sdbus::Variant& variant) = 0;
|
||||
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) = 0;
|
||||
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() = 0;
|
||||
virtual 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 void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0;
|
||||
virtual sdbus::Signature getSignature() = 0;
|
||||
virtual sdbus::ObjectPath getObjPath() = 0;
|
||||
virtual sdbus::UnixFd getUnixFd() = 0;
|
||||
virtual std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
|
||||
virtual void throwError() = 0;
|
||||
virtual void throwErrorWithNoReply() = 0;
|
||||
virtual void doPrivilegedStuff() = 0;
|
||||
virtual void emitTwoSimpleSignals() = 0;
|
||||
|
||||
private:
|
||||
virtual uint32_t action() = 0;
|
||||
virtual void action(const uint32_t& value) = 0;
|
||||
virtual bool blocking() = 0;
|
||||
virtual void blocking(const bool& value) = 0;
|
||||
virtual std::string state() = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file sdbus-c++-integration-tests.cpp
|
||||
*
|
||||
|
||||
+42
-29
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file client.cpp
|
||||
*
|
||||
@@ -24,37 +25,44 @@
|
||||
*/
|
||||
|
||||
#include "perftests-proxy.h"
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
uint64_t totalDuration = 0;
|
||||
uint64_t totalDuration = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
class PerftestProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::perftests_proxy>
|
||||
{
|
||||
public:
|
||||
PerftestProxy(std::string destination, std::string objectPath)
|
||||
PerftestProxy(sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
|
||||
: ProxyInterfaces(std::move(destination), std::move(objectPath))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
PerftestProxy(const PerftestProxy&) = delete;
|
||||
PerftestProxy& operator=(const PerftestProxy&) = delete;
|
||||
PerftestProxy(PerftestProxy&&) = delete;
|
||||
PerftestProxy& operator=(PerftestProxy&&) = delete;
|
||||
|
||||
~PerftestProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void onDataSignal([[maybe_unused]] const std::string& data) override
|
||||
void onDataSignal([[maybe_unused]] const std::string& data) override
|
||||
{
|
||||
static unsigned int counter = 0;
|
||||
static std::chrono::time_point<std::chrono::steady_clock> startTime;
|
||||
@@ -70,7 +78,7 @@ protected:
|
||||
auto stopTime = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
|
||||
totalDuration += duration;
|
||||
std::cout << "Received " << m_msgCount << " signals in: " << duration << " ms" << std::endl;
|
||||
std::cout << "Received " << m_msgCount << " signals in: " << duration << " ms" << '\n';
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
@@ -89,8 +97,13 @@ std::string createRandomString(size_t length)
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[ rand() % max_index ];
|
||||
return charset[ random() % max_index ];
|
||||
};
|
||||
|
||||
struct timespec ts{};
|
||||
(void)timespec_get(&ts, TIME_UTC);
|
||||
srandom(ts.tv_nsec ^ ts.tv_sec); /* Seed the PRNG */
|
||||
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
@@ -100,49 +113,49 @@ std::string createRandomString(size_t length)
|
||||
//-----------------------------------------
|
||||
int main(int /*argc*/, char */*argv*/[])
|
||||
{
|
||||
const char* destinationName = "org.sdbuscpp.perftests";
|
||||
const char* objectPath = "/org/sdbuscpp/perftests";
|
||||
PerftestProxy client(destinationName, objectPath);
|
||||
sdbus::ServiceName destination{"org.sdbuscpp.perftests"};
|
||||
sdbus::ObjectPath objectPath{"/org/sdbuscpp/perftests"};
|
||||
PerftestProxy client(std::move(destination), std::move(objectPath));
|
||||
|
||||
const unsigned int repetitions{20};
|
||||
unsigned int msgCount = 1000;
|
||||
unsigned int const msgCount = 1000;
|
||||
unsigned int msgSize{};
|
||||
|
||||
msgSize = 20;
|
||||
std::cout << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
|
||||
std::cout << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
|
||||
client.m_msgCount = msgCount; client.m_msgSize = msgSize;
|
||||
for (unsigned int r = 0; r < repetitions; ++r)
|
||||
for (unsigned int i = 0; i < repetitions; ++i)
|
||||
{
|
||||
client.sendDataSignals(msgCount, msgSize);
|
||||
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
}
|
||||
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
|
||||
totalDuration = 0;
|
||||
|
||||
msgSize = 1000;
|
||||
std::cout << std::endl << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
|
||||
std::cout << '\n' << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
|
||||
client.m_msgCount = msgCount; client.m_msgSize = msgSize;
|
||||
for (unsigned int r = 0; r < repetitions; ++r)
|
||||
for (unsigned int i = 0; i < repetitions; ++i)
|
||||
{
|
||||
client.sendDataSignals(msgCount, msgSize);
|
||||
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
}
|
||||
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
|
||||
totalDuration = 0;
|
||||
|
||||
msgSize = 20;
|
||||
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
|
||||
for (unsigned int r = 0; r < repetitions; ++r)
|
||||
std::cout << '\n' << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
|
||||
for (unsigned int i = 0; i < repetitions; ++i)
|
||||
{
|
||||
auto str1 = createRandomString(msgSize/2);
|
||||
auto str2 = createRandomString(msgSize/2);
|
||||
|
||||
auto startTime = std::chrono::steady_clock::now();
|
||||
for (unsigned int i = 0; i < msgCount; i++)
|
||||
for (unsigned int j = 0; j < msgCount; j++)
|
||||
{
|
||||
auto result = client.concatenateTwoStrings(str1, str2);
|
||||
|
||||
@@ -152,23 +165,23 @@ int main(int /*argc*/, char */*argv*/[])
|
||||
auto stopTime = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
|
||||
totalDuration += duration;
|
||||
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << std::endl;
|
||||
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << '\n';
|
||||
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
}
|
||||
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
|
||||
totalDuration = 0;
|
||||
|
||||
msgSize = 1000;
|
||||
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
|
||||
for (unsigned int r = 0; r < repetitions; ++r)
|
||||
std::cout << '\n' << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
|
||||
for (unsigned int i = 0; i < repetitions; ++i)
|
||||
{
|
||||
auto str1 = createRandomString(msgSize/2);
|
||||
auto str2 = createRandomString(msgSize/2);
|
||||
|
||||
auto startTime = std::chrono::steady_clock::now();
|
||||
for (unsigned int i = 0; i < msgCount; i++)
|
||||
for (unsigned int j = 0; j < msgCount; j++)
|
||||
{
|
||||
auto result = client.concatenateTwoStrings(str1, str2);
|
||||
|
||||
@@ -178,12 +191,12 @@ int main(int /*argc*/, char */*argv*/[])
|
||||
auto stopTime = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
|
||||
totalDuration += duration;
|
||||
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << std::endl;
|
||||
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << '\n';
|
||||
|
||||
std::this_thread::sleep_for(1000ms);
|
||||
}
|
||||
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
|
||||
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
|
||||
totalDuration = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
+13
-8
@@ -20,24 +20,29 @@ public:
|
||||
|
||||
protected:
|
||||
perftests_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
: m_object(object)
|
||||
{
|
||||
object_->registerMethod("sendDataSignals").onInterface(INTERFACE_NAME).withInputParamNames("numberOfSignals", "signalMsgSize").implementedAs([this](const uint32_t& numberOfSignals, const uint32_t& signalMsgSize){ return this->sendDataSignals(numberOfSignals, signalMsgSize); });
|
||||
object_->registerMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withInputParamNames("string1", "string2").withOutputParamNames("result").implementedAs([this](const std::string& string1, const std::string& string2){ return this->concatenateTwoStrings(string1, string2); });
|
||||
object_->registerSignal("dataSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("data");
|
||||
}
|
||||
|
||||
perftests_adaptor(const perftests_adaptor&) = delete;
|
||||
perftests_adaptor& operator=(const perftests_adaptor&) = delete;
|
||||
perftests_adaptor(perftests_adaptor&&) = default;
|
||||
perftests_adaptor& operator=(perftests_adaptor&&) = default;
|
||||
perftests_adaptor(perftests_adaptor&&) = delete;
|
||||
perftests_adaptor& operator=(perftests_adaptor&&) = delete;
|
||||
|
||||
~perftests_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable( sdbus::registerMethod("sendDataSignals").withInputParamNames("numberOfSignals", "signalMsgSize").implementedAs([this](const uint32_t& numberOfSignals, const uint32_t& signalMsgSize){ return this->sendDataSignals(numberOfSignals, signalMsgSize); })
|
||||
, sdbus::registerMethod("concatenateTwoStrings").withInputParamNames("string1", "string2").withOutputParamNames("result").implementedAs([this](const std::string& string1, const std::string& string2){ return this->concatenateTwoStrings(string1, string2); })
|
||||
, sdbus::registerSignal("dataSignal").withParameters<std::string>("data")
|
||||
).forInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
public:
|
||||
void emitDataSignal(const std::string& data)
|
||||
{
|
||||
object_->emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
|
||||
m_object.emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -45,7 +50,7 @@ private:
|
||||
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
+11
-7
@@ -20,35 +20,39 @@ public:
|
||||
|
||||
protected:
|
||||
perftests_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
proxy_->uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); });
|
||||
}
|
||||
|
||||
perftests_proxy(const perftests_proxy&) = delete;
|
||||
perftests_proxy& operator=(const perftests_proxy&) = delete;
|
||||
perftests_proxy(perftests_proxy&&) = default;
|
||||
perftests_proxy& operator=(perftests_proxy&&) = default;
|
||||
perftests_proxy(perftests_proxy&&) = delete;
|
||||
perftests_proxy& operator=(perftests_proxy&&) = delete;
|
||||
|
||||
~perftests_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
m_proxy.uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); });
|
||||
}
|
||||
|
||||
virtual void onDataSignal(const std::string& data) = 0;
|
||||
|
||||
public:
|
||||
void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize)
|
||||
{
|
||||
proxy_->callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
|
||||
m_proxy.callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
|
||||
}
|
||||
|
||||
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2)
|
||||
{
|
||||
std::string result;
|
||||
proxy_->callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
|
||||
m_proxy.callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
+25
-11
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file server.cpp
|
||||
*
|
||||
@@ -24,10 +25,13 @@
|
||||
*/
|
||||
|
||||
#include "perftests-adaptor.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
@@ -39,19 +43,24 @@ std::string createRandomString(size_t length);
|
||||
class PerftestAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::perftests_adaptor>
|
||||
{
|
||||
public:
|
||||
PerftestAdaptor(sdbus::IConnection& connection, std::string objectPath)
|
||||
PerftestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
|
||||
: AdaptorInterfaces(connection, std::move(objectPath))
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
PerftestAdaptor(const PerftestAdaptor&) = delete;
|
||||
PerftestAdaptor& operator=(const PerftestAdaptor&) = delete;
|
||||
PerftestAdaptor(PerftestAdaptor&&) = delete;
|
||||
PerftestAdaptor& operator=(PerftestAdaptor&&) = delete;
|
||||
|
||||
~PerftestAdaptor()
|
||||
{
|
||||
unregisterAdaptor();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) override
|
||||
void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) override // NOLINT(bugprone-easily-swappable-parameters)
|
||||
{
|
||||
auto data = createRandomString(signalMsgSize);
|
||||
|
||||
@@ -62,10 +71,10 @@ protected:
|
||||
emitDataSignal(data);
|
||||
}
|
||||
auto stop_time = std::chrono::steady_clock::now();
|
||||
std::cout << "Server sent " << numberOfSignals << " signals in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count() << " ms" << std::endl;
|
||||
std::cout << "Server sent " << numberOfSignals << " signals in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count() << " ms" << '\n';
|
||||
}
|
||||
|
||||
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) override
|
||||
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) override
|
||||
{
|
||||
return string1 + string2;
|
||||
}
|
||||
@@ -80,8 +89,13 @@ std::string createRandomString(size_t length)
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
const size_t max_index = (sizeof(charset) - 1);
|
||||
return charset[ rand() % max_index ];
|
||||
return charset[ random() % max_index ];
|
||||
};
|
||||
|
||||
struct timespec ts{};
|
||||
(void)timespec_get(&ts, TIME_UTC);
|
||||
srandom(ts.tv_nsec ^ ts.tv_sec); /* Seed the PRNG */
|
||||
|
||||
std::string str(length, 0);
|
||||
std::generate_n(str.begin(), length, randchar);
|
||||
return str;
|
||||
@@ -91,11 +105,11 @@ std::string createRandomString(size_t length)
|
||||
//-----------------------------------------
|
||||
int main(int /*argc*/, char */*argv*/[])
|
||||
{
|
||||
const char* serviceName = "org.sdbuscpp.perftests";
|
||||
sdbus::ServiceName const serviceName{"org.sdbuscpp.perftests"};
|
||||
auto connection = sdbus::createSystemBusConnection(serviceName);
|
||||
|
||||
const char* objectPath = "/org/sdbuscpp/perftests";
|
||||
PerftestAdaptor server(*connection, objectPath);
|
||||
sdbus::ObjectPath objectPath{"/org/sdbuscpp/perftests"};
|
||||
PerftestAdaptor server(*connection, std::move(objectPath)); // NOLINT(misc-const-correctness)
|
||||
|
||||
connection->enterEventLoop();
|
||||
}
|
||||
|
||||
+9
-5
@@ -22,23 +22,27 @@ public:
|
||||
|
||||
protected:
|
||||
thermometer_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
: m_object(object)
|
||||
{
|
||||
object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
|
||||
}
|
||||
|
||||
thermometer_adaptor(const thermometer_adaptor&) = delete;
|
||||
thermometer_adaptor& operator=(const thermometer_adaptor&) = delete;
|
||||
thermometer_adaptor(thermometer_adaptor&&) = default;
|
||||
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
|
||||
thermometer_adaptor(thermometer_adaptor&&) = delete;
|
||||
thermometer_adaptor& operator=(thermometer_adaptor&&) = delete;
|
||||
|
||||
~thermometer_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable(sdbus::registerMethod("getCurrentTemperature").withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); })).forInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual uint32_t getCurrentTemperature() = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}}}} // namespaces
|
||||
+9
-5
@@ -22,27 +22,31 @@ public:
|
||||
|
||||
protected:
|
||||
thermometer_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
thermometer_proxy(const thermometer_proxy&) = delete;
|
||||
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
|
||||
thermometer_proxy(thermometer_proxy&&) = default;
|
||||
thermometer_proxy& operator=(thermometer_proxy&&) = default;
|
||||
thermometer_proxy(thermometer_proxy&&) = delete;
|
||||
thermometer_proxy& operator=(thermometer_proxy&&) = delete;
|
||||
|
||||
~thermometer_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t getCurrentTemperature()
|
||||
{
|
||||
uint32_t result;
|
||||
proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
}}}} // namespaces
|
||||
+12
-7
@@ -21,30 +21,35 @@ public:
|
||||
|
||||
protected:
|
||||
concatenator_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
: m_object(object)
|
||||
{
|
||||
object_->registerMethod("concatenate").onInterface(INTERFACE_NAME).withInputParamNames("params").withOutputParamNames("result").implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); });
|
||||
object_->registerSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
|
||||
}
|
||||
|
||||
concatenator_adaptor(const concatenator_adaptor&) = delete;
|
||||
concatenator_adaptor& operator=(const concatenator_adaptor&) = delete;
|
||||
concatenator_adaptor(concatenator_adaptor&&) = default;
|
||||
concatenator_adaptor& operator=(concatenator_adaptor&&) = default;
|
||||
concatenator_adaptor(concatenator_adaptor&&) = delete;
|
||||
concatenator_adaptor& operator=(concatenator_adaptor&&) = delete;
|
||||
|
||||
~concatenator_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable( sdbus::registerMethod("concatenate").withInputParamNames("params").withOutputParamNames("result").implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); })
|
||||
, sdbus::registerSignal("concatenatedSignal").withParameters<std::string>("concatenatedString")
|
||||
).forInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
public:
|
||||
void emitConcatenatedSignal(const std::string& concatenatedString)
|
||||
{
|
||||
object_->emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
|
||||
m_object.emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
+11
-7
@@ -21,30 +21,34 @@ public:
|
||||
|
||||
protected:
|
||||
concatenator_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
proxy_->uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
|
||||
}
|
||||
|
||||
concatenator_proxy(const concatenator_proxy&) = delete;
|
||||
concatenator_proxy& operator=(const concatenator_proxy&) = delete;
|
||||
concatenator_proxy(concatenator_proxy&&) = default;
|
||||
concatenator_proxy& operator=(concatenator_proxy&&) = default;
|
||||
concatenator_proxy(concatenator_proxy&&) = delete;
|
||||
concatenator_proxy& operator=(concatenator_proxy&&) = delete;
|
||||
|
||||
~concatenator_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
m_proxy.uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
|
||||
}
|
||||
|
||||
virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0;
|
||||
|
||||
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) = 0;
|
||||
virtual void onConcatenateReply(const std::string& result, std::optional<sdbus::Error> error) = 0;
|
||||
|
||||
public:
|
||||
sdbus::PendingAsyncCall concatenate(const std::map<std::string, sdbus::Variant>& params)
|
||||
{
|
||||
return proxy_->callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
|
||||
return m_proxy.callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](std::optional<sdbus::Error> error, const std::string& result){ this->onConcatenateReply(result, std::move(error)); });
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
+20
-11
@@ -22,23 +22,27 @@ public:
|
||||
|
||||
protected:
|
||||
thermometer_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
: m_object(object)
|
||||
{
|
||||
object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
|
||||
}
|
||||
|
||||
thermometer_adaptor(const thermometer_adaptor&) = delete;
|
||||
thermometer_adaptor& operator=(const thermometer_adaptor&) = delete;
|
||||
thermometer_adaptor(thermometer_adaptor&&) = default;
|
||||
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
|
||||
thermometer_adaptor(thermometer_adaptor&&) = delete;
|
||||
thermometer_adaptor& operator=(thermometer_adaptor&&) = delete;
|
||||
|
||||
~thermometer_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable(sdbus::registerMethod("getCurrentTemperature").withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); })).forInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual uint32_t getCurrentTemperature() = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}}}} // namespaces
|
||||
@@ -56,25 +60,30 @@ public:
|
||||
|
||||
protected:
|
||||
factory_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
: m_object(object)
|
||||
{
|
||||
object_->registerMethod("createDelegateObject").onInterface(INTERFACE_NAME).withOutputParamNames("delegate").implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); });
|
||||
object_->registerMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withInputParamNames("delegate").implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
|
||||
}
|
||||
|
||||
factory_adaptor(const factory_adaptor&) = delete;
|
||||
factory_adaptor& operator=(const factory_adaptor&) = delete;
|
||||
factory_adaptor(factory_adaptor&&) = default;
|
||||
factory_adaptor& operator=(factory_adaptor&&) = default;
|
||||
factory_adaptor(factory_adaptor&&) = delete;
|
||||
factory_adaptor& operator=(factory_adaptor&&) = delete;
|
||||
|
||||
~factory_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable( sdbus::registerMethod("createDelegateObject").withOutputParamNames("delegate").implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); })
|
||||
, sdbus::registerMethod("destroyDelegateObject").withInputParamNames("delegate").implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply()
|
||||
).forInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) = 0;
|
||||
virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}}}}} // namespaces
|
||||
+19
-11
@@ -22,27 +22,31 @@ public:
|
||||
|
||||
protected:
|
||||
thermometer_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
thermometer_proxy(const thermometer_proxy&) = delete;
|
||||
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
|
||||
thermometer_proxy(thermometer_proxy&&) = default;
|
||||
thermometer_proxy& operator=(thermometer_proxy&&) = default;
|
||||
thermometer_proxy(thermometer_proxy&&) = delete;
|
||||
thermometer_proxy& operator=(thermometer_proxy&&) = delete;
|
||||
|
||||
~thermometer_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
uint32_t getCurrentTemperature()
|
||||
{
|
||||
uint32_t result;
|
||||
proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
}}}} // namespaces
|
||||
@@ -60,32 +64,36 @@ public:
|
||||
|
||||
protected:
|
||||
factory_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(&proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
factory_proxy(const factory_proxy&) = delete;
|
||||
factory_proxy& operator=(const factory_proxy&) = delete;
|
||||
factory_proxy(factory_proxy&&) = default;
|
||||
factory_proxy& operator=(factory_proxy&&) = default;
|
||||
factory_proxy(factory_proxy&&) = delete;
|
||||
factory_proxy& operator=(factory_proxy&&) = delete;
|
||||
|
||||
~factory_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
sdbus::ObjectPath createDelegateObject()
|
||||
{
|
||||
sdbus::ObjectPath result;
|
||||
proxy_->callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
m_proxy.callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void destroyDelegateObject(const sdbus::ObjectPath& delegate)
|
||||
{
|
||||
proxy_->callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
|
||||
m_proxy.callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy* proxy_;
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
}}}}} // namespaces
|
||||
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file sdbus-c++-stress-tests.cpp
|
||||
*
|
||||
@@ -33,42 +34,52 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
|
||||
#define SERVICE_1_BUS_NAME "org.sdbuscpp.stresstests.service1"s
|
||||
#define SERVICE_2_BUS_NAME "org.sdbuscpp.stresstests.service2"s
|
||||
#define CELSIUS_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/celsius/thermometer"s
|
||||
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"s
|
||||
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"s
|
||||
const sdbus::ServiceName SERVICE_1_BUS_NAME{"org.sdbuscpp.stresstests.service1"};
|
||||
const sdbus::ServiceName SERVICE_2_BUS_NAME{"org.sdbuscpp.stresstests.service2"};
|
||||
const sdbus::ObjectPath CELSIUS_THERMOMETER_OBJECT_PATH{"/org/sdbuscpp/stresstests/celsius/thermometer"};
|
||||
const sdbus::ObjectPath FAHRENHEIT_THERMOMETER_OBJECT_PATH{"/org/sdbuscpp/stresstests/fahrenheit/thermometer"};
|
||||
const sdbus::ObjectPath CONCATENATOR_OBJECT_PATH{"/org/sdbuscpp/stresstests/concatenator"};
|
||||
|
||||
class CelsiusThermometerAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>
|
||||
{
|
||||
public:
|
||||
CelsiusThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
|
||||
CelsiusThermometerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
|
||||
: AdaptorInterfaces(connection, std::move(objectPath))
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
CelsiusThermometerAdaptor(const CelsiusThermometerAdaptor&) = delete;
|
||||
CelsiusThermometerAdaptor& operator=(const CelsiusThermometerAdaptor&) = delete;
|
||||
CelsiusThermometerAdaptor(CelsiusThermometerAdaptor&&) = delete;
|
||||
CelsiusThermometerAdaptor& operator=(CelsiusThermometerAdaptor&&) = delete;
|
||||
|
||||
~CelsiusThermometerAdaptor()
|
||||
{
|
||||
unregisterAdaptor();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual uint32_t getCurrentTemperature() override
|
||||
uint32_t getCurrentTemperature() override
|
||||
{
|
||||
return m_currentTemperature++;
|
||||
}
|
||||
@@ -80,12 +91,17 @@ private:
|
||||
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>
|
||||
{
|
||||
public:
|
||||
CelsiusThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
|
||||
CelsiusThermometerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
CelsiusThermometerProxy(const CelsiusThermometerProxy&) = delete;
|
||||
CelsiusThermometerProxy& operator=(const CelsiusThermometerProxy&) = delete;
|
||||
CelsiusThermometerProxy(CelsiusThermometerProxy&&) = delete;
|
||||
CelsiusThermometerProxy& operator=(CelsiusThermometerProxy&&) = delete;
|
||||
|
||||
~CelsiusThermometerProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
@@ -96,7 +112,7 @@ class FahrenheitThermometerAdaptor final : public sdbus::AdaptorInterfaces< org:
|
||||
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >
|
||||
{
|
||||
public:
|
||||
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath, bool isDelegate)
|
||||
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath, bool isDelegate)
|
||||
: AdaptorInterfaces(connection, std::move(objectPath))
|
||||
, celsiusProxy_(connection, SERVICE_2_BUS_NAME, CELSIUS_THERMOMETER_OBJECT_PATH)
|
||||
{
|
||||
@@ -127,7 +143,7 @@ public:
|
||||
{
|
||||
// Create new delegate object
|
||||
auto& connection = getObject().getConnection();
|
||||
sdbus::ObjectPath newObjectPath = FAHRENHEIT_THERMOMETER_OBJECT_PATH + "/" + std::to_string(request.objectNr);
|
||||
sdbus::ObjectPath newObjectPath{FAHRENHEIT_THERMOMETER_OBJECT_PATH + "/" + std::to_string(request.objectNr)};
|
||||
|
||||
// Here we are testing dynamic creation of a D-Bus object in an async way
|
||||
auto adaptor = std::make_unique<FahrenheitThermometerAdaptor>(connection, newObjectPath, true);
|
||||
@@ -142,7 +158,7 @@ public:
|
||||
{
|
||||
// Destroy existing delegate object
|
||||
// Here we are testing dynamic removal of a D-Bus object in an async way
|
||||
std::lock_guard<std::mutex> lock{childrenMutex_};
|
||||
const std::lock_guard lock{childrenMutex_};
|
||||
children_.erase(request.delegateObjectPath);
|
||||
}
|
||||
}
|
||||
@@ -152,6 +168,11 @@ public:
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
FahrenheitThermometerAdaptor(const FahrenheitThermometerAdaptor&) = delete;
|
||||
FahrenheitThermometerAdaptor& operator=(const FahrenheitThermometerAdaptor&) = delete;
|
||||
FahrenheitThermometerAdaptor(FahrenheitThermometerAdaptor&&) = delete;
|
||||
FahrenheitThermometerAdaptor& operator=(FahrenheitThermometerAdaptor&&) = delete;
|
||||
|
||||
~FahrenheitThermometerAdaptor()
|
||||
{
|
||||
exit_ = true;
|
||||
@@ -163,24 +184,24 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual uint32_t getCurrentTemperature() override
|
||||
uint32_t getCurrentTemperature() override
|
||||
{
|
||||
// In this D-Bus call, make yet another D-Bus call to another service over the same connection
|
||||
return static_cast<uint32_t>(celsiusProxy_.getCurrentTemperature() * 1.8 + 32.);
|
||||
}
|
||||
|
||||
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) override
|
||||
void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) override
|
||||
{
|
||||
static size_t objectCounter{};
|
||||
objectCounter++;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
requests_.push(WorkItem{objectCounter, std::string{}, std::move(result)});
|
||||
requests_.push(WorkItem{objectCounter, {}, std::move(result)});
|
||||
lock.unlock();
|
||||
cond_.notify_one();
|
||||
}
|
||||
|
||||
virtual void destroyDelegateObject(sdbus::Result<>&& /*result*/, sdbus::ObjectPath delegate) override
|
||||
void destroyDelegateObject(sdbus::Result<>&& /*result*/, sdbus::ObjectPath delegate) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
requests_.push(WorkItem{0, std::move(delegate), {}});
|
||||
@@ -190,7 +211,7 @@ protected:
|
||||
|
||||
private:
|
||||
CelsiusThermometerProxy celsiusProxy_;
|
||||
std::map<std::string, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
|
||||
std::map<sdbus::ObjectPath, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
|
||||
std::mutex childrenMutex_;
|
||||
|
||||
struct WorkItem
|
||||
@@ -203,19 +224,24 @@ private:
|
||||
std::condition_variable cond_;
|
||||
std::queue<WorkItem> requests_;
|
||||
std::vector<std::thread> workers_;
|
||||
std::atomic<bool> exit_{};
|
||||
std::atomic<bool> exit_;
|
||||
};
|
||||
|
||||
class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_proxy
|
||||
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >
|
||||
{
|
||||
public:
|
||||
FahrenheitThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
|
||||
FahrenheitThermometerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
FahrenheitThermometerProxy(const FahrenheitThermometerProxy&) = delete;
|
||||
FahrenheitThermometerProxy& operator=(const FahrenheitThermometerProxy&) = delete;
|
||||
FahrenheitThermometerProxy(FahrenheitThermometerProxy&&) = delete;
|
||||
FahrenheitThermometerProxy& operator=(FahrenheitThermometerProxy&&) = delete;
|
||||
|
||||
~FahrenheitThermometerProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
@@ -225,7 +251,7 @@ public:
|
||||
class ConcatenatorAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::concatenator_adaptor>
|
||||
{
|
||||
public:
|
||||
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
|
||||
ConcatenatorAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
|
||||
: AdaptorInterfaces(connection, std::move(objectPath))
|
||||
{
|
||||
unsigned int workers = std::thread::hardware_concurrency();
|
||||
@@ -262,6 +288,11 @@ public:
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
ConcatenatorAdaptor(const ConcatenatorAdaptor&) = delete;
|
||||
ConcatenatorAdaptor& operator=(const ConcatenatorAdaptor&) = delete;
|
||||
ConcatenatorAdaptor(ConcatenatorAdaptor&&) = delete;
|
||||
ConcatenatorAdaptor& operator=(ConcatenatorAdaptor&&) = delete;
|
||||
|
||||
~ConcatenatorAdaptor()
|
||||
{
|
||||
exit_ = true;
|
||||
@@ -273,7 +304,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) override
|
||||
void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
requests_.push(WorkItem{std::move(params), std::move(result)});
|
||||
@@ -291,48 +322,53 @@ private:
|
||||
std::condition_variable cond_;
|
||||
std::queue<WorkItem> requests_;
|
||||
std::vector<std::thread> workers_;
|
||||
std::atomic<bool> exit_{};
|
||||
std::atomic<bool> exit_;
|
||||
};
|
||||
|
||||
class ConcatenatorProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>
|
||||
{
|
||||
public:
|
||||
ConcatenatorProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
|
||||
ConcatenatorProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
ConcatenatorProxy(const ConcatenatorProxy&) = delete;
|
||||
ConcatenatorProxy& operator=(const ConcatenatorProxy&) = delete;
|
||||
ConcatenatorProxy(ConcatenatorProxy&&) = delete;
|
||||
ConcatenatorProxy& operator=(ConcatenatorProxy&&) = delete;
|
||||
|
||||
~ConcatenatorProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void onConcatenateReply(const std::string& result, [[maybe_unused]] const sdbus::Error* error) override
|
||||
void onConcatenateReply(const std::string& result, [[maybe_unused]] std::optional<sdbus::Error> error) override
|
||||
{
|
||||
assert(error == nullptr);
|
||||
assert(error == std::nullopt);
|
||||
|
||||
std::stringstream str(result);
|
||||
std::string aString;
|
||||
str >> aString;
|
||||
assert(aString == "sdbus-c++-stress-tests");
|
||||
|
||||
uint32_t aNumber;
|
||||
uint32_t aNumber{};
|
||||
str >> aNumber;
|
||||
assert(aNumber > 0);
|
||||
|
||||
++repliesReceived_;
|
||||
}
|
||||
|
||||
virtual void onConcatenatedSignal(const std::string& concatenatedString) override
|
||||
void onConcatenatedSignal(const std::string& concatenatedString) override
|
||||
{
|
||||
std::stringstream str(concatenatedString);
|
||||
std::string aString;
|
||||
str >> aString;
|
||||
assert(aString == "sdbus-c++-stress-tests");
|
||||
|
||||
uint32_t aNumber;
|
||||
uint32_t aNumber{};
|
||||
str >> aNumber;
|
||||
assert(aNumber > 0);
|
||||
|
||||
@@ -340,15 +376,15 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
std::atomic<uint32_t> repliesReceived_{};
|
||||
std::atomic<uint32_t> signalsReceived_{};
|
||||
std::atomic<uint32_t> repliesReceived_;
|
||||
std::atomic<uint32_t> signalsReceived_;
|
||||
};
|
||||
|
||||
//-----------------------------------------
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char *argv[]) // NOLINT(bugprone-exception-escape, readability-function-cognitive-complexity)
|
||||
{
|
||||
long loops;
|
||||
long loopDuration;
|
||||
long loops{};
|
||||
long loopDuration{};
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
@@ -357,49 +393,52 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else if (argc == 3)
|
||||
{
|
||||
loops = std::atol(argv[1]);
|
||||
loopDuration = std::atol(argv[2]);
|
||||
loops = std::atol(argv[1]); // NOLINT(cert-err34-c, cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
loopDuration = std::atol(argv[2]); // NOLINT(cert-err34-c, cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Wrong program options");
|
||||
|
||||
std::cout << "Going on with " << loops << " loops and " << loopDuration << "ms loop duration" << std::endl;
|
||||
std::cout << "Going on with " << loops << " loops and " << loopDuration << "ms loop duration\n";
|
||||
|
||||
std::atomic<uint32_t> concatenationCallsMade{0};
|
||||
std::atomic<uint32_t> concatenationRepliesReceived{0};
|
||||
std::atomic<uint32_t> concatenationSignalsReceived{0};
|
||||
std::atomic<uint32_t> thermometerCallsMade{0};
|
||||
|
||||
std::atomic<bool> exitLogger{};
|
||||
std::atomic exitLogger{false};
|
||||
std::thread loggerThread([&]()
|
||||
{
|
||||
while (!exitLogger)
|
||||
{
|
||||
std::this_thread::sleep_for(1s);
|
||||
|
||||
std::cout << "Made " << concatenationCallsMade << " concatenation calls, received " << concatenationRepliesReceived << " replies and " << concatenationSignalsReceived << " signals so far." << std::endl;
|
||||
std::cout << "Made " << thermometerCallsMade << " thermometer calls so far." << std::endl << std::endl;
|
||||
std::cout << "Made " << concatenationCallsMade << " concatenation calls, received " << concatenationRepliesReceived << " replies and " << concatenationSignalsReceived << " signals so far.\n";
|
||||
std::cout << "Made " << thermometerCallsMade << " thermometer calls so far.\n\n";
|
||||
}
|
||||
});
|
||||
|
||||
for (long loop = 0; loop < loops; ++loop)
|
||||
{
|
||||
std::cout << "Entering loop " << loop+1 << std::endl;
|
||||
std::cout << "Entering loop " << loop+1 << '\n';
|
||||
|
||||
auto service2Connection = sdbus::createSystemBusConnection(SERVICE_2_BUS_NAME);
|
||||
std::atomic<bool> service2ThreadReady{};
|
||||
std::atomic service2ThreadReady{false};
|
||||
std::thread service2Thread([&con = *service2Connection, &service2ThreadReady]()
|
||||
{
|
||||
// NOLINTNEXTLINE(misc-const-correctness)
|
||||
CelsiusThermometerAdaptor thermometer(con, CELSIUS_THERMOMETER_OBJECT_PATH);
|
||||
service2ThreadReady = true;
|
||||
con.enterEventLoop();
|
||||
});
|
||||
|
||||
auto service1Connection = sdbus::createSystemBusConnection(SERVICE_1_BUS_NAME);
|
||||
std::atomic<bool> service1ThreadReady{};
|
||||
std::atomic service1ThreadReady{false};
|
||||
std::thread service1Thread([&con = *service1Connection, &service1ThreadReady]()
|
||||
{
|
||||
// NOLINTNEXTLINE(misc-const-correctness)
|
||||
ConcatenatorAdaptor concatenator(con, CONCATENATOR_OBJECT_PATH);
|
||||
// NOLINTNEXTLINE(misc-const-correctness)
|
||||
FahrenheitThermometerAdaptor thermometer(con, FAHRENHEIT_THERMOMETER_OBJECT_PATH, false);
|
||||
service1ThreadReady = true;
|
||||
con.enterEventLoop();
|
||||
@@ -415,7 +454,7 @@ int main(int argc, char *argv[])
|
||||
bool clientThreadExit{};
|
||||
std::thread clientThread([&, &con = *clientConnection]()
|
||||
{
|
||||
std::atomic<bool> stopClients{false};
|
||||
std::atomic stopClients{false};
|
||||
|
||||
std::thread concatenatorThread([&]()
|
||||
{
|
||||
@@ -442,8 +481,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Update statistics
|
||||
concatenationCallsMade = localCounter;
|
||||
concatenationRepliesReceived = (uint32_t)concatenator.repliesReceived_;
|
||||
concatenationSignalsReceived = (uint32_t)concatenator.signalsReceived_;
|
||||
concatenationRepliesReceived = static_cast<uint32_t>(concatenator.repliesReceived_);
|
||||
concatenationSignalsReceived = static_cast<uint32_t>(concatenator.signalsReceived_);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Connection_test.cpp
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
@@ -26,9 +26,16 @@
|
||||
*/
|
||||
|
||||
#include "Connection.h"
|
||||
#include "unittests/mocks/SdBusMock.h"
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/Types.h"
|
||||
#include "mocks/SdBusMock.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gtest/gtest.h> // IWYU pragma: export
|
||||
#include <cerrno>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::DoAll;
|
||||
@@ -179,10 +186,10 @@ template<> void AConnectionNameRequest<Connection::pseudo_bus_t>::setUpBusOpenEx
|
||||
// `sd_bus_start` for pseudo connection shall return an error value, remember this is a fake connection...
|
||||
EXPECT_CALL(*sdBusIntfMock_, sd_bus_start(fakeBusPtr_)).WillOnce(Return(-EINVAL));
|
||||
}
|
||||
template <typename _BusTypeTag>
|
||||
std::unique_ptr<Connection> AConnectionNameRequest<_BusTypeTag>::makeConnection()
|
||||
template <typename BusTypeTag>
|
||||
std::unique_ptr<Connection> AConnectionNameRequest<BusTypeTag>::makeConnection()
|
||||
{
|
||||
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), _BusTypeTag{});
|
||||
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), BusTypeTag{});
|
||||
}
|
||||
template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::custom_session_bus_t>::makeConnection()
|
||||
{
|
||||
@@ -193,26 +200,32 @@ template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::remote
|
||||
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), Connection::remote_system_bus, "some host");
|
||||
}
|
||||
|
||||
typedef ::testing::Types< Connection::default_bus_t
|
||||
, Connection::system_bus_t
|
||||
, Connection::session_bus_t
|
||||
, Connection::custom_session_bus_t
|
||||
, Connection::remote_system_bus_t
|
||||
, Connection::pseudo_bus_t
|
||||
> BusTypeTags;
|
||||
using BusTypeTags = ::testing::Types< Connection::default_bus_t
|
||||
, Connection::system_bus_t
|
||||
, Connection::session_bus_t
|
||||
, Connection::custom_session_bus_t
|
||||
, Connection::remote_system_bus_t
|
||||
, Connection::pseudo_bus_t
|
||||
>;
|
||||
|
||||
TYPED_TEST_SUITE(AConnectionNameRequest, BusTypeTags);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TYPED_TEST(AConnectionNameRequest, DoesNotThrowOnSuccess)
|
||||
{
|
||||
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1));
|
||||
this->con_->requestName("org.sdbuscpp.somename");
|
||||
const sdbus::ConnectionName name{"org.sdbuscpp.somename"};
|
||||
|
||||
this->con_->requestName(name);
|
||||
}
|
||||
|
||||
TYPED_TEST(AConnectionNameRequest, ThrowsOnFail)
|
||||
{
|
||||
const sdbus::ConnectionName name{"org.sdbuscpp.somename"};
|
||||
|
||||
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1));
|
||||
|
||||
ASSERT_THROW(this->con_->requestName("org.sdbuscpp.somename"), sdbus::Error);
|
||||
ASSERT_THROW(this->con_->requestName(name), sdbus::Error);
|
||||
}
|
||||
|
||||
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Message_test.cpp
|
||||
*
|
||||
@@ -24,16 +24,28 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::StrEq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::DoubleEq;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::SizeIs;
|
||||
using ::testing::ElementsAre;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
@@ -44,14 +56,15 @@ namespace
|
||||
msg >> str;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
template <typename _ElementType>
|
||||
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
|
||||
template <typename ElementType>
|
||||
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<ElementType>& items)
|
||||
{
|
||||
msg.openContainer(sdbus::signature_of<_ElementType>::str());
|
||||
// TODO: This can also be simplified on the basis of a callback (see dictionary...)
|
||||
msg.openContainer<ElementType>();
|
||||
|
||||
for (const auto& item : items)
|
||||
msg << item;
|
||||
@@ -61,15 +74,15 @@ namespace sdbus {
|
||||
return msg;
|
||||
}
|
||||
|
||||
template <typename _ElementType>
|
||||
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
|
||||
template <typename ElementType>
|
||||
sdbus::Message& operator>>(sdbus::Message& msg, std::list<ElementType>& items)
|
||||
{
|
||||
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
|
||||
if(!msg.enterContainer<ElementType>())
|
||||
return msg;
|
||||
|
||||
while (true)
|
||||
{
|
||||
_ElementType elem;
|
||||
ElementType elem;
|
||||
if (msg >> elem)
|
||||
items.emplace_back(std::move(elem));
|
||||
else
|
||||
@@ -83,44 +96,59 @@ namespace sdbus {
|
||||
return msg;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
struct sdbus::signature_of<std::list<_Element, _Allocator>>
|
||||
: sdbus::signature_of<std::vector<_Element, _Allocator>>
|
||||
template <typename Element, typename Allocator>
|
||||
struct sdbus::signature_of<std::list<Element, Allocator>>
|
||||
: sdbus::signature_of<std::vector<Element, Allocator>>
|
||||
{};
|
||||
|
||||
namespace my {
|
||||
enum class Enum : std::uint8_t
|
||||
{
|
||||
Value1,
|
||||
Value2,
|
||||
Value3
|
||||
};
|
||||
|
||||
struct Struct
|
||||
{
|
||||
int i;
|
||||
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)};
|
||||
}
|
||||
int i{};
|
||||
std::string s;
|
||||
Enum e{};
|
||||
Struct x;
|
||||
|
||||
sdbus::Message& operator>>(sdbus::Message& msg, Struct& items)
|
||||
{
|
||||
sdbus::Struct s{std::forward_as_tuple(items.i, items.s, items.l)};
|
||||
return msg >> s;
|
||||
}
|
||||
friend bool operator==(const NestedStruct& lhs, const NestedStruct& rhs) = default;
|
||||
};
|
||||
} // namespace my
|
||||
|
||||
}
|
||||
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l, e); // NOLINT(readability-identifier-length)
|
||||
|
||||
template <>
|
||||
struct sdbus::signature_of<my::Struct>
|
||||
: sdbus::signature_of<sdbus::Struct<int, std::string, std::list<double>>>
|
||||
{};
|
||||
SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(my::RelaxedStruct);
|
||||
SDBUSCPP_REGISTER_STRUCT(my::RelaxedStruct, i, s, l, e); // NOLINT(readability-identifier-length)
|
||||
|
||||
SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(my::NestedStruct);
|
||||
SDBUSCPP_REGISTER_STRUCT(my::NestedStruct, i, s, e, x); // NOLINT(readability-identifier-length)
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
@@ -133,7 +161,7 @@ TEST(AMessage, CanBeDefaultConstructed)
|
||||
|
||||
TEST(AMessage, IsInvalidAfterDefaultConstructed)
|
||||
{
|
||||
sdbus::PlainMessage msg;
|
||||
const sdbus::PlainMessage msg;
|
||||
|
||||
ASSERT_FALSE(msg.isValid());
|
||||
}
|
||||
@@ -198,7 +226,22 @@ TEST(AMessage, CanCarryASimpleInteger)
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
int dataRead;
|
||||
int dataRead{};
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryAStringAsAStringView)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::string_view dataWritten = "Hello";
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::string dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
@@ -223,7 +266,7 @@ TEST(AMessage, CanCarryAVariant)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const auto dataWritten = sdbus::Variant((double)3.14);
|
||||
const auto dataWritten = sdbus::Variant(3.14);
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
@@ -238,7 +281,7 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
std::vector<sdbus::Variant> value{sdbus::Variant{"hello"s}, sdbus::Variant{(double)3.14}};
|
||||
std::vector<sdbus::Variant> value{sdbus::Variant{"hello"s}, sdbus::Variant{3.14}};
|
||||
const auto dataWritten = sdbus::Variant{value};
|
||||
|
||||
msg << dataWritten;
|
||||
@@ -270,7 +313,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdVector)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::vector<sdbus::Signature> dataWritten{"s", "u", "b"};
|
||||
const std::vector dataWritten{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
@@ -285,12 +328,12 @@ TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdArray)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::array<int, 3> dataWritten{3545342, 43643532, 324325};
|
||||
const std::array dataWritten{3545342, 43643532, 324325};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::array<int, 3> dataRead;
|
||||
std::array<int, 3> dataRead{};
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
@@ -300,7 +343,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::array<sdbus::Signature, 3> dataWritten{"s", "u", "b"};
|
||||
const std::array dataWritten{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
@@ -311,18 +354,18 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
#ifdef __cpp_lib_span
|
||||
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdSpan)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::array<int, 3> sourceArray{3545342, 43643532, 324325};
|
||||
const std::array sourceArray{3545342, 43643532, 324325};
|
||||
const std::span dataWritten{sourceArray};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::array<int, 3> destinationArray;
|
||||
std::array<int, 3> destinationArray{};
|
||||
std::span dataRead{destinationArray};
|
||||
msg >> dataRead;
|
||||
|
||||
@@ -333,7 +376,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::array<sdbus::Signature, 3> sourceArray{"s", "u", "b"};
|
||||
const std::array sourceArray{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
|
||||
const std::span dataWritten{sourceArray};
|
||||
|
||||
msg << dataWritten;
|
||||
@@ -347,6 +390,24 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan)
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(AMessage, CanCarryAnEnumValue)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const enum class EnumA : int16_t {X = 5} aWritten{EnumA::X}; // NOLINT(performance-enum-size)
|
||||
const enum EnumB {Y = 11} bWritten{EnumB::Y}; // NOLINT(performance-enum-size)
|
||||
|
||||
msg << aWritten << bWritten;
|
||||
msg.seal();
|
||||
|
||||
EnumA aRead{};
|
||||
EnumB bRead{};
|
||||
msg >> aRead >> bRead;
|
||||
|
||||
ASSERT_THAT(aRead, Eq(aWritten));
|
||||
ASSERT_THAT(bRead, Eq(bWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, ThrowsWhenDestinationStdArrayIsTooSmallDuringDeserialization)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
@@ -356,11 +417,11 @@ TEST(AMessage, ThrowsWhenDestinationStdArrayIsTooSmallDuringDeserialization)
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::array<int, 3> dataRead;
|
||||
std::array<int, 3> dataRead{};
|
||||
ASSERT_THROW(msg >> dataRead, sdbus::Error);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
#ifdef __cpp_lib_span
|
||||
TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
@@ -370,7 +431,7 @@ TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization)
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::array<int, 2> destinationArray;
|
||||
std::array<int, 2> destinationArray{};
|
||||
std::span dataRead{destinationArray};
|
||||
ASSERT_THROW(msg >> dataRead, sdbus::Error);
|
||||
}
|
||||
@@ -380,7 +441,7 @@ TEST(AMessage, CanCarryADictionary)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
|
||||
const std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
@@ -415,7 +476,7 @@ TEST(AMessage, CanCarryAComplexType)
|
||||
>
|
||||
>;
|
||||
|
||||
ComplexType dataWritten = { {1, {{{5, {{"/some/object", true, 45, {{6, "hello"}, {7, "world"}}}}}}, "av", 3.14}}};
|
||||
const ComplexType dataWritten = { {1, {{{5, {{sdbus::ObjectPath{"/some/object"}, true, 45, {{6, "hello"}, {7, "world"}}}}}}, sdbus::Signature{"av"}, 3.14}}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
@@ -432,11 +493,10 @@ TEST(AMessage, CanPeekASimpleType)
|
||||
msg << 123;
|
||||
msg.seal();
|
||||
|
||||
std::string type;
|
||||
std::string contents;
|
||||
msg.peekType(type, contents);
|
||||
ASSERT_THAT(type, "i");
|
||||
ASSERT_THAT(contents, "");
|
||||
auto [type, contents] = msg.peekType();
|
||||
|
||||
ASSERT_THAT(type, Eq('i'));
|
||||
ASSERT_THAT(contents, IsNull());
|
||||
}
|
||||
|
||||
TEST(AMessage, CanPeekContainerContents)
|
||||
@@ -445,11 +505,10 @@ TEST(AMessage, CanPeekContainerContents)
|
||||
msg << std::map<int, std::string>{{1, "one"}, {2, "two"}};
|
||||
msg.seal();
|
||||
|
||||
std::string type;
|
||||
std::string contents;
|
||||
msg.peekType(type, contents);
|
||||
ASSERT_THAT(type, "a");
|
||||
ASSERT_THAT(contents, "{is}");
|
||||
auto [type, contents] = msg.peekType();
|
||||
|
||||
ASSERT_THAT(type, Eq('a'));
|
||||
ASSERT_THAT(contents, StrEq("{is}"));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryDBusArrayGivenAsCustomType)
|
||||
@@ -469,11 +528,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();
|
||||
@@ -483,3 +542,149 @@ 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();
|
||||
|
||||
const 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();
|
||||
|
||||
const 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();
|
||||
|
||||
const 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>>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(AMessage, CanCarryDBusVariantGivenAsStdVariant)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::variant<int32_t, std::string, my::Struct> dataWritten{GetParam()};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::variant<int32_t, std::string, my::Struct> dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST_P(AMessage, ThrowsWhenDestinationStdVariantHasWrongTypeDuringDeserialization)
|
||||
{
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
const std::variant<int32_t, std::string, my::Struct> dataWritten{GetParam()};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::variant<std::vector<bool>> dataRead;
|
||||
ASSERT_THROW(msg >> dataRead, sdbus::Error);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P( StringIntStruct
|
||||
, AMessage
|
||||
, ::testing::Values("hello"s, 1, my::Struct{}));
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file PollData_test.cpp
|
||||
*
|
||||
* Created on: Jan 19, 2023
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <chrono>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Ge;
|
||||
using ::testing::Le;
|
||||
using ::testing::AllOf;
|
||||
using namespace std::string_literals;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(PollData, ReturnsZeroRelativeTimeoutForZeroAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
pollData.timeout = std::chrono::microseconds::zero();
|
||||
|
||||
auto relativeTimeout = pollData.getRelativeTimeout();
|
||||
|
||||
EXPECT_THAT(relativeTimeout, Eq(std::chrono::microseconds::zero()));
|
||||
}
|
||||
|
||||
TEST(PollData, ReturnsZeroPollTimeoutForZeroAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
pollData.timeout = std::chrono::microseconds::zero();
|
||||
|
||||
auto pollTimeout = pollData.getPollTimeout();
|
||||
|
||||
EXPECT_THAT(pollTimeout, Eq(0));
|
||||
}
|
||||
|
||||
TEST(PollData, ReturnsInfiniteRelativeTimeoutForInfiniteAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
pollData.timeout = std::chrono::microseconds::max();
|
||||
|
||||
auto relativeTimeout = pollData.getRelativeTimeout();
|
||||
|
||||
EXPECT_THAT(relativeTimeout, Eq(std::chrono::microseconds::max()));
|
||||
}
|
||||
|
||||
TEST(PollData, ReturnsNegativePollTimeoutForInfiniteAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
pollData.timeout = std::chrono::microseconds::max();
|
||||
|
||||
auto pollTimeout = pollData.getPollTimeout();
|
||||
|
||||
EXPECT_THAT(pollTimeout, Eq(-1));
|
||||
}
|
||||
|
||||
TEST(PollData, ReturnsZeroRelativeTimeoutForPastAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
auto past = std::chrono::steady_clock::now() - 10s;
|
||||
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch());
|
||||
|
||||
auto relativeTimeout = pollData.getRelativeTimeout();
|
||||
|
||||
EXPECT_THAT(relativeTimeout, Eq(0us));
|
||||
}
|
||||
|
||||
TEST(PollData, ReturnsZeroPollTimeoutForPastAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
auto past = std::chrono::steady_clock::now() - 10s;
|
||||
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch());
|
||||
|
||||
auto pollTimeout = pollData.getPollTimeout();
|
||||
|
||||
EXPECT_THAT(pollTimeout, Eq(0));
|
||||
}
|
||||
|
||||
TEST(PollData, ReturnsCorrectRelativeTimeoutForFutureAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
auto future = std::chrono::steady_clock::now() + 1s;
|
||||
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch());
|
||||
|
||||
auto relativeTimeout = pollData.getRelativeTimeout();
|
||||
|
||||
EXPECT_THAT(relativeTimeout, AllOf(Ge(900ms), Le(1100ms)));
|
||||
}
|
||||
|
||||
TEST(PollData, ReturnsCorrectPollTimeoutForFutureAbsoluteTimeout)
|
||||
{
|
||||
sdbus::IConnection::PollData pollData{};
|
||||
auto future = std::chrono::steady_clock::now() + 1s;
|
||||
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch());
|
||||
|
||||
auto pollTimeout = pollData.getPollTimeout();
|
||||
|
||||
EXPECT_THAT(pollTimeout, AllOf(Ge(900), Le(1100)));
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TypeTraits_test.cpp
|
||||
*
|
||||
@@ -30,6 +30,15 @@
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <span>
|
||||
#include <tuple>
|
||||
|
||||
using ::testing::Eq;
|
||||
|
||||
@@ -39,20 +48,37 @@ namespace
|
||||
// FIXTURE DEFINITION FOR TYPED TESTS
|
||||
// ----
|
||||
|
||||
template <typename _T>
|
||||
template <typename>
|
||||
class Type2DBusTypeSignatureConversion
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
const std::string dbusTypeSignature_{getDBusTypeSignature()};
|
||||
std::string dbusTypeSignature_{getDBusTypeSignature()};
|
||||
private:
|
||||
static std::string getDBusTypeSignature();
|
||||
};
|
||||
|
||||
enum class SomeEnumClass : uint8_t
|
||||
{
|
||||
A, B, C
|
||||
};
|
||||
|
||||
enum struct SomeEnumStruct : int64_t // NOLINT(performance-enum-size)
|
||||
{
|
||||
A, B, C
|
||||
};
|
||||
|
||||
enum SomeClassicEnum // NOLINT(performance-enum-size)
|
||||
{
|
||||
A, B, C
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define TYPE(...) \
|
||||
template <> \
|
||||
std::string Type2DBusTypeSignatureConversion<__VA_ARGS__>::getDBusTypeSignature() \
|
||||
/**/
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define HAS_DBUS_TYPE_SIGNATURE(_SIG) \
|
||||
{ \
|
||||
return (_SIG); \
|
||||
@@ -70,17 +96,28 @@ namespace
|
||||
TYPE(double)HAS_DBUS_TYPE_SIGNATURE("d")
|
||||
TYPE(const char*)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(std::string)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(std::string_view)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(sdbus::BusName)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(sdbus::InterfaceName)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(sdbus::MemberName)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(sdbus::ObjectPath)HAS_DBUS_TYPE_SIGNATURE("o")
|
||||
TYPE(sdbus::Signature)HAS_DBUS_TYPE_SIGNATURE("g")
|
||||
TYPE(sdbus::Variant)HAS_DBUS_TYPE_SIGNATURE("v")
|
||||
TYPE(std::variant<int16_t, std::string>)HAS_DBUS_TYPE_SIGNATURE("v")
|
||||
TYPE(sdbus::UnixFd)HAS_DBUS_TYPE_SIGNATURE("h")
|
||||
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
|
||||
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
|
||||
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
|
||||
TYPE(std::array<int16_t, 3>)HAS_DBUS_TYPE_SIGNATURE("an")
|
||||
#if __cplusplus >= 202002L
|
||||
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("ao")
|
||||
#ifdef __cpp_lib_span
|
||||
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}")
|
||||
TYPE(std::unordered_map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
|
||||
using ComplexType = std::map<
|
||||
@@ -100,40 +137,51 @@ namespace
|
||||
>,
|
||||
sdbus::Signature,
|
||||
sdbus::UnixFd,
|
||||
const char*
|
||||
const char*,
|
||||
std::string_view
|
||||
>
|
||||
>;
|
||||
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghs)}")
|
||||
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghss)}")
|
||||
|
||||
typedef ::testing::Types< bool
|
||||
, uint8_t
|
||||
, int16_t
|
||||
, uint16_t
|
||||
, int32_t
|
||||
, uint32_t
|
||||
, int64_t
|
||||
, uint64_t
|
||||
, double
|
||||
, const char*
|
||||
, std::string
|
||||
, sdbus::ObjectPath
|
||||
, sdbus::Signature
|
||||
, sdbus::Variant
|
||||
, sdbus::UnixFd
|
||||
, sdbus::Struct<bool>
|
||||
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
|
||||
, std::vector<int16_t>
|
||||
, std::array<int16_t, 3>
|
||||
#if __cplusplus >= 202002L
|
||||
, std::span<int16_t>
|
||||
using DBusSupportedTypes = ::testing::Types< bool
|
||||
, uint8_t
|
||||
, int16_t
|
||||
, uint16_t
|
||||
, int32_t
|
||||
, uint32_t
|
||||
, int64_t
|
||||
, uint64_t
|
||||
, double
|
||||
, const char*
|
||||
, std::string
|
||||
, std::string_view
|
||||
, sdbus::BusName
|
||||
, sdbus::InterfaceName
|
||||
, sdbus::MemberName
|
||||
, sdbus::ObjectPath
|
||||
, sdbus::Signature
|
||||
, sdbus::Variant
|
||||
, std::variant<int16_t, std::string>
|
||||
, sdbus::UnixFd
|
||||
, sdbus::Struct<bool>
|
||||
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
|
||||
, std::vector<int16_t>
|
||||
, std::array<int16_t, 3>
|
||||
#ifdef __cpp_lib_span
|
||||
, std::span<int16_t>
|
||||
#endif
|
||||
, std::map<int32_t, int64_t>
|
||||
, std::unordered_map<int32_t, int64_t>
|
||||
, ComplexType
|
||||
> DBusSupportedTypes;
|
||||
, SomeEnumClass
|
||||
, const SomeEnumClass
|
||||
, volatile SomeEnumClass
|
||||
, const volatile SomeEnumClass
|
||||
, SomeEnumStruct
|
||||
, SomeClassicEnum
|
||||
, std::map<int32_t, int64_t>
|
||||
, std::unordered_map<int32_t, int64_t>
|
||||
, ComplexType >;
|
||||
|
||||
TYPED_TEST_SUITE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
@@ -141,7 +189,8 @@ namespace
|
||||
|
||||
TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
|
||||
{
|
||||
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
|
||||
constexpr auto signature = sdbus::as_null_terminated(sdbus::signature_of_v<TypeParam>);
|
||||
ASSERT_THAT(signature.data(), Eq(this->dbusTypeSignature_));
|
||||
}
|
||||
|
||||
TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)
|
||||
@@ -150,11 +199,11 @@ TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)
|
||||
using Fnc = decltype(f);
|
||||
|
||||
static_assert(!sdbus::is_async_method_v<Fnc>, "Free function incorrectly detected as async method");
|
||||
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<>>::value, "Incorrectly detected free function parameters");
|
||||
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<>>::value, "Incorrectly detected tuple of free function parameters");
|
||||
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, void>::value, "Incorrectly detected tuple of free function return types");
|
||||
static_assert(std::is_same_v<sdbus::function_arguments_t<Fnc>, std::tuple<>>, "Incorrectly detected free function parameters");
|
||||
static_assert(std::is_same_v<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<>>, "Incorrectly detected tuple of free function parameters");
|
||||
static_assert(std::is_same_v<sdbus::tuple_of_function_output_arg_types_t<Fnc>, void>, "Incorrectly detected tuple of free function return types");
|
||||
static_assert(sdbus::function_argument_count_v<Fnc> == 0, "Incorrectly detected free function parameter count");
|
||||
static_assert(std::is_void<sdbus::function_result_t<Fnc>>::value, "Incorrectly detected free function return type");
|
||||
static_assert(std::is_void_v<sdbus::function_result_t<Fnc>>, "Incorrectly detected free function return type");
|
||||
}
|
||||
|
||||
TEST(FreeFunctionTypeTraits, DetectsTraitsOfNontrivialSignatureFunction)
|
||||
@@ -163,11 +212,11 @@ TEST(FreeFunctionTypeTraits, DetectsTraitsOfNontrivialSignatureFunction)
|
||||
using Fnc = decltype(f);
|
||||
|
||||
static_assert(!sdbus::is_async_method_v<Fnc>, "Free function incorrectly detected as async method");
|
||||
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>::value, "Incorrectly detected free function parameters");
|
||||
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>::value, "Incorrectly detected tuple of free function parameters");
|
||||
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected tuple of free function return types");
|
||||
static_assert(std::is_same_v<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>, "Incorrectly detected free function parameters");
|
||||
static_assert(std::is_same_v<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>, "Incorrectly detected tuple of free function parameters");
|
||||
static_assert(std::is_same_v<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected tuple of free function return types");
|
||||
static_assert(sdbus::function_argument_count_v<Fnc> == 3, "Incorrectly detected free function parameter count");
|
||||
static_assert(std::is_same<sdbus::function_result_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected free function return type");
|
||||
static_assert(std::is_same_v<sdbus::function_result_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected free function return type");
|
||||
}
|
||||
|
||||
TEST(FreeFunctionTypeTraits, DetectsTraitsOfAsyncFunction)
|
||||
@@ -176,9 +225,9 @@ TEST(FreeFunctionTypeTraits, DetectsTraitsOfAsyncFunction)
|
||||
using Fnc = decltype(f);
|
||||
|
||||
static_assert(sdbus::is_async_method_v<Fnc>, "Free async function incorrectly detected as sync method");
|
||||
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>::value, "Incorrectly detected free function parameters");
|
||||
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>::value, "Incorrectly detected tuple of free function parameters");
|
||||
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected tuple of free function return types");
|
||||
static_assert(std::is_same_v<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>, "Incorrectly detected free function parameters");
|
||||
static_assert(std::is_same_v<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>, "Incorrectly detected tuple of free function parameters");
|
||||
static_assert(std::is_same_v<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected tuple of free function return types");
|
||||
static_assert(sdbus::function_argument_count_v<Fnc> == 3, "Incorrectly detected free function parameter count");
|
||||
static_assert(std::is_same<sdbus::function_result_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected free function return type");
|
||||
static_assert(std::is_same_v<sdbus::function_result_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected free function return type");
|
||||
}
|
||||
|
||||
+223
-59
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Types_test.cpp
|
||||
*
|
||||
@@ -24,22 +24,35 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
#include <cerrno>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
#include <sys/eventfd.h>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::HasSubstr;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const uint64_t ANY_UINT64 = 84578348354;
|
||||
constexpr const double ANY_DOUBLE = 3.14;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
@@ -52,14 +65,14 @@ TEST(AVariant, CanBeDefaultConstructed)
|
||||
|
||||
TEST(AVariant, ContainsNoValueAfterDefaultConstructed)
|
||||
{
|
||||
sdbus::Variant v;
|
||||
const sdbus::Variant var;
|
||||
|
||||
ASSERT_TRUE(v.isEmpty());
|
||||
ASSERT_TRUE(var.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeConstructedFromASimpleValue)
|
||||
{
|
||||
int value = 5;
|
||||
const int value = 5;
|
||||
|
||||
ASSERT_NO_THROW(sdbus::Variant{value});
|
||||
}
|
||||
@@ -67,53 +80,140 @@ TEST(AVariant, CanBeConstructedFromASimpleValue)
|
||||
TEST(AVariant, CanBeConstructedFromAComplexValue)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
|
||||
ASSERT_NO_THROW(sdbus::Variant{value});
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeConstructedFromAnStdVariant)
|
||||
{
|
||||
using ComplexType = std::vector<sdbus::Struct<std::string, double>>;
|
||||
using StdVariantType = std::variant<std::string, uint64_t, ComplexType>;
|
||||
ComplexType value{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}};
|
||||
const StdVariantType stdVariant{value};
|
||||
|
||||
const sdbus::Variant sdbusVariant{stdVariant};
|
||||
|
||||
ASSERT_TRUE(sdbusVariant.containsValueOfType<ComplexType>());
|
||||
ASSERT_THAT(sdbusVariant.get<ComplexType>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeCopied)
|
||||
{
|
||||
auto value = "hello"s;
|
||||
sdbus::Variant variant(value);
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
auto variantCopy1{variant};
|
||||
auto variantCopy2 = variantCopy1;
|
||||
const auto variantCopy1{variant}; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
const auto variantCopy2 = variantCopy1; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
|
||||
ASSERT_THAT(variantCopy1.get<std::string>(), Eq(value));
|
||||
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()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeMovedIntoAMap)
|
||||
{
|
||||
const auto value = "hello"s;
|
||||
sdbus::Variant variant(value); // NOLINT(misc-const-correctness)
|
||||
|
||||
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()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
|
||||
}
|
||||
|
||||
TEST(AVariant, IsNotEmptyWhenContainsAValue)
|
||||
{
|
||||
sdbus::Variant v("hello");
|
||||
const sdbus::Variant var("hello");
|
||||
|
||||
ASSERT_FALSE(v.isEmpty());
|
||||
ASSERT_FALSE(var.isEmpty());
|
||||
}
|
||||
|
||||
TEST(ASimpleVariant, ReturnsTheSimpleValueWhenAsked)
|
||||
{
|
||||
int value = 5;
|
||||
const int value = 5;
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<int>(), Eq(value));
|
||||
}
|
||||
|
||||
#ifndef SDBUS_basu // Dumping message or variant to a string is not supported on basu backend
|
||||
TEST(ASimpleVariant, CanBeDumpedToAString)
|
||||
{
|
||||
const int value = 5;
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
// This should produce something like:
|
||||
// VARIANT "i" {
|
||||
// INT32 5;
|
||||
// };
|
||||
const auto str = variant.dumpToString();
|
||||
|
||||
EXPECT_THAT(str, ::HasSubstr("VARIANT \"i\""));
|
||||
EXPECT_THAT(str, ::HasSubstr("INT32"));
|
||||
EXPECT_THAT(str, ::HasSubstr("5"));
|
||||
}
|
||||
#endif // SDBUS_basu
|
||||
|
||||
TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(variant.get<ComplexType>(), Eq(value));
|
||||
}
|
||||
|
||||
#ifndef SDBUS_basu // Dumping message or variant to a string is not supported on basu backend
|
||||
TEST(AComplexVariant, CanBeDumpedToAString)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
// This should produce something like:
|
||||
// VARIANT "a{ta(sd)}" {
|
||||
// ARRAY "{ta(sd)}" {
|
||||
// DICT_ENTRY "ta(sd)" {
|
||||
// UINT64 84578348354;
|
||||
// ARRAY "(sd)" {
|
||||
// STRUCT "sd" {
|
||||
// STRING "hello";
|
||||
// DOUBLE 3.14;
|
||||
// };
|
||||
// STRUCT "sd" {
|
||||
// STRING "world";
|
||||
// DOUBLE 3.14;
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
// };
|
||||
const auto str = variant.dumpToString();
|
||||
|
||||
EXPECT_THAT(str, ::HasSubstr("VARIANT \"a{ta(sd)}\""));
|
||||
EXPECT_THAT(str, ::HasSubstr("hello"));
|
||||
EXPECT_THAT(str, ::HasSubstr("world"));
|
||||
}
|
||||
#endif // SDBUS_basu
|
||||
|
||||
TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
|
||||
{
|
||||
std::string value{"I am a string"};
|
||||
sdbus::Variant variant(value);
|
||||
const std::string value{"I am a string"};
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
@@ -123,18 +223,42 @@ TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
|
||||
TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_TRUE(variant.containsValueOfType<ComplexType>());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeConvertedIntoAnStdVariant)
|
||||
{
|
||||
using ComplexType = std::vector<sdbus::Struct<std::string, double>>;
|
||||
using StdVariantType = std::variant<std::string, uint64_t, ComplexType>;
|
||||
const ComplexType value{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}};
|
||||
const sdbus::Variant sdbusVariant{value};
|
||||
StdVariantType stdVariant{sdbusVariant};
|
||||
|
||||
ASSERT_TRUE(std::holds_alternative<ComplexType>(stdVariant));
|
||||
ASSERT_THAT(std::get<ComplexType>(stdVariant), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, IsImplicitlyInterchangeableWithStdVariant)
|
||||
{
|
||||
using ComplexType = std::vector<sdbus::Struct<std::string, double>>;
|
||||
using StdVariantType = std::variant<std::string, uint64_t, ComplexType>;
|
||||
ComplexType value{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}};
|
||||
const StdVariantType stdVariant{value};
|
||||
|
||||
auto stdVariantCopy = [](const sdbus::Variant &var) -> StdVariantType { return var; }(stdVariant);
|
||||
|
||||
ASSERT_THAT(stdVariantCopy, Eq(stdVariant));
|
||||
}
|
||||
|
||||
TEST(ASimpleVariant, ReturnsFalseWhenAskedIfItContainsTypeItDoesntReallyContain)
|
||||
{
|
||||
int value = 5;
|
||||
const int value = 5;
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_FALSE(variant.containsValueOfType<double>());
|
||||
}
|
||||
@@ -143,17 +267,17 @@ TEST(AVariant, CanContainOtherEmbeddedVariants)
|
||||
{
|
||||
using TypeWithVariants = std::vector<sdbus::Struct<sdbus::Variant, double>>;
|
||||
TypeWithVariants value;
|
||||
value.push_back({sdbus::Variant("a string"), ANY_DOUBLE});
|
||||
value.push_back({sdbus::Variant(ANY_UINT64), ANY_DOUBLE});
|
||||
value.emplace_back(sdbus::Variant("a string"), ANY_DOUBLE);
|
||||
value.emplace_back(sdbus::Variant(ANY_UINT64), ANY_DOUBLE);
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_TRUE(variant.containsValueOfType<TypeWithVariants>());
|
||||
}
|
||||
|
||||
TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
|
||||
{
|
||||
sdbus::Variant variant("a string");
|
||||
const sdbus::Variant variant("a string");
|
||||
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
@@ -162,7 +286,7 @@ TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
|
||||
|
||||
TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
|
||||
{
|
||||
sdbus::Variant variant;
|
||||
const sdbus::Variant variant;
|
||||
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
|
||||
@@ -172,8 +296,8 @@ TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
|
||||
TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
sdbus::Variant variant(value);
|
||||
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
const sdbus::Variant variant(value);
|
||||
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
variant.serializeTo(msg);
|
||||
@@ -181,30 +305,30 @@ TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
|
||||
sdbus::Variant variant2;
|
||||
variant2.deserializeFrom(msg);
|
||||
|
||||
ASSERT_THAT(variant2.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(variant2.get<ComplexType>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
sdbus::Variant variant(value);
|
||||
auto variantCopy1{variant};
|
||||
auto variantCopy2 = variant;
|
||||
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
|
||||
const sdbus::Variant variant(value);
|
||||
auto variantCopy1{variant}; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
auto variantCopy2 = variant; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
|
||||
auto msg = sdbus::createPlainMessage();
|
||||
variant.serializeTo(msg);
|
||||
variantCopy1.serializeTo(msg);
|
||||
variantCopy2.serializeTo(msg);
|
||||
msg.seal();
|
||||
sdbus::Variant receivedVariant1, receivedVariant2, receivedVariant3;
|
||||
sdbus::Variant receivedVariant1, receivedVariant2, receivedVariant3; // NOLINT(readability-isolate-declaration)
|
||||
receivedVariant1.deserializeFrom(msg);
|
||||
receivedVariant2.deserializeFrom(msg);
|
||||
receivedVariant3.deserializeFrom(msg);
|
||||
|
||||
ASSERT_THAT(receivedVariant1.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant2.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant3.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant1.get<ComplexType>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant2.get<ComplexType>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant3.get<ComplexType>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AStruct, CanBeCreatedFromStdTuple)
|
||||
@@ -235,7 +359,7 @@ TEST(AStruct, CanBeUsedLikeStdTupleType)
|
||||
|
||||
TEST(AStruct, CanBeUsedInStructuredBinding)
|
||||
{
|
||||
sdbus::Struct valueStruct(1234, "abcd", true);
|
||||
const sdbus::Struct valueStruct(1234, "abcd", true);
|
||||
|
||||
auto [first, second, third] = valueStruct;
|
||||
|
||||
@@ -251,7 +375,7 @@ TEST(AnObjectPath, CanBeConstructedFromCString)
|
||||
|
||||
TEST(AnObjectPath, CanBeConstructedFromStdString)
|
||||
{
|
||||
std::string aPath{"/some/path"};
|
||||
const std::string aPath{"/some/path"};
|
||||
|
||||
ASSERT_THAT(sdbus::ObjectPath{aPath}, Eq(aPath));
|
||||
}
|
||||
@@ -262,7 +386,6 @@ TEST(AnObjectPath, CanBeMovedLikeAStdString)
|
||||
sdbus::ObjectPath oPath{aPath};
|
||||
|
||||
ASSERT_THAT(sdbus::ObjectPath{std::move(oPath)}, Eq(sdbus::ObjectPath(std::move(aPath))));
|
||||
ASSERT_THAT(std::string(oPath), Eq(aPath));
|
||||
}
|
||||
|
||||
TEST(ASignature, CanBeConstructedFromCString)
|
||||
@@ -274,7 +397,7 @@ TEST(ASignature, CanBeConstructedFromCString)
|
||||
|
||||
TEST(ASignature, CanBeConstructedFromStdString)
|
||||
{
|
||||
std::string aSignature{"us"};
|
||||
const std::string aSignature{"us"};
|
||||
|
||||
ASSERT_THAT(sdbus::Signature{aSignature}, Eq(aSignature));
|
||||
}
|
||||
@@ -285,7 +408,6 @@ TEST(ASignature, CanBeMovedLikeAStdString)
|
||||
sdbus::Signature oSignature{aSignature};
|
||||
|
||||
ASSERT_THAT(sdbus::Signature{std::move(oSignature)}, Eq(sdbus::Signature(std::move(aSignature))));
|
||||
ASSERT_THAT(std::string(oSignature), Eq(aSignature));
|
||||
}
|
||||
|
||||
TEST(AUnixFd, DuplicatesAndOwnsFdUponStandardConstruction)
|
||||
@@ -306,9 +428,9 @@ TEST(AUnixFd, AdoptsAndOwnsFdAsIsUponAdoptionConstruction)
|
||||
|
||||
TEST(AUnixFd, DuplicatesFdUponCopyConstruction)
|
||||
{
|
||||
sdbus::UnixFd unixFd(::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK));
|
||||
const sdbus::UnixFd unixFd(::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK));
|
||||
|
||||
sdbus::UnixFd unixFdCopy{unixFd};
|
||||
const sdbus::UnixFd unixFdCopy{unixFd}; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
|
||||
EXPECT_THAT(unixFdCopy.get(), Gt(unixFd.get()));
|
||||
}
|
||||
@@ -318,20 +440,20 @@ TEST(AUnixFd, TakesOverFdUponMoveConstruction)
|
||||
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
|
||||
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
|
||||
|
||||
sdbus::UnixFd unixFdNew{std::move(unixFd)};
|
||||
const sdbus::UnixFd unixFdNew{std::move(unixFd)};
|
||||
|
||||
EXPECT_FALSE(unixFd.isValid());
|
||||
EXPECT_FALSE(unixFd.isValid()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move)
|
||||
EXPECT_THAT(unixFdNew.get(), Eq(fd));
|
||||
}
|
||||
|
||||
TEST(AUnixFd, ClosesFdProperlyUponDestruction)
|
||||
{
|
||||
int fd, fdCopy;
|
||||
int fd{}, fdCopy{}; // NOLINT(readability-isolate-declaration)
|
||||
{
|
||||
fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
|
||||
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
|
||||
auto unixFdNew = std::move(unixFd);
|
||||
auto unixFdCopy = unixFdNew;
|
||||
auto unixFdCopy = unixFdNew; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
fdCopy = unixFdCopy.get();
|
||||
}
|
||||
|
||||
@@ -342,7 +464,7 @@ TEST(AUnixFd, ClosesFdProperlyUponDestruction)
|
||||
TEST(AUnixFd, DoesNotCloseReleasedFd)
|
||||
{
|
||||
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
|
||||
int fdReleased;
|
||||
int fdReleased{};
|
||||
{
|
||||
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
|
||||
fdReleased = unixFd.release();
|
||||
@@ -391,18 +513,60 @@ TEST(AUnixFd, TakesOverNewFdAndClosesOriginalFdOnAdoptingReset)
|
||||
|
||||
TEST(AnError, CanBeConstructedFromANameAndAMessage)
|
||||
{
|
||||
auto error = sdbus::Error("name", "message");
|
||||
EXPECT_THAT(error.getName(), Eq<std::string>("name"));
|
||||
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)
|
||||
{
|
||||
auto error1 = sdbus::Error("name");
|
||||
auto error2 = sdbus::Error("name", nullptr);
|
||||
EXPECT_THAT(error1.getName(), Eq<std::string>("name"));
|
||||
EXPECT_THAT(error2.getName(), Eq<std::string>("name"));
|
||||
auto error1 = sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.error"});
|
||||
auto error2 = sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.error"}, nullptr);
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file SdBusMock.h
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
@@ -35,18 +35,18 @@
|
||||
class SdBusMock : public sdbus::internal::ISdBus
|
||||
{
|
||||
public:
|
||||
MOCK_METHOD1(sd_bus_message_ref, sd_bus_message*(sd_bus_message *m));
|
||||
MOCK_METHOD1(sd_bus_message_unref, sd_bus_message*(sd_bus_message *m));
|
||||
MOCK_METHOD1(sd_bus_message_ref, sd_bus_message*(sd_bus_message *msg));
|
||||
MOCK_METHOD1(sd_bus_message_unref, sd_bus_message*(sd_bus_message *msg));
|
||||
|
||||
MOCK_METHOD3(sd_bus_send, int(sd_bus *bus, sd_bus_message *m, uint64_t *cookie));
|
||||
MOCK_METHOD5(sd_bus_call, int(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply));
|
||||
MOCK_METHOD6(sd_bus_call_async, int(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec));
|
||||
MOCK_METHOD3(sd_bus_send, int(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie));
|
||||
MOCK_METHOD5(sd_bus_call, int(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply));
|
||||
MOCK_METHOD6(sd_bus_call_async, int(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec));
|
||||
|
||||
MOCK_METHOD3(sd_bus_message_new, int(sd_bus *bus, sd_bus_message **m, uint8_t type));
|
||||
MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member));
|
||||
MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member));
|
||||
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **m));
|
||||
MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e));
|
||||
MOCK_METHOD3(sd_bus_message_new, int(sd_bus *bus, sd_bus_message **msg, uint8_t type));
|
||||
MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member));
|
||||
MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member));
|
||||
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **msg));
|
||||
MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err));
|
||||
|
||||
MOCK_METHOD2(sd_bus_set_method_call_timeout, int(sd_bus *bus, uint64_t usec));
|
||||
MOCK_METHOD2(sd_bus_get_method_call_timeout, int(sd_bus *bus, uint64_t *ret));
|
||||
@@ -72,21 +72,24 @@ public:
|
||||
MOCK_METHOD3(sd_bus_add_object_manager, int(sd_bus *bus, sd_bus_slot **slot, const char *path));
|
||||
MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata));
|
||||
MOCK_METHOD6(sd_bus_add_match_async, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata));
|
||||
MOCK_METHOD8(sd_bus_match_signal, int(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata));
|
||||
MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot));
|
||||
|
||||
MOCK_METHOD1(sd_bus_new, int(sd_bus **ret));
|
||||
MOCK_METHOD1(sd_bus_start, int(sd_bus *bus));
|
||||
|
||||
MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r));
|
||||
MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **ret));
|
||||
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_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));
|
||||
|
||||
MOCK_METHOD2(sd_bus_message_set_destination, int(sd_bus_message *m, const char *destination));
|
||||
MOCK_METHOD2(sd_bus_message_set_destination, int(sd_bus_message *msg, 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 *));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file sdbus-c++-unit-tests.cpp
|
||||
*
|
||||
|
||||
+28
-19
@@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(sdbus-c++-tools VERSION 1.5.0)
|
||||
project(sdbus-c++-tools VERSION 2.3.1)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -35,7 +35,8 @@ set(SDBUSCPP_XML2CPP_SRCS
|
||||
# GENERAL COMPILER CONFIGURATION
|
||||
#-------------------------------
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
unset(CMAKE_CXX_CLANG_TIDY) # Do not propagate clang-tidy to tools
|
||||
|
||||
#----------------------------------
|
||||
# EXECUTABLE BUILD INFORMATION
|
||||
@@ -44,12 +45,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})
|
||||
|
||||
#----------------------------------
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
|
||||
install(TARGETS sdbus-c++-xml2cpp EXPORT sdbus-c++-tools-targets DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT dev)
|
||||
target_compile_definitions(sdbus-c++-xml2cpp PRIVATE SDBUS_XML2CPP_VERSION="${CMAKE_PROJECT_VERSION}")
|
||||
|
||||
#----------------------------------
|
||||
# CMAKE CONFIG & PACKAGE CONFIG
|
||||
@@ -57,19 +53,32 @@ install(TARGETS sdbus-c++-xml2cpp EXPORT sdbus-c++-tools-targets DESTINATION ${C
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
install(EXPORT sdbus-c++-tools-targets
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
|
||||
NAMESPACE SDBusCpp::
|
||||
COMPONENT dev)
|
||||
|
||||
configure_package_config_file(cmake/sdbus-c++-tools-config.cmake.in cmake/sdbus-c++-tools-config.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++)
|
||||
write_basic_package_version_file(cmake/sdbus-c++-tools-config-version.cmake COMPATIBILITY SameMajorVersion)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
|
||||
COMPONENT dev)
|
||||
|
||||
configure_file(pkgconfig/sdbus-c++-tools.pc.in pkgconfig/sdbus-c++-tools.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++-tools.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
|
||||
|
||||
#----------------------------------
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
|
||||
if(NOT DEFINED SDBUSCPP_INSTALL)
|
||||
set(SDBUSCPP_INSTALL ON)
|
||||
endif()
|
||||
|
||||
if (SDBUSCPP_INSTALL)
|
||||
install(TARGETS sdbus-c++-xml2cpp
|
||||
EXPORT sdbus-c++-tools-targets
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-dev)
|
||||
install(EXPORT sdbus-c++-tools-targets
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
|
||||
NAMESPACE SDBusCpp::
|
||||
COMPONENT sdbus-c++-dev)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
|
||||
COMPONENT sdbus-c++-dev)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++-tools.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT sdbus-c++-dev)
|
||||
endif()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file AdaptorGenerator.cpp
|
||||
*
|
||||
@@ -85,7 +85,17 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
<< tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
|
||||
<< "protected:" << endl
|
||||
<< tab << className << "(sdbus::IObject& object)" << endl
|
||||
<< tab << tab << ": object_(&object)" << endl;
|
||||
<< tab << tab << ": m_object(object)" << endl
|
||||
<< tab << "{" << endl
|
||||
<< tab << "}" << endl << endl;
|
||||
|
||||
// Rule of Five
|
||||
body << tab << className << "(const " << className << "&) = delete;" << endl;
|
||||
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
|
||||
body << tab << className << "(" << className << "&&) = delete;" << endl;
|
||||
body << tab << className << "& operator=(" << className << "&&) = delete;" << endl << endl;
|
||||
|
||||
body << tab << "~" << className << "() = default;" << endl << endl;
|
||||
|
||||
Nodes methods = interface["method"];
|
||||
Nodes signals = interface["signal"];
|
||||
@@ -111,34 +121,29 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
if(!annotationRegistration.empty())
|
||||
{
|
||||
std::stringstream str;
|
||||
str << tab << tab << "object_->setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl;
|
||||
str << "sdbus::setInterfaceFlags()" << annotationRegistration;
|
||||
annotationRegistration = str.str();
|
||||
}
|
||||
|
||||
std::string methodRegistration, methodDeclaration;
|
||||
std::tie(methodRegistration, methodDeclaration) = processMethods(methods);
|
||||
std::vector<std::string> methodRegistrations;
|
||||
std::string methodDeclaration;
|
||||
std::tie(methodRegistrations, methodDeclaration) = processMethods(methods);
|
||||
|
||||
std::string signalRegistration, signalMethods;
|
||||
std::tie(signalRegistration, signalMethods) = processSignals(signals);
|
||||
std::vector<std::string> signalRegistrations;
|
||||
std::string signalMethods;
|
||||
std::tie(signalRegistrations, signalMethods) = processSignals(signals);
|
||||
|
||||
std::string propertyRegistration, propertyAccessorDeclaration;
|
||||
std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties);
|
||||
std::vector<std::string> propertyRegistrations;
|
||||
std::string propertyAccessorDeclaration;
|
||||
std::tie(propertyRegistrations, propertyAccessorDeclaration) = processProperties(properties);
|
||||
|
||||
body << tab << "{" << endl
|
||||
<< annotationRegistration
|
||||
<< methodRegistration
|
||||
<< signalRegistration
|
||||
<< propertyRegistration
|
||||
auto vtableRegistration = createVTableRegistration(annotationRegistration, methodRegistrations, signalRegistrations, propertyRegistrations);
|
||||
|
||||
body << tab << "void registerAdaptor()" << endl
|
||||
<< tab << "{" << endl
|
||||
<< vtableRegistration << endl
|
||||
<< tab << "}" << endl << endl;
|
||||
|
||||
// Rule of Five
|
||||
body << tab << className << "(const " << className << "&) = delete;" << endl;
|
||||
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
|
||||
body << tab << className << "(" << className << "&&) = default;" << endl;
|
||||
body << tab << className << "& operator=(" << className << "&&) = default;" << endl << endl;
|
||||
|
||||
body << tab << "~" << className << "() = default;" << endl << endl;
|
||||
|
||||
if (!signalMethods.empty())
|
||||
{
|
||||
body << "public:" << endl << signalMethods;
|
||||
@@ -155,7 +160,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
}
|
||||
|
||||
body << "private:" << endl
|
||||
<< tab << "sdbus::IObject* object_;" << endl
|
||||
<< tab << "sdbus::IObject& m_object;" << endl
|
||||
<< "};" << endl << endl
|
||||
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
|
||||
|
||||
@@ -163,12 +168,16 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
|
||||
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
std::ostringstream declarationSS;
|
||||
|
||||
std::vector<std::string> methodRegistrations;
|
||||
|
||||
for (const auto& method : methods)
|
||||
{
|
||||
std::ostringstream registrationSS;
|
||||
|
||||
auto methodName = method->get("name");
|
||||
auto methodNameSafe = mangle_name(methodName);
|
||||
|
||||
@@ -217,9 +226,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
registrationSS << tab << tab << "object_->registerMethod(\""
|
||||
registrationSS << "sdbus::registerMethod(\""
|
||||
<< methodName << "\")"
|
||||
<< ".onInterface(INTERFACE_NAME)"
|
||||
<< (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
|
||||
<< (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "")
|
||||
<< ".implementedAs("
|
||||
@@ -229,7 +237,9 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
||||
<< "){ " << (async ? "" : "return ") << "this->" << methodNameSafe << "("
|
||||
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argStr << "); })"
|
||||
<< annotationRegistration << ";" << endl;
|
||||
<< annotationRegistration;
|
||||
|
||||
methodRegistrations.push_back(registrationSS.str());
|
||||
|
||||
declarationSS << tab
|
||||
<< "virtual "
|
||||
@@ -241,16 +251,20 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
||||
<< ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
return std::make_tuple(methodRegistrations, declarationSS.str());
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
|
||||
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
|
||||
{
|
||||
std::ostringstream signalRegistrationSS, signalMethodSS;
|
||||
std::ostringstream signalMethodSS;
|
||||
|
||||
std::vector<std::string> signalRegistrations;
|
||||
|
||||
for (const auto& signal : signals)
|
||||
{
|
||||
std::stringstream signalRegistrationSS;
|
||||
|
||||
auto name = signal->get("name");
|
||||
|
||||
auto annotations = getAnnotations(*signal);
|
||||
@@ -272,9 +286,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
|
||||
std::string argStr, argTypeStr, typeStr, argStringsStr;
|
||||
std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
|
||||
|
||||
signalRegistrationSS << tab << tab
|
||||
<< "object_->registerSignal(\"" << name << "\")"
|
||||
".onInterface(INTERFACE_NAME)";
|
||||
signalRegistrationSS << "sdbus::registerSignal(\"" << name << "\")";
|
||||
|
||||
if (args.size() > 0)
|
||||
{
|
||||
@@ -282,7 +294,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
|
||||
}
|
||||
|
||||
signalRegistrationSS << annotationRegistration;
|
||||
signalRegistrationSS << ";" << endl;
|
||||
|
||||
signalRegistrations.push_back(signalRegistrationSS.str());
|
||||
|
||||
auto nameWithCapFirstLetter = name;
|
||||
nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]);
|
||||
@@ -290,7 +303,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
|
||||
|
||||
signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl
|
||||
<< tab << "{" << endl
|
||||
<< tab << tab << "object_->emitSignal(\"" << name << "\")"
|
||||
<< tab << tab << "m_object.emitSignal(\"" << name << "\")"
|
||||
".onInterface(INTERFACE_NAME)";
|
||||
|
||||
if (!argStr.empty())
|
||||
@@ -302,16 +315,20 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
|
||||
<< tab << "}" << endl << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(signalRegistrationSS.str(), signalMethodSS.str());
|
||||
return std::make_tuple(signalRegistrations, signalMethodSS.str());
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
|
||||
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
std::ostringstream declarationSS;
|
||||
|
||||
std::vector<std::string> propertyRegistrations;
|
||||
|
||||
for (const auto& property : properties)
|
||||
{
|
||||
std::ostringstream registrationSS;
|
||||
|
||||
auto propertyName = property->get("name");
|
||||
auto propertyNameSafe = mangle_name(propertyName);
|
||||
auto propertyAccess = property->get("access");
|
||||
@@ -339,9 +356,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
|
||||
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
|
||||
}
|
||||
|
||||
registrationSS << tab << tab << "object_->registerProperty(\""
|
||||
<< propertyName << "\")"
|
||||
<< ".onInterface(INTERFACE_NAME)";
|
||||
registrationSS << "sdbus::registerProperty(\""
|
||||
<< propertyName << "\")";
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
{
|
||||
@@ -356,7 +372,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
|
||||
}
|
||||
|
||||
registrationSS << annotationRegistration;
|
||||
registrationSS << ";" << endl;
|
||||
|
||||
propertyRegistrations.push_back(registrationSS.str());
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
declarationSS << tab << "virtual " << propertyType << " " << propertyNameSafe << "() = 0;" << endl;
|
||||
@@ -364,7 +381,38 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
|
||||
declarationSS << tab << "virtual void " << propertyNameSafe << "(" << propertyTypeArg << ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
return std::make_tuple(propertyRegistrations, declarationSS.str());
|
||||
}
|
||||
|
||||
std::string AdaptorGenerator::createVTableRegistration(const std::string& annotationRegistration,
|
||||
const std::vector<std::string>& methodRegistrations,
|
||||
const std::vector<std::string>& signalRegistrations,
|
||||
const std::vector<std::string>& propertyRegistrations) const
|
||||
{
|
||||
std::vector<std::string> allRegistrations;
|
||||
if (!annotationRegistration.empty())
|
||||
allRegistrations.push_back(annotationRegistration);
|
||||
allRegistrations.insert(allRegistrations.end(), methodRegistrations.begin(), methodRegistrations.end());
|
||||
allRegistrations.insert(allRegistrations.end(), signalRegistrations.begin(), signalRegistrations.end());
|
||||
allRegistrations.insert(allRegistrations.end(), propertyRegistrations.begin(), propertyRegistrations.end());
|
||||
|
||||
if (allRegistrations.empty())
|
||||
return {};
|
||||
|
||||
std::ostringstream registrationSS;
|
||||
if (allRegistrations.size() == 1)
|
||||
{
|
||||
registrationSS << tab << tab << "m_object.addVTable(" << allRegistrations[0] << ").forInterface(INTERFACE_NAME);";
|
||||
}
|
||||
else
|
||||
{
|
||||
registrationSS << tab << tab << "m_object.addVTable( " << allRegistrations[0] << endl;
|
||||
for (size_t i = 1; i < allRegistrations.size(); ++i)
|
||||
registrationSS << tab << tab << " , " << allRegistrations[i] << endl;
|
||||
registrationSS << tab << tab << " ).forInterface(INTERFACE_NAME);";
|
||||
}
|
||||
|
||||
return registrationSS.str();
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> AdaptorGenerator::getAnnotations( sdbuscpp::xml::Node& node) const
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file AdaptorGenerator.h
|
||||
*
|
||||
@@ -59,21 +59,26 @@ private:
|
||||
* @param methods
|
||||
* @return tuple: registration of methods, declaration of abstract methods
|
||||
*/
|
||||
std::tuple<std::string, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
|
||||
std::tuple<std::vector<std::string>, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
|
||||
|
||||
/**
|
||||
* Generate source code for signals
|
||||
* @param signals
|
||||
* @return tuple: registration of signals, definition of signal methods
|
||||
*/
|
||||
std::tuple<std::string, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
|
||||
std::tuple<std::vector<std::string>, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
|
||||
|
||||
/**
|
||||
* Generate source code for properties
|
||||
* @param properties
|
||||
* @return tuple: registration of properties, declaration of property accessor virtual methods
|
||||
*/
|
||||
std::tuple<std::string, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
|
||||
std::tuple<std::vector<std::string>, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
|
||||
|
||||
std::string createVTableRegistration(const std::string& annotationRegistration,
|
||||
const std::vector<std::string>& methodRegistrations,
|
||||
const std::vector<std::string>& signalRegistrations,
|
||||
const std::vector<std::string>& propertyRegistrations) const;
|
||||
|
||||
/**
|
||||
* Get annotations listed for a given node
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file BaseGenerator.cpp
|
||||
*
|
||||
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user