forked from Kistler-Group/sdbus-cpp
Compare commits
267 Commits
v0.3.1
...
fix/add-at
Author | SHA1 | Date | |
---|---|---|---|
91258ffbb8 | |||
5caea3b72b | |||
b7a9c63ff0 | |||
7f437a6e06 | |||
f492472e9f | |||
f673e57a47 | |||
bca8e81037 | |||
33ff69ecd2 | |||
b8eb0e8ceb | |||
23fdd0ce8f | |||
bb0f3f0242 | |||
0b8f2d9752 | |||
9b8a15339e | |||
ef4d9bcba2 | |||
41d33117cc | |||
442670ec18 | |||
b01db13ff7 | |||
dc0f487751 | |||
55310659e8 | |||
65782bbf43 | |||
125cb1616c | |||
f025b92d76 | |||
33aa5768a5 | |||
0703324015 | |||
f05f63cd48 | |||
de9cd46d8a | |||
ca05b1541f | |||
35176c4988 | |||
a5ecbbfcec | |||
4e908612ed | |||
1d930f324e | |||
a341754533 | |||
9cb8b89a01 | |||
c422de641a | |||
d77bb6b869 | |||
8320429ef7 | |||
a72e17b932 | |||
b4f5c0f46c | |||
6433b38ed1 | |||
a95fcf5693 | |||
022831b8c3 | |||
e16ffb1288 | |||
75ea127374 | |||
d74365c535 | |||
a5e94f07bf | |||
fa9569fdd9 | |||
118faa58f6 | |||
d65744b1fc | |||
6df67469ad | |||
b0a72cbe92 | |||
5ee4c61a1b | |||
d46cbba23c | |||
70778bfae0 | |||
5f271abc0c | |||
bbffcbf49e | |||
dc6d55a282 | |||
3f54b5e762 | |||
fe8cdce107 | |||
d47e9d1834 | |||
b9723850b8 | |||
e1008dd8cf | |||
fc9f770512 | |||
5e03e78451 | |||
3f74512f8e | |||
0090ca97ee | |||
a649a0225e | |||
cfb9956de6 | |||
fb008445b1 | |||
1e2a09cccf | |||
a9f2043daa | |||
96b2dfff69 | |||
d6fdacafbe | |||
b190646aa5 | |||
1c56f069dd | |||
6e8e5aadb6 | |||
3b735bf1aa | |||
2f7b35c5a8 | |||
d5867e1197 | |||
250aa2bbe3 | |||
e63357b222 | |||
2c6be0307f | |||
138a437b22 | |||
cc8d88cc64 | |||
bded067496 | |||
c1c4512f9f | |||
175c43ec53 | |||
c137dfa213 | |||
a0dadcc6fe | |||
0d010440c5 | |||
ae8849e545 | |||
fb35a9a196 | |||
6cbd49ddaa | |||
fb0a70a831 | |||
9af20af001 | |||
63bbb07ef0 | |||
00d0837d98 | |||
e91bedd4cb | |||
dc66efbbcb | |||
a23aadbe5e | |||
ae57c6760b | |||
21820f7529 | |||
3a4f343fb9 | |||
dee6adce02 | |||
3e68fee4cd | |||
8dfd29b0f0 | |||
db71707be4 | |||
c9583f2887 | |||
975f1bf07f | |||
d591b69f92 | |||
49586001d6 | |||
aa8e9123de | |||
eade6a0e44 | |||
10977c6137 | |||
1e455b8ef3 | |||
75709e31f1 | |||
245db893b8 | |||
477c5dd714 | |||
b25534013f | |||
68b5eac9e9 | |||
4310a3bd17 | |||
f41d9bc395 | |||
5121d46eed | |||
121ed1a975 | |||
cc495811f9 | |||
839bc13625 | |||
5fe0f503ca | |||
d50a15b2a2 | |||
3a76e9c120 | |||
304b69dd8b | |||
099bc857ad | |||
c139110112 | |||
e7155c5506 | |||
0f7de608ac | |||
c6d4d2710f | |||
0440dcb15b | |||
e30ce194ab | |||
8dea11bac6 | |||
750dab3927 | |||
bf35157a4a | |||
a09362f79a | |||
c264f83e83 | |||
71adb5cf30 | |||
00177a7e4c | |||
9826d28f51 | |||
ab34b0ae50 | |||
ff944c9e95 | |||
7049d00a78 | |||
236c10ff56 | |||
dcad208ffe | |||
57c840637c | |||
efe799ef3f | |||
5c0a8d5ab4 | |||
65b3e7ba00 | |||
b2b0bddf02 | |||
11f0edf7b8 | |||
946cc8d0cd | |||
07625a435b | |||
fbb5242729 | |||
dbeaf87208 | |||
45176c9eb7 | |||
38b51bddc6 | |||
01e2a7a570 | |||
38552483ca | |||
91fa35140b | |||
4b0c23204d | |||
c13ee60b7e | |||
6ee66dfc47 | |||
ed5c7a1fd5 | |||
6629d31733 | |||
0014bb0b6e | |||
ad3749f2c2 | |||
0045e8fcdc | |||
e12a9c3914 | |||
19d852e1b9 | |||
8da3e312bc | |||
81b5a67f35 | |||
b87b0c9dd9 | |||
76414ff09e | |||
4998895f41 | |||
7763c66513 | |||
7c3f91310f | |||
2256adf707 | |||
2c218ab3ba | |||
0cffed4574 | |||
36269897fd | |||
1b1b9ae8ae | |||
1b02c604d8 | |||
981206fa8c | |||
b0dfea041d | |||
824aaa711e | |||
882262bc2f | |||
4ede37d6a3 | |||
58647c6e7d | |||
a23d88a628 | |||
29c438b3bb | |||
8b7b9197eb | |||
2d27f99b32 | |||
9bde7c7b68 | |||
62a546c9d3 | |||
5b99658f36 | |||
d3749741d1 | |||
1d44d8b37f | |||
e3a74a3ff2 | |||
99160156fe | |||
ee30375cfc | |||
06ca6539f3 | |||
ed0745c83a | |||
93b6e5237a | |||
e7c78460cf | |||
f5da0dabcb | |||
c9ef1849cd | |||
d154022205 | |||
94fd3c88d8 | |||
a919058d13 | |||
08945acbc4 | |||
5673a9bcf2 | |||
b46fb170ea | |||
878ce6fa5c | |||
461ac241c8 | |||
a5692c08ea | |||
4fd2479b06 | |||
581e849534 | |||
63637b639f | |||
fc60700e1b | |||
a04ab9f445 | |||
1c4abab3e4 | |||
d489eee9c0 | |||
cbf2218301 | |||
6f79c5bf14 | |||
7c968e78cb | |||
fd7be39dd4 | |||
26c6ea8730 | |||
663df31398 | |||
004f158817 | |||
ab407aa8c8 | |||
bb2bf5811b | |||
41a10d644f | |||
b9ce1ca3ce | |||
850e211dca | |||
2b83d7ca2d | |||
8c76e3ef8b | |||
a6d0b62ff5 | |||
757cc44381 | |||
6dbcbbfa98 | |||
b813680192 | |||
84b15776a3 | |||
8c5c774727 | |||
cd1efd66a5 | |||
fad81e7659 | |||
1dafd6262c | |||
0b27f222c4 | |||
58895d2730 | |||
d957948274 | |||
47fad7dd63 | |||
7378cea833 | |||
2526546432 | |||
9c0e98c580 | |||
2c78e08d19 | |||
97372155a6 | |||
1def4e247a | |||
d764afec93 | |||
eb58d2fa52 | |||
a6bb8c070e | |||
108c33faac | |||
ec06462713 | |||
234145cade | |||
e971f95bad |
82
.github/workflows/ci.yml
vendored
Normal file
82
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-18.04, ubuntu-20.04]
|
||||
compiler: [g++, clang]
|
||||
build: [shared-libsystemd]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
compiler: g++
|
||||
build: embedded-static-libsystemd
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: install-libsystemd-toolchain
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y meson ninja-build libcap-dev libmount-dev m4 gperf
|
||||
- name: install-libsystemd-dev
|
||||
if: matrix.build == 'shared-libsystemd'
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y libsystemd-dev
|
||||
- name: install-clang
|
||||
if: matrix.compiler == 'clang'
|
||||
run: |
|
||||
sudo apt-get install -y 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-debug
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-18.04'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
- name: configure-release
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
|
||||
- name: configure-with-embedded-libsystemd
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 ..
|
||||
- name: make
|
||||
run: |
|
||||
cd build
|
||||
cmake --build . -j2
|
||||
- name: verify
|
||||
run: |
|
||||
cd build
|
||||
sudo cmake --build . --target install
|
||||
ctest
|
||||
- name: pack
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
|
||||
run: |
|
||||
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@v2
|
||||
with:
|
||||
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"
|
||||
path: |
|
||||
build/sdbus-c++*.deb
|
||||
build/sdbus-c++*.ddeb
|
||||
retention-days: 10
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -7,9 +7,9 @@ build/
|
||||
.deps/
|
||||
.dirstamp
|
||||
.libs/
|
||||
test/libsdbus-c++_unittests
|
||||
test/libsdbus-c++_integrationtests
|
||||
test/run-test-on-device.sh
|
||||
tests/libsdbus-c++_unittests
|
||||
tests/libsdbus-c++_integrationtests
|
||||
tests/run-test-on-device.sh
|
||||
|
||||
#eclipse
|
||||
.cproject
|
||||
|
35
.project
35
.project
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>sdbus-cpp</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.autotools.core.genmakebuilderV2</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
<nature>org.yocto.sdk.ide.YoctoSDKNature</nature>
|
||||
<nature>org.eclipse.cdt.autotools.core.autotoolsNatureV2</nature>
|
||||
<nature>org.yocto.sdk.ide.YoctoSDKAutotoolsNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
10
AUTHORS
10
AUTHORS
@ -1,5 +1,7 @@
|
||||
The library:
|
||||
Stanislav Angelovic (stanislav.angelovic[at]kistler.com)
|
||||
sdbus-c++ library:
|
||||
Stanislav Angelovic (https://github.com/sangelovic)
|
||||
|
||||
Stub generator:
|
||||
Lukas Durfina (lukas.durfina[at]kistler.com)
|
||||
Code generator:
|
||||
Lukas Durfina (https://github.com/lukasdurfina)
|
||||
|
||||
... and other contributors :)
|
||||
|
238
CMakeLists.txt
Normal file
238
CMakeLists.txt
Normal file
@ -0,0 +1,238 @@
|
||||
#-------------------------------
|
||||
# PROJECT INFORMATION
|
||||
#-------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
project(sdbus-c++ VERSION 1.1.0 LANGUAGES C CXX)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
#-------------------------------
|
||||
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES
|
||||
#-------------------------------
|
||||
|
||||
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(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++)")
|
||||
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)
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
#-------------------------------
|
||||
# SOURCE FILES CONFIGURATION
|
||||
#-------------------------------
|
||||
|
||||
set(SDBUSCPP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
set(SDBUSCPP_INCLUDE_SUBDIR sdbus-c++)
|
||||
set(SDBUSCPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include/${SDBUSCPP_INCLUDE_SUBDIR})
|
||||
|
||||
set(SDBUSCPP_CPP_SRCS
|
||||
${SDBUSCPP_SOURCE_DIR}/Connection.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Error.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Message.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Object.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Proxy.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Types.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/Flags.cpp
|
||||
${SDBUSCPP_SOURCE_DIR}/VTableUtils.c
|
||||
${SDBUSCPP_SOURCE_DIR}/SdBus.cpp)
|
||||
|
||||
set(SDBUSCPP_HDR_SRCS
|
||||
${SDBUSCPP_SOURCE_DIR}/Connection.h
|
||||
${SDBUSCPP_SOURCE_DIR}/IConnection.h
|
||||
${SDBUSCPP_SOURCE_DIR}/MessageUtils.h
|
||||
${SDBUSCPP_SOURCE_DIR}/Utils.h
|
||||
${SDBUSCPP_SOURCE_DIR}/Object.h
|
||||
${SDBUSCPP_SOURCE_DIR}/Proxy.h
|
||||
${SDBUSCPP_SOURCE_DIR}/ScopeGuard.h
|
||||
${SDBUSCPP_SOURCE_DIR}/VTableUtils.h
|
||||
${SDBUSCPP_SOURCE_DIR}/SdBus.h
|
||||
${SDBUSCPP_SOURCE_DIR}/ISdBus.h)
|
||||
|
||||
set(SDBUSCPP_PUBLIC_HDRS
|
||||
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.inl
|
||||
${SDBUSCPP_INCLUDE_DIR}/Error.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/IConnection.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/AdaptorInterfaces.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/ProxyInterfaces.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/StandardInterfaces.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/IObject.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/IProxy.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/Message.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/MethodResult.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/Types.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/TypeTraits.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/Flags.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/sdbus-c++.h)
|
||||
|
||||
set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HDRS})
|
||||
|
||||
#-------------------------------
|
||||
# GENERAL COMPILER CONFIGURATION
|
||||
#-------------------------------
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
#----------------------------------
|
||||
# LIBRARY BUILD INFORMATION
|
||||
#----------------------------------
|
||||
|
||||
set(SDBUSCPP_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(SDBUSCPP_VERSION "${PROJECT_VERSION}")
|
||||
|
||||
# We promote BUILD_SHARED_LIBS flags to (global) option only if we are the main project
|
||||
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
|
||||
endif()
|
||||
|
||||
# Having an object target allows unit tests to reuse already built sources without re-building
|
||||
add_library(sdbus-c++-objlib OBJECT ${SDBUSCPP_SRCS})
|
||||
target_compile_definitions(sdbus-c++-objlib PRIVATE BUILD_LIB=1 LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
|
||||
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
|
||||
if(DEFINED BUILD_SHARED_LIBS)
|
||||
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS})
|
||||
endif()
|
||||
if(BUILD_LIBSYSTEMD)
|
||||
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
|
||||
endif()
|
||||
target_link_libraries(sdbus-c++-objlib
|
||||
PUBLIC
|
||||
Systemd::Libsystemd
|
||||
Threads::Threads
|
||||
atomic)
|
||||
|
||||
add_library(sdbus-c++)
|
||||
target_include_directories(sdbus-c++ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
|
||||
set_target_properties(sdbus-c++
|
||||
PROPERTIES PUBLIC_HEADER "${SDBUSCPP_PUBLIC_HDRS}"
|
||||
VERSION "${SDBUSCPP_VERSION}"
|
||||
SOVERSION "${SDBUSCPP_VERSION_MAJOR}"
|
||||
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
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT dev)
|
||||
|
||||
#----------------------------------
|
||||
# TESTS
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_TESTS "Build and install tests (default OFF)" OFF)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
message(STATUS "Building with tests")
|
||||
enable_testing()
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# UTILS
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_CODE_GEN "Build and install interface stub code generator (default OFF)" OFF)
|
||||
|
||||
if(BUILD_CODE_GEN)
|
||||
message(STATUS "Building with code generator tool")
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools")
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# EXAMPLES
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_EXAMPLES "Build example programs (default OFF)" OFF)
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
message(STATUS "Building with examples")
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# DOCUMENTATION
|
||||
#----------------------------------
|
||||
|
||||
option(BUILD_DOC "Build documentation for sdbus-c++" ON)
|
||||
|
||||
if(BUILD_DOC)
|
||||
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()
|
||||
|
||||
#----------------------------------
|
||||
# CMAKE CONFIG & PACKAGE CONFIG
|
||||
#----------------------------------
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
#----------------------------------
|
||||
set(CPACK_PACKAGE_VENDOR "Kistler")
|
||||
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")
|
||||
|
||||
# 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_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)")
|
||||
|
||||
include(CPack)
|
22
COPYING-LGPL-Exception
Normal file
22
COPYING-LGPL-Exception
Normal file
@ -0,0 +1,22 @@
|
||||
sdbus-c++ LGPL Exception version 1.0
|
||||
|
||||
As an additional permission to the GNU Lesser General Public License version
|
||||
2.1, the object code form of a "work that uses the Library" may incorporate
|
||||
material from a header file that is part of the Library. You may distribute
|
||||
such object code under terms of your choice, provided that:
|
||||
(i) the header files of the Library have not been modified; and
|
||||
(ii) the incorporated material is limited to numerical parameters, data
|
||||
structure layouts, accessors, macros, inline functions and
|
||||
templates; and
|
||||
(iii) you comply with the terms of Section 6 of the GNU Lesser General
|
||||
Public License version 2.1.
|
||||
|
||||
Moreover, you may apply this exception to a modified version of the Library,
|
||||
provided that such modification does not involve copying material from the
|
||||
Library into the modified Library's header files unless such material is
|
||||
limited to (i) numerical parameters; (ii) data structure layouts;
|
||||
(iii) accessors; and (iv) small macros, templates and inline functions of
|
||||
five lines or less in length.
|
||||
|
||||
Furthermore, you are not required to apply this additional permission to a
|
||||
modified version of the Library.
|
231
ChangeLog
231
ChangeLog
@ -1,28 +1,203 @@
|
||||
v0.2.3
|
||||
- Initially published version
|
||||
|
||||
v0.2.4
|
||||
- Fixed closing of file descriptor of event loop's semaphore on exec
|
||||
- Fixed interrupt handling when polling
|
||||
- Improved tutorial
|
||||
- Fixed issue with googlemock
|
||||
- Added object proxy factory overload that takes unique_ptr to a connection
|
||||
- Workaround: Clang compilation error when compiling sdbus::Struct (seems like an issue of Clang)
|
||||
|
||||
v0.2.5
|
||||
- Real fix for issue with sdbus::Struct inherited constructors
|
||||
- Little code refactorings and improvements
|
||||
|
||||
v0.2.6
|
||||
- Fixed memory leak in Message copy operations
|
||||
|
||||
v0.3.0
|
||||
- Introduced support for asynchronous server-side methods
|
||||
- Refactored the concept of Message into distinctive concrete message types
|
||||
- As this release comes with breaking API changes:
|
||||
* if you're using lower-level API, please rename 'Message' to whatever concrete message type (MethodCall, MethodReply, Signal) is needed there,
|
||||
* if you're using higher-level API, please re-compile and re-link your application against sdbus-c++,
|
||||
* if you're using generated stub headers, please re-compile and re-link your application against sdbus-c++.
|
||||
|
||||
v0.3.1
|
||||
- Fixed hogging the CPU by server with async methods (issue #15)
|
||||
v0.2.3
|
||||
- Initially published version
|
||||
|
||||
v0.2.4
|
||||
- Fixed closing of file descriptor of event loop's semaphore on exec
|
||||
- Fixed interrupt handling when polling
|
||||
- Improved tutorial
|
||||
- Fixed issue with googlemock
|
||||
- Added object proxy factory overload that takes unique_ptr to a connection
|
||||
- Workaround: Clang compilation error when compiling sdbus::Struct (seems like an issue of Clang)
|
||||
|
||||
v0.2.5
|
||||
- Real fix for issue with sdbus::Struct inherited constructors
|
||||
- Little code refactorings and improvements
|
||||
|
||||
v0.2.6
|
||||
- Fixed memory leak in Message copy operations
|
||||
|
||||
v0.3.0
|
||||
- Introduced support for asynchronous server-side methods
|
||||
- Refactored the concept of Message into distinctive concrete message types
|
||||
- As this release comes with breaking API changes:
|
||||
* if you're using lower-level API, please rename 'Message' to whatever concrete message type (MethodCall, MethodReply, Signal) is needed there,
|
||||
* if you're using higher-level API, please re-compile and re-link your application against sdbus-c++,
|
||||
* if you're using generated stub headers, please re-compile and re-link your application against sdbus-c++.
|
||||
|
||||
v0.3.1
|
||||
- Fixed hogging the CPU by server with async methods (issue #15)
|
||||
|
||||
v0.3.2
|
||||
- Switched from autotools to CMake build system
|
||||
|
||||
v0.3.3
|
||||
- Minor fixes in tutorial examples
|
||||
- Add comment on threading traits of Variant and its const methods
|
||||
- Fix broken invariant of const Variant::peekValueType() method
|
||||
|
||||
v0.4.0
|
||||
- Introduce support and implementation of common D-Bus annotations
|
||||
- Remove hard-coded warning-related compiler options from the build system
|
||||
|
||||
v0.4.1
|
||||
- Change constexpr member into a getter method to be compatible across odr-usage rules of various compiler versions
|
||||
|
||||
v0.4.2
|
||||
- Improve documentation
|
||||
- Minor code improvements
|
||||
- Introduce sdbus-c++ performance tests with measurements
|
||||
|
||||
v0.4.3
|
||||
- Add another abstraction layer for sd-bus to improve isolation for unit testing
|
||||
- Minor code improvements for consistency and clarity
|
||||
- Not-existing interface name and object path fixes in integration tests
|
||||
|
||||
v0.5.0
|
||||
- Redesign of the Connection model for scalability, thread-safety, simplicity and clarity
|
||||
- [[Breaking ABI change]] Introduce support for asynchronous invocation of D-Bus methods on client-side
|
||||
- Revise and extend sdbus-c++ tutorial
|
||||
- Introduce stress tests that involve a D-Bus server and client interacting in dense communication in both sync and async ways
|
||||
- Compilation error fixes for older gcc versions
|
||||
|
||||
v0.5.1
|
||||
- Cleanups of CMakeLists.txt
|
||||
- Rename sdbus-c++ test executables for consistency
|
||||
- Rename sdbus-c++ stub code generator executable
|
||||
- Introduce generation of doxygen documentation
|
||||
- Improve documentation
|
||||
|
||||
v0.5.2
|
||||
- Simplify and unify basic API level callbacks for both sync and async methods
|
||||
|
||||
v0.5.3
|
||||
- Extend stress tests with dynamic object and proxy creation and destruction from multiple threads
|
||||
- [[Breaking ABI change]] Add getConnection() method to IObject
|
||||
- A few minor fixes
|
||||
|
||||
v0.6.0
|
||||
- This release comes with a number of API and ABI breaking changes (that hopefully lead to a more mature and stable API for near future):
|
||||
- Some constructs have been renamed (see below for details), you'll have to adapt your sources whether you're using the basic or the convenience sdbus-c++ API.
|
||||
- You'll also have to re-generate adaptor/proxy stubs with the new stub code generator if you're using them.
|
||||
- And you'll have to take care of manual registeration/deregistration in the constructors/destructors of your final adaptor and proxy classes. See updated using sdbus-c++ tutorial.
|
||||
- Cleaning up some names (of class, methods, files):
|
||||
* ConvenienceClasses.h/.inl/.cpp -> ConvenienceApiClasses.h/.inl/.cpp
|
||||
* IObjectProxy class -> IProxy
|
||||
* Interfaces class -> AdaptorInterfaces
|
||||
* Interfaces.h -> split into AdaptorInterfaces.h and ProxyInterfaces.h
|
||||
* createObjectProxy() method -> createProxy()
|
||||
- Add new unregister() virtual function to IObject and IProxy to allow for safe construction and destructions of D-Bus ojects and proxies hooked to live D-Bus connections.
|
||||
- Extend stress tests to allow testing safe initialization/deinitialization of objects and proxies
|
||||
- Fix gcc warnings
|
||||
- Use release v1.8.1 of googletest for tests
|
||||
|
||||
v0.7.0
|
||||
- [[Breaking ABI change]] Added full support for ObjectManager, Properties and other standard D-Bus interfaces (Reordering virtual functions in interface classes)
|
||||
- sdbus-c++ now can automatically download, build and incorporate libsystemd static library, which makes things pretty easy in non-systemd environments
|
||||
- Minor changes in generated proxy/adaptor code:
|
||||
* renamed interfaceName to INTERFACE_NAME to avoid potential naming clashes
|
||||
* signal emitting methods now begin with "emit" prefix and capitalized first letter of signal name
|
||||
- sdbus-c++ file organization has been adjusted to comply to de-facto OSS project standards
|
||||
- Build system improvements -- moving towards more modern CMake
|
||||
- Using googletest from master branch instead of v1.8.1 which has THREADS_PTHREAD_ARG issue when cross-compiling
|
||||
- Documentation improvements
|
||||
- Minor changes (renamings) in the code generated by sdbus-c++-xml2cpp generator
|
||||
- Other minor fixes and internal design improvements
|
||||
|
||||
v0.7.1
|
||||
- Fixed waiting in integration tests
|
||||
- Added object manager automatically in ObjectManager_adaptor constructor
|
||||
- Introduced support for unix fd type
|
||||
- Redesigned Message class hierarchy to be more idiomatic, clean and intent-revealing
|
||||
- Resolved a few clang-tidy warnings and suggestions
|
||||
- Extended the tutorial with info on standard D-Bus interfaces
|
||||
- Added protected non-virtual destructor in generated *_proxy/*_adaptor classes
|
||||
|
||||
v0.7.2
|
||||
- Rewrite UnixFd implementation from plain UnixFd struct to full-ownership-semantics UnixFd class
|
||||
|
||||
v0.7.3
|
||||
- Add ability to integrate with external event loops
|
||||
- Add getSenderName() method to Message
|
||||
- Skip GetMachineId integration test case when /etc/machine-id is not available
|
||||
|
||||
v0.7.4
|
||||
- Add support for custom timeout of D-Bus method calls
|
||||
- Add support for opening a connection to a remote system bus using ssh
|
||||
- Internal refactoring: Use tag dispatching to construct various types of Connection
|
||||
|
||||
v0.7.5
|
||||
- [[Breaking ABI change]] No more hiding from C++17: Move API code containing C++17 uncaught_exceptions calls from within library to public API
|
||||
- Add a method to retrieve the unique name of a connection
|
||||
|
||||
v0.7.6
|
||||
- Fixes of clang-8 errors and warnings
|
||||
|
||||
v0.7.7
|
||||
- Fix race condition of polling on D-Bus fd from two threads (event loop thread and sync D-Bus call thread)
|
||||
- Little ordering fix in stress tests
|
||||
|
||||
v0.7.8
|
||||
- Switch from thread_local to global bus instance that is used to create Variant instances (thread_local caused issues with Variant in very special inter-thread situations)
|
||||
|
||||
v0.8.0
|
||||
- [[Breaking ABI change]] Implement support for input & output parameter names for D-Bus methods and signals, which are used in introspection
|
||||
- Explain better in tutorial the design and how to use connections in relation to objects and proxies
|
||||
|
||||
v0.8.1
|
||||
- Switch to full C++17 support
|
||||
- Switch to more modern CMake (>=3.12)
|
||||
- Provide better names to event loop-related IConnection methods, keep old ones marked as deprecated for backwards compatibility
|
||||
|
||||
v0.8.2
|
||||
- Introduce support for cancellable async calls
|
||||
- Add getObjectPath() for proxy and object classes
|
||||
- Sanitize names of namespaces/methods/signals/properties/arguments in sdbus-c++-xml2cpp
|
||||
- Fix delivery of signals to multiple proxies subscribed to them
|
||||
- Fix file existence condition in sdbus-c++-xml2cpp
|
||||
- Fix CallData race condition in Proxy::callMethod
|
||||
- Fix integration tests for libsystemd older than 242
|
||||
- Fix installation of public sd-bus headers in internal libsystemd build
|
||||
- Fix integration test cases failing in specific situations
|
||||
- Fix build with clang 9.0.1 and libcxx
|
||||
- Fix potential data race in Proxy's condition variable
|
||||
|
||||
v0.8.3
|
||||
- Fix build with gcc 8.3
|
||||
- Address a few inconsistencies and make code more idiomatic
|
||||
- Clean up integration tests
|
||||
- Remove non-virtual-dtor warnings by making classes final
|
||||
- Update CMake configuration flag names
|
||||
- Fix unused variable warning for release builds
|
||||
- Introduce CI workflow based on GitHub Actions
|
||||
|
||||
v0.9.0
|
||||
- Provide CMake config and PkgConfig files for tools
|
||||
- Provide access to D-Bus message in high-level API
|
||||
- Add API to set signal destination
|
||||
- Add IProxy::getConnection() method
|
||||
- Add README and sdbus-c++ tutorial as additional pages in doxydocs
|
||||
- Enable default construction of PendingAsyncCall
|
||||
- Add API to get message path and message destination
|
||||
- Avoid propagating msg unpack exceptions to the event loop
|
||||
- Fix issue #145: signals are not filtered by sender
|
||||
- Fix race condition in Proxy and Object destructor
|
||||
- Fix seg fault in Message::peekType()
|
||||
- Add information to documentation about conan recipe
|
||||
- Add cpack to build debian packages, split the packages by components
|
||||
- Catch sdbus-c++ exceptions flying from Proxy callbacks to libsystemd
|
||||
- Add createDefaultBusConnection() method
|
||||
- Make resetting loop thread ID exception-safe
|
||||
- Support Error parameter in signal handlers
|
||||
- Add specific sections for tips and notes in the tutorial
|
||||
- A few additional documentation and test updates and improvements
|
||||
|
||||
v1.0.0
|
||||
- [[Breaking API change]] Fixed the API to send org.freedesktop.DBus.ObjectManager.InterfacesAdded and org.freedesktop.DBus.ObjectManager.InterfacesRemoved signals via the generated stubs layer.
|
||||
- StandardInterfaces.h: Split ObjectManager_adaptor and ManagedObject_adaptor.
|
||||
- New examples directory. First example covers the object manager. Further examples might follow.
|
||||
|
||||
v1.1.0
|
||||
- Fix timeout handling for asynchronous method calls
|
||||
- Add support for unregistering signal handler
|
||||
- Add support for chrono literals in sdbus-c++-xml2cpp generator
|
||||
- Additional little fixes and improvements in code, build system, and documentation
|
||||
|
4
INSTALL
4
INSTALL
@ -1,5 +1,7 @@
|
||||
Building:
|
||||
$ ./autogen.sh ${CONFIGURE_FLAGS}
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake .. -DCMAKE_BUILD_TYPE=Release ${OTHER_CONFIG_FLAGS}
|
||||
$ make
|
||||
|
||||
Installing:
|
||||
|
22
Makefile.am
22
Makefile.am
@ -1,22 +0,0 @@
|
||||
EXTRA_DIST = autogen.sh
|
||||
SUBDIRS = include src test
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS=
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = sdbus-c++.pc
|
||||
|
||||
CLEANFILES = *~ *.lo *.la
|
||||
|
||||
MOSTLYCLEANFILES = *.o
|
||||
|
||||
DISTCLEANFILES = \
|
||||
*libtool* \
|
||||
aclocal.m4 \
|
||||
compile config.* configure \
|
||||
depcomp install-sh \
|
||||
ltmain.sh \
|
||||
Makefile Makefile.in \
|
||||
missing \
|
||||
stamp-h1 \
|
||||
sdbus-c++.pc
|
87
README.md
87
README.md
@ -1,37 +1,100 @@
|
||||
sdbus-c++
|
||||
=========
|
||||
|
||||
sdbus-c++ is a C++ API library for D-Bus IPC, based on sd-bus implementation.
|
||||

|
||||

|
||||

|
||||
|
||||
sdbus-c++ is a high-level C++ D-Bus library for Linux designed to provide expressive, easy-to-use API in modern C++. It adds another layer of abstraction on top of sd-bus, a nice, fresh C D-Bus implementation by systemd.
|
||||
|
||||
sdbus-c++ has been written primarily as a replacement of dbus-c++, which currently suffers from a number of (unresolved) bugs, concurrency issues and inherent design complexities and limitations. sdbus-c++ has learned from dbus-c++ and has chosen a different path, a path of simple yet powerful design that is intuitive and friendly to the user and inherently free of those bugs.
|
||||
|
||||
Even though sdbus-c++ uses sd-bus library, it is not necessarily constrained to systemd and can perfectly be used in non-systemd environments as well.
|
||||
|
||||
Building and installing the library
|
||||
-----------------------------------
|
||||
|
||||
The library is built using CMake:
|
||||
|
||||
```bash
|
||||
$ ./autogen.sh ${CONFIGURE_FLAGS}
|
||||
$ make
|
||||
$ sudo make install
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake .. -DCMAKE_BUILD_TYPE=Release ${OTHER_CONFIG_FLAGS}
|
||||
$ cmake --build .
|
||||
$ sudo cmake --build . --target install
|
||||
```
|
||||
|
||||
Use `--disable-tests` flag when configuring to disable building unit and integration tests for the library.
|
||||
### CMake configuration flags for sdbus-c++
|
||||
|
||||
* `BUILD_CODE_GEN` [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_DOC` [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:
|
||||
|
||||
* `BUILD_DOXYGEN_DOC` [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_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:
|
||||
|
||||
* `ENABLE_PERF_TESTS` [boolean]
|
||||
|
||||
Option for building sdbus-c++ performance tests. Default value: `OFF`.
|
||||
|
||||
* `ENABLE_STRESS_TESTS` [boolean]
|
||||
|
||||
Option for building sdbus-c++ stress tests. Default value: `OFF`.
|
||||
|
||||
* `TESTS_INSTALL_PATH` [string]
|
||||
|
||||
Path where the test binaries shall get installed. Default value: `/opt/test/bin`.
|
||||
|
||||
* `BUILD_LIBSYSTEMD` [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:
|
||||
|
||||
* `LIBSYSTEMD_VERSION` [string]
|
||||
|
||||
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`.
|
||||
|
||||
* `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 :)
|
||||
|
||||
* `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`
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* `C++17` - the library uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature.
|
||||
* `libsystemd` - systemd library containing sd-bus implementation. Systemd v236 at least is needed for sdbus-c++ to compile.
|
||||
* `googletest` - google unit testing framework, only necessary when building tests
|
||||
* `C++17` - the library uses C++17 features.
|
||||
* `libsystemd` - systemd library containing sd-bus implementation. This library is part of systemd. Systemd at least v236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
|
||||
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
|
||||
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
|
||||
* `expat` - necessary when building xml2cpp code generator (`BUILD_CODE_GEN` option is ON).
|
||||
|
||||
Licensing
|
||||
---------
|
||||
|
||||
The library is distributed under LGPLv2.1 license.
|
||||
The library is distributed under LGPLv2.1 license, with a specific exception for macro/template/inline code in library header files.
|
||||
|
||||
References/documentation
|
||||
------------------------
|
||||
|
||||
* [D-Bus Specification](https://dbus.freedesktop.org/doc/dbus-specification.html)
|
||||
* [Using sdbus-c++](docs/using-sdbus-c++.md) - *the* main, comprehensive tutorial on sdbus-c++
|
||||
* [Systemd and dbus configuration](docs/systemd-dbus-config.md)
|
||||
* [D-Bus Specification](https://dbus.freedesktop.org/docs/dbus-specification.html)
|
||||
* [sd-bus Overview](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html)
|
||||
* [Tutorial: Using sdbus-c++](doc/using-sdbus-c++.md)
|
||||
|
||||
Contributing
|
||||
------------
|
||||
@ -41,4 +104,4 @@ Contributions that increase the library quality, functionality, or fix issues ar
|
||||
Contact
|
||||
-------
|
||||
|
||||
stanislav.angelovic[at]kistler.com
|
||||
https://github.com/Kistler-Group/sdbus-cpp
|
||||
|
10
autogen.sh
10
autogen.sh
@ -1,10 +0,0 @@
|
||||
#! /bin/sh
|
||||
[ -e config.cache ] && rm -f config.cache
|
||||
|
||||
libtoolize --automake
|
||||
aclocal ${OECORE_ACLOCAL_OPTS}
|
||||
autoconf
|
||||
autoheader
|
||||
automake -a
|
||||
./configure $@
|
||||
exit
|
60
cmake/LibsystemdExternalProject.cmake
Normal file
60
cmake/LibsystemdExternalProject.cmake
Normal file
@ -0,0 +1,60 @@
|
||||
find_program(MESON meson)
|
||||
find_program(NINJA ninja)
|
||||
find_program(GPERF gperf)
|
||||
|
||||
if((NOT MESON) OR (NOT NINJA))
|
||||
message(FATAL_ERROR "Meson and Ninja are required to build libsystemd")
|
||||
endif()
|
||||
|
||||
find_library(GLIBC_RT_LIBRARY rt)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(MOUNT mount)
|
||||
pkg_check_modules(CAP REQUIRED libcap)
|
||||
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++")
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(LIBSYSTEMD_BUILD_TYPE "plain")
|
||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(LIBSYSTEMD_BUILD_TYPE "debug")
|
||||
else()
|
||||
set(LIBSYSTEMD_BUILD_TYPE "release")
|
||||
endif()
|
||||
|
||||
if(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")
|
||||
set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h)
|
||||
endif()
|
||||
|
||||
message(STATUS "Building with embedded libsystemd v${LIBSYSTEMD_VERSION}")
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(LibsystemdBuildProject
|
||||
PREFIX libsystemd-v${LIBSYSTEMD_VERSION}
|
||||
GIT_REPOSITORY https://github.com/systemd/systemd-stable.git
|
||||
GIT_TAG v${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} -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR>
|
||||
BUILD_COMMAND ${BUILD_VERSION_H}
|
||||
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
|
||||
BUILD_ALWAYS 0
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory <SOURCE_DIR>/src/systemd <INSTALL_DIR>/include/systemd
|
||||
LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1
|
||||
BUILD_BYPRODUCTS <BINARY_DIR>/libsystemd.a)
|
||||
|
||||
ExternalProject_Get_property(LibsystemdBuildProject SOURCE_DIR)
|
||||
ExternalProject_Get_property(LibsystemdBuildProject BINARY_DIR)
|
||||
ExternalProject_Get_property(LibsystemdBuildProject INSTALL_DIR)
|
||||
|
||||
add_library(Systemd::Libsystemd STATIC IMPORTED)
|
||||
set_target_properties(Systemd::Libsystemd PROPERTIES IMPORTED_LOCATION ${BINARY_DIR}/libsystemd.a)
|
||||
file(MAKE_DIRECTORY ${INSTALL_DIR}/include/systemd) # Trick for CMake to stop complaining about non-existent ${INSTALL_DIR}/include directory
|
||||
target_include_directories(Systemd::Libsystemd INTERFACE ${INSTALL_DIR}/include)
|
||||
target_link_libraries(Systemd::Libsystemd INTERFACE ${CAP_LIBRARIES} ${GLIBC_RT_LIBRARY} ${MOUNT_LIBRARIES})
|
4
cmake/sdbus-c++-config.cmake.in
Normal file
4
cmake/sdbus-c++-config.cmake.in
Normal file
@ -0,0 +1,4 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
|
||||
check_required_components("@PROJECT_NAME@")
|
62
configure.ac
62
configure.ac
@ -1,62 +0,0 @@
|
||||
AC_PREREQ(2.61)
|
||||
|
||||
# package version number (not shared library version)
|
||||
# odd micro numbers indicate in-progress development
|
||||
# even micro numbers indicate released versions
|
||||
m4_define(sdbus_cpp_version_major, 0)
|
||||
m4_define(sdbus_cpp_version_minor, 3)
|
||||
m4_define(sdbus_cpp_version_micro, 1)
|
||||
|
||||
m4_define([sdbus_cpp_version],
|
||||
[sdbus_cpp_version_major.sdbus_cpp_version_minor.sdbus_cpp_version_micro])
|
||||
m4_define([sdbus_cpp_api_version],
|
||||
[sdbus_cpp_version_major.sdbus_cpp_version_minor])
|
||||
|
||||
AC_INIT(libsdbus-c++, sdbus_cpp_version)
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_LIBTOOL
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
|
||||
# enable pkg-config
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
PKG_CHECK_MODULES(SYSTEMD, [libsystemd >= 236],,
|
||||
AC_MSG_ERROR([You need the libsystemd library (version 236 or newer)]
|
||||
[https://www.freedesktop.org/wiki/Software/systemd/])
|
||||
)
|
||||
|
||||
# Checks for library functions.
|
||||
#AC_CHECK_FUNCS([memset])
|
||||
|
||||
AC_SUBST(libsdbus_cpp_CFLAGS)
|
||||
AC_SUBST(libsdbus_cpp_LIBS)
|
||||
|
||||
AC_ARG_ENABLE([tests],
|
||||
[AS_HELP_STRING([--enable-tests],
|
||||
[build and install tests @<:@default=yes@:>@])],
|
||||
[],
|
||||
[enable_tests=yes])
|
||||
AM_CONDITIONAL([ENABLE_TESTS], [test x$enable_tests = xyes])
|
||||
|
||||
#icondir=${datadir}/icons/hicolor/32x32/apps
|
||||
#AC_SUBST(icondir)
|
||||
|
||||
AC_OUTPUT([
|
||||
Makefile
|
||||
include/Makefile
|
||||
src/Makefile
|
||||
test/Makefile
|
||||
sdbus-c++.pc
|
||||
])
|
||||
|
||||
echo ""
|
||||
echo " sdbus-cpp $VERSION"
|
||||
echo " ====================="
|
||||
echo ""
|
||||
echo " To build the project, run \"make\""
|
||||
echo ""
|
Binary file not shown.
Before Width: | Height: | Size: 32 KiB |
@ -1,847 +0,0 @@
|
||||
Using sdbus-c++ library
|
||||
=======================
|
||||
|
||||
**Table of contents**
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
2. [Integrating sdbus-c++ into your project](#integrating-sdbus-c-into-your-project)
|
||||
3. [Header files and namespaces](#header-files-and-namespaces)
|
||||
4. [Error signalling and propagation](#error-signalling-and-propagation)
|
||||
5. [Multiple layers of sdbus-c++ API](#multiple-layers-of-sdbus-c-api)
|
||||
6. [An example: Number concatenator](#an-example-number-concatenator)
|
||||
7. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
|
||||
8. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
|
||||
9. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs)
|
||||
10. [Asynchronous server-side methods](#asynchronous-server-side-methods)
|
||||
11. [Using D-Bus properties](#using-d-bus-properties)
|
||||
12. [Conclusion](#conclusion)
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
sdbus-c++ is a C++ wrapper library built on top of [sd-bus](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html), a lightweight D-Bus client
|
||||
library implemented within systemd project. It provides D-Bus functionality on a higher level of abstraction, trying to employ C++ type system
|
||||
to shift as much work as possible from the developer to the compiler.
|
||||
|
||||
sdbus-c++ does not cover the entire sd-bus API, but provides tools for implementing the most common functionality - RPC
|
||||
method calls, signals and properties. There is room for additions and improvements, as needed and when needed.
|
||||
|
||||
Integrating sdbus-c++ into your project
|
||||
---------------------------------------
|
||||
|
||||
The library build system is based on Autotools. The library supports `pkg-config`, so integrating it into your autotools project
|
||||
is a two step process.
|
||||
|
||||
1. Add `PKG_CHECK_MODULES` macro into your `configure.ac`:
|
||||
```bash
|
||||
PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.1],,
|
||||
AC_MSG_ERROR([You need the sdbus-c++ library (version 0.1 or better)]
|
||||
[http://www.kistler.com/])
|
||||
)
|
||||
```
|
||||
|
||||
2. Update `*_CFLAGS` and `*_LDFLAGS` in Makefiles of modules that use *sdbus-c++*, for example like this:
|
||||
```bash
|
||||
AM_CXXFLAGS = -std=c++17 -pedantic -W -Wall @SDBUSCPP_CFLAGS@ ...
|
||||
AM_LDFLAGS = @SDBUSCPP_LIBS@ ...
|
||||
```
|
||||
|
||||
Note: sdbus-c++ library depends on C++17, since it uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature. To use the library, make sure you have a C++ standard library that supports the feature. The feature is supported by e.g. gcc >= 6, and clang >= 3.7.
|
||||
|
||||
Header files and namespaces
|
||||
---------------------------
|
||||
|
||||
All sdbus-c++ header files reside in the `sdbus-c++` subdirectory within the standard include directory. Users can either include
|
||||
individual header files, like so:
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
```
|
||||
|
||||
or just include the global header file that pulls in everything:
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
```
|
||||
|
||||
All public types and functions of sdbus-c++ reside in the `sdbus` namespace.
|
||||
|
||||
Error signalling and propagation
|
||||
--------------------------------
|
||||
|
||||
`sdbus::Error` exception is used to signal errors in sdbus-c++. There are two types of errors:
|
||||
* D-Bus related errors, like call timeouts, failed socket allocation, etc.
|
||||
* user errors, i.e. errors signalled and propagated from remote methods back to the caller.
|
||||
|
||||
The exception object carries the error name and error message with it.
|
||||
|
||||
sdbus-c++ design
|
||||
----------------
|
||||
|
||||
The following diagram illustrates the major entities in sdbus-c++.
|
||||
|
||||

|
||||
|
||||
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign unique service names to those connections. A processing loop can be run on the connection.
|
||||
|
||||
`IObject` represents the concept of an object that exposes its methods, signals and properties. Its responsibilities are:
|
||||
* registering (possibly multiple) interfaces and methods, signals, properties on those interfaces,
|
||||
* emitting signals.
|
||||
|
||||
`IObjectProxy` represents the concept of the proxy, which is a view of the `Object` from the client side. Its responsibilities are:
|
||||
* invoking remote methods of the corresponding object,
|
||||
* registering handlers for signals.
|
||||
|
||||
`Message` class represents a message, which is the fundamental DBus concept. There are three distinctive types of message that derive from the `Message` class:
|
||||
* `MethodCall` (with serialized parameters),
|
||||
* `MethodReply` (with serialized return values),
|
||||
* or a `Signal` (with serialized parameters).
|
||||
|
||||
Multiple layers of sdbus-c++ API
|
||||
-------------------------------
|
||||
|
||||
sdbus-c++ API comes in two layers:
|
||||
* [the basic layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer), which is a simple wrapper layer on top of sd-bus, using mechanisms that are native to C++ (e.g. serialization/deserialization of data from messages),
|
||||
* [the convenience layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer), building on top of the basic layer, which aims at alleviating users from unnecessary details and enables them to write shorter, safer, and more expressive code.
|
||||
|
||||
sdbus-c++ also ships with a stub generator tool that converts D-Bus IDL in XML format into stub code for the adaptor as well as proxy part. Hierarchically, these stubs provide yet another layer of convenience (the "stubs layer"), making it possible for D-Bus RPC calls to completely look like native C++ calls on a local object.
|
||||
|
||||
An example: Number concatenator
|
||||
-------------------------------
|
||||
|
||||
Let's have an object `/org/sdbuscpp/concatenator` that implements the `org.sdbuscpp.concatenator` interface. The interface exposes the following:
|
||||
* a `concatenate` method that takes a collection of integers and a separator string and returns a string that is the concatenation of all
|
||||
integers from the collection using given separator,
|
||||
* a `concatenated` signal that is emitted at the end of each successful concatenation.
|
||||
|
||||
In the following sections, we will elaborate on the ways of implementing such an object on both the server and the client side.
|
||||
|
||||
Implementing the Concatenator example using basic sdbus-c++ API layer
|
||||
---------------------------------------------------------------------
|
||||
|
||||
In the basic API layer, we already have abstractions for D-Bus connections, objects and object proxies, with which we can interact via
|
||||
interfaces. However, we still work with the concept of messages. To issue a method call for example, we have to go through several steps:
|
||||
we have to create a method call message first, serialize method arguments into the message, and send the message at last. We get the reply
|
||||
message (if applicable) in return, so we have to deserialize the return values from it manually.
|
||||
|
||||
Overloaded versions of C++ insertion/extraction operators are used for serialization/deserialization. That makes the client code much simpler.
|
||||
|
||||
### Server side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Yeah, global variable is ugly, but this is just an example and we want to access
|
||||
// the concatenator instance from within the concatenate method handler to be able
|
||||
// to emit signals.
|
||||
sdbus::IObject* g_concatenator{};
|
||||
|
||||
void concatenate(sdbus::MethodCall& call, sdbus::MethodReply& reply)
|
||||
{
|
||||
// Deserialize the collection of numbers from the message
|
||||
std::vector<int> numbers;
|
||||
call >> numbers;
|
||||
|
||||
// Deserialize separator from the message
|
||||
std::string separator;
|
||||
call >> separator;
|
||||
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Serialize resulting string to the reply
|
||||
reply << result;
|
||||
|
||||
// Emit 'concatenated' signal
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||
signal << result;
|
||||
g_concatenator->emitSignal(signal);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection to the system bus and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createSystemBusConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenator = sdbus::createObject(*connection, objectPath);
|
||||
|
||||
g_concatenator = concatenator.get();
|
||||
|
||||
// Register D-Bus methods and signals on the concatenator object, and exports the object.
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenator->registerMethod(interfaceName, "concatenate", "ais", "s", &concatenate);
|
||||
concatenator->registerSignal(interfaceName, "concatenated", "s");
|
||||
concatenator->finishRegistration();
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
}
|
||||
```
|
||||
|
||||
### Client side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
void onConcatenated(sdbus::Signal& signal)
|
||||
{
|
||||
std::string concatenatedString;
|
||||
signal >> concatenatedString;
|
||||
|
||||
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create proxy object for the concatenator object on the server side. Since we don't pass
|
||||
// the D-Bus connection object to the proxy constructor, the proxy will internally create
|
||||
// its own connection to the system bus.
|
||||
const char* destinationName = "org.sdbuscpp.concatenator";
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenatorProxy = sdbus::createObjectProxy(destinationName, objectPath);
|
||||
|
||||
// Let's subscribe for the 'concatenated' signals
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenatorProxy->registerSignalHandler(interfaceName, "concatenated", &onConcatenated);
|
||||
concatenatorProxy->finishRegistration();
|
||||
|
||||
std::vector<int> numbers = {1, 2, 3};
|
||||
std::string separator = ":";
|
||||
|
||||
// Invoke concatenate on given interface of the object
|
||||
{
|
||||
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
|
||||
method << numbers << separator;
|
||||
auto reply = concatenatorProxy->callMethod(method);
|
||||
std::string result;
|
||||
reply >> result;
|
||||
assert(result == "1:2:3");
|
||||
}
|
||||
|
||||
// Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
{
|
||||
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
|
||||
method << std::vector<int>() << separator;
|
||||
try
|
||||
{
|
||||
auto reply = concatenatorProxy->callMethod(method);
|
||||
assert(false);
|
||||
}
|
||||
catch(const sdbus::Error& e)
|
||||
{
|
||||
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The object proxy is created without explicitly providing a D-Bus connection as an argument in its factory function. In that case, the proxy
|
||||
will create its own connection to the *system* bus and listen to signals on it in a separate thread. That means the `onConcatenated` method is invoked always
|
||||
in the context of a thread different from the main thread.
|
||||
|
||||
Implementing the Concatenator example using convenience sdbus-c++ API layer
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
One of the major sdbus-c++ design goals is to make the sdbus-c++ API easy to use correctly, and hard to use incorrectly.
|
||||
|
||||
The convenience API layer abstracts the concept of underlying D-Bus messages away completely. It abstracts away D-Bus signatures. And it tries
|
||||
to provide an interface that uses small, focused functions, with one or zero parameters, to form a chained function statement that reads like
|
||||
a sentence to a human reading the code. To achieve that, sdbus-c++ utilizes the power of the C++ type system, so many issues are resolved at
|
||||
compile time, and the run-time performance cost compared to the basic layer is close to zero.
|
||||
|
||||
Thus, in the end of the day, the code written using the convenience API is:
|
||||
- more expressive,
|
||||
- closer to the abstraction level of the problem being solved,
|
||||
- shorter,
|
||||
- almost as fast (if not equally fast) as one written using the basic API layer.
|
||||
|
||||
Rather than *how*, the code written using this layer expresses *what* it does. Let's look at code samples to see if you agree :)
|
||||
|
||||
### Server side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Yeah, global variable is ugly, but this is just an example and we want to access
|
||||
// the concatenator instance from within the concatenate method handler to be able
|
||||
// to emit signals.
|
||||
sdbus::IObject* g_concatenator{};
|
||||
|
||||
std::string concatenate(const std::vector<int> numbers, const std::string& separator)
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Emit 'concatenated' signal
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
g_concatenator->emitSignal("concatenated").onInterface(interfaceName).withArguments(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection to the system bus and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createSystemBusConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenator = sdbus::createObject(*connection, objectPath);
|
||||
|
||||
g_concatenator = concatenator.get();
|
||||
|
||||
// Register D-Bus methods and signals on the concatenator object, and exports the object.
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenator->registerMethod("concatenate").onInterface(interfaceName).implementedAs(&concatenate);
|
||||
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>();
|
||||
concatenator->finishRegistration();
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
}
|
||||
```
|
||||
|
||||
### Client side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
void onConcatenated(const std::string& concatenatedString)
|
||||
{
|
||||
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create proxy object for the concatenator object on the server side
|
||||
const char* destinationName = "org.sdbuscpp.concatenator";
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenatorProxy = sdbus::createObjectProxy(destinationName, objectPath);
|
||||
|
||||
// Let's subscribe for the 'concatenated' signals
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenatorProxy->uponSignal("concatenated").onInterface(interfaceName).call([this](const std::string& str){ onConcatenated(str); });
|
||||
concatenatorProxy->finishRegistration();
|
||||
|
||||
std::vector<int> numbers = {1, 2, 3};
|
||||
std::string separator = ":";
|
||||
|
||||
// Invoke concatenate on given interface of the object
|
||||
{
|
||||
std::string concatenatedString;
|
||||
concatenatorProxy->callMethod("concatenate").onInterface(interfaceName).withArguments(numbers, separator).storeResultsTo(concatenatedString);
|
||||
assert(concatenatedString == "1:2:3");
|
||||
}
|
||||
|
||||
// Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
{
|
||||
try
|
||||
{
|
||||
concatenatorProxy->callMethod("concatenate").onInterface(interfaceName).withArguments(std::vector<int>(), separator);
|
||||
assert(false);
|
||||
}
|
||||
catch(const sdbus::Error& e)
|
||||
{
|
||||
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Several lines of code have shrunk into one-liners when registering/calling methods or signals. D-Bus signatures and the serialization/deserialization
|
||||
of arguments from the messages is generated at compile time, by introspecting signatures of provided callbacks or deducing types of provided arguments.
|
||||
|
||||
Implementing the Concatenator example using sdbus-c++-generated stubs
|
||||
---------------------------------------------------------------------
|
||||
|
||||
sdbus-c++ ships with the native stub generator tool called sdbuscpp-xml2cpp. The tool is very similar to dbusxx-xml2cpp tool that comes from
|
||||
dbus-c++ project.
|
||||
|
||||
The generator tool takes D-Bus XML IDL description of D-Bus interfaces on its input, and can be instructed to generate one or both of these:
|
||||
an adaptor header file for use at server side, and a proxy header file for use at client side. Like this:
|
||||
|
||||
```bash
|
||||
sdbuscpp-xml2cpp database-bindings.xml --adaptor=database-server-glue.h --proxy=database-client-glue.h
|
||||
```
|
||||
|
||||
The adaptor header file contains classes that can be used to implement described interfaces. The proxy header file contains classes that can be used
|
||||
to make calls to remote objects.
|
||||
|
||||
### XML description of the Concatenator interface
|
||||
|
||||
As an example, let's look at an XML description of our Concatenator's interfaces.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/concatenator">
|
||||
<interface name="org.sdbuscpp.Concatenator">
|
||||
<method name="concatenate">
|
||||
<arg type="ai" name="numbers" direction="in" />
|
||||
<arg type="s" name="separator" direction="in" />
|
||||
<arg type="s" name="concatenatedString" direction="out" />
|
||||
</method>
|
||||
<signal name="concatenated">
|
||||
<arg type="s" name="concatenatedString" />
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
```
|
||||
|
||||
After running this through the stubs generator, we get the stub code that is described in the following two subsections.
|
||||
|
||||
### concatenator-server-glue.h
|
||||
|
||||
There is specific class for each interface in the XML IDL file. The class is de facto an interface which shall be implemented by inheriting from it.
|
||||
The class' constructor takes care of registering all methods, signals and properties. For each D-Bus method there is a pure virtual member function.
|
||||
These pure virtual functions must be implemented in the child class. For each signal, there is a public function member that emits this signal.
|
||||
|
||||
```cpp
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__concatenator_server_glue_h__adaptor__H__
|
||||
#define __sdbuscpp__concatenator_server_glue_h__adaptor__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
|
||||
class Concatenator_adaptor
|
||||
{
|
||||
public:
|
||||
static constexpr const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
|
||||
protected:
|
||||
Concatenator_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.registerMethod("concatenate").onInterface(interfaceName).implementedAs([this](const std::vector<int32_t>& numbers, const std::string& separator){ return this->concatenate(numbers, separator); });
|
||||
object_.registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>();
|
||||
}
|
||||
|
||||
public:
|
||||
void concatenated(const std::string& concatenatedString)
|
||||
{
|
||||
object_.emitSignal("concatenated").onInterface(interfaceName).withArguments(concatenatedString);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
### concatenator-client-glue.h
|
||||
|
||||
Analogously to the adaptor classes described above, there is specific class for each interface in the XML IDL file. The class is de facto a proxy
|
||||
to the concrete interface of a remote object. For each D-Bus signal there is a pure virtual member function whose body must be provided in a child
|
||||
class. For each method, there is a public function member that calls the method remotely.
|
||||
|
||||
```cpp
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__concatenator_client_glue_h__proxy__H__
|
||||
#define __sdbuscpp__concatenator_client_glue_h__proxy__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
|
||||
class Concatenator_proxy
|
||||
{
|
||||
public:
|
||||
static constexpr const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
|
||||
protected:
|
||||
Concatenator_proxy(sdbus::IObjectProxy& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.uponSignal("concatenated").onInterface(interfaceName).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
|
||||
}
|
||||
|
||||
virtual void onConcatenated(const std::string& concatenatedString) = 0;
|
||||
|
||||
public:
|
||||
std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)
|
||||
{
|
||||
std::string result;
|
||||
object_.callMethod("concatenate").onInterface(interfaceName).withArguments(numbers, separator).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObjectProxy& object_;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
### Providing server implementation based on generated adaptors
|
||||
|
||||
To implement a D-Bus object that implements all its D-Bus interfaces, we shall create a class representing the object that inherits from all
|
||||
corresponding `*_adaptor` classes and implements all pure virtual member functions. Specifically, the object class shall inherit from the
|
||||
`Interfaces` template class, the template arguments of which are individual adaptor classes. The `Interfaces` is just a convenience class that
|
||||
hides a few boiler-plate details. For example, in its constructor, it creates an `Object` instance, it takes care of proper initialization of
|
||||
all adaptor superclasses, and exports the object finally.
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include "concatenator-server-glue.h"
|
||||
|
||||
class Concatenator : public sdbus::Interfaces<org::sdbuscpp::Concatenator_adaptor /*, more adaptor classes if there are more interfaces*/>
|
||||
{
|
||||
public:
|
||||
Concatenator(sdbus::IConnection& connection, std::string objectPath)
|
||||
: sdbus::Interfaces<org::sdbuscpp::Concatenator_adaptor>(connection, std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) override
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
|
||||
// Concatenate the numbers
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Emit the 'concatenated' signal with the resulting string
|
||||
concatenated(result);
|
||||
|
||||
// Return the resulting string
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
That's it. We now have an implementation of a D-Bus object implementing `org.sdbuscpp.Concatenator` interface. Let's now create a service
|
||||
publishing the object.
|
||||
|
||||
```cpp
|
||||
#include "Concatenator.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection to the system bus and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createSystemBusConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
Concatenator concatenator(*connection, objectPath);
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
}
|
||||
```
|
||||
|
||||
It's that simple!
|
||||
|
||||
### Providing client implementation based on generated proxies
|
||||
|
||||
To implement a proxy for a remote D-Bus object, we shall create a class representing the object proxy that inherits from all corresponding
|
||||
`*_proxy` classes and -- if applicable -- implements all pure virtual member functions. Specifically, the object proxy class shall inherit
|
||||
from the `ProxyInterfaces` template class. As its template arguments we shall provide all proxy classes. The `ProxyInterfaces` is just a
|
||||
convenience class that hides a few boiler-plate details. For example, in its constructor, it creates an `ObjectProxy` instance, and it takes
|
||||
care of proper initialization of all proxy superclasses.
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include "concatenator-client-glue.h"
|
||||
|
||||
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::Concatenator_proxy /*, more proxy classes if there are more interfaces*/>
|
||||
{
|
||||
public:
|
||||
ConcatenatorProxy(std::string destination, std::string objectPath)
|
||||
: sdbus::ProxyInterfaces<org::sdbuscpp::Concatenator_proxy>(std::move(destination), std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void onConcatenated(const std::string& concatenatedString) override
|
||||
{
|
||||
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Now let's use this proxy to make remote calls and listen to signals in a real application.
|
||||
|
||||
```cpp
|
||||
#include "ConcatenatorProxy.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create proxy object for the concatenator object on the server side
|
||||
const char* destinationName = "org.sdbuscpp.concatenator";
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
ConcatenatorProxy concatenatorProxy(destinationName, objectPath);
|
||||
|
||||
std::vector<int> numbers = {1, 2, 3};
|
||||
std::string separator = ":";
|
||||
|
||||
// Invoke concatenate with some numbers
|
||||
auto concatenatedString = concatenatorProxy.concatenate(numbers, separator);
|
||||
assert(concatenatedString == "1:2:3");
|
||||
|
||||
// Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
try
|
||||
{
|
||||
auto concatenatedString = concatenatorProxy.concatenate(std::vector<int>(), separator);
|
||||
assert(false);
|
||||
}
|
||||
catch(const sdbus::Error& e)
|
||||
{
|
||||
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
|
||||
}
|
||||
|
||||
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Asynchronous server-side methods
|
||||
--------------------------------
|
||||
|
||||
So far in our tutorial, we have only considered simple server methods that are executed in a synchronous way. Sometimes the method call may take longer, however, and we don't want to block (potentially starve) other clients (whose requests may take relative short time). The solution is to execute the D-Bus methods asynchronously. How physically is that done is up to the server design (e.g. thread pool), but sdbus-c++ provides API supporting async methods.
|
||||
|
||||
### Lower-level API
|
||||
|
||||
Considering the Concatenator example based on lower-level API, if we wanted to write `concatenate` method in an asynchronous way, you only have to adapt method signature and its body (registering the method and all the other stuff stays the same):
|
||||
|
||||
```c++
|
||||
void concatenate(sdbus::MethodCall& call, sdbus::MethodResult result)
|
||||
{
|
||||
// Deserialize the collection of numbers from the message
|
||||
std::vector<int> numbers;
|
||||
call >> numbers;
|
||||
|
||||
// Deserialize separator from the message
|
||||
std::string separator;
|
||||
call >> separator;
|
||||
|
||||
// Launch a thread for async execution...
|
||||
std::thread([numbers, separator, result = std::move(result)]()
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
{
|
||||
// This will send the error reply message back to the client
|
||||
result.returnError("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string concatenatedStr;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
concatenatedStr += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// This will send the reply message back to the client
|
||||
result.returnResults(concatenatedStr);
|
||||
|
||||
// Note: emitting signals from other than D-Bus dispatcher thread is not supported yet...
|
||||
/*
|
||||
// Emit 'concatenated' signal
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||
signal << result;
|
||||
g_concatenator->emitSignal(signal);
|
||||
*/
|
||||
}).detach();
|
||||
}
|
||||
```
|
||||
|
||||
Notice these differences as compared to the synchronous version:
|
||||
|
||||
* Instead of `MethodReply` message given by reference, there is `MethodResult` as a second parameter of the callback, which will hold method results and can be written to from any thread.
|
||||
* You shall pass the result holder (`MethodResult` instance) by moving it to the thread of execution, and eventually write method results (or method error) to it via its `returnResults()` or `returnError()` method, respectively.
|
||||
* Unlike in sync methods, reporting errors cannot be done by throwing sdbus::Error, since the execution takes place out of context of the D-Bus dispatcher thread. Instead, just pass the error name and message to the `returnError` method of the result holder.
|
||||
|
||||
That's all.
|
||||
|
||||
Note: We can use the concept of asynchronous D-Bus methods in both the synchronous and asynchronous way. Whether we return the results directly in the callback in the synchronous way, or we pass the arguments and the result holder to a different thread, and compute and set the results in there, is irrelevant to sdbus-c++. This has the benefit that we can decide at run-time, per each method call, whether we execute it synchronously or (perhaps in case of complex operation) execute it asynchronously to e.g. a thread pool.
|
||||
|
||||
### Convenience API
|
||||
|
||||
Method callbacks in convenience sdbus-c++ API also need to take the result object as a parameter. The requirements are:
|
||||
|
||||
* The result holder is of type `sdbus::Result<Types...>`, where `Types...` is a list of method output argument types.
|
||||
* The result object must be a first physical parameter of the callback taken by value.
|
||||
* The callback itself is physically a void-returning function.
|
||||
|
||||
For example, we would have to change the concatenate callback signature from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string> result, const std::vector<int32_t>& numbers, const std::string& separator)`.
|
||||
|
||||
`sdbus::Result` class template has effectively the same API as `sdbus::MethodResult` class from above example (it inherits from MethodResult), so you use it in the very same way to send the results or an error back to the client.
|
||||
|
||||
Nothing else has to be changed. The registration of the method callback (`implementedAs`) and all the mechanics around remains completely the same.
|
||||
|
||||
### Marking async methods in the IDL
|
||||
|
||||
sdbus-c++ stub generator can generate stub code for server-side async methods. We just need to annotate the method with the `annotate` element having the "org.freedesktop.DBus.Method.Async" name, like so:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/concatenator">
|
||||
<interface name="org.sdbuscpp.Concatenator">
|
||||
<method name="concatenate">
|
||||
<annotation name="org.freedesktop.DBus.Method.Async" value="server" />
|
||||
<arg type="ai" name="numbers" direction="in" />
|
||||
<arg type="s" name="separator" direction="in" />
|
||||
<arg type="s" name="concatenatedString" direction="out" />
|
||||
</method>
|
||||
<signal name="concatenated">
|
||||
<arg type="s" name="concatenatedString" />
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
```
|
||||
|
||||
Using D-Bus properties
|
||||
----------------------
|
||||
|
||||
Defining and working with D-Bus properties using XML description is quite easy.
|
||||
|
||||
### Defining a property in the XML
|
||||
|
||||
A property element has no arg child element. It just has the attributes name, type and access, which are all mandatory. The access attribute allows the values ‘readwrite’, ‘read’, and ‘write’.
|
||||
|
||||
An example of a read-write property `status`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/propertyprovider">
|
||||
<interface name="org.sdbuscpp.PropertyProvider">
|
||||
<!--...-->
|
||||
<property name="status" type="u" access="readwrite"/>
|
||||
<!--...-->
|
||||
</interface>
|
||||
</node>
|
||||
```
|
||||
|
||||
### Generated stubs
|
||||
|
||||
This is how generated adaptor and proxy classes would look like with the read-write `status` property. The adaptor:
|
||||
|
||||
```cpp
|
||||
class PropertyProvider_adaptor
|
||||
{
|
||||
/*...*/
|
||||
|
||||
public:
|
||||
PropertyProvider_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.registerProperty("status").onInterface(INTERFACE_NAME).withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); });
|
||||
}
|
||||
|
||||
private:
|
||||
// property getter
|
||||
virtual uint32_t status() = 0;
|
||||
// property setter
|
||||
virtual void status(const uint32_t& value) = 0;
|
||||
|
||||
/*...*/
|
||||
};
|
||||
#endif
|
||||
```
|
||||
|
||||
The proxy:
|
||||
|
||||
```cpp
|
||||
class PropertyProvider_proxy
|
||||
{
|
||||
/*...*/
|
||||
|
||||
public:
|
||||
// getting the property value
|
||||
uint32_t status()
|
||||
{
|
||||
return object_.getProperty("status").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
// setting the property value
|
||||
void status(const uint32_t& value)
|
||||
{
|
||||
object_.setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
/*...*/
|
||||
};
|
||||
```
|
||||
|
||||
When implementing the adaptor, we simply need to provide the body for `status` getter and setter method by overriding them. Then in the proxy, we just call them.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
There is no conclusion. Happy journeys by D-Bus with sdbus-c++!
|
29
docs/CMakeLists.txt
Normal file
29
docs/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
||||
# Building doxygen documentation
|
||||
|
||||
find_package(Doxygen)
|
||||
|
||||
if(BUILD_DOXYGEN_DOC)
|
||||
if(DOXYGEN_FOUND)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
|
||||
|
||||
add_custom_target(doc
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen"
|
||||
VERBATIM)
|
||||
|
||||
# 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)
|
2496
docs/Doxyfile.in
Normal file
2496
docs/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
BIN
docs/sdbus-c++-class-diagram.png
Normal file
BIN
docs/sdbus-c++-class-diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
@ -12,9 +12,10 @@ interface IObject {
|
||||
+emitSignal()
|
||||
}
|
||||
|
||||
interface IObjectProxy {
|
||||
interface IProxy {
|
||||
+callMethod()
|
||||
+subscribeToSignal()
|
||||
+get/setProperty()
|
||||
}
|
||||
|
||||
class Message {
|
||||
@ -39,21 +40,24 @@ class Object {
|
||||
string objectPath
|
||||
List interfaces
|
||||
List methods
|
||||
List signals
|
||||
List properties
|
||||
}
|
||||
|
||||
class ObjectProxy {
|
||||
class Proxy {
|
||||
IConnectionInternal& connection
|
||||
string destination
|
||||
string objectPath
|
||||
List signalHandlers
|
||||
}
|
||||
|
||||
IConnection <|-- Connection
|
||||
IConnectionInternal <|- Connection
|
||||
IObject <|-- Object
|
||||
IObjectProxy <|-- ObjectProxy
|
||||
IProxy <|-- Proxy
|
||||
Connection <-- Object : "use"
|
||||
Connection <-- ObjectProxy : "use"
|
||||
Connection <-- Proxy : "use"
|
||||
Message <.. Object : "send/receive"
|
||||
Message <.. ObjectProxy : "send/receive"
|
||||
Message <.. Proxy : "send/receive"
|
||||
|
||||
@enduml
|
54
docs/systemd-dbus-config.md
Normal file
54
docs/systemd-dbus-config.md
Normal file
@ -0,0 +1,54 @@
|
||||
Systemd and dbus configuration
|
||||
=======================
|
||||
|
||||
**Table of contents**
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
2. [Systemd configuration](#systemd-configuration)
|
||||
3. [Dbus configuration](#dbus-configuration)
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
To run executable as a systemd service you may need some additional setup. For example, you may need explicitly allow
|
||||
the usage of your service. Following chapters contain template configurations.
|
||||
|
||||
|
||||
Systemd configuration
|
||||
---------------------------------------
|
||||
|
||||
Filename should use `.service` extension. It also must be placed in configuration directory (/etc/systemd/system in
|
||||
Ubuntu 18.04.1 LTS)
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=nameOfService
|
||||
|
||||
[Service]
|
||||
ExecStart=/path/to/executable
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Dbus configuration
|
||||
------------------
|
||||
|
||||
Typical default D-Bus configuration does not allow to register services except explicitly allowed. Filename should
|
||||
contain name of your service, e.g `/etc/dbus-1/system.d/org.sdbuscpp.concatenator.conf`. So, here is template
|
||||
configuration to use dbus interface under root:
|
||||
|
||||
```
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<policy user="root">
|
||||
<allow own="org.sdbuscpp.concatenator"/>
|
||||
<allow send_destination="org.sdbuscpp"/>
|
||||
<allow send_interface="org.sdbuscpp.concatenator"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
```
|
||||
|
||||
If you need access from other user `root` should be substituted by desired username. For more refer to `man dbus-daemon`.
|
1281
docs/using-sdbus-c++.md
Normal file
1281
docs/using-sdbus-c++.md
Normal file
File diff suppressed because it is too large
Load Diff
6
examples/CMakeLists.txt
Normal file
6
examples/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
|
||||
add_executable(obj-manager-server org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp)
|
||||
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++)
|
@ -0,0 +1,50 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__examplemanager_planet1_client_glue_h__proxy__H__
|
||||
#define __sdbuscpp__examplemanager_planet1_client_glue_h__proxy__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace ExampleManager {
|
||||
|
||||
class Planet1_proxy
|
||||
{
|
||||
public:
|
||||
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.ExampleManager.Planet1";
|
||||
|
||||
protected:
|
||||
Planet1_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
~Planet1_proxy() = default;
|
||||
|
||||
public:
|
||||
uint64_t GetPopulation()
|
||||
{
|
||||
uint64_t result;
|
||||
proxy_.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
std::string Name()
|
||||
{
|
||||
return proxy_.getProperty("Name").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& proxy_;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
@ -0,0 +1,44 @@
|
||||
|
||||
/*
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__examplemanager_planet1_server_glue_h__adaptor__H__
|
||||
#define __sdbuscpp__examplemanager_planet1_server_glue_h__adaptor__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
namespace ExampleManager {
|
||||
|
||||
class Planet1_adaptor
|
||||
{
|
||||
public:
|
||||
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.ExampleManager.Planet1";
|
||||
|
||||
protected:
|
||||
Planet1_adaptor(sdbus::IObject& object)
|
||||
: 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() = default;
|
||||
|
||||
private:
|
||||
virtual uint64_t GetPopulation() = 0;
|
||||
|
||||
private:
|
||||
virtual std::string Name() = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Example of a D-Bus client which implements org.freedesktop.DBus.ObjectManager
|
||||
*
|
||||
* The example uses the generated stub API layer to listen to interfaces added to new objects under
|
||||
* "org.sdbuscpp.examplemanager". If added, we access "org.sdbuscpp.ExampleManager.Planet1" to print
|
||||
* info like this:
|
||||
* /org/sdbuscpp/examplemanager/Planet1/Earth added: org.sdbuscpp.ExampleManager.Planet1
|
||||
* Earth has a population of 7874965825.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "examplemanager-planet1-client-glue.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
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))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
~PlanetProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
~ManagerProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
|
||||
void handleExistingObjects()
|
||||
{
|
||||
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
|
||||
objectsInterfacesAndProperties = GetManagedObjects();
|
||||
for (const auto& [object, interfacesAndProperties] : objectsInterfacesAndProperties) {
|
||||
onInterfacesAdded(object, interfacesAndProperties);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
|
||||
{
|
||||
std::cout << objectPath << " added:\t";
|
||||
for (const auto& [interface, _] : interfacesAndProperties) {
|
||||
std::cout << interface << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Parse and print some more info
|
||||
auto planetInterface = interfacesAndProperties.find(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>();
|
||||
// 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;
|
||||
}
|
||||
|
||||
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces) override
|
||||
{
|
||||
std::cout << objectPath << " removed:\t";
|
||||
for (const auto& interface : interfaces) {
|
||||
std::cout << interface << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
sdbus::IConnection& m_connection;
|
||||
std::string m_destination;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
auto connection = sdbus::createSessionBusConnection();
|
||||
|
||||
auto managerProxy = std::make_unique<ManagerProxy>(*connection, "org.sdbuscpp.examplemanager", "/org/sdbuscpp/examplemanager");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
connection->enterEventLoop();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Example of a D-Bus server which implements org.freedesktop.DBus.ObjectManager
|
||||
*
|
||||
* The example uses the generated stub API layer to register an object manager under "org.sdbuscpp.examplemanager"
|
||||
* and add objects underneath which implement "org.sdbuscpp.ExampleManager.Planet1".
|
||||
*
|
||||
* We add and remove objects after a few seconds and print info like this:
|
||||
* Creating PlanetAdaptor in 5 4 3 2 1
|
||||
* Creating PlanetAdaptor in 5 4 3 2 1
|
||||
* Creating PlanetAdaptor in 5 4 3 2 1
|
||||
* Removing PlanetAdaptor in 5 4 3 2 1
|
||||
* Removing PlanetAdaptor in 5 4 3 2 1
|
||||
*/
|
||||
|
||||
#include "examplemanager-planet1-server-glue.h"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
class ManagerAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
|
||||
{
|
||||
public:
|
||||
ManagerAdaptor(sdbus::IConnection& connection, std::string path)
|
||||
: AdaptorInterfaces(connection, std::move(path))
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
~ManagerAdaptor()
|
||||
{
|
||||
unregisterAdaptor();
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
registerAdaptor();
|
||||
emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
|
||||
}
|
||||
|
||||
~PlanetAdaptor()
|
||||
{
|
||||
emitInterfacesRemovedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
|
||||
unregisterAdaptor();
|
||||
}
|
||||
|
||||
uint64_t GetPopulation() override
|
||||
{
|
||||
return m_population;
|
||||
}
|
||||
|
||||
std::string Name() override
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
uint64_t m_population;
|
||||
};
|
||||
|
||||
void printCountDown(const std::string& message, int seconds)
|
||||
{
|
||||
std::cout << message << std::flush;
|
||||
for (int i = seconds; i > 0; i--) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
std::cout << i << " " << std::flush;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
auto connection = sdbus::createSessionBusConnection();
|
||||
connection->requestName("org.sdbuscpp.examplemanager");
|
||||
connection->enterEventLoopAsync();
|
||||
|
||||
auto manager = std::make_unique<ManagerAdaptor>(*connection, "/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);
|
||||
printCountDown("Creating PlanetAdaptor in ", 5);
|
||||
auto trantor = std::make_unique<PlanetAdaptor>(*connection, "/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);
|
||||
printCountDown("Removing PlanetAdaptor in ", 5);
|
||||
earth.reset();
|
||||
printCountDown("Removing PlanetAdaptor in ", 5);
|
||||
trantor.reset();
|
||||
printCountDown("Removing PlanetAdaptor in ", 5);
|
||||
laconia.reset();
|
||||
}
|
||||
|
||||
connection->releaseName("org.sdbuscpp.examplemanager");
|
||||
connection->leaveEventLoop();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.sdbuscpp.ExampleManager.Planet1">
|
||||
|
||||
<!--
|
||||
@brief get the population of this planet
|
||||
@param [out] population of the planet
|
||||
-->
|
||||
<method name="GetPopulation">
|
||||
<arg name="population" type="t" direction="out" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
@brief This planet's name
|
||||
-->
|
||||
<property name="Name" type="s" access="read"/>
|
||||
|
||||
</interface>
|
||||
</node>
|
@ -1,24 +0,0 @@
|
||||
|
||||
# Distribution
|
||||
# Headers will be installed to $(includedir)/sdbus-c++ directory
|
||||
|
||||
HEADER_DIR = sdbus-c++
|
||||
libsdbuscppdir = $(includedir)/$(HEADER_DIR)
|
||||
libsdbuscpp_HEADERS = \
|
||||
$(HEADER_DIR)/ConvenienceClasses.h \
|
||||
$(HEADER_DIR)/ConvenienceClasses.inl \
|
||||
$(HEADER_DIR)/Error.h \
|
||||
$(HEADER_DIR)/IConnection.h \
|
||||
$(HEADER_DIR)/Interfaces.h \
|
||||
$(HEADER_DIR)/Introspection.h \
|
||||
$(HEADER_DIR)/IObject.h \
|
||||
$(HEADER_DIR)/IObjectProxy.h \
|
||||
$(HEADER_DIR)/Message.h \
|
||||
$(HEADER_DIR)/MethodResult.h \
|
||||
$(HEADER_DIR)/Types.h \
|
||||
$(HEADER_DIR)/TypeTraits.h \
|
||||
$(HEADER_DIR)/sdbus-c++.h
|
||||
|
||||
EXTRA_DIST = ($libsdbuscpp_HEADERS)
|
||||
|
||||
DISTCLEANFILES = Makefile Makefile.in
|
149
include/sdbus-c++/AdaptorInterfaces.h
Normal file
149
include/sdbus-c++/AdaptorInterfaces.h
Normal file
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file AdaptorInterfaces.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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_ADAPTORINTERFACES_H_
|
||||
#define SDBUS_CXX_ADAPTORINTERFACES_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class ObjectHolder
|
||||
*
|
||||
* ObjectHolder is a helper that simply owns and provides
|
||||
* access to an object to other classes in the inheritance
|
||||
* hierarchy of an object based on generated interface classes.
|
||||
*
|
||||
***********************************************/
|
||||
class ObjectHolder
|
||||
{
|
||||
protected:
|
||||
ObjectHolder(std::unique_ptr<IObject>&& object)
|
||||
: object_(std::move(object))
|
||||
{
|
||||
}
|
||||
|
||||
const IObject& getObject() const
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
}
|
||||
|
||||
IObject& getObject()
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<IObject> object_;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class AdaptorInterfaces
|
||||
*
|
||||
* AdaptorInterfaces is a helper template class that joins all interface classes of a remote
|
||||
* D-Bus object generated by sdbus-c++-xml2cpp to be used on the server (the adaptor) side,
|
||||
* including some auxiliary classes. AdaptorInterfaces is the class that native-like object
|
||||
* implementation classes written by users should inherit from and implement all pure virtual
|
||||
* methods. So the _Interfaces template parameter is a list of sdbus-c++-xml2cpp-generated
|
||||
* 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();`).
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class AdaptorInterfaces
|
||||
: protected ObjectHolder
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Creates object instance
|
||||
*
|
||||
* @param[in] connection D-Bus connection where the object will publish itself
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
*
|
||||
* For more information, consult @ref createObject(sdbus::IConnection&,std::string)
|
||||
*/
|
||||
AdaptorInterfaces(IConnection& connection, std::string objectPath)
|
||||
: ObjectHolder(createObject(connection, std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Finishes adaptor API registration and publishes the adaptor on the bus
|
||||
*
|
||||
* This function must be called in the constructor of the final adaptor class that implements AdaptorInterfaces.
|
||||
*
|
||||
* For more information, see underlying @ref IObject::finishRegistration()
|
||||
*/
|
||||
void registerAdaptor()
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Unregisters adaptors's API and removes it from the bus
|
||||
*
|
||||
* This function must be called in the destructor of the final adaptor class that implements AdaptorInterfaces.
|
||||
*
|
||||
* For more information, see underlying @ref IObject::unregister()
|
||||
*/
|
||||
void unregisterAdaptor()
|
||||
{
|
||||
getObject().unregister();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
const std::string& getObjectPath() const
|
||||
{
|
||||
return getObject().getObjectPath();
|
||||
}
|
||||
|
||||
protected:
|
||||
using base_type = AdaptorInterfaces;
|
||||
~AdaptorInterfaces() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_ADAPTORINTERFACES_H_ */
|
257
include/sdbus-c++/ConvenienceApiClasses.h
Normal file
257
include/sdbus-c++/ConvenienceApiClasses.h
Normal file
@ -0,0 +1,257 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ConvenienceApiClasses.h
|
||||
*
|
||||
* Created on: Jan 19, 2017
|
||||
* 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_CONVENIENCEAPICLASSES_H_
|
||||
#define SDBUS_CXX_CONVENIENCEAPICLASSES_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IObject;
|
||||
class IProxy;
|
||||
class Variant;
|
||||
class Error;
|
||||
class PendingAsyncCall;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
class MethodRegistrator
|
||||
{
|
||||
public:
|
||||
MethodRegistrator(IObject& object, const 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();
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& methodName_;
|
||||
std::string interfaceName_;
|
||||
std::string inputSignature_;
|
||||
std::vector<std::string> inputParamNames_;
|
||||
std::string outputSignature_;
|
||||
std::vector<std::string> outputParamNames_;
|
||||
method_callback methodCallback_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
|
||||
};
|
||||
|
||||
class SignalRegistrator
|
||||
{
|
||||
public:
|
||||
SignalRegistrator(IObject& object, const 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_;
|
||||
const std::string& signalName_;
|
||||
std::string interfaceName_;
|
||||
std::string signalSignature_;
|
||||
std::vector<std::string> paramNames_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
class SignalEmitter
|
||||
{
|
||||
public:
|
||||
SignalEmitter(IObject& object, const std::string& signalName);
|
||||
SignalEmitter(SignalEmitter&& other) = default;
|
||||
~SignalEmitter() noexcept(false);
|
||||
SignalEmitter& onInterface(const std::string& interfaceName);
|
||||
template <typename... _Args> void withArguments(_Args&&... args);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& signalName_;
|
||||
Signal signal_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
||||
};
|
||||
|
||||
class MethodInvoker
|
||||
{
|
||||
public:
|
||||
MethodInvoker(IProxy& proxy, const std::string& methodName);
|
||||
MethodInvoker(MethodInvoker&& other) = default;
|
||||
~MethodInvoker() noexcept(false);
|
||||
|
||||
MethodInvoker& onInterface(const std::string& 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);
|
||||
|
||||
void dontExpectReply();
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& methodName_;
|
||||
uint64_t timeout_{};
|
||||
MethodCall method_;
|
||||
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
||||
bool methodCalled_{};
|
||||
};
|
||||
|
||||
class AsyncMethodInvoker
|
||||
{
|
||||
public:
|
||||
AsyncMethodInvoker(IProxy& proxy, const std::string& methodName);
|
||||
AsyncMethodInvoker& onInterface(const std::string& 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);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& methodName_;
|
||||
uint64_t timeout_{};
|
||||
MethodCall method_;
|
||||
};
|
||||
|
||||
class SignalSubscriber
|
||||
{
|
||||
public:
|
||||
SignalSubscriber(IProxy& proxy, const std::string& signalName);
|
||||
SignalSubscriber& onInterface(std::string interfaceName);
|
||||
template <typename _Function> void call(_Function&& callback);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& signalName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
class SignalUnsubscriber
|
||||
{
|
||||
public:
|
||||
SignalUnsubscriber(IProxy& proxy, const std::string& signalName);
|
||||
void onInterface(std::string interfaceName);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& signalName_;
|
||||
};
|
||||
|
||||
class PropertyGetter
|
||||
{
|
||||
public:
|
||||
PropertyGetter(IProxy& proxy, const std::string& propertyName);
|
||||
sdbus::Variant onInterface(const std::string& interfaceName);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& propertyName_;
|
||||
};
|
||||
|
||||
class PropertySetter
|
||||
{
|
||||
public:
|
||||
PropertySetter(IProxy& proxy, const std::string& propertyName);
|
||||
PropertySetter& onInterface(std::string interfaceName);
|
||||
template <typename _Value> void toValue(const _Value& value);
|
||||
void toValue(const sdbus::Variant& value);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const std::string& propertyName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */
|
745
include/sdbus-c++/ConvenienceApiClasses.inl
Normal file
745
include/sdbus-c++/ConvenienceApiClasses.inl
Normal file
@ -0,0 +1,745 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ConvenienceApiClasses.inl
|
||||
*
|
||||
* Created on: Dec 19, 2016
|
||||
* 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_CONVENIENCEAPICLASSES_INL_
|
||||
#define SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <exception>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/*** ----------------- ***/
|
||||
/*** MethodRegistrator ***/
|
||||
/*** ----------------- ***/
|
||||
|
||||
inline MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
|
||||
: object_(object)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the method if MethodRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
assert(methodCallback_); // implementedAs() must be placed/called prior to this function
|
||||
|
||||
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerMethod( interfaceName_
|
||||
, std::move(methodName_)
|
||||
, std::move(inputSignature_)
|
||||
, std::move(inputParamNames_)
|
||||
, std::move(outputSignature_)
|
||||
, std::move(outputParamNames_)
|
||||
, std::move(methodCallback_)
|
||||
, std::move(flags_));
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
MethodRegistrator& MethodRegistrator::implementedAs(_Function&& callback)
|
||||
{
|
||||
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
|
||||
methodCallback_ = [callback = std::forward<_Function>(callback)](MethodCall call)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<_Function> inputArgs;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple.
|
||||
call >> inputArgs;
|
||||
|
||||
if constexpr (!is_async_method_v<_Function>)
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
auto ret = sdbus::apply(callback, inputArgs);
|
||||
|
||||
// Store output arguments to the reply message and send it back.
|
||||
auto reply = call.createReply();
|
||||
reply << ret;
|
||||
reply.send();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple and with result object to be set later
|
||||
using AsyncResult = typename function_traits<_Function>::async_result_t;
|
||||
sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
|
||||
}
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::withInputParamNames(std::vector<std::string> paramNames)
|
||||
{
|
||||
inputParamNames_ = std::move(paramNames);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _String>
|
||||
inline MethodRegistrator& MethodRegistrator::withInputParamNames(_String... paramNames)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withInputParamNames({paramNames...});
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(std::vector<std::string> paramNames)
|
||||
{
|
||||
outputParamNames_ = std::move(paramNames);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _String>
|
||||
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(_String... paramNames)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withOutputParamNames({paramNames...});
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::markAsDeprecated()
|
||||
{
|
||||
flags_.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::markAsPrivileged()
|
||||
{
|
||||
flags_.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::withNoReply()
|
||||
{
|
||||
flags_.set(Flags::METHOD_NO_REPLY);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** ----------------- ***/
|
||||
/*** SignalRegistrator ***/
|
||||
/*** ----------------- ***/
|
||||
|
||||
inline SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerSignal( interfaceName_
|
||||
, std::move(signalName_)
|
||||
, std::move(signalSignature_)
|
||||
, std::move(paramNames_)
|
||||
, std::move(flags_) );
|
||||
}
|
||||
|
||||
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline SignalRegistrator& SignalRegistrator::withParameters()
|
||||
{
|
||||
signalSignature_ = signature_of_function_input_arguments<void(_Args...)>::str();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline SignalRegistrator& SignalRegistrator::withParameters(std::vector<std::string> paramNames)
|
||||
{
|
||||
paramNames_ = std::move(paramNames);
|
||||
|
||||
return withParameters<_Args...>();
|
||||
}
|
||||
|
||||
template <typename... _Args, typename... _String>
|
||||
inline SignalRegistrator& SignalRegistrator::withParameters(_String... paramNames)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
|
||||
|
||||
return withParameters<_Args...>({paramNames...});
|
||||
}
|
||||
|
||||
inline SignalRegistrator& SignalRegistrator::markAsDeprecated()
|
||||
{
|
||||
flags_.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** ------------------- ***/
|
||||
/*** PropertyRegistrator ***/
|
||||
/*** ------------------- ***/
|
||||
|
||||
inline PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
|
||||
: object_(object)
|
||||
, propertyName_(propertyName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerProperty() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerProperty( interfaceName_
|
||||
, propertyName_
|
||||
, propertySignature_
|
||||
, std::move(getter_)
|
||||
, std::move(setter_)
|
||||
, flags_ );
|
||||
}
|
||||
|
||||
inline PropertyRegistrator& PropertyRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_traits<_Function>::arity == 0, "Property getter function must not take any arguments");
|
||||
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
|
||||
|
||||
if (propertySignature_.empty())
|
||||
propertySignature_ = signature_of_function_output_arguments<_Function>::str();
|
||||
|
||||
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 PropertyRegistrator& PropertyRegistrator::withSetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_traits<_Function>::arity == 1, "Property setter function must take one parameter - the property value");
|
||||
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
|
||||
|
||||
if (propertySignature_.empty())
|
||||
propertySignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
|
||||
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 PropertyRegistrator& PropertyRegistrator::markAsDeprecated()
|
||||
{
|
||||
flags_.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyRegistrator& PropertyRegistrator::markAsPrivileged()
|
||||
{
|
||||
flags_.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyRegistrator& PropertyRegistrator::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
|
||||
{
|
||||
flags_.set(behavior);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** InterfaceFlagsSetter ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
inline InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
|
||||
: object_(object)
|
||||
, interfaceName_(interfaceName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline InterfaceFlagsSetter::~InterfaceFlagsSetter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't set any flags if InterfaceFlagsSetter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow setInterfaceFlags() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.setInterfaceFlags(interfaceName_, std::move(flags_));
|
||||
}
|
||||
|
||||
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsDeprecated()
|
||||
{
|
||||
flags_.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsPrivileged()
|
||||
{
|
||||
flags_.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsSetter& InterfaceFlagsSetter::withNoReplyMethods()
|
||||
{
|
||||
flags_.set(Flags::METHOD_NO_REPLY);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsSetter& InterfaceFlagsSetter::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
|
||||
{
|
||||
flags_.set(behavior);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*** ------------- ***/
|
||||
/*** SignalEmitter ***/
|
||||
/*** ------------- ***/
|
||||
|
||||
inline SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't emit the signal if SignalEmitter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
// emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow emitSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.emitSignal(signal_);
|
||||
}
|
||||
|
||||
inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
signal_ = object_.createSignal(interfaceName, signalName_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void SignalEmitter::withArguments(_Args&&... args)
|
||||
{
|
||||
assert(signal_.isValid()); // onInterface() must be placed/called prior to withArguments()
|
||||
|
||||
detail::serialize_pack(signal_, std::forward<_Args>(args)...);
|
||||
}
|
||||
|
||||
/*** ------------- ***/
|
||||
/*** MethodInvoker ***/
|
||||
/*** ------------- ***/
|
||||
|
||||
inline MethodInvoker::MethodInvoker(IProxy& proxy, const std::string& methodName)
|
||||
: proxy_(proxy)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't call the method if it has been called already or if MethodInvoker
|
||||
// threw an exception in one of its methods
|
||||
if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
// callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow callMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
proxy_.callMethod(method_, timeout_);
|
||||
}
|
||||
|
||||
inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
method_ = proxy_.createMethodCall(interfaceName, methodName_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodInvoker& MethodInvoker::withTimeout(uint64_t usec)
|
||||
{
|
||||
timeout_ = usec;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline MethodInvoker& MethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
return withTimeout(microsecs.count());
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline MethodInvoker& MethodInvoker::withArguments(_Args&&... args)
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
detail::serialize_pack(method_, std::forward<_Args>(args)...);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void MethodInvoker::storeResultsTo(_Args&... args)
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
auto reply = proxy_.callMethod(method_, timeout_);
|
||||
methodCalled_ = true;
|
||||
|
||||
detail::deserialize_pack(reply, args...);
|
||||
}
|
||||
|
||||
inline void MethodInvoker::dontExpectReply()
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
method_.dontExpectReply();
|
||||
}
|
||||
|
||||
/*** ------------------ ***/
|
||||
/*** AsyncMethodInvoker ***/
|
||||
/*** ------------------ ***/
|
||||
|
||||
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const std::string& methodName)
|
||||
: proxy_(proxy)
|
||||
, methodName_(methodName)
|
||||
{
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
method_ = proxy_.createMethodCall(interfaceName, methodName_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(uint64_t usec)
|
||||
{
|
||||
timeout_ = usec;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline AsyncMethodInvoker& AsyncMethodInvoker::withTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
return withTimeout(microsecs.count());
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline AsyncMethodInvoker& AsyncMethodInvoker::withArguments(_Args&&... args)
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
detail::serialize_pack(method_, std::forward<_Args>(args)...);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall AsyncMethodInvoker::uponReplyInvoke(_Function&& callback)
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
auto asyncReplyHandler = [callback = std::forward<_Function>(callback)](MethodReply& reply, const Error* error)
|
||||
{
|
||||
// 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> args;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple (if no error occurred).
|
||||
if (error == nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
reply >> args;
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
// Catch message unpack exceptions and pass them to the callback
|
||||
// in the expected manner to avoid propagating them up the call
|
||||
// stack to the event loop.
|
||||
sdbus::apply(callback, &e, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, error, args);
|
||||
};
|
||||
|
||||
return proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
|
||||
}
|
||||
|
||||
/*** ---------------- ***/
|
||||
/*** SignalSubscriber ***/
|
||||
/*** ---------------- ***/
|
||||
|
||||
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const std::string& signalName)
|
||||
: proxy_(proxy)
|
||||
, signalName_(signalName)
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline void SignalSubscriber::call(_Function&& callback)
|
||||
{
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
proxy_.registerSignalHandler( interfaceName_
|
||||
, signalName_
|
||||
, [callback = std::forward<_Function>(callback)](Signal& signal)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the signal message.
|
||||
tuple_of_function_input_arg_types_t<_Function> signalArgs;
|
||||
|
||||
// The signal handler can take pure signal parameters only, or an additional `const Error*` as its first
|
||||
// parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
|
||||
// the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
|
||||
// will be communicated as a non-zero Error pointer to the client's signal handler.
|
||||
if constexpr (has_error_param_v<_Function>)
|
||||
{
|
||||
// Deserialize input arguments from the signal message into the tuple
|
||||
try
|
||||
{
|
||||
signal >> signalArgs;
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
// Invoke callback with error argument and input arguments from the tuple.
|
||||
sdbus::apply(callback, &e, signalArgs);
|
||||
}
|
||||
|
||||
// Invoke callback with no error and input arguments from the tuple.
|
||||
sdbus::apply(callback, nullptr, signalArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Deserialize input arguments from the signal message into the tuple
|
||||
signal >> signalArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, signalArgs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*** ------------------ ***/
|
||||
/*** SignalUnsubscriber ***/
|
||||
/*** ------------------ ***/
|
||||
|
||||
inline SignalUnsubscriber::SignalUnsubscriber(IProxy& proxy, const std::string& signalName)
|
||||
: proxy_(proxy)
|
||||
, signalName_(signalName)
|
||||
{
|
||||
}
|
||||
|
||||
inline void SignalUnsubscriber::onInterface(std::string interfaceName)
|
||||
{
|
||||
proxy_.unregisterSignalHandler(interfaceName, signalName_);
|
||||
}
|
||||
|
||||
/*** -------------- ***/
|
||||
/*** PropertyGetter ***/
|
||||
/*** -------------- ***/
|
||||
|
||||
inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
|
||||
: proxy_(proxy)
|
||||
, propertyName_(propertyName)
|
||||
{
|
||||
}
|
||||
|
||||
inline sdbus::Variant PropertyGetter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
sdbus::Variant var;
|
||||
proxy_
|
||||
.callMethod("Get")
|
||||
.onInterface("org.freedesktop.DBus.Properties")
|
||||
.withArguments(interfaceName, propertyName_)
|
||||
.storeResultsTo(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
/*** -------------- ***/
|
||||
/*** PropertySetter ***/
|
||||
/*** -------------- ***/
|
||||
|
||||
inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
|
||||
: proxy_(proxy)
|
||||
, propertyName_(propertyName)
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertySetter& PropertySetter::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Value>
|
||||
inline void PropertySetter::toValue(const _Value& value)
|
||||
{
|
||||
PropertySetter::toValue(sdbus::Variant{value});
|
||||
}
|
||||
|
||||
inline void PropertySetter::toValue(const sdbus::Variant& value)
|
||||
{
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
proxy_
|
||||
.callMethod("Set")
|
||||
.onInterface("org.freedesktop.DBus.Properties")
|
||||
.withArguments(interfaceName_, propertyName_, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */
|
@ -1,174 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ConvenienceClasses.h
|
||||
*
|
||||
* Created on: Jan 19, 2017
|
||||
* 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_CONVENIENCECLASSES_H_
|
||||
#define SDBUS_CXX_CONVENIENCECLASSES_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IObject;
|
||||
class IObjectProxy;
|
||||
class Variant;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
class MethodRegistrator
|
||||
{
|
||||
public:
|
||||
MethodRegistrator(IObject& object, const std::string& methodName);
|
||||
MethodRegistrator& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function>
|
||||
std::enable_if_t<!is_async_method_v<_Function>> implementedAs(_Function&& callback);
|
||||
template <typename _Function>
|
||||
std::enable_if_t<is_async_method_v<_Function>> implementedAs(_Function&& callback);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& methodName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
class SignalRegistrator
|
||||
{
|
||||
public:
|
||||
SignalRegistrator(IObject& object, const std::string& signalName);
|
||||
SignalRegistrator(SignalRegistrator&& other) = default;
|
||||
SignalRegistrator& operator=(SignalRegistrator&& other) = default;
|
||||
~SignalRegistrator() noexcept(false);
|
||||
SignalRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename... _Args> void withParameters();
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& signalName_;
|
||||
std::string interfaceName_;
|
||||
std::string signalSignature_;
|
||||
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& operator=(PropertyRegistrator&& other) = default;
|
||||
~PropertyRegistrator() noexcept(false);
|
||||
PropertyRegistrator& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function> PropertyRegistrator& withGetter(_Function&& callback);
|
||||
template <typename _Function> PropertyRegistrator& withSetter(_Function&& callback);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& propertyName_;
|
||||
std::string interfaceName_;
|
||||
std::string propertySignature_;
|
||||
property_get_callback getter_;
|
||||
property_set_callback setter_;
|
||||
int exceptions_{}; // Number of active exceptions when PropertyRegistrator is constructed
|
||||
};
|
||||
|
||||
class SignalEmitter
|
||||
{
|
||||
public:
|
||||
SignalEmitter(IObject& object, const std::string& signalName);
|
||||
SignalEmitter(SignalEmitter&& other) = default;
|
||||
SignalEmitter& operator=(SignalEmitter&& other) = default;
|
||||
~SignalEmitter() noexcept(false);
|
||||
SignalEmitter& onInterface(const std::string& interfaceName);
|
||||
template <typename... _Args> void withArguments(_Args&&... args);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& signalName_;
|
||||
Signal signal_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
||||
};
|
||||
|
||||
class MethodInvoker
|
||||
{
|
||||
public:
|
||||
MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName);
|
||||
MethodInvoker(MethodInvoker&& other) = default;
|
||||
MethodInvoker& operator=(MethodInvoker&& other) = default;
|
||||
~MethodInvoker() noexcept(false);
|
||||
|
||||
MethodInvoker& onInterface(const std::string& interfaceName);
|
||||
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
|
||||
template <typename... _Args> void storeResultsTo(_Args&... args);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
const std::string& methodName_;
|
||||
MethodCall method_;
|
||||
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
||||
bool methodCalled_{};
|
||||
};
|
||||
|
||||
class SignalSubscriber
|
||||
{
|
||||
public:
|
||||
SignalSubscriber(IObjectProxy& objectProxy, const std::string& signalName);
|
||||
SignalSubscriber& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function> void call(_Function&& callback);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
std::string signalName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
class PropertyGetter
|
||||
{
|
||||
public:
|
||||
PropertyGetter(IObjectProxy& objectProxy, const std::string& propertyName);
|
||||
sdbus::Variant onInterface(const std::string& interfaceName);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
std::string propertyName_;
|
||||
};
|
||||
|
||||
class PropertySetter
|
||||
{
|
||||
public:
|
||||
PropertySetter(IObjectProxy& objectProxy, const std::string& propertyName);
|
||||
PropertySetter& onInterface(const std::string& interfaceName);
|
||||
template <typename _Value> void toValue(const _Value& value);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
const std::string& propertyName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_CONVENIENCECLASSES_H_ */
|
@ -1,425 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ConvenienceClasses.inl
|
||||
*
|
||||
* Created on: Dec 19, 2016
|
||||
* 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_CONVENIENCECLASSES_INL_
|
||||
#define SDBUS_CPP_CONVENIENCECLASSES_INL_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
/*#include <exception>*/
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
inline MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
|
||||
: object_(object)
|
||||
, methodName_(methodName)
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline std::enable_if_t<!is_async_method_v<_Function>> MethodRegistrator::implementedAs(_Function&& callback)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||
|
||||
object_.registerMethod( interfaceName_
|
||||
, methodName_
|
||||
, signature_of_function_input_arguments<_Function>::str()
|
||||
, signature_of_function_output_arguments<_Function>::str()
|
||||
, [callback = std::forward<_Function>(callback)](MethodCall& msg, MethodReply& reply)
|
||||
{
|
||||
// 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
|
||||
msg >> inputArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
// For callbacks returning a non-void value, `apply' also returns that value.
|
||||
// For callbacks returning void, `apply' returns an empty tuple.
|
||||
auto ret = sdbus::apply(callback, inputArgs); // We don't yet have C++17's std::apply :-(
|
||||
|
||||
// The return value is stored to the reply message.
|
||||
// In case of void functions, ret is an empty tuple and thus nothing is stored.
|
||||
reply << ret;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline std::enable_if_t<is_async_method_v<_Function>> MethodRegistrator::implementedAs(_Function&& callback)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||
|
||||
object_.registerMethod( interfaceName_
|
||||
, methodName_
|
||||
, signature_of_function_input_arguments<_Function>::str()
|
||||
, signature_of_function_output_arguments<_Function>::str() //signature_of<last_function_argument_t<_Function>>::str() // because last argument contains output types
|
||||
, [callback = std::forward<_Function>(callback)](MethodCall& msg, MethodResult result)
|
||||
{
|
||||
// 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,
|
||||
// plus store the result object as a last item of the tuple.
|
||||
msg >> inputArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, std::move(result), inputArgs); // TODO: Use std::apply when switching to full C++17 support
|
||||
});
|
||||
}
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
|
||||
: object_(object)
|
||||
, signalName_(std::move(signalName))
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (interfaceName_.empty())
|
||||
throw sdbus::Exception("DBus interface not specified when registering a DBus signal");
|
||||
|
||||
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerSignal(interfaceName_, signalName_, signalSignature_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void SignalRegistrator::withParameters()
|
||||
{
|
||||
signalSignature_ = signature_of_function_input_arguments<void(_Args...)>::str();
|
||||
}
|
||||
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline PropertyRegistrator::PropertyRegistrator(IObject& object, std::string propertyName)
|
||||
: object_(object)
|
||||
, propertyName_(std::move(propertyName))
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus property", EINVAL);
|
||||
|
||||
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerProperty() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerProperty( std::move(interfaceName_)
|
||||
, std::move(propertyName_)
|
||||
, std::move(propertySignature_)
|
||||
, std::move(getter_)
|
||||
, std::move(setter_) );
|
||||
}
|
||||
*/
|
||||
|
||||
inline PropertyRegistrator& PropertyRegistrator::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_traits<_Function>::arity == 0, "Property getter function must not take any arguments");
|
||||
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
|
||||
|
||||
if (propertySignature_.empty())
|
||||
propertySignature_ = signature_of_function_output_arguments<_Function>::str();
|
||||
|
||||
getter_ = [callback = std::forward<_Function>(callback)](Message& msg)
|
||||
{
|
||||
// Get the propety value and serialize it into the message
|
||||
msg << callback();
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline PropertyRegistrator& PropertyRegistrator::withSetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_traits<_Function>::arity == 1, "Property setter function must take one parameter - the property value");
|
||||
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
|
||||
|
||||
if (propertySignature_.empty())
|
||||
propertySignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
|
||||
setter_ = [callback = std::forward<_Function>(callback)](Message& msg)
|
||||
{
|
||||
// Default-construct property value
|
||||
using property_type = function_argument_t<_Function, 0>;
|
||||
std::decay_t<property_type> property;
|
||||
|
||||
// Deserialize property value from the message
|
||||
msg >> property;
|
||||
|
||||
// Invoke setter with the value
|
||||
callback(property);
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't emit the signal if SignalEmitter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (!signal_.isValid())
|
||||
throw sdbus::Exception("DBus interface not specified when emitting a DBus signal");
|
||||
|
||||
// emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow emitSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.emitSignal(signal_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
signal_ = object_.createSignal(interfaceName, signalName_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void SignalEmitter::withArguments(_Args&&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!signal_.isValid(), "DBus interface not specified when emitting a DBus signal", EINVAL);
|
||||
|
||||
detail::serialize_pack(signal_, std::forward<_Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline MethodInvoker::MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName)
|
||||
: objectProxy_(objectProxy)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't call the method if it has been called already or if MethodInvoker
|
||||
// threw an exception in one of its methods
|
||||
if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (!method_.isValid())
|
||||
throw sdbus::Exception("DBus interface not specified when calling a DBus method");
|
||||
|
||||
// callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow callMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
objectProxy_.callMethod(method_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
method_ = objectProxy_.createMethodCall(interfaceName, methodName_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline MethodInvoker& MethodInvoker::withArguments(_Args&&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
|
||||
detail::serialize_pack(method_, std::forward<_Args>(args)...);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void MethodInvoker::storeResultsTo(_Args&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
|
||||
auto reply = objectProxy_.callMethod(method_);
|
||||
methodCalled_ = true;
|
||||
|
||||
detail::deserialize_pack(reply, args...);
|
||||
}
|
||||
|
||||
|
||||
inline SignalSubscriber::SignalSubscriber(IObjectProxy& objectProxy, const std::string& signalName)
|
||||
: objectProxy_(objectProxy)
|
||||
, signalName_(signalName)
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline void SignalSubscriber::call(_Function&& callback)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when subscribing to a signal", EINVAL);
|
||||
|
||||
objectProxy_.registerSignalHandler( interfaceName_
|
||||
, signalName_
|
||||
, [callback = std::forward<_Function>(callback)](Signal& signal)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the signal message.
|
||||
tuple_of_function_input_arg_types_t<_Function> signalArgs;
|
||||
|
||||
// Deserialize input arguments from the signal message into the tuple
|
||||
signal >> signalArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, signalArgs); // We don't yet have C++17's std::apply :-(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
inline PropertyGetter::PropertyGetter(IObjectProxy& objectProxy, const std::string& propertyName)
|
||||
: objectProxy_(objectProxy)
|
||||
, propertyName_(propertyName)
|
||||
{
|
||||
}
|
||||
|
||||
inline sdbus::Variant PropertyGetter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
sdbus::Variant var;
|
||||
objectProxy_
|
||||
.callMethod("Get")
|
||||
.onInterface("org.freedesktop.DBus.Properties")
|
||||
.withArguments(interfaceName, propertyName_)
|
||||
.storeResultsTo(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
|
||||
inline PropertySetter::PropertySetter(IObjectProxy& objectProxy, const std::string& propertyName)
|
||||
: objectProxy_(objectProxy)
|
||||
, propertyName_(propertyName)
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Value>
|
||||
inline void PropertySetter::toValue(const _Value& value)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when setting a property", EINVAL);
|
||||
|
||||
objectProxy_
|
||||
.callMethod("Set")
|
||||
.onInterface("org.freedesktop.DBus.Properties")
|
||||
.withArguments(interfaceName_, propertyName_, sdbus::Variant{value});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CPP_CONVENIENCECLASSES_INL_ */
|
25
include/sdbus-c++/Error.h
Executable file → Normal file
25
include/sdbus-c++/Error.h
Executable file → Normal file
@ -1,7 +1,8 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ConvenienceClasses.h
|
||||
* @file Error.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* Project: sdbus-c++
|
||||
@ -26,7 +27,9 @@
|
||||
#ifndef SDBUS_CXX_ERROR_H_
|
||||
#define SDBUS_CXX_ERROR_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -40,6 +43,11 @@ namespace sdbus {
|
||||
: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit Error(const std::string& name, const char* message = nullptr)
|
||||
: Error(name, std::string(message ? message : ""))
|
||||
{
|
||||
}
|
||||
|
||||
Error(const std::string& name, const std::string& message)
|
||||
: std::runtime_error("[" + name + "] " + message)
|
||||
, name_(name)
|
||||
@ -57,6 +65,11 @@ namespace sdbus {
|
||||
return message_;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !getName().empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string message_;
|
||||
@ -65,12 +78,12 @@ namespace sdbus {
|
||||
sdbus::Error createError(int errNo, const std::string& customMsg);
|
||||
}
|
||||
|
||||
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
|
||||
throw sdbus::createError((_ERRNO), (_MSG)) \
|
||||
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
|
||||
throw sdbus::createError((_ERRNO), (_MSG)) \
|
||||
/**/
|
||||
|
||||
#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \
|
||||
if (_COND) SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \
|
||||
#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \
|
||||
if (!(_COND)) ; else SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \
|
||||
/**/
|
||||
|
||||
#endif /* SDBUS_CXX_ERROR_H_ */
|
||||
|
99
include/sdbus-c++/Flags.h
Normal file
99
include/sdbus-c++/Flags.h
Normal file
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Flags.h
|
||||
*
|
||||
* Created on: Dec 31, 2018
|
||||
* 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_FLAGS_H_
|
||||
#define SDBUS_CXX_FLAGS_H_
|
||||
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// D-Bus interface, method, signal or property flags
|
||||
class Flags
|
||||
{
|
||||
public:
|
||||
enum GeneralFlags : uint8_t
|
||||
{ DEPRECATED = 0
|
||||
, METHOD_NO_REPLY = 1
|
||||
, PRIVILEGED = 2
|
||||
};
|
||||
|
||||
enum PropertyUpdateBehaviorFlags : uint8_t
|
||||
{ EMITS_CHANGE_SIGNAL = 3
|
||||
, EMITS_INVALIDATION_SIGNAL = 4
|
||||
, EMITS_NO_SIGNAL = 5
|
||||
, CONST_PROPERTY_VALUE = 6
|
||||
};
|
||||
|
||||
enum : uint8_t
|
||||
{ FLAG_COUNT = 7
|
||||
};
|
||||
|
||||
Flags()
|
||||
{
|
||||
// EMITS_CHANGE_SIGNAL is on by default
|
||||
flags_.set(EMITS_CHANGE_SIGNAL, true);
|
||||
}
|
||||
|
||||
void set(GeneralFlags flag, bool value = true)
|
||||
{
|
||||
flags_.set(flag, value);
|
||||
}
|
||||
|
||||
void set(PropertyUpdateBehaviorFlags flag, bool value = true)
|
||||
{
|
||||
flags_.set(EMITS_CHANGE_SIGNAL, false);
|
||||
flags_.set(EMITS_INVALIDATION_SIGNAL, false);
|
||||
flags_.set(EMITS_NO_SIGNAL, false);
|
||||
flags_.set(CONST_PROPERTY_VALUE, false);
|
||||
|
||||
flags_.set(flag, value);
|
||||
}
|
||||
|
||||
bool test(GeneralFlags flag) const
|
||||
{
|
||||
return flags_.test(flag);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
std::bitset<FLAG_COUNT> flags_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_FLAGS_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file IConnection.h
|
||||
*
|
||||
@ -26,9 +27,11 @@
|
||||
#ifndef SDBUS_CXX_ICONNECTION_H_
|
||||
#define SDBUS_CXX_ICONNECTION_H_
|
||||
|
||||
//#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -36,122 +39,351 @@ namespace sdbus {
|
||||
* @class IConnection
|
||||
*
|
||||
* An interface to D-Bus bus connection. Incorporates implementation
|
||||
* of both synchronous and asynchronous processing loop.
|
||||
* of both synchronous and asynchronous D-Bus I/O event loop.
|
||||
*
|
||||
* All methods throw @sdbus::Error in case of failure. The class is
|
||||
* thread-aware, but not thread-safe.
|
||||
* All methods throw sdbus::Error in case of failure. All methods in
|
||||
* this class are thread-aware, but not thread-safe.
|
||||
*
|
||||
***********************************************/
|
||||
class IConnection
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Requests D-Bus name on the connection
|
||||
*
|
||||
* @param[in] name Name to request
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
* 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;
|
||||
};
|
||||
|
||||
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
|
||||
*/
|
||||
* @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 Enters the D-Bus processing loop
|
||||
*
|
||||
* The incoming D-Bus messages are processed in the loop. The method
|
||||
* blocks indefinitely, until unblocked via @leaveProcessingLoop.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void enterProcessingLoop() = 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 the D-Bus processing loop in a separate thread
|
||||
*
|
||||
* The same as @enterProcessingLoop, except that it doesn't block
|
||||
* because it runs the loop in a separate thread managed internally.
|
||||
*/
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
* @brief Enters I/O event loop on this bus connection
|
||||
*
|
||||
* The incoming D-Bus messages are processed in the loop. The method
|
||||
* blocks indefinitely, until unblocked through leaveEventLoop().
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void enterEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Leaves the D-Bus processing loop
|
||||
*
|
||||
* Ends the previously started processing loop.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
* @brief Enters I/O event loop on this bus connection in a separate thread
|
||||
*
|
||||
* The same as enterEventLoop, except that it doesn't block
|
||||
* because it runs the loop in a separate, internally managed thread.
|
||||
*/
|
||||
virtual void enterEventLoopAsync() = 0;
|
||||
|
||||
inline virtual ~IConnection() = 0;
|
||||
/*!
|
||||
* @brief Leaves the I/O event loop running on this bus connection
|
||||
*
|
||||
* This causes the loop to exit and frees the thread serving the loop
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void leaveEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager at the specified D-Bus object path
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addObjectManager(const std::string& objectPath) = 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.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual PollData getEventLoopPollData() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Process a pending request
|
||||
*
|
||||
* @returns true if an event was processed, false if poll should be called
|
||||
*
|
||||
* Processes a single dbus event. All of sdbus-c++'s callbacks will be called
|
||||
* from within this method. This method should ONLY be used in conjuction
|
||||
* with 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.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual bool processPendingRequest() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Sets general method call timeout
|
||||
*
|
||||
* @param[in] timeout Timeout value in microseconds
|
||||
*
|
||||
* General method call timeout is used for all method calls upon this connection.
|
||||
* Method call-specific timeout overrides this general setting.
|
||||
*
|
||||
* Supported by libsystemd>=v240.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void setMethodCallTimeout(uint64_t timeout) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::setMethodCallTimeout(uint64_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
void setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
|
||||
/*!
|
||||
* @brief Gets general method call timeout
|
||||
*
|
||||
* @return Timeout value in microseconds
|
||||
*
|
||||
* Supported by libsystemd>=v240.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual uint64_t getMethodCallTimeout() const = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::enterEventLoop()
|
||||
*
|
||||
* @deprecated This function has been replaced by enterEventLoop()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop();
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::enterProcessingLoopAsync()
|
||||
*
|
||||
* @deprecated This function has been replaced by enterEventLoopAsync()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync();
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::leaveProcessingLoop()
|
||||
*
|
||||
* @deprecated This function has been replaced by leaveEventLoop()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by leaveEventLoop()")]] void leaveProcessingLoop();
|
||||
|
||||
/*!
|
||||
* @copydoc IConnection::getProcessLoopPollData()
|
||||
*
|
||||
* @deprecated This function has been replaced by getEventLoopPollData()
|
||||
*/
|
||||
[[deprecated("This function has been replaced by getEventLoopPollData()")]] PollData getProcessLoopPollData() const;
|
||||
};
|
||||
|
||||
IConnection::~IConnection() {}
|
||||
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 connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createConnection();
|
||||
* @brief Creates/opens D-Bus system connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system 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
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
|
||||
* @brief Creates/opens D-Bus system 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 system connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
|
||||
* @brief Creates/opens D-Bus user connection when in a user context,
|
||||
* and a system connection, otherwise.
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system 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
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
|
||||
* @brief Creates/opens D-Bus user connection with a name when in a user
|
||||
* context, and a system connection with a name, otherwise.
|
||||
*
|
||||
* @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> createDefaultBusConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
|
||||
* @brief Creates/opens D-Bus system connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session 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
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
|
||||
* @brief Creates/opens D-Bus system 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> createSystemBusConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session 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> createSessionBusConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection on a remote host using ssh
|
||||
*
|
||||
* @param[in] host Name of the host to connect
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_ICONNECTION_H_ */
|
||||
|
603
include/sdbus-c++/IObject.h
Executable file → Normal file
603
include/sdbus-c++/IObject.h
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file IObject.h
|
||||
*
|
||||
@ -26,11 +27,13 @@
|
||||
#ifndef SDBUS_CXX_IOBJECT_H_
|
||||
#define SDBUS_CXX_IOBJECT_H_
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.h>
|
||||
#include <sdbus-c++/ConvenienceApiClasses.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@ -43,218 +46,420 @@ namespace sdbus {
|
||||
/********************************************//**
|
||||
* @class IObject
|
||||
*
|
||||
* An interface to D-Bus object. Provides API for registration
|
||||
* of methods, signals, properties, and for emitting signals.
|
||||
* IObject class represents a D-Bus object instance identified by a specific object path.
|
||||
* D-Bus object provides its interfaces, methods, signals and properties on a bus
|
||||
* identified by a specific bus name.
|
||||
*
|
||||
* All methods throw @c sdbus::Error in case of failure. The class is
|
||||
* thread-aware, but not thread-safe.
|
||||
* All IObject member methods throw @c sdbus::Error in case of D-Bus or sdbus-c++ error.
|
||||
* The IObject class has been designed as thread-aware. However, the operation of
|
||||
* creating and sending asynchronous method replies, as well as creating and emitting
|
||||
* signals, is thread-safe by design.
|
||||
*
|
||||
***********************************************/
|
||||
class IObject
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] methodCallback Callback that implements the body of the method
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback ) = 0;
|
||||
virtual ~IObject() = default;
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] asyncMethodCallback Callback that implements the body of the method
|
||||
*
|
||||
* This overload register a method callback that will have a freedom to execute
|
||||
* its body in asynchronous contexts, and send the results from those contexts.
|
||||
* This can help in e.g. long operations, which then don't block the D-Bus processing
|
||||
* loop thread.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] 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)
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, async_method_callback asyncMethodCallback ) = 0;
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, std::string outputSignature
|
||||
, method_callback methodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will emit on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal will fall under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signature D-Bus signature of signal parameters
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] inputNames Names of input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] outputNames Names of output parameters
|
||||
* @param[in] methodCallback Callback that implements the body of the method
|
||||
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
|
||||
*
|
||||
* Provided names of input and output parameters will be included in the introspection
|
||||
* description (given that at least version 242 of underlying libsystemd library is
|
||||
* used; otherwise, names of parameters are ignored). This usually helps better describe
|
||||
* the API to the introspector.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, const std::vector<std::string>& inputNames
|
||||
, std::string outputSignature
|
||||
, const std::vector<std::string>& outputNames
|
||||
, method_callback methodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @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] flags D-Bus signal flags (deprecated)
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) = 0;
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback ) = 0;
|
||||
* @brief Registers signal that the object will emit on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal will fall under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signature D-Bus signature of signal parameters
|
||||
* @param[in] paramNames Names of parameters of the signal
|
||||
* @param[in] flags D-Bus signal flags (deprecated)
|
||||
*
|
||||
* Provided names of signal output parameters will be included in the introspection
|
||||
* description (given that at least version 242 of underlying libsystemd library is
|
||||
* used; otherwise, names of parameters are ignored). This usually helps better describe
|
||||
* the API to the introspector.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, const std::vector<std::string>& paramNames
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
* @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
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback ) = 0;
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Finishes the registration and exports object API on D-Bus
|
||||
*
|
||||
* The method exports all up to now registered methods, signals and properties on D-Bus.
|
||||
* Must be called only once, after all methods, signals and properties have been registered.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
* @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 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
|
||||
*/
|
||||
* @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 on D-Bus
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
* @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;
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
MethodRegistrator registerMethod(const std::string& methodName);
|
||||
* @brief Emits PropertyChanged signal for specified properties under a given interface of this object path
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that properties belong to
|
||||
* @param[in] propNames Names of properties that will be included in the PropertiesChanged signal
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) = 0;
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
SignalRegistrator registerSignal(const std::string& signalName);
|
||||
* @brief Emits PropertyChanged signal for all properties on a given interface of this object path
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const std::string& interfaceName) = 0;
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
PropertyRegistrator registerProperty(const std::string& propertyName);
|
||||
* @brief Emits InterfacesAdded signal on this object path
|
||||
*
|
||||
* This emits an InterfacesAdded signal on this object path, by iterating all registered
|
||||
* interfaces on the path. All properties are queried and included in the signal.
|
||||
* This call is equivalent to emitInterfacesAddedSignal() with an explicit list of
|
||||
* registered interfaces. However, unlike emitInterfacesAddedSignal(interfaces), this
|
||||
* call can figure out the list of supported interfaces itself. Furthermore, it properly
|
||||
* adds the builtin org.freedesktop.DBus.* interfaces.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesAddedSignal() = 0;
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
SignalEmitter emitSignal(const std::string& signalName);
|
||||
* @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.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) = 0;
|
||||
|
||||
virtual ~IObject() = 0;
|
||||
/*!
|
||||
* @brief Emits InterfacesRemoved signal on this object path
|
||||
*
|
||||
* This is like sd_bus_emit_object_added(), but emits an InterfacesRemoved signal on this
|
||||
* object path. This only includes any registered interfaces but skips the properties.
|
||||
* This function shall be called (just) before destroying the object.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesRemovedSignal() = 0;
|
||||
|
||||
/*!
|
||||
* @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.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager interface at the path of this D-Bus object
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addObjectManager() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Removes an ObjectManager interface from the path of this D-Bus object
|
||||
*
|
||||
* @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;
|
||||
|
||||
/*!
|
||||
* @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);
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
virtual const std::string& getObjectPath() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides currently processed D-Bus message
|
||||
*
|
||||
* This method provides immutable 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.
|
||||
*
|
||||
* @return A pointer to the currently processed D-Bus message
|
||||
*/
|
||||
virtual const Message* getCurrentlyProcessedMessage() const = 0;
|
||||
};
|
||||
|
||||
// Out-of-line member definitions
|
||||
|
||||
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
|
||||
{
|
||||
return MethodRegistrator(*this, methodName);
|
||||
@ -262,12 +467,17 @@ namespace sdbus {
|
||||
|
||||
inline SignalRegistrator IObject::registerSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalRegistrator(*this, std::move(signalName));
|
||||
return SignalRegistrator(*this, signalName);
|
||||
}
|
||||
|
||||
inline PropertyRegistrator IObject::registerProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertyRegistrator(*this, std::move(propertyName));
|
||||
return PropertyRegistrator(*this, propertyName);
|
||||
}
|
||||
|
||||
inline InterfaceFlagsSetter IObject::setInterfaceFlags(const std::string& interfaceName)
|
||||
{
|
||||
return InterfaceFlagsSetter(*this, interfaceName);
|
||||
}
|
||||
|
||||
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
|
||||
@ -275,27 +485,28 @@ namespace sdbus {
|
||||
return SignalEmitter(*this, signalName);
|
||||
}
|
||||
|
||||
inline IObject::~IObject() {}
|
||||
|
||||
/*!
|
||||
* @brief Creates instance representing a D-Bus object
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
* @return Pointer to the object representation instance
|
||||
*
|
||||
* The provided connection will be used by the object to export methods,
|
||||
* issue signals and provide properties.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
|
||||
* @brief Creates instance representing a D-Bus object
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
* @return Pointer to the object representation instance
|
||||
*
|
||||
* The provided connection will be used by the object to export methods,
|
||||
* issue signals and provide properties.
|
||||
*
|
||||
* Creating a D-Bus object instance is (thread-)safe even upon the connection
|
||||
* which is already running its I/O event loop.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.inl>
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IOBJECT_H_ */
|
||||
|
@ -1,271 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file IObjectProxy.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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_IOBJECTPROXY_H_
|
||||
#define SDBUS_CXX_IOBJECTPROXY_H_
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class IObjectProxy
|
||||
*
|
||||
* An interface to D-Bus object proxy. Provides API for calling
|
||||
* methods, getting/setting properties, and for registering to signals.
|
||||
*
|
||||
* All methods throw @c sdbus::Error in case of failure. The class is
|
||||
* thread-aware, but not thread-safe.
|
||||
*
|
||||
***********************************************/
|
||||
class IObjectProxy
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Creates a method call message
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method is defined under
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A method call message message
|
||||
*
|
||||
* Serialize method arguments into the returned message and invoke the method by passing
|
||||
* the message with serialized arguments to the @c callMethod function.
|
||||
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object
|
||||
*
|
||||
* @param[in] message Message representing a method call
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual MethodReply callMethod(const sdbus::MethodCall& message) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs to
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signalHandler Callback that implements the body of the signal handler
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Finishes the registration of signal handlers
|
||||
*
|
||||
* The method physically subscribes to the desired signals.
|
||||
* Must be called only once, after all signals have been registered already.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void finishRegistration() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object
|
||||
*
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A helper object for convenient invocation of the method
|
||||
*
|
||||
* This is a high-level, convenience way of calling 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 provided native arguments
|
||||
* and return values.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int result, a = ..., b = ...;
|
||||
* object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
MethodInvoker callMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal handler for a given signal of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient registration of the signal handler
|
||||
*
|
||||
* This is a high-level, convenience way of registering to 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 parameters
|
||||
* of the provided native signal callback.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
SignalSubscriber uponSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Gets value of a property of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient getting of property value
|
||||
*
|
||||
* This is a high-level, convenience way of reading D-Bus property values that abstracts
|
||||
* from the D-Bus message concept. sdbus::Variant is returned which shall then be converted
|
||||
* to the real property type (implicit conversion is supported).
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int state = object.getProperty("state").onInterface("com.kistler.foo");
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertyGetter getProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets value of a property of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient setting of property value
|
||||
*
|
||||
* This is a high-level, convenience way of writing D-Bus property values that abstracts
|
||||
* from the D-Bus message concept.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int state = ...;
|
||||
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertySetter setProperty(const std::string& propertyName);
|
||||
|
||||
virtual ~IObjectProxy() = 0;
|
||||
};
|
||||
|
||||
inline MethodInvoker IObjectProxy::callMethod(const std::string& methodName)
|
||||
{
|
||||
return MethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline SignalSubscriber IObjectProxy::uponSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalSubscriber(*this, signalName);
|
||||
}
|
||||
|
||||
inline PropertyGetter IObjectProxy::getProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertyGetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline PropertySetter IObjectProxy::setProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertySetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline IObjectProxy::~IObjectProxy() {}
|
||||
|
||||
/*!
|
||||
* @brief Creates object proxy instance
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* The provided connection will be used by the proxy to issue calls against the object,
|
||||
* and signals, if any, will be subscribed to on this connection.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObjectProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( sdbus::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates object proxy instance
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* The provided connection will be used by the proxy to issue calls against the object,
|
||||
* and signals, if any, will be subscribed to on this connection. Object proxy becomes
|
||||
* an exclusive owner of this connection.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObjectProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates object proxy instance that uses its own D-Bus connection
|
||||
*
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* This factory overload creates a proxy that manages its own D-Bus connection(s).
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObjectProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IOBJECTPROXY_H_ */
|
485
include/sdbus-c++/IProxy.h
Normal file
485
include/sdbus-c++/IProxy.h
Normal file
@ -0,0 +1,485 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file IProxy.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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_IPROXY_H_
|
||||
#define SDBUS_CXX_IPROXY_H_
|
||||
|
||||
#include <sdbus-c++/ConvenienceApiClasses.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class IConnection;
|
||||
class PendingAsyncCall;
|
||||
namespace internal {
|
||||
class Proxy;
|
||||
}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class IProxy
|
||||
*
|
||||
* IProxy class represents a proxy object, which is a convenient local object created
|
||||
* to represent a remote D-Bus object in another process.
|
||||
* The proxy enables calling methods on remote objects, receiving signals from remote
|
||||
* objects, and getting/setting properties of remote objects.
|
||||
*
|
||||
* All IProxy member methods throw @c sdbus::Error in case of D-Bus or sdbus-c++ error.
|
||||
* The IProxy class has been designed as thread-aware. However, the operation of
|
||||
* creating and sending method calls (both synchronously and asynchronously) is
|
||||
* thread-safe by design.
|
||||
*
|
||||
***********************************************/
|
||||
class IProxy
|
||||
{
|
||||
public:
|
||||
virtual ~IProxy() = default;
|
||||
|
||||
/*!
|
||||
* @brief Creates a method call message
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that provides a given method
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A method call message
|
||||
*
|
||||
* Serialize method arguments into the returned message and invoke the method by passing
|
||||
* the message with serialized arguments to the @c callMethod function.
|
||||
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object
|
||||
*
|
||||
* @param[in] message Message representing a method call
|
||||
* @param[in] timeout Timeout for dbus call in microseconds
|
||||
* @return A method reply message
|
||||
*
|
||||
* Normally, the call is blocking, i.e. it waits for the remote method to finish with either
|
||||
* a return value or an error.
|
||||
*
|
||||
* If the method call argument is set to not expect reply, the call will not wait for the remote
|
||||
* method to finish, i.e. the call will be non-blocking, and the function will return an empty,
|
||||
* invalid MethodReply object (representing void).
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout = 0) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodCall&,uint64_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object asynchronously
|
||||
*
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] asyncReplyCallback Handler for the async reply
|
||||
* @param[in] timeout Timeout for dbus call in microseconds
|
||||
* @return Cookie for the the pending asynchronous call
|
||||
*
|
||||
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
|
||||
* the provided async reply handler will get invoked from the context of the connection
|
||||
* I/O event loop thread.
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout = 0) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
|
||||
/*!
|
||||
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs to
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signalHandler Callback that implements the body of the signal handler
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Unregisters the handler of the desired signal
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs to
|
||||
* @param[in] signalName Name of the signal
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void unregisterSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Finishes the registration of signal handlers
|
||||
*
|
||||
* The method physically subscribes to the desired signals.
|
||||
* Must be called only once, after all signals have been registered already.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void finishRegistration() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Unregisters proxy's signal handlers and stops receving replies to pending async calls
|
||||
*
|
||||
* Unregistration is done automatically also in proxy's destructor. This method makes
|
||||
* sense if, in the process of proxy removal, we need to make sure that callbacks
|
||||
* are unregistered explicitly before the final destruction of the proxy instance.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void unregister() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object
|
||||
*
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A helper object for convenient invocation of the method
|
||||
*
|
||||
* This is a high-level, convenience way of calling 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 provided native arguments
|
||||
* and return values.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int result, a = ..., b = ...;
|
||||
* object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] MethodInvoker callMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object asynchronously
|
||||
*
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A helper object for convenient asynchronous invocation of the method
|
||||
*
|
||||
* This is a high-level, convenience way of calling 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 provided native arguments
|
||||
* and return values.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int a = ..., b = ...;
|
||||
* object_.callMethodAsync("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result)
|
||||
* {
|
||||
* std::cout << "Got result of multiplying " << a << " and " << b << ": " << result << std::endl;
|
||||
* });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal handler for a given signal of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient registration of the signal handler
|
||||
*
|
||||
* This is a high-level, convenience way of registering to 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 parameters
|
||||
* of the provided native signal callback.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Unregisters signal handler of a given signal of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient unregistration of the signal handler
|
||||
*
|
||||
* This is a high-level, convenience way of unregistering a D-Bus signal's handler.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.muteSignal("fooSignal").onInterface("com.kistler.foo");
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] SignalUnsubscriber muteSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Gets value of a property of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient getting of property value
|
||||
*
|
||||
* This is a high-level, convenience way of reading D-Bus property values that abstracts
|
||||
* from the D-Bus message concept. sdbus::Variant is returned which shall then be converted
|
||||
* to the real property type (implicit conversion is supported).
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int state = object.getProperty("state").onInterface("com.kistler.foo");
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] PropertyGetter getProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets value of a property of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient setting of property value
|
||||
*
|
||||
* This is a high-level, convenience way of writing D-Bus property values that abstracts
|
||||
* from the D-Bus message concept.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int state = ...;
|
||||
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] PropertySetter setProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Provides D-Bus connection used by the proxy
|
||||
*
|
||||
* @return Reference to the D-Bus connection
|
||||
*/
|
||||
virtual sdbus::IConnection& getConnection() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
virtual const std::string& getObjectPath() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides currently processed D-Bus message
|
||||
*
|
||||
* This method provides immutable 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 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.
|
||||
*
|
||||
* @return A pointer to the currently processed D-Bus message
|
||||
*/
|
||||
virtual const Message* getCurrentlyProcessedMessage() const = 0;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class PendingAsyncCall
|
||||
*
|
||||
* PendingAsyncCall represents a simple handle type to cancel the delivery
|
||||
* of the asynchronous D-Bus call result to the application.
|
||||
*
|
||||
* The handle is lifetime-independent from the originating Proxy object.
|
||||
* It's safe to call its methods even after the Proxy has gone.
|
||||
*
|
||||
***********************************************/
|
||||
class PendingAsyncCall
|
||||
{
|
||||
public:
|
||||
PendingAsyncCall() = default;
|
||||
|
||||
/*!
|
||||
* @brief Cancels the delivery of the pending asynchronous call result
|
||||
*
|
||||
* This function effectively removes the callback handler registered to the
|
||||
* async D-Bus method call result delivery. Does nothing if the call was
|
||||
* completed already, or if the originating Proxy object has gone meanwhile.
|
||||
*/
|
||||
void cancel();
|
||||
|
||||
/*!
|
||||
* @brief Answers whether the asynchronous call is still pending
|
||||
*
|
||||
* @return True if the call is pending, false if the call has been fully completed
|
||||
*
|
||||
* Pending call in this context means a call whose results have not arrived, or
|
||||
* have arrived and are currently being processed by the callback handler.
|
||||
*/
|
||||
bool isPending() const;
|
||||
|
||||
private:
|
||||
friend internal::Proxy;
|
||||
PendingAsyncCall(std::weak_ptr<void> callData);
|
||||
|
||||
private:
|
||||
std::weak_ptr<void> callData_;
|
||||
};
|
||||
|
||||
// Out-of-line member definitions
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline MethodReply IProxy::callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout)
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
return callMethod(message, microsecs.count());
|
||||
}
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline PendingAsyncCall IProxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout)
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
return callMethod(message, std::move(asyncReplyCallback), microsecs.count());
|
||||
}
|
||||
|
||||
inline MethodInvoker IProxy::callMethod(const std::string& methodName)
|
||||
{
|
||||
return MethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker IProxy::callMethodAsync(const std::string& methodName)
|
||||
{
|
||||
return AsyncMethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline SignalSubscriber IProxy::uponSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalSubscriber(*this, signalName);
|
||||
}
|
||||
|
||||
inline SignalUnsubscriber IProxy::muteSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalUnsubscriber(*this, signalName);
|
||||
}
|
||||
|
||||
inline PropertyGetter IProxy::getProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertyGetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline PropertySetter IProxy::setProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertySetter(*this, propertyName);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides the remote D-Bus object
|
||||
* @param[in] objectPath Path of the remote D-Bus object
|
||||
* @return Pointer to the proxy object instance
|
||||
*
|
||||
* The provided connection will be used by the proxy to issue calls against the object,
|
||||
* and signals, if any, will be subscribed to on this connection. The caller still
|
||||
* remains the owner of the connection (the proxy just keeps a reference to it), and
|
||||
* should make sure that an I/O event loop is running on that connection, so the proxy
|
||||
* may receive incoming signals and asynchronous method replies.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides the remote D-Bus object
|
||||
* @param[in] objectPath Path of the remote D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* The provided connection will be used by the proxy to issue calls against the object,
|
||||
* and signals, if any, will be subscribed to on this connection. The Object proxy becomes
|
||||
* an exclusive owner of this connection, and will automatically start a procesing loop
|
||||
* upon that connection in a separate internal thread. Handlers for incoming signals and
|
||||
* asynchronous method replies will be executed in the context of that thread.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @param[in] destination Bus name that provides the remote D-Bus object
|
||||
* @param[in] objectPath Path of the remote D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* No D-Bus connection is provided here, so the object proxy will create and manage
|
||||
* his own connection, and will automatically start a procesing loop upon that connection
|
||||
* in a separate internal thread. Handlers for incoming signals and asynchronous
|
||||
* method replies will be executed in the context of that thread.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IPROXY_H_ */
|
@ -1,120 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Interfaces.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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_INTERFACES_H_
|
||||
#define SDBUS_CXX_INTERFACES_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
template <typename _Object>
|
||||
class ObjectHolder
|
||||
{
|
||||
protected:
|
||||
ObjectHolder(std::unique_ptr<_Object>&& object)
|
||||
: object_(std::move(object))
|
||||
{
|
||||
}
|
||||
|
||||
const _Object& getObject() const
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
}
|
||||
|
||||
_Object& getObject()
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<_Object> object_;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Interfaces
|
||||
*
|
||||
* A helper template class that a user class representing a D-Bus object
|
||||
* should inherit from, providing as template arguments the adaptor
|
||||
* classes representing D-Bus interfaces that the object implements.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class Interfaces
|
||||
: private ObjectHolder<IObject>
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
Interfaces(IConnection& connection, std::string objectPath)
|
||||
: ObjectHolder<IObject>(createObject(connection, std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
}
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Interfaces
|
||||
*
|
||||
* A helper template class that a user class representing a proxy of a
|
||||
* D-Bus object should inherit from, providing as template arguments the proxy
|
||||
* classes representing object D-Bus interfaces that the object implements.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class ProxyInterfaces
|
||||
: private ObjectHolder<IObjectProxy>
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
ProxyInterfaces(std::string destination, std::string objectPath)
|
||||
: ObjectHolder<IObjectProxy>(createObjectProxy(std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
}
|
||||
|
||||
ProxyInterfaces(IConnection& connection, std::string destination, std::string objectPath)
|
||||
: ObjectHolder<IObjectProxy>(createObjectProxy(connection, std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERFACES_H_ */
|
@ -1,84 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Introspection.h
|
||||
*
|
||||
* Created on: Dec 13, 2016
|
||||
* 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_INTROSPECTION_H_
|
||||
#define SDBUS_CXX_INTROSPECTION_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <string>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// Proxy for introspection
|
||||
class introspectable_proxy
|
||||
{
|
||||
static constexpr const char* interfaceName = "org.freedesktop.DBus.Introspectable";
|
||||
|
||||
protected:
|
||||
introspectable_proxy(sdbus::IObjectProxy& object)
|
||||
: object_(object)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::string Introspect()
|
||||
{
|
||||
std::string xml;
|
||||
object_.callMethod("Introspect").onInterface(interfaceName).storeResultsTo(xml);
|
||||
return xml;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObjectProxy& object_;
|
||||
};
|
||||
|
||||
// Adaptor is not necessary if we want to rely on sdbus-provided introspection
|
||||
// class introspectable_adaptor
|
||||
// {
|
||||
// static constexpr const char* interfaceName = "org.freedesktop.DBus.Introspectable";
|
||||
//
|
||||
// protected:
|
||||
// introspectable_adaptor(sdbus::IObject& object)
|
||||
// : object_(object)
|
||||
// {
|
||||
// object_.registerMethod("Introspect").onInterface(interfaceName).implementedAs([this](){ return object_.introspect(); });
|
||||
// }
|
||||
//
|
||||
// public:
|
||||
// std::string introspect()
|
||||
// {
|
||||
// std::string xml;
|
||||
// object_.callMethod("Introspect").onInterface(interfaceName).storeResultsTo(xml);
|
||||
// return xml;
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// sdbus::IObject& object_;
|
||||
// };
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTROSPECTION_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Message.h
|
||||
*
|
||||
@ -35,8 +36,8 @@
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <sys/types.h>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@ -44,16 +45,20 @@ namespace sdbus {
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
|
||||
class Message;
|
||||
class MethodCall;
|
||||
class UnixFd;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
template <typename... _Results> class Result;
|
||||
namespace internal {
|
||||
class ISdBus;
|
||||
class IConnection;
|
||||
}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// Assume the caller has already obtained message ownership
|
||||
struct adopt_message_t { explicit adopt_message_t() = default; };
|
||||
inline constexpr adopt_message_t adopt_message{};
|
||||
|
||||
/********************************************//**
|
||||
* @class Message
|
||||
*
|
||||
@ -65,20 +70,12 @@ namespace sdbus {
|
||||
* by D-Bus.
|
||||
*
|
||||
* You don't need to work with this class directly if you use high-level APIs
|
||||
* of @c IObject and @c IObjectProxy.
|
||||
* of @c IObject and @c IProxy.
|
||||
*
|
||||
***********************************************/
|
||||
class Message
|
||||
class [[nodiscard]] Message
|
||||
{
|
||||
public:
|
||||
Message() = default;
|
||||
Message(void *msg) noexcept;
|
||||
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);
|
||||
@ -93,6 +90,7 @@ namespace sdbus {
|
||||
Message& operator<<(const Variant &item);
|
||||
Message& operator<<(const ObjectPath &item);
|
||||
Message& operator<<(const Signature &item);
|
||||
Message& operator<<(const UnixFd &item);
|
||||
|
||||
Message& operator>>(bool& item);
|
||||
Message& operator>>(int16_t& item);
|
||||
@ -108,6 +106,7 @@ namespace sdbus {
|
||||
Message& operator>>(Variant &item);
|
||||
Message& operator>>(ObjectPath &item);
|
||||
Message& operator>>(Signature &item);
|
||||
Message& operator>>(UnixFd &item);
|
||||
|
||||
Message& openContainer(const std::string& signature);
|
||||
Message& closeContainer();
|
||||
@ -127,11 +126,14 @@ namespace sdbus {
|
||||
Message& enterStruct(const std::string& signature);
|
||||
Message& exitStruct();
|
||||
|
||||
operator bool() const;
|
||||
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;
|
||||
bool isValid() const;
|
||||
bool isEmpty() const;
|
||||
@ -140,37 +142,117 @@ namespace sdbus {
|
||||
void seal();
|
||||
void rewind(bool complete);
|
||||
|
||||
protected:
|
||||
void* getMsg() const;
|
||||
pid_t getCredsPid() const;
|
||||
uid_t getCredsUid() const;
|
||||
uid_t getCredsEuid() const;
|
||||
gid_t getCredsGid() const;
|
||||
gid_t getCredsEgid() const;
|
||||
std::vector<gid_t> getCredsSupplementaryGids() const;
|
||||
std::string getSELinuxContext() const;
|
||||
|
||||
private:
|
||||
class Factory;
|
||||
|
||||
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();
|
||||
|
||||
friend Factory;
|
||||
|
||||
protected:
|
||||
void* msg_{};
|
||||
internal::ISdBus* sdbus_{};
|
||||
mutable bool ok_{true};
|
||||
};
|
||||
|
||||
struct dont_request_slot_t { explicit dont_request_slot_t() = default; };
|
||||
inline constexpr dont_request_slot_t dont_request_slot{};
|
||||
|
||||
class MethodCall : public Message
|
||||
{
|
||||
public:
|
||||
using Message::Message;
|
||||
MethodReply send() const;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
using Slot = std::unique_ptr<void, std::function<void(void*)>>;
|
||||
|
||||
MethodCall() = default;
|
||||
|
||||
MethodReply send(uint64_t timeout) const;
|
||||
void send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const;
|
||||
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout) const;
|
||||
|
||||
MethodReply createReply() const;
|
||||
MethodReply createErrorReply(const sdbus::Error& error) const;
|
||||
|
||||
void dontExpectReply();
|
||||
bool doesntExpectReply() const;
|
||||
|
||||
protected:
|
||||
MethodCall(void *msg, internal::ISdBus* sdbus, const 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
|
||||
{
|
||||
public:
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
MethodReply() = default;
|
||||
void send() const;
|
||||
};
|
||||
|
||||
class Signal : public Message
|
||||
{
|
||||
public:
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
Signal() = default;
|
||||
void setDestination(const std::string& destination);
|
||||
void send() const;
|
||||
};
|
||||
|
||||
class PropertySetCall : public Message
|
||||
{
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
PropertySetCall() = default;
|
||||
};
|
||||
|
||||
class PropertyGetReply : public Message
|
||||
{
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
PropertyGetReply() = default;
|
||||
};
|
||||
|
||||
class PlainMessage : public Message
|
||||
{
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
PlainMessage() = default;
|
||||
};
|
||||
|
||||
template <typename _Element>
|
||||
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
|
||||
{
|
||||
@ -210,10 +292,7 @@ namespace sdbus {
|
||||
template <typename... _Args>
|
||||
void serialize_pack(Message& msg, _Args&&... args)
|
||||
{
|
||||
// Use initializer_list because it guarantees left to right order, and can be empty
|
||||
using _ = std::initializer_list<int>;
|
||||
// We are not interested in the list itself, but in the side effects
|
||||
(void)_{(void(msg << std::forward<_Args>(args)), 0)...};
|
||||
(void)(msg << ... << args);
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
@ -305,10 +384,7 @@ namespace sdbus {
|
||||
template <typename... _Args>
|
||||
void deserialize_pack(Message& msg, _Args&... args)
|
||||
{
|
||||
// Use initializer_list because it guarantees left to right order, and can be empty
|
||||
using _ = std::initializer_list<int>;
|
||||
// We are not interested in the list itself, but in the side effects
|
||||
(void)_{(void(msg >> args), 0)...};
|
||||
(void)(msg >> ... >> args);
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
|
80
include/sdbus-c++/MethodResult.h
Executable file → Normal file
80
include/sdbus-c++/MethodResult.h
Executable file → Normal file
@ -1,7 +1,8 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ConvenienceClasses.h
|
||||
* @file MethodResult.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* Project: sdbus-c++
|
||||
@ -31,60 +32,11 @@
|
||||
|
||||
// Forward declaration
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
class Object;
|
||||
}
|
||||
class Error;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class MethodResult
|
||||
*
|
||||
* Represents result of an asynchronous server-side method.
|
||||
* An instance is provided to the method and shall be set
|
||||
* by the method to either method return value or an error.
|
||||
*
|
||||
***********************************************/
|
||||
class MethodResult
|
||||
{
|
||||
protected:
|
||||
friend sdbus::internal::Object;
|
||||
MethodResult() = default;
|
||||
MethodResult(const MethodCall& msg, sdbus::internal::Object& object);
|
||||
|
||||
template <typename... _Results> void returnResults(const _Results&... results) const;
|
||||
void returnError(const Error& error) const;
|
||||
|
||||
private:
|
||||
void send(const MethodReply& reply) const;
|
||||
|
||||
private:
|
||||
MethodCall call_;
|
||||
sdbus::internal::Object* object_{};
|
||||
};
|
||||
|
||||
template <typename... _Results>
|
||||
inline void MethodResult::returnResults(const _Results&... results) const
|
||||
{
|
||||
assert(call_.isValid());
|
||||
auto reply = call_.createReply();
|
||||
#ifdef __cpp_fold_expressions
|
||||
(reply << ... << results);
|
||||
#else
|
||||
using _ = std::initializer_list<int>;
|
||||
(void)_{(void(reply << results), 0)...};
|
||||
#endif
|
||||
send(reply);
|
||||
}
|
||||
|
||||
inline void MethodResult::returnError(const Error& error) const
|
||||
{
|
||||
auto reply = call_.createErrorReply(error);
|
||||
send(reply);
|
||||
}
|
||||
|
||||
/********************************************//**
|
||||
* @class Result
|
||||
*
|
||||
@ -94,31 +46,45 @@ namespace sdbus {
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Results>
|
||||
class Result : protected MethodResult
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
Result() = default;
|
||||
Result(MethodResult result);
|
||||
Result(MethodCall call);
|
||||
|
||||
Result(const Result&) = delete;
|
||||
Result& operator=(const Result&) = delete;
|
||||
|
||||
Result(Result&& other) = default;
|
||||
Result& operator=(Result&& other) = default;
|
||||
|
||||
void returnResults(const _Results&... results) const;
|
||||
void returnError(const Error& error) const;
|
||||
|
||||
private:
|
||||
MethodCall call_;
|
||||
};
|
||||
|
||||
template <typename... _Results>
|
||||
inline Result<_Results...>::Result(MethodResult result)
|
||||
: MethodResult(std::move(result))
|
||||
inline Result<_Results...>::Result(MethodCall call)
|
||||
: call_(std::move(call))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::returnResults(const _Results&... results) const
|
||||
{
|
||||
MethodResult::returnResults(results...);
|
||||
assert(call_.isValid());
|
||||
auto reply = call_.createReply();
|
||||
(reply << ... << results);
|
||||
reply.send();
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::returnError(const Error& error) const
|
||||
{
|
||||
MethodResult::returnError(error);
|
||||
auto reply = call_.createErrorReply(error);
|
||||
reply.send();
|
||||
}
|
||||
|
||||
}
|
||||
|
183
include/sdbus-c++/ProxyInterfaces.h
Normal file
183
include/sdbus-c++/ProxyInterfaces.h
Normal file
@ -0,0 +1,183 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ProxyInterfaces.h
|
||||
*
|
||||
* Created on: Apr 8, 2019
|
||||
* 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_PROXYINTERFACES_H_
|
||||
#define SDBUS_CXX_PROXYINTERFACES_H_
|
||||
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class ProxyObjectHolder
|
||||
*
|
||||
* ProxyObjectHolder is a helper that simply owns and provides
|
||||
* access to a proxy object to other classes in the inheritance
|
||||
* hierarchy of a native-like proxy object based on generated
|
||||
* interface classes.
|
||||
*
|
||||
***********************************************/
|
||||
class ProxyObjectHolder
|
||||
{
|
||||
protected:
|
||||
ProxyObjectHolder(std::unique_ptr<IProxy>&& proxy)
|
||||
: proxy_(std::move(proxy))
|
||||
{
|
||||
assert(proxy_ != nullptr);
|
||||
}
|
||||
|
||||
const IProxy& getProxy() const
|
||||
{
|
||||
assert(proxy_ != nullptr);
|
||||
return *proxy_;
|
||||
}
|
||||
|
||||
IProxy& getProxy()
|
||||
{
|
||||
assert(proxy_ != nullptr);
|
||||
return *proxy_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<IProxy> proxy_;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class ProxyInterfaces
|
||||
*
|
||||
* ProxyInterfaces is a helper template class that joins all interface classes of a remote
|
||||
* D-Bus object generated by sdbus-c++-xml2cpp to be used on the client (the proxy) side,
|
||||
* including some auxiliary classes. ProxyInterfaces is the class that native-like proxy
|
||||
* implementation classes written by users should inherit from and implement all pure virtual
|
||||
* 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();`).
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class ProxyInterfaces
|
||||
: protected ProxyObjectHolder
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Creates native-like proxy object instance
|
||||
*
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
*
|
||||
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
|
||||
* For more information on its behavior, consult @ref createProxy(std::string,std::string)
|
||||
*/
|
||||
ProxyInterfaces(std::string destination, std::string objectPath)
|
||||
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Creates native-like proxy object instance
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
*
|
||||
* The proxy created this way 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)
|
||||
: ProxyObjectHolder(createProxy(connection, std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Creates native-like proxy object instance
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
*
|
||||
* The proxy created this way becomes an owner of the connection.
|
||||
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string)
|
||||
*/
|
||||
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath)
|
||||
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Finishes proxy registration and makes the proxy ready for use
|
||||
*
|
||||
* This function must be called in the constructor of the final proxy class that implements ProxyInterfaces.
|
||||
*
|
||||
* For more information, see underlying @ref IProxy::finishRegistration()
|
||||
*/
|
||||
void registerProxy()
|
||||
{
|
||||
getProxy().finishRegistration();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Unregisters the proxy so it no more receives signals and async call replies
|
||||
*
|
||||
* This function must be called in the destructor of the final proxy class that implements ProxyInterfaces.
|
||||
*
|
||||
* See underlying @ref IProxy::unregister()
|
||||
*/
|
||||
void unregisterProxy()
|
||||
{
|
||||
getProxy().unregister();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
const std::string& getObjectPath() const
|
||||
{
|
||||
return getProxy().getObjectPath();
|
||||
}
|
||||
|
||||
protected:
|
||||
using base_type = ProxyInterfaces;
|
||||
~ProxyInterfaces() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERFACES_H_ */
|
315
include/sdbus-c++/StandardInterfaces.h
Normal file
315
include/sdbus-c++/StandardInterfaces.h
Normal file
@ -0,0 +1,315 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file StandardInterfaces.h
|
||||
*
|
||||
* Created on: Dec 13, 2016
|
||||
* 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_STANDARDINTERFACES_H_
|
||||
#define SDBUS_CXX_STANDARDINTERFACES_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// Proxy for peer
|
||||
class Peer_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
|
||||
|
||||
protected:
|
||||
Peer_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
~Peer_proxy() = default;
|
||||
|
||||
public:
|
||||
void Ping()
|
||||
{
|
||||
proxy_.callMethod("Ping").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
std::string GetMachineId()
|
||||
{
|
||||
std::string machineUUID;
|
||||
proxy_.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
|
||||
return machineUUID;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& proxy_;
|
||||
};
|
||||
|
||||
// Proxy for introspection
|
||||
class Introspectable_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
|
||||
|
||||
protected:
|
||||
Introspectable_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
~Introspectable_proxy() = default;
|
||||
|
||||
public:
|
||||
std::string Introspect()
|
||||
{
|
||||
std::string xml;
|
||||
proxy_.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
|
||||
return xml;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& proxy_;
|
||||
};
|
||||
|
||||
// Proxy for properties
|
||||
class Properties_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
this->onPropertiesChanged(interfaceName, changedProperties, invalidatedProperties);
|
||||
});
|
||||
}
|
||||
|
||||
~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;
|
||||
|
||||
public:
|
||||
sdbus::Variant Get(const std::string& interfaceName, const std::string& propertyName)
|
||||
{
|
||||
return proxy_.getProperty(propertyName).onInterface(interfaceName);
|
||||
}
|
||||
|
||||
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
|
||||
{
|
||||
proxy_.setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
||||
}
|
||||
|
||||
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
|
||||
{
|
||||
std::map<std::string, sdbus::Variant> props;
|
||||
proxy_.callMethod("GetAll").onInterface(INTERFACE_NAME).withArguments(interfaceName).storeResultsTo(props);
|
||||
return props;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& proxy_;
|
||||
};
|
||||
|
||||
// Proxy for object manager
|
||||
class ObjectManager_proxy
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
|
||||
protected:
|
||||
ObjectManager_proxy(sdbus::IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
{
|
||||
proxy_
|
||||
.uponSignal("InterfacesAdded")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
|
||||
{
|
||||
this->onInterfacesAdded(objectPath, interfacesAndProperties);
|
||||
});
|
||||
|
||||
proxy_
|
||||
.uponSignal("InterfacesRemoved")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
{
|
||||
this->onInterfacesRemoved(objectPath, interfaces);
|
||||
});
|
||||
}
|
||||
|
||||
~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;
|
||||
|
||||
public:
|
||||
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects()
|
||||
{
|
||||
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
|
||||
proxy_.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
|
||||
return objectsInterfacesAndProperties;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& proxy_;
|
||||
};
|
||||
|
||||
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
|
||||
// is provided by underlying libsystemd implementation. The exception is Properties_adaptor,
|
||||
// ObjectManager_adaptor and ManagedObject_adaptor, which provide convenience functionality to emit signals.
|
||||
|
||||
// Adaptor for properties
|
||||
class Properties_adaptor
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
}
|
||||
|
||||
~Properties_adaptor() = default;
|
||||
|
||||
public:
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
|
||||
{
|
||||
object_.emitPropertiesChangedSignal(interfaceName, properties);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const std::string& interfaceName)
|
||||
{
|
||||
object_.emitPropertiesChangedSignal(interfaceName);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Object Manager Convenience Adaptor
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Note that there can be multiple object managers in a path hierarchy. InterfacesAdded/InterfacesRemoved
|
||||
* signals are sent from the closest object manager at either the same path or the closest parent path of an object.
|
||||
*/
|
||||
class ObjectManager_adaptor
|
||||
{
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
|
||||
protected:
|
||||
explicit ObjectManager_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.addObjectManager();
|
||||
}
|
||||
|
||||
~ObjectManager_adaptor() = default;
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
/*!
|
||||
* @brief Managed Object Convenience Adaptor
|
||||
*
|
||||
* 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
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
|
||||
~ManagedObject_adaptor() = default;
|
||||
|
||||
public:
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesAddedSignal().
|
||||
*/
|
||||
void emitInterfacesAddedSignal()
|
||||
{
|
||||
object_.emitInterfacesAddedSignal();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesAddedSignal().
|
||||
*/
|
||||
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
|
||||
{
|
||||
object_.emitInterfacesAddedSignal(interfaces);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesRemoved signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesRemovedSignal().
|
||||
*/
|
||||
void emitInterfacesRemovedSignal()
|
||||
{
|
||||
object_.emitInterfacesRemovedSignal();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesRemoved signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesRemovedSignal().
|
||||
*/
|
||||
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
|
||||
{
|
||||
object_.emitInterfacesRemovedSignal(interfaces);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_STANDARDINTERFACES_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file TypeTraits.h
|
||||
*
|
||||
@ -40,21 +41,23 @@ namespace sdbus {
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
class Message;
|
||||
class UnixFd;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
class MethodResult;
|
||||
class PropertySetCall;
|
||||
class PropertyGetReply;
|
||||
template <typename... _Results> class Result;
|
||||
class Error;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
using method_callback = std::function<void(MethodCall& msg, MethodReply& reply)>;
|
||||
using async_method_callback = std::function<void(MethodCall& msg, MethodResult result)>;
|
||||
using method_callback = std::function<void(MethodCall msg)>;
|
||||
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
|
||||
using signal_handler = std::function<void(Signal& signal)>;
|
||||
using property_set_callback = std::function<void(Message& msg)>;
|
||||
using property_get_callback = std::function<void(Message& reply)>;
|
||||
using property_set_callback = std::function<void(PropertySetCall& msg)>;
|
||||
using property_get_callback = std::function<void(PropertyGetReply& reply)>;
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of
|
||||
@ -242,11 +245,9 @@ namespace sdbus {
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
std::initializer_list<std::string> signatures{signature_of<_ValueTypes>::str()...};
|
||||
std::string signature;
|
||||
signature += "(";
|
||||
for (const auto& item : signatures)
|
||||
signature += item;
|
||||
(signature += ... += signature_of<_ValueTypes>::str());
|
||||
signature += ")";
|
||||
return signature;
|
||||
}
|
||||
@ -285,6 +286,17 @@ namespace sdbus {
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<UnixFd>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "h";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Element>
|
||||
struct signature_of<std::vector<_Element>>
|
||||
{
|
||||
@ -366,6 +378,14 @@ namespace sdbus {
|
||||
: public function_traits_base<_ReturnType, _Args...>
|
||||
{
|
||||
static constexpr bool is_async = false;
|
||||
static constexpr bool has_error_param = false;
|
||||
};
|
||||
|
||||
template <typename... _Args>
|
||||
struct function_traits<void(const Error*, _Args...)>
|
||||
: public function_traits_base<void, _Args...>
|
||||
{
|
||||
static constexpr bool has_error_param = true;
|
||||
};
|
||||
|
||||
template <typename... _Args, typename... _Results>
|
||||
@ -373,6 +393,15 @@ namespace sdbus {
|
||||
: public function_traits_base<std::tuple<_Results...>, _Args...>
|
||||
{
|
||||
static constexpr bool is_async = true;
|
||||
using async_result_t = Result<_Results...>;
|
||||
};
|
||||
|
||||
template <typename... _Args, typename... _Results>
|
||||
struct function_traits<void(Result<_Results...>&&, _Args...)>
|
||||
: public function_traits_base<std::tuple<_Results...>, _Args...>
|
||||
{
|
||||
static constexpr bool is_async = true;
|
||||
using async_result_t = Result<_Results...>;
|
||||
};
|
||||
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
@ -416,6 +445,9 @@ namespace sdbus {
|
||||
template <class _Function>
|
||||
constexpr auto is_async_method_v = function_traits<_Function>::is_async;
|
||||
|
||||
template <class _Function>
|
||||
constexpr auto has_error_param_v = function_traits<_Function>::has_error_param;
|
||||
|
||||
template <typename _FunctionType>
|
||||
using function_arguments_t = typename function_traits<_FunctionType>::arguments_type;
|
||||
|
||||
@ -460,11 +492,8 @@ namespace sdbus {
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
// TODO: This could be a fold expression in C++17...
|
||||
std::initializer_list<std::string> signatures{signature_of<std::decay_t<_Types>>::str()...};
|
||||
std::string signature;
|
||||
for (const auto& item : signatures)
|
||||
signature += item;
|
||||
(void)(signature += ... += signature_of<std::decay_t<_Types>>::str());
|
||||
return signature;
|
||||
}
|
||||
};
|
||||
@ -489,36 +518,35 @@ namespace sdbus {
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, MethodResult&& r
|
||||
, Result<_Args...>&& r
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...> )
|
||||
{
|
||||
return std::forward<_Function>(f)(std::move(r), std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// Version of apply_impl for functions returning non-void values.
|
||||
// In this case just forward function return value.
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, const Error* e
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...>
|
||||
, std::enable_if_t<!std::is_void<function_result_t<_Function>>::value>* = nullptr)
|
||||
, std::index_sequence<_I...> )
|
||||
{
|
||||
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
return std::forward<_Function>(f)(e, std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// Version of apply_impl for functions returning void.
|
||||
// In this case, to have uniform code on the caller side, return empty tuple, our synonym for `void'.
|
||||
// For non-void returning functions, apply_impl simply returns function return value (a tuple of values).
|
||||
// For void-returning functions, apply_impl returns an empty tuple.
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...>
|
||||
, std::enable_if_t<std::is_void<function_result_t<_Function>>::value>* = nullptr)
|
||||
, std::index_sequence<_I...> )
|
||||
{
|
||||
std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
return std::tuple<>{};
|
||||
if constexpr (!std::is_void_v<function_result_t<_Function>>)
|
||||
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
else
|
||||
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...), std::tuple<>{};
|
||||
}
|
||||
}
|
||||
|
||||
@ -534,14 +562,25 @@ namespace sdbus {
|
||||
|
||||
// Convert tuple `t' of values into a list of arguments
|
||||
// and invoke function `f' with those arguments.
|
||||
template <class _Function, class _Tuple>
|
||||
constexpr decltype(auto) apply(_Function&& f, MethodResult&& r, _Tuple&& t)
|
||||
template <class _Function, class _Tuple, typename... _Args>
|
||||
constexpr decltype(auto) apply(_Function&& f, Result<_Args...>&& r, _Tuple&& t)
|
||||
{
|
||||
return detail::apply_impl( std::forward<_Function>(f)
|
||||
, std::move(r)
|
||||
, std::forward<_Tuple>(t)
|
||||
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
||||
}
|
||||
|
||||
// Convert tuple `t' of values into a list of arguments
|
||||
// and invoke function `f' with those arguments.
|
||||
template <class _Function, class _Tuple>
|
||||
constexpr decltype(auto) apply(_Function&& f, const Error* e, _Tuple&& t)
|
||||
{
|
||||
return detail::apply_impl( std::forward<_Function>(f)
|
||||
, e
|
||||
, std::forward<_Tuple>(t)
|
||||
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_TYPETRAITS_H_ */
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Types.h
|
||||
*
|
||||
@ -33,6 +34,7 @@
|
||||
#include <typeinfo>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -40,6 +42,12 @@ namespace sdbus {
|
||||
* @class Variant
|
||||
*
|
||||
* Variant can hold value of any D-Bus-supported type.
|
||||
*
|
||||
* Note: Even though thread-aware, Variant objects are not thread-safe.
|
||||
* 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.
|
||||
*
|
||||
***********************************************/
|
||||
class Variant
|
||||
@ -88,9 +96,15 @@ namespace sdbus {
|
||||
std::string peekValueType() const;
|
||||
|
||||
private:
|
||||
mutable Message msg_{};
|
||||
mutable PlainMessage msg_{};
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Struct
|
||||
*
|
||||
* Representation of struct D-Bus type
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _ValueTypes>
|
||||
class Struct
|
||||
: public std::tuple<_ValueTypes...>
|
||||
@ -129,20 +143,144 @@ namespace sdbus {
|
||||
return result_type(std::forward<_Elements>(args)...);
|
||||
}
|
||||
|
||||
/********************************************//**
|
||||
* @class ObjectPath
|
||||
*
|
||||
* Representation of object path D-Bus type
|
||||
*
|
||||
***********************************************/
|
||||
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))
|
||||
{}
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Signature
|
||||
*
|
||||
* Representation of Signature D-Bus type
|
||||
*
|
||||
***********************************************/
|
||||
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))
|
||||
{}
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
struct adopt_fd_t { explicit adopt_fd_t() = default; };
|
||||
inline constexpr adopt_fd_t adopt_fd{};
|
||||
|
||||
/********************************************//**
|
||||
* @struct UnixFd
|
||||
*
|
||||
* UnixFd is a representation of file descriptor D-Bus type that owns
|
||||
* the underlying fd, provides access to it, and closes the fd when
|
||||
* the UnixFd goes out of scope.
|
||||
*
|
||||
* UnixFd can be default constructed (owning invalid fd), or constructed from
|
||||
* an explicitly provided fd by either duplicating or adopting that fd as-is.
|
||||
*
|
||||
***********************************************/
|
||||
class UnixFd
|
||||
{
|
||||
public:
|
||||
UnixFd() = default;
|
||||
|
||||
explicit UnixFd(int fd)
|
||||
: fd_(::dup(fd))
|
||||
{
|
||||
}
|
||||
|
||||
UnixFd(int fd, adopt_fd_t)
|
||||
: fd_(fd)
|
||||
{
|
||||
}
|
||||
|
||||
UnixFd(const UnixFd& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
UnixFd& operator=(const UnixFd& other)
|
||||
{
|
||||
close();
|
||||
fd_ = ::dup(other.fd_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
UnixFd(UnixFd&& other)
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
UnixFd& operator=(UnixFd&& other)
|
||||
{
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UnixFd()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
int get() const
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
|
||||
void reset(int fd = -1)
|
||||
{
|
||||
*this = UnixFd{fd};
|
||||
}
|
||||
|
||||
void reset(int fd, adopt_fd_t)
|
||||
{
|
||||
*this = UnixFd{fd, adopt_fd};
|
||||
}
|
||||
|
||||
int release()
|
||||
{
|
||||
auto fd = fd_;
|
||||
fd_ = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return fd_ >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void close()
|
||||
{
|
||||
if (fd_ >= 0)
|
||||
::close(fd_);
|
||||
}
|
||||
|
||||
int fd_ = -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_TYPES_H_ */
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file sdbus-c++.h
|
||||
*
|
||||
@ -25,11 +26,13 @@
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <sdbus-c++/Interfaces.h>
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include <sdbus-c++/AdaptorInterfaces.h>
|
||||
#include <sdbus-c++/ProxyInterfaces.h>
|
||||
#include <sdbus-c++/StandardInterfaces.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Introspection.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
|
11
pkgconfig/sdbus-c++.pc.in
Normal file
11
pkgconfig/sdbus-c++.pc.in
Normal file
@ -0,0 +1,11 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: C++ library on top of sd-bus, a systemd D-Bus library
|
||||
Requires: libsystemd
|
||||
Version: @SDBUSCPP_VERSION@
|
||||
Libs: -L${libdir} -l@PROJECT_NAME@
|
||||
Cflags: -I${includedir}
|
@ -1,11 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: @PACKAGE@
|
||||
Description: C++ bindings library for sd-bus
|
||||
Requires: libsystemd
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lsdbus-c++
|
||||
Cflags: -I${includedir}
|
560
src/Connection.cpp
Executable file → Normal file
560
src/Connection.cpp
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Connection.cpp
|
||||
*
|
||||
@ -24,6 +25,9 @@
|
||||
*/
|
||||
|
||||
#include "Connection.h"
|
||||
#include "SdBus.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "Utils.h"
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "ScopeGuard.h"
|
||||
@ -32,39 +36,69 @@
|
||||
#include <poll.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
Connection::Connection(Connection::BusType type)
|
||||
: busType_(type)
|
||||
Connection::Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory)
|
||||
: iface_(std::move(interface))
|
||||
, bus_(openBus(busFactory))
|
||||
{
|
||||
auto bus = openBus(busType_);
|
||||
bus_.reset(bus);
|
||||
assert(iface_ != nullptr);
|
||||
}
|
||||
|
||||
finishHandshake(bus);
|
||||
Connection::Connection(std::unique_ptr<ISdBus>&& interface, default_bus_t)
|
||||
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open(bus); })
|
||||
{
|
||||
}
|
||||
|
||||
notificationFd_ = createLoopNotificationDescriptor();
|
||||
Connection::Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t)
|
||||
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open_system(bus); })
|
||||
{
|
||||
}
|
||||
|
||||
Connection::Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t)
|
||||
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open_user(bus); })
|
||||
{
|
||||
}
|
||||
|
||||
Connection::Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host)
|
||||
: Connection(std::move(interface), [this, &host](sd_bus** bus){ return iface_->sd_bus_open_system_remote(bus, host.c_str()); })
|
||||
{
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
leaveProcessingLoop();
|
||||
closeLoopNotificationDescriptor(notificationFd_);
|
||||
Connection::leaveEventLoop();
|
||||
}
|
||||
|
||||
void Connection::requestName(const std::string& name)
|
||||
{
|
||||
auto r = sd_bus_request_name(bus_.get(), name.c_str(), 0);
|
||||
SDBUS_CHECK_SERVICE_NAME(name);
|
||||
|
||||
auto r = iface_->sd_bus_request_name(bus_.get(), name.c_str(), 0);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to request bus name", -r);
|
||||
}
|
||||
|
||||
void Connection::releaseName(const std::string& name)
|
||||
{
|
||||
auto r = sd_bus_release_name(bus_.get(), name.c_str());
|
||||
auto r = iface_->sd_bus_release_name(bus_.get(), name.c_str());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to release bus name", -r);
|
||||
}
|
||||
|
||||
void Connection::enterProcessingLoop()
|
||||
std::string Connection::getUniqueName() const
|
||||
{
|
||||
const char* unique = nullptr;
|
||||
auto r = iface_->sd_bus_get_unique_name(bus_.get(), &unique);
|
||||
SDBUS_THROW_ERROR_IF(r < 0 || unique == nullptr, "Failed to get unique bus name", -r);
|
||||
return unique;
|
||||
}
|
||||
|
||||
void Connection::enterEventLoop()
|
||||
{
|
||||
loopThreadId_ = std::this_thread::get_id();
|
||||
SCOPE_EXIT{ loopThreadId_ = std::thread::id{}; };
|
||||
|
||||
std::lock_guard guard(loopMutex_);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto processed = processPendingRequest();
|
||||
@ -73,138 +107,253 @@ void Connection::enterProcessingLoop()
|
||||
|
||||
auto success = waitForNextRequest();
|
||||
if (!success)
|
||||
break; // Exit processing loop
|
||||
if (success.asyncMsgsToProcess)
|
||||
processAsynchronousMessages();
|
||||
break; // Exit I/O event loop
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::enterProcessingLoopAsync()
|
||||
void Connection::enterEventLoopAsync()
|
||||
{
|
||||
asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); });
|
||||
if (!asyncLoopThread_.joinable())
|
||||
asyncLoopThread_ = std::thread([this](){ enterEventLoop(); });
|
||||
}
|
||||
|
||||
void Connection::leaveProcessingLoop()
|
||||
void Connection::leaveEventLoop()
|
||||
{
|
||||
notifyProcessingLoopToExit();
|
||||
joinWithProcessingLoop();
|
||||
notifyEventLoopToExit();
|
||||
joinWithEventLoop();
|
||||
}
|
||||
|
||||
void* Connection::addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const void* vtable
|
||||
, void* userData )
|
||||
Connection::PollData Connection::getEventLoopPollData() const
|
||||
{
|
||||
ISdBus::PollData pollData{};
|
||||
auto r = iface_->sd_bus_get_poll_data(bus_.get(), &pollData);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r);
|
||||
|
||||
return {pollData.fd, pollData.events, pollData.timeout_usec};
|
||||
}
|
||||
|
||||
const ISdBus& Connection::getSdBusInterface() const
|
||||
{
|
||||
return *iface_.get();
|
||||
}
|
||||
|
||||
ISdBus& Connection::getSdBusInterface()
|
||||
{
|
||||
return *iface_.get();
|
||||
}
|
||||
|
||||
void Connection::addObjectManager(const std::string& objectPath)
|
||||
{
|
||||
Connection::addObjectManager(objectPath, nullptr);
|
||||
}
|
||||
|
||||
SlotPtr Connection::addObjectManager(const std::string& objectPath, void* /*dummy*/)
|
||||
{
|
||||
sd_bus_slot *slot{};
|
||||
|
||||
auto r = sd_bus_add_object_vtable( bus_.get()
|
||||
, &slot
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, static_cast<const sd_bus_vtable*>(vtable)
|
||||
, userData );
|
||||
auto r = iface_->sd_bus_add_object_manager( bus_.get()
|
||||
, &slot
|
||||
, objectPath.c_str() );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add object manager", -r);
|
||||
|
||||
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
}
|
||||
|
||||
void Connection::setMethodCallTimeout(uint64_t timeout)
|
||||
{
|
||||
auto r = iface_->sd_bus_set_method_call_timeout(bus_.get(), timeout);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set method call timeout", -r);
|
||||
}
|
||||
|
||||
uint64_t Connection::getMethodCallTimeout() const
|
||||
{
|
||||
uint64_t timeout;
|
||||
|
||||
auto r = iface_->sd_bus_get_method_call_timeout(bus_.get(), &timeout);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get method call timeout", -r);
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
SlotPtr Connection::addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData )
|
||||
{
|
||||
sd_bus_slot *slot{};
|
||||
|
||||
auto r = iface_->sd_bus_add_object_vtable( bus_.get()
|
||||
, &slot
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, vtable
|
||||
, userData );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to register object vtable", -r);
|
||||
|
||||
return slot;
|
||||
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
}
|
||||
|
||||
void Connection::removeObjectVTable(void* vtableHandle)
|
||||
PlainMessage Connection::createPlainMessage() const
|
||||
{
|
||||
sd_bus_slot_unref((sd_bus_slot *)vtableHandle);
|
||||
sd_bus_message* sdbusMsg{};
|
||||
|
||||
auto r = iface_->sd_bus_message_new(bus_.get(), &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a plain message", -r);
|
||||
|
||||
return Message::Factory::create<PlainMessage>(sdbusMsg, iface_.get(), adopt_message);
|
||||
}
|
||||
|
||||
sdbus::MethodCall Connection::createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const
|
||||
MethodCall Connection::createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const
|
||||
{
|
||||
sd_bus_message *sdbusMsg{};
|
||||
|
||||
// Returned message will become an owner of sdbusMsg
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); };
|
||||
|
||||
auto r = sd_bus_message_new_method_call( bus_.get()
|
||||
, &sdbusMsg
|
||||
, destination.c_str()
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, methodName.c_str() );
|
||||
auto r = iface_->sd_bus_message_new_method_call( bus_.get()
|
||||
, &sdbusMsg
|
||||
, destination.c_str()
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, methodName.c_str() );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
|
||||
|
||||
return MethodCall(sdbusMsg);
|
||||
return Message::Factory::create<MethodCall>(sdbusMsg, iface_.get(), this, adopt_message);
|
||||
}
|
||||
|
||||
sdbus::Signal Connection::createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const
|
||||
Signal Connection::createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const
|
||||
{
|
||||
sd_bus_message *sdbusSignal{};
|
||||
sd_bus_message *sdbusMsg{};
|
||||
|
||||
// Returned message will become an owner of sdbusSignal
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusSignal); };
|
||||
|
||||
auto r = sd_bus_message_new_signal( bus_.get()
|
||||
, &sdbusSignal
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, signalName.c_str() );
|
||||
auto r = iface_->sd_bus_message_new_signal( bus_.get()
|
||||
, &sdbusMsg
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, signalName.c_str() );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
|
||||
|
||||
return Signal(sdbusSignal);
|
||||
return Message::Factory::create<Signal>(sdbusMsg, iface_.get(), adopt_message);
|
||||
}
|
||||
|
||||
void* Connection::registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData )
|
||||
void Connection::emitPropertiesChangedSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::vector<std::string>& propNames )
|
||||
{
|
||||
auto names = to_strv(propNames);
|
||||
|
||||
auto r = iface_->sd_bus_emit_properties_changed_strv( bus_.get()
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, propNames.empty() ? nullptr : &names[0] );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit PropertiesChanged signal", -r);
|
||||
}
|
||||
|
||||
void Connection::emitInterfacesAddedSignal(const std::string& objectPath)
|
||||
{
|
||||
auto r = iface_->sd_bus_emit_object_added(bus_.get(), objectPath.c_str());
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesAdded signal for all registered interfaces", -r);
|
||||
}
|
||||
|
||||
void Connection::emitInterfacesAddedSignal( const std::string& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
{
|
||||
auto names = to_strv(interfaces);
|
||||
|
||||
auto r = iface_->sd_bus_emit_interfaces_added_strv( bus_.get()
|
||||
, objectPath.c_str()
|
||||
, interfaces.empty() ? nullptr : &names[0] );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesAdded signal", -r);
|
||||
}
|
||||
|
||||
void Connection::emitInterfacesRemovedSignal(const std::string& objectPath)
|
||||
{
|
||||
auto r = iface_->sd_bus_emit_object_removed(bus_.get(), objectPath.c_str());
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesRemoved signal for all registered interfaces", -r);
|
||||
}
|
||||
|
||||
void Connection::emitInterfacesRemovedSignal( const std::string& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
{
|
||||
auto names = to_strv(interfaces);
|
||||
|
||||
auto r = iface_->sd_bus_emit_interfaces_removed_strv( bus_.get()
|
||||
, objectPath.c_str()
|
||||
, interfaces.empty() ? nullptr : &names[0] );
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesRemoved signal", -r);
|
||||
}
|
||||
|
||||
SlotPtr Connection::registerSignalHandler( const std::string& sender
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData )
|
||||
{
|
||||
sd_bus_slot *slot{};
|
||||
|
||||
auto filter = composeSignalMatchFilter(objectPath, interfaceName, signalName);
|
||||
auto r = sd_bus_add_match(bus_.get(), &slot, filter.c_str(), callback, userData);
|
||||
// Alternatively to our own composeSignalMatchFilter() implementation, we could use sd_bus_match_signal() from
|
||||
// https://www.freedesktop.org/software/systemd/man/sd_bus_add_match.html .
|
||||
// But this would require libsystemd v237 or higher.
|
||||
auto filter = composeSignalMatchFilter(sender, objectPath, interfaceName, signalName);
|
||||
auto r = iface_->sd_bus_add_match(bus_.get(), &slot, filter.c_str(), callback, userData);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to register signal handler", -r);
|
||||
|
||||
return slot;
|
||||
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
}
|
||||
|
||||
void Connection::unregisterSignalHandler(void* handlerCookie)
|
||||
MethodReply Connection::tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout)
|
||||
{
|
||||
sd_bus_slot_unref((sd_bus_slot *)handlerCookie);
|
||||
}
|
||||
auto loopThreadId = loopThreadId_.load(std::memory_order_relaxed);
|
||||
|
||||
void Connection::sendReplyAsynchronously(const sdbus::MethodReply& reply)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
asyncReplies_.push(reply);
|
||||
notifyProcessingLoop();
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> Connection::clone() const
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(busType_);
|
||||
}
|
||||
|
||||
sd_bus* Connection::openBus(Connection::BusType type)
|
||||
{
|
||||
static std::map<sdbus::internal::Connection::BusType, int(*)(sd_bus **)> busTypeToFactory
|
||||
// Is the loop not yet on? => Go make synchronous call
|
||||
while (loopThreadId == std::thread::id{})
|
||||
{
|
||||
{sdbus::internal::Connection::BusType::eSystem, &sd_bus_open_system},
|
||||
{sdbus::internal::Connection::BusType::eSession, &sd_bus_open_user}
|
||||
};
|
||||
// Did the loop begin in the meantime? Or try_lock() failed spuriously?
|
||||
if (!loopMutex_.try_lock())
|
||||
{
|
||||
loopThreadId = loopThreadId_.load(std::memory_order_relaxed);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Synchronous D-Bus call
|
||||
std::lock_guard guard(loopMutex_, std::adopt_lock);
|
||||
return message.send(timeout);
|
||||
}
|
||||
|
||||
// Is the loop on and we are in the same thread? => Go for synchronous call
|
||||
if (loopThreadId == std::this_thread::get_id())
|
||||
{
|
||||
assert(!loopMutex_.try_lock());
|
||||
return message.send(timeout);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Connection::BusPtr Connection::openBus(const BusFactory& busFactory)
|
||||
{
|
||||
sd_bus* bus{};
|
||||
|
||||
auto r = busTypeToFactory[type](&bus);
|
||||
|
||||
int r = busFactory(&bus);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open bus", -r);
|
||||
assert(bus != nullptr);
|
||||
|
||||
return bus;
|
||||
BusPtr busPtr{bus, [this](sd_bus* bus){ return iface_->sd_bus_flush_close_unref(bus); }};
|
||||
finishHandshake(busPtr.get());
|
||||
return busPtr;
|
||||
}
|
||||
|
||||
void Connection::finishHandshake(sd_bus* bus)
|
||||
@ -215,43 +364,46 @@ void Connection::finishHandshake(sd_bus* bus)
|
||||
|
||||
assert(bus != nullptr);
|
||||
|
||||
auto r = sd_bus_flush(bus);
|
||||
auto r = iface_->sd_bus_flush(bus);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
|
||||
}
|
||||
|
||||
int Connection::createLoopNotificationDescriptor()
|
||||
void Connection::notifyEventLoop(int fd) const
|
||||
{
|
||||
auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void Connection::closeLoopNotificationDescriptor(int fd)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void Connection::notifyProcessingLoop()
|
||||
{
|
||||
assert(notificationFd_ >= 0);
|
||||
assert(fd >= 0);
|
||||
|
||||
uint64_t value = 1;
|
||||
auto r = write(notificationFd_, &value, sizeof(value));
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno);
|
||||
auto r = write(fd, &value, sizeof(value));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify event loop", -errno);
|
||||
}
|
||||
|
||||
void Connection::notifyProcessingLoopToExit()
|
||||
void Connection::notifyEventLoopToExit() const
|
||||
{
|
||||
exitLoopThread_ = true;
|
||||
|
||||
notifyProcessingLoop();
|
||||
notifyEventLoop(loopExitFd_.fd);
|
||||
}
|
||||
|
||||
void Connection::joinWithProcessingLoop()
|
||||
void Connection::notifyEventLoopNewTimeout() const
|
||||
{
|
||||
// The extra notifications for new timeouts are only needed if calls are made asynchronously to the event loop.
|
||||
// Are we in the same thread as the event loop? Note that it's ok to fail this check because the event loop isn't yet started.
|
||||
if (loopThreadId_.load(std::memory_order_relaxed) == std::this_thread::get_id())
|
||||
return;
|
||||
|
||||
// Get the new timeout from sd-bus
|
||||
auto sdbusPollData = getEventLoopPollData();
|
||||
if (sdbusPollData.timeout_usec < activeTimeout_.load(std::memory_order_relaxed))
|
||||
notifyEventLoop(eventFd_.fd);
|
||||
}
|
||||
|
||||
void Connection::clearEventLoopNotification(int fd) const
|
||||
{
|
||||
uint64_t value{};
|
||||
auto r = read(fd, &value, sizeof(value));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
|
||||
}
|
||||
|
||||
void Connection::joinWithEventLoop()
|
||||
{
|
||||
if (asyncLoopThread_.joinable())
|
||||
asyncLoopThread_.join();
|
||||
@ -260,79 +412,60 @@ void Connection::joinWithProcessingLoop()
|
||||
bool Connection::processPendingRequest()
|
||||
{
|
||||
auto bus = bus_.get();
|
||||
|
||||
assert(bus != nullptr);
|
||||
|
||||
int r = sd_bus_process(bus, nullptr);
|
||||
|
||||
int r = iface_->sd_bus_process(bus, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to process bus requests", -r);
|
||||
|
||||
return r > 0;
|
||||
}
|
||||
|
||||
void Connection::processAsynchronousMessages()
|
||||
bool Connection::waitForNextRequest()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
while (!asyncReplies_.empty())
|
||||
{
|
||||
auto reply = asyncReplies_.front();
|
||||
asyncReplies_.pop();
|
||||
reply.send();
|
||||
}
|
||||
}
|
||||
assert(bus_ != nullptr);
|
||||
assert(eventFd_.fd >= 0);
|
||||
|
||||
Connection::WaitResult Connection::waitForNextRequest()
|
||||
{
|
||||
auto bus = bus_.get();
|
||||
|
||||
assert(bus != nullptr);
|
||||
assert(notificationFd_ != 0);
|
||||
|
||||
auto r = sd_bus_get_fd(bus);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r);
|
||||
auto sdbusFd = r;
|
||||
|
||||
r = sd_bus_get_events(bus);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus events", -r);
|
||||
short int sdbusEvents = r;
|
||||
|
||||
uint64_t usec;
|
||||
sd_bus_get_timeout(bus, &usec);
|
||||
|
||||
struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {notificationFd_, POLLIN, 0}};
|
||||
auto sdbusPollData = getEventLoopPollData();
|
||||
struct pollfd fds[] = {
|
||||
{sdbusPollData.fd, sdbusPollData.events, 0},
|
||||
{eventFd_.fd, POLLIN, 0},
|
||||
{loopExitFd_.fd, POLLIN, 0}
|
||||
};
|
||||
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
||||
|
||||
r = poll(fds, fdsCount, usec == (uint64_t) -1 ? -1 : (usec+999)/1000);
|
||||
auto timeout = sdbusPollData.getPollTimeout();
|
||||
activeTimeout_.store(sdbusPollData.timeout_usec, std::memory_order_relaxed);
|
||||
auto r = poll(fds, fdsCount, timeout);
|
||||
|
||||
if (r < 0 && errno == EINTR)
|
||||
return {true, false}; // Try again
|
||||
return true; // Try again
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
|
||||
|
||||
// new timeout notification
|
||||
if (fds[1].revents & POLLIN)
|
||||
{
|
||||
if (exitLoopThread_)
|
||||
return {false, false}; // Got exit notification
|
||||
|
||||
// Otherwise we have some async messages to process
|
||||
|
||||
uint64_t value{};
|
||||
auto r = read(notificationFd_, &value, sizeof(value));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
|
||||
|
||||
return {false, true};
|
||||
clearEventLoopNotification(fds[1].fd);
|
||||
}
|
||||
// loop exit notification
|
||||
if (fds[2].revents & POLLIN)
|
||||
{
|
||||
clearEventLoopNotification(fds[2].fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return {true, false};
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Connection::composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName )
|
||||
std::string Connection::composeSignalMatchFilter(const std::string &sender,
|
||||
const std::string &objectPath,
|
||||
const std::string &interfaceName,
|
||||
const std::string &signalName)
|
||||
{
|
||||
std::string filter;
|
||||
|
||||
filter += "type='signal',";
|
||||
filter += "sender='" + sender + "',";
|
||||
filter += "interface='" + interfaceName + "',";
|
||||
filter += "member='" + signalName + "',";
|
||||
filter += "path='" + objectPath + "'";
|
||||
@ -340,7 +473,69 @@ std::string Connection::composeSignalMatchFilter( const std::string& objectPath
|
||||
return filter;
|
||||
}
|
||||
|
||||
}}
|
||||
std::vector</*const */char*> Connection::to_strv(const std::vector<std::string>& strings)
|
||||
{
|
||||
std::vector</*const */char*> strv;
|
||||
for (auto& str : strings)
|
||||
strv.push_back(const_cast<char*>(str.c_str()));
|
||||
strv.push_back(nullptr);
|
||||
return strv;
|
||||
}
|
||||
|
||||
Connection::EventFd::EventFd()
|
||||
{
|
||||
fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
SDBUS_THROW_ERROR_IF(fd < 0, "Failed to create event object", -errno);
|
||||
}
|
||||
|
||||
Connection::EventFd::~EventFd()
|
||||
{
|
||||
assert(fd >= 0);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
} // namespace sdbus::internal
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::optional<std::chrono::microseconds> IConnection::PollData::getRelativeTimeout() const
|
||||
{
|
||||
constexpr auto zero = std::chrono::microseconds::zero();
|
||||
if (timeout_usec == 0)
|
||||
return zero;
|
||||
else if (timeout_usec == UINT64_MAX)
|
||||
return std::nullopt;
|
||||
|
||||
// We need C so that we use the same clock as the underlying sd-bus lib.
|
||||
// We use POSIX's clock_gettime in favour of std::chrono::steady_clock to ensure this.
|
||||
struct timespec ts{};
|
||||
auto r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "clock_gettime failed: ", -errno);
|
||||
auto now = std::chrono::nanoseconds(ts.tv_nsec) + std::chrono::seconds(ts.tv_sec);
|
||||
auto absTimeout = std::chrono::microseconds(timeout_usec);
|
||||
auto result = std::chrono::duration_cast<std::chrono::microseconds>(absTimeout - now);
|
||||
return std::max(result, zero);
|
||||
}
|
||||
|
||||
int IConnection::PollData::getPollTimeout() const
|
||||
{
|
||||
auto timeout = getRelativeTimeout();
|
||||
return timeout ? static_cast<int>(std::chrono::ceil<std::chrono::milliseconds>(timeout.value()).count()) : -1;
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> createConnection()
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
SCOPE_EXIT{ connection.release(); };
|
||||
auto connectionInternal = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
return std::unique_ptr<sdbus::internal::IConnection>(connectionInternal);
|
||||
}
|
||||
|
||||
} // namespace sdbus::internal
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -354,9 +549,25 @@ std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name)
|
||||
return createSystemBusConnection(name);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createDefaultBusConnection()
|
||||
{
|
||||
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||
constexpr sdbus::internal::Connection::default_bus_t default_bus;
|
||||
return std::make_unique<sdbus::internal::Connection>(std::move(interface), default_bus);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createDefaultBusConnection(const std::string& name)
|
||||
{
|
||||
auto conn = createDefaultBusConnection();
|
||||
conn->requestName(name);
|
||||
return conn;
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection()
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSystem);
|
||||
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||
constexpr sdbus::internal::Connection::system_bus_t system_bus;
|
||||
return std::make_unique<sdbus::internal::Connection>(std::move(interface), system_bus);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name)
|
||||
@ -368,7 +579,9 @@ std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string&
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection()
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSession);
|
||||
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||
constexpr sdbus::internal::Connection::session_bus_t session_bus;
|
||||
return std::make_unique<sdbus::internal::Connection>(std::move(interface), session_bus);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name)
|
||||
@ -378,4 +591,11 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string
|
||||
return conn;
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host)
|
||||
{
|
||||
auto interface = std::make_unique<sdbus::internal::SdBus>();
|
||||
constexpr sdbus::internal::Connection::remote_system_bus_t remote_system_bus;
|
||||
return std::make_unique<sdbus::internal::Connection>(std::move(interface), remote_system_bus, host);
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
153
src/Connection.h
Executable file → Normal file
153
src/Connection.h
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Connection.h
|
||||
*
|
||||
@ -29,96 +30,124 @@
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include "IConnection.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include "ISdBus.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Connection
|
||||
class Connection final
|
||||
: public sdbus::IConnection // External, public interface
|
||||
, public sdbus::internal::IConnection // Internal, private interface
|
||||
{
|
||||
public:
|
||||
enum class BusType
|
||||
{
|
||||
eSystem,
|
||||
eSession
|
||||
};
|
||||
// Bus type tags
|
||||
struct default_bus_t{};
|
||||
struct system_bus_t{};
|
||||
struct session_bus_t{};
|
||||
struct remote_system_bus_t{};
|
||||
|
||||
Connection(BusType type);
|
||||
~Connection();
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, default_bus_t);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host);
|
||||
~Connection() override;
|
||||
|
||||
void requestName(const std::string& name) override;
|
||||
void releaseName(const std::string& name) override;
|
||||
void enterProcessingLoop() override;
|
||||
void enterProcessingLoopAsync() override;
|
||||
void leaveProcessingLoop() override;
|
||||
std::string getUniqueName() const override;
|
||||
void enterEventLoop() override;
|
||||
void enterEventLoopAsync() override;
|
||||
void leaveEventLoop() override;
|
||||
PollData getEventLoopPollData() const override;
|
||||
bool processPendingRequest() override;
|
||||
|
||||
void* addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const void* vtable
|
||||
, void* userData ) override;
|
||||
void removeObjectVTable(void* vtableHandle) override;
|
||||
void addObjectManager(const std::string& objectPath) override;
|
||||
SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/) override;
|
||||
|
||||
sdbus::MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const override;
|
||||
sdbus::Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const override;
|
||||
void setMethodCallTimeout(uint64_t timeout) override;
|
||||
uint64_t getMethodCallTimeout() const override;
|
||||
|
||||
void* registerSignalHandler( const std::string& objectPath
|
||||
const ISdBus& getSdBusInterface() const override;
|
||||
ISdBus& getSdBusInterface() override;
|
||||
|
||||
SlotPtr addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData ) override;
|
||||
|
||||
PlainMessage createPlainMessage() const override;
|
||||
MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) override;
|
||||
void unregisterSignalHandler(void* handlerCookie) override;
|
||||
, const std::string& methodName ) const override;
|
||||
Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const override;
|
||||
|
||||
void sendReplyAsynchronously(const sdbus::MethodReply& reply) 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;
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> clone() const override;
|
||||
SlotPtr registerSignalHandler( const std::string& sender
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) override;
|
||||
|
||||
MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) override;
|
||||
|
||||
private:
|
||||
struct WaitResult
|
||||
using BusFactory = std::function<int(sd_bus**)>;
|
||||
using BusPtr = std::unique_ptr<sd_bus, std::function<sd_bus*(sd_bus*)>>;
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory);
|
||||
|
||||
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
|
||||
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;
|
||||
|
||||
private:
|
||||
void joinWithEventLoop();
|
||||
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
|
||||
|
||||
struct EventFd
|
||||
{
|
||||
bool msgsToProcess;
|
||||
bool asyncMsgsToProcess;
|
||||
operator bool()
|
||||
{
|
||||
return msgsToProcess || asyncMsgsToProcess;
|
||||
}
|
||||
EventFd();
|
||||
~EventFd();
|
||||
int fd{-1};
|
||||
};
|
||||
static sd_bus* openBus(Connection::BusType type);
|
||||
static void finishHandshake(sd_bus* bus);
|
||||
static int createLoopNotificationDescriptor();
|
||||
static void closeLoopNotificationDescriptor(int fd);
|
||||
bool processPendingRequest();
|
||||
void processAsynchronousMessages();
|
||||
WaitResult waitForNextRequest();
|
||||
static std::string composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName );
|
||||
void notifyProcessingLoop();
|
||||
void notifyProcessingLoopToExit();
|
||||
void joinWithProcessingLoop();
|
||||
|
||||
private:
|
||||
std::unique_ptr<sd_bus, decltype(&sd_bus_flush_close_unref)> bus_{nullptr, &sd_bus_flush_close_unref};
|
||||
std::unique_ptr<ISdBus> iface_;
|
||||
BusPtr bus_;
|
||||
std::thread asyncLoopThread_;
|
||||
std::mutex mutex_;
|
||||
std::queue<MethodReply> asyncReplies_;
|
||||
std::atomic<bool> exitLoopThread_;
|
||||
int notificationFd_{-1};
|
||||
BusType busType_;
|
||||
|
||||
static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000;
|
||||
std::atomic<std::thread::id> loopThreadId_;
|
||||
std::mutex loopMutex_;
|
||||
EventFd loopExitFd_;
|
||||
EventFd eventFd_;
|
||||
std::atomic<uint64_t> activeTimeout_{};
|
||||
};
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_CONNECTION_H_ */
|
||||
|
@ -1,150 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ConvenienceClasses.cpp
|
||||
*
|
||||
* Created on: Jan 19, 2017
|
||||
* 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++/ConvenienceClasses.h>
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus signal", EINVAL);
|
||||
|
||||
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerSignal(interfaceName_, signalName_, signalSignature_);
|
||||
}
|
||||
|
||||
|
||||
PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
|
||||
: object_(object)
|
||||
, propertyName_(propertyName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus property", EINVAL);
|
||||
|
||||
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerProperty() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerProperty( std::move(interfaceName_)
|
||||
, std::move(propertyName_)
|
||||
, std::move(propertySignature_)
|
||||
, std::move(getter_)
|
||||
, std::move(setter_) );
|
||||
}
|
||||
|
||||
|
||||
SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't emit the signal if SignalEmitter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!signal_.isValid(), "DBus interface not specified when emitting a DBus signal", EINVAL);
|
||||
|
||||
// emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow emitSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.emitSignal(signal_);
|
||||
}
|
||||
|
||||
|
||||
MethodInvoker::MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName)
|
||||
: objectProxy_(objectProxy)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't call the method if it has been called already or if MethodInvoker
|
||||
// threw an exception in one of its methods
|
||||
if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
|
||||
// callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow callMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
objectProxy_.callMethod(method_);
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Error.cpp
|
||||
*
|
||||
|
112
src/Flags.cpp
Normal file
112
src/Flags.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Flags.cpp
|
||||
*
|
||||
* Created on: Dec 31, 2018
|
||||
* 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++/Flags.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
uint64_t Flags::toSdBusInterfaceFlags() const
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
if (!flags_.test(Flags::PRIVILEGED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_UNPRIVILEGED;
|
||||
|
||||
if (flags_.test(Flags::EMITS_CHANGE_SIGNAL))
|
||||
sdbusFlags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
|
||||
else if (flags_.test(Flags::EMITS_INVALIDATION_SIGNAL))
|
||||
sdbusFlags |= SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION;
|
||||
else if (flags_.test(Flags::CONST_PROPERTY_VALUE))
|
||||
sdbusFlags |= SD_BUS_VTABLE_PROPERTY_CONST;
|
||||
else if (flags_.test(Flags::EMITS_NO_SIGNAL))
|
||||
sdbusFlags |= 0;
|
||||
|
||||
return sdbusFlags;
|
||||
}
|
||||
|
||||
uint64_t Flags::toSdBusMethodFlags() const
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
if (!flags_.test(Flags::PRIVILEGED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_UNPRIVILEGED;
|
||||
if (flags_.test(Flags::METHOD_NO_REPLY))
|
||||
sdbusFlags |= SD_BUS_VTABLE_METHOD_NO_REPLY;
|
||||
|
||||
return sdbusFlags;
|
||||
}
|
||||
|
||||
uint64_t Flags::toSdBusSignalFlags() const
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
|
||||
return sdbusFlags;
|
||||
}
|
||||
|
||||
uint64_t Flags::toSdBusPropertyFlags() const
|
||||
{
|
||||
uint64_t sdbusFlags{};
|
||||
|
||||
using namespace sdbus;
|
||||
if (flags_.test(Flags::DEPRECATED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
|
||||
//if (!flags_.test(Flags::PRIVILEGED))
|
||||
// sdbusFlags |= SD_BUS_VTABLE_UNPRIVILEGED;
|
||||
|
||||
if (flags_.test(Flags::EMITS_CHANGE_SIGNAL))
|
||||
sdbusFlags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE;
|
||||
else if (flags_.test(Flags::EMITS_INVALIDATION_SIGNAL))
|
||||
sdbusFlags |= SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION;
|
||||
else if (flags_.test(Flags::CONST_PROPERTY_VALUE))
|
||||
sdbusFlags |= SD_BUS_VTABLE_PROPERTY_CONST;
|
||||
else if (flags_.test(Flags::EMITS_NO_SIGNAL))
|
||||
sdbusFlags |= 0;
|
||||
|
||||
return sdbusFlags;
|
||||
}
|
||||
|
||||
uint64_t Flags::toSdBusWritablePropertyFlags() const
|
||||
{
|
||||
auto sdbusFlags = toSdBusPropertyFlags();
|
||||
|
||||
using namespace sdbus;
|
||||
if (!flags_.test(Flags::PRIVILEGED))
|
||||
sdbusFlags |= SD_BUS_VTABLE_UNPRIVILEGED;
|
||||
|
||||
return sdbusFlags;
|
||||
}
|
||||
}
|
87
src/IConnection.h
Executable file → Normal file
87
src/IConnection.h
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file IConnection.h
|
||||
*
|
||||
@ -29,53 +30,73 @@
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
// Forward declaration
|
||||
namespace sdbus {
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
class PlainMessage;
|
||||
namespace internal {
|
||||
class ISdBus;
|
||||
}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
using SlotPtr = std::unique_ptr<void, std::function<void(void*)>>;
|
||||
|
||||
class IConnection
|
||||
{
|
||||
public:
|
||||
virtual void* addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const void* vtable
|
||||
, void* userData ) = 0;
|
||||
virtual void removeObjectVTable(void* vtableHandle) = 0;
|
||||
|
||||
virtual sdbus::MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
|
||||
virtual sdbus::Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
|
||||
virtual void* registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) = 0;
|
||||
virtual void unregisterSignalHandler(void* handlerCookie) = 0;
|
||||
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
|
||||
virtual void sendReplyAsynchronously(const sdbus::MethodReply& reply) = 0;
|
||||
|
||||
virtual std::unique_ptr<sdbus::internal::IConnection> clone() const = 0;
|
||||
|
||||
virtual ~IConnection() = default;
|
||||
|
||||
virtual const ISdBus& getSdBusInterface() const = 0;
|
||||
virtual ISdBus& getSdBusInterface() = 0;
|
||||
|
||||
[[nodiscard]] virtual SlotPtr addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData ) = 0;
|
||||
|
||||
virtual PlainMessage createPlainMessage() const = 0;
|
||||
virtual MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
virtual Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
|
||||
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;
|
||||
|
||||
[[nodiscard]] virtual SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/ = nullptr) = 0;
|
||||
|
||||
[[nodiscard]] virtual SlotPtr registerSignalHandler( const std::string& sender
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) = 0;
|
||||
|
||||
virtual void enterEventLoopAsync() = 0;
|
||||
virtual void leaveEventLoop() = 0;
|
||||
virtual void notifyEventLoopNewTimeout() const = 0;
|
||||
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createConnection();
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_ICONNECTION_H_ */
|
||||
|
103
src/ISdBus.h
Normal file
103
src/ISdBus.h
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ISdBus.h
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
*
|
||||
* Created on: Mar 12, 2019
|
||||
* 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_ISDBUS_H
|
||||
#define SDBUS_CXX_ISDBUS_H
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
class ISdBus
|
||||
{
|
||||
public:
|
||||
struct PollData
|
||||
{
|
||||
int fd;
|
||||
short int events;
|
||||
uint64_t timeout_usec;
|
||||
};
|
||||
|
||||
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 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_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_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;
|
||||
|
||||
virtual int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) = 0;
|
||||
virtual int sd_bus_emit_object_added(sd_bus *bus, const char *path) = 0;
|
||||
virtual int sd_bus_emit_object_removed(sd_bus *bus, const char *path) = 0;
|
||||
virtual int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) = 0;
|
||||
virtual int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) = 0;
|
||||
|
||||
virtual int sd_bus_open(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_open_user(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_open_system(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 0;
|
||||
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0;
|
||||
virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0;
|
||||
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) = 0;
|
||||
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) = 0;
|
||||
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 sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
|
||||
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0;
|
||||
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0;
|
||||
|
||||
virtual int sd_bus_flush(sd_bus *bus) = 0;
|
||||
virtual sd_bus *sd_bus_flush_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_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_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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //SDBUS_CXX_ISDBUS_H
|
@ -1,27 +0,0 @@
|
||||
|
||||
lib_LTLIBRARIES = libsdbus-c++.la
|
||||
|
||||
libsdbus_c___la_SOURCES = \
|
||||
Connection.cpp \
|
||||
ConvenienceClasses.cpp \
|
||||
Message.cpp \
|
||||
MethodResult.cpp \
|
||||
Object.cpp \
|
||||
ObjectProxy.cpp \
|
||||
Types.cpp \
|
||||
Error.cpp \
|
||||
VTableUtils.c
|
||||
|
||||
libsdbus_c___la_LIBADD = @SYSTEMD_LIBS@
|
||||
|
||||
# Setting per-file flags
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
AM_CXXFLAGS = @libsdbus_cpp_CFLAGS@ @SYSTEMD_CFLAGS@ -std=c++17 -pipe -pedantic -W -Wall
|
||||
AM_LDFLAGS = @libsdbus_cpp_LIBS@ @SYSTEMD_LIBS@
|
||||
|
||||
libsdbus_c___la_LDFLAGS = -version-info 0:0:0
|
||||
|
||||
# Cleaning
|
||||
CLEANFILES = *~ *.lo *.la
|
||||
MOSTLYCLEANFILES = *.o
|
||||
DISTCLEANFILES = Makefile Makefile.in
|
319
src/Message.cpp
Executable file → Normal file
319
src/Message.cpp
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Message.cpp
|
||||
*
|
||||
@ -27,17 +28,35 @@
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "MessageUtils.h"
|
||||
#include "ISdBus.h"
|
||||
#include "IConnection.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
Message::Message(void *msg) noexcept
|
||||
Message::Message(internal::ISdBus* sdbus) noexcept
|
||||
: sdbus_(sdbus)
|
||||
{
|
||||
assert(sdbus_ != nullptr);
|
||||
}
|
||||
|
||||
Message::Message(void *msg, internal::ISdBus* sdbus) noexcept
|
||||
: msg_(msg)
|
||||
, sdbus_(sdbus)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
assert(sdbus_ != nullptr);
|
||||
sdbus_->sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message::Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept
|
||||
: msg_(msg)
|
||||
, sdbus_(sdbus)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
assert(sdbus_ != nullptr);
|
||||
}
|
||||
|
||||
Message::Message(const Message& other) noexcept
|
||||
@ -48,12 +67,13 @@ Message::Message(const Message& other) noexcept
|
||||
Message& Message::operator=(const Message& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
sdbus_ = other.sdbus_;
|
||||
ok_ = other.ok_;
|
||||
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
sdbus_->sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -66,10 +86,12 @@ Message::Message(Message&& other) noexcept
|
||||
Message& Message::operator=(Message&& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
other.msg_ = nullptr;
|
||||
sdbus_ = other.sdbus_;
|
||||
other.sdbus_ = nullptr;
|
||||
ok_ = other.ok_;
|
||||
other.ok_ = true;
|
||||
|
||||
@ -79,7 +101,7 @@ Message& Message::operator=(Message&& other) noexcept
|
||||
Message::~Message()
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message& Message::operator<<(bool item)
|
||||
@ -190,7 +212,16 @@ 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());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an Signature value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a Signature value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a UnixFd value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -363,6 +394,20 @@ Message& Message::operator>>(Signature &item)
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a UnixFd value", -r);
|
||||
|
||||
item.reset(fd);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Message& Message::openContainer(const std::string& signature)
|
||||
{
|
||||
@ -538,12 +583,31 @@ void Message::rewind(bool complete)
|
||||
|
||||
std::string Message::getInterfaceName() const
|
||||
{
|
||||
return sd_bus_message_get_interface((sd_bus_message*)msg_);
|
||||
auto interface = sd_bus_message_get_interface((sd_bus_message*)msg_);
|
||||
return interface != nullptr ? interface : "";
|
||||
}
|
||||
|
||||
std::string Message::getMemberName() const
|
||||
{
|
||||
return sd_bus_message_get_member((sd_bus_message*)msg_);
|
||||
auto member = sd_bus_message_get_member((sd_bus_message*)msg_);
|
||||
return member != nullptr ? member : "";
|
||||
}
|
||||
|
||||
std::string Message::getSender() const
|
||||
{
|
||||
return sd_bus_message_get_sender((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
std::string Message::getPath() const
|
||||
{
|
||||
auto path = sd_bus_message_get_path((sd_bus_message*)msg_);
|
||||
return path != nullptr ? path : "";
|
||||
}
|
||||
|
||||
std::string Message::getDestination() const
|
||||
{
|
||||
auto destination = sd_bus_message_get_destination((sd_bus_message*)msg_);
|
||||
return destination != nullptr ? destination : "";
|
||||
}
|
||||
|
||||
void Message::peekType(std::string& type, std::string& contents) const
|
||||
@ -553,54 +617,213 @@ void Message::peekType(std::string& type, std::string& contents) const
|
||||
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r);
|
||||
type = typeSig;
|
||||
contents = contentsSig;
|
||||
contents = contentsSig ? contentsSig : "";
|
||||
}
|
||||
|
||||
bool Message::isValid() const
|
||||
{
|
||||
return msg_ != nullptr;
|
||||
return msg_ != nullptr && sdbus_ != nullptr;
|
||||
}
|
||||
|
||||
bool Message::isEmpty() const
|
||||
{
|
||||
return sd_bus_message_is_empty((sd_bus_message*)msg_);
|
||||
return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0;
|
||||
}
|
||||
|
||||
void* Message::getMsg() const
|
||||
pid_t Message::getCredsPid() const
|
||||
{
|
||||
return msg_;
|
||||
uint64_t mask = SD_BUS_CREDS_PID | 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);
|
||||
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);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred pid", -r);
|
||||
return pid;
|
||||
}
|
||||
|
||||
MethodReply MethodCall::send() const
|
||||
uid_t Message::getCredsUid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_UID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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]);
|
||||
}
|
||||
|
||||
return gids;
|
||||
}
|
||||
|
||||
std::string Message::getSELinuxContext() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_AUGMENT | SD_BUS_CREDS_SELINUX_CONTEXT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
|
||||
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
|
||||
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);
|
||||
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
|
||||
, adopt_message_t) noexcept
|
||||
: Message(msg, sdbus, adopt_message)
|
||||
, connection_(connection)
|
||||
{
|
||||
assert(connection_ != nullptr);
|
||||
}
|
||||
|
||||
void MethodCall::dontExpectReply()
|
||||
{
|
||||
auto r = sd_bus_message_set_expect_reply((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_);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get the dont-expect-reply flag", -r);
|
||||
return r == 0;
|
||||
}
|
||||
|
||||
MethodReply MethodCall::send(uint64_t timeout) const
|
||||
{
|
||||
if (!doesntExpectReply())
|
||||
return sendWithReply(timeout);
|
||||
else
|
||||
return sendWithNoReply();
|
||||
}
|
||||
|
||||
MethodReply MethodCall::sendWithReply(uint64_t timeout) const
|
||||
{
|
||||
sd_bus_message* sdbusReply{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
|
||||
auto r = sd_bus_call(nullptr, (sd_bus_message*)getMsg(), 0, &sdbusError, &sdbusReply);
|
||||
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 MethodReply(sdbusReply);
|
||||
return Factory::create<MethodReply>(sdbusReply, sdbus_, 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);
|
||||
|
||||
return Factory::create<MethodReply>(); // No reply
|
||||
}
|
||||
|
||||
void MethodCall::send(void* callback, void* userData, uint64_t timeout, dont_request_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();
|
||||
}
|
||||
|
||||
MethodCall::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{slot, [sdbus_ = sdbus_](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
|
||||
}
|
||||
|
||||
MethodReply MethodCall::createReply() const
|
||||
{
|
||||
sd_bus_message *sdbusReply{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply
|
||||
|
||||
auto r = sd_bus_message_new_method_return((sd_bus_message*)getMsg(), &sdbusReply);
|
||||
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);
|
||||
|
||||
assert(sdbusReply != nullptr);
|
||||
|
||||
return MethodReply(sdbusReply);
|
||||
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::createErrorReply(const Error& error) const
|
||||
@ -609,44 +832,36 @@ MethodReply MethodCall::createErrorReply(const Error& error) const
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str());
|
||||
|
||||
sd_bus_message *sdbusErrorReply{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusErrorReply); }; // Returned message will become an owner of sdbusErrorReply
|
||||
|
||||
auto r = sd_bus_message_new_method_error((sd_bus_message*)getMsg(), &sdbusErrorReply, &sdbusError);
|
||||
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);
|
||||
|
||||
assert(sdbusErrorReply != nullptr);
|
||||
|
||||
return MethodReply(sdbusErrorReply);
|
||||
return Factory::create<MethodReply>(sdbusErrorReply, sdbus_, adopt_message);
|
||||
}
|
||||
|
||||
void MethodReply::send() const
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), nullptr);
|
||||
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
|
||||
}
|
||||
|
||||
void Signal::send() const
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), nullptr);
|
||||
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
|
||||
}
|
||||
|
||||
Message createPlainMessage()
|
||||
void Signal::setDestination(const std::string& destination)
|
||||
{
|
||||
int r;
|
||||
|
||||
sd_bus* bus{};
|
||||
SCOPE_EXIT{ sd_bus_unref(bus); }; // sdbusMsg will hold reference to the bus
|
||||
r = sd_bus_default_system(&bus);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get default system bus", -r);
|
||||
|
||||
sd_bus_message* sdbusMsg{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); }; // Returned message will become an owner of sdbusMsg
|
||||
r = sd_bus_message_new(bus, &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a new message", -r);
|
||||
|
||||
return Message(sdbusMsg);
|
||||
auto r = sdbus_->sd_bus_message_set_destination((sd_bus_message*)msg_, destination.c_str());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
|
||||
}
|
||||
|
||||
PlainMessage createPlainMessage()
|
||||
{
|
||||
static auto connection = internal::createConnection();
|
||||
return connection->createPlainMessage();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file MessageUtils.h
|
||||
*
|
||||
@ -30,7 +31,41 @@
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
Message createPlainMessage();
|
||||
class Message::Factory
|
||||
{
|
||||
public:
|
||||
template<typename _Msg>
|
||||
static _Msg create()
|
||||
{
|
||||
return _Msg{};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg)
|
||||
{
|
||||
return _Msg{msg};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg, internal::ISdBus* sdbus)
|
||||
{
|
||||
return _Msg{msg, sdbus};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg, internal::ISdBus* sdbus, 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};
|
||||
}
|
||||
};
|
||||
|
||||
PlainMessage createPlainMessage();
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */
|
||||
|
317
src/Object.cpp
Executable file → Normal file
317
src/Object.cpp
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Object.cpp
|
||||
*
|
||||
@ -24,106 +25,136 @@
|
||||
*/
|
||||
|
||||
#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 "IConnection.h"
|
||||
#include "Utils.h"
|
||||
#include "VTableUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
|
||||
: connection_(connection), objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_);
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback )
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, std::string outputSignature
|
||||
, method_callback methodCallback
|
||||
, Flags flags )
|
||||
{
|
||||
registerMethod( interfaceName
|
||||
, std::move(methodName)
|
||||
, std::move(inputSignature)
|
||||
, {}
|
||||
, std::move(outputSignature)
|
||||
, {}
|
||||
, std::move(methodCallback)
|
||||
, std::move(flags) );
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, const std::vector<std::string>& inputNames
|
||||
, std::string outputSignature
|
||||
, const std::vector<std::string>& outputNames
|
||||
, method_callback methodCallback
|
||||
, Flags flags )
|
||||
{
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
|
||||
SDBUS_CHECK_MEMBER_NAME(methodName);
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
||||
|
||||
auto syncCallback = [callback = std::move(methodCallback)](MethodCall& msg)
|
||||
{
|
||||
auto reply = msg.createReply();
|
||||
callback(msg, reply);
|
||||
reply.send();
|
||||
};
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(syncCallback)};
|
||||
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, async_method_callback asyncMethodCallback )
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!asyncMethodCallback, "Invalid method callback provided", EINVAL);
|
||||
|
||||
auto asyncCallback = [this, callback = std::move(asyncMethodCallback)](MethodCall& msg)
|
||||
{
|
||||
MethodResult result{msg, *this};
|
||||
callback(msg, result);
|
||||
};
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(asyncCallback)};
|
||||
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
||||
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;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature )
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, Flags flags )
|
||||
{
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
registerSignal(interfaceName, std::move(signalName), std::move(signature), {}, std::move(flags));
|
||||
}
|
||||
|
||||
InterfaceData::SignalData signalData{signature};
|
||||
auto inserted = interface.signals_.emplace(signalName, std::move(signalData)).second;
|
||||
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
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback )
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, Flags flags )
|
||||
{
|
||||
registerProperty( interfaceName
|
||||
, propertyName
|
||||
, signature
|
||||
, getCallback
|
||||
, property_set_callback{} );
|
||||
, std::move(propertyName)
|
||||
, std::move(signature)
|
||||
, std::move(getCallback)
|
||||
, {}
|
||||
, std::move(flags) );
|
||||
}
|
||||
|
||||
void Object::registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback )
|
||||
, 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 = interfaces_[interfaceName];
|
||||
auto& interface = getInterface(interfaceName);
|
||||
|
||||
InterfaceData::PropertyData propertyData{signature, std::move(getCallback), std::move(setCallback)};
|
||||
auto inserted = interface.properties_.emplace(propertyName, std::move(propertyData)).second;
|
||||
InterfaceData::PropertyData propertyData{ std::move(signature)
|
||||
, std::move(getCallback)
|
||||
, std::move(setCallback)
|
||||
, std::move(flags) };
|
||||
auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
|
||||
{
|
||||
auto& interface = getInterface(interfaceName);
|
||||
interface.flags = flags;
|
||||
}
|
||||
|
||||
void Object::finishRegistration()
|
||||
{
|
||||
for (auto& item : interfaces_)
|
||||
@ -136,6 +167,12 @@ void Object::finishRegistration()
|
||||
}
|
||||
}
|
||||
|
||||
void Object::unregister()
|
||||
{
|
||||
interfaces_.clear();
|
||||
removeObjectManager();
|
||||
}
|
||||
|
||||
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
|
||||
{
|
||||
return connection_.createSignal(objectPath_, interfaceName, signalName);
|
||||
@ -143,23 +180,82 @@ sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::
|
||||
|
||||
void Object::emitSignal(const sdbus::Signal& message)
|
||||
{
|
||||
// TODO: Make signal emitting asynchronous. Now signal can probably be emitted only from user code
|
||||
// handled within the D-Bus processing loop thread, but not from any thread. In principle it will
|
||||
// be the same as async replies.
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
|
||||
|
||||
message.send();
|
||||
}
|
||||
|
||||
void Object::sendReplyAsynchronously(const MethodReply& reply)
|
||||
void Object::emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames)
|
||||
{
|
||||
connection_.sendReplyAsynchronously(reply);
|
||||
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const std::string& interfaceName)
|
||||
{
|
||||
Object::emitPropertiesChangedSignal(interfaceName, {});
|
||||
}
|
||||
|
||||
void Object::emitInterfacesAddedSignal()
|
||||
{
|
||||
connection_.emitInterfacesAddedSignal(objectPath_);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
|
||||
{
|
||||
connection_.emitInterfacesAddedSignal(objectPath_, interfaces);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesRemovedSignal()
|
||||
{
|
||||
connection_.emitInterfacesRemovedSignal(objectPath_);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
|
||||
{
|
||||
connection_.emitInterfacesRemovedSignal(objectPath_, interfaces);
|
||||
}
|
||||
|
||||
void Object::addObjectManager()
|
||||
{
|
||||
objectManagerSlot_ = connection_.addObjectManager(objectPath_);
|
||||
}
|
||||
|
||||
void Object::removeObjectManager()
|
||||
{
|
||||
objectManagerSlot_.reset();
|
||||
}
|
||||
|
||||
bool Object::hasObjectManager() const
|
||||
{
|
||||
return objectManagerSlot_ != nullptr;
|
||||
}
|
||||
|
||||
sdbus::IConnection& Object::getConnection() const
|
||||
{
|
||||
return dynamic_cast<sdbus::IConnection&>(connection_);
|
||||
}
|
||||
|
||||
const std::string& Object::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
const Message* Object::getCurrentlyProcessedMessage() const
|
||||
{
|
||||
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
Object::InterfaceData& Object::getInterface(const std::string& interfaceName)
|
||||
{
|
||||
return interfaces_.emplace(interfaceName, *this).first->second;
|
||||
}
|
||||
|
||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||
{
|
||||
auto& vtable = interfaceData.vtable_;
|
||||
auto& vtable = interfaceData.vtable;
|
||||
assert(vtable.empty());
|
||||
|
||||
vtable.push_back(createVTableStartItem());
|
||||
vtable.push_back(createVTableStartItem(interfaceData.flags.toSdBusInterfaceFlags()));
|
||||
registerMethodsToVTable(interfaceData, vtable);
|
||||
registerSignalsToVTable(interfaceData, vtable);
|
||||
registerPropertiesToVTable(interfaceData, vtable);
|
||||
@ -170,46 +266,52 @@ const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& i
|
||||
|
||||
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.methods_)
|
||||
for (const auto& item : interfaceData.methods)
|
||||
{
|
||||
const auto& methodName = item.first;
|
||||
const auto& methodData = item.second;
|
||||
|
||||
vtable.push_back(createVTableMethodItem( methodName.c_str()
|
||||
, methodData.inputArgs_.c_str()
|
||||
, methodData.outputArgs_.c_str()
|
||||
, &Object::sdbus_method_callback ));
|
||||
, methodData.inputArgs.c_str()
|
||||
, methodData.outputArgs.c_str()
|
||||
, methodData.paramNames.c_str()
|
||||
, &Object::sdbus_method_callback
|
||||
, methodData.flags.toSdBusMethodFlags() ));
|
||||
}
|
||||
}
|
||||
|
||||
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.signals_)
|
||||
for (const auto& item : interfaceData.signals)
|
||||
{
|
||||
const auto& signalName = item.first;
|
||||
const auto& signalData = item.second;
|
||||
|
||||
vtable.push_back(createVTableSignalItem( signalName.c_str()
|
||||
, signalData.signature_.c_str() ));
|
||||
, signalData.signature.c_str()
|
||||
, signalData.paramNames.c_str()
|
||||
, signalData.flags.toSdBusSignalFlags() ));
|
||||
}
|
||||
}
|
||||
|
||||
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.properties_)
|
||||
for (const auto& item : interfaceData.properties)
|
||||
{
|
||||
const auto& propertyName = item.first;
|
||||
const auto& propertyData = item.second;
|
||||
|
||||
if (!propertyData.setCallback_)
|
||||
if (!propertyData.setCallback)
|
||||
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, &Object::sdbus_property_get_callback ));
|
||||
, propertyData.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, propertyData.flags.toSdBusPropertyFlags() ));
|
||||
else
|
||||
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, propertyData.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, &Object::sdbus_property_set_callback ));
|
||||
, &Object::sdbus_property_set_callback
|
||||
, propertyData.flags.toSdBusWritablePropertyFlags() ));
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,26 +319,39 @@ void Object::activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable )
|
||||
{
|
||||
// Tell, don't ask
|
||||
auto slot = (sd_bus_slot*) connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
|
||||
interfaceData.slot_.reset(slot);
|
||||
interfaceData.slot_.get_deleter() = [this](void *slot){ connection_.removeObjectVTable(slot); };
|
||||
interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], &interfaceData);
|
||||
}
|
||||
|
||||
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
|
||||
{
|
||||
std::string names;
|
||||
for (const auto& name : paramNames)
|
||||
names += name + '\0';
|
||||
return names;
|
||||
}
|
||||
|
||||
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
MethodCall message(sdbusMessage);
|
||||
auto* interfaceData = static_cast<InterfaceData*>(userData);
|
||||
assert(interfaceData != nullptr);
|
||||
auto& object = interfaceData->object;
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
|
||||
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object.connection_.getSdBusInterface());
|
||||
|
||||
object.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
auto& callback = interfaceData->methods[message.getMemberName()].callback;
|
||||
assert(callback);
|
||||
|
||||
try
|
||||
{
|
||||
callback(message);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
@ -246,17 +361,17 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
|
||||
|
||||
int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
, const char */*objectPath*/
|
||||
, const char *interface
|
||||
, const char */*interface*/
|
||||
, const char *property
|
||||
, sd_bus_message *sdbusReply
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
Message reply(sdbusReply);
|
||||
auto* interfaceData = static_cast<InterfaceData*>(userData);
|
||||
assert(interfaceData != nullptr);
|
||||
auto& object = interfaceData->object;
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[interface].properties_[property].getCallback_;
|
||||
auto& callback = interfaceData->properties[property].getCallback;
|
||||
// Getter can be empty - the case of "write-only" property
|
||||
if (!callback)
|
||||
{
|
||||
@ -264,11 +379,13 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object.connection_.getSdBusInterface());
|
||||
|
||||
try
|
||||
{
|
||||
callback(reply);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
@ -278,24 +395,32 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
|
||||
int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
, const char */*objectPath*/
|
||||
, const char *interface
|
||||
, const char */*interface*/
|
||||
, const char *property
|
||||
, sd_bus_message *sdbusValue
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
Message value(sdbusValue);
|
||||
auto* interfaceData = static_cast<InterfaceData*>(userData);
|
||||
assert(interfaceData != nullptr);
|
||||
auto& object = interfaceData->object;
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[interface].properties_[property].setCallback_;
|
||||
auto& callback = interfaceData->properties[property].setCallback;
|
||||
assert(callback);
|
||||
|
||||
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object.connection_.getSdBusInterface());
|
||||
|
||||
object.m_CurrentlyProcessedMessage.store(&value, std::memory_order_relaxed);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
callback(value);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
@ -303,7 +428,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
|
112
src/Object.h
112
src/Object.h
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Object.h
|
||||
*
|
||||
@ -34,10 +35,10 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Object
|
||||
: public IObject
|
||||
@ -46,70 +47,107 @@ namespace internal {
|
||||
Object(sdbus::internal::IConnection& connection, std::string objectPath);
|
||||
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback ) override;
|
||||
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, std::string outputSignature
|
||||
, method_callback methodCallback
|
||||
, Flags flags ) override;
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, async_method_callback asyncMethodCallback ) override;
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, const std::vector<std::string>& inputNames
|
||||
, std::string outputSignature
|
||||
, const std::vector<std::string>& outputNames
|
||||
, method_callback methodCallback
|
||||
, Flags flags ) override;
|
||||
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) override;
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, Flags flags ) override;
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, const std::vector<std::string>& paramNames
|
||||
, Flags flags ) override;
|
||||
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback ) override;
|
||||
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback ) override;
|
||||
, 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 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;
|
||||
void emitInterfacesAddedSignal() override;
|
||||
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) override;
|
||||
void emitInterfacesRemovedSignal() override;
|
||||
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) override;
|
||||
|
||||
void sendReplyAsynchronously(const MethodReply& reply);
|
||||
void addObjectManager() override;
|
||||
void removeObjectManager() override;
|
||||
bool hasObjectManager() const override;
|
||||
|
||||
sdbus::IConnection& getConnection() const override;
|
||||
const std::string& getObjectPath() const override;
|
||||
const Message* getCurrentlyProcessedMessage() const override;
|
||||
|
||||
private:
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
{
|
||||
InterfaceData(Object& object) : object(object) {}
|
||||
|
||||
using MethodName = std::string;
|
||||
struct MethodData
|
||||
{
|
||||
std::string inputArgs_;
|
||||
std::string outputArgs_;
|
||||
std::function<void(MethodCall&)> callback_;
|
||||
const std::string inputArgs;
|
||||
const std::string outputArgs;
|
||||
const std::string paramNames;
|
||||
method_callback callback;
|
||||
Flags flags;
|
||||
};
|
||||
std::map<MethodName, MethodData> methods_;
|
||||
std::map<MethodName, MethodData> methods;
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
{
|
||||
std::string signature_;
|
||||
const std::string signature;
|
||||
const std::string paramNames;
|
||||
Flags flags;
|
||||
};
|
||||
std::map<SignalName, SignalData> signals_;
|
||||
std::map<SignalName, SignalData> signals;
|
||||
using PropertyName = std::string;
|
||||
struct PropertyData
|
||||
{
|
||||
std::string signature_;
|
||||
property_get_callback getCallback_;
|
||||
property_set_callback setCallback_;
|
||||
const std::string signature;
|
||||
property_get_callback getCallback;
|
||||
property_set_callback setCallback;
|
||||
Flags flags;
|
||||
};
|
||||
std::map<PropertyName, PropertyData> properties_;
|
||||
std::vector<sd_bus_vtable> vtable_;
|
||||
std::map<PropertyName, PropertyData> properties;
|
||||
std::vector<sd_bus_vtable> vtable;
|
||||
Flags flags;
|
||||
Object& object;
|
||||
|
||||
std::unique_ptr<void, std::function<void(void*)>> slot_;
|
||||
// This is intentionally the last member, because it must be destructed first,
|
||||
// releasing callbacks above before the callbacks themselves are destructed.
|
||||
SlotPtr 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);
|
||||
@ -117,6 +155,7 @@ namespace internal {
|
||||
void activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable );
|
||||
static std::string paramNamesToString(const std::vector<std::string>& paramNames);
|
||||
|
||||
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_property_get_callback( sd_bus *bus
|
||||
@ -138,9 +177,10 @@ namespace internal {
|
||||
sdbus::internal::IConnection& connection_;
|
||||
std::string objectPath_;
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
SlotPtr objectManagerSlot_;
|
||||
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_OBJECT_H_ */
|
||||
|
@ -1,196 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ObjectProxy.cpp
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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 "ObjectProxy.h"
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "IConnection.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
ObjectProxy::ObjectProxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
|
||||
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
|
||||
, ownConnection_(false)
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectProxy::ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
: connection_(std::move(connection))
|
||||
, ownConnection_(true)
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectProxy::~ObjectProxy()
|
||||
{
|
||||
// If the dedicated connection for signals is used, we have to stop the processing loop
|
||||
// upon this connection prior to unregistering signal slots in the interfaces_ container,
|
||||
// otherwise we might have a race condition of two threads working upon one connection.
|
||||
if (signalConnection_ != nullptr)
|
||||
signalConnection_->leaveProcessingLoop();
|
||||
}
|
||||
|
||||
MethodCall ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
|
||||
{
|
||||
// Tell, don't ask
|
||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||
}
|
||||
|
||||
MethodReply ObjectProxy::callMethod(const MethodCall& message)
|
||||
{
|
||||
return message.send();
|
||||
}
|
||||
|
||||
void ObjectProxy::registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler )
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
|
||||
InterfaceData::SignalData signalData{std::move(signalHandler), nullptr};
|
||||
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
|
||||
|
||||
auto inserted = insertionResult.second;
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL);
|
||||
}
|
||||
|
||||
void ObjectProxy::finishRegistration()
|
||||
{
|
||||
bool hasSignals = listensToSignals();
|
||||
|
||||
if (hasSignals && ownConnection_)
|
||||
{
|
||||
// Let's use dedicated signalConnection_ for signals,
|
||||
// which will then be used by the processing loop thread.
|
||||
signalConnection_ = connection_->clone();
|
||||
registerSignalHandlers(*signalConnection_);
|
||||
signalConnection_->enterProcessingLoopAsync();
|
||||
}
|
||||
else if (hasSignals)
|
||||
{
|
||||
// Let's used connection provided from the outside.
|
||||
registerSignalHandlers(*connection_);
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectProxy::listensToSignals() const
|
||||
{
|
||||
for (auto& interfaceItem : interfaces_)
|
||||
if (!interfaceItem.second.signals_.empty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObjectProxy::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& slot = signalItem.second.slot_;
|
||||
auto* rawSlotPtr = connection.registerSignalHandler( objectPath_
|
||||
, interfaceName
|
||||
, signalName
|
||||
, &ObjectProxy::sdbus_signal_callback
|
||||
, this );
|
||||
slot.reset(rawSlotPtr);
|
||||
slot.get_deleter() = [&connection](void *slot){ connection.unregisterSignalHandler(slot); };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
||||
{
|
||||
Signal message(sdbusMessage);
|
||||
|
||||
auto* object = static_cast<ObjectProxy*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_;
|
||||
assert(callback);
|
||||
|
||||
callback(message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
return std::make_unique<sdbus::internal::ObjectProxy>( *sdbusConnection
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::unique_ptr<IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
connection.release();
|
||||
|
||||
return std::make_unique<sdbus::internal::ObjectProxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
|
||||
assert(sdbusConnection != nullptr);
|
||||
|
||||
return std::make_unique<sdbus::internal::ObjectProxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ObjectProxy.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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_INTERNAL_OBJECTPROXY_H_
|
||||
#define SDBUS_CXX_INTERNAL_OBJECTPROXY_H_
|
||||
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus { namespace internal {
|
||||
class IConnection;
|
||||
}}
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
|
||||
class ObjectProxy
|
||||
: public IObjectProxy
|
||||
{
|
||||
public:
|
||||
ObjectProxy( sdbus::internal::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
~ObjectProxy();
|
||||
|
||||
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
MethodReply callMethod(const MethodCall& message) override;
|
||||
|
||||
void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler ) override;
|
||||
void finishRegistration() override;
|
||||
|
||||
private:
|
||||
bool listensToSignals() const;
|
||||
void registerSignalHandlers(sdbus::internal::IConnection& connection);
|
||||
static int sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
std::unique_ptr< sdbus::internal::IConnection
|
||||
, std::function<void(sdbus::internal::IConnection*)>
|
||||
> connection_;
|
||||
bool ownConnection_{};
|
||||
std::unique_ptr<sdbus::internal::IConnection> signalConnection_;
|
||||
std::string destination_;
|
||||
std::string objectPath_;
|
||||
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
{
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
{
|
||||
signal_handler callback_;
|
||||
std::unique_ptr<void, std::function<void(void*)>> slot_;
|
||||
};
|
||||
std::map<SignalName, SignalData> signals_;
|
||||
};
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_OBJECTPROXY_H_ */
|
379
src/Proxy.cpp
Normal file
379
src/Proxy.cpp
Normal file
@ -0,0 +1,379 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Proxy.cpp
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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 "Proxy.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 <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#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 */ })
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_);
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_);
|
||||
|
||||
// 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 )
|
||||
: connection_(std::move(connection))
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_);
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_);
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
|
||||
{
|
||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||
}
|
||||
|
||||
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
PendingAsyncCall Proxy::callMethod(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), {}});
|
||||
auto weakData = std::weak_ptr<AsyncCalls::CallData>{callData};
|
||||
|
||||
callData->slot = message.send(callback, callData.get(), timeout);
|
||||
|
||||
auto slotPtr = callData->slot.get();
|
||||
pendingAsyncCalls_.addCall(slotPtr, std::move(callData));
|
||||
|
||||
return {weakData};
|
||||
}
|
||||
|
||||
MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout)
|
||||
{
|
||||
/*thread_local*/ SyncCallReplyData syncCallReplyData;
|
||||
|
||||
async_reply_handler asyncReplyCallback = [&syncCallReplyData](MethodReply& reply, const Error* error)
|
||||
{
|
||||
syncCallReplyData.sendMethodReplyToWaitingThread(reply, error);
|
||||
};
|
||||
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
|
||||
AsyncCalls::CallData callData{*this, std::move(asyncReplyCallback), {}};
|
||||
|
||||
message.send(callback, &callData, timeout, dont_request_slot);
|
||||
|
||||
return syncCallReplyData.waitForMethodReply();
|
||||
}
|
||||
|
||||
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
|
||||
, signal_handler signalHandler )
|
||||
{
|
||||
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 signalData = std::make_unique<InterfaceData::SignalData>(*this, std::move(signalHandler), nullptr);
|
||||
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Proxy::unregister()
|
||||
{
|
||||
pendingAsyncCalls_.clear();
|
||||
interfaces_.clear();
|
||||
}
|
||||
|
||||
sdbus::IConnection& Proxy::getConnection() const
|
||||
{
|
||||
return dynamic_cast<sdbus::IConnection&>(*connection_);
|
||||
}
|
||||
|
||||
const std::string& Proxy::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
const Message* Proxy::getCurrentlyProcessedMessage() const
|
||||
{
|
||||
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
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 slot = asyncCallData->slot.get();
|
||||
|
||||
// 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 slot == nullptr)
|
||||
if (slot)
|
||||
proxy.pendingAsyncCalls_.removeCall(slot);
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
const auto* error = sd_bus_message_get_error(sdbusMessage);
|
||||
if (error == nullptr)
|
||||
{
|
||||
asyncCallData->callback(message, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Error exception(error->name, error->message);
|
||||
asyncCallData->callback(message, &exception);
|
||||
}
|
||||
}
|
||||
catch (const Error&)
|
||||
{
|
||||
// Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 message = Message::Factory::create<Signal>(sdbusMessage, &signalData->proxy.connection_->getSdBusInterface());
|
||||
|
||||
signalData->proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
|
||||
SCOPE_EXIT
|
||||
{
|
||||
signalData->proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
signalData->callback(message);
|
||||
}
|
||||
catch (const Error&)
|
||||
{
|
||||
// Intentionally left blank -- sdbus-c++ exceptions shall not bubble up to the underlying C sd-bus library
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callData)
|
||||
: callData_(std::move(callData))
|
||||
{
|
||||
}
|
||||
|
||||
void PendingAsyncCall::cancel()
|
||||
{
|
||||
if (auto ptr = callData_.lock(); ptr != nullptr)
|
||||
{
|
||||
auto* callData = static_cast<internal::Proxy::AsyncCalls::CallData*>(ptr.get());
|
||||
callData->proxy.pendingAsyncCalls_.removeCall(callData->slot.get());
|
||||
|
||||
// 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
|
||||
// callback is currently being processed, the sd-bus mutex is locked by the event
|
||||
// loop thread, thus access to the callData item is synchronized and thread-safe.
|
||||
}
|
||||
}
|
||||
|
||||
bool PendingAsyncCall::isPending() const
|
||||
{
|
||||
return !callData_.expired();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::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
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
connection.release();
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
|
||||
assert(sdbusConnection != nullptr);
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
}
|
187
src/Proxy.h
Normal file
187
src/Proxy.h
Normal file
@ -0,0 +1,187 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Proxy.h
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* 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_INTERNAL_PROXY_H_
|
||||
#define SDBUS_CXX_INTERNAL_PROXY_H_
|
||||
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include "IConnection.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Proxy
|
||||
: 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 );
|
||||
|
||||
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
|
||||
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
|
||||
|
||||
void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler ) override;
|
||||
void unregisterSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName ) override;
|
||||
|
||||
void finishRegistration() override;
|
||||
void unregister() override;
|
||||
|
||||
sdbus::IConnection& getConnection() const override;
|
||||
const std::string& getObjectPath() const override;
|
||||
const 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);
|
||||
|
||||
private:
|
||||
friend PendingAsyncCall;
|
||||
|
||||
std::unique_ptr< sdbus::internal::IConnection
|
||||
, std::function<void(sdbus::internal::IConnection*)>
|
||||
> connection_;
|
||||
std::string destination_;
|
||||
std::string objectPath_;
|
||||
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
{
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
{
|
||||
SignalData(Proxy& proxy, signal_handler callback, SlotPtr 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.
|
||||
SlotPtr slot;
|
||||
};
|
||||
std::map<SignalName, std::unique_ptr<SignalData>> signals_;
|
||||
};
|
||||
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
|
||||
{
|
||||
public:
|
||||
struct CallData
|
||||
{
|
||||
Proxy& proxy;
|
||||
async_reply_handler callback;
|
||||
MethodCall::Slot slot;
|
||||
};
|
||||
|
||||
~AsyncCalls()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
bool addCall(void* slot, std::shared_ptr<CallData> asyncCallData)
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
return calls_.emplace(slot, std::move(asyncCallData)).second;
|
||||
}
|
||||
|
||||
void removeCall(void* slot)
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
if (auto it = calls_.find(slot); it != calls_.end())
|
||||
{
|
||||
auto callData = std::move(it->second);
|
||||
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.
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::unordered_map<void*, std::shared_ptr<CallData>> calls_;
|
||||
} pendingAsyncCalls_;
|
||||
|
||||
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_PROXY_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file ScopeGuard.h
|
||||
*
|
||||
|
338
src/SdBus.cpp
Normal file
338
src/SdBus.cpp
Normal file
@ -0,0 +1,338 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file SdBus.cpp
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
*
|
||||
* Created on: Mar 3, 2019
|
||||
* 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.h"
|
||||
#include <sdbus-c++/Error.h>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *m)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_ref(m);
|
||||
}
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_unref(m);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_send(bus, m, cookie);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_call(bus, m, usec, ret_error, reply);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new(bus, m, type);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_signal(bus, m, path, interface, member);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_return(call, m);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_new_method_error(call, m, e);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
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");
|
||||
#endif
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
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");
|
||||
#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_);
|
||||
|
||||
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_);
|
||||
|
||||
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_);
|
||||
|
||||
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_);
|
||||
|
||||
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_);
|
||||
|
||||
return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open(sd_bus **ret)
|
||||
{
|
||||
return ::sd_bus_open(ret);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_user(sd_bus **ret)
|
||||
{
|
||||
return ::sd_bus_open_user(ret);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_system(sd_bus **ret)
|
||||
{
|
||||
return ::sd_bus_open_system(ret);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
|
||||
{
|
||||
return ::sd_bus_open_system_remote(ret, host);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags)
|
||||
{
|
||||
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_);
|
||||
|
||||
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_);
|
||||
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_);
|
||||
|
||||
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_);
|
||||
|
||||
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_);
|
||||
|
||||
return :: sd_bus_add_match(bus, slot, match, callback, userdata);
|
||||
}
|
||||
|
||||
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_slot_unref(slot);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_process(bus, r);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
auto r = ::sd_bus_get_fd(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
data->fd = r;
|
||||
|
||||
r = ::sd_bus_get_events(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
data->events = static_cast<short int>(r);
|
||||
|
||||
r = ::sd_bus_get_timeout(bus, &data->timeout_usec);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_flush(sd_bus *bus)
|
||||
{
|
||||
return ::sd_bus_flush(bus);
|
||||
}
|
||||
|
||||
sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus)
|
||||
{
|
||||
return ::sd_bus_flush_close_unref(bus);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_message_set_destination(sd_bus_message *m, const char *destination)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_message_set_destination(m, destination);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_query_sender_creds(m, mask, c);
|
||||
}
|
||||
|
||||
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *c)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_unref(c);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_pid(c, pid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_uid(c, uid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_euid(c, euid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_gid(c, gid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_egid(sd_bus_creds *c, uid_t *egid)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_egid(c, egid);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_supplementary_gids(c, gids);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_get_selinux_context(c, label);
|
||||
}
|
||||
|
||||
}
|
98
src/SdBus.h
Normal file
98
src/SdBus.h
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file SdBus.h
|
||||
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
|
||||
*
|
||||
* Created on: Mar 3, 2019
|
||||
* 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_SDBUS_H
|
||||
#define SDBUS_CXX_SDBUS_H
|
||||
|
||||
#include "ISdBus.h"
|
||||
#include <mutex>
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
virtual int sd_bus_open(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_user(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_system(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) 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 sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) 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;
|
||||
|
||||
virtual int sd_bus_flush(sd_bus *bus) override;
|
||||
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override;
|
||||
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) override;
|
||||
|
||||
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) override;
|
||||
virtual sd_bus_creds* sd_bus_creds_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;
|
||||
|
||||
private:
|
||||
std::recursive_mutex sdbusMutex_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //SDBUS_C_SDBUS_H
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Types.cpp
|
||||
*
|
||||
@ -29,7 +30,7 @@
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { /*namespace internal {*/
|
||||
namespace sdbus {
|
||||
|
||||
Variant::Variant()
|
||||
: msg_(createPlainMessage())
|
||||
@ -51,6 +52,7 @@ void Variant::deserializeFrom(Message& msg)
|
||||
|
||||
std::string Variant::peekValueType() const
|
||||
{
|
||||
msg_.rewind(false);
|
||||
std::string type;
|
||||
std::string contents;
|
||||
msg_.peekType(type, contents);
|
||||
|
53
src/Utils.h
Normal file
53
src/Utils.h
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2022 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Utils.h
|
||||
*
|
||||
* Created on: Feb 9, 2022
|
||||
* 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_INTERNAL_UTILS_H_
|
||||
#define SDBUS_CXX_INTERNAL_UTILS_H_
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#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_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_SERVICE_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_service_name_is_valid(_NAME.c_str()), "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) \
|
||||
/**/
|
||||
#else
|
||||
#define SDBUS_CHECK_OBJECT_PATH(_PATH)
|
||||
#define SDBUS_CHECK_INTERFACE_NAME(_NAME)
|
||||
#define SDBUS_CHECK_SERVICE_NAME(_NAME)
|
||||
#define SDBUS_CHECK_MEMBER_NAME(_NAME)
|
||||
#endif
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_UTILS_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file VTableUtils.c
|
||||
*
|
||||
@ -26,42 +27,74 @@
|
||||
#include "VTableUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
sd_bus_vtable createVTableStartItem()
|
||||
sd_bus_vtable createVTableStartItem(uint64_t flags)
|
||||
{
|
||||
struct sd_bus_vtable vtableStart = SD_BUS_VTABLE_START(0);
|
||||
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
|
||||
, sd_bus_message_handler_t handler )
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_METHOD(member, signature, result, handler, SD_BUS_VTABLE_UNPRIVILEGED);
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
// We have to expand macro SD_BUS_METHOD_WITH_NAMES manually here, because the macro expects literal char strings
|
||||
/*struct sd_bus_vtable vtableItem = SD_BUS_METHOD_WITH_NAMES(member, signature, innames, result, outnames, handler, flags);*/
|
||||
struct sd_bus_vtable vtableItem =
|
||||
{
|
||||
.type = _SD_BUS_VTABLE_METHOD,
|
||||
.flags = flags,
|
||||
.x = {
|
||||
.method = {
|
||||
.member = member,
|
||||
.signature = signature,
|
||||
.result = result,
|
||||
.handler = handler,
|
||||
.offset = 0,
|
||||
.names = paramNames,
|
||||
},
|
||||
},
|
||||
};
|
||||
#else
|
||||
(void)paramNames;
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_METHOD(member, signature, result, handler, flags);
|
||||
#endif
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableSignalItem( const char *member
|
||||
, const char *signature )
|
||||
, const char *signature
|
||||
, const char *outnames
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL(member, signature, 0);
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL_WITH_NAMES(member, signature, outnames, flags);
|
||||
#else
|
||||
(void)outnames;
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL(member, signature, flags);
|
||||
#endif
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter )
|
||||
, sd_bus_property_get_t getter
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, 0);
|
||||
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 )
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, 0);
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, flags);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file VTableUtils.h
|
||||
*
|
||||
@ -27,25 +28,32 @@
|
||||
#define SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
sd_bus_vtable createVTableStartItem();
|
||||
sd_bus_vtable createVTableStartItem(uint64_t flags);
|
||||
sd_bus_vtable createVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, sd_bus_message_handler_t handler );
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createVTableSignalItem( const char *member
|
||||
, const char *signature );
|
||||
, 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 );
|
||||
, 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 );
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createVTableEndItem();
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -1,266 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file AdaptorGenerator.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "generator_utils.h"
|
||||
#include "AdaptorGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
using std::endl;
|
||||
|
||||
using sdbuscpp::xml::Document;
|
||||
using sdbuscpp::xml::Node;
|
||||
using sdbuscpp::xml::Nodes;
|
||||
|
||||
/**
|
||||
* Generate adaptor code - server glue
|
||||
*/
|
||||
int AdaptorGenerator::transformXmlToFileImpl(const Document& doc, const char* filename) const
|
||||
{
|
||||
Node &root = *(doc.root);
|
||||
Nodes interfaces = root["interface"];
|
||||
|
||||
std::ostringstream code;
|
||||
code << createHeader(filename, StubType::ADAPTOR);
|
||||
|
||||
for (const auto& interface : interfaces)
|
||||
{
|
||||
code << processInterface(*interface);
|
||||
}
|
||||
|
||||
code << "#endif" << endl;
|
||||
|
||||
return writeToFile(filename, code.str());
|
||||
}
|
||||
|
||||
|
||||
std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
{
|
||||
std::string ifaceName = interface.get("name");
|
||||
std::cout << "Generating adaptor code for interface " << ifaceName << endl;
|
||||
|
||||
unsigned int namespacesCount = 0;
|
||||
std::string namespacesStr;
|
||||
std::tie(namespacesCount, namespacesStr) = generateNamespaces(ifaceName);
|
||||
|
||||
std::ostringstream body;
|
||||
body << namespacesStr;
|
||||
|
||||
std::string className = ifaceName.substr(ifaceName.find_last_of(".") + 1)
|
||||
+ "_adaptor";
|
||||
|
||||
body << "class " << className << endl
|
||||
<< "{" << endl
|
||||
<< "public:" << endl
|
||||
<< tab << "static constexpr const char* interfaceName = \"" << ifaceName << "\";" << endl << endl
|
||||
<< "protected:" << endl
|
||||
<< tab << className << "(sdbus::IObject& object)" << endl
|
||||
<< tab << tab << ": object_(object)" << endl;
|
||||
|
||||
Nodes methods = interface["method"];
|
||||
Nodes signals = interface["signal"];
|
||||
Nodes properties = interface["property"];
|
||||
|
||||
std::string methodRegistration, methodDeclaration;
|
||||
std::tie(methodRegistration, methodDeclaration) = processMethods(methods);
|
||||
|
||||
std::string signalRegistration, signalMethods;
|
||||
std::tie(signalRegistration, signalMethods) = processSignals(signals);
|
||||
|
||||
std::string propertyRegistration, propertyAccessorDeclaration;
|
||||
std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties);
|
||||
|
||||
body << tab << "{" << endl
|
||||
<< methodRegistration
|
||||
<< signalRegistration
|
||||
<< propertyRegistration
|
||||
<< tab << "}" << endl << endl;
|
||||
|
||||
if (!signalMethods.empty())
|
||||
{
|
||||
body << "public:" << endl << signalMethods;
|
||||
}
|
||||
|
||||
if (!methodDeclaration.empty())
|
||||
{
|
||||
body << "private:" << endl << methodDeclaration << endl;
|
||||
}
|
||||
|
||||
if (!propertyAccessorDeclaration.empty())
|
||||
{
|
||||
body << "private:" << endl << propertyAccessorDeclaration << endl;
|
||||
}
|
||||
|
||||
body << "private:" << endl
|
||||
<< tab << "sdbus::IObject& object_;" << endl
|
||||
<< "};" << endl << endl
|
||||
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
|
||||
|
||||
return body.str();
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
|
||||
for (const auto& method : methods)
|
||||
{
|
||||
auto methodName = method->get("name");
|
||||
|
||||
bool async{false};
|
||||
Nodes annotations = (*method)["annotation"];
|
||||
|
||||
for (const auto& annotation : annotations)
|
||||
{
|
||||
if (annotation->get("name") == "org.freedesktop.DBus.Method.Async"
|
||||
&& (annotation->get("value") == "server" || annotation->get("value") == "clientserver"))
|
||||
{
|
||||
async = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Nodes args = (*method)["arg"];
|
||||
Nodes inArgs = args.select("direction" , "in");
|
||||
Nodes outArgs = args.select("direction" , "out");
|
||||
|
||||
std::string argStr, argTypeStr;
|
||||
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
registrationSS << tab << tab << "object_.registerMethod(\""
|
||||
<< methodName << "\")"
|
||||
<< ".onInterface(interfaceName)"
|
||||
<< ".implementedAs("
|
||||
<< "[this]("
|
||||
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argTypeStr
|
||||
<< "){ " << (async ? "" : "return ") << "this->" << methodName << "("
|
||||
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argStr << "); });" << endl;
|
||||
|
||||
declarationSS << tab
|
||||
<< "virtual "
|
||||
<< (async ? "void" : outArgsToType(outArgs))
|
||||
<< " " << methodName
|
||||
<< "("
|
||||
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argTypeStr
|
||||
<< ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
|
||||
{
|
||||
std::ostringstream signalRegistrationSS, signalMethodSS;
|
||||
|
||||
for (const auto& signal : signals)
|
||||
{
|
||||
auto name = signal->get("name");
|
||||
Nodes args = (*signal)["arg"];
|
||||
|
||||
std::string argStr, argTypeStr, typeStr;;
|
||||
std::tie(argStr, argTypeStr, typeStr) = argsToNamesAndTypes(args);
|
||||
|
||||
signalRegistrationSS << tab << tab
|
||||
<< "object_.registerSignal(\"" << name << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
|
||||
if (args.size() > 0)
|
||||
{
|
||||
signalRegistrationSS << ".withParameters<" << typeStr << ">()";
|
||||
}
|
||||
|
||||
signalRegistrationSS << ";" << endl;
|
||||
|
||||
signalMethodSS << tab << "void " << name << "(" << argTypeStr << ")" << endl
|
||||
<< tab << "{" << endl
|
||||
<< tab << tab << "object_.emitSignal(\"" << name << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
|
||||
if (!argStr.empty())
|
||||
{
|
||||
signalMethodSS << ".withArguments(" << argStr << ")";
|
||||
}
|
||||
|
||||
signalMethodSS << ";" << endl
|
||||
<< tab << "}" << endl << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(signalRegistrationSS.str(), signalMethodSS.str());
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
|
||||
for (const auto& property : properties)
|
||||
{
|
||||
auto propertyName = property->get("name");
|
||||
auto propertyAccess = property->get("access");
|
||||
auto propertySignature = property->get("type");
|
||||
|
||||
auto propertyType = signature_to_type(propertySignature);
|
||||
auto propertyArg = std::string("value");
|
||||
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
|
||||
|
||||
registrationSS << tab << tab << "object_.registerProperty(\""
|
||||
<< propertyName << "\")"
|
||||
<< ".onInterface(interfaceName)";
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
{
|
||||
registrationSS << ".withGetter([this](){ return this->" << propertyName << "(); })";
|
||||
}
|
||||
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
{
|
||||
registrationSS
|
||||
<< ".withSetter([this](" << propertyTypeArg << ")"
|
||||
"{ this->" << propertyName << "(" << propertyArg << "); })";
|
||||
}
|
||||
|
||||
registrationSS << ";" << endl;
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
declarationSS << tab << "virtual " << propertyType << " " << propertyName << "() = 0;" << endl;
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
declarationSS << tab << "virtual void " << propertyName << "(" << propertyTypeArg << ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
cmake_minimum_required (VERSION 3.4)
|
||||
|
||||
add_definitions(-std=c++14)
|
||||
|
||||
project (sdbuscpp-xml2cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} xml2cpp.cpp xml.cpp generator_utils.cpp BaseGenerator.cpp AdaptorGenerator.cpp ProxyGenerator.cpp)
|
||||
|
||||
find_package (EXPAT REQUIRED)
|
||||
|
||||
target_link_libraries (${PROJECT_NAME} ${EXPAT_LIBRARIES})
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} DESTINATION bin)
|
@ -1,224 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ProxyGenerator.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "generator_utils.h"
|
||||
#include "ProxyGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
|
||||
using std::endl;
|
||||
|
||||
using sdbuscpp::xml::Document;
|
||||
using sdbuscpp::xml::Node;
|
||||
using sdbuscpp::xml::Nodes;
|
||||
|
||||
/**
|
||||
* Generate proxy code - client glue
|
||||
*/
|
||||
int ProxyGenerator::transformXmlToFileImpl(const Document &doc, const char *filename) const
|
||||
{
|
||||
Node &root = *(doc.root);
|
||||
Nodes interfaces = root["interface"];
|
||||
|
||||
std::ostringstream code;
|
||||
code << createHeader(filename, StubType::PROXY);
|
||||
|
||||
for (const auto& interface : interfaces)
|
||||
{
|
||||
code << processInterface(*interface);
|
||||
}
|
||||
|
||||
code << "#endif" << endl;
|
||||
|
||||
return writeToFile(filename, code.str());
|
||||
}
|
||||
|
||||
|
||||
std::string ProxyGenerator::processInterface(Node& interface) const
|
||||
{
|
||||
std::string ifaceName = interface.get("name");
|
||||
std::cout << "Generating proxy code for interface " << ifaceName << endl;
|
||||
|
||||
unsigned int namespacesCount = 0;
|
||||
std::string namespacesStr;
|
||||
std::tie(namespacesCount, namespacesStr) = generateNamespaces(ifaceName);
|
||||
|
||||
std::ostringstream body;
|
||||
body << namespacesStr;
|
||||
|
||||
std::string className = ifaceName.substr(ifaceName.find_last_of(".") + 1)
|
||||
+ "_proxy";
|
||||
|
||||
body << "class " << className << endl
|
||||
<< "{" << endl
|
||||
<< "public:" << endl
|
||||
<< tab << "static constexpr const char* interfaceName = \"" << ifaceName << "\";" << endl << endl
|
||||
<< "protected:" << endl
|
||||
<< tab << className << "(sdbus::IObjectProxy& object)" << endl
|
||||
<< tab << tab << ": object_(object)" << endl;
|
||||
|
||||
Nodes methods = interface["method"];
|
||||
Nodes signals = interface["signal"];
|
||||
Nodes properties = interface["property"];
|
||||
|
||||
std::string registration, declaration;
|
||||
std::tie(registration, declaration) = processSignals(signals);
|
||||
|
||||
body << tab << "{" << endl
|
||||
<< registration
|
||||
<< tab << "}" << endl << endl
|
||||
<< declaration << endl;
|
||||
|
||||
std::string methodDefinitions = processMethods(methods);
|
||||
if (!methodDefinitions.empty())
|
||||
{
|
||||
body << "public:" << endl << methodDefinitions;
|
||||
}
|
||||
|
||||
std::string propertyDefinitions = processProperties(properties);
|
||||
if (!propertyDefinitions.empty())
|
||||
{
|
||||
body << "public:" << endl << propertyDefinitions;
|
||||
}
|
||||
|
||||
body << "private:" << endl
|
||||
<< tab << "sdbus::IObjectProxy& object_;" << endl
|
||||
<< "};" << endl << endl
|
||||
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
|
||||
|
||||
return body.str();
|
||||
}
|
||||
|
||||
std::string ProxyGenerator::processMethods(const Nodes& methods) const
|
||||
{
|
||||
std::ostringstream methodSS;
|
||||
for (const auto& method : methods)
|
||||
{
|
||||
auto name = method->get("name");
|
||||
Nodes args = (*method)["arg"];
|
||||
Nodes inArgs = args.select("direction" , "in");
|
||||
Nodes outArgs = args.select("direction" , "out");
|
||||
|
||||
auto retType = outArgsToType(outArgs);
|
||||
std::string argStr, argTypeStr;
|
||||
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
|
||||
|
||||
methodSS << tab << retType << " " << name << "(" << argTypeStr << ")" << endl
|
||||
<< tab << "{" << endl;
|
||||
|
||||
if (outArgs.size() > 0)
|
||||
{
|
||||
methodSS << tab << tab << retType << " result;" << endl;
|
||||
}
|
||||
|
||||
methodSS << tab << tab << "object_.callMethod(\"" << name << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
|
||||
if (inArgs.size() > 0)
|
||||
{
|
||||
methodSS << ".withArguments(" << argStr << ")";
|
||||
}
|
||||
|
||||
if (outArgs.size() > 0)
|
||||
{
|
||||
methodSS << ".storeResultsTo(result);" << endl
|
||||
<< tab << tab << "return result";
|
||||
}
|
||||
|
||||
methodSS << ";" << endl << tab << "}" << endl << endl;
|
||||
}
|
||||
|
||||
return methodSS.str();
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> ProxyGenerator::processSignals(const Nodes& signals) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
|
||||
for (const auto& signal : signals)
|
||||
{
|
||||
auto name = signal->get("name");
|
||||
Nodes args = (*signal)["arg"];
|
||||
|
||||
auto nameBigFirst = name;
|
||||
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
|
||||
|
||||
std::string argStr, argTypeStr;
|
||||
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(args);
|
||||
|
||||
registrationSS << tab << tab << "object_"
|
||||
".uponSignal(\"" << name << "\")"
|
||||
".onInterface(interfaceName)"
|
||||
".call([this](" << argTypeStr << ")"
|
||||
"{ this->on" << nameBigFirst << "(" << argStr << "); });" << endl;
|
||||
|
||||
declarationSS << tab << "virtual void on" << nameBigFirst << "(" << argTypeStr << ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
}
|
||||
|
||||
std::string ProxyGenerator::processProperties(const Nodes& properties) const
|
||||
{
|
||||
std::ostringstream propertySS;
|
||||
for (const auto& property : properties)
|
||||
{
|
||||
auto propertyName = property->get("name");
|
||||
auto propertyAccess = property->get("access");
|
||||
auto propertySignature = property->get("type");
|
||||
|
||||
auto propertyType = signature_to_type(propertySignature);
|
||||
auto propertyArg = std::string("value");
|
||||
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
{
|
||||
propertySS << tab << propertyType << " " << propertyName << "()" << endl
|
||||
<< tab << "{" << endl;
|
||||
propertySS << tab << tab << "return object_.getProperty(\"" << propertyName << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
propertySS << ";" << endl << tab << "}" << endl << endl;
|
||||
}
|
||||
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
{
|
||||
propertySS << tab << "void " << propertyName << "(" << propertyTypeArg << ")" << endl
|
||||
<< tab << "{" << endl;
|
||||
propertySS << tab << tab << "object_.setProperty(\"" << propertyName << "\")"
|
||||
".onInterface(interfaceName)"
|
||||
".toValue(" << propertyArg << ")";
|
||||
propertySS << ";" << endl << tab << "}" << endl << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return propertySS.str();
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
# Defines how to build and install libsdbus-c++ tests
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
# Target dirs for test binaries, scripts and files
|
||||
testbindir = /opt/test/bin
|
||||
dbusconfdir = $(sysconfdir)/dbus-1/system.d
|
||||
|
||||
# ENABLE_TESTS is defined by configure when user enables tests during configuration
|
||||
if ENABLE_TESTS
|
||||
testbin_PROGRAMS = libsdbus-c++_unittests libsdbus-c++_integrationtests
|
||||
dbusconf_DATA = integrationtests/files/libsdbus-cpp-test.conf
|
||||
endif
|
||||
|
||||
# Setting per-file flags
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
|
||||
AM_CXXFLAGS = @libsdbus_cpp_CFLAGS@ @SYSTEMD_CFLAGS@ -W -Wall -Werror -pedantic -pipe -std=c++17
|
||||
AM_LDFLAGS = @libsdbus_cpp_LIBS@ @SYSTEMD_LIBS@
|
||||
|
||||
CLEANFILES = *~ *.lo *.la
|
||||
MOSTLYCLEANFILES = *.o
|
||||
|
||||
TESTS =
|
||||
|
||||
# Configuration for libsdbus-c++_unittests
|
||||
libsdbus_c___unittests_SOURCES = \
|
||||
unittests/libsdbus-c++_unittests.cpp \
|
||||
unittests/TypeTraits_test.cpp \
|
||||
unittests/Types_test.cpp \
|
||||
unittests/Message_test.cpp
|
||||
|
||||
libsdbus_c___unittests_LDFLAGS = -L$(top_builddir)/src -pthread
|
||||
|
||||
libsdbus_c___unittests_LDADD = \
|
||||
-lsdbus-c++ \
|
||||
@libsdbus_cpp_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
-lgmock
|
||||
|
||||
TESTS += libsdbus-c++_unittests
|
||||
|
||||
# Configuration for libsdbus-c++_integrationtests
|
||||
libsdbus_c___integrationtests_SOURCES = \
|
||||
integrationtests/libsdbus-c++_integrationtests.cpp \
|
||||
integrationtests/Connection_test.cpp \
|
||||
integrationtests/AdaptorAndProxy_test.cpp
|
||||
|
||||
libsdbus_c___integrationtests_LDFLAGS = -L$(top_builddir)/src -pthread
|
||||
|
||||
libsdbus_c___integrationtests_LDADD = \
|
||||
-lsdbus-c++ \
|
||||
@libsdbus_cpp_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
-lgmock
|
||||
|
||||
TESTS += libsdbus-c++_integrationtests
|
||||
|
||||
check_PROGRAMS = libsdbus-c++_unittests libsdbus-c++_integrationtests
|
||||
|
||||
DISTCLEANFILES = Makefile Makefile.in
|
||||
|
||||
# Post-build action: executing tests from the IDE
|
||||
if ENABLE_TESTS
|
||||
all-local: libsdbus-c++_unittests libsdbus-c++_integrationtests
|
||||
if [ "${UNIT_TESTS_RUNNER}" ]; then "${UNIT_TESTS_RUNNER}" --deviceip="${TEST_DEVICE_IP}" --testbin=.libs/libsdbus-c++_unittests; fi; exit 0
|
||||
if [ "${UNIT_TESTS_RUNNER}" ]; then "${UNIT_TESTS_RUNNER}" --deviceip="${TEST_DEVICE_IP}" --testbin=.libs/libsdbus-c++_integrationtests; fi; exit 0
|
||||
endif
|
@ -1,348 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file AdaptorAndProxy_test.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Connection.h"
|
||||
|
||||
#include "TestingAdaptor.h"
|
||||
#include "TestingProxy.h"
|
||||
|
||||
// sdbus
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
// gmock
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
// STL
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::ElementsAre;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AdaptorAndProxyFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
m_connection.requestName(INTERFACE_NAME);
|
||||
m_connection.enterProcessingLoopAsync();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
m_connection.leaveProcessingLoop();
|
||||
m_connection.releaseName(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetUp() override
|
||||
{
|
||||
m_adaptor = std::make_unique<TestingAdaptor>(m_connection);
|
||||
m_proxy = std::make_unique<TestingProxy>(INTERFACE_NAME, OBJECT_PATH);
|
||||
std::this_thread::sleep_for(50ms); // Give time for the proxy to start listening to signals
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_proxy.reset();
|
||||
m_adaptor.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
static sdbus::internal::Connection m_connection;
|
||||
|
||||
std::unique_ptr<TestingAdaptor> m_adaptor;
|
||||
std::unique_ptr<TestingProxy> m_proxy;
|
||||
};
|
||||
|
||||
sdbus::internal::Connection AdaptorAndProxyFixture::m_connection{sdbus::internal::Connection::BusType::eSystem};
|
||||
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(INTERFACE_NAME);
|
||||
|
||||
ASSERT_NO_THROW(TestingAdaptor adaptor(*connection));
|
||||
ASSERT_NO_THROW(TestingProxy proxy(INTERFACE_NAME, OBJECT_PATH));
|
||||
|
||||
connection->releaseName(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
// Methods
|
||||
|
||||
using SdbusTestObject = AdaptorAndProxyFixture;
|
||||
|
||||
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
|
||||
{
|
||||
auto resInt = m_proxy->getInt();
|
||||
ASSERT_THAT(resInt, Eq(INT32_VALUE));
|
||||
|
||||
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
|
||||
{
|
||||
auto resTuple = 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)
|
||||
{
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
|
||||
auto vectorRes = m_proxy->getInts16FromStruct(a);
|
||||
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{
|
||||
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
|
||||
};
|
||||
vectorRes = m_proxy->getInts16FromStruct(b);
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
|
||||
{
|
||||
sdbus::Variant v{DOUBLE_VALUE};
|
||||
auto variantRes = m_proxy->processVariant(v);
|
||||
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
|
||||
{
|
||||
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{{-2, false}, {0, false}, {2, 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)
|
||||
{
|
||||
auto val = m_proxy->getStructInStruct();
|
||||
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
|
||||
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
|
||||
{
|
||||
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
|
||||
{
|
||||
auto val = m_proxy->sumVectorItems({1, 7}, {2, 3});
|
||||
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
|
||||
{
|
||||
auto resSignature = m_proxy->getSignature();
|
||||
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
|
||||
{
|
||||
auto resObjectPath = m_proxy->getObjectPath();
|
||||
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
||||
{
|
||||
auto resComplex = m_proxy->getComplex();
|
||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, DoesServerSideAsynchoronousMethodInParallel)
|
||||
{
|
||||
// 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{};
|
||||
auto call = [&](uint32_t param)
|
||||
{
|
||||
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
|
||||
++startedCount;
|
||||
while (!invoke) ;
|
||||
auto result = proxy.doOperationAsync(param);
|
||||
std::lock_guard<std::mutex> 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(); });
|
||||
|
||||
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
{
|
||||
std::mutex mtx;
|
||||
std::atomic<size_t> resultCount{};
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
auto call = [&]()
|
||||
{
|
||||
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
|
||||
++startedCount;
|
||||
while (!invoke) ;
|
||||
|
||||
size_t localResultCount{};
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
auto result = proxy.doOperationAsync(i % 2);
|
||||
if (result == (i % 2)) // Correct return value?
|
||||
localResultCount++;
|
||||
}
|
||||
|
||||
resultCount += localResultCount;
|
||||
};
|
||||
|
||||
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(); });
|
||||
|
||||
ASSERT_THAT(resultCount, Eq(1500));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
|
||||
{
|
||||
TestingProxy proxy("wrongDestination", OBJECT_PATH);
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
|
||||
{
|
||||
TestingProxy proxy(INTERFACE_NAME, "/wrong/path");
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
// Signals
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
|
||||
{
|
||||
auto count = m_proxy->getSimpleCallCount();
|
||||
m_adaptor->simpleSignal();
|
||||
usleep(10000);
|
||||
|
||||
ASSERT_THAT(m_proxy->getSimpleCallCount(), Eq(count + 1));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
|
||||
{
|
||||
m_adaptor->signalWithMap({{0, "zero"}, {1, "one"}});
|
||||
usleep(10000);
|
||||
|
||||
auto map = m_proxy->getMap();
|
||||
ASSERT_THAT(map[0], Eq("zero"));
|
||||
ASSERT_THAT(map[1], Eq("one"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
|
||||
{
|
||||
double d = 3.14;
|
||||
m_adaptor->signalWithVariant(3.14);
|
||||
usleep(10000);
|
||||
|
||||
ASSERT_THAT(m_proxy->getVariantValue(), d);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
|
||||
{
|
||||
m_adaptor->signalWithoutRegistration({"platform", {"av"}});
|
||||
usleep(10000);
|
||||
|
||||
auto signature = m_proxy->getSignatureFromSignal();
|
||||
ASSERT_THAT(signature["platform"], Eq("av"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, failsEmitingSignalOnNonexistentInterface)
|
||||
{
|
||||
ASSERT_THROW(m_adaptor->emitSignalOnNonexistentInterface(), sdbus::Error);
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
TEST_F(SdbusTestObject, ReadsReadPropertySuccesfully)
|
||||
{
|
||||
ASSERT_THAT(m_proxy->state(), Eq(STRING_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
|
||||
{
|
||||
auto x = 42;
|
||||
ASSERT_NO_THROW(m_proxy->action(x));
|
||||
ASSERT_THAT(m_proxy->action(), Eq(x));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, WritesToWritePropertySuccesfully)
|
||||
{
|
||||
auto x = true;
|
||||
ASSERT_NO_THROW(m_proxy->blocking(x));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CannotReadFromWriteProperty)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->blocking(), sdbus::Error);
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Connection_test.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "defs.h"
|
||||
|
||||
// sdbus
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
|
||||
// gmock
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
// STL
|
||||
#include <thread>
|
||||
|
||||
using ::testing::Eq;
|
||||
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(Connection, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(sdbus::createConnection());
|
||||
}
|
||||
|
||||
TEST(Connection, CanRequestRegisteredDbusName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
ASSERT_NO_THROW(connection->requestName(INTERFACE_NAME));
|
||||
connection->releaseName(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
TEST(Connection, CannotRequestNonregisteredDbusName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->requestName("some_random_not_supported_dbus_name"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanReleasedRequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
connection->requestName(INTERFACE_NAME);
|
||||
ASSERT_NO_THROW(connection->releaseName(INTERFACE_NAME));
|
||||
}
|
||||
|
||||
TEST(Connection, CannotReleaseNonrequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->releaseName("some_random_nonrequested_name"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanEnterAndLeaveProcessingLoop)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(INTERFACE_NAME);
|
||||
|
||||
std::thread t([&](){ connection->enterProcessingLoop(); });
|
||||
connection->leaveProcessingLoop();
|
||||
|
||||
t.join();
|
||||
|
||||
connection->releaseName(INTERFACE_NAME);
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file TestingAdaptor.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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_INTEGRATIONTESTS_TESTINGADAPTOR_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
|
||||
|
||||
#include "adaptor-glue.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
class TestingAdaptor : public sdbus::Interfaces<testing_adaptor>
|
||||
{
|
||||
public:
|
||||
TestingAdaptor(sdbus::IConnection& connection) :
|
||||
sdbus::Interfaces<::testing_adaptor>(connection, OBJECT_PATH) { }
|
||||
|
||||
virtual ~TestingAdaptor() { }
|
||||
|
||||
protected:
|
||||
|
||||
void noArgNoReturn() const { }
|
||||
|
||||
int32_t getInt() const { return INT32_VALUE; }
|
||||
|
||||
std::tuple<uint32_t, std::string> getTuple() const { return std::make_tuple(UINT32_VALUE, STRING_VALUE); }
|
||||
|
||||
double multiply(const int64_t& a, const double& b) const { return a * b; }
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const
|
||||
{
|
||||
std::vector<int16_t> res{x.get<1>()};
|
||||
auto y = std::get<std::vector<int16_t>>(x);
|
||||
res.insert(res.end(), y.begin(), y.end());
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Variant processVariant(sdbus::Variant& v)
|
||||
{
|
||||
sdbus::Variant res = static_cast<int32_t>(v.get<double>());
|
||||
return res;
|
||||
}
|
||||
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const
|
||||
{
|
||||
std::map<int32_t, sdbus::Variant> res;
|
||||
for (auto item : x)
|
||||
{
|
||||
res[item] = (item <= 0) ? std::get<0>(y) : std::get<1>(y);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const
|
||||
{
|
||||
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}));
|
||||
}
|
||||
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
{
|
||||
int32_t res{0};
|
||||
res += std::get<0>(a) + std::get<1>(a);
|
||||
res += std::get<0>(b) + std::get<1>(b);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
|
||||
{
|
||||
uint32_t res{0};
|
||||
for (auto x : a)
|
||||
{
|
||||
res += x;
|
||||
}
|
||||
for (auto x : b)
|
||||
{
|
||||
res += x;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t doOperationSync(uint32_t param)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||
return param;
|
||||
}
|
||||
|
||||
void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result)
|
||||
{
|
||||
if (param == 0)
|
||||
{
|
||||
// Don't sleep and return the result from this thread
|
||||
result.returnResults(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process asynchronously in another thread and return the result from there
|
||||
std::thread([param, result = std::move(result)]()
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||
result.returnResults(param);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
sdbus::Signature getSignature() const { return SIGNATURE_VALUE; }
|
||||
sdbus::ObjectPath getObjectPath() const { return OBJECT_PATH_VALUE; }
|
||||
|
||||
ComplexType getComplex() const
|
||||
{
|
||||
return { // map
|
||||
{
|
||||
0, // uint_64_t
|
||||
{ // struct
|
||||
{ // map
|
||||
{
|
||||
'a', // uint8_t
|
||||
{ // vector
|
||||
{ // struct
|
||||
"/object/path", // object path
|
||||
false,
|
||||
3.14,
|
||||
{ // map
|
||||
{0, "zero"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"a{t(a{ya(obva{is})}gs)}", // signature
|
||||
""
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::string state() { return STRING_VALUE; }
|
||||
uint32_t action() { return m_action; }
|
||||
void action(const uint32_t& value) { m_action = value; }
|
||||
bool blocking() { return m_blocking; }
|
||||
void blocking(const bool& value) { m_blocking = value; }
|
||||
|
||||
private:
|
||||
uint32_t m_action;
|
||||
bool m_blocking;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* INTEGRATIONTESTS_TESTINGADAPTOR_H_ */
|
@ -1,66 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file TestingProxy.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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_INTEGRATIONTESTS_TESTINGPROXY_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_
|
||||
|
||||
#include "proxy-glue.h"
|
||||
|
||||
class TestingProxy : public sdbus::ProxyInterfaces<::testing_proxy>
|
||||
{
|
||||
public:
|
||||
using sdbus::ProxyInterfaces<::testing_proxy>::ProxyInterfaces;
|
||||
|
||||
int getSimpleCallCount() const { return m_simpleCallCounter; }
|
||||
std::map<int32_t, std::string> getMap() const { return m_map; }
|
||||
double getVariantValue() const { return m_variantValue; }
|
||||
std::map<std::string, std::string> getSignatureFromSignal() const { return m_signature; }
|
||||
|
||||
protected:
|
||||
void onSimpleSignal() override { ++m_simpleCallCounter; }
|
||||
|
||||
void onSignalWithMap(const std::map<int32_t, std::string>& m) override { m_map = m; }
|
||||
|
||||
void onSignalWithVariant(const sdbus::Variant& v) override
|
||||
{
|
||||
m_variantValue = v.get<double>();
|
||||
}
|
||||
|
||||
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) override
|
||||
{
|
||||
m_signature[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
|
||||
}
|
||||
|
||||
private:
|
||||
int m_simpleCallCounter{};
|
||||
std::map<int32_t, std::string> m_map;
|
||||
double m_variantValue;
|
||||
std::map<std::string, std::string> m_signature;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ */
|
@ -1,168 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file adaptor-glue.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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_INTEGRATIONTESTS_ADAPTOR_GLUE_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
// sdbus
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
using ComplexType = std::map<
|
||||
uint64_t,
|
||||
sdbus::Struct<
|
||||
std::map<
|
||||
uint8_t,
|
||||
std::vector<
|
||||
sdbus::Struct<
|
||||
sdbus::ObjectPath,
|
||||
bool,
|
||||
sdbus::Variant,
|
||||
std::map<int, std::string>
|
||||
>
|
||||
>
|
||||
>,
|
||||
sdbus::Signature,
|
||||
std::string // char* leads to type and memory issues, std::string is best choice
|
||||
>
|
||||
>;
|
||||
|
||||
class testing_adaptor
|
||||
{
|
||||
|
||||
protected:
|
||||
testing_adaptor(sdbus::IObject& object) :
|
||||
object_(object)
|
||||
{
|
||||
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
|
||||
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getInt(); });
|
||||
object_.registerMethod("getTuple").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getTuple(); });
|
||||
|
||||
object_.registerMethod("multiply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
|
||||
object_.registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x){ return this->getInts16FromStruct(x); });
|
||||
|
||||
object_.registerMethod("processVariant").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Variant& v){ return this->processVariant(v); });
|
||||
|
||||
object_.registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x ,y); });
|
||||
|
||||
object_.registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getStructInStruct(); });
|
||||
|
||||
object_.registerMethod("sumStructItems").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b){
|
||||
return this->sumStructItems(a, b);
|
||||
});
|
||||
|
||||
object_.registerMethod("sumVectorItems").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const std::vector<uint16_t>& a, const std::vector<uint64_t>& b){
|
||||
return this->sumVectorItems(a, b);
|
||||
});
|
||||
|
||||
object_.registerMethod("doOperationSync").onInterface(INTERFACE_NAME).implementedAs([this](uint32_t param)
|
||||
{
|
||||
return this->doOperationSync(param);
|
||||
});
|
||||
|
||||
object_.registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Result<uint32_t> result, uint32_t param)
|
||||
{
|
||||
this->doOperationAsync(param, std::move(result));
|
||||
});
|
||||
|
||||
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
|
||||
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
|
||||
|
||||
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); });
|
||||
|
||||
// registration of signals is optional, it is useful because of introspection
|
||||
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>();
|
||||
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>();
|
||||
|
||||
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); });
|
||||
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); });
|
||||
object_.registerProperty("blocking").onInterface(INTERFACE_NAME)./*withGetter([this](){ return this->blocking(); }).*/withSetter([this](const bool& value){ this->blocking(value); });
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
void simpleSignal()
|
||||
{
|
||||
object_.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void signalWithMap(const std::map<int32_t, std::string>& map)
|
||||
{
|
||||
object_.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(map);
|
||||
}
|
||||
|
||||
void signalWithVariant(const sdbus::Variant& v)
|
||||
{
|
||||
object_.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(v);
|
||||
}
|
||||
|
||||
void signalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
{
|
||||
object_.emitSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).withArguments(s);
|
||||
}
|
||||
|
||||
void emitSignalOnNonexistentInterface()
|
||||
{
|
||||
object_.emitSignal("simpleSignal").onInterface("interfaceThatDoesNotExists");
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void noArgNoReturn() const = 0;
|
||||
virtual int32_t getInt() const = 0;
|
||||
virtual std::tuple<uint32_t, std::string> getTuple() const = 0;
|
||||
virtual double multiply(const int64_t& a, const double& b) const = 0;
|
||||
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const = 0;
|
||||
virtual sdbus::Variant processVariant(sdbus::Variant& v) = 0;
|
||||
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const = 0;
|
||||
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const = 0;
|
||||
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) = 0;
|
||||
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) = 0;
|
||||
virtual uint32_t doOperationSync(uint32_t param) = 0;
|
||||
virtual void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) = 0;
|
||||
virtual sdbus::Signature getSignature() const = 0;
|
||||
virtual sdbus::ObjectPath getObjectPath() const = 0;
|
||||
virtual ComplexType getComplex() const = 0;
|
||||
|
||||
virtual std::string state() = 0;
|
||||
virtual uint32_t action() = 0;
|
||||
virtual void action(const uint32_t& value) = 0;
|
||||
virtual bool blocking() = 0;
|
||||
virtual void blocking(const bool& value) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_ */
|
@ -1,204 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file proxy-glue.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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_INTEGRATIONTESTS_PROXY_GLUE_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
// sdbus
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
class testing_proxy
|
||||
{
|
||||
protected:
|
||||
testing_proxy(sdbus::IObjectProxy& object) :
|
||||
object_(object)
|
||||
{
|
||||
object_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
|
||||
object_.uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& map){ this->onSignalWithMap(map); });
|
||||
object_.uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& v){ this->onSignalWithVariant(v); });
|
||||
|
||||
object_.uponSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
{ this->onSignalWithoutRegistration(s); });
|
||||
}
|
||||
|
||||
|
||||
virtual void onSimpleSignal() = 0;
|
||||
virtual void onSignalWithMap(const std::map<int32_t, std::string>& map) = 0;
|
||||
virtual void onSignalWithVariant(const sdbus::Variant& v) = 0;
|
||||
virtual void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) = 0;
|
||||
|
||||
public:
|
||||
void noArgNoReturn()
|
||||
{
|
||||
object_.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
int32_t getInt()
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<uint32_t, std::string> getTuple()
|
||||
{
|
||||
std::tuple<uint32_t, std::string> result;
|
||||
object_.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
double multiply(const int64_t& a, const double& b)
|
||||
{
|
||||
double result;
|
||||
object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x)
|
||||
{
|
||||
std::vector<int16_t> result;
|
||||
object_.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(x).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Variant processVariant(const sdbus::Variant& v)
|
||||
{
|
||||
sdbus::Variant result;
|
||||
object_.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(v).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;
|
||||
object_.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;
|
||||
object_.callMethod("getStructInStruct").onInterface(INTERFACE_NAME).withArguments().storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
|
||||
{
|
||||
uint32_t result;
|
||||
object_.callMethod("sumVectorItems").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t doOperationSync(uint32_t param)
|
||||
{
|
||||
uint32_t result;
|
||||
object_.callMethod("doOperationSync").onInterface(INTERFACE_NAME).withArguments(param).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t doOperationAsync(uint32_t param)
|
||||
{
|
||||
uint32_t result;
|
||||
object_.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(param).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Signature getSignature()
|
||||
{
|
||||
sdbus::Signature result;
|
||||
object_.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::ObjectPath getObjectPath()
|
||||
{
|
||||
sdbus::ObjectPath result;
|
||||
object_.callMethod("getObjectPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
ComplexType getComplex()
|
||||
{
|
||||
ComplexType result;
|
||||
object_.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t callNonexistentMethod()
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("callNonexistentMethod").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t callMethodOnNonexistentInterface()
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("someMethod").onInterface("interfaceThatDoesNotExist").storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string state()
|
||||
{
|
||||
return object_.getProperty("state").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
uint32_t action()
|
||||
{
|
||||
return object_.getProperty("action").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void action(const uint32_t& value)
|
||||
{
|
||||
object_.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
bool blocking()
|
||||
{
|
||||
return object_.getProperty("blocking").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void blocking(const bool& value)
|
||||
{
|
||||
object_.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
sdbus::IObjectProxy& object_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_ */
|
165
tests/CMakeLists.txt
Normal file
165
tests/CMakeLists.txt
Normal file
@ -0,0 +1,165 @@
|
||||
#-------------------------------
|
||||
# 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)
|
||||
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})
|
||||
if(TARGET PkgConfig::GMock)
|
||||
add_library(GTest::gmock ALIAS PkgConfig::GMock)
|
||||
endif()
|
||||
endif()
|
||||
# GTest was not found in the system, build it on our own
|
||||
if (NOT TARGET GTest::gmock)
|
||||
include(FetchContent)
|
||||
|
||||
message("Fetching googletest v${GOOGLETEST_VERSION}...")
|
||||
FetchContent_Declare(googletest
|
||||
GIT_REPOSITORY ${GOOGLETEST_GIT_REPO}
|
||||
GIT_TAG release-${GOOGLETEST_VERSION}
|
||||
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()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#-------------------------------
|
||||
# SOURCE FILES CONFIGURATION
|
||||
#-------------------------------
|
||||
|
||||
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}/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_SRCS
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusConnectionTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusGeneralTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusMethodsTests.cpp
|
||||
${INTEGRATIONTESTS_SOURCE_DIR}/DBusAsyncMethodsTests.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)
|
||||
|
||||
set(PERFTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/perftests)
|
||||
set(STRESSTESTS_CLIENT_SRCS
|
||||
${PERFTESTS_SOURCE_DIR}/client.cpp
|
||||
${PERFTESTS_SOURCE_DIR}/perftests-proxy.h)
|
||||
set(STRESSTESTS_SERVER_SRCS
|
||||
${PERFTESTS_SOURCE_DIR}/server.cpp
|
||||
${PERFTESTS_SOURCE_DIR}/perftests-adaptor.h)
|
||||
|
||||
set(STRESSTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stresstests)
|
||||
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})
|
||||
|
||||
#----------------------------------
|
||||
# BUILD INFORMATION
|
||||
#----------------------------------
|
||||
|
||||
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
|
||||
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
|
||||
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib 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)
|
||||
|
||||
# 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)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if(ENABLE_PERF_TESTS)
|
||||
message(STATUS "Building with performance tests")
|
||||
add_executable(sdbus-c++-perf-tests-client ${STRESSTESTS_CLIENT_SRCS})
|
||||
target_link_libraries(sdbus-c++-perf-tests-client sdbus-c++ Threads::Threads)
|
||||
add_executable(sdbus-c++-perf-tests-server ${STRESSTESTS_SERVER_SRCS})
|
||||
target_link_libraries(sdbus-c++-perf-tests-server sdbus-c++ Threads::Threads)
|
||||
endif()
|
||||
|
||||
if(ENABLE_STRESS_TESTS)
|
||||
message(STATUS "Building with stress tests")
|
||||
add_executable(sdbus-c++-stress-tests ${STRESSTESTS_SRCS})
|
||||
target_link_libraries(sdbus-c++-stress-tests sdbus-c++ Threads::Threads)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
|
||||
set(TESTS_INSTALL_PATH "/opt/test/bin" 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 /etc/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 /etc/dbus-1/system.d COMPONENT 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 /etc/dbus-1/system.d COMPONENT test)
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# RUNNING THE TESTS UPON BUILD
|
||||
#----------------------------------
|
||||
|
||||
if(NOT CMAKE_CROSSCOMPILING)
|
||||
add_test(NAME sdbus-c++-unit-tests COMMAND sdbus-c++-unit-tests)
|
||||
add_test(NAME sdbus-c++-integration-tests COMMAND sdbus-c++-integration-tests)
|
||||
endif()
|
240
tests/integrationtests/DBusAsyncMethodsTests.cpp
Normal file
240
tests/integrationtests/DBusAsyncMethodsTests.cpp
Normal file
@ -0,0 +1,240 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2020 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file DBusAsyncMethodsTests.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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 "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
#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::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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (err == nullptr)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*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
|
||||
future.get();
|
||||
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
ASSERT_THAT(e.getName(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
|
||||
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Method call timed out"));
|
||||
auto measuredTimeout = std::chrono::steady_clock::now() - start;
|
||||
ASSERT_THAT(measuredTimeout, Le(50ms));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
|
||||
{
|
||||
// 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{};
|
||||
auto call = [&](uint32_t param)
|
||||
{
|
||||
TestProxy proxy{BUS_NAME, OBJECT_PATH};
|
||||
++startedCount;
|
||||
while (!invoke) ;
|
||||
auto result = proxy.doOperationAsync(param);
|
||||
std::lock_guard<std::mutex> 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(); });
|
||||
|
||||
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
{
|
||||
std::atomic<size_t> resultCount{};
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
auto call = [&]()
|
||||
{
|
||||
TestProxy proxy{BUS_NAME, OBJECT_PATH};
|
||||
++startedCount;
|
||||
while (!invoke) ;
|
||||
|
||||
size_t localResultCount{};
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
auto result = proxy.doOperationAsync(i % 2);
|
||||
if (result == (i % 2)) // Correct return value?
|
||||
localResultCount++;
|
||||
}
|
||||
|
||||
resultCount += localResultCount;
|
||||
};
|
||||
|
||||
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(); });
|
||||
|
||||
ASSERT_THAT(resultCount, Eq(1500));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
});
|
||||
|
||||
m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(100));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
|
||||
{
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
|
||||
|
||||
auto call = m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_TRUE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, 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);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
|
||||
{
|
||||
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);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_FALSE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, 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); });
|
||||
|
||||
auto call = m_proxy->doOperationClientSideAsync(0);
|
||||
(void) future.get(); // Wait for the call to finish
|
||||
|
||||
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
|
||||
{
|
||||
sdbus::PendingAsyncCall call;
|
||||
|
||||
ASSERT_FALSE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, SupportsAsyncCallCopyAssignment)
|
||||
{
|
||||
sdbus::PendingAsyncCall call;
|
||||
|
||||
call = m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_TRUE(call.isPending());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
|
||||
{
|
||||
if (err == nullptr)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*err));
|
||||
});
|
||||
|
||||
m_proxy->doErroneousOperationClientSideAsync();
|
||||
|
||||
ASSERT_THROW(future.get(), sdbus::Error);
|
||||
}
|
133
tests/integrationtests/DBusConnectionTests.cpp
Normal file
133
tests/integrationtests/DBusConnectionTests.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2020 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file DBusConnectionTests.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Defs.h"
|
||||
|
||||
// sdbus
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
|
||||
// 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 -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(Connection, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(auto con = sdbus::createConnection());
|
||||
}
|
||||
|
||||
TEST(Connection, CanRequestRegisteredDbusName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
ASSERT_NO_THROW(connection->requestName(BUS_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)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->requestName("some.random.not.supported.dbus.name"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanReleasedRequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
connection->requestName(BUS_NAME);
|
||||
ASSERT_NO_THROW(connection->releaseName(BUS_NAME));
|
||||
}
|
||||
|
||||
TEST(Connection, CannotReleaseNonrequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanEnterAndLeaveEventLoop)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(BUS_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);
|
||||
}
|
54
tests/integrationtests/DBusGeneralTests.cpp
Normal file
54
tests/integrationtests/DBusGeneralTests.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2020 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file DBusGeneralTests.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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 "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <future>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace sdbus::test;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(BUS_NAME);
|
||||
|
||||
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
|
||||
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
|
||||
}
|
274
tests/integrationtests/DBusMethodsTests.cpp
Normal file
274
tests/integrationtests/DBusMethodsTests.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2020 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file DBusMethodsTests.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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 "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
#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::Le;
|
||||
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, CallsEmptyMethodSuccesfully)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
|
||||
{
|
||||
auto resInt = m_proxy->getInt();
|
||||
ASSERT_THAT(resInt, Eq(INT32_VALUE));
|
||||
|
||||
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
|
||||
{
|
||||
auto resTuple = 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)
|
||||
{
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
|
||||
auto vectorRes = m_proxy->getInts16FromStruct(a);
|
||||
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{
|
||||
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
|
||||
};
|
||||
vectorRes = m_proxy->getInts16FromStruct(b);
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
|
||||
{
|
||||
sdbus::Variant v{DOUBLE_VALUE};
|
||||
auto variantRes = m_proxy->processVariant(v);
|
||||
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
|
||||
{
|
||||
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{{-2, false}, {0, false}, {2, 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)
|
||||
{
|
||||
auto val = m_proxy->getStructInStruct();
|
||||
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
|
||||
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
|
||||
{
|
||||
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
|
||||
{
|
||||
auto val = m_proxy->sumVectorItems({1, 7}, {2, 3});
|
||||
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
|
||||
{
|
||||
auto resSignature = m_proxy->getSignature();
|
||||
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
|
||||
{
|
||||
auto resObjectPath = m_proxy->getObjPath();
|
||||
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
|
||||
{
|
||||
auto resUnixFd = m_proxy->getUnixFd();
|
||||
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
||||
{
|
||||
auto resComplex = m_proxy->getComplex();
|
||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
|
||||
{
|
||||
auto res = m_proxy->doOperationWithTimeout(500ms, 20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
|
||||
ASSERT_THAT(res, Eq(20));
|
||||
}
|
||||
|
||||
TEST_F(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
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
ASSERT_THAT(e.getName(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
|
||||
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Method call timed out"));
|
||||
auto measuredTimeout = std::chrono::steady_clock::now() - start;
|
||||
ASSERT_THAT(measuredTimeout, Le(50ms));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_proxy->throwError();
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
ASSERT_THAT(e.getName(), Eq("org.freedesktop.DBus.Error.AccessDenied"));
|
||||
ASSERT_THAT(e.getMessage(), Eq("A test error occurred (Operation not permitted)"));
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
|
||||
{
|
||||
TestProxy proxy("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
|
||||
{
|
||||
TestProxy proxy(BUS_NAME, "/sdbuscpp/path/that/does/not/exist");
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
|
||||
{
|
||||
m_proxy->emitTwoSimpleSignals();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
|
||||
{
|
||||
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("doOperation"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
|
||||
{
|
||||
m_proxy->doOperationAsync(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"));
|
||||
}
|
||||
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
|
||||
{
|
||||
s_adaptorConnection->setMethodCallTimeout(5000000);
|
||||
ASSERT_THAT(s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
|
||||
}
|
||||
#else
|
||||
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
|
||||
{
|
||||
ASSERT_THROW(s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
|
||||
ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
|
||||
}
|
||||
#endif
|
85
tests/integrationtests/DBusPropertiesTests.cpp
Normal file
85
tests/integrationtests/DBusPropertiesTests.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2020 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file DBusPropertiesTests.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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 "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
#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)
|
||||
{
|
||||
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->setStateProperty("new_value"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
|
||||
{
|
||||
uint32_t newActionValue = 5678;
|
||||
|
||||
m_proxy->action(newActionValue);
|
||||
|
||||
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
|
||||
{
|
||||
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()));
|
||||
}
|
157
tests/integrationtests/DBusSignalsTests.cpp
Normal file
157
tests/integrationtests/DBusSignalsTests.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file AdaptorAndProxy_test.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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 "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#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)
|
||||
{
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
|
||||
{
|
||||
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
|
||||
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
|
||||
{
|
||||
auto otherBusName = BUS_NAME + "2";
|
||||
auto connection2 = sdbus::createConnection(otherBusName);
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*connection2, OBJECT_PATH);
|
||||
|
||||
adaptor2->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
|
||||
{
|
||||
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"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
|
||||
{
|
||||
double d = 3.14;
|
||||
m_adaptor->emitSignalWithVariant(d);
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
|
||||
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
|
||||
{
|
||||
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
|
||||
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
|
||||
{
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
waitUntil(m_proxy->m_gotSimpleSignal);
|
||||
|
||||
ASSERT_THAT(m_proxy->m_signalMsg, NotNull());
|
||||
ASSERT_THAT(m_proxy->m_signalMemberName, Eq("simpleSignal"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, UnregistersSignalHandler)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
|
||||
{
|
||||
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(m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, ReRegistersSignalHandler)
|
||||
{
|
||||
// unregister simple-signal handler
|
||||
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
|
||||
|
||||
// re-register simple-signal handler
|
||||
ASSERT_NO_THROW(m_proxy->reRegisterSimpleSignalHandler());
|
||||
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
}
|
258
tests/integrationtests/DBusStandardInterfacesTests.cpp
Normal file
258
tests/integrationtests/DBusStandardInterfacesTests.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2020 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file DBusStandardInterfacesTests.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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 "TestFixture.h"
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
#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 namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
using SdbusTestObject = TestFixture;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST_F(SdbusTestObject, PingsViaPeerInterface)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->Ping());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
|
||||
{
|
||||
// If /etc/machine-id does not exist in your system (which is very likely because you have
|
||||
// a non-systemd Linux), org.freedesktop.DBus.Peer.GetMachineId() will not work. To solve
|
||||
// this, you can create /etc/machine-id yourself as symlink to /var/lib/dbus/machine-id,
|
||||
// and then org.freedesktop.DBus.Peer.GetMachineId() will start to work.
|
||||
if (::access("/etc/machine-id", F_OK) == -1)
|
||||
GTEST_SKIP() << "/etc/machine-id file does not exist, GetMachineId() will not work";
|
||||
|
||||
ASSERT_NO_THROW(m_proxy->GetMachineId());
|
||||
}
|
||||
|
||||
// TODO: Adjust expected xml and uncomment this test
|
||||
//TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
|
||||
//{
|
||||
// ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
|
||||
//}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
|
||||
{
|
||||
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2345;
|
||||
|
||||
m_proxy->Set(INTERFACE_NAME, "action", newActionValue);
|
||||
|
||||
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, 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*/ )
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at("blocking").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"});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
ASSERT_THAT(invalidatedProperties, SizeIs(1));
|
||||
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
|
||||
{
|
||||
m_adaptor.reset();
|
||||
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
|
||||
|
||||
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
|
||||
{
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
|
||||
const auto objectsInterfacesAndProperties = 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));
|
||||
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2)
|
||||
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
|
||||
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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"));
|
||||
#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"));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
|
||||
{
|
||||
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 )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
|
||||
#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"));
|
||||
#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"));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesAddedSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
ASSERT_THAT(interfaces, SizeIs(1));
|
||||
EXPECT_THAT(interfaces[0], Eq(INTERFACE_NAME));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<std::string>& interfaces )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
m_adaptor->emitInterfacesRemovedSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file defs.h
|
||||
* @file Defs.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
@ -27,9 +28,16 @@
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_
|
||||
|
||||
#include "sdbus-c++/Types.h"
|
||||
#include <chrono>
|
||||
#include <ostream>
|
||||
|
||||
const std::string INTERFACE_NAME{"com.kistler.testsdbuscpp"};
|
||||
const std::string OBJECT_PATH{"/"};
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
|
||||
const std::string BUS_NAME = INTERFACE_NAME;
|
||||
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"};
|
||||
|
||||
constexpr const uint8_t UINT8_VALUE{1};
|
||||
constexpr const int16_t INT16_VALUE{21};
|
||||
@ -40,7 +48,26 @@ 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 int UNIX_FD_VALUE = 0;
|
||||
|
||||
const std::string DEFAULT_STATE_VALUE{"default-state-value"};
|
||||
const uint32_t DEFAULT_ACTION_VALUE{999};
|
||||
const bool DEFAULT_BLOCKING_VALUE{true};
|
||||
|
||||
constexpr const double DOUBLE_VALUE{3.24L};
|
||||
|
||||
}}
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ */
|
426
tests/integrationtests/TestAdaptor.cpp
Normal file
426
tests/integrationtests/TestAdaptor.cpp
Normal file
@ -0,0 +1,426 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file TestAdaptor.cpp
|
||||
*
|
||||
* Created on: May 23, 2020
|
||||
* 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 "TestAdaptor.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) :
|
||||
AdaptorInterfaces(connection, path)
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
TestAdaptor::~TestAdaptor()
|
||||
{
|
||||
unregisterAdaptor();
|
||||
}
|
||||
|
||||
void TestAdaptor::noArgNoReturn()
|
||||
{
|
||||
}
|
||||
|
||||
int32_t TestAdaptor::getInt()
|
||||
{
|
||||
return INT32_VALUE;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
|
||||
void TestAdaptor::multiplyWithNoReply(const int64_t& a, const double& b)
|
||||
{
|
||||
m_multiplyResult = a * b;
|
||||
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> res{x.get<1>()};
|
||||
auto y = std::get<std::vector<int16_t>>(x);
|
||||
res.insert(res.end(), y.begin(), y.end());
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Variant TestAdaptor::processVariant(const sdbus::Variant& v)
|
||||
{
|
||||
sdbus::Variant res = static_cast<int32_t>(v.get<double>());
|
||||
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> res;
|
||||
for (auto item : x)
|
||||
{
|
||||
res[item] = (item <= 0) ? std::get<0>(y) : std::get<1>(y);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> TestAdaptor::getStructInStruct()
|
||||
{
|
||||
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}));
|
||||
}
|
||||
|
||||
int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
{
|
||||
int32_t res{0};
|
||||
res += std::get<0>(a) + std::get<1>(a);
|
||||
res += std::get<0>(b) + std::get<1>(b);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t TestAdaptor::sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
|
||||
{
|
||||
uint32_t res{0};
|
||||
for (auto x : a)
|
||||
{
|
||||
res += x;
|
||||
}
|
||||
for (auto x : b)
|
||||
{
|
||||
res += x;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
return param;
|
||||
}
|
||||
|
||||
void TestAdaptor::doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t param)
|
||||
{
|
||||
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
|
||||
m_methodCallMemberName = m_methodCallMsg->getMemberName();
|
||||
|
||||
if (param == 0)
|
||||
{
|
||||
// Don't sleep and return the result from this thread
|
||||
result.returnResults(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process asynchronously in another thread and return the result from there
|
||||
std::thread([param, result = std::move(result)]()
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||
result.returnResults(param);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
sdbus::Signature TestAdaptor::getSignature()
|
||||
{
|
||||
return SIGNATURE_VALUE;
|
||||
}
|
||||
sdbus::ObjectPath TestAdaptor::getObjPath()
|
||||
{
|
||||
return OBJECT_PATH_VALUE;
|
||||
}
|
||||
sdbus::UnixFd TestAdaptor::getUnixFd()
|
||||
{
|
||||
return sdbus::UnixFd{UNIX_FD_VALUE};
|
||||
}
|
||||
|
||||
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> TestAdaptor::getComplex()
|
||||
{
|
||||
return { // map
|
||||
{
|
||||
0, // uint_64_t
|
||||
{ // struct
|
||||
{ // map
|
||||
{
|
||||
23, // uint8_t
|
||||
{ // vector
|
||||
{ // struct
|
||||
"/object/path", // object path
|
||||
false,
|
||||
Variant{3.14},
|
||||
{ // map
|
||||
{0, "zero"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"a{t(a{ya(obva{is})}gs)}", // signature
|
||||
std::string{}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void TestAdaptor::throwError()
|
||||
{
|
||||
m_wasThrowErrorCalled = true;
|
||||
throw sdbus::createError(1, "A test error occurred");
|
||||
}
|
||||
|
||||
void TestAdaptor::throwErrorWithNoReply()
|
||||
{
|
||||
TestAdaptor::throwError();
|
||||
}
|
||||
|
||||
void TestAdaptor::doPrivilegedStuff()
|
||||
{
|
||||
// Intentionally left blank
|
||||
}
|
||||
|
||||
void TestAdaptor::emitTwoSimpleSignals()
|
||||
{
|
||||
emitSimpleSignal();
|
||||
emitSignalWithMap({});
|
||||
}
|
||||
|
||||
std::string TestAdaptor::state()
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
uint32_t TestAdaptor::action()
|
||||
{
|
||||
return m_action;
|
||||
}
|
||||
|
||||
void TestAdaptor::action(const uint32_t& value)
|
||||
{
|
||||
m_action = value;
|
||||
}
|
||||
|
||||
bool TestAdaptor::blocking()
|
||||
{
|
||||
return m_blocking;
|
||||
}
|
||||
|
||||
void TestAdaptor::blocking(const bool& value)
|
||||
{
|
||||
m_propertySetMsg = getObject().getCurrentlyProcessedMessage();
|
||||
m_propertySetSender = m_propertySetMsg->getSender();
|
||||
|
||||
m_blocking = value;
|
||||
}
|
||||
|
||||
void TestAdaptor::emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
{
|
||||
getObject().emitSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).withArguments(s);
|
||||
}
|
||||
|
||||
std::string TestAdaptor::getExpectedXmlApiDescription() const
|
||||
{
|
||||
return
|
||||
R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.freedesktop.DBus.Peer">
|
||||
<method name="Ping"/>
|
||||
<method name="GetMachineId">
|
||||
<arg type="s" name="machine_uuid" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="org.freedesktop.DBus.Introspectable">
|
||||
<method name="Introspect">
|
||||
<arg name="data" type="s" direction="out"/>
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="org.freedesktop.DBus.Properties">
|
||||
<method name="Get">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="out" type="v"/>
|
||||
</method>
|
||||
<method name="GetAll">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="properties" direction="out" type="a{sv}"/>
|
||||
</method>
|
||||
<method name="Set">
|
||||
<arg name="interface" direction="in" type="s"/>
|
||||
<arg name="property" direction="in" type="s"/>
|
||||
<arg name="value" direction="in" type="v"/>
|
||||
</method>
|
||||
<signal name="PropertiesChanged">
|
||||
<arg type="s" name="interface"/>
|
||||
<arg type="a{sv}" name="changed_properties"/>
|
||||
<arg type="as" name="invalidated_properties"/>
|
||||
</signal>
|
||||
</interface>
|
||||
<interface name="org.freedesktop.DBus.ObjectManager">
|
||||
<method name="GetManagedObjects">
|
||||
<arg type="a{oa{sa{sv}}}" name="object_paths_interfaces_and_properties" direction="out"/>
|
||||
</method>
|
||||
<signal name="InterfacesAdded">
|
||||
<arg type="o" name="object_path"/>
|
||||
<arg type="a{sa{sv}}" name="interfaces_and_properties"/>
|
||||
</signal>
|
||||
<signal name="InterfacesRemoved">
|
||||
<arg type="o" name="object_path"/>
|
||||
<arg type="as" name="interfaces"/>
|
||||
</signal>
|
||||
</interface>
|
||||
<interface name="org.sdbuscpp.integrationtests">
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
<method name="doOperation">
|
||||
<arg type="u" direction="in"/>
|
||||
<arg type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="doOperationAsync">
|
||||
<arg type="u" direction="in"/>
|
||||
<arg type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="doPrivilegedStuff">
|
||||
<annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
|
||||
</method>
|
||||
<method name="emitTwoSimpleSignals">
|
||||
</method>
|
||||
<method name="getComplex">
|
||||
<arg type="a{t(a{ya(obva{is})}gs)}" direction="out"/>
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
</method>
|
||||
<method name="getInt">)delimiter"
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
R"delimiter(
|
||||
<arg type="i" name="anInt" direction="out"/>)delimiter"
|
||||
#else
|
||||
R"delimiter(
|
||||
<arg type="i" direction="out"/>)delimiter"
|
||||
#endif
|
||||
R"delimiter(
|
||||
</method>
|
||||
<method name="getInts16FromStruct">
|
||||
<arg type="(yndsan)" direction="in"/>
|
||||
<arg type="an" direction="out"/>
|
||||
</method>
|
||||
<method name="getMapOfVariants">)delimiter"
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
R"delimiter(
|
||||
<arg type="ai" name="x" direction="in"/>
|
||||
<arg type="(vv)" name="y" direction="in"/>
|
||||
<arg type="a{iv}" name="aMapOfVariants" direction="out"/>)delimiter"
|
||||
#else
|
||||
R"delimiter(
|
||||
<arg type="ai" direction="in"/>
|
||||
<arg type="(vv)" direction="in"/>
|
||||
<arg type="a{iv}" direction="out"/>)delimiter"
|
||||
#endif
|
||||
R"delimiter(
|
||||
</method>
|
||||
<method name="getObjPath">
|
||||
<arg type="o" direction="out"/>
|
||||
</method>
|
||||
<method name="getSignature">
|
||||
<arg type="g" direction="out"/>
|
||||
</method>
|
||||
<method name="getStructInStruct">
|
||||
<arg type="(s(a{ii}))" direction="out"/>
|
||||
</method>
|
||||
<method name="getTuple">
|
||||
<arg type="u" direction="out"/>
|
||||
<arg type="s" direction="out"/>
|
||||
</method>
|
||||
<method name="getUnixFd">
|
||||
<arg type="h" direction="out"/>
|
||||
</method>
|
||||
<method name="multiply">)delimiter"
|
||||
#if LIBSYSTEMD_VERSION>=242
|
||||
R"delimiter(
|
||||
<arg type="x" name="a" direction="in"/>
|
||||
<arg type="d" name="b" direction="in"/>
|
||||
<arg type="d" name="result" direction="out"/>)delimiter"
|
||||
#else
|
||||
R"delimiter(
|
||||
<arg type="x" direction="in"/>
|
||||
<arg type="d" direction="in"/>
|
||||
<arg type="d" direction="out"/>)delimiter"
|
||||
#endif
|
||||
R"delimiter(
|
||||
</method>
|
||||
<method name="multiplyWithNoReply">
|
||||
<arg type="x" direction="in"/>
|
||||
<arg type="d" direction="in"/>
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||
</method>
|
||||
<method name="noArgNoReturn">
|
||||
</method>
|
||||
<method name="processVariant">
|
||||
<arg type="v" direction="in"/>
|
||||
<arg type="v" direction="out"/>
|
||||
</method>
|
||||
<method name="sumStructItems">
|
||||
<arg type="(yq)" direction="in"/>
|
||||
<arg type="(ix)" direction="in"/>
|
||||
<arg type="i" direction="out"/>
|
||||
</method>
|
||||
<method name="sumVectorItems">
|
||||
<arg type="aq" direction="in"/>
|
||||
<arg type="at" direction="in"/>
|
||||
<arg type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="throwError">
|
||||
</method>
|
||||
<method name="throwErrorWithNoReply">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||
</method>
|
||||
<signal name="signalWithMap">
|
||||
<arg type="a{is}"/>
|
||||
</signal>
|
||||
<signal name="signalWithVariant">
|
||||
<arg type="v"/>
|
||||
</signal>
|
||||
<signal name="simpleSignal">
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
</signal>
|
||||
<property name="action" type="u" access="readwrite">
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates"/>
|
||||
</property>
|
||||
<property name="blocking" type="b" access="readwrite">
|
||||
</property>
|
||||
<property name="state" type="s" access="read">
|
||||
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
|
||||
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
|
||||
</property>
|
||||
</interface>
|
||||
</node>
|
||||
)delimiter";
|
||||
}
|
||||
|
||||
}}
|
114
tests/integrationtests/TestAdaptor.h
Normal file
114
tests/integrationtests/TestAdaptor.h
Normal file
@ -0,0 +1,114 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file TestAdaptor.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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_INTEGRATIONTESTS_TESTADAPTOR_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTADAPTOR_H_
|
||||
|
||||
#include "integrationtests-adaptor.h"
|
||||
#include "Defs.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
class ObjectManagerTestAdaptor final : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
|
||||
{
|
||||
public:
|
||||
ObjectManagerTestAdaptor(sdbus::IConnection& connection, std::string path) :
|
||||
AdaptorInterfaces(connection, std::move(path))
|
||||
{
|
||||
registerAdaptor();
|
||||
}
|
||||
|
||||
~ObjectManagerTestAdaptor()
|
||||
{
|
||||
unregisterAdaptor();
|
||||
}
|
||||
};
|
||||
|
||||
class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integrationtests_adaptor
|
||||
, sdbus::Properties_adaptor
|
||||
, sdbus::ManagedObject_adaptor >
|
||||
{
|
||||
public:
|
||||
TestAdaptor(sdbus::IConnection& connection, const std::string& path);
|
||||
~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;
|
||||
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 sumVectorItems(const std::vector<uint16_t>& arg0, const std::vector<uint64_t>& arg1) override;
|
||||
uint32_t doOperation(const uint32_t& arg0) override;
|
||||
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override;
|
||||
sdbus::Signature getSignature() override;
|
||||
sdbus::ObjectPath getObjPath() override;
|
||||
sdbus::UnixFd getUnixFd() override;
|
||||
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() override;
|
||||
void throwError() override;
|
||||
void throwErrorWithNoReply() override;
|
||||
void doPrivilegedStuff() override;
|
||||
void emitTwoSimpleSignals() override;
|
||||
|
||||
uint32_t action() override;
|
||||
void action(const uint32_t& 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;
|
||||
|
||||
private:
|
||||
const std::string m_state{DEFAULT_STATE_VALUE};
|
||||
uint32_t m_action{DEFAULT_ACTION_VALUE};
|
||||
bool m_blocking{DEFAULT_BLOCKING_VALUE};
|
||||
|
||||
public: // for tests
|
||||
// For dont-expect-reply method call verifications
|
||||
mutable std::atomic<bool> m_wasMultiplyCalled{false};
|
||||
mutable double m_multiplyResult{};
|
||||
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
|
||||
|
||||
const Message* m_methodCallMsg{};
|
||||
std::string m_methodCallMemberName;
|
||||
const Message* m_propertySetMsg{};
|
||||
std::string m_propertySetSender;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* INTEGRATIONTESTS_TESTADAPTOR_H_ */
|
@ -1,9 +1,10 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file Object.cpp
|
||||
* @file TestAdaptor.cpp
|
||||
*
|
||||
* Created on: Nov 8, 2016
|
||||
* Created on: May 23, 2020
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
@ -23,21 +24,11 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include "Object.h"
|
||||
#include "TestFixture.h"
|
||||
|
||||
namespace sdbus {
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
|
||||
std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection();
|
||||
|
||||
MethodResult::MethodResult(const MethodCall& msg, sdbus::internal::Object& object)
|
||||
: call_(msg)
|
||||
, object_(&object)
|
||||
{
|
||||
}
|
||||
|
||||
void MethodResult::send(const MethodReply& reply) const
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
object_->sendReplyAsynchronously(reply);
|
||||
}
|
||||
|
||||
}
|
||||
}}
|
111
tests/integrationtests/TestFixture.h
Normal file
111
tests/integrationtests/TestFixture.h
Normal file
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file TestAdaptor.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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_INTEGRATIONTESTS_TESTFIXTURE_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_
|
||||
|
||||
#include "TestAdaptor.h"
|
||||
#include "TestProxy.h"
|
||||
#include "Defs.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
class TestFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
s_adaptorConnection->releaseName(BUS_NAME);
|
||||
s_adaptorConnection->leaveEventLoop();
|
||||
s_proxyConnection->leaveEventLoop();
|
||||
}
|
||||
|
||||
template <typename _Fnc>
|
||||
static bool waitUntil(_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 {
|
||||
std::this_thread::sleep_for(step);
|
||||
elapsed += step;
|
||||
if (elapsed > timeout)
|
||||
return false;
|
||||
} while (!fnc());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = std::chrono::seconds(5))
|
||||
{
|
||||
return waitUntil([&flag]() -> bool { return flag; }, timeout);
|
||||
}
|
||||
|
||||
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_objectManagerAdaptor = std::make_unique<ObjectManagerTestAdaptor>(*s_adaptorConnection, MANAGER_PATH);
|
||||
m_adaptor = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_proxy.reset();
|
||||
m_adaptor.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
static std::unique_ptr<sdbus::IConnection> s_adaptorConnection;
|
||||
static std::unique_ptr<sdbus::IConnection> s_proxyConnection;
|
||||
std::unique_ptr<ObjectManagerTestAdaptor> m_objectManagerAdaptor;
|
||||
std::unique_ptr<ObjectManagerTestProxy> m_objectManagerProxy;
|
||||
std::unique_ptr<TestAdaptor> m_adaptor;
|
||||
std::unique_ptr<TestProxy> m_proxy;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_ */
|
161
tests/integrationtests/TestProxy.cpp
Normal file
161
tests/integrationtests/TestProxy.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file TestAdaptor.cpp
|
||||
*
|
||||
* Created on: May 23, 2020
|
||||
* 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 "TestProxy.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
TestProxy::TestProxy(std::string destination, std::string 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); });
|
||||
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
TestProxy::TestProxy(sdbus::IConnection& connection, std::string destination, std::string 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); });
|
||||
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
TestProxy::~TestProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
|
||||
void TestProxy::onSimpleSignal()
|
||||
{
|
||||
m_signalMsg = getProxy().getCurrentlyProcessedMessage();
|
||||
m_signalMemberName = m_signalMsg->getMemberName();
|
||||
|
||||
m_gotSimpleSignal = true;
|
||||
}
|
||||
|
||||
void TestProxy::onSignalWithMap(const std::map<int32_t, std::string>& aMap)
|
||||
{
|
||||
m_mapFromSignal = aMap;
|
||||
m_gotSignalWithMap = true;
|
||||
}
|
||||
|
||||
void TestProxy::onSignalWithVariant(const sdbus::Variant& aVariant)
|
||||
{
|
||||
m_variantFromSignal = aVariant.get<double>();
|
||||
m_gotSignalWithVariant = true;
|
||||
}
|
||||
|
||||
void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
{
|
||||
m_signatureFromSignal[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
|
||||
m_gotSignalWithSignature = true;
|
||||
}
|
||||
|
||||
void TestProxy::onDoOperationReply(uint32_t returnValue, const sdbus::Error* error)
|
||||
{
|
||||
if (m_DoOperationClientSideAsyncReplyHandler)
|
||||
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
|
||||
}
|
||||
|
||||
void TestProxy::onPropertiesChanged( const std::string& interfaceName
|
||||
, const std::map<std::string, sdbus::Variant>& changedProperties
|
||||
, const std::vector<std::string>& invalidatedProperties )
|
||||
{
|
||||
if (m_onPropertiesChangedHandler)
|
||||
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
|
||||
}
|
||||
|
||||
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
|
||||
{
|
||||
m_DoOperationClientSideAsyncReplyHandler = std::move(handler);
|
||||
}
|
||||
|
||||
uint32_t TestProxy::doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
uint32_t result;
|
||||
getProxy().callMethod("doOperation").onInterface(sdbus::test::INTERFACE_NAME).withTimeout(timeout).withArguments(param).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
});
|
||||
}
|
||||
|
||||
void TestProxy::doErroneousOperationClientSideAsync()
|
||||
{
|
||||
getProxy().callMethodAsync("throwError")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error)
|
||||
{
|
||||
this->onDoOperationReply(0, error);
|
||||
});
|
||||
}
|
||||
|
||||
void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
getProxy().callMethodAsync("doOperation")
|
||||
.onInterface(sdbus::test::INTERFACE_NAME)
|
||||
.withTimeout(timeout)
|
||||
.withArguments(param)
|
||||
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
|
||||
{
|
||||
this->onDoOperationReply(returnValue, error);
|
||||
});
|
||||
}
|
||||
|
||||
int32_t TestProxy::callNonexistentMethod()
|
||||
{
|
||||
int32_t result;
|
||||
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);
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestProxy::setStateProperty(const std::string& value)
|
||||
{
|
||||
getProxy().setProperty("state").onInterface(sdbus::test::INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
}}
|
122
tests/integrationtests/TestProxy.h
Normal file
122
tests/integrationtests/TestProxy.h
Normal file
@ -0,0 +1,122 @@
|
||||
/**
|
||||
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
|
||||
*
|
||||
* @file TestAdaptor.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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_INTEGRATIONTESTS_TESTPROXY_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_
|
||||
|
||||
#include "integrationtests-proxy.h"
|
||||
#include "Defs.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
class ObjectManagerTestProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
|
||||
{
|
||||
public:
|
||||
ObjectManagerTestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
|
||||
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
~ObjectManagerTestProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
protected:
|
||||
void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
|
||||
{
|
||||
if (m_onInterfacesAddedHandler)
|
||||
m_onInterfacesAddedHandler(objectPath, interfacesAndProperties);
|
||||
}
|
||||
|
||||
void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector<std::string>& 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;
|
||||
};
|
||||
|
||||
class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
|
||||
, sdbus::Peer_proxy
|
||||
, sdbus::Introspectable_proxy
|
||||
, sdbus::Properties_proxy >
|
||||
{
|
||||
public:
|
||||
TestProxy(std::string destination, std::string objectPath);
|
||||
TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath);
|
||||
~TestProxy();
|
||||
|
||||
protected:
|
||||
void onSimpleSignal() override;
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
public:
|
||||
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler);
|
||||
uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
|
||||
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param);
|
||||
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
|
||||
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;
|
||||
std::atomic<bool> m_gotSignalWithSignature{false};
|
||||
std::map<std::string, std::string> m_signatureFromSignal;
|
||||
|
||||
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;
|
||||
|
||||
const Message* m_signalMsg{};
|
||||
std::string m_signalMemberName;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_ */
|
@ -0,0 +1,20 @@
|
||||
<!-- This configuration file specifies the required security policies
|
||||
for the Kistler DBUS example to run core daemon to work. -->
|
||||
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
|
||||
<!-- ../system.conf have denied everything, so we just punch some holes -->
|
||||
|
||||
<policy context="default">
|
||||
<allow own="org.sdbuscpp.integrationtests"/>
|
||||
<allow send_destination="org.sdbuscpp.integrationtests"/>
|
||||
<allow send_interface="org.sdbuscpp.integrationtests"/>
|
||||
|
||||
<allow own="org.sdbuscpp.integrationtests2"/>
|
||||
<allow send_destination="org.sdbuscpp.integrationtests2"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user