forked from Kistler-Group/sdbus-cpp
Compare commits
421 Commits
Author | SHA1 | Date | |
---|---|---|---|
7fbfcec455 | |||
0430ae0ad9 | |||
6212b12159 | |||
4389ea39bf | |||
48ea775531 | |||
fafe8487ff | |||
0261d0ec60 | |||
a1419ee45d | |||
02ca7212d1 | |||
8a117f8b42 | |||
84130b1406 | |||
c55907069b | |||
107c6a1a97 | |||
1b7acaa735 | |||
b7a038f11f | |||
c6705faf2f | |||
e6b87b106c | |||
50cc636058 | |||
3d3aa26255 | |||
14942db075 | |||
e62472b210 | |||
b7d85f936d | |||
798eaf8626 | |||
2bc9d3ebb3 | |||
26c6da11be | |||
0c75ad2b71 | |||
84a6fbbf86 | |||
83ece48ab0 | |||
310161207a | |||
ef552ec089 | |||
7894cda577 | |||
d41a176c2a | |||
f15c260750 | |||
d856969051 | |||
32a97f214f | |||
42f0bd07c0 | |||
fe21ee9656 | |||
0dc0c87cc9 | |||
29e94c3b68 | |||
c433d26176 | |||
abc4d755f7 | |||
4c35e98668 | |||
87500ad9ad | |||
9a5616a87a | |||
4a90f80933 | |||
d177d2e365 | |||
c205178fc0 | |||
280fcfb067 | |||
0074c79e7f | |||
3de2812a60 | |||
914d133d10 | |||
4721060d76 | |||
1c72f1ce52 | |||
b65461c404 | |||
24776f2047 | |||
f1b9226491 | |||
dc0c2562b8 | |||
84932a6985 | |||
bdf313bc60 | |||
e76d38c317 | |||
c92f5722c7 | |||
20a13eeafb | |||
4fd09d4e81 | |||
35fd3ff5ce | |||
6f35f00fcf | |||
9412940d1e | |||
c6afa26541 | |||
b4d036c503 | |||
0bfda9ab92 | |||
64337e545d | |||
788168eded | |||
b9aa770f58 | |||
2efb6909b5 | |||
3e20fc639e | |||
7450515d0b | |||
c769637f3b | |||
334fcb8833 | |||
b9088cc801 | |||
a73eb9b8c1 | |||
700a011b80 | |||
8a9cfc1f19 | |||
30d9f1d462 | |||
28921ad424 | |||
721f583db1 | |||
47a84ab889 | |||
d80483cdc0 | |||
934d51fa8a | |||
fb9e4ae371 | |||
6e348f3910 | |||
f50e4676fe | |||
1aa30e3a20 | |||
e2b3e98374 | |||
9490b3351f | |||
9da18aec25 | |||
b7b454ba38 | |||
f420b216aa | |||
b482cd6d08 | |||
aac7e590ea | |||
0ad2553417 | |||
621b3d0862 | |||
189fd23744 | |||
cfb71bd6cf | |||
c437b4d508 | |||
1e2d13a04a | |||
290078d6af | |||
0eda855745 | |||
2a992ca84d | |||
3717e63c64 | |||
8113bf88ad | |||
3e84b254e9 | |||
8728653359 | |||
6620a447d1 | |||
24a3d83c3f | |||
dcd9d46b9c | |||
605fbe48c0 | |||
fb61420bf0 | |||
0a2bda9c67 | |||
f6e597a583 | |||
29c877a89a | |||
98f4929337 | |||
8d0d9b0d40 | |||
c39bc637b8 | |||
737f04abc7 | |||
3a56113422 | |||
f332f46087 | |||
c9e157e3e1 | |||
8ca3fdd5ce | |||
55c306ce05 | |||
6c5e72326c | |||
8bbeeeb4ce | |||
7a09e9bcc8 | |||
c812d03bc7 | |||
e7d4e07926 | |||
031f4687ca | |||
aeae79003a | |||
74d849d933 | |||
f336811fc7 | |||
35bffd0f25 | |||
e33a890ce7 | |||
751c1addc4 | |||
2991fa4960 | |||
0f2362d8c3 | |||
e07c1f3981 | |||
58426966f4 | |||
4e105081c9 | |||
5ec6027d5f | |||
d864e1dfa4 | |||
b7f3d7c876 | |||
2a4c241303 | |||
5e933c3f17 | |||
b5aee6d019 | |||
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 | |||
4f5dfbc301 | |||
fa878e594c | |||
d3d698f02a | |||
d8fd053714 |
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Real (buggy) behavior**
|
||||
A clear and concise description of what really happened in contrast to your expectation.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here, like version of sdbus-c++ library, version of systemd used, OS used.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
105
.github/workflows/ci.yml
vendored
Normal file
105
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- release/v2.0
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, ubuntu-24.04]
|
||||
compiler: [g++, clang]
|
||||
build: [shared-libsystemd, embedded-static-libsystemd]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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 libc++-dev
|
||||
sudo update-alternatives --remove-all cc
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
|
||||
sudo update-alternatives --remove-all c++
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
|
||||
# We need to use libc++ with Clang because there is a bug in libstdc++ chrono headers that Clang has issues with
|
||||
echo "SDBUSCPP_EXTRA_CXX_FLAGS=-stdlib=libc++" >> $GITHUB_ENV
|
||||
# We don't install googletest but we let it be built within sdbus-c++ builds below, since it needs to be built against libc++ for Clang jobs to pass
|
||||
# - name: install-googletest
|
||||
# run: |
|
||||
# sudo apt-get install -y libgmock-dev
|
||||
- name: configure-debug-gcc11 # For gcc 11, turn off the annoying deprecated-copy warning
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.compiler == 'g++' && matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Wno-deprecated-copy -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: configure-debug
|
||||
if: matrix.build == 'shared-libsystemd' && (matrix.compiler != 'g++' || matrix.os != 'ubuntu-22.04')
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: configure-release-with-embedded-libsystemd
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="$SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_BUILD_LIBSYSTEMD=ON -DSDBUSCPP_LIBSYSTEMD_VERSION=252 -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: make
|
||||
run: |
|
||||
cd build
|
||||
cmake --build . -j4
|
||||
- name: verify
|
||||
run: |
|
||||
cd build
|
||||
sudo cmake --build . --target install
|
||||
ctest --output-on-failure
|
||||
- name: pack
|
||||
if: matrix.build == 'shared-libsystemd'
|
||||
run: |
|
||||
cd build
|
||||
cpack -G DEB
|
||||
- name: 'Upload Artifact'
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.compiler == 'g++'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"
|
||||
path: |
|
||||
build/sdbus-c++*.deb
|
||||
build/sdbus-c++*.ddeb
|
||||
retention-days: 10
|
||||
freebsd-build:
|
||||
name: build (freebsd, clang/libc++, basu)
|
||||
runs-on: ubuntu-22.04 # until https://github.com/actions/runner/issues/385
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test in FreeBSD VM
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
copyback: false
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y cmake ninja pkgconf basu expat googletest
|
||||
run: |
|
||||
cmake -B _build -G Ninja -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON
|
||||
cmake --build _build
|
||||
cmake --install _build
|
||||
pkg install -y dbus
|
||||
service dbus onestart
|
||||
ctest --output-on-failure --test-dir _build
|
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 :)
|
||||
|
333
CMakeLists.txt
Normal file
333
CMakeLists.txt
Normal file
@ -0,0 +1,333 @@
|
||||
#-------------------------------
|
||||
# PROJECT INFORMATION
|
||||
#-------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(sdbus-c++ VERSION 2.1.0 LANGUAGES CXX C)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
# -------------------------------
|
||||
# CONFIGURATION OPTIONS
|
||||
# -------------------------------
|
||||
|
||||
option(SDBUSCPP_BUILD_LIBSYSTEMD "Fetch & build libsystemd static library and make it part of libsdbus-c++, instead of searching for it in the system" OFF)
|
||||
if(NOT SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
set(SDBUSCPP_SDBUS_LIB "default" CACHE STRING "sd-bus implementation library to search for and use (default, systemd, elogind, or basu)")
|
||||
set_property(CACHE SDBUSCPP_SDBUS_LIB PROPERTY STRINGS default systemd elogind basu)
|
||||
else()
|
||||
set(SDBUSCPP_LIBSYSTEMD_VERSION "252" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
|
||||
set(SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS "" CACHE STRING "Additional configuration options to be passed as-is to libsystemd build system")
|
||||
endif()
|
||||
option(SDBUSCPP_INSTALL "Enable installation of sdbus-c++ (downstream projects embedding sdbus-c++ may want to turn this OFF)" ON)
|
||||
option(SDBUSCPP_BUILD_TESTS "Build tests" OFF)
|
||||
if (SDBUSCPP_BUILD_TESTS)
|
||||
option(SDBUSCPP_BUILD_PERF_TESTS "Build also sdbus-c++ performance tests" OFF)
|
||||
option(SDBUSCPP_BUILD_STRESS_TESTS "Build also sdbus-c++ stress tests" OFF)
|
||||
set(SDBUSCPP_TESTS_INSTALL_PATH "tests/${PROJECT_NAME}" CACHE STRING "Specifies where the test binaries will be installed")
|
||||
set(SDBUSCPP_GOOGLETEST_VERSION 1.14.0 CACHE STRING "Version of gmock library to use")
|
||||
set(SDBUSCPP_GOOGLETEST_GIT_REPO "https://github.com/google/googletest.git" CACHE STRING "A git repo to clone and build googletest from if gmock is not found in the system")
|
||||
endif()
|
||||
option(SDBUSCPP_BUILD_CODEGEN "Build generator tool for C++ native bindings" OFF)
|
||||
option(SDBUSCPP_BUILD_EXAMPLES "Build example programs" OFF)
|
||||
option(SDBUSCPP_BUILD_DOCS "Build documentation for sdbus-c++" ON)
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
option(SDBUSCPP_BUILD_DOXYGEN_DOCS "Build doxygen documentation for sdbus-c++ API" OFF)
|
||||
endif()
|
||||
#option(SDBUSCPP_CLANG_TIDY "Co-compile with clang-tidy static analyzer" OFF)
|
||||
#option(SDBUSCPP_COVERAGE "Build sdbus-c++ with code coverage instrumentation" OFF)
|
||||
# We promote the BUILD_SHARED_LIBS flag to a (global) option only if we are the main project
|
||||
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
|
||||
endif()
|
||||
|
||||
message(STATUS "")
|
||||
message(STATUS " *******************************************")
|
||||
message(STATUS " * SDBUS-C++ CONFIGURATION *")
|
||||
message(STATUS " *******************************************")
|
||||
message(STATUS "")
|
||||
message(STATUS " SDBUSCPP_BUILD_LIBSYSTEMD: ${SDBUSCPP_BUILD_LIBSYSTEMD}")
|
||||
if(SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
message(STATUS " SDBUSCPP_LIBSYSTEMD_VERSION: ${SDBUSCPP_LIBSYSTEMD_VERSION}")
|
||||
message(STATUS " SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS: ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}")
|
||||
endif()
|
||||
message(STATUS " SDBUSCPP_INSTALL: ${SDBUSCPP_INSTALL}")
|
||||
message(STATUS " SDBUSCPP_BUILD_TESTS: ${SDBUSCPP_BUILD_TESTS}")
|
||||
if(SDBUSCPP_BUILD_TESTS)
|
||||
message(STATUS " SDBUSCPP_BUILD_PERF_TESTS: ${SDBUSCPP_BUILD_PERF_TESTS}")
|
||||
message(STATUS " SDBUSCPP_BUILD_STRESS_TESTS: ${SDBUSCPP_BUILD_STRESS_TESTS}")
|
||||
message(STATUS " SDBUSCPP_TESTS_INSTALL_PATH: ${SDBUSCPP_TESTS_INSTALL_PATH}")
|
||||
message(STATUS " SDBUSCPP_GOOGLETEST_VERSION: ${SDBUSCPP_GOOGLETEST_VERSION}")
|
||||
message(STATUS " SDBUSCPP_GOOGLETEST_GIT_REPO: ${SDBUSCPP_GOOGLETEST_GIT_REPO}")
|
||||
endif()
|
||||
message(STATUS " SDBUSCPP_BUILD_CODEGEN: ${SDBUSCPP_BUILD_CODEGEN}")
|
||||
message(STATUS " SDBUSCPP_BUILD_EXAMPLES: ${SDBUSCPP_BUILD_EXAMPLES}")
|
||||
message(STATUS " SDBUSCPP_BUILD_DOCS: ${SDBUSCPP_BUILD_DOCS}")
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
message(STATUS " SDBUSCPP_BUILD_DOXYGEN_DOCS: ${SDBUSCPP_BUILD_DOXYGEN_DOCS}")
|
||||
endif()
|
||||
message(STATUS " BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}")
|
||||
message(STATUS "")
|
||||
|
||||
#-------------------------------
|
||||
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES
|
||||
#-------------------------------
|
||||
|
||||
if(SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
# Build static libsystemd library as an external project
|
||||
include(cmake/LibsystemdExternalProject.cmake)
|
||||
set(SDBUS_IMPL "systemd")
|
||||
set(SDBUS_LIB "libsystemd")
|
||||
else()
|
||||
# Search for sd-bus implementations in the system as per user's configuration
|
||||
set(SDBUS_LIBS ${SDBUSCPP_SDBUS_LIB})
|
||||
if(SDBUSCPP_SDBUS_LIB STREQUAL "default")
|
||||
set(SDBUS_LIBS systemd elogind basu) # This is the default search order
|
||||
endif()
|
||||
|
||||
set(MINIMUM_SDBUS_VERSION 238)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
foreach(LIB ${SDBUS_LIBS})
|
||||
if(LIB STREQUAL "systemd")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=${MINIMUM_SDBUS_VERSION})
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(SDBUS_IMPL "systemd")
|
||||
set(SDBUS_LIB "libsystemd")
|
||||
break()
|
||||
endif()
|
||||
elseif(LIB STREQUAL "elogind")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=${MINIMUM_SDBUS_VERSION})
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(SDBUS_IMPL "elogind")
|
||||
set(SDBUS_LIB "libelogind")
|
||||
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
|
||||
list(GET VERSION_LIST 0 Systemd_VERSION)
|
||||
break()
|
||||
endif()
|
||||
elseif(LIB STREQUAL "basu")
|
||||
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL basu)
|
||||
if(TARGET PkgConfig::Systemd)
|
||||
set(SDBUS_IMPL "basu")
|
||||
set(SDBUS_LIB "basu")
|
||||
set(Systemd_VERSION "240") # https://git.sr.ht/~emersion/basu/commit/d4d185d29a26
|
||||
break()
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Unrecognized sd-bus implementation library ${LIB}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT TARGET PkgConfig::Systemd)
|
||||
message(FATAL_ERROR "None of ${SDBUS_LIBS} libraries implementing sd-bus was found (Are their dev packages installed?)")
|
||||
endif()
|
||||
add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd)
|
||||
string(REGEX MATCHALL "([0-9]+)" SYSTEMD_VERSION_LIST "${Systemd_VERSION}")
|
||||
list(GET SYSTEMD_VERSION_LIST 0 SDBUSCPP_LIBSYSTEMD_VERSION)
|
||||
message(STATUS "Building with libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
|
||||
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}/VTableItems.h
|
||||
${SDBUSCPP_INCLUDE_DIR}/VTableItems.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 20)
|
||||
|
||||
#----------------------------------
|
||||
# LIBRARY BUILD INFORMATION
|
||||
#----------------------------------
|
||||
|
||||
set(SDBUSCPP_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
|
||||
set(SDBUSCPP_VERSION "${PROJECT_VERSION}")
|
||||
|
||||
# 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=${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
SDBUS_${SDBUS_IMPL}
|
||||
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
|
||||
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
if(SDBUSCPP_BUILD_LIBSYSTEMD)
|
||||
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
|
||||
endif()
|
||||
target_link_libraries(sdbus-c++-objlib
|
||||
PUBLIC
|
||||
Systemd::Libsystemd
|
||||
Threads::Threads)
|
||||
|
||||
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)
|
||||
|
||||
#----------------------------------
|
||||
# TESTS
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_BUILD_TESTS)
|
||||
message(STATUS "Building with tests")
|
||||
enable_testing()
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# UTILS
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_BUILD_CODEGEN)
|
||||
message(STATUS "Building with code generator tool")
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools")
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# EXAMPLES
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_BUILD_EXAMPLES)
|
||||
message(STATUS "Building with examples")
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# DOCUMENTATION
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
message(STATUS "Building with documentation")
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/docs")
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# CMAKE CONFIG & PACKAGE CONFIG
|
||||
#----------------------------------
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
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)
|
||||
|
||||
if(BUILD_SHARED_LIBS AND (SDBUSCPP_BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
|
||||
set(PKGCONFIG_REQS ".private")
|
||||
else()
|
||||
set(PKGCONFIG_REQS "")
|
||||
endif()
|
||||
set(PKGCONFIG_DEPS ${SDBUS_LIB})
|
||||
configure_file(pkgconfig/sdbus-c++.pc.in pkgconfig/sdbus-c++.pc @ONLY)
|
||||
|
||||
#----------------------------------
|
||||
# INSTALLATION
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_INSTALL)
|
||||
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 sdbus-c++-runtime
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sdbus-c++-runtime NAMELINK_COMPONENT sdbus-c++-dev
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sdbus-c++-dev
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT sdbus-c++-dev)
|
||||
install(EXPORT sdbus-c++-targets
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
|
||||
NAMESPACE SDBusCpp::
|
||||
COMPONENT sdbus-c++-dev)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config-version.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
|
||||
COMPONENT sdbus-c++-dev)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT sdbus-c++-dev)
|
||||
if(SDBUSCPP_BUILD_DOCS)
|
||||
install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# CPACK
|
||||
#----------------------------------
|
||||
|
||||
set(CPACK_PACKAGE_VENDOR "Kistler")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High-level C++ D-Bus library")
|
||||
set(CPACK_PACKAGE_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 (>=${MINIMUM_SDBUS_VERSION})")
|
||||
|
||||
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.
|
320
ChangeLog
320
ChangeLog
@ -1,17 +1,303 @@
|
||||
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.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
|
||||
|
||||
v1.2.0
|
||||
- Add support for match rules
|
||||
- Add support for session bus connection at custom address
|
||||
- Add CMake variable for extra libsystemd config options
|
||||
- Use pseudo D-Bus connection for plain messages
|
||||
- Rename dont_request_slot tag to floating_slot
|
||||
- Add validity checks for names and paths
|
||||
- Remove executable flag from source files
|
||||
- Detect missing type after array declaration
|
||||
- Fix invalid assert on event fd
|
||||
- Enable move for ObjectPath and Signature
|
||||
- Add printer for std::chrono in googletest v1.11.0
|
||||
- Fix potential undefined behavior in creation of sdbus::Error
|
||||
- Additional little fixes and improvements in code, build system, and documentation
|
||||
|
||||
v1.3.0
|
||||
- Add support for light-weight proxies (proxies without own event loop threads)
|
||||
- Extend documentation with explicit mapping between D-Bus and corresponding C++ types
|
||||
- Support move semantics in generated adaptor and proxy classes
|
||||
- Adaptations for libsystemd v251
|
||||
- Fix for proper complete sending of long D-Bus messages by explicitly flushing them
|
||||
- Add support for std::future-based async calls
|
||||
- Fix race condition in async Proxy::callMethod
|
||||
- Fix pseudo-connection static lifetime issue with Phoenix pattern
|
||||
- Speed up performance of of serialization of arrays of trivial D-Bus types
|
||||
- Make sdbus::Struct a tuple-like class, so it's usable wherever std::tuple is
|
||||
- Add support for std::array, std::span and std::unordered_map as additional C++ types for D-Bus array types
|
||||
- Add support for libelogind as an addition to libsystemd
|
||||
- Add support for std::future-based async methods in codegen tool
|
||||
- Additional little fixes and improvements in code, build system, CI, and documentation
|
||||
|
||||
v1.4.0
|
||||
- Implement API for convenient asynchronous property get/set on the client-side
|
||||
- Add support for FreeBSD systems (including support for basu implementation of sd-bus on non-systemd machines)
|
||||
- Add support for direct, peer-to-peer connections
|
||||
- Add option to create IConnection directly from an underlying sd_bus instance
|
||||
- Some additional fixes
|
||||
|
||||
v1.5.0
|
||||
- Improve handling of exceptions from callback handlers
|
||||
- Add support for async registration of matches
|
||||
- Correctly add libsystemd dependency to pkgconfi
|
||||
- Fix request name signal handling issue
|
||||
- Add INSTALL_TESTS CMake option
|
||||
- Minor UnixFd cleanups
|
||||
- Additional little fixes and updates in code, build system, CI, and documentation
|
||||
|
||||
v1.6.0
|
||||
- Add support for enums in D-Bus serialization and signatures
|
||||
- Add support for std::variant as an alternative C++ type for D-Bus Variant
|
||||
- Add support for implicit conversions between std::variant and sdbus::Variant
|
||||
- Fix missing includes
|
||||
|
||||
v2.0.0
|
||||
- A new major version with revamped API, bringing numerous new features, simplifications, fixes and breaking changes improving not only API consistency, safety and expressiveness, but also performance
|
||||
- Add section 'Migrating to sdbus-c++ v2' to the 'Using sdbus-c++' document providing the complete list of breaking API/ABI/behavior changes and migration notes
|
||||
- Switch to C++20 standard (but the API is backwards-compatible with C++17, skipping C++20 features in such a case)
|
||||
- Add strong types to public API
|
||||
- Add support for string_view as a D-Bus type representation
|
||||
- Enable compile-time D-Bus signature generation
|
||||
- Redesign Object vtable registration API
|
||||
- Improve Proxy signal subscription API
|
||||
- Refactor object manager API for consistency
|
||||
- Introduce native sd-event integration
|
||||
- Let callbacks take message objects by value
|
||||
- Simplify async D-Bus connection handling
|
||||
- Simplify async call state flag
|
||||
- Make Variant constructor explicit
|
||||
- Use optional for passing potential call errors
|
||||
- Rename and re-organize CMake options
|
||||
- Rename connection creation methods
|
||||
- Have all async function names finish with *Async for consistency
|
||||
- Implement more flexible searching for sd-bus libs
|
||||
- Add new `SDBUSCPP_SDBUS_LIB` CMake configuration variable determining which sd-bus library shall be picked
|
||||
- Make Variant conversion operator explicit
|
||||
- Use sd-bus API to get the current message
|
||||
- Use sd_bus_match_signal() for signal registration
|
||||
- Provide also const char* overloads for convenience functions
|
||||
- Disable move in generated adaptor and proxy classes
|
||||
- Improve and make more efficient some Message API
|
||||
- Add Slot-returning overloads of async method calls
|
||||
- Remove floating_slot_t tag and use return_slot_t instead
|
||||
- Add [[nodiscard]] to many relevant API functions
|
||||
- Add proper fix for timeout handling
|
||||
- Fix for external event loops in which the event loop thread ID was not correctly initialized (now fixed and simplified by not needing the thread ID anymore)
|
||||
- Remove deprecated stuff
|
||||
- Add `SDBUSCPP_` prefix to CMake configuration variables to avoid conflicts with downstream projects
|
||||
- Require systemd of at least v238
|
||||
- Many other fixes and updates in code, tests, build system, CI, and documentation
|
||||
|
||||
v2.1.0
|
||||
- Add SDBUSCPP_REGISTER_STRUCT macro to conveniently teach sdbus-c++ about user-defined structs
|
||||
- Extend the SDBUSCPP_REGISTER_STRUCT macro with serialization of user-defined structs as dicts, and deserialization of dicts into user-defined structs
|
||||
- Make createPlainMessage() function public
|
||||
- Solve the problem of sending large D-Bus messages properly (through the event loop thread)
|
||||
- Fix partially renamed BUILD_DOXYGEN_DOC CMake option
|
||||
- Change googletest to default version 1.14.0
|
||||
- Add version parameter to the xml2cpp codegen tool
|
||||
- A few other internal refactorings and improvements
|
||||
|
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
|
99
README.md
99
README.md
@ -1,37 +1,114 @@
|
||||
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++
|
||||
|
||||
* `SDBUSCPP_BUILD_CODEGEN` [boolean]
|
||||
|
||||
Build the codegen tool `sdbus-c++-xml2cpp` for generating the high level C++ bindings out of the D-Bus IDL XML description. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_CODEGEN=ON` flag to turn on building the code gen.
|
||||
|
||||
* `SDBUSCPP_BUILD_DOCS` [boolean]
|
||||
|
||||
Include sdbus-c++ documentation files and tutorials. Default value: `ON`. With this option turned on, you may also enable/disable the following option:
|
||||
|
||||
* `SDBUSCPP_BUILD_DOXYGEN_DOCS` [boolean]
|
||||
|
||||
Build Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `cmake --build . --target doc`. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_DOXYGEN_DOCS=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
|
||||
|
||||
* `SDBUSCPP_BUILD_TESTS` [boolean]
|
||||
|
||||
Build sdbus-c++ unit and integration tests, invokable by `cmake --build . --target test` (Note: before invoking `cmake --build . --target test`, make sure you copy `tests/integrationtests/files/org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory). That incorporates downloading and building static libraries of Google Test. Default value: `OFF`. Use `-DBUILD_TESTS=ON` to enable building the tests. With this option turned on, you may also enable/disable the following options:
|
||||
|
||||
* `SDBUSCPP_BUILD_PERF_TESTS` [boolean]
|
||||
|
||||
Build sdbus-c++ performance tests. Default value: `OFF`.
|
||||
|
||||
* `SDBUSCPP_BUILD_STRESS_TESTS` [boolean]
|
||||
|
||||
Build sdbus-c++ stress tests. Default value: `OFF`.
|
||||
|
||||
* `SDBUSCPP_TESTS_INSTALL_PATH` [string]
|
||||
|
||||
Path where the test binaries shall get installed. Default value: `${CMAKE_INSTALL_PREFIX}/tests/sdbus-c++` (previously: `/opt/test/bin`).
|
||||
|
||||
* `SDBUSCPP_BUILD_EXAMPLES` [boolean]
|
||||
|
||||
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`.
|
||||
|
||||
* `SDBUSCPP_BUILD_LIBSYSTEMD` [boolean]
|
||||
|
||||
Build sd-bus (libsystemd library) instead of searching for it in the system, and make it part of sdbus-c++ library. Default value: `OFF`, which means that the sd-bus implementation library (`libsystemd`, `libelogind`, or `basu`) will be searched via `pkg-config` in the system.
|
||||
|
||||
This option may be very helpful in environments where sd-bus implementation library is unavailable (see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information).
|
||||
|
||||
With this option turned off, you may provide the following additional configuration flag:
|
||||
|
||||
* `SDBUSCPP_SDBUS_LIB` [string]
|
||||
|
||||
Defines which sd-bus implementation library to search for and use. Allowed values: `default`, `systemd`, `elogind`, `basu`. Default value: `default`, which means that sdbus-c++ will try to find any of `systemd`, `elogind`, `basu` in the order as listed here.
|
||||
|
||||
With this option turned on, you may provide the following additional configuration flag:
|
||||
|
||||
* `SDBUSCPP_LIBSYSTEMD_VERSION` [string]
|
||||
|
||||
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `252`, minimum value: `239`.
|
||||
|
||||
* `SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
|
||||
|
||||
Additional options to be passed as-is to the libsystemd build system in its configure step. Can be used for passing e.g. toolchain file path in case of cross builds. Default value: empty.
|
||||
|
||||
* `CMAKE_BUILD_TYPE` [string]
|
||||
|
||||
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]
|
||||
|
||||
Global CMake flag, promoted in sdbus-c++ project to a CMake option. Use this to control whether sdbus-c++ is built as either a shared or static library. Default value: `ON`.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* `C++17` - the library uses C++17 `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++20/C++17` - the library uses C++20 features, but its public API is backwards compatible with C++17 and provides optional extra features when C++20 features are available
|
||||
* `libsystemd`/`libelogind`/`basu` - libraries containing sd-bus implementation that sdbus-c++ is written around. In case of `libsystemd` and `libelogind`, version >= 238 is needed. (In case you have you're missing any of those sd-bus implementations, don't worry, see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information.)
|
||||
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
|
||||
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
|
||||
* `expat` - necessary when building the xml2cpp binding code generator (`SDBUSCPP_BUILD_CODEGEN` option is `ON`).
|
||||
|
||||
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
|
||||
------------------------
|
||||
|
||||
* [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/doc/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 +118,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
|
62
cmake/LibsystemdExternalProject.cmake
Normal file
62
cmake/LibsystemdExternalProject.cmake
Normal file
@ -0,0 +1,62 @@
|
||||
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()
|
||||
|
||||
if(NOT GPERF)
|
||||
message(WARNING "gperf was not found, libsystemd configuration may fail")
|
||||
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()
|
||||
|
||||
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(SDBUSCPP_LIBSYSTEMD_VERSION LESS "239")
|
||||
message(FATAL_ERROR "Only libsystemd version >=239 can be built as static part of sdbus-c++")
|
||||
endif()
|
||||
if(SDBUSCPP_LIBSYSTEMD_VERSION GREATER "240")
|
||||
set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h)
|
||||
endif()
|
||||
|
||||
message(STATUS "Building with embedded libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(LibsystemdBuildProject
|
||||
PREFIX libsystemd-v${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
GIT_REPOSITORY https://github.com/systemd/systemd-stable.git
|
||||
GIT_TAG v${SDBUSCPP_LIBSYSTEMD_VERSION}-stable
|
||||
GIT_SHALLOW 1
|
||||
UPDATE_COMMAND ""
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
|
||||
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Drootprefix=<INSTALL_DIR> -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}
|
||||
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, 2)
|
||||
m4_define(sdbus_cpp_version_micro, 6)
|
||||
|
||||
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 >= 0.10.1],,
|
||||
AC_MSG_ERROR([You need the libsystemd library (version 0.10.1 or better)]
|
||||
[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,748 +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. [Using D-Bus properties](#using-d-bus-properties)
|
||||
11. [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. The message can be
|
||||
* a method call (with serialized parameters),
|
||||
* a method reply (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::Message& msg, sdbus::Message& reply)
|
||||
{
|
||||
// Deserialize the collection of numbers from the message
|
||||
std::vector<int> numbers;
|
||||
msg >> numbers;
|
||||
|
||||
// Deserialize separator from the message
|
||||
std::string separator;
|
||||
msg >> 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 signalMsg = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||
signalMsg << result;
|
||||
g_concatenator->emitSignal(signalMsg);
|
||||
}
|
||||
|
||||
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::Message& signalMsg)
|
||||
{
|
||||
std::string concatenatedString;
|
||||
msg >> 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;
|
||||
}
|
||||
```
|
||||
|
||||
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++!
|
33
docs/CMakeLists.txt
Normal file
33
docs/CMakeLists.txt
Normal file
@ -0,0 +1,33 @@
|
||||
# Building doxygen documentation
|
||||
|
||||
find_package(Doxygen)
|
||||
|
||||
if(SDBUSCPP_BUILD_DOXYGEN_DOCS)
|
||||
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/.)
|
||||
else()
|
||||
message(WARNING "Documentation enabled, but Doxygen cannot be found")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(SDBUSCPP_INSTALL)
|
||||
install(FILES sdbus-c++-class-diagram.png
|
||||
sdbus-c++-class-diagram.uml
|
||||
systemd-dbus-config.md
|
||||
using-sdbus-c++.md
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
|
||||
|
||||
if (SDBUSCPP_BUILD_DOXYGEN_DOCS AND DOXYGEN_FOUND)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT sdbus-c++-doc)
|
||||
endif()
|
||||
endif()
|
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
|
50
docs/systemd-dbus-config.md
Normal file
50
docs/systemd-dbus-config.md
Normal file
@ -0,0 +1,50 @@
|
||||
Systemd and D-Bus 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
|
||||
```
|
||||
|
||||
D-Bus configuration
|
||||
------------------
|
||||
|
||||
Typical default D-Bus configuration does not allow to register services except explicitly allowed. To allow a service to register its D-Bus API, we must place an appropriate conf file in `/etc/dbus-1/system.d/` directory. The conf file name must be `<service-name>.conf`. I.e., full file path for Concatenator example from sdbus-c++ tutorial would be `/etc/dbus-1/system.d/org.sdbuscpp.concatenator.conf`. And here is template configuration to use its D-Bus 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.concatenator"/>
|
||||
<allow send_interface="org.sdbuscpp.concatenator"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
```
|
||||
|
||||
If you need access from other user then `root` should be substituted by desired username. Or you can simply use policy `<policy context="default">` like [conf file](/tests/integrationtests/files/org.sdbuscpp.integrationtests.conf) for sdbus-c++ integration tests is doing it. For more information refer to `man dbus-daemon`.
|
1894
docs/using-sdbus-c++.md
Normal file
1894
docs/using-sdbus-c++.md
Normal file
File diff suppressed because it is too large
Load Diff
12
examples/CMakeLists.txt
Normal file
12
examples/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
# Building examples
|
||||
|
||||
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++)
|
||||
|
||||
if(SDBUSCPP_INSTALL)
|
||||
install(TARGETS obj-manager-server DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-examples)
|
||||
install(TARGETS obj-manager-client DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-examples)
|
||||
endif()
|
@ -0,0 +1,59 @@
|
||||
|
||||
/*
|
||||
* 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)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
Planet1_proxy(const Planet1_proxy&) = delete;
|
||||
Planet1_proxy& operator=(const Planet1_proxy&) = delete;
|
||||
Planet1_proxy(Planet1_proxy&&) = delete;
|
||||
Planet1_proxy& operator=(Planet1_proxy&&) = delete;
|
||||
|
||||
~Planet1_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
uint64_t GetPopulation()
|
||||
{
|
||||
uint64_t result;
|
||||
m_proxy.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
std::string Name()
|
||||
{
|
||||
return m_proxy.getProperty("Name").onInterface(INTERFACE_NAME).get<std::string>();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
@ -0,0 +1,54 @@
|
||||
|
||||
/*
|
||||
* 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)
|
||||
: m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
Planet1_adaptor(const Planet1_adaptor&) = delete;
|
||||
Planet1_adaptor& operator=(const Planet1_adaptor&) = delete;
|
||||
Planet1_adaptor(Planet1_adaptor&&) = delete;
|
||||
Planet1_adaptor& operator=(Planet1_adaptor&&) = delete;
|
||||
|
||||
~Planet1_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addVTable( sdbus::registerMethod("GetPopulation").withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); })
|
||||
, sdbus::registerProperty("Name").withGetter([this](){ return this->Name(); })
|
||||
).forInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual uint64_t GetPopulation() = 0;
|
||||
|
||||
private:
|
||||
virtual std::string Name() = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}}} // namespaces
|
||||
|
||||
#endif
|
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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, sdbus::ServiceName destination, sdbus::ObjectPath 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, sdbus::ServiceName destination, sdbus::ObjectPath path)
|
||||
: ProxyInterfaces(connection, destination, std::move(path))
|
||||
, m_connection(connection)
|
||||
, m_destination(destination)
|
||||
{
|
||||
registerProxy();
|
||||
}
|
||||
|
||||
~ManagerProxy()
|
||||
{
|
||||
unregisterProxy();
|
||||
}
|
||||
|
||||
void handleExistingObjects()
|
||||
{
|
||||
auto objectsInterfacesAndProperties = GetManagedObjects();
|
||||
for (const auto& [object, interfacesAndProperties] : objectsInterfacesAndProperties) {
|
||||
onInterfacesAdded(object, interfacesAndProperties);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties) override
|
||||
{
|
||||
std::cout << objectPath << " added:\t";
|
||||
for (const auto& [interface, _] : interfacesAndProperties) {
|
||||
std::cout << interface << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
// Parse and print some more info
|
||||
auto planetInterface = interfacesAndProperties.find(sdbus::InterfaceName{org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME});
|
||||
if (planetInterface == interfacesAndProperties.end()) {
|
||||
return;
|
||||
}
|
||||
const auto& properties = planetInterface->second;
|
||||
// get a property which was passed as part of the signal.
|
||||
const auto& name = properties.at(sdbus::PropertyName{"Name"}).get<std::string>();
|
||||
// or create a proxy instance to the newly added object.
|
||||
PlanetProxy planet(m_connection, m_destination, objectPath);
|
||||
std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << std::endl;
|
||||
}
|
||||
|
||||
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces) override
|
||||
{
|
||||
std::cout << objectPath << " removed:\t";
|
||||
for (const auto& interface : interfaces) {
|
||||
std::cout << interface << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
sdbus::IConnection& m_connection;
|
||||
sdbus::ServiceName m_destination;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
auto connection = sdbus::createSessionBusConnection();
|
||||
|
||||
sdbus::ServiceName destination{"org.sdbuscpp.examplemanager"};
|
||||
sdbus::ObjectPath objectPath{"/org/sdbuscpp/examplemanager"};
|
||||
auto managerProxy = std::make_unique<ManagerProxy>(*connection, std::move(destination), std::move(objectPath));
|
||||
try {
|
||||
managerProxy->handleExistingObjects();
|
||||
}
|
||||
catch (const sdbus::Error& e) {
|
||||
if (e.getName() == "org.freedesktop.DBus.Error.ServiceUnknown") {
|
||||
std::cout << "Waiting for server to start ..." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
connection->enterEventLoop();
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* 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>
|
||||
|
||||
using sdbus::ObjectPath;
|
||||
|
||||
class ManagerAdaptor : public sdbus::AdaptorInterfaces<sdbus::ObjectManager_adaptor>
|
||||
{
|
||||
public:
|
||||
ManagerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath 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, sdbus::ObjectPath path, std::string name, uint64_t population)
|
||||
: AdaptorInterfaces(connection, std::move(path))
|
||||
, m_name(std::move(name))
|
||||
, m_population(population)
|
||||
{
|
||||
registerAdaptor();
|
||||
emitInterfacesAddedSignal({sdbus::InterfaceName{org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME}});
|
||||
}
|
||||
|
||||
~PlanetAdaptor()
|
||||
{
|
||||
emitInterfacesRemovedSignal({sdbus::InterfaceName{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();
|
||||
sdbus::ServiceName serviceName{"org.sdbuscpp.examplemanager"};
|
||||
connection->requestName(serviceName);
|
||||
connection->enterEventLoopAsync();
|
||||
|
||||
auto manager = std::make_unique<ManagerAdaptor>(*connection, ObjectPath{"/org/sdbuscpp/examplemanager"});
|
||||
while (true)
|
||||
{
|
||||
printCountDown("Creating PlanetAdaptor in ", 5);
|
||||
auto earth = std::make_unique<PlanetAdaptor>(*connection, ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Earth"}, "Earth", 7'874'965'825);
|
||||
printCountDown("Creating PlanetAdaptor in ", 5);
|
||||
auto trantor = std::make_unique<PlanetAdaptor>(*connection, ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Trantor"}, "Trantor", 40'000'000'000);
|
||||
printCountDown("Creating PlanetAdaptor in ", 5);
|
||||
auto laconia = std::make_unique<PlanetAdaptor>(*connection, ObjectPath{"/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(serviceName);
|
||||
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,23 +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)/Types.h \
|
||||
$(HEADER_DIR)/TypeTraits.h \
|
||||
$(HEADER_DIR)/sdbus-c++.h
|
||||
|
||||
EXTRA_DIST = ($libsdbuscpp_HEADERS)
|
||||
|
||||
DISTCLEANFILES = Makefile Makefile.in
|
152
include/sdbus-c++/AdaptorInterfaces.h
Normal file
152
include/sdbus-c++/AdaptorInterfaces.h
Normal file
@ -0,0 +1,152 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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, one needs to make sure:
|
||||
* 1. to call `registerAdaptor();` in the class constructor, and, conversely,
|
||||
* 2. to call `unregisterAdaptor();` in the class destructor,
|
||||
* so that the object API vtable is registered and unregistered at the proper time.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
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, ObjectPath objectPath)
|
||||
: ObjectHolder(createObject(connection, std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Adds object vtable (i.e. D-Bus API) definitions for all interfaces it implements
|
||||
*
|
||||
* This function must be called in the constructor of the final adaptor class that implements AdaptorInterfaces.
|
||||
*
|
||||
* See also @ref IObject::addVTable()
|
||||
*/
|
||||
void registerAdaptor()
|
||||
{
|
||||
(_Interfaces::registerAdaptor(), ...);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @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 reference to the underlying IObject instance
|
||||
*/
|
||||
using ObjectHolder::getObject;
|
||||
|
||||
protected:
|
||||
using base_type = AdaptorInterfaces;
|
||||
|
||||
AdaptorInterfaces(const AdaptorInterfaces&) = delete;
|
||||
AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete;
|
||||
AdaptorInterfaces(AdaptorInterfaces&&) = delete;
|
||||
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = delete;
|
||||
~AdaptorInterfaces() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_ADAPTORINTERFACES_H_ */
|
290
include/sdbus-c++/ConvenienceApiClasses.h
Normal file
290
include/sdbus-c++/ConvenienceApiClasses.h
Normal file
@ -0,0 +1,290 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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++/Types.h>
|
||||
#include <sdbus-c++/VTableItems.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IObject;
|
||||
class IProxy;
|
||||
class Error;
|
||||
class PendingAsyncCall;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
class VTableAdder
|
||||
{
|
||||
public:
|
||||
void forInterface(InterfaceName interfaceName);
|
||||
void forInterface(std::string interfaceName);
|
||||
[[nodiscard]] Slot forInterface(InterfaceName interfaceName, return_slot_t);
|
||||
[[nodiscard]] Slot forInterface(std::string interfaceName, return_slot_t);
|
||||
|
||||
private:
|
||||
friend IObject;
|
||||
VTableAdder(IObject& object, std::vector<VTableItem> vtable);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
std::vector<VTableItem> vtable_;
|
||||
};
|
||||
|
||||
class SignalEmitter
|
||||
{
|
||||
public:
|
||||
SignalEmitter& onInterface(const InterfaceName& interfaceName);
|
||||
SignalEmitter& onInterface(const std::string& interfaceName);
|
||||
SignalEmitter& onInterface(const char* interfaceName);
|
||||
template <typename... _Args> void withArguments(_Args&&... args);
|
||||
|
||||
SignalEmitter(SignalEmitter&& other) = default;
|
||||
~SignalEmitter() noexcept(false);
|
||||
|
||||
private:
|
||||
friend IObject;
|
||||
SignalEmitter(IObject& object, const SignalName& signalName);
|
||||
SignalEmitter(IObject& object, const char* signalName);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const char* signalName_;
|
||||
Signal signal_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
||||
};
|
||||
|
||||
class MethodInvoker
|
||||
{
|
||||
public:
|
||||
MethodInvoker& onInterface(const InterfaceName& interfaceName);
|
||||
MethodInvoker& onInterface(const std::string& interfaceName);
|
||||
MethodInvoker& onInterface(const char* interfaceName);
|
||||
MethodInvoker& withTimeout(uint64_t usec);
|
||||
template <typename _Rep, typename _Period>
|
||||
MethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
|
||||
template <typename... _Args> void storeResultsTo(_Args&... args);
|
||||
void dontExpectReply();
|
||||
|
||||
MethodInvoker(MethodInvoker&& other) = default;
|
||||
~MethodInvoker() noexcept(false);
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
MethodInvoker(IProxy& proxy, const MethodName& methodName);
|
||||
MethodInvoker(IProxy& proxy, const char* methodName);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const char* methodName_;
|
||||
uint64_t timeout_{};
|
||||
MethodCall method_;
|
||||
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
||||
bool methodCalled_{};
|
||||
};
|
||||
|
||||
class AsyncMethodInvoker
|
||||
{
|
||||
public:
|
||||
AsyncMethodInvoker& onInterface(const InterfaceName& interfaceName);
|
||||
AsyncMethodInvoker& onInterface(const std::string& interfaceName);
|
||||
AsyncMethodInvoker& onInterface(const char* interfaceName);
|
||||
AsyncMethodInvoker& withTimeout(uint64_t usec);
|
||||
template <typename _Rep, typename _Period>
|
||||
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
|
||||
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
|
||||
// Returned future will be std::future<void> for no (void) D-Bus method return value
|
||||
// or std::future<T> for single D-Bus method return value
|
||||
// or std::future<std::tuple<...>> for multiple method return values
|
||||
template <typename... _Args> std::future<future_return_t<_Args...>> getResultAsFuture();
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName);
|
||||
AsyncMethodInvoker(IProxy& proxy, const char* methodName);
|
||||
template <typename _Function> async_reply_handler makeAsyncReplyHandler(_Function&& callback);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const char* methodName_;
|
||||
uint64_t timeout_{};
|
||||
MethodCall method_;
|
||||
};
|
||||
|
||||
class SignalSubscriber
|
||||
{
|
||||
public:
|
||||
SignalSubscriber& onInterface(const InterfaceName& interfaceName);
|
||||
SignalSubscriber& onInterface(const std::string& interfaceName);
|
||||
SignalSubscriber& onInterface(const char* interfaceName);
|
||||
template <typename _Function> void call(_Function&& callback);
|
||||
template <typename _Function> [[nodiscard]] Slot call(_Function&& callback, return_slot_t);
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
SignalSubscriber(IProxy& proxy, const SignalName& signalName);
|
||||
SignalSubscriber(IProxy& proxy, const char* signalName);
|
||||
template <typename _Function> signal_handler makeSignalHandler(_Function&& callback);
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
const char* signalName_;
|
||||
const char* interfaceName_{};
|
||||
};
|
||||
|
||||
class PropertyGetter
|
||||
{
|
||||
public:
|
||||
Variant onInterface(std::string_view interfaceName);
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
PropertyGetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
std::string_view propertyName_;
|
||||
};
|
||||
|
||||
class AsyncPropertyGetter
|
||||
{
|
||||
public:
|
||||
AsyncPropertyGetter& onInterface(std::string_view interfaceName);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
|
||||
std::future<Variant> getResultAsFuture();
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
std::string_view propertyName_;
|
||||
std::string_view interfaceName_;
|
||||
};
|
||||
|
||||
class PropertySetter
|
||||
{
|
||||
public:
|
||||
PropertySetter& onInterface(std::string_view interfaceName);
|
||||
template <typename _Value> void toValue(const _Value& value);
|
||||
template <typename _Value> void toValue(const _Value& value, dont_expect_reply_t);
|
||||
void toValue(const Variant& value);
|
||||
void toValue(const Variant& value, dont_expect_reply_t);
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
PropertySetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
std::string_view propertyName_;
|
||||
std::string_view interfaceName_;
|
||||
};
|
||||
|
||||
class AsyncPropertySetter
|
||||
{
|
||||
public:
|
||||
AsyncPropertySetter& onInterface(std::string_view interfaceName);
|
||||
template <typename _Value> AsyncPropertySetter& toValue(_Value&& value);
|
||||
AsyncPropertySetter& toValue(Variant value);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
|
||||
std::future<void> getResultAsFuture();
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
AsyncPropertySetter(IProxy& proxy, std::string_view propertyName);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
std::string_view propertyName_;
|
||||
std::string_view interfaceName_;
|
||||
Variant value_;
|
||||
};
|
||||
|
||||
class AllPropertiesGetter
|
||||
{
|
||||
public:
|
||||
std::map<PropertyName, Variant> onInterface(std::string_view interfaceName);
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
AllPropertiesGetter(IProxy& proxy);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
};
|
||||
|
||||
class AsyncAllPropertiesGetter
|
||||
{
|
||||
public:
|
||||
AsyncAllPropertiesGetter& onInterface(std::string_view interfaceName);
|
||||
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
|
||||
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
|
||||
std::future<std::map<PropertyName, Variant>> getResultAsFuture();
|
||||
|
||||
private:
|
||||
friend IProxy;
|
||||
AsyncAllPropertiesGetter(IProxy& proxy);
|
||||
|
||||
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
private:
|
||||
IProxy& proxy_;
|
||||
std::string_view interfaceName_;
|
||||
};
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */
|
718
include/sdbus-c++/ConvenienceApiClasses.inl
Normal file
718
include/sdbus-c++/ConvenienceApiClasses.inl
Normal file
@ -0,0 +1,718 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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++/Error.h>
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IProxy.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/*** ------------- ***/
|
||||
/*** VTableAdder ***/
|
||||
/*** ------------- ***/
|
||||
|
||||
inline VTableAdder::VTableAdder(IObject& object, std::vector<VTableItem> vtable)
|
||||
: object_(object)
|
||||
, vtable_(std::move(vtable))
|
||||
{
|
||||
}
|
||||
|
||||
inline void VTableAdder::forInterface(InterfaceName interfaceName)
|
||||
{
|
||||
object_.addVTable(std::move(interfaceName), std::move(vtable_));
|
||||
}
|
||||
|
||||
inline void VTableAdder::forInterface(std::string interfaceName)
|
||||
{
|
||||
forInterface(InterfaceName{std::move(interfaceName)});
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Slot VTableAdder::forInterface(InterfaceName interfaceName, return_slot_t)
|
||||
{
|
||||
return object_.addVTable(std::move(interfaceName), std::move(vtable_), return_slot);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Slot VTableAdder::forInterface(std::string interfaceName, return_slot_t)
|
||||
{
|
||||
return forInterface(InterfaceName{std::move(interfaceName)}, return_slot);
|
||||
}
|
||||
|
||||
/*** ------------- ***/
|
||||
/*** SignalEmitter ***/
|
||||
/*** ------------- ***/
|
||||
|
||||
inline SignalEmitter::SignalEmitter(IObject& object, const SignalName& signalName)
|
||||
: SignalEmitter(object, signalName.c_str())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalEmitter::SignalEmitter(IObject& object, const char* 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 InterfaceName& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline SignalEmitter& SignalEmitter::onInterface(const char* 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 MethodName& methodName)
|
||||
: MethodInvoker(proxy, methodName.c_str())
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodInvoker::MethodInvoker(IProxy& proxy, const char* 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 InterfaceName& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline MethodInvoker& MethodInvoker::onInterface(const char* 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 MethodName& methodName)
|
||||
: AsyncMethodInvoker(proxy, methodName.c_str())
|
||||
{
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const char* methodName)
|
||||
: proxy_(proxy)
|
||||
, methodName_(methodName)
|
||||
{
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const InterfaceName& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const char* 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
|
||||
|
||||
return proxy_.callMethodAsync(method_, makeAsyncReplyHandler(std::forward<_Function>(callback)), timeout_);
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot AsyncMethodInvoker::uponReplyInvoke(_Function&& callback, return_slot_t)
|
||||
{
|
||||
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync( method_
|
||||
, makeAsyncReplyHandler(std::forward<_Function>(callback))
|
||||
, timeout_
|
||||
, return_slot );
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline async_reply_handler AsyncMethodInvoker::makeAsyncReplyHandler(_Function&& callback)
|
||||
{
|
||||
return [callback = std::forward<_Function>(callback)](MethodReply reply, std::optional<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)
|
||||
{
|
||||
try
|
||||
{
|
||||
reply >> args;
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
// Pass message deserialization exceptions to the client via callback error parameter,
|
||||
// instead of propagating them up the message loop call stack.
|
||||
sdbus::apply(callback, e, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
sdbus::apply(callback, std::move(error), args);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
std::future<future_return_t<_Args...>> AsyncMethodInvoker::getResultAsFuture()
|
||||
{
|
||||
auto promise = std::make_shared<std::promise<future_return_t<_Args...>>>();
|
||||
auto future = promise->get_future();
|
||||
|
||||
uponReplyInvoke([promise = std::move(promise)](std::optional<Error> error, _Args... args)
|
||||
{
|
||||
if (!error)
|
||||
if constexpr (!std::is_void_v<future_return_t<_Args...>>)
|
||||
promise->set_value({std::move(args)...});
|
||||
else
|
||||
promise->set_value();
|
||||
else
|
||||
promise->set_exception(std::make_exception_ptr(*std::move(error)));
|
||||
});
|
||||
|
||||
// Will be std::future<void> for no D-Bus method return value
|
||||
// or std::future<T> for single D-Bus method return value
|
||||
// or std::future<std::tuple<...>> for multiple method return values
|
||||
return future;
|
||||
}
|
||||
|
||||
/*** ---------------- ***/
|
||||
/*** SignalSubscriber ***/
|
||||
/*** ---------------- ***/
|
||||
|
||||
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const SignalName& signalName)
|
||||
: SignalSubscriber(proxy, signalName.c_str())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const char* signalName)
|
||||
: proxy_(proxy)
|
||||
, signalName_(signalName)
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(const InterfaceName& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
return onInterface(interfaceName.c_str());
|
||||
}
|
||||
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(const char* interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline void SignalSubscriber::call(_Function&& callback)
|
||||
{
|
||||
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||
|
||||
proxy_.registerSignalHandler( interfaceName_
|
||||
, signalName_
|
||||
, makeSignalHandler(std::forward<_Function>(callback)) );
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] inline Slot SignalSubscriber::call(_Function&& callback, return_slot_t)
|
||||
{
|
||||
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.registerSignalHandler( interfaceName_
|
||||
, signalName_
|
||||
, makeSignalHandler(std::forward<_Function>(callback))
|
||||
, return_slot );
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline signal_handler SignalSubscriber::makeSignalHandler(_Function&& callback)
|
||||
{
|
||||
return [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 `std::optional<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 to the client's signal handler as a valid Error object inside the std::optional parameter.
|
||||
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)
|
||||
{
|
||||
// Pass message deserialization exceptions to the client via callback error parameter,
|
||||
// instead of propagating them up the message loop call stack.
|
||||
sdbus::apply(callback, e, signalArgs);
|
||||
return;
|
||||
}
|
||||
|
||||
// Invoke callback with no error and input arguments from the tuple.
|
||||
sdbus::apply(callback, {}, 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*** -------------- ***/
|
||||
/*** PropertyGetter ***/
|
||||
/*** -------------- ***/
|
||||
|
||||
inline PropertyGetter::PropertyGetter(IProxy& proxy, std::string_view propertyName)
|
||||
: proxy_(proxy)
|
||||
, propertyName_(std::move(propertyName))
|
||||
{
|
||||
}
|
||||
|
||||
inline Variant PropertyGetter::onInterface(std::string_view interfaceName)
|
||||
{
|
||||
Variant var;
|
||||
proxy_.callMethod("Get")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName, propertyName_)
|
||||
.storeResultsTo(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
/*** ------------------- ***/
|
||||
/*** AsyncPropertyGetter ***/
|
||||
/*** ------------------- ***/
|
||||
|
||||
inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName)
|
||||
: proxy_(proxy)
|
||||
, propertyName_(std::move(propertyName))
|
||||
{
|
||||
}
|
||||
|
||||
inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(std::string_view interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(_Function&& callback)
|
||||
{
|
||||
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, Variant>
|
||||
, "Property get callback function must accept std::optional<Error> and property value as Variant" );
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("Get")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_)
|
||||
.uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot AsyncPropertyGetter::uponReplyInvoke(_Function&& callback, return_slot_t)
|
||||
{
|
||||
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, Variant>
|
||||
, "Property get callback function must accept std::optional<Error> and property value as Variant" );
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("Get")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_)
|
||||
.uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
inline std::future<Variant> AsyncPropertyGetter::getResultAsFuture()
|
||||
{
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("Get")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_)
|
||||
.getResultAsFuture<Variant>();
|
||||
}
|
||||
|
||||
/*** -------------- ***/
|
||||
/*** PropertySetter ***/
|
||||
/*** -------------- ***/
|
||||
|
||||
inline PropertySetter::PropertySetter(IProxy& proxy, std::string_view propertyName)
|
||||
: proxy_(proxy)
|
||||
, propertyName_(std::move(propertyName))
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertySetter& PropertySetter::onInterface(std::string_view interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Value>
|
||||
inline void PropertySetter::toValue(const _Value& value)
|
||||
{
|
||||
PropertySetter::toValue(Variant{value});
|
||||
}
|
||||
|
||||
template <typename _Value>
|
||||
inline void PropertySetter::toValue(const _Value& value, dont_expect_reply_t)
|
||||
{
|
||||
PropertySetter::toValue(Variant{value}, dont_expect_reply);
|
||||
}
|
||||
|
||||
inline void PropertySetter::toValue(const Variant& value)
|
||||
{
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
proxy_.callMethod("Set")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_, value);
|
||||
}
|
||||
|
||||
inline void PropertySetter::toValue(const Variant& value, dont_expect_reply_t)
|
||||
{
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
proxy_.callMethod("Set")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_, value)
|
||||
.dontExpectReply();
|
||||
}
|
||||
|
||||
/*** ------------------- ***/
|
||||
/*** AsyncPropertySetter ***/
|
||||
/*** ------------------- ***/
|
||||
|
||||
inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, std::string_view propertyName)
|
||||
: proxy_(proxy)
|
||||
, propertyName_(propertyName)
|
||||
{
|
||||
}
|
||||
|
||||
inline AsyncPropertySetter& AsyncPropertySetter::onInterface(std::string_view interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Value>
|
||||
inline AsyncPropertySetter& AsyncPropertySetter::toValue(_Value&& value)
|
||||
{
|
||||
return AsyncPropertySetter::toValue(Variant{std::forward<_Value>(value)});
|
||||
}
|
||||
|
||||
inline AsyncPropertySetter& AsyncPropertySetter::toValue(Variant value)
|
||||
{
|
||||
value_ = std::move(value);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(_Function&& callback)
|
||||
{
|
||||
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>>
|
||||
, "Property set callback function must accept std::optional<Error> only" );
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("Set")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_, std::move(value_))
|
||||
.uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot AsyncPropertySetter::uponReplyInvoke(_Function&& callback, return_slot_t)
|
||||
{
|
||||
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>>
|
||||
, "Property set callback function must accept std::optional<Error> only" );
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("Set")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_, std::move(value_))
|
||||
.uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
inline std::future<void> AsyncPropertySetter::getResultAsFuture()
|
||||
{
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("Set")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_, propertyName_, std::move(value_))
|
||||
.getResultAsFuture<>();
|
||||
}
|
||||
|
||||
/*** ------------------- ***/
|
||||
/*** AllPropertiesGetter ***/
|
||||
/*** ------------------- ***/
|
||||
|
||||
inline AllPropertiesGetter::AllPropertiesGetter(IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
inline std::map<PropertyName, Variant> AllPropertiesGetter::onInterface(std::string_view interfaceName)
|
||||
{
|
||||
std::map<PropertyName, Variant> props;
|
||||
proxy_.callMethod("GetAll")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(std::move(interfaceName))
|
||||
.storeResultsTo(props);
|
||||
return props;
|
||||
}
|
||||
|
||||
/*** ------------------------ ***/
|
||||
/*** AsyncAllPropertiesGetter ***/
|
||||
/*** ------------------------ ***/
|
||||
|
||||
inline AsyncAllPropertiesGetter::AsyncAllPropertiesGetter(IProxy& proxy)
|
||||
: proxy_(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(std::string_view interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback)
|
||||
{
|
||||
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, std::map<PropertyName, Variant>>
|
||||
, "All properties get callback function must accept std::optional<Error> and a map of property names to their values" );
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("GetAll")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_)
|
||||
.uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback, return_slot_t)
|
||||
{
|
||||
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, std::map<PropertyName, Variant>>
|
||||
, "All properties get callback function must accept std::optional<Error> and a map of property names to their values" );
|
||||
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("GetAll")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_)
|
||||
.uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
inline std::future<std::map<PropertyName, Variant>> AsyncAllPropertiesGetter::getResultAsFuture()
|
||||
{
|
||||
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
|
||||
|
||||
return proxy_.callMethodAsync("GetAll")
|
||||
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
|
||||
.withArguments(interfaceName_)
|
||||
.getResultAsFuture<std::map<PropertyName, Variant>>();
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */
|
@ -1,169 +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 <string>
|
||||
|
||||
// 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> void 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_;
|
||||
Message 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_;
|
||||
Message 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,401 +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++/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 void 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)](Message& msg, Message& 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 = 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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 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)](Message& 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.
|
||||
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_ */
|
58
include/sdbus-c++/Error.h
Executable file → Normal file
58
include/sdbus-c++/Error.h
Executable file → Normal file
@ -1,7 +1,8 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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,37 +43,64 @@ namespace sdbus {
|
||||
: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
Error(const std::string& name, const std::string& message)
|
||||
: std::runtime_error("[" + name + "] " + message)
|
||||
, name_(name)
|
||||
, message_(message)
|
||||
// Strong type representing the D-Bus error name
|
||||
class Name : public std::string
|
||||
{
|
||||
public:
|
||||
Name() = default;
|
||||
explicit Name(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit Name(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
explicit Error(Name name, const char* message = nullptr)
|
||||
: Error(std::move(name), std::string(message ? message : ""))
|
||||
{
|
||||
}
|
||||
|
||||
const std::string& getName() const
|
||||
Error(Name name, std::string message)
|
||||
: std::runtime_error("[" + name + "] " + message)
|
||||
, name_(std::move(name))
|
||||
, message_(std::move(message))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] const Name& getName() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
const std::string& getMessage() const
|
||||
[[nodiscard]] const std::string& getMessage() const
|
||||
{
|
||||
return message_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isValid() const
|
||||
{
|
||||
return !getName().empty();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
Name name_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
sdbus::Error createError(int errNo, const std::string& customMsg);
|
||||
Error createError(int errNo, std::string customMsg);
|
||||
|
||||
inline const Error::Name SDBUSCPP_ERROR_NAME{"org.sdbuscpp.Error"};
|
||||
}
|
||||
|
||||
#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 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool test(GeneralFlags flag) const
|
||||
{
|
||||
return flags_.test(flag);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool test(PropertyUpdateBehaviorFlags flag) const
|
||||
{
|
||||
return flags_.test(flag);
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t toSdBusInterfaceFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusMethodFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusSignalFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusPropertyFlags() const;
|
||||
[[nodiscard]] uint64_t toSdBusWritablePropertyFlags() const;
|
||||
|
||||
private:
|
||||
std::bitset<FLAG_COUNT> flags_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_FLAGS_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file IConnection.h
|
||||
*
|
||||
@ -26,9 +27,23 @@
|
||||
#ifndef SDBUS_CXX_ICONNECTION_H_
|
||||
#define SDBUS_CXX_ICONNECTION_H_
|
||||
|
||||
//#include <cstdint>
|
||||
#include <string>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
// Forward declarations
|
||||
struct sd_bus;
|
||||
struct sd_event;
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class ObjectPath;
|
||||
class BusName;
|
||||
using ServiceName = BusName;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -36,122 +51,534 @@ 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
|
||||
*/
|
||||
virtual void requestName(const std::string& name) = 0;
|
||||
struct PollData;
|
||||
|
||||
virtual ~IConnection() = default;
|
||||
|
||||
/*!
|
||||
* @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 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 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 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;
|
||||
|
||||
/*!
|
||||
* @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 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 Leaves the D-Bus processing loop
|
||||
*
|
||||
* Ends the previously started processing loop.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
* @brief Attaches the bus connection to an sd-event event loop
|
||||
*
|
||||
* @param[in] event sd-event event loop object
|
||||
* @param[in] priority Specified priority
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*
|
||||
* See `man sd_bus_attach_event'.
|
||||
*/
|
||||
virtual void attachSdEventLoop(sd_event *event, int priority = 0) = 0;
|
||||
|
||||
inline virtual ~IConnection() = 0;
|
||||
/*!
|
||||
* @brief Detaches the bus connection from an sd-event event loop
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void detachSdEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Gets current sd-event event loop for the bus connection
|
||||
*
|
||||
* @return Pointer to event loop object if attached, nullptr otherwise
|
||||
*/
|
||||
virtual sd_event *getSdEventLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns fd's, I/O events and timeout data to be used in an external event loop
|
||||
*
|
||||
* This function is useful to hook up a bus connection object with an
|
||||
* external (like GMainLoop, boost::asio, etc.) or manual event loop
|
||||
* involving poll() or a similar I/O polling call.
|
||||
*
|
||||
* Before **each** invocation of the I/O polling call, this function
|
||||
* should be invoked. Returned PollData::fd file descriptor should
|
||||
* be polled for the events indicated by PollData::events, and the I/O
|
||||
* call should block for that up to the returned PollData::timeout.
|
||||
*
|
||||
* Additionally, returned PollData::eventFd should be polled for POLLIN
|
||||
* events.
|
||||
*
|
||||
* After each I/O polling call the bus connection needs to process
|
||||
* incoming or outgoing data, by invoking processPendingEvent().
|
||||
*
|
||||
* Note that the returned timeout should be considered only a maximum
|
||||
* sleeping time. It is permissible (and even expected) that shorter
|
||||
* timeouts are used by the calling program, in case other event sources
|
||||
* are polled in the same event loop. Note that the returned time-value
|
||||
* is absolute, based of CLOCK_MONOTONIC and specified in microseconds.
|
||||
* Use PollData::getPollTimeout() to have the timeout value converted
|
||||
* in a form that can be passed to poll(2).
|
||||
*
|
||||
* The bus connection conveniently integrates sd-event event loop.
|
||||
* To attach the bus connection to an sd-event event loop, use
|
||||
* attachSdEventLoop() function.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual PollData getEventLoopPollData() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Processes a pending event
|
||||
*
|
||||
* @returns True if an event was processed, false if no operations were pending
|
||||
*
|
||||
* This function drives the D-Bus connection. It processes pending I/O events.
|
||||
* Queued outgoing messages (or parts thereof) are sent out. Queued incoming
|
||||
* messages are dispatched to registered callbacks. Timeouts are recalculated.
|
||||
*
|
||||
* It returns false when no operations were pending and true if a message was
|
||||
* processed. When false is returned the caller should synchronously poll for
|
||||
* I/O events before calling into processPendingEvent() again.
|
||||
* Don't forget to call getEventLoopPollData() each time before the next poll.
|
||||
*
|
||||
* You don't need to directly call this method or getEventLoopPollData() method
|
||||
* when using convenient, internal bus connection event loops through
|
||||
* enterEventLoop() or enterEventLoopAsync() calls, or when the bus is
|
||||
* connected to an sd-event event loop through attachSdEventLoop().
|
||||
* It is invoked automatically when necessary.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual bool processPendingEvent() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides access to the currently processed D-Bus message
|
||||
*
|
||||
* This method provides access to the currently processed incoming D-Bus message.
|
||||
* "Currently processed" means that the registered callback handler(s) for that message
|
||||
* are being invoked. This method is meant to be called from within a callback handler
|
||||
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
|
||||
* guaranteed to return a valid D-Bus message instance for which the handler is called.
|
||||
* If called from other contexts/threads, it may return a valid or invalid message, depending
|
||||
* on whether a message was processed or not at the time of the call.
|
||||
*
|
||||
* @return Currently processed D-Bus message
|
||||
*/
|
||||
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Sets general method call timeout
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
[[nodiscard]] virtual uint64_t getMethodCallTimeout() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager at the specified D-Bus object path
|
||||
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
|
||||
*
|
||||
* Creates an ObjectManager interface at the specified object path on
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
* to see what objects it has.
|
||||
*
|
||||
* This call creates a floating registration. The ObjectManager will
|
||||
* be there for the object path until the connection is destroyed.
|
||||
*
|
||||
* Another, recommended way to add object managers is directly through IObject API.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addObjectManager(const ObjectPath& objectPath) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager at the specified D-Bus object path
|
||||
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
|
||||
* @return Slot handle owning the registration
|
||||
*
|
||||
* Creates an ObjectManager interface at the specified object path on
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
* to see what objects it has.
|
||||
*
|
||||
* This call returns an owning slot. The lifetime of the ObjectManager
|
||||
* interface is bound to the lifetime of the returned slot instance.
|
||||
*
|
||||
* Another, recommended way to add object managers is directly through IObject API.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Installs a floating match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
*
|
||||
* The method installs a match rule for messages received on the specified bus connection.
|
||||
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
|
||||
* The specified handler function callback is called for each incoming message matching the specified
|
||||
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
|
||||
* sends a control message requesting the match to be added to the broker and waits until the broker
|
||||
* confirms the match has been installed successfully.
|
||||
*
|
||||
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||
* is bound to the lifetime of the bus connection.
|
||||
*
|
||||
* For more information, consult `man sd_bus_add_match`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addMatch(const std::string& match, message_handler callback) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Installs a match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @return RAII-style slot handle representing the ownership of the subscription
|
||||
*
|
||||
* The method installs a match rule for messages received on the specified bus connection.
|
||||
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
|
||||
* The specified handler function callback is called for each incoming message matching the specified
|
||||
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
|
||||
* sends a control message requesting the match to be added to the broker and waits until the broker
|
||||
* confirms the match has been installed successfully.
|
||||
*
|
||||
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
|
||||
* the slot instance implies uninstalling of the match rule from the bus connection.
|
||||
*
|
||||
* For more information, consult `man sd_bus_add_match`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback, return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
*
|
||||
* This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously,
|
||||
* in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response.
|
||||
* The `installCallback' callable is called when the response is later received, with the response message
|
||||
* from the broker as parameter. If it's an empty function object, a default implementation is used that
|
||||
* terminates the bus connection should installing the match fail.
|
||||
*
|
||||
* The method installs a floating match rule for messages received on the specified bus connection.
|
||||
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
|
||||
* is bound to the lifetime of the bus connection.
|
||||
*
|
||||
* For more information, consult `man sd_bus_add_match_async`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Asynchronously installs a match rule for messages received on this bus connection
|
||||
*
|
||||
* @param[in] match Match expression to filter incoming D-Bus message
|
||||
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
|
||||
* @return RAII-style slot handle representing the ownership of the subscription
|
||||
*
|
||||
* This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously,
|
||||
* in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response.
|
||||
* The `installCallback' callable is called when the response is later received, with the response message
|
||||
* from the broker as parameter. If it's an empty function object, a default implementation is used that
|
||||
* terminates the bus connection should installing the match fail.
|
||||
*
|
||||
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
|
||||
* the slot instance implies the uninstalling of the match rule from the bus connection.
|
||||
*
|
||||
* For more information, consult `man sd_bus_add_match_async`.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addMatchAsync( const std::string& match
|
||||
, message_handler callback
|
||||
, message_handler installCallback
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Retrieves the unique name of a connection. E.g. ":1.xx"
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual BusName getUniqueName() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Requests a well-known D-Bus service name on a bus
|
||||
*
|
||||
* @param[in] name Name to request
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void requestName(const ServiceName& name) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Releases an acquired well-known D-Bus service name on a bus
|
||||
*
|
||||
* @param[in] name Name to release
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void releaseName(const ServiceName& name) = 0;
|
||||
|
||||
/*!
|
||||
* @struct PollData
|
||||
*
|
||||
* Carries poll data needed for integration with external event loop implementations.
|
||||
*
|
||||
* See getEventLoopPollData() for more info.
|
||||
*/
|
||||
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 microseconds, based of CLOCK_MONOTONIC.
|
||||
*
|
||||
* Call getPollTimeout() to get timeout recalculated to relative timeout that can be passed to poll(2).
|
||||
*/
|
||||
std::chrono::microseconds timeout;
|
||||
|
||||
/*!
|
||||
* An additional event fd to be monitored by the event loop for POLLIN events.
|
||||
*/
|
||||
int eventFd;
|
||||
|
||||
/*!
|
||||
* Returns the timeout as relative value from now.
|
||||
*
|
||||
* Returned value is std::chrono::microseconds::max() if the timeout is indefinite.
|
||||
*
|
||||
* @return Relative timeout as a time duration
|
||||
*/
|
||||
[[nodiscard]] std::chrono::microseconds getRelativeTimeout() const;
|
||||
|
||||
/*!
|
||||
* Returns relative timeout in the form which can be passed as argument 'timeout' to poll(2)
|
||||
*
|
||||
* @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block.
|
||||
* An integer in milliseconds otherwise.
|
||||
*/
|
||||
[[nodiscard]] int getPollTimeout() const;
|
||||
};
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/*!
|
||||
* @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 session bus connection when in a user context, and a system bus connection, otherwise.
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createBusConnection();
|
||||
|
||||
/*!
|
||||
* @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 session bus connection with a name when in a user context, and a system bus 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> createBusConnection(const ServiceName& 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 system bus connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
|
||||
|
||||
/*!
|
||||
* @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 system bus connection with a name
|
||||
*
|
||||
* @param[in] name Name to request on the connection after its opening
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const ServiceName& 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 session bus 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
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
|
||||
* @brief Creates/opens D-Bus session bus connection with a name
|
||||
*
|
||||
* @param[in] name Name to request on the connection after its opening
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const ServiceName& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session bus connection at a custom address
|
||||
*
|
||||
* @param[in] address ";"-separated list of addresses of bus brokers to try to connect
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*
|
||||
* Consult manual pages for `sd_bus_set_address` of the underlying sd-bus library for more information.
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnectionWithAddress(const std::string& address);
|
||||
|
||||
/*!
|
||||
* @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);
|
||||
|
||||
/*!
|
||||
* @brief Opens direct D-Bus connection at a custom address
|
||||
*
|
||||
* @param[in] address ";"-separated list of addresses of bus brokers to try to connect to
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDirectBusConnection(const std::string& address);
|
||||
|
||||
/*!
|
||||
* @brief Opens direct D-Bus connection at the given file descriptor
|
||||
*
|
||||
* @param[in] fd File descriptor used to communicate directly from/to a D-Bus server
|
||||
* @return Connection instance
|
||||
*
|
||||
* The underlying sdbus-c++ connection instance takes over ownership of fd, so the caller can let it go.
|
||||
* If, however, the call throws an exception, the ownership of fd remains with the caller.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDirectBusConnection(int fd);
|
||||
|
||||
/*!
|
||||
* @brief Opens direct D-Bus connection at fd as a server
|
||||
*
|
||||
* @param[in] fd File descriptor to use for server DBus connection
|
||||
* @return Server connection instance
|
||||
*
|
||||
* This creates a new, custom bus object in server mode. One can then call createDirectBusConnection()
|
||||
* on client side to connect to this bus.
|
||||
*
|
||||
* The underlying sdbus-c++ connection instance takes over ownership of fd, so the caller can let it go.
|
||||
* If, however, the call throws an exception, the ownership of fd remains with the caller.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createServerBus(int fd);
|
||||
|
||||
/*!
|
||||
* @brief Creates sdbus-c++ bus connection representation out of underlying sd_bus instance
|
||||
*
|
||||
* @param[in] bus File descriptor to use for server DBus connection
|
||||
* @return Connection instance
|
||||
*
|
||||
* This functions is helpful in cases where clients need a custom, tweaked configuration of their
|
||||
* bus object. Since sdbus-c++ does not provide C++ API for all bus connection configuration
|
||||
* functions of the underlying sd-bus library, clients can use these sd-bus functions themselves
|
||||
* to create and configure their sd_bus object, and create sdbus-c++ IConnection on top of it.
|
||||
*
|
||||
* The IConnection instance assumes unique ownership of the provided bus object. The bus object
|
||||
* must have been started by the client before this call.
|
||||
* The bus object will get flushed, closed, and unreffed when the IConnection instance is destroyed.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* sd_bus* bus{};
|
||||
* ::sd_bus_new(&bus);
|
||||
* ::sd_bus_set_address(bus, address);
|
||||
* ::sd_bus_set_anonymous(bus, true);
|
||||
* ::sd_bus_start(bus);
|
||||
* auto con = sdbus::createBusConnection(bus); // IConnection consumes sd_bus object
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createBusConnection(sd_bus *bus);
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_ICONNECTION_H_ */
|
||||
|
601
include/sdbus-c++/IObject.h
Executable file → Normal file
601
include/sdbus-c++/IObject.h
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file IObject.h
|
||||
*
|
||||
@ -26,16 +27,21 @@
|
||||
#ifndef SDBUS_CXX_IOBJECT_H_
|
||||
#define SDBUS_CXX_IOBJECT_H_
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.h>
|
||||
#include <sdbus-c++/ConvenienceApiClasses.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/VTableItems.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class Signal;
|
||||
class IConnection;
|
||||
class ObjectPath;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
@ -43,237 +49,436 @@ 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;
|
||||
public: // High-level, convenience API
|
||||
virtual ~IObject() = default;
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) = 0;
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] vtable Individual instances of VTable item structures stored in a vector
|
||||
* @return VTableAdder high-level helper class
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* Parameter `vtable' represents a vtable definition that may contain method declarations
|
||||
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
|
||||
* struct), signal declarations (using SignalVTableItem struct), or global interface
|
||||
* flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime.
|
||||
*
|
||||
* When called like `addVTable(vtable).forInterface(interface)`, then an internal registration
|
||||
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
|
||||
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
|
||||
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
|
||||
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
|
||||
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
|
||||
* can be added or removed by the user at runtime.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] VTableAdder addVTable(std::vector<VTableItem> vtable);
|
||||
|
||||
/*!
|
||||
* @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 Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] items Individual instances of VTable item structures
|
||||
* @return VTableAdder high-level helper class
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* Parameter pack contains vtable definition that may contain method declarations
|
||||
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
|
||||
* struct), signal declarations (using SignalVTableItem struct), or global interface
|
||||
* flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime.
|
||||
*
|
||||
* When called like `addVTable(items...).forInterface(interface)`, then an internal registration
|
||||
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
|
||||
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
|
||||
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
|
||||
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
|
||||
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
|
||||
* can be added or removed by the user at runtime.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
template < typename... VTableItems
|
||||
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
|
||||
[[nodiscard]] VTableAdder addVTable(VTableItems&&... items);
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback ) = 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 = ...;
|
||||
* SignalName fooSignal{"fooSignal"};
|
||||
* object_.emitSignal(fooSignal).onInterface("com.kistler.foo").withArguments(arg1, arg2);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] SignalEmitter emitSignal(const SignalName& signalName);
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
virtual void finishRegistration() = 0;
|
||||
* @copydoc IObject::emitSignal(const SignalName&)
|
||||
*/
|
||||
[[nodiscard]] SignalEmitter emitSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @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 Message createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
|
||||
* @copydoc IObject::emitSignal(const SignalName&)
|
||||
*/
|
||||
[[nodiscard]] SignalEmitter emitSignal(const char* signalName);
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
virtual void emitSignal(const sdbus::Message& message) = 0;
|
||||
* @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 InterfaceName& interfaceName, const std::vector<PropertyName>& propNames) = 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);
|
||||
* @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&,const std::vector<PropertyName>&)
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& 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 InterfaceName& 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);
|
||||
* @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&)
|
||||
*/
|
||||
virtual void emitPropertiesChangedSignal(const char* interfaceName) = 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, 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;
|
||||
|
||||
virtual ~IObject() = 0;
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal on this object path
|
||||
*
|
||||
* This emits an InterfacesAdded signal on this object path with explicitly provided list
|
||||
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
|
||||
* object interfaces and their vtables, so this method now makes more sense.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces) = 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. Since v2.0, sdbus-c++ supports dynamically addable/removable
|
||||
* object interfaces and their vtables, so this method now makes more sense.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& 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.
|
||||
*
|
||||
* This call creates a so-called floating registration. This means that
|
||||
* the ObjectManager interface stays there for the lifetime of the object.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addObjectManager() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds an ObjectManager interface at the path of this D-Bus object
|
||||
*
|
||||
* @return Slot handle owning the registration
|
||||
*
|
||||
* Creates an ObjectManager interface at the specified object path on
|
||||
* the connection. This is a convenient way to interrogate a connection
|
||||
* to see what objects it has.
|
||||
*
|
||||
* The lifetime of the ObjectManager interface is bound to the lifetime
|
||||
* of the returned slot instance.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addObjectManager(return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides D-Bus connection used by the object
|
||||
*
|
||||
* @return Reference to the D-Bus connection
|
||||
*/
|
||||
[[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
[[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides access to the currently processed D-Bus message
|
||||
*
|
||||
* This method provides access to the currently processed incoming D-Bus message.
|
||||
* "Currently processed" means that the registered callback handler(s) for that message
|
||||
* are being invoked. This method is meant to be called from within a callback handler
|
||||
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
|
||||
* guaranteed to return a valid D-Bus message instance for which the handler is called.
|
||||
* If called from other contexts/threads, it may return a valid or invalid message, depending
|
||||
* on whether a message was processed or not at the time of the call.
|
||||
*
|
||||
* @return Currently processed D-Bus message
|
||||
*/
|
||||
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief 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;
|
||||
|
||||
public: // Lower-level, message-based API
|
||||
/*!
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] items Individual instances of VTable item structures
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* Parameter `items' represents a vtable definition that may contain method declarations
|
||||
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
|
||||
* struct), signal declarations (using SignalVTableItem struct), or global interface
|
||||
* flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime. For each vtable an internal
|
||||
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
template < typename... VTableItems
|
||||
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
|
||||
void addVTable(InterfaceName interfaceName, VTableItems&&... items);
|
||||
|
||||
/*!
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
|
||||
* property declarations (using PropertyVTableItem struct), signal declarations (using
|
||||
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime. For each vtable an internal
|
||||
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
|
||||
* property declarations (using PropertyVTableItem struct), signal declarations (using
|
||||
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
|
||||
*
|
||||
* The method can be called at any time during object's lifetime. For each vtable an internal
|
||||
* registration slot is created and is returned to the caller. The returned slot should be destroyed
|
||||
* when the vtable is not needed anymore. This allows for "dynamic" object API where vtables
|
||||
* can be added or removed by the user at runtime.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Creates a signal message
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A signal message
|
||||
*
|
||||
* Serialize signal arguments into the returned message and emit the signal by passing
|
||||
* the message with serialized arguments to the @c emitSignal function.
|
||||
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits signal for this object path
|
||||
*
|
||||
* @param[in] message Signal message to be sent out
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitSignal(const sdbus::Signal& message) = 0;
|
||||
|
||||
protected: // Internal API for efficiency reasons used by high-level API helper classes
|
||||
friend SignalEmitter;
|
||||
|
||||
[[nodiscard]] virtual Signal createSignal(const char* interfaceName, const char* signalName) const = 0;
|
||||
};
|
||||
|
||||
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
|
||||
{
|
||||
return MethodRegistrator(*this, methodName);
|
||||
}
|
||||
// Out-of-line member definitions
|
||||
|
||||
inline SignalRegistrator IObject::registerSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalRegistrator(*this, std::move(signalName));
|
||||
}
|
||||
|
||||
inline PropertyRegistrator IObject::registerProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertyRegistrator(*this, std::move(propertyName));
|
||||
}
|
||||
|
||||
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
|
||||
inline SignalEmitter IObject::emitSignal(const SignalName& signalName)
|
||||
{
|
||||
return SignalEmitter(*this, signalName);
|
||||
}
|
||||
|
||||
inline IObject::~IObject() {}
|
||||
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalEmitter(*this, signalName.c_str());
|
||||
}
|
||||
|
||||
inline SignalEmitter IObject::emitSignal(const char* signalName)
|
||||
{
|
||||
return SignalEmitter(*this, signalName);
|
||||
}
|
||||
|
||||
template <typename... VTableItems, typename>
|
||||
void IObject::addVTable(InterfaceName interfaceName, VTableItems&&... items)
|
||||
{
|
||||
addVTable(std::move(interfaceName), {std::forward<VTableItems>(items)...});
|
||||
}
|
||||
|
||||
template <typename... VTableItems, typename>
|
||||
VTableAdder IObject::addVTable(VTableItems&&... items)
|
||||
{
|
||||
return addVTable(std::vector<VTableItem>{std::forward<VTableItems>(items)...});
|
||||
}
|
||||
|
||||
inline VTableAdder IObject::addVTable(std::vector<VTableItem> vtable)
|
||||
{
|
||||
return VTableAdder(*this, std::move(vtable));
|
||||
}
|
||||
|
||||
/*!
|
||||
* @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, ObjectPath objectPath);
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.inl>
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
#include <sdbus-c++/VTableItems.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IOBJECT_H_ */
|
||||
|
@ -1,270 +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 Message;
|
||||
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 Message 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 Message callMethod(const sdbus::Message& 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_ */
|
981
include/sdbus-c++/IProxy.h
Normal file
981
include/sdbus-c++/IProxy.h
Normal file
@ -0,0 +1,981 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class IConnection;
|
||||
class ObjectPath;
|
||||
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: // High-level, convenience API
|
||||
virtual ~IProxy() = default;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the 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 = ...;
|
||||
* MethodName multiply{"multiply"};
|
||||
* object_.callMethod(multiply).onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] MethodInvoker callMethod(const MethodName& methodName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodName&)
|
||||
*/
|
||||
[[nodiscard]] MethodInvoker callMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodName&)
|
||||
*/
|
||||
[[nodiscard]] MethodInvoker callMethod(const char* methodName);
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the 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 = ...;
|
||||
* MethodName multiply{"multiply"};
|
||||
* 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 MethodName& methodName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethodAsync(const MethodName&)
|
||||
*/
|
||||
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethodAsync(const MethodName&)
|
||||
*/
|
||||
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const char* methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal handler for a given signal of the 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.
|
||||
*
|
||||
* A signal can be subscribed to and unsubscribed from at any time during proxy
|
||||
* lifetime. The subscription is active immediately after the call.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.uponSignal("stateChanged").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onStateChanged(arg1, arg2); });
|
||||
* sdbus::InterfaceName foo{"com.kistler.foo"};
|
||||
* sdbus::SignalName levelChanged{"levelChanged"};
|
||||
* object_.uponSignal(levelChanged).onInterface(foo).call([this](uint16_t level){ this->onLevelChanged(level); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] SignalSubscriber uponSignal(const SignalName& signalName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::uponSignal(const SignalName&)
|
||||
*/
|
||||
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::uponSignal(const SignalName&)
|
||||
*/
|
||||
[[nodiscard]] SignalSubscriber uponSignal(const char* signalName);
|
||||
|
||||
/*!
|
||||
* @brief Gets value of a property of the 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");
|
||||
* sdbus::InterfaceName foo{"com.kistler.foo"};
|
||||
* sdbus::PropertyName level{"level"};
|
||||
* int level = object.getProperty(level).onInterface(foo);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] PropertyGetter getProperty(const PropertyName& propertyName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::getProperty(const PropertyName&)
|
||||
*/
|
||||
[[nodiscard]] PropertyGetter getProperty(std::string_view propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Gets value of a property of the D-Bus object asynchronously
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient asynchronous 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.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* std::future<sdbus::Variant> state = object.getPropertyAsync("state").onInterface("com.kistler.foo").getResultAsFuture();
|
||||
* auto callback = [](std::optional<sdbus::Error> err, const sdbus::Variant& value){ ... };
|
||||
* object.getPropertyAsync("state").onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] AsyncPropertyGetter getPropertyAsync(const PropertyName& propertyName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::getPropertyAsync(const PropertyName&)
|
||||
*/
|
||||
[[nodiscard]] AsyncPropertyGetter getPropertyAsync(std::string_view propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets value of a property of the 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.
|
||||
* Setting property value with NoReply flag is also supported.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int state = ...;
|
||||
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
|
||||
* // Or we can just send the set message call without waiting for the reply
|
||||
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state, dont_expect_reply);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] PropertySetter setProperty(const PropertyName& propertyName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::setProperty(const PropertyName&)
|
||||
*/
|
||||
[[nodiscard]] PropertySetter setProperty(std::string_view propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets value of a property of the D-Bus object asynchronously
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient asynchronous 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 = ...;
|
||||
* // We can wait until the set operation finishes by waiting on the future
|
||||
* std::future<void> res = object_.setPropertyAsync("state").onInterface("com.kistler.foo").toValue(state).getResultAsFuture();
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] AsyncPropertySetter setPropertyAsync(const PropertyName& propertyName);
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::setPropertyAsync(const PropertyName&)
|
||||
*/
|
||||
[[nodiscard]] AsyncPropertySetter setPropertyAsync(std::string_view propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Gets values of all properties of the D-Bus object
|
||||
*
|
||||
* @return A helper object for convenient getting of properties' values
|
||||
*
|
||||
* This is a high-level, convenience way of reading D-Bus properties' values that abstracts
|
||||
* from the D-Bus message concept.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* auto props = object.getAllProperties().onInterface("com.kistler.foo");
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] AllPropertiesGetter getAllProperties();
|
||||
|
||||
/*!
|
||||
* @brief Gets values of all properties of the D-Bus object asynchronously
|
||||
*
|
||||
* @return A helper object for convenient asynchronous getting of properties' values
|
||||
*
|
||||
* This is a high-level, convenience way of reading D-Bus properties' values that abstracts
|
||||
* from the D-Bus message concept.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* auto callback = [](std::optional<sdbus::Error> err, const std::map<PropertyName, Variant>>& properties){ ... };
|
||||
* auto props = object.getAllPropertiesAsync().onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] AsyncAllPropertiesGetter getAllPropertiesAsync();
|
||||
|
||||
/*!
|
||||
* @brief Provides D-Bus connection used by the proxy
|
||||
*
|
||||
* @return Reference to the D-Bus connection
|
||||
*/
|
||||
[[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Returns object path of the underlying DBus object
|
||||
*/
|
||||
[[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Provides access to the currently processed D-Bus message
|
||||
*
|
||||
* This method provides access to the currently processed incoming D-Bus message.
|
||||
* "Currently processed" means that the registered callback handler(s) for that message
|
||||
* are being invoked. This method is meant to be called from within a callback handler
|
||||
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
|
||||
* guaranteed to return a valid D-Bus message instance for which the handler is called.
|
||||
* If called from other contexts/threads, it may return a valid or invalid message, depending
|
||||
* on whether a message was processed or not at the time of the call.
|
||||
*
|
||||
* @return Currently processed D-Bus message
|
||||
*/
|
||||
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Unregisters proxy's signal handlers and stops receiving 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;
|
||||
|
||||
public: // Lower-level, message-based API
|
||||
/*!
|
||||
* @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
|
||||
*/
|
||||
[[nodiscard]] virtual MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the remote D-Bus object
|
||||
*
|
||||
* @param[in] message Message representing a method call
|
||||
* @return A method reply message
|
||||
*
|
||||
* The call does not block if the method call has dont-expect-reply flag set. In that case,
|
||||
* the call returns immediately and the return value is an empty, invalid method reply.
|
||||
*
|
||||
* The call blocks otherwise, waiting for the remote peer to send back a reply or an error,
|
||||
* or until the call times out.
|
||||
*
|
||||
* While blocking, other concurrent operations (in other threads) on the underlying bus
|
||||
* connection are stalled until the call returns. This is not an issue in vast majority of
|
||||
* (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving
|
||||
* shared bus connections, this may be an issue. It is advised to instead use an asynchronous
|
||||
* callMethod() function overload, which does not block the bus connection, or do the synchronous
|
||||
* call from another Proxy instance created just before the call and then destroyed (which is
|
||||
* anyway quite a typical approach in D-Bus implementations). Such proxy instance must have
|
||||
* its own bus connection. So-called light-weight proxies (ones running without an event loop thread)
|
||||
* tag are designed for exactly that purpose.
|
||||
*
|
||||
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure (also in case the remote function returned an error)
|
||||
*/
|
||||
virtual MethodReply callMethod(const MethodCall& message) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the remote D-Bus object
|
||||
*
|
||||
* @param[in] message Message representing a method call
|
||||
* @param[in] timeout Method call timeout (in microseconds)
|
||||
* @return A method reply message
|
||||
*
|
||||
* The call does not block if the method call has dont-expect-reply flag set. In that case,
|
||||
* the call returns immediately and the return value is an empty, invalid method reply.
|
||||
*
|
||||
* The call blocks otherwise, waiting for the remote peer to send back a reply or an error,
|
||||
* or until the call times out.
|
||||
*
|
||||
* While blocking, other concurrent operations (in other threads) on the underlying bus
|
||||
* connection are stalled until the call returns. This is not an issue in vast majority of
|
||||
* (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving
|
||||
* shared bus connections, this may be an issue. It is advised to instead use an asynchronous
|
||||
* callMethod() function overload, which does not block the bus connection, or do the synchronous
|
||||
* call from another Proxy instance created just before the call and then destroyed (which is
|
||||
* anyway quite a typical approach in D-Bus implementations). Such proxy instance must have
|
||||
* its own bus connection. So-called light-weight proxies (ones running without an event loop thread)
|
||||
* tag are designed for exactly that purpose.
|
||||
*
|
||||
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure (also in case the remote function returned an error)
|
||||
*/
|
||||
virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout) = 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 D-Bus object asynchronously
|
||||
*
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] asyncReplyCallback Handler for the async reply
|
||||
* @return Observing handle for the the pending asynchronous call
|
||||
*
|
||||
* This is a callback-based way of asynchronously calling a remote D-Bus method.
|
||||
*
|
||||
* The call itself 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 bus
|
||||
* connection I/O event loop thread.
|
||||
*
|
||||
* An non-owning, observing async call handle is returned that can be used to query call status or cancel the call.
|
||||
*
|
||||
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the D-Bus object asynchronously
|
||||
*
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] asyncReplyCallback Handler for the async reply
|
||||
* @return RAII-style slot handle representing the ownership of the async call
|
||||
*
|
||||
* This is a callback-based way of asynchronously calling a remote D-Bus method.
|
||||
*
|
||||
* The call itself 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 bus
|
||||
* connection I/O event loop thread.
|
||||
*
|
||||
* A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot.
|
||||
* The slot can be used to cancel the method call at a later time by simply destroying it.
|
||||
*
|
||||
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the D-Bus object asynchronously, with custom timeout
|
||||
*
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] asyncReplyCallback Handler for the async reply
|
||||
* @param[in] timeout Method call timeout (in microseconds)
|
||||
* @return Observing handle for the the pending asynchronous call
|
||||
*
|
||||
* This is a callback-based way of asynchronously calling a remote D-Bus method.
|
||||
*
|
||||
* The call itself 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 bus
|
||||
* connection I/O event loop thread.
|
||||
*
|
||||
* An non-owning, observing async call handle is returned that can be used to query call status or cancel the call.
|
||||
*
|
||||
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual PendingAsyncCall callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, uint64_t timeout ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the D-Bus object asynchronously, with custom timeout
|
||||
*
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] asyncReplyCallback Handler for the async reply
|
||||
* @param[in] timeout Method call timeout (in microseconds)
|
||||
* @return RAII-style slot handle representing the ownership of the async call
|
||||
*
|
||||
* This is a callback-based way of asynchronously calling a remote D-Bus method.
|
||||
*
|
||||
* The call itself 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 bus
|
||||
* connection I/O event loop thread.
|
||||
*
|
||||
* A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot.
|
||||
* The slot can be used to cancel the method call at a later time by simply destroying it.
|
||||
*
|
||||
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, uint64_t timeout
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
PendingAsyncCall callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, const std::chrono::duration<_Rep, _Period>& timeout );
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t,return_slot_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
[[nodiscard]] Slot callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, const std::chrono::duration<_Rep, _Period>& timeout
|
||||
, return_slot_t );
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the D-Bus object asynchronously
|
||||
*
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] Tag denoting a std::future-based overload
|
||||
* @return Future object providing access to the future method reply message
|
||||
*
|
||||
* This is a std::future-based way of asynchronously calling a remote D-Bus method.
|
||||
*
|
||||
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
|
||||
* the provided future object will be set to contain the reply (or sdbus::Error
|
||||
* in case the remote method threw an exception).
|
||||
*
|
||||
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual std::future<MethodReply> callMethodAsync(const MethodCall& message, with_future_t) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the D-Bus object asynchronously, with custom timeout
|
||||
*
|
||||
* @param[in] message Message representing an async method call
|
||||
* @param[in] timeout Method call timeout
|
||||
* @param[in] Tag denoting a std::future-based overload
|
||||
* @return Future object providing access to the future method reply message
|
||||
*
|
||||
* This is a std::future-based way of asynchronously calling a remote D-Bus method.
|
||||
*
|
||||
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
|
||||
* the provided future object will be set to contain the reply (or sdbus::Error
|
||||
* in case the remote method threw an exception, or the call timed out).
|
||||
*
|
||||
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual std::future<MethodReply> callMethodAsync( const MethodCall& message
|
||||
, uint64_t timeout
|
||||
, with_future_t ) = 0;
|
||||
|
||||
/*!
|
||||
* @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t)
|
||||
*/
|
||||
template <typename _Rep, typename _Period>
|
||||
std::future<MethodReply> callMethodAsync( const MethodCall& message
|
||||
, const std::chrono::duration<_Rep, _Period>& timeout
|
||||
, with_future_t );
|
||||
|
||||
/*!
|
||||
* @brief Registers a handler for the desired signal emitted by the 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
|
||||
*
|
||||
* A signal can be subscribed to at any time during proxy lifetime. The subscription
|
||||
* is active immediately after the call, and stays active for the entire lifetime
|
||||
* of the Proxy object.
|
||||
*
|
||||
* To be able to unsubscribe from the signal at a later time, use the registerSignalHandler()
|
||||
* overload with request_slot tag.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers a handler for the desired signal emitted by the 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
|
||||
*
|
||||
* @return RAII-style slot handle representing the ownership of the subscription
|
||||
*
|
||||
* A signal can be subscribed to and unsubscribed from at any time during proxy
|
||||
* lifetime. The subscription is active immediately after the call. The lifetime
|
||||
* of the subscription is bound to the lifetime of the slot object. The subscription
|
||||
* is unregistered by letting go of the slot object.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
[[nodiscard]] virtual Slot registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
protected: // Internal API for efficiency reasons used by high-level API helper classes
|
||||
friend MethodInvoker;
|
||||
friend AsyncMethodInvoker;
|
||||
friend SignalSubscriber;
|
||||
|
||||
[[nodiscard]] virtual MethodCall createMethodCall(const char* interfaceName, const char* methodName) const = 0;
|
||||
virtual void registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler ) = 0;
|
||||
[[nodiscard]] virtual Slot registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t ) = 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.
|
||||
*/
|
||||
[[nodiscard]] bool isPending() const;
|
||||
|
||||
private:
|
||||
friend internal::Proxy;
|
||||
PendingAsyncCall(std::weak_ptr<void> callInfo);
|
||||
|
||||
private:
|
||||
std::weak_ptr<void> callInfo_;
|
||||
};
|
||||
|
||||
// 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::callMethodAsync( 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 callMethodAsync(message, std::move(asyncReplyCallback), microsecs.count());
|
||||
}
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline Slot IProxy::callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, const std::chrono::duration<_Rep, _Period>& timeout
|
||||
, return_slot_t )
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
return callMethodAsync(message, std::move(asyncReplyCallback), microsecs.count(), return_slot);
|
||||
}
|
||||
|
||||
template <typename _Rep, typename _Period>
|
||||
inline std::future<MethodReply> IProxy::callMethodAsync( const MethodCall& message
|
||||
, const std::chrono::duration<_Rep, _Period>& timeout
|
||||
, with_future_t )
|
||||
{
|
||||
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
|
||||
return callMethodAsync(message, microsecs.count(), with_future);
|
||||
}
|
||||
|
||||
inline MethodInvoker IProxy::callMethod(const MethodName& methodName)
|
||||
{
|
||||
return MethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline MethodInvoker IProxy::callMethod(const std::string& methodName)
|
||||
{
|
||||
return MethodInvoker(*this, methodName.c_str());
|
||||
}
|
||||
|
||||
inline MethodInvoker IProxy::callMethod(const char* methodName)
|
||||
{
|
||||
return MethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker IProxy::callMethodAsync(const MethodName& methodName)
|
||||
{
|
||||
return AsyncMethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker IProxy::callMethodAsync(const std::string& methodName)
|
||||
{
|
||||
return AsyncMethodInvoker(*this, methodName.c_str());
|
||||
}
|
||||
|
||||
inline AsyncMethodInvoker IProxy::callMethodAsync(const char* methodName)
|
||||
{
|
||||
return AsyncMethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline SignalSubscriber IProxy::uponSignal(const SignalName& signalName)
|
||||
{
|
||||
return SignalSubscriber(*this, signalName);
|
||||
}
|
||||
|
||||
inline SignalSubscriber IProxy::uponSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalSubscriber(*this, signalName.c_str());
|
||||
}
|
||||
|
||||
inline SignalSubscriber IProxy::uponSignal(const char* signalName)
|
||||
{
|
||||
return SignalSubscriber(*this, signalName);
|
||||
}
|
||||
|
||||
inline PropertyGetter IProxy::getProperty(const PropertyName& propertyName)
|
||||
{
|
||||
return PropertyGetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline PropertyGetter IProxy::getProperty(std::string_view propertyName)
|
||||
{
|
||||
return PropertyGetter(*this, std::move(propertyName));
|
||||
}
|
||||
|
||||
inline AsyncPropertyGetter IProxy::getPropertyAsync(const PropertyName& propertyName)
|
||||
{
|
||||
return AsyncPropertyGetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline AsyncPropertyGetter IProxy::getPropertyAsync(std::string_view propertyName)
|
||||
{
|
||||
return AsyncPropertyGetter(*this, std::move(propertyName));
|
||||
}
|
||||
|
||||
inline PropertySetter IProxy::setProperty(const PropertyName& propertyName)
|
||||
{
|
||||
return PropertySetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline PropertySetter IProxy::setProperty(std::string_view propertyName)
|
||||
{
|
||||
return PropertySetter(*this, std::move(propertyName));
|
||||
}
|
||||
|
||||
inline AsyncPropertySetter IProxy::setPropertyAsync(const PropertyName& propertyName)
|
||||
{
|
||||
return AsyncPropertySetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline AsyncPropertySetter IProxy::setPropertyAsync(std::string_view propertyName)
|
||||
{
|
||||
return AsyncPropertySetter(*this, std::move(propertyName));
|
||||
}
|
||||
|
||||
inline AllPropertiesGetter IProxy::getAllProperties()
|
||||
{
|
||||
return AllPropertiesGetter(*this);
|
||||
}
|
||||
|
||||
inline AsyncAllPropertiesGetter IProxy::getAllPropertiesAsync()
|
||||
{
|
||||
return AsyncAllPropertiesGetter(*this);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @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.
|
||||
*
|
||||
* The destination parameter may be an empty string (useful e.g. in case of direct
|
||||
* D-Bus connections to a custom server bus).
|
||||
*
|
||||
* 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
|
||||
, ServiceName destination
|
||||
, ObjectPath 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.
|
||||
*
|
||||
* The destination parameter may be an empty string (useful e.g. in case of direct
|
||||
* D-Bus connections to a custom server bus).
|
||||
*
|
||||
* 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
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides the remote D-Bus object
|
||||
* @param[in] objectPath Path of the remote D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* The provided connection will be used by the proxy to issue calls against the object.
|
||||
* The Object proxy becomes an exclusive owner of this connection, but will not start
|
||||
* an event loop thread on this connection. This is cheap construction and is suitable
|
||||
* for short-lived proxies created just to execute simple synchronous D-Bus calls and
|
||||
* then destroyed. Such blocking request-reply calls will work without an event loop
|
||||
* (but signals, async calls, etc. won't).
|
||||
*
|
||||
* The destination parameter may be an empty string (useful e.g. in case of direct
|
||||
* D-Bus connections to a custom server bus).
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo", sdbus::dont_run_event_loop_thread);
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* Does the same thing as createProxy(std::unique_ptr<sdbus::IConnection>&&, ServiceName, ObjectPath, dont_run_event_loop_thread_t);
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createLightWeightProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @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 an event 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( ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @param[in] destination Bus name that provides the remote D-Bus object
|
||||
* @param[in] objectPath Path of the remote D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* No D-Bus connection is provided here, so the object proxy will create and manage
|
||||
* his own connection, but it will not start an event loop thread. This is cheap
|
||||
* construction and is suitable for short-lived proxies created just to execute simple
|
||||
* synchronous D-Bus calls and then destroyed. Such blocking request-reply calls
|
||||
* will work without an event loop (but signals, async calls, etc. won't).
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo", sdbus::dont_run_event_loop_thread );
|
||||
* @endcode
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* Does the same thing as createProxy(ServiceName, ObjectPath, dont_run_event_loop_thread_t);
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath);
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
|
||||
#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 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Message.h
|
||||
*
|
||||
@ -26,17 +27,27 @@
|
||||
#ifndef SDBUS_CXX_MESSAGE_H_
|
||||
#define SDBUS_CXX_MESSAGE_H_
|
||||
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#ifdef __has_include
|
||||
# if __has_include(<span>)
|
||||
# include <span>
|
||||
# endif
|
||||
#endif
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@ -44,6 +55,11 @@ namespace sdbus {
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
class UnixFd;
|
||||
class MethodReply;
|
||||
namespace internal {
|
||||
class IConnection;
|
||||
}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
@ -52,29 +68,18 @@ namespace sdbus {
|
||||
* @class Message
|
||||
*
|
||||
* Message represents a D-Bus message, which can be either method call message,
|
||||
* method reply message, signal message, or a plain message serving as a storage
|
||||
* for serialized data.
|
||||
* method reply message, signal message, or a plain message.
|
||||
*
|
||||
* Serialization and deserialization functions are provided for types supported
|
||||
* by D-Bus.
|
||||
*
|
||||
* You don't need to work with this class directly if you use high-level APIs
|
||||
* of @c IObject and @c IObjectProxy.
|
||||
* You mostly don't need to work with this class directly if you use high-level
|
||||
* APIs of @c IObject and @c IProxy.
|
||||
*
|
||||
***********************************************/
|
||||
class Message
|
||||
class [[nodiscard]] Message
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
ePlainMessage
|
||||
, eMethodCall
|
||||
, eMethodReply
|
||||
, eSignal
|
||||
};
|
||||
|
||||
Message() = default;
|
||||
Message(void *msg, Type type = Type::ePlainMessage) noexcept;
|
||||
Message(const Message&) noexcept;
|
||||
Message& operator=(const Message&) noexcept;
|
||||
Message(Message&& other) noexcept;
|
||||
@ -92,9 +97,33 @@ namespace sdbus {
|
||||
Message& operator<<(double item);
|
||||
Message& operator<<(const char *item);
|
||||
Message& operator<<(const std::string &item);
|
||||
Message& operator<<(std::string_view item);
|
||||
Message& operator<<(const Variant &item);
|
||||
template <typename ...Elements>
|
||||
Message& operator<<(const std::variant<Elements...>& value);
|
||||
Message& operator<<(const ObjectPath &item);
|
||||
Message& operator<<(const Signature &item);
|
||||
Message& operator<<(const UnixFd &item);
|
||||
template <typename _Element, typename _Allocator>
|
||||
Message& operator<<(const std::vector<_Element, _Allocator>& items);
|
||||
template <typename _Element, std::size_t _Size>
|
||||
Message& operator<<(const std::array<_Element, _Size>& items);
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
Message& operator<<(const std::span<_Element, _Extent>& items);
|
||||
#endif
|
||||
template <typename _Enum, typename = std::enable_if_t<std::is_enum_v<_Enum>>>
|
||||
Message& operator<<(const _Enum& item);
|
||||
template <typename _Key, typename _Value>
|
||||
Message& operator<<(const DictEntry<_Key, _Value>& value);
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items);
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator<<(const Struct<_ValueTypes...>& item);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator<<(const std::tuple<_ValueTypes...>& item);
|
||||
|
||||
Message& operator>>(bool& item);
|
||||
Message& operator>>(int16_t& item);
|
||||
@ -108,82 +137,322 @@ namespace sdbus {
|
||||
Message& operator>>(char*& item);
|
||||
Message& operator>>(std::string &item);
|
||||
Message& operator>>(Variant &item);
|
||||
template <typename ...Elements>
|
||||
Message& operator>>(std::variant<Elements...>& value);
|
||||
Message& operator>>(ObjectPath &item);
|
||||
Message& operator>>(Signature &item);
|
||||
Message& operator>>(UnixFd &item);
|
||||
template <typename _Element, typename _Allocator>
|
||||
Message& operator>>(std::vector<_Element, _Allocator>& items);
|
||||
template <typename _Element, std::size_t _Size>
|
||||
Message& operator>>(std::array<_Element, _Size>& items);
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
Message& operator>>(std::span<_Element, _Extent>& items);
|
||||
#endif
|
||||
template <typename _Enum, typename = std::enable_if_t<std::is_enum_v<_Enum>>>
|
||||
Message& operator>>(_Enum& item);
|
||||
template <typename _Key, typename _Value>
|
||||
Message& operator>>(DictEntry<_Key, _Value>& value);
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items);
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator>>(Struct<_ValueTypes...>& item);
|
||||
template <typename... _ValueTypes>
|
||||
Message& operator>>(std::tuple<_ValueTypes...>& item);
|
||||
|
||||
Message& openContainer(const std::string& signature);
|
||||
template <typename _ElementType>
|
||||
Message& openContainer();
|
||||
Message& openContainer(const char* signature);
|
||||
Message& closeContainer();
|
||||
Message& openDictEntry(const std::string& signature);
|
||||
template <typename _KeyType, typename _ValueType>
|
||||
Message& openDictEntry();
|
||||
Message& openDictEntry(const char* signature);
|
||||
Message& closeDictEntry();
|
||||
Message& openVariant(const std::string& signature);
|
||||
template <typename _ValueType>
|
||||
Message& openVariant();
|
||||
Message& openVariant(const char* signature);
|
||||
Message& closeVariant();
|
||||
Message& openStruct(const std::string& signature);
|
||||
template <typename... _ValueTypes>
|
||||
Message& openStruct();
|
||||
Message& openStruct(const char* signature);
|
||||
Message& closeStruct();
|
||||
|
||||
Message& enterContainer(const std::string& signature);
|
||||
template <typename _ElementType>
|
||||
Message& enterContainer();
|
||||
Message& enterContainer(const char* signature);
|
||||
Message& exitContainer();
|
||||
Message& enterDictEntry(const std::string& signature);
|
||||
template <typename _KeyType, typename _ValueType>
|
||||
Message& enterDictEntry();
|
||||
Message& enterDictEntry(const char* signature);
|
||||
Message& exitDictEntry();
|
||||
Message& enterVariant(const std::string& signature);
|
||||
template <typename _ValueType>
|
||||
Message& enterVariant();
|
||||
Message& enterVariant(const char* signature);
|
||||
Message& exitVariant();
|
||||
Message& enterStruct(const std::string& signature);
|
||||
template <typename... _ValueTypes>
|
||||
Message& enterStruct();
|
||||
Message& enterStruct(const char* signature);
|
||||
Message& exitStruct();
|
||||
|
||||
operator bool() const;
|
||||
Message& appendArray(char type, const void *ptr, size_t size);
|
||||
Message& readArray(char type, const void **ptr, size_t *size);
|
||||
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
Message& serializeDictionary(const _Callback& callback);
|
||||
template <typename _Key, typename _Value>
|
||||
Message& serializeDictionary(const std::initializer_list<DictEntry<_Key, _Value>>& dictEntries);
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
Message& deserializeDictionary(const _Callback& callback);
|
||||
|
||||
explicit operator bool() const;
|
||||
void clearFlags();
|
||||
|
||||
std::string getInterfaceName() const;
|
||||
std::string getMemberName() const;
|
||||
void peekType(std::string& type, std::string& contents) const;
|
||||
const char* getInterfaceName() const;
|
||||
const char* getMemberName() const;
|
||||
const char* getSender() const;
|
||||
const char* getPath() const;
|
||||
const char* getDestination() const;
|
||||
uint64_t getCookie() const;
|
||||
// TODO: short docs in whole Message API
|
||||
std::pair<char, const char*> peekType() const;
|
||||
bool isValid() const;
|
||||
bool isEmpty() const;
|
||||
Type getType() const;
|
||||
bool isAtEnd(bool complete) const;
|
||||
|
||||
void copyTo(Message& destination, bool complete) const;
|
||||
void seal();
|
||||
void rewind(bool complete);
|
||||
|
||||
Message createReply() const;
|
||||
Message send() 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;
|
||||
|
||||
class Factory;
|
||||
|
||||
private:
|
||||
template <typename _Array>
|
||||
void serializeArray(const _Array& items);
|
||||
template <typename _Array>
|
||||
void deserializeArray(_Array& items);
|
||||
template <typename _Array>
|
||||
void deserializeArrayFast(_Array& items);
|
||||
template <typename _Element, typename _Allocator>
|
||||
void deserializeArrayFast(std::vector<_Element, _Allocator>& items);
|
||||
template <typename _Array>
|
||||
void deserializeArraySlow(_Array& items);
|
||||
template <typename _Element, typename _Allocator>
|
||||
void deserializeArraySlow(std::vector<_Element, _Allocator>& items);
|
||||
|
||||
protected:
|
||||
Message() = default;
|
||||
explicit Message(internal::IConnection* connection) noexcept;
|
||||
Message(void *msg, internal::IConnection* connection) noexcept;
|
||||
Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
|
||||
|
||||
friend Factory;
|
||||
|
||||
protected:
|
||||
void* msg_{};
|
||||
Type type_{Type::ePlainMessage};
|
||||
internal::IConnection* connection_{};
|
||||
mutable bool ok_{true};
|
||||
};
|
||||
|
||||
template <typename _Element>
|
||||
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
|
||||
class MethodCall : public Message
|
||||
{
|
||||
msg.openContainer(signature_of<_Element>::str());
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
for (const auto& item : items)
|
||||
msg << item;
|
||||
public:
|
||||
MethodCall() = default;
|
||||
|
||||
msg.closeContainer();
|
||||
MethodReply send(uint64_t timeout) const;
|
||||
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout, return_slot_t) const;
|
||||
|
||||
return msg;
|
||||
MethodReply createReply() const;
|
||||
MethodReply createErrorReply(const sdbus::Error& error) const;
|
||||
|
||||
void dontExpectReply();
|
||||
bool doesntExpectReply() const;
|
||||
|
||||
protected:
|
||||
MethodCall(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
|
||||
|
||||
private:
|
||||
MethodReply sendWithReply(uint64_t timeout = 0) const;
|
||||
MethodReply sendWithNoReply() const;
|
||||
};
|
||||
|
||||
class MethodReply : public Message
|
||||
{
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
MethodReply() = default;
|
||||
void send() const;
|
||||
uint64_t getReplyCookie() const;
|
||||
};
|
||||
|
||||
class Signal : public Message
|
||||
{
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
Signal() = default;
|
||||
void setDestination(const std::string& destination);
|
||||
void setDestination(const char* 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;
|
||||
};
|
||||
|
||||
// Represents any of the above message types, or just a message that serves as a container for data
|
||||
class PlainMessage : public Message
|
||||
{
|
||||
using Message::Message;
|
||||
friend Factory;
|
||||
|
||||
public:
|
||||
PlainMessage() = default;
|
||||
};
|
||||
|
||||
PlainMessage createPlainMessage();
|
||||
|
||||
template <typename ...Elements>
|
||||
inline Message& Message::operator<<(const std::variant<Elements...>& value)
|
||||
{
|
||||
std::visit([this](const auto& inner)
|
||||
{
|
||||
openVariant<decltype(inner)>();
|
||||
*this << inner;
|
||||
closeVariant();
|
||||
}, value);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
|
||||
{
|
||||
serializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Element, std::size_t _Size>
|
||||
inline Message& Message::operator<<(const std::array<_Element, _Size>& items)
|
||||
{
|
||||
serializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
inline Message& Message::operator<<(const std::span<_Element, _Extent>& items)
|
||||
{
|
||||
serializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename _Enum, typename>
|
||||
inline Message& Message::operator<<(const _Enum &item)
|
||||
{
|
||||
return operator<<(static_cast<std::underlying_type_t<_Enum>>(item));
|
||||
}
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::serializeArray(const _Array& items)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
|
||||
// Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool,
|
||||
// otherwise use step-by-step serialization of individual elements.
|
||||
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
|
||||
appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType));
|
||||
}
|
||||
else
|
||||
{
|
||||
openContainer<ElementType>();
|
||||
|
||||
for (const auto& item : items)
|
||||
*this << item;
|
||||
|
||||
closeContainer();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& operator<<(Message& msg, const std::map<_Key, _Value>& items)
|
||||
inline Message& Message::operator<<(const DictEntry<_Key, _Value>& value)
|
||||
{
|
||||
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
|
||||
const std::string arraySignature = "{" + dictEntrySignature + "}";
|
||||
openDictEntry<_Key, _Value>();
|
||||
*this << value.first;
|
||||
*this << value.second;
|
||||
closeDictEntry();
|
||||
|
||||
msg.openContainer(arraySignature);
|
||||
return *this;
|
||||
}
|
||||
|
||||
for (const auto& item : items)
|
||||
{
|
||||
msg.openDictEntry(dictEntrySignature);
|
||||
msg << item.first;
|
||||
msg << item.second;
|
||||
msg.closeDictEntry();
|
||||
}
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items)
|
||||
{
|
||||
serializeDictionary<_Key, _Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
|
||||
|
||||
msg.closeContainer();
|
||||
return *this;
|
||||
}
|
||||
|
||||
return msg;
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
|
||||
{
|
||||
serializeDictionary<_Key, _Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& Message::serializeDictionary(const std::initializer_list<DictEntry<_Key, _Value>>& items)
|
||||
{
|
||||
serializeDictionary<_Key, _Value>([&](Message& msg){ for (const auto& item : items) msg << item; });
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
inline Message& Message::serializeDictionary(const _Callback& callback)
|
||||
{
|
||||
openContainer<DictEntry<_Key, _Value>>();
|
||||
callback(*this);
|
||||
closeContainer();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
@ -191,10 +460,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>
|
||||
@ -207,78 +473,216 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator<<(Message& msg, const Struct<_ValueTypes...>& item)
|
||||
inline Message& Message::operator<<(const Struct<_ValueTypes...>& item)
|
||||
{
|
||||
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
|
||||
assert(structSignature.size() > 2);
|
||||
// Remove opening and closing parenthesis from the struct signature to get contents signature
|
||||
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
|
||||
openStruct<_ValueTypes...>();
|
||||
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
closeStruct();
|
||||
|
||||
msg.openStruct(structContentSignature);
|
||||
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
msg.closeStruct();
|
||||
|
||||
return msg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator<<(Message& msg, const std::tuple<_ValueTypes...>& item)
|
||||
inline Message& Message::operator<<(const std::tuple<_ValueTypes...>& item)
|
||||
{
|
||||
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
return msg;
|
||||
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <typename _Element>
|
||||
inline Message& operator>>(Message& msg, std::vector<_Element>& items)
|
||||
namespace detail
|
||||
{
|
||||
if(!msg.enterContainer(signature_of<_Element>::str()))
|
||||
return msg;
|
||||
template <typename _Element, typename... _Elements>
|
||||
bool deserialize_variant(Message& msg, std::variant<_Elements...>& value, const char* signature)
|
||||
{
|
||||
constexpr auto elemSignature = as_null_terminated(sdbus::signature_of_v<_Element>);
|
||||
if (std::strcmp(signature, elemSignature.data()) != 0)
|
||||
return false;
|
||||
|
||||
_Element temp;
|
||||
msg.enterVariant(signature);
|
||||
msg >> temp;
|
||||
msg.exitVariant();
|
||||
value = std::move(temp);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Elements>
|
||||
inline Message& Message::operator>>(std::variant<Elements...>& value)
|
||||
{
|
||||
auto [type, contents] = peekType();
|
||||
bool result = (detail::deserialize_variant<Elements>(*this, value, contents) || ...);
|
||||
SDBUS_THROW_ERROR_IF(!result, "Failed to deserialize variant: signature did not match any of the variant types", EINVAL);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
|
||||
{
|
||||
deserializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Element, std::size_t _Size>
|
||||
inline Message& Message::operator>>(std::array<_Element, _Size>& items)
|
||||
{
|
||||
deserializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
inline Message& Message::operator>>(std::span<_Element, _Extent>& items)
|
||||
{
|
||||
deserializeArray(items);
|
||||
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename _Enum, typename>
|
||||
inline Message& Message::operator>>(_Enum& item)
|
||||
{
|
||||
std::underlying_type_t<_Enum> val;
|
||||
*this >> val;
|
||||
item = static_cast<_Enum>(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::deserializeArray(_Array& items)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
|
||||
// Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool,
|
||||
// otherwise use step-by-step deserialization of individual elements.
|
||||
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
|
||||
{
|
||||
deserializeArrayFast(items);
|
||||
}
|
||||
else
|
||||
{
|
||||
deserializeArraySlow(items);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::deserializeArrayFast(_Array& items)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
|
||||
size_t arraySize{};
|
||||
const ElementType* arrayPtr{};
|
||||
|
||||
constexpr auto signature = as_null_terminated(sdbus::signature_of_v<ElementType>);
|
||||
readArray(*signature.data(), (const void**)&arrayPtr, &arraySize);
|
||||
|
||||
size_t elementsInMsg = arraySize / sizeof(ElementType);
|
||||
bool notEnoughSpace = items.size() < elementsInMsg;
|
||||
SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL);
|
||||
|
||||
std::copy_n(arrayPtr, elementsInMsg, items.begin());
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
void Message::deserializeArrayFast(std::vector<_Element, _Allocator>& items)
|
||||
{
|
||||
size_t arraySize{};
|
||||
const _Element* arrayPtr{};
|
||||
|
||||
constexpr auto signature = as_null_terminated(sdbus::signature_of_v<_Element>);
|
||||
readArray(*signature.data(), (const void**)&arrayPtr, &arraySize);
|
||||
|
||||
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
|
||||
}
|
||||
|
||||
template <typename _Array>
|
||||
inline void Message::deserializeArraySlow(_Array& items)
|
||||
{
|
||||
using ElementType = typename _Array::value_type;
|
||||
|
||||
if(!enterContainer<ElementType>())
|
||||
return;
|
||||
|
||||
for (auto& elem : items)
|
||||
if (!(*this >> elem))
|
||||
break; // Keep the rest in the destination sequence untouched
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!isAtEnd(false), "Failed to deserialize array: not enough space in destination sequence", EINVAL);
|
||||
|
||||
clearFlags();
|
||||
|
||||
exitContainer();
|
||||
}
|
||||
|
||||
template <typename _Element, typename _Allocator>
|
||||
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
|
||||
{
|
||||
if(!enterContainer<_Element>())
|
||||
return;
|
||||
|
||||
while (true)
|
||||
{
|
||||
_Element elem;
|
||||
if (msg >> elem)
|
||||
if (*this >> elem)
|
||||
items.emplace_back(std::move(elem));
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
msg.clearFlags();
|
||||
clearFlags();
|
||||
|
||||
msg.exitContainer();
|
||||
|
||||
return msg;
|
||||
exitContainer();
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& operator>>(Message& msg, std::map<_Key, _Value>& items)
|
||||
inline Message& Message::operator>>(DictEntry<_Key, _Value>& value)
|
||||
{
|
||||
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
|
||||
const std::string arraySignature = "{" + dictEntrySignature + "}";
|
||||
if (!enterDictEntry<_Key, _Value>())
|
||||
return *this;
|
||||
*this >> value.first >> value.second;
|
||||
exitDictEntry();
|
||||
|
||||
if (!msg.enterContainer(arraySignature))
|
||||
return msg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items)
|
||||
{
|
||||
deserializeDictionary<_Key, _Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
|
||||
{
|
||||
deserializeDictionary<_Key, _Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value, typename _Callback>
|
||||
inline Message& Message::deserializeDictionary(const _Callback& callback)
|
||||
{
|
||||
if (!enterContainer<DictEntry<_Key, _Value>>())
|
||||
return *this;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!msg.enterDictEntry(dictEntrySignature))
|
||||
DictEntry<_Key, _Value> dictEntry;
|
||||
*this >> dictEntry;
|
||||
if (!*this)
|
||||
break;
|
||||
|
||||
_Key key;
|
||||
_Value value;
|
||||
msg >> key >> value;
|
||||
|
||||
items.emplace(std::move(key), std::move(value));
|
||||
|
||||
msg.exitDictEntry();
|
||||
callback(std::move(dictEntry));
|
||||
}
|
||||
clearFlags();
|
||||
|
||||
msg.clearFlags();
|
||||
exitContainer();
|
||||
|
||||
msg.exitContainer();
|
||||
|
||||
return msg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
@ -286,10 +690,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>
|
||||
@ -302,27 +703,79 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator>>(Message& msg, Struct<_ValueTypes...>& item)
|
||||
inline Message& Message::operator>>(Struct<_ValueTypes...>& item)
|
||||
{
|
||||
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
|
||||
// Remove opening and closing parenthesis from the struct signature to get contents signature
|
||||
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
|
||||
if (!enterStruct<_ValueTypes...>())
|
||||
return *this;
|
||||
|
||||
if (!msg.enterStruct(structContentSignature))
|
||||
return msg;
|
||||
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
|
||||
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
exitStruct();
|
||||
|
||||
msg.exitStruct();
|
||||
|
||||
return msg;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator>>(Message& msg, std::tuple<_ValueTypes...>& item)
|
||||
inline Message& Message::operator>>(std::tuple<_ValueTypes...>& item)
|
||||
{
|
||||
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
return msg;
|
||||
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _ElementType>
|
||||
inline Message& Message::openContainer()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>);
|
||||
return openContainer(signature.data());
|
||||
}
|
||||
|
||||
template <typename _KeyType, typename _ValueType>
|
||||
inline Message& Message::openDictEntry()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_KeyType, _ValueType>>);
|
||||
return openDictEntry(signature.data());
|
||||
}
|
||||
|
||||
template <typename _ValueType>
|
||||
inline Message& Message::openVariant()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>);
|
||||
return openVariant(signature.data());
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& Message::openStruct()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_ValueTypes...>>);
|
||||
return openStruct(signature.data());
|
||||
}
|
||||
|
||||
template <typename _ElementType>
|
||||
inline Message& Message::enterContainer()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>);
|
||||
return enterContainer(signature.data());
|
||||
}
|
||||
|
||||
template <typename _KeyType, typename _ValueType>
|
||||
inline Message& Message::enterDictEntry()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_KeyType, _ValueType>>);
|
||||
return enterDictEntry(signature.data());
|
||||
}
|
||||
|
||||
template <typename _ValueType>
|
||||
inline Message& Message::enterVariant()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>);
|
||||
return enterVariant(signature.data());
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& Message::enterStruct()
|
||||
{
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_ValueTypes...>>);
|
||||
return enterStruct(signature.data());
|
||||
}
|
||||
|
||||
}
|
||||
|
93
include/sdbus-c++/MethodResult.h
Normal file
93
include/sdbus-c++/MethodResult.h
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file MethodResult.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_METHODRESULT_H_
|
||||
#define SDBUS_CXX_METHODRESULT_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Error;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class Result
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Results>
|
||||
class Result
|
||||
{
|
||||
public:
|
||||
Result() = default;
|
||||
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(MethodCall call)
|
||||
: call_(std::move(call))
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::returnResults(const _Results&... results) const
|
||||
{
|
||||
assert(call_.isValid());
|
||||
auto reply = call_.createReply();
|
||||
(void)(reply << ... << results);
|
||||
reply.send();
|
||||
}
|
||||
|
||||
template <typename... _Results>
|
||||
inline void Result<_Results...>::returnError(const Error& error) const
|
||||
{
|
||||
auto reply = call_.createErrorReply(error);
|
||||
reply.send();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_METHODRESULT_H_ */
|
217
include/sdbus-c++/ProxyInterfaces.h
Normal file
217
include/sdbus-c++/ProxyInterfaces.h
Normal file
@ -0,0 +1,217 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 adaptor class inherited from ProxyInterfaces, one needs to make sure:
|
||||
* 1. to call `registerProxy();` in the class constructor, and, conversely,
|
||||
* 2. to call `unregisterProxy();` in the class destructor,
|
||||
* so that the signals are subscribed to and unsubscribed from at a proper time.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
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(ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Creates native-like proxy object instance
|
||||
*
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
*
|
||||
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
|
||||
* For more information on its behavior, consult @ref createProxy(std::string,std::string,sdbus::dont_run_event_loop_thread_t)
|
||||
*/
|
||||
ProxyInterfaces(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
|
||||
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
|
||||
, _Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @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, ServiceName destination, ObjectPath 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, ServiceName destination, ObjectPath objectPath)
|
||||
: ProxyObjectHolder(createProxy(std::move(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,sdbus::dont_run_event_loop_thread_t)
|
||||
*/
|
||||
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
|
||||
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
|
||||
, _Interfaces(getProxy())...
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Registers handlers for D-Bus signals of the remote object
|
||||
*
|
||||
* This function must be called in the constructor of the final proxy class that implements ProxyInterfaces.
|
||||
*
|
||||
* See also @ref IProxy::registerSignalHandler()
|
||||
*/
|
||||
void registerProxy()
|
||||
{
|
||||
(_Interfaces::registerProxy(), ...);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @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 reference to the underlying IProxy instance
|
||||
*/
|
||||
using ProxyObjectHolder::getProxy;
|
||||
|
||||
protected:
|
||||
using base_type = ProxyInterfaces;
|
||||
|
||||
ProxyInterfaces(const ProxyInterfaces&) = delete;
|
||||
ProxyInterfaces& operator=(const ProxyInterfaces&) = delete;
|
||||
ProxyInterfaces(ProxyInterfaces&&) = delete;
|
||||
ProxyInterfaces& operator=(ProxyInterfaces&&) = delete;
|
||||
~ProxyInterfaces() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERFACES_H_ */
|
530
include/sdbus-c++/StandardInterfaces.h
Normal file
530
include/sdbus-c++/StandardInterfaces.h
Normal file
@ -0,0 +1,530 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 <string_view>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// Proxy for peer
|
||||
class Peer_proxy
|
||||
{
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
|
||||
|
||||
protected:
|
||||
Peer_proxy(sdbus::IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
Peer_proxy(const Peer_proxy&) = delete;
|
||||
Peer_proxy& operator=(const Peer_proxy&) = delete;
|
||||
Peer_proxy(Peer_proxy&&) = delete;
|
||||
Peer_proxy& operator=(Peer_proxy&&) = delete;
|
||||
|
||||
~Peer_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void Ping()
|
||||
{
|
||||
m_proxy.callMethod("Ping").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
std::string GetMachineId()
|
||||
{
|
||||
std::string machineUUID;
|
||||
m_proxy.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
|
||||
return machineUUID;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// Proxy for introspection
|
||||
class Introspectable_proxy
|
||||
{
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
|
||||
|
||||
protected:
|
||||
Introspectable_proxy(sdbus::IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
Introspectable_proxy(const Introspectable_proxy&) = delete;
|
||||
Introspectable_proxy& operator=(const Introspectable_proxy&) = delete;
|
||||
Introspectable_proxy(Introspectable_proxy&&) = delete;
|
||||
Introspectable_proxy& operator=(Introspectable_proxy&&) = delete;
|
||||
|
||||
~Introspectable_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::string Introspect()
|
||||
{
|
||||
std::string xml;
|
||||
m_proxy.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
|
||||
return xml;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// Proxy for properties
|
||||
class Properties_proxy
|
||||
{
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_proxy(sdbus::IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
Properties_proxy(const Properties_proxy&) = delete;
|
||||
Properties_proxy& operator=(const Properties_proxy&) = delete;
|
||||
Properties_proxy(Properties_proxy&&) = delete;
|
||||
Properties_proxy& operator=(Properties_proxy&&) = delete;
|
||||
|
||||
~Properties_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
m_proxy
|
||||
.uponSignal("PropertiesChanged")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const InterfaceName& interfaceName
|
||||
, const std::map<PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<PropertyName>& invalidatedProperties )
|
||||
{
|
||||
this->onPropertiesChanged(interfaceName, changedProperties, invalidatedProperties);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void onPropertiesChanged( const InterfaceName& interfaceName
|
||||
, const std::map<PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<PropertyName>& invalidatedProperties ) = 0;
|
||||
|
||||
public:
|
||||
sdbus::Variant Get(const InterfaceName& interfaceName, const PropertyName& propertyName)
|
||||
{
|
||||
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
|
||||
}
|
||||
|
||||
sdbus::Variant Get(std::string_view interfaceName, std::string_view propertyName)
|
||||
{
|
||||
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, _Function&& callback)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<sdbus::Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<sdbus::Variant> GetAsync(std::string_view interfaceName, std::string_view propertyName, with_future_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
||||
}
|
||||
|
||||
void Set(std::string_view interfaceName, const std::string_view propertyName, const sdbus::Variant& value)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
|
||||
}
|
||||
|
||||
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, dont_expect_reply_t)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
|
||||
}
|
||||
|
||||
void Set(std::string_view interfaceName, const std::string_view propertyName, const sdbus::Variant& value, dont_expect_reply_t)
|
||||
{
|
||||
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
}
|
||||
|
||||
std::map<PropertyName, sdbus::Variant> GetAll(const InterfaceName& interfaceName)
|
||||
{
|
||||
return m_proxy.getAllProperties().onInterface(interfaceName);
|
||||
}
|
||||
|
||||
std::map<PropertyName, sdbus::Variant> GetAll(std::string_view interfaceName)
|
||||
{
|
||||
return m_proxy.getAllProperties().onInterface(interfaceName);
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, _Function&& callback)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot GetAllAsync(const InterfaceName& interfaceName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(const InterfaceName& interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot GetAllAsync(std::string_view interfaceName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(std::string_view interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// Proxy for object manager
|
||||
class ObjectManager_proxy
|
||||
{
|
||||
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
|
||||
protected:
|
||||
ObjectManager_proxy(sdbus::IProxy& proxy)
|
||||
: m_proxy(proxy)
|
||||
{
|
||||
}
|
||||
|
||||
ObjectManager_proxy(const ObjectManager_proxy&) = delete;
|
||||
ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete;
|
||||
ObjectManager_proxy(ObjectManager_proxy&&) = delete;
|
||||
ObjectManager_proxy& operator=(ObjectManager_proxy&&) = delete;
|
||||
|
||||
~ObjectManager_proxy() = default;
|
||||
|
||||
void registerProxy()
|
||||
{
|
||||
m_proxy
|
||||
.uponSignal("InterfacesAdded")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>& interfacesAndProperties )
|
||||
{
|
||||
this->onInterfacesAdded(objectPath, interfacesAndProperties);
|
||||
});
|
||||
|
||||
m_proxy
|
||||
.uponSignal("InterfacesRemoved")
|
||||
.onInterface(INTERFACE_NAME)
|
||||
.call([this]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces )
|
||||
{
|
||||
this->onInterfacesRemoved(objectPath, interfaces);
|
||||
});
|
||||
}
|
||||
|
||||
virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>& interfacesAndProperties) = 0;
|
||||
virtual void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces) = 0;
|
||||
|
||||
public:
|
||||
std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>> GetManagedObjects()
|
||||
{
|
||||
std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>> objectsInterfacesAndProperties;
|
||||
m_proxy.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
|
||||
return objectsInterfacesAndProperties;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetManagedObjectsAsync(_Function&& callback)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot GetManagedObjectsAsync(_Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>>> GetManagedObjectsAsync(with_future_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsFuture<std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>>>();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
// 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 inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_adaptor(sdbus::IObject& object) : m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
Properties_adaptor(const Properties_adaptor&) = delete;
|
||||
Properties_adaptor& operator=(const Properties_adaptor&) = delete;
|
||||
Properties_adaptor(Properties_adaptor&&) = delete;
|
||||
Properties_adaptor& operator=(Properties_adaptor&&) = delete;
|
||||
|
||||
~Properties_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& properties)
|
||||
{
|
||||
m_object.emitPropertiesChangedSignal(interfaceName, properties);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& properties)
|
||||
{
|
||||
m_object.emitPropertiesChangedSignal(interfaceName, properties);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName)
|
||||
{
|
||||
m_object.emitPropertiesChangedSignal(interfaceName);
|
||||
}
|
||||
|
||||
void emitPropertiesChangedSignal(const char* interfaceName)
|
||||
{
|
||||
m_object.emitPropertiesChangedSignal(interfaceName);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& m_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 inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
|
||||
|
||||
protected:
|
||||
explicit ObjectManager_adaptor(sdbus::IObject& object) : m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
|
||||
ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete;
|
||||
ObjectManager_adaptor(ObjectManager_adaptor&&) = delete;
|
||||
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = delete;
|
||||
|
||||
~ObjectManager_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
m_object.addObjectManager();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& m_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)
|
||||
: m_object(object)
|
||||
{
|
||||
}
|
||||
|
||||
ManagedObject_adaptor(const ManagedObject_adaptor&) = delete;
|
||||
ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete;
|
||||
ManagedObject_adaptor(ManagedObject_adaptor&&) = delete;
|
||||
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = delete;
|
||||
|
||||
~ManagedObject_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesAddedSignal().
|
||||
*/
|
||||
void emitInterfacesAddedSignal()
|
||||
{
|
||||
m_object.emitInterfacesAddedSignal();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesAddedSignal().
|
||||
*/
|
||||
void emitInterfacesAddedSignal(const std::vector<sdbus::InterfaceName>& interfaces)
|
||||
{
|
||||
m_object.emitInterfacesAddedSignal(interfaces);
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesRemoved signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesRemovedSignal().
|
||||
*/
|
||||
void emitInterfacesRemovedSignal()
|
||||
{
|
||||
m_object.emitInterfacesRemovedSignal();
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Emits InterfacesRemoved signal for this object path
|
||||
*
|
||||
* See IObject::emitInterfacesRemovedSignal().
|
||||
*/
|
||||
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
|
||||
{
|
||||
m_object.emitInterfacesRemovedSignal(interfaces);
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& m_object;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_STANDARDINTERFACES_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file TypeTraits.h
|
||||
*
|
||||
@ -26,12 +27,27 @@
|
||||
#ifndef SDBUS_CXX_TYPETRAITS_H_
|
||||
#define SDBUS_CXX_TYPETRAITS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <sdbus-c++/Error.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#ifdef __has_include
|
||||
# if __has_include(<span>)
|
||||
# include <span>
|
||||
# endif
|
||||
#endif
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@ -39,295 +55,371 @@ namespace sdbus {
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
class UnixFd;
|
||||
template<typename _T1, typename _T2> using DictEntry = std::pair<_T1, _T2>;
|
||||
class BusName;
|
||||
class InterfaceName;
|
||||
class MemberName;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
class Message;
|
||||
class PropertySetCall;
|
||||
class PropertyGetReply;
|
||||
template <typename... _Results> class Result;
|
||||
class Error;
|
||||
template <typename _T, typename _Enable = void> struct signature_of;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
using method_callback = std::function<void(Message& msg, Message& reply)>;
|
||||
using signal_handler = std::function<void(Message& signal)>;
|
||||
using property_set_callback = std::function<void(Message& msg)>;
|
||||
using property_get_callback = std::function<void(Message& reply)>;
|
||||
// Callbacks from sdbus-c++
|
||||
using method_callback = std::function<void(MethodCall msg)>;
|
||||
using async_reply_handler = std::function<void(MethodReply reply, std::optional<Error> error)>;
|
||||
using signal_handler = std::function<void(Signal signal)>;
|
||||
using message_handler = std::function<void(Message msg)>;
|
||||
using property_set_callback = std::function<void(PropertySetCall msg)>;
|
||||
using property_get_callback = std::function<void(PropertyGetReply& reply)>;
|
||||
|
||||
// Primary template
|
||||
// Type-erased RAII-style handle to callbacks/subscriptions registered to sdbus-c++
|
||||
using Slot = std::unique_ptr<void, std::function<void(void*)>>;
|
||||
|
||||
// Tag specifying that an owning handle (so-called slot) of the logical resource shall be provided to the client
|
||||
struct return_slot_t { explicit return_slot_t() = default; };
|
||||
inline constexpr return_slot_t return_slot{};
|
||||
// Tag specifying that the library shall own the slot resulting from the call of the function (so-called floating slot)
|
||||
struct floating_slot_t { explicit floating_slot_t() = default; };
|
||||
inline constexpr floating_slot_t floating_slot{};
|
||||
// Tag denoting the assumption that the caller has already obtained message ownership
|
||||
struct adopt_message_t { explicit adopt_message_t() = default; };
|
||||
inline constexpr adopt_message_t adopt_message{};
|
||||
// Tag denoting the assumption that the caller has already obtained fd ownership
|
||||
struct adopt_fd_t { explicit adopt_fd_t() = default; };
|
||||
inline constexpr adopt_fd_t adopt_fd{};
|
||||
// Tag specifying that the proxy shall not run an event loop thread on its D-Bus connection.
|
||||
// Such proxies are typically created to carry out a simple synchronous D-Bus call(s) and then are destroyed.
|
||||
struct dont_run_event_loop_thread_t { explicit dont_run_event_loop_thread_t() = default; };
|
||||
inline constexpr dont_run_event_loop_thread_t dont_run_event_loop_thread{};
|
||||
// Tag denoting an asynchronous call that returns std::future as a handle
|
||||
struct with_future_t { explicit with_future_t() = default; };
|
||||
inline constexpr with_future_t with_future{};
|
||||
// Tag denoting a call where the reply shouldn't be waited for
|
||||
struct dont_expect_reply_t { explicit dont_expect_reply_t() = default; };
|
||||
inline constexpr dont_expect_reply_t dont_expect_reply{};
|
||||
// Tag denoting that the variant shall embed the other variant as its value, instead of creating a copy
|
||||
struct embed_variant_t { explicit embed_variant_t() = default; };
|
||||
inline constexpr embed_variant_t embed_variant{};
|
||||
|
||||
// Helper for static assert
|
||||
template <class... _T> constexpr bool always_false = false;
|
||||
|
||||
// Helper operator+ for concatenation of `std::array`s
|
||||
template <typename _T, std::size_t _N1, std::size_t _N2>
|
||||
constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs);
|
||||
|
||||
// Template specializations for getting D-Bus signatures from C++ types
|
||||
template <typename _T>
|
||||
constexpr auto signature_of_v = signature_of<_T>::value;
|
||||
|
||||
template <typename _T, typename _Enable>
|
||||
struct signature_of
|
||||
{
|
||||
static constexpr bool is_valid = false;
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
|
||||
static const std::string str()
|
||||
static constexpr void* value = []
|
||||
{
|
||||
// sizeof(_T) < 0 is here to make compiler not being able to figure out
|
||||
// the assertion expression before the template instantiation takes place.
|
||||
static_assert(sizeof(_T) < 0, "Unknown DBus type");
|
||||
return "";
|
||||
}
|
||||
// See using-sdbus-c++.md, section "Extending sdbus-c++ type system",
|
||||
// on how to teach sdbus-c++ about your custom types
|
||||
static_assert(always_false<_T>, "Unsupported D-Bus type (specialize `signature_of` for your custom types)");
|
||||
};
|
||||
};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<const _T> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<volatile _T> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<const volatile _T> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<_T&> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<void>
|
||||
{
|
||||
static constexpr std::array<char, 0> value{};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<bool>
|
||||
{
|
||||
static constexpr std::array value{'b'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "b";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint8_t>
|
||||
{
|
||||
static constexpr std::array value{'y'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "y";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<int16_t>
|
||||
{
|
||||
static constexpr std::array value{'n'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "n";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint16_t>
|
||||
{
|
||||
static constexpr std::array value{'q'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "q";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<int32_t>
|
||||
{
|
||||
static constexpr std::array value{'i'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "i";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint32_t>
|
||||
{
|
||||
static constexpr std::array value{'u'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "u";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<int64_t>
|
||||
{
|
||||
static constexpr std::array value{'x'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "x";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint64_t>
|
||||
{
|
||||
static constexpr std::array value{'t'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "t";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<double>
|
||||
{
|
||||
static constexpr std::array value{'d'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "d";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<char*>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<const char*>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t _N>
|
||||
struct signature_of<char[_N]>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t _N>
|
||||
struct signature_of<const char[_N]>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = true;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<std::string>
|
||||
{
|
||||
static constexpr std::array value{'s'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<std::string_view> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<char*> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<const char*> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <std::size_t _N>
|
||||
struct signature_of<char[_N]> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <std::size_t _N>
|
||||
struct signature_of<const char[_N]> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<BusName> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<InterfaceName> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<MemberName> : signature_of<std::string>
|
||||
{};
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
struct signature_of<Struct<_ValueTypes...>>
|
||||
{
|
||||
static constexpr std::array contents = (signature_of_v<_ValueTypes> + ...);
|
||||
static constexpr std::array value = std::array{'('} + contents + std::array{')'};
|
||||
static constexpr char type_value{'r'}; /* Not actually used in signatures on D-Bus, see specs */
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static 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 += ")";
|
||||
return signature;
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<Variant>
|
||||
{
|
||||
static constexpr std::array value{'v'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "v";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <typename... Elements>
|
||||
struct signature_of<std::variant<Elements...>> : signature_of<Variant>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<ObjectPath>
|
||||
{
|
||||
static constexpr std::array value{'o'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "o";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<Signature>
|
||||
{
|
||||
static constexpr std::array value{'g'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "g";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <typename _Element>
|
||||
struct signature_of<std::vector<_Element>>
|
||||
template <>
|
||||
struct signature_of<UnixFd>
|
||||
{
|
||||
static constexpr std::array value{'h'};
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "a" + signature_of<_Element>::str();
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
struct signature_of<std::map<_Key, _Value>>
|
||||
template <typename _T1, typename _T2>
|
||||
struct signature_of<DictEntry<_T1, _T2>>
|
||||
{
|
||||
static constexpr std::array value = std::array{'{'} + signature_of_v<std::tuple<_T1, _T2>> + std::array{'}'};
|
||||
static constexpr char type_value{'e'}; /* Not actually used in signatures on D-Bus, see specs */
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "a{" + signature_of<_Key>::str() + signature_of<_Value>::str() + "}";
|
||||
}
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits
|
||||
: public function_traits<decltype(&_Type::operator())>
|
||||
template <typename _Element, typename _Allocator>
|
||||
struct signature_of<std::vector<_Element, _Allocator>>
|
||||
{
|
||||
static constexpr std::array value = std::array{'a'} + signature_of_v<_Element>;
|
||||
static constexpr bool is_valid = true;
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <typename _Element, std::size_t _Size>
|
||||
struct signature_of<std::array<_Element, _Size>> : signature_of<std::vector<_Element>>
|
||||
{
|
||||
};
|
||||
|
||||
#ifdef __cpp_lib_span
|
||||
template <typename _Element, std::size_t _Extent>
|
||||
struct signature_of<std::span<_Element, _Extent>> : signature_of<std::vector<_Element>>
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename _Enum> // is_const_v and is_volatile_v to avoid ambiguity conflicts with const and volatile specializations of signature_of
|
||||
struct signature_of<_Enum, typename std::enable_if_t<std::is_enum_v<_Enum> && !std::is_const_v<_Enum> && !std::is_volatile_v<_Enum>>>
|
||||
: signature_of<std::underlying_type_t<_Enum>>
|
||||
{};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits<const _Type>
|
||||
: public function_traits<_Type>
|
||||
{};
|
||||
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
|
||||
struct signature_of<std::map<_Key, _Value, _Compare, _Allocator>>
|
||||
{
|
||||
static constexpr std::array value = std::array{'a'} + signature_of_v<DictEntry<_Key, _Value>>;
|
||||
static constexpr bool is_valid = true;
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits<_Type&>
|
||||
: public function_traits<_Type>
|
||||
{};
|
||||
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
|
||||
struct signature_of<std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>>
|
||||
: signature_of<std::map<_Key, _Value>>
|
||||
{
|
||||
};
|
||||
|
||||
template <typename... _Types>
|
||||
struct signature_of<std::tuple<_Types...>> // A simple concatenation of signatures of _Types
|
||||
{
|
||||
static constexpr std::array value = (std::array<char, 0>{} + ... + signature_of_v<_Types>);
|
||||
static constexpr bool is_valid = false;
|
||||
static constexpr bool is_trivial_dbus_type = false;
|
||||
};
|
||||
|
||||
// To simplify conversions of arrays to C strings
|
||||
template <typename _T, std::size_t _N>
|
||||
constexpr auto as_null_terminated(std::array<_T, _N> arr)
|
||||
{
|
||||
return arr + std::array<_T, 1>{0};
|
||||
}
|
||||
|
||||
// Function traits implementation inspired by (c) kennytm,
|
||||
// https://github.com/kennytm/utils/blob/master/traits.hpp
|
||||
template <typename _Type>
|
||||
struct function_traits : function_traits<decltype(&_Type::operator())>
|
||||
{};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits<const _Type> : function_traits<_Type>
|
||||
{};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits<_Type&> : function_traits<_Type>
|
||||
{};
|
||||
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_Args...)>
|
||||
struct function_traits_base
|
||||
{
|
||||
typedef _ReturnType result_type;
|
||||
typedef std::tuple<_Args...> arguments_type;
|
||||
typedef std::tuple<std::decay_t<_Args>...> decayed_arguments_type;
|
||||
|
||||
typedef _ReturnType function_type(_Args...);
|
||||
|
||||
static constexpr std::size_t arity = sizeof...(_Args);
|
||||
|
||||
// template <size_t _Idx, typename _Enabled = void>
|
||||
// struct arg;
|
||||
//
|
||||
// template <size_t _Idx>
|
||||
// struct arg<_Idx, std::enable_if_t<(_Idx < arity)>>
|
||||
// {
|
||||
// typedef std::tuple_element_t<_Idx, arguments_type> type;
|
||||
// };
|
||||
//
|
||||
// template <size_t _Idx>
|
||||
// struct arg<_Idx, std::enable_if_t<!(_Idx < arity)>>
|
||||
// {
|
||||
// typedef void type;
|
||||
// };
|
||||
|
||||
template <size_t _Idx>
|
||||
struct arg
|
||||
{
|
||||
@ -339,134 +431,214 @@ namespace sdbus {
|
||||
};
|
||||
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(*)(_Args...)>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
struct function_traits<_ReturnType(_Args...)> : 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(std::optional<Error>, _Args...)> : function_traits_base<void, _Args...>
|
||||
{
|
||||
static constexpr bool has_error_param = true;
|
||||
};
|
||||
|
||||
template <typename... _Args, typename... _Results>
|
||||
struct function_traits<void(Result<_Results...>, _Args...)> : 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...)> : function_traits_base<std::tuple<_Results...>, _Args...>
|
||||
{
|
||||
static constexpr bool is_async = true;
|
||||
using async_result_t = Result<_Results...>;
|
||||
};
|
||||
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(*)(_Args...)> : function_traits<_ReturnType(_Args...)>
|
||||
{};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...)>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...)> : function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const> : function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef const _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile> : function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef volatile _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile> : function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef const volatile _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename FunctionType>
|
||||
struct function_traits<std::function<FunctionType>>
|
||||
: public function_traits<FunctionType>
|
||||
struct function_traits<std::function<FunctionType>> : function_traits<FunctionType>
|
||||
{};
|
||||
|
||||
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;
|
||||
|
||||
template <typename _FunctionType, size_t _Idx>
|
||||
using function_argument_t = typename function_traits<_FunctionType>::template arg_t<_Idx>;
|
||||
|
||||
template <typename _FunctionType>
|
||||
constexpr auto function_argument_count_v = function_traits<_FunctionType>::arity;
|
||||
|
||||
template <typename _FunctionType>
|
||||
using function_result_t = typename function_traits<_FunctionType>::result_type;
|
||||
|
||||
template <typename _Type>
|
||||
struct aggregate_signature
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return signature_of<std::decay_t<_Type>>::str();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... _Types>
|
||||
struct aggregate_signature<std::tuple<_Types...>>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
std::initializer_list<std::string> signatures{signature_of<std::decay_t<_Types>>::str()...};
|
||||
std::string signature;
|
||||
for (const auto& item : signatures)
|
||||
signature += item;
|
||||
return signature;
|
||||
}
|
||||
};
|
||||
|
||||
// Get a tuple of function input argument types from function signature.
|
||||
// But first, convert provided function signature to the standardized form `out(in...)'.
|
||||
template <typename _Function>
|
||||
struct tuple_of_function_input_arg_types
|
||||
: public tuple_of_function_input_arg_types<typename function_traits<_Function>::function_type>
|
||||
{};
|
||||
|
||||
// Get a tuple of function input argument types from function signature.
|
||||
// Function signature is expected in the standardized form `out(in...)'.
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
struct tuple_of_function_input_arg_types<_ReturnType(_Args...)>
|
||||
{
|
||||
// Arguments may be cv-qualified and may be references, so we have to strip cv and references
|
||||
// with decay_t in order to get real 'naked' types.
|
||||
// Example: for a function with signature void(const int i, const std::vector<float> v, double d)
|
||||
// the `type' will be `std::tuple<int, std::vector<float>, double>'.
|
||||
typedef std::tuple<std::decay_t<_Args>...> type;
|
||||
typedef typename function_traits<_Function>::decayed_arguments_type type;
|
||||
};
|
||||
|
||||
template <typename _Function>
|
||||
using tuple_of_function_input_arg_types_t = typename tuple_of_function_input_arg_types<_Function>::type;
|
||||
|
||||
template <typename _Function>
|
||||
struct signature_of_function_input_arguments
|
||||
struct tuple_of_function_output_arg_types
|
||||
{
|
||||
static const std::string str()
|
||||
typedef typename function_traits<_Function>::result_type type;
|
||||
};
|
||||
|
||||
template <typename _Function>
|
||||
using tuple_of_function_output_arg_types_t = typename tuple_of_function_output_arg_types<_Function>::type;
|
||||
|
||||
template <typename _Function>
|
||||
struct signature_of_function_input_arguments : signature_of<tuple_of_function_input_arg_types_t<_Function>>
|
||||
{
|
||||
static std::string value_as_string()
|
||||
{
|
||||
return aggregate_signature<tuple_of_function_input_arg_types_t<_Function>>::str();
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_input_arg_types_t<_Function>>);
|
||||
return signature.data();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Function>
|
||||
struct signature_of_function_output_arguments
|
||||
inline auto signature_of_function_input_arguments_v = signature_of_function_input_arguments<_Function>::value_as_string();
|
||||
|
||||
template <typename _Function>
|
||||
struct signature_of_function_output_arguments : signature_of<tuple_of_function_output_arg_types_t<_Function>>
|
||||
{
|
||||
static const std::string str()
|
||||
static std::string value_as_string()
|
||||
{
|
||||
return aggregate_signature<function_result_t<_Function>>::str();
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_output_arg_types_t<_Function>>);
|
||||
return signature.data();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Function>
|
||||
inline auto signature_of_function_output_arguments_v = signature_of_function_output_arguments<_Function>::value_as_string();
|
||||
|
||||
// std::future stuff for return values of async calls
|
||||
template <typename... _Args> struct future_return
|
||||
{
|
||||
typedef std::tuple<_Args...> type;
|
||||
};
|
||||
|
||||
template <> struct future_return<>
|
||||
{
|
||||
typedef void type;
|
||||
};
|
||||
|
||||
template <typename _Type> struct future_return<_Type>
|
||||
{
|
||||
typedef _Type type;
|
||||
};
|
||||
|
||||
template <typename... _Args>
|
||||
using future_return_t = typename future_return<_Args...>::type;
|
||||
|
||||
// Credit: Piotr Skotnicki (https://stackoverflow.com/a/57639506)
|
||||
template <typename, typename>
|
||||
constexpr bool is_one_of_variants_types = false;
|
||||
|
||||
template <typename... _VariantTypes, typename _QueriedType>
|
||||
constexpr bool is_one_of_variants_types<std::variant<_VariantTypes...>, _QueriedType>
|
||||
= (std::is_same_v<_QueriedType, _VariantTypes> || ...);
|
||||
|
||||
// Wrapper (tag) denoting we want to serialize user-defined struct
|
||||
// into a D-Bus message as a dictionary of strings to variants.
|
||||
template <typename _Struct>
|
||||
struct as_dictionary
|
||||
{
|
||||
explicit as_dictionary(const _Struct& s) : m_struct(s) {}
|
||||
const _Struct& m_struct;
|
||||
};
|
||||
|
||||
template <typename _Type>
|
||||
const _Type& as_dictionary_if_struct(const _Type& object)
|
||||
{
|
||||
return object; // identity in case _Type is not struct (user-defined structs shall provide an overload)
|
||||
}
|
||||
|
||||
// By default, the dict-as-struct deserialization strategy is strict.
|
||||
// Strict means that every key of the deserialized dictionary must have its counterpart member in the struct, otherwise an exception is thrown.
|
||||
// Relaxed means that a key that does not have a matching struct member is silently ignored.
|
||||
// The behavior can be overridden for user-defined struct by specializing this variable template.
|
||||
template <typename _Struct>
|
||||
constexpr auto strict_dict_as_struct_deserialization_v = true;
|
||||
|
||||
// By default, the struct-as-dict serialization strategy is single-level only (as opposed to nested).
|
||||
// Single-level means that only the specific struct is serialized as a dictionary, serializing members that are structs always as structs.
|
||||
// Nested means that the struct *and* its members that are structs are all serialized as a dictionary. If nested strategy is also
|
||||
// defined for the nested struct, then the same behavior applies for that struct, recursively.
|
||||
// The behavior can be overridden for user-defined struct by specializing this variable template.
|
||||
template <typename _Struct>
|
||||
constexpr auto nested_struct_as_dict_serialization_v = false;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// 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>
|
||||
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, Result<_Args...>&& r
|
||||
, _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)(std::move(r), std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// Version of apply_impl for functions returning void.
|
||||
// In this case, to have uniform code on the caller side, return empty tuple, our synonym for `void'.
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
decltype(auto) apply_impl( _Function&& f
|
||||
, std::optional<Error> e
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...> )
|
||||
{
|
||||
return std::forward<_Function>(f)(std::move(e), std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// 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<>{};
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,6 +652,47 @@ namespace sdbus {
|
||||
, 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, 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>
|
||||
decltype(auto) apply(_Function&& f, std::optional<Error> e, _Tuple&& t)
|
||||
{
|
||||
return detail::apply_impl( std::forward<_Function>(f)
|
||||
, std::move(e)
|
||||
, std::forward<_Tuple>(t)
|
||||
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
||||
}
|
||||
|
||||
// Convenient concatenation of arrays
|
||||
template <typename _T, std::size_t _N1, std::size_t _N2>
|
||||
constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs)
|
||||
{
|
||||
std::array<_T, _N1 + _N2> result{};
|
||||
std::size_t index = 0;
|
||||
|
||||
for (auto& el : lhs) {
|
||||
result[index] = std::move(el);
|
||||
++index;
|
||||
}
|
||||
for (auto& el : rhs) {
|
||||
result[index] = std::move(el);
|
||||
++index;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_TYPETRAITS_H_ */
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Types.h
|
||||
*
|
||||
@ -28,11 +29,15 @@
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
@ -41,6 +46,12 @@ namespace sdbus {
|
||||
*
|
||||
* 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
|
||||
{
|
||||
@ -48,21 +59,46 @@ namespace sdbus {
|
||||
Variant();
|
||||
|
||||
template <typename _ValueType>
|
||||
Variant(const _ValueType& value)
|
||||
: Variant()
|
||||
explicit Variant(const _ValueType& value) : Variant()
|
||||
{
|
||||
msg_.openVariant(signature_of<_ValueType>::str());
|
||||
msg_.openVariant<_ValueType>();
|
||||
msg_ << value;
|
||||
msg_.closeVariant();
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
Variant(const Variant& value, embed_variant_t) : Variant()
|
||||
{
|
||||
msg_.openVariant<Variant>();
|
||||
msg_ << value;
|
||||
msg_.closeVariant();
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename _Struct>
|
||||
explicit Variant(const as_dictionary<_Struct>& value) : Variant()
|
||||
{
|
||||
msg_.openVariant<std::map<std::string, Variant>>();
|
||||
msg_ << as_dictionary(value.m_struct);
|
||||
msg_.closeVariant();
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename... _Elements>
|
||||
Variant(const std::variant<_Elements...>& value)
|
||||
: Variant()
|
||||
{
|
||||
msg_ << value;
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename _ValueType>
|
||||
_ValueType get() const
|
||||
{
|
||||
_ValueType val;
|
||||
msg_.rewind(false);
|
||||
msg_.enterVariant(signature_of<_ValueType>::str());
|
||||
|
||||
msg_.enterVariant<_ValueType>();
|
||||
_ValueType val;
|
||||
msg_ >> val;
|
||||
msg_.exitVariant();
|
||||
return val;
|
||||
@ -70,27 +106,47 @@ namespace sdbus {
|
||||
|
||||
// Only allow conversion operator for true D-Bus type representations in C++
|
||||
template <typename _ValueType, typename = std::enable_if_t<signature_of<_ValueType>::is_valid>>
|
||||
operator _ValueType() const
|
||||
explicit operator _ValueType() const
|
||||
{
|
||||
return get<_ValueType>();
|
||||
}
|
||||
|
||||
template <typename... _Elements>
|
||||
operator std::variant<_Elements...>() const
|
||||
{
|
||||
std::variant<_Elements...> result;
|
||||
msg_.rewind(false);
|
||||
msg_ >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename _Type>
|
||||
bool containsValueOfType() const
|
||||
{
|
||||
return signature_of<_Type>::str() == peekValueType();
|
||||
constexpr auto signature = as_null_terminated(signature_of_v<_Type>);
|
||||
return std::strcmp(signature.data(), peekValueType()) == 0;
|
||||
}
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
void serializeTo(Message& msg) const;
|
||||
void deserializeFrom(Message& msg);
|
||||
std::string peekValueType() const;
|
||||
const char* peekValueType() const;
|
||||
|
||||
private:
|
||||
mutable Message msg_{};
|
||||
mutable PlainMessage msg_{};
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Struct
|
||||
*
|
||||
* Representation of struct D-Bus type
|
||||
*
|
||||
* Struct implements tuple protocol, i.e. it's a tuple-like class.
|
||||
* It can be used with std::get<>(), std::tuple_element,
|
||||
* std::tuple_size and in structured bindings.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _ValueTypes>
|
||||
class Struct
|
||||
: public std::tuple<_ValueTypes...>
|
||||
@ -98,15 +154,12 @@ namespace sdbus {
|
||||
public:
|
||||
using std::tuple<_ValueTypes...>::tuple;
|
||||
|
||||
// Disable constructor if an older then 7.1.0 version of GCC is used
|
||||
#if !((defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !(__GNUC__ > 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 0)))))
|
||||
Struct() = default;
|
||||
|
||||
explicit Struct(const std::tuple<_ValueTypes...>& t)
|
||||
: std::tuple<_ValueTypes...>(t)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template <std::size_t _I>
|
||||
auto& get()
|
||||
@ -121,6 +174,9 @@ namespace sdbus {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... _Elements>
|
||||
Struct(_Elements...) -> Struct<_Elements...>;
|
||||
|
||||
template<typename... _Elements>
|
||||
constexpr Struct<std::decay_t<_Elements>...>
|
||||
make_struct(_Elements&&... args)
|
||||
@ -129,20 +185,413 @@ namespace sdbus {
|
||||
return result_type(std::forward<_Elements>(args)...);
|
||||
}
|
||||
|
||||
/********************************************//**
|
||||
* @class ObjectPath
|
||||
*
|
||||
* Strong type representing the D-Bus object path
|
||||
*
|
||||
***********************************************/
|
||||
class ObjectPath : public std::string
|
||||
{
|
||||
public:
|
||||
using std::string::string;
|
||||
ObjectPath() = default;
|
||||
explicit ObjectPath(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit ObjectPath(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class BusName
|
||||
*
|
||||
* Strong type representing the D-Bus bus/service/connection name
|
||||
*
|
||||
***********************************************/
|
||||
class BusName : public std::string
|
||||
{
|
||||
public:
|
||||
BusName() = default;
|
||||
explicit BusName(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit BusName(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
using ServiceName = BusName;
|
||||
using ConnectionName = BusName;
|
||||
|
||||
/********************************************//**
|
||||
* @class InterfaceName
|
||||
*
|
||||
* Strong type representing the D-Bus interface name
|
||||
*
|
||||
***********************************************/
|
||||
class InterfaceName : public std::string
|
||||
{
|
||||
public:
|
||||
InterfaceName() = default;
|
||||
explicit InterfaceName(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit InterfaceName(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class MemberName
|
||||
*
|
||||
* Strong type representing the D-Bus member name
|
||||
*
|
||||
***********************************************/
|
||||
class MemberName : public std::string
|
||||
{
|
||||
public:
|
||||
MemberName() = default;
|
||||
explicit MemberName(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit MemberName(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
using MethodName = MemberName;
|
||||
using SignalName = MemberName;
|
||||
using PropertyName = MemberName;
|
||||
|
||||
/********************************************//**
|
||||
* @class Signature
|
||||
*
|
||||
* Strong type representing the D-Bus object path
|
||||
*
|
||||
***********************************************/
|
||||
class Signature : public std::string
|
||||
{
|
||||
public:
|
||||
using std::string::string;
|
||||
Signature() = default;
|
||||
explicit Signature(std::string value)
|
||||
: std::string(std::move(value))
|
||||
{}
|
||||
explicit Signature(const char* value)
|
||||
: std::string(value)
|
||||
{}
|
||||
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @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_(checkedDup(fd))
|
||||
{
|
||||
}
|
||||
|
||||
UnixFd(int fd, adopt_fd_t)
|
||||
: fd_(fd)
|
||||
{
|
||||
}
|
||||
|
||||
UnixFd(const UnixFd& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
UnixFd& operator=(const UnixFd& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
close();
|
||||
fd_ = checkedDup(other.fd_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
UnixFd(UnixFd&& other)
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
UnixFd& operator=(UnixFd&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
close();
|
||||
fd_ = std::exchange(other.fd_, -1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~UnixFd()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
[[nodiscard]] 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()
|
||||
{
|
||||
return std::exchange(fd_, -1);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isValid() const
|
||||
{
|
||||
return fd_ >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Closes file descriptor, but does not set it to -1.
|
||||
void close();
|
||||
|
||||
/// Returns negative argument unchanged.
|
||||
/// Otherwise, call ::dup and throw on failure.
|
||||
static int checkedDup(int fd);
|
||||
|
||||
int fd_ = -1;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @typedef DictEntry
|
||||
*
|
||||
* DictEntry is implemented as std::pair, a standard
|
||||
* value_type in STL(-like) associative containers.
|
||||
*
|
||||
***********************************************/
|
||||
template<typename _T1, typename _T2>
|
||||
using DictEntry = std::pair<_T1, _T2>;
|
||||
|
||||
}
|
||||
|
||||
// Making sdbus::Struct implement the tuple-protocol, i.e. be a tuple-like type
|
||||
template <size_t _I, typename... _ValueTypes>
|
||||
struct std::tuple_element<_I, sdbus::Struct<_ValueTypes...>>
|
||||
: std::tuple_element<_I, std::tuple<_ValueTypes...>>
|
||||
{};
|
||||
template <typename... _ValueTypes>
|
||||
struct std::tuple_size<sdbus::Struct<_ValueTypes...>>
|
||||
: std::tuple_size<std::tuple<_ValueTypes...>>
|
||||
{};
|
||||
|
||||
/********************************************//**
|
||||
* @name SDBUSCPP_REGISTER_STRUCT
|
||||
*
|
||||
* A convenient way to extend sdbus-c++ type system with user-defined structs.
|
||||
*
|
||||
* The macro teaches sdbus-c++ to recognize the user-defined struct
|
||||
* as a valid C++ representation of a D-Bus Struct type, and enables
|
||||
* clients to use their struct conveniently instead of the (too
|
||||
* generic and less expressive) `sdbus::Struct<...>` in sdbus-c++ API.
|
||||
*
|
||||
* It also enables to serialize a user-defined struct as an a{sv} dictionary,
|
||||
* and to deserialize an a{sv} dictionary into the user-defined struct.
|
||||
*
|
||||
* The first argument is the struct type name and the remaining arguments
|
||||
* are names of struct members. Members must be of types supported by
|
||||
* sdbus-c++ (or of user-defined types that sdbus-c++ was taught to support).
|
||||
* Members can be other structs (nesting is supported).
|
||||
* The macro must be placed in the global namespace.
|
||||
*
|
||||
* For example, given the user-defined struct `ABC`:
|
||||
*
|
||||
* namespace foo {
|
||||
* struct ABC
|
||||
* {
|
||||
* int number;
|
||||
* std::string name;
|
||||
* std::vector<double> data;
|
||||
* };
|
||||
* }
|
||||
*
|
||||
* one can teach sdbus-c++ about the contents of this struct simply with:
|
||||
*
|
||||
* SDBUSCPP_REGISTER_STRUCT(foo::ABC, number, name, data);
|
||||
*
|
||||
* Up to 16 struct members are supported by the macro.
|
||||
*
|
||||
***********************************************/
|
||||
#define SDBUSCPP_REGISTER_STRUCT(STRUCT, ...) \
|
||||
namespace sdbus { \
|
||||
static_assert(SDBUSCPP_PP_NARG(__VA_ARGS__) <= 16, \
|
||||
"Not more than 16 struct members are supported, please open an issue if you need more"); \
|
||||
\
|
||||
template <> \
|
||||
struct signature_of<STRUCT> \
|
||||
: signature_of<sdbus::Struct<SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, __VA_ARGS__)>> \
|
||||
{}; \
|
||||
\
|
||||
inline auto as_dictionary_if_struct(const STRUCT& object) \
|
||||
{ \
|
||||
return as_dictionary<STRUCT>(object); \
|
||||
} \
|
||||
\
|
||||
inline sdbus::Message& operator<<(sdbus::Message& msg, const STRUCT& items) \
|
||||
{ \
|
||||
return msg << sdbus::Struct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(items, __VA_ARGS__))}; \
|
||||
} \
|
||||
\
|
||||
inline Message& operator<<(Message& msg, const as_dictionary<STRUCT>& s) \
|
||||
{ \
|
||||
if constexpr (!nested_struct_as_dict_serialization_v<STRUCT>) \
|
||||
return msg.serializeDictionary<std::string, Variant>({SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \
|
||||
else \
|
||||
return msg.serializeDictionary<std::string, Variant>({SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \
|
||||
} \
|
||||
\
|
||||
inline Message& operator>>(Message& msg, STRUCT& s) \
|
||||
{ \
|
||||
/* First, try to deserialize as a struct */ \
|
||||
if (msg.peekType().first == signature_of<STRUCT>::type_value) \
|
||||
{ \
|
||||
Struct sdbusStruct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(s, __VA_ARGS__))}; \
|
||||
return msg >> sdbusStruct; \
|
||||
} \
|
||||
\
|
||||
/* Otherwise try to deserialize as a dictionary of strings to variants */ \
|
||||
\
|
||||
return msg.deserializeDictionary<std::string, Variant>([&s](const auto& dictEntry) \
|
||||
{ \
|
||||
const std::string& key = dictEntry.first; /* Intentionally not using structured bindings */ \
|
||||
const Variant& value = dictEntry.second; \
|
||||
\
|
||||
using namespace std::string_literals; \
|
||||
/* This also handles members which are structs serialized as dict of strings to variants, recursively */ \
|
||||
SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(s, __VA_ARGS__) \
|
||||
SDBUS_THROW_ERROR_IF( strict_dict_as_struct_deserialization_v<STRUCT> \
|
||||
, ("Failed to deserialize struct from a dictionary: could not find field '"s += key) += "' in struct 'my::Struct'" \
|
||||
, EINVAL ); \
|
||||
}); \
|
||||
} \
|
||||
} \
|
||||
/**/
|
||||
|
||||
/********************************************//**
|
||||
* @name SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION
|
||||
*
|
||||
* Enables relaxed deserialization of an a{sv} dictionary into a user-defined struct STRUCT.
|
||||
*
|
||||
* The default (strict) deserialization mode is that if there are entries in the dictionary
|
||||
* which do not have a corresponding field in the struct, an exception is thrown.
|
||||
* In the relaxed mode, such entries are silently skipped.
|
||||
*
|
||||
* The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro,
|
||||
* and must be placed before SDBUSCPP_REGISTER_STRUCT macro.
|
||||
***********************************************/
|
||||
#define SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(STRUCT) \
|
||||
template <> \
|
||||
constexpr auto sdbus::strict_dict_as_struct_deserialization_v<STRUCT> = false; \
|
||||
/**/
|
||||
|
||||
/********************************************//**
|
||||
* @name SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION
|
||||
*
|
||||
* Enables nested serialization of user-defined struct STRUCT as an a{sv} dictionary.
|
||||
*
|
||||
* By default, STRUCT fields which are structs themselves are serialized as D-Bus structs.
|
||||
* This macro tells sdbus-c++ to also serialize nested structs, in a recursive fashion,
|
||||
* as a{sv} dictionaries.
|
||||
*
|
||||
* The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro,
|
||||
* and must be placed before SDBUSCPP_REGISTER_STRUCT macro.
|
||||
***********************************************/
|
||||
#define SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(STRUCT) \
|
||||
template <> \
|
||||
constexpr auto sdbus::nested_struct_as_dict_serialization_v<STRUCT> = true \
|
||||
/**/
|
||||
|
||||
/*!
|
||||
* @cond SDBUSCPP_INTERNAL
|
||||
*
|
||||
* Internal helper preprocessor facilities
|
||||
*/
|
||||
#define SDBUSCPP_STRUCT_MEMBERS(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER(STRUCT, MEMBER) STRUCT.MEMBER
|
||||
|
||||
#define SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_TYPE, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER_TYPE(STRUCT, MEMBER) decltype(STRUCT::MEMBER)
|
||||
|
||||
#define SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{STRUCT.MEMBER}}
|
||||
|
||||
#define SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{as_dictionary_if_struct(STRUCT.MEMBER)}}
|
||||
|
||||
#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(STRUCT, ...) \
|
||||
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER, SDBUSCPP_PP_SPACE, STRUCT, __VA_ARGS__) \
|
||||
/**/
|
||||
#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER(STRUCT, MEMBER) if (key == #MEMBER) STRUCT.MEMBER = value.get<decltype(STRUCT.MEMBER)>(); else
|
||||
|
||||
#define SDBUSCPP_FOR_EACH_1(M, D, S, M1) M(S, M1)
|
||||
#define SDBUSCPP_FOR_EACH_2(M, D, S, M1, M2) M(S, M1) D M(S, M2)
|
||||
#define SDBUSCPP_FOR_EACH_3(M, D, S, M1, M2, M3) M(S, M1) D M(S, M2) D M(S, M3)
|
||||
#define SDBUSCPP_FOR_EACH_4(M, D, S, M1, M2, M3, M4) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4)
|
||||
#define SDBUSCPP_FOR_EACH_5(M, D, S, M1, M2, M3, M4, M5) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5)
|
||||
#define SDBUSCPP_FOR_EACH_6(M, D, S, M1, M2, M3, M4, M5, M6) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6)
|
||||
#define SDBUSCPP_FOR_EACH_7(M, D, S, M1, M2, M3, M4, M5, M6, M7) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7)
|
||||
#define SDBUSCPP_FOR_EACH_8(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8)
|
||||
#define SDBUSCPP_FOR_EACH_9(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9)
|
||||
#define SDBUSCPP_FOR_EACH_10(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10)
|
||||
#define SDBUSCPP_FOR_EACH_11(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11)
|
||||
#define SDBUSCPP_FOR_EACH_12(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12)
|
||||
#define SDBUSCPP_FOR_EACH_13(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13)
|
||||
#define SDBUSCPP_FOR_EACH_14(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14)
|
||||
#define SDBUSCPP_FOR_EACH_15(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15)
|
||||
#define SDBUSCPP_FOR_EACH_16(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15, M16) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15) D M(S, M16)
|
||||
|
||||
#define SDBUSCPP_PP_CAT(X, Y) SDBUSCPP_PP_CAT_IMPL(X, Y)
|
||||
#define SDBUSCPP_PP_CAT_IMPL(X, Y) X##Y
|
||||
#define SDBUSCPP_PP_NARG(...) SDBUSCPP_PP_NARG_IMPL(__VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#define SDBUSCPP_PP_NARG_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _N, ...) _N
|
||||
|
||||
#define SDBUSCPP_PP_COMMA ,
|
||||
#define SDBUSCPP_PP_SPACE
|
||||
|
||||
#endif /* SDBUS_CXX_TYPES_H_ */
|
||||
|
112
include/sdbus-c++/VTableItems.h
Normal file
112
include/sdbus-c++/VTableItems.h
Normal file
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableItems.h
|
||||
*
|
||||
* Created on: Dec 14, 2023
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CXX_VTABLEITEMS_H_
|
||||
#define SDBUS_CXX_VTABLEITEMS_H_
|
||||
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
struct MethodVTableItem
|
||||
{
|
||||
template <typename _Function> MethodVTableItem& implementedAs(_Function&& callback);
|
||||
MethodVTableItem& withInputParamNames(std::vector<std::string> names);
|
||||
template <typename... _String> MethodVTableItem& withInputParamNames(_String... names);
|
||||
MethodVTableItem& withOutputParamNames(std::vector<std::string> names);
|
||||
template <typename... _String> MethodVTableItem& withOutputParamNames(_String... names);
|
||||
MethodVTableItem& markAsDeprecated();
|
||||
MethodVTableItem& markAsPrivileged();
|
||||
MethodVTableItem& withNoReply();
|
||||
|
||||
MethodName name;
|
||||
Signature inputSignature;
|
||||
std::vector<std::string> inputParamNames;
|
||||
Signature outputSignature;
|
||||
std::vector<std::string> outputParamNames;
|
||||
method_callback callbackHandler;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
MethodVTableItem registerMethod(MethodName methodName);
|
||||
MethodVTableItem registerMethod(std::string methodName);
|
||||
|
||||
struct SignalVTableItem
|
||||
{
|
||||
template <typename... _Args> SignalVTableItem& withParameters();
|
||||
template <typename... _Args> SignalVTableItem& withParameters(std::vector<std::string> names);
|
||||
template <typename... _Args, typename... _String> SignalVTableItem& withParameters(_String... names);
|
||||
SignalVTableItem& markAsDeprecated();
|
||||
|
||||
SignalName name;
|
||||
Signature signature;
|
||||
std::vector<std::string> paramNames;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
SignalVTableItem registerSignal(SignalName signalName);
|
||||
SignalVTableItem registerSignal(std::string signalName);
|
||||
|
||||
struct PropertyVTableItem
|
||||
{
|
||||
template <typename _Function> PropertyVTableItem& withGetter(_Function&& callback);
|
||||
template <typename _Function> PropertyVTableItem& withSetter(_Function&& callback);
|
||||
PropertyVTableItem& markAsDeprecated();
|
||||
PropertyVTableItem& markAsPrivileged();
|
||||
PropertyVTableItem& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
|
||||
|
||||
PropertyName name;
|
||||
Signature signature;
|
||||
property_get_callback getter;
|
||||
property_set_callback setter;
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
PropertyVTableItem registerProperty(PropertyName propertyName);
|
||||
PropertyVTableItem registerProperty(std::string propertyName);
|
||||
|
||||
struct InterfaceFlagsVTableItem
|
||||
{
|
||||
InterfaceFlagsVTableItem& markAsDeprecated();
|
||||
InterfaceFlagsVTableItem& markAsPrivileged();
|
||||
InterfaceFlagsVTableItem& withNoReplyMethods();
|
||||
InterfaceFlagsVTableItem& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
|
||||
|
||||
Flags flags;
|
||||
};
|
||||
|
||||
InterfaceFlagsVTableItem setInterfaceFlags();
|
||||
|
||||
using VTableItem = std::variant<MethodVTableItem, SignalVTableItem, PropertyVTableItem, InterfaceFlagsVTableItem>;
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_VTABLEITEMS_H_ */
|
301
include/sdbus-c++/VTableItems.inl
Normal file
301
include/sdbus-c++/VTableItems.inl
Normal file
@ -0,0 +1,301 @@
|
||||
/**
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableItems.inl
|
||||
*
|
||||
* Created on: Dec 14, 2023
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_VTABLEITEMS_INL_
|
||||
#define SDBUS_CPP_VTABLEITEMS_INL_
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** Method VTable Item ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
template <typename _Function>
|
||||
MethodVTableItem& MethodVTableItem::implementedAs(_Function&& callback)
|
||||
{
|
||||
inputSignature = signature_of_function_input_arguments_v<_Function>;
|
||||
outputSignature = signature_of_function_output_arguments_v<_Function>;
|
||||
callbackHandler = [callback = std::forward<_Function>(callback)](MethodCall call)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<_Function> inputArgs;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple.
|
||||
call >> inputArgs;
|
||||
|
||||
if constexpr (!is_async_method_v<_Function>)
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
auto ret = sdbus::apply(callback, inputArgs);
|
||||
|
||||
// Store output arguments to the reply message and send it back.
|
||||
auto reply = call.createReply();
|
||||
reply << ret;
|
||||
reply.send();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke callback with input arguments from the tuple and with result object to be set later
|
||||
using AsyncResult = typename function_traits<_Function>::async_result_t;
|
||||
sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
|
||||
}
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::withInputParamNames(std::vector<std::string> names)
|
||||
{
|
||||
inputParamNames = std::move(names);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _String>
|
||||
inline MethodVTableItem& MethodVTableItem::withInputParamNames(_String... names)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withInputParamNames({names...});
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::withOutputParamNames(std::vector<std::string> names)
|
||||
{
|
||||
outputParamNames = std::move(names);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _String>
|
||||
inline MethodVTableItem& MethodVTableItem::withOutputParamNames(_String... names)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
|
||||
return withOutputParamNames({names...});
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::markAsPrivileged()
|
||||
{
|
||||
flags.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem& MethodVTableItem::withNoReply()
|
||||
{
|
||||
flags.set(Flags::METHOD_NO_REPLY);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline MethodVTableItem registerMethod(MethodName methodName)
|
||||
{
|
||||
return {std::move(methodName), {}, {}, {}, {}, {}, {}};
|
||||
}
|
||||
|
||||
inline MethodVTableItem registerMethod(std::string methodName)
|
||||
{
|
||||
return registerMethod(MethodName{std::move(methodName)});
|
||||
}
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** Signal VTable Item ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
template <typename... _Args>
|
||||
inline SignalVTableItem& SignalVTableItem::withParameters()
|
||||
{
|
||||
signature = signature_of_function_input_arguments_v<void(_Args...)>;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline SignalVTableItem& SignalVTableItem::withParameters(std::vector<std::string> names)
|
||||
{
|
||||
paramNames = std::move(names);
|
||||
|
||||
return withParameters<_Args...>();
|
||||
}
|
||||
|
||||
template <typename... _Args, typename... _String>
|
||||
inline SignalVTableItem& SignalVTableItem::withParameters(_String... names)
|
||||
{
|
||||
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
|
||||
static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
|
||||
|
||||
return withParameters<_Args...>({names...});
|
||||
}
|
||||
|
||||
inline SignalVTableItem& SignalVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline SignalVTableItem registerSignal(SignalName signalName)
|
||||
{
|
||||
return {std::move(signalName), {}, {}, {}};
|
||||
}
|
||||
|
||||
inline SignalVTableItem registerSignal(std::string signalName)
|
||||
{
|
||||
return registerSignal(SignalName{std::move(signalName)});
|
||||
}
|
||||
|
||||
/*** -------------------- ***/
|
||||
/*** Property VTable Item ***/
|
||||
/*** -------------------- ***/
|
||||
|
||||
template <typename _Function>
|
||||
inline PropertyVTableItem& PropertyVTableItem::withGetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_argument_count_v<_Function> == 0, "Property getter function must not take any arguments");
|
||||
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
|
||||
|
||||
if (signature.empty())
|
||||
signature = signature_of_function_output_arguments_v<_Function>;
|
||||
|
||||
getter = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply)
|
||||
{
|
||||
// Get the propety value and serialize it into the pre-constructed reply message
|
||||
reply << callback();
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline PropertyVTableItem& PropertyVTableItem::withSetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_argument_count_v<_Function> == 1, "Property setter function must take one parameter - the property value");
|
||||
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
|
||||
|
||||
if (signature.empty())
|
||||
signature = signature_of_function_input_arguments_v<_Function>;
|
||||
|
||||
setter = [callback = std::forward<_Function>(callback)](PropertySetCall call)
|
||||
{
|
||||
// Default-construct property value
|
||||
using property_type = function_argument_t<_Function, 0>;
|
||||
std::decay_t<property_type> property;
|
||||
|
||||
// Deserialize property value from the incoming call message
|
||||
call >> property;
|
||||
|
||||
// Invoke setter with the value
|
||||
callback(property);
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem& PropertyVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem& PropertyVTableItem::markAsPrivileged()
|
||||
{
|
||||
flags.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem& PropertyVTableItem::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
|
||||
{
|
||||
flags.set(behavior);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline PropertyVTableItem registerProperty(PropertyName propertyName)
|
||||
{
|
||||
return {std::move(propertyName), {}, {}, {}, {}};
|
||||
}
|
||||
|
||||
inline PropertyVTableItem registerProperty(std::string propertyName)
|
||||
{
|
||||
return registerProperty(PropertyName{std::move(propertyName)});
|
||||
}
|
||||
|
||||
/*** --------------------------- ***/
|
||||
/*** Interface Flags VTable Item ***/
|
||||
/*** --------------------------- ***/
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsDeprecated()
|
||||
{
|
||||
flags.set(Flags::DEPRECATED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsPrivileged()
|
||||
{
|
||||
flags.set(Flags::PRIVILEGED);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withNoReplyMethods()
|
||||
{
|
||||
flags.set(Flags::METHOD_NO_REPLY);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
|
||||
{
|
||||
flags.set(behavior);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline InterfaceFlagsVTableItem setInterfaceFlags()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CPP_VTABLEITEMS_INL_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file sdbus-c++.h
|
||||
*
|
||||
@ -25,10 +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@PKGCONFIG_REQS@: @PKGCONFIG_DEPS@
|
||||
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}
|
997
src/Connection.cpp
Executable file → Normal file
997
src/Connection.cpp
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
274
src/Connection.h
Executable file → Normal file
274
src/Connection.h
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Connection.h
|
||||
*
|
||||
@ -26,81 +27,230 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
|
||||
#include "sdbus-c++/Message.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "ISdBus.h"
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include SDBUS_HEADER
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
class Connection
|
||||
: public sdbus::IConnection // External, public interface
|
||||
, public sdbus::internal::IConnection // Internal, private interface
|
||||
// Forward declarations
|
||||
struct sd_event_source;
|
||||
namespace sdbus {
|
||||
class ObjectPath;
|
||||
class InterfaceName;
|
||||
class BusName;
|
||||
using ServiceName = BusName;
|
||||
class MemberName;
|
||||
using MethodName = MemberName;
|
||||
using SignalName = MemberName;
|
||||
using PropertyName = MemberName;
|
||||
}
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Connection final
|
||||
: public sdbus::internal::IConnection
|
||||
{
|
||||
public:
|
||||
enum class BusType
|
||||
// Bus type tags
|
||||
struct default_bus_t{};
|
||||
inline static constexpr default_bus_t default_bus{};
|
||||
struct system_bus_t{};
|
||||
inline static constexpr system_bus_t system_bus{};
|
||||
struct session_bus_t{};
|
||||
inline static constexpr session_bus_t session_bus{};
|
||||
struct custom_session_bus_t{};
|
||||
inline static constexpr custom_session_bus_t custom_session_bus{};
|
||||
struct remote_system_bus_t{};
|
||||
inline static constexpr remote_system_bus_t remote_system_bus{};
|
||||
struct private_bus_t{};
|
||||
inline static constexpr private_bus_t private_bus{};
|
||||
struct server_bus_t{};
|
||||
inline static constexpr server_bus_t server_bus{};
|
||||
struct sdbus_bus_t{}; // A bus connection created directly from existing sd_bus instance
|
||||
inline static constexpr sdbus_bus_t sdbus_bus{};
|
||||
struct pseudo_bus_t{}; // A bus connection that is not really established with D-Bus daemon
|
||||
inline static constexpr pseudo_bus_t pseudo_bus{};
|
||||
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, default_bus_t);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, custom_session_bus_t, const std::string& address);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, private_bus_t, const std::string& address);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, private_bus_t, int fd);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, server_bus_t, int fd);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, sdbus_bus_t, sd_bus *bus);
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t);
|
||||
~Connection() override;
|
||||
|
||||
void requestName(const ServiceName & name) override;
|
||||
void releaseName(const ServiceName& name) override;
|
||||
[[nodiscard]] BusName getUniqueName() const override;
|
||||
void enterEventLoop() override;
|
||||
void enterEventLoopAsync() override;
|
||||
void leaveEventLoop() override;
|
||||
[[nodiscard]] PollData getEventLoopPollData() const override;
|
||||
bool processPendingEvent() override;
|
||||
Message getCurrentlyProcessedMessage() const override;
|
||||
|
||||
void addObjectManager(const ObjectPath& objectPath) override;
|
||||
Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) override;
|
||||
|
||||
void setMethodCallTimeout(uint64_t timeout) override;
|
||||
[[nodiscard]] uint64_t getMethodCallTimeout() const override;
|
||||
|
||||
void addMatch(const std::string& match, message_handler callback) override;
|
||||
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback, return_slot_t) override;
|
||||
void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override;
|
||||
[[nodiscard]] Slot addMatchAsync( const std::string& match
|
||||
, message_handler callback
|
||||
, message_handler installCallback
|
||||
, return_slot_t ) override;
|
||||
|
||||
void attachSdEventLoop(sd_event *event, int priority) override;
|
||||
void detachSdEventLoop() override;
|
||||
sd_event *getSdEventLoop() override;
|
||||
|
||||
Slot addObjectVTable( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData
|
||||
, return_slot_t ) override;
|
||||
|
||||
[[nodiscard]] PlainMessage createPlainMessage() const override;
|
||||
[[nodiscard]] MethodCall createMethodCall( const ServiceName& destination
|
||||
, const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const MethodName& methodName ) const override;
|
||||
[[nodiscard]] MethodCall createMethodCall( const char* destination
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* methodName ) const override;
|
||||
[[nodiscard]] Signal createSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const SignalName& signalName ) const override;
|
||||
[[nodiscard]] Signal createSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName ) const override;
|
||||
|
||||
void emitPropertiesChangedSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) override;
|
||||
void emitPropertiesChangedSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) override;
|
||||
void emitInterfacesAddedSignal(const ObjectPath& objectPath) override;
|
||||
void emitInterfacesAddedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) override;
|
||||
void emitInterfacesRemovedSignal(const ObjectPath& objectPath) override;
|
||||
void emitInterfacesRemovedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) override;
|
||||
|
||||
Slot registerSignalHandler( const char* sender
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData
|
||||
, return_slot_t ) override;
|
||||
|
||||
sd_bus_message* incrementMessageRefCount(sd_bus_message* sdbusMsg) override;
|
||||
sd_bus_message* decrementMessageRefCount(sd_bus_message* sdbusMsg) override;
|
||||
|
||||
int querySenderCredentials(sd_bus_message* sdbusMsg, uint64_t mask, sd_bus_creds **creds) override;
|
||||
sd_bus_creds* incrementCredsRefCount(sd_bus_creds* creds) override;
|
||||
sd_bus_creds* decrementCredsRefCount(sd_bus_creds* creds) override;
|
||||
|
||||
sd_bus_message* callMethod(sd_bus_message* sdbusMsg, uint64_t timeout) override;
|
||||
Slot callMethodAsync(sd_bus_message* sdbusMsg, sd_bus_message_handler_t callback, void* userData, uint64_t timeout, return_slot_t) override;
|
||||
void sendMessage(sd_bus_message* sdbusMsg) override;
|
||||
|
||||
sd_bus_message* createMethodReply(sd_bus_message* sdbusMsg) override;
|
||||
sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) override;
|
||||
|
||||
private:
|
||||
using BusFactory = std::function<int(sd_bus**)>;
|
||||
using BusPtr = std::unique_ptr<sd_bus, std::function<sd_bus*(sd_bus*)>>;
|
||||
Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory);
|
||||
|
||||
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
|
||||
BusPtr openPseudoBus();
|
||||
void finishHandshake(sd_bus* bus);
|
||||
bool waitForNextEvent();
|
||||
|
||||
[[nodiscard]] bool arePendingMessagesInQueues() const;
|
||||
|
||||
void notifyEventLoopToExit();
|
||||
void notifyEventLoopToWakeUpFromPoll();
|
||||
void wakeUpEventLoopIfMessagesInQueue();
|
||||
void joinWithEventLoop();
|
||||
|
||||
template <typename StringBasedType>
|
||||
static std::vector</*const */char*> to_strv(const std::vector<StringBasedType>& strings);
|
||||
|
||||
static int sdbus_match_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_match_install_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
#ifndef SDBUS_basu // sd_event integration is not supported if instead of libsystemd we are based on basu
|
||||
Slot createSdEventSlot(sd_event *event);
|
||||
Slot createSdTimeEventSourceSlot(sd_event *event, int priority);
|
||||
Slot createSdIoEventSourceSlot(sd_event *event, int fd, int priority);
|
||||
Slot createSdInternalEventSourceSlot(sd_event *event, int fd, int priority);
|
||||
static void deleteSdEventSource(sd_event_source *s);
|
||||
|
||||
static int onSdTimerEvent(sd_event_source *s, uint64_t usec, void *userdata);
|
||||
static int onSdIoEvent(sd_event_source *s, int fd, uint32_t revents, void *userdata);
|
||||
static int onSdInternalEvent(sd_event_source *s, int fd, uint32_t revents, void *userdata);
|
||||
static int onSdEventPrepare(sd_event_source *s, void *userdata);
|
||||
#endif
|
||||
|
||||
struct EventFd
|
||||
{
|
||||
eSystem,
|
||||
eSession
|
||||
EventFd();
|
||||
~EventFd();
|
||||
void notify();
|
||||
bool clear();
|
||||
|
||||
int fd{-1};
|
||||
};
|
||||
|
||||
Connection(BusType type);
|
||||
~Connection();
|
||||
struct MatchInfo
|
||||
{
|
||||
message_handler callback;
|
||||
message_handler installCallback;
|
||||
Connection& connection;
|
||||
Slot slot;
|
||||
};
|
||||
|
||||
void requestName(const std::string& name) override;
|
||||
void releaseName(const std::string& name) override;
|
||||
void enterProcessingLoop() override;
|
||||
void enterProcessingLoopAsync() override;
|
||||
void leaveProcessingLoop() override;
|
||||
|
||||
void* addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const void* vtable
|
||||
, void* userData ) override;
|
||||
void removeObjectVTable(void* vtableHandle) override;
|
||||
|
||||
sdbus::Message createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const override;
|
||||
sdbus::Message createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const override;
|
||||
|
||||
void* registerSignalHandler( 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;
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> clone() const override;
|
||||
// sd-event integration
|
||||
struct SdEvent
|
||||
{
|
||||
Slot sdEvent;
|
||||
Slot sdTimeEventSource;
|
||||
Slot sdIoEventSource;
|
||||
Slot sdInternalEventSource;
|
||||
};
|
||||
|
||||
private:
|
||||
static sd_bus* openBus(Connection::BusType type);
|
||||
static void finishHandshake(sd_bus* bus);
|
||||
static int createProcessingExitDescriptor();
|
||||
static void closeProcessingExitDescriptor(int fd);
|
||||
static bool processPendingRequest(sd_bus* bus);
|
||||
static bool waitForNextRequest(sd_bus* bus, int exitFd);
|
||||
static std::string composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName );
|
||||
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> sdbus_;
|
||||
BusPtr bus_;
|
||||
std::thread asyncLoopThread_;
|
||||
std::atomic<int> exitLoopFd_{-1};
|
||||
BusType busType_;
|
||||
|
||||
static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000;
|
||||
EventFd loopExitFd_; // To wake up event loop I/O polling to exit
|
||||
EventFd eventFd_; // To wake up event loop I/O polling to re-enter poll with fresh PollData values
|
||||
std::vector<Slot> floatingMatchRules_;
|
||||
std::unique_ptr<SdEvent> sdEvent_; // Integration of systemd sd-event event loop implementation
|
||||
};
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
#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 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Error.cpp
|
||||
*
|
||||
@ -23,20 +24,26 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include "sdbus-c++/Error.h"
|
||||
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
#include SDBUS_HEADER
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
sdbus::Error createError(int errNo, const std::string& customMsg)
|
||||
sdbus::Error createError(int errNo, std::string customMsg)
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
sd_bus_error_set_errno(&sdbusError, errNo);
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
|
||||
std::string name(sdbusError.name);
|
||||
std::string message(customMsg + " (" + sdbusError.message + ")");
|
||||
return sdbus::Error(name, message);
|
||||
Error::Name name(sdbusError.name);
|
||||
std::string message(std::move(customMsg));
|
||||
message.append(" (");
|
||||
message.append(sdbusError.message);
|
||||
message.append(")");
|
||||
|
||||
return Error(std::move(name), std::move(message));
|
||||
}
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
112
src/Flags.cpp
Normal file
112
src/Flags.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 SDBUS_HEADER
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
120
src/IConnection.h
Executable file → Normal file
120
src/IConnection.h
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file IConnection.h
|
||||
*
|
||||
@ -26,52 +27,109 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_ICONNECTION_H_
|
||||
#define SDBUS_CXX_INTERNAL_ICONNECTION_H_
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
|
||||
// Forward declaration
|
||||
#include "sdbus-c++/TypeTraits.h"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include SDBUS_HEADER
|
||||
#include <vector>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
class PlainMessage;
|
||||
class ObjectPath;
|
||||
class InterfaceName;
|
||||
class BusName;
|
||||
using ServiceName = BusName;
|
||||
class MemberName;
|
||||
using MethodName = MemberName;
|
||||
using SignalName = MemberName;
|
||||
using PropertyName = MemberName;
|
||||
class Error;
|
||||
namespace internal {
|
||||
class ISdBus;
|
||||
}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
class IConnection
|
||||
: public ::sdbus::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;
|
||||
~IConnection() override = default;
|
||||
|
||||
virtual sdbus::Message createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
[[nodiscard]] virtual Slot addObjectVTable( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const sd_bus_vtable* vtable
|
||||
, void* userData
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
virtual sdbus::Message createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
[[nodiscard]] virtual PlainMessage createPlainMessage() const = 0;
|
||||
[[nodiscard]] virtual MethodCall createMethodCall( const ServiceName& destination
|
||||
, const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const MethodName& methodName ) const = 0;
|
||||
[[nodiscard]] virtual MethodCall createMethodCall( const char* destination
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* methodName ) const = 0;
|
||||
[[nodiscard]] virtual Signal createSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const SignalName& signalName ) const = 0;
|
||||
[[nodiscard]] virtual Signal createSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName ) const = 0;
|
||||
|
||||
virtual void* 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 emitPropertiesChangedSignal( const ObjectPath& objectPath
|
||||
, const InterfaceName& interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) = 0;
|
||||
virtual void emitPropertiesChangedSignal( const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const std::vector<PropertyName>& propNames ) = 0;
|
||||
virtual void emitInterfacesAddedSignal(const ObjectPath& objectPath) = 0;
|
||||
virtual void emitInterfacesAddedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) = 0;
|
||||
virtual void emitInterfacesRemovedSignal(const ObjectPath& objectPath) = 0;
|
||||
virtual void emitInterfacesRemovedSignal( const ObjectPath& objectPath
|
||||
, const std::vector<InterfaceName>& interfaces ) = 0;
|
||||
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
[[nodiscard]] virtual Slot registerSignalHandler( const char* sender
|
||||
, const char* objectPath
|
||||
, const char* interfaceName
|
||||
, const char* signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData
|
||||
, return_slot_t ) = 0;
|
||||
|
||||
virtual std::unique_ptr<sdbus::internal::IConnection> clone() const = 0;
|
||||
virtual sd_bus_message* incrementMessageRefCount(sd_bus_message* sdbusMsg) = 0;
|
||||
virtual sd_bus_message* decrementMessageRefCount(sd_bus_message* sdbusMsg) = 0;
|
||||
|
||||
virtual ~IConnection() = default;
|
||||
// TODO: Refactor to higher level (Creds class will ownership handling and getters)
|
||||
virtual int querySenderCredentials(sd_bus_message* sdbusMsg, uint64_t mask, sd_bus_creds **creds) = 0;
|
||||
virtual sd_bus_creds* incrementCredsRefCount(sd_bus_creds* creds) = 0;
|
||||
virtual sd_bus_creds* decrementCredsRefCount(sd_bus_creds* creds) = 0;
|
||||
|
||||
virtual sd_bus_message* callMethod(sd_bus_message* sdbusMsg, uint64_t timeout) = 0;
|
||||
[[nodiscard]] virtual Slot callMethodAsync( sd_bus_message* sdbusMsg
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData
|
||||
, uint64_t timeout
|
||||
, return_slot_t ) = 0;
|
||||
virtual void sendMessage(sd_bus_message* sdbusMsg) = 0;
|
||||
|
||||
virtual sd_bus_message* createMethodReply(sd_bus_message* sdbusMsg) = 0;
|
||||
virtual sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createPseudoConnection();
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_ICONNECTION_H_ */
|
||||
|
115
src/ISdBus.h
Normal file
115
src/ISdBus.h
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 SDBUS_HEADER
|
||||
|
||||
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_system(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_open_user(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) = 0;
|
||||
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 0;
|
||||
virtual int sd_bus_open_direct(sd_bus **ret, const char* address) = 0;
|
||||
virtual int sd_bus_open_direct(sd_bus **ret, int fd) = 0;
|
||||
virtual int sd_bus_open_server(sd_bus **ret, int fd) = 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 int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) = 0;
|
||||
virtual int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata) = 0;
|
||||
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
|
||||
|
||||
virtual int sd_bus_new(sd_bus **ret) = 0;
|
||||
virtual int sd_bus_start(sd_bus *bus) = 0;
|
||||
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0;
|
||||
virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) = 0;
|
||||
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0;
|
||||
virtual int sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) = 0;
|
||||
virtual int sd_bus_flush(sd_bus *bus) = 0;
|
||||
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
|
||||
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) = 0;
|
||||
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) = 0;
|
||||
|
||||
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) = 0;
|
||||
virtual sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *c) = 0;
|
||||
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) = 0;
|
||||
|
||||
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) = 0;
|
||||
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,26 +0,0 @@
|
||||
|
||||
lib_LTLIBRARIES = libsdbus-c++.la
|
||||
|
||||
libsdbus_c___la_SOURCES = \
|
||||
Connection.cpp \
|
||||
ConvenienceClasses.cpp \
|
||||
Message.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
|
511
src/Message.cpp
Executable file → Normal file
511
src/Message.cpp
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Message.cpp
|
||||
*
|
||||
@ -23,22 +24,42 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "sdbus-c++/Message.h"
|
||||
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include SDBUS_HEADER
|
||||
|
||||
namespace sdbus { /*namespace internal {*/
|
||||
namespace sdbus {
|
||||
|
||||
Message::Message(void *msg, Type type) noexcept
|
||||
Message::Message(internal::IConnection* connection) noexcept
|
||||
: connection_(connection)
|
||||
{
|
||||
assert(connection_ != nullptr);
|
||||
}
|
||||
|
||||
Message::Message(void *msg, internal::IConnection* connection) noexcept
|
||||
: msg_(msg)
|
||||
, type_(type)
|
||||
, connection_(connection)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
assert(connection_ != nullptr);
|
||||
connection_->incrementMessageRefCount((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message::Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept
|
||||
: msg_(msg)
|
||||
, connection_(connection)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
assert(connection_ != nullptr);
|
||||
}
|
||||
|
||||
Message::Message(const Message& other) noexcept
|
||||
@ -49,13 +70,13 @@ Message::Message(const Message& other) noexcept
|
||||
Message& Message::operator=(const Message& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
type_ = other.type_;
|
||||
connection_ = other.connection_;
|
||||
ok_ = other.ok_;
|
||||
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
connection_->incrementMessageRefCount((sd_bus_message*)msg_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -68,12 +89,12 @@ Message::Message(Message&& other) noexcept
|
||||
Message& Message::operator=(Message&& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
other.msg_ = nullptr;
|
||||
type_ = other.type_;
|
||||
other.type_ = {};
|
||||
connection_ = other.connection_;
|
||||
other.connection_ = nullptr;
|
||||
ok_ = other.ok_;
|
||||
other.ok_ = true;
|
||||
|
||||
@ -83,13 +104,17 @@ Message& Message::operator=(Message&& other) noexcept
|
||||
Message::~Message()
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
connection_->decrementMessageRefCount((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message& Message::operator<<(bool item)
|
||||
{
|
||||
int intItem = item;
|
||||
|
||||
// Direct sd-bus method, bypassing SdBus mutex, are called here, since Message serialization/deserialization,
|
||||
// as well as getter/setter methods are not thread safe by design. Additionally, they are called frequently,
|
||||
// so some overhead is spared. What is thread-safe in Message class is Message constructors, copy/move operations
|
||||
// and the destructor, so the Message instance can be passed from one thread to another safely.
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a bool value", -r);
|
||||
|
||||
@ -176,6 +201,17 @@ Message& Message::operator<<(const std::string& item)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator<<(std::string_view item)
|
||||
{
|
||||
char* destPtr{};
|
||||
auto r = sd_bus_message_append_string_space((sd_bus_message*)msg_, item.length(), &destPtr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a string_view value", -r);
|
||||
|
||||
std::memcpy(destPtr, item.data(), item.length());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator<<(const Variant &item)
|
||||
{
|
||||
item.serializeTo(*this);
|
||||
@ -194,11 +230,27 @@ 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;
|
||||
}
|
||||
|
||||
Message& Message::appendArray(char type, const void *ptr, size_t size)
|
||||
{
|
||||
auto r = sd_bus_message_append_array((sd_bus_message*)msg_, type, ptr, size);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an array", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator>>(bool& item)
|
||||
{
|
||||
@ -291,6 +343,17 @@ Message& Message::operator>>(uint64_t& item)
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::readArray(char type, const void **ptr, size_t *size)
|
||||
{
|
||||
auto r = sd_bus_message_read_array((sd_bus_message*)msg_, type, ptr, size);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize an array", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator>>(double& item)
|
||||
{
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
|
||||
@ -367,10 +430,23 @@ Message& Message::operator>>(Signature &item)
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Message& Message::openContainer(const std::string& signature)
|
||||
Message& Message::operator>>(UnixFd &item)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
|
||||
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 char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a container", -r);
|
||||
|
||||
return *this;
|
||||
@ -384,9 +460,9 @@ Message& Message::closeContainer()
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::openDictEntry(const std::string& signature)
|
||||
Message& Message::openDictEntry(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
@ -400,9 +476,9 @@ Message& Message::closeDictEntry()
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::openVariant(const std::string& signature)
|
||||
Message& Message::openVariant(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a variant", -r);
|
||||
|
||||
return *this;
|
||||
@ -416,9 +492,9 @@ Message& Message::closeVariant()
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::openStruct(const std::string& signature)
|
||||
Message& Message::openStruct(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a struct", -r);
|
||||
|
||||
return *this;
|
||||
@ -432,10 +508,9 @@ Message& Message::closeStruct()
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Message& Message::enterContainer(const std::string& signature)
|
||||
Message& Message::enterContainer(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@ -452,9 +527,9 @@ Message& Message::exitContainer()
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::enterDictEntry(const std::string& signature)
|
||||
Message& Message::enterDictEntry(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@ -471,9 +546,9 @@ Message& Message::exitDictEntry()
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::enterVariant(const std::string& signature)
|
||||
Message& Message::enterVariant(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@ -490,9 +565,9 @@ Message& Message::exitVariant()
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::enterStruct(const std::string& signature)
|
||||
Message& Message::enterStruct(const char* signature)
|
||||
{
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
|
||||
@ -540,109 +615,319 @@ void Message::rewind(bool complete)
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
|
||||
}
|
||||
|
||||
Message Message::send() const
|
||||
{
|
||||
if (type_ == Type::eMethodCall)
|
||||
{
|
||||
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*)msg_, 0, &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 Message(sdbusReply);
|
||||
}
|
||||
else if (type_ == Type::eMethodReply)
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
|
||||
|
||||
return Message();
|
||||
}
|
||||
else if (type_ == Type::eSignal)
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
|
||||
|
||||
return Message();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
return Message();
|
||||
}
|
||||
}
|
||||
|
||||
Message Message::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*)msg_, &sdbusReply);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
|
||||
|
||||
assert(sdbusReply != nullptr);
|
||||
|
||||
return Message(sdbusReply, Type::eMethodReply);
|
||||
}
|
||||
|
||||
std::string Message::getInterfaceName() const
|
||||
const char* Message::getInterfaceName() const
|
||||
{
|
||||
return sd_bus_message_get_interface((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
std::string Message::getMemberName() const
|
||||
const char* Message::getMemberName() const
|
||||
{
|
||||
return sd_bus_message_get_member((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
void Message::peekType(std::string& type, std::string& contents) const
|
||||
const char* Message::getSender() const
|
||||
{
|
||||
char typeSig;
|
||||
const char* contentsSig;
|
||||
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig);
|
||||
return sd_bus_message_get_sender((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
const char* Message::getPath() const
|
||||
{
|
||||
return sd_bus_message_get_path((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
const char* Message::getDestination() const
|
||||
{
|
||||
return sd_bus_message_get_destination((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
uint64_t Message::getCookie() const
|
||||
{
|
||||
uint64_t cookie;
|
||||
auto r = sd_bus_message_get_cookie((sd_bus_message*)msg_, &cookie);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
std::pair<char, const char*> Message::peekType() const
|
||||
{
|
||||
char typeSignature{};
|
||||
const char* contentsSignature{};
|
||||
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSignature, &contentsSignature);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r);
|
||||
type = typeSig;
|
||||
contents = contentsSig;
|
||||
return {typeSignature, contentsSignature};
|
||||
}
|
||||
|
||||
bool Message::isValid() const
|
||||
{
|
||||
return msg_ != nullptr;
|
||||
return msg_ != nullptr && connection_ != 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;
|
||||
}
|
||||
|
||||
Message::Type Message::getType() const
|
||||
bool Message::isAtEnd(bool complete) const
|
||||
{
|
||||
return type_;
|
||||
return sd_bus_message_at_end((sd_bus_message*)msg_, complete) > 0;
|
||||
}
|
||||
|
||||
Message createPlainMessage()
|
||||
// TODO: Create a RAII ownership class for creds with copy&move semantics, doing ref()/unref() under the hood.
|
||||
// Create a method Message::querySenderCreds() that will return an object of this class by value, through IConnection and SdBus mutex.
|
||||
// The class will expose methods like getPid(), getUid(), etc. that will directly call sd_bus_creds_* functions, no need for mutex here.
|
||||
pid_t Message::getCredsPid() const
|
||||
{
|
||||
int r;
|
||||
uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
|
||||
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);
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -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, Message::Type::ePlainMessage);
|
||||
pid_t pid = 0;
|
||||
r = sd_bus_creds_get_pid(creds, &pid);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred pid", -r);
|
||||
return pid;
|
||||
}
|
||||
|
||||
/*}*/}
|
||||
uid_t Message::getCredsUid() const
|
||||
{
|
||||
uint64_t mask = SD_BUS_CREDS_UID | SD_BUS_CREDS_AUGMENT;
|
||||
sd_bus_creds *creds = nullptr;
|
||||
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
uid_t uid = (uid_t)-1;
|
||||
r = 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{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
uid_t euid = (uid_t)-1;
|
||||
r = 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{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
gid_t gid = (gid_t)-1;
|
||||
r = 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{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
gid_t egid = (gid_t)-1;
|
||||
r = 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{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
const gid_t *cGids = nullptr;
|
||||
r = 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{ connection_->decrementCredsRefCount(creds); };
|
||||
int r = connection_->querySenderCredentials((sd_bus_message*)msg_, mask, &creds);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
|
||||
|
||||
const char *cLabel = nullptr;
|
||||
r = 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::IConnection *connection
|
||||
, adopt_message_t) noexcept
|
||||
: Message(msg, connection, adopt_message)
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
auto* sdbusReply = connection_->callMethod((sd_bus_message*)msg_, timeout);
|
||||
|
||||
return Factory::create<MethodReply>(sdbusReply, connection_, adopt_message);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::sendWithNoReply() const
|
||||
{
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
|
||||
return Factory::create<MethodReply>(); // No reply
|
||||
}
|
||||
|
||||
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout, return_slot_t) const
|
||||
{
|
||||
return connection_->callMethodAsync((sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout, return_slot);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::createReply() const
|
||||
{
|
||||
auto* sdbusReply = connection_->createMethodReply((sd_bus_message*)msg_);
|
||||
|
||||
return Factory::create<MethodReply>(sdbusReply, connection_, adopt_message);
|
||||
}
|
||||
|
||||
MethodReply MethodCall::createErrorReply(const Error& error) const
|
||||
{
|
||||
sd_bus_message* sdbusErrorReply = connection_->createErrorReplyMessage((sd_bus_message*)msg_, error);
|
||||
|
||||
return Factory::create<MethodReply>(sdbusErrorReply, connection_, adopt_message);
|
||||
}
|
||||
|
||||
void MethodReply::send() const
|
||||
{
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
uint64_t MethodReply::getReplyCookie() const
|
||||
{
|
||||
uint64_t cookie;
|
||||
auto r = sd_bus_message_get_reply_cookie((sd_bus_message*)msg_, &cookie);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
void Signal::send() const
|
||||
{
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
void Signal::setDestination(const std::string& destination)
|
||||
{
|
||||
return setDestination(destination.c_str());
|
||||
}
|
||||
|
||||
void Signal::setDestination(const char* destination)
|
||||
{
|
||||
auto r = sd_bus_message_set_destination((sd_bus_message*)msg_, destination);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Pseudo-connection lifetime handling. In standard cases, we could do with simply function-local static pseudo
|
||||
// connection instance below. However, it may happen that client's sdbus-c++ objects outlive this static connection
|
||||
// instance (because they are used in global application objects that were created before this connection, and thus
|
||||
// are destroyed later). This by itself sounds like a smell in client's application design, but it is downright bad
|
||||
// in sdbus-c++ because it has no control over when client's dependent statics get destroyed. A "Phoenix" pattern
|
||||
// (see Modern C++ Design - Generic Programming and Design Patterns Applied, by Andrei Alexandrescu) is applied to fix
|
||||
// this by re-creating the connection again in such cases and keeping it alive until the next exit handler is invoked.
|
||||
// Please note that the solution is NOT thread-safe.
|
||||
// Another common solution is global sdbus-c++ startup/shutdown functions, but that would be an intrusive change.
|
||||
|
||||
#ifdef __cpp_constinit
|
||||
constinit static bool pseudoConnectionDestroyed{};
|
||||
#else
|
||||
static bool pseudoConnectionDestroyed{};
|
||||
#endif
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection, void(*)(sdbus::internal::IConnection*)> createPseudoConnection()
|
||||
{
|
||||
auto deleter = [](sdbus::internal::IConnection* con)
|
||||
{
|
||||
delete con;
|
||||
pseudoConnectionDestroyed = true;
|
||||
};
|
||||
|
||||
return {internal::createPseudoConnection().release(), std::move(deleter)};
|
||||
}
|
||||
|
||||
sdbus::internal::IConnection& getPseudoConnectionInstance()
|
||||
{
|
||||
static auto connection = createPseudoConnection();
|
||||
|
||||
if (pseudoConnectionDestroyed)
|
||||
{
|
||||
connection = createPseudoConnection(); // Phoenix rising from the ashes
|
||||
atexit([](){ connection.~unique_ptr(); }); // We have to manually take care of deleting the phoenix
|
||||
pseudoConnectionDestroyed = false;
|
||||
}
|
||||
|
||||
assert(connection != nullptr);
|
||||
|
||||
return *connection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PlainMessage createPlainMessage()
|
||||
{
|
||||
// Let's create a pseudo connection -- one that does not really connect to the real bus.
|
||||
// This is a bit of a hack, but it enables use to work with D-Bus message locally without
|
||||
// the need of D-Bus daemon. This is especially useful in unit tests of both sdbus-c++ and client code.
|
||||
// Additionally, it's light-weight and fast solution.
|
||||
const auto& connection = getPseudoConnectionInstance();
|
||||
return connection.createPlainMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file MessageUtils.h
|
||||
*
|
||||
@ -30,7 +31,33 @@
|
||||
|
||||
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::IConnection* connection)
|
||||
{
|
||||
return _Msg{msg, connection};
|
||||
}
|
||||
|
||||
template<typename _Msg>
|
||||
static _Msg create(void *msg, internal::IConnection* connection, adopt_message_t)
|
||||
{
|
||||
return _Msg{msg, connection, adopt_message};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */
|
||||
|
449
src/Object.cpp
Executable file → Normal file
449
src/Object.cpp
Executable file → Normal file
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Object.cpp
|
||||
*
|
||||
@ -24,261 +25,375 @@
|
||||
*/
|
||||
|
||||
#include "Object.h"
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
|
||||
#include "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/Flags.h"
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
#include "sdbus-c++/Message.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include "Utils.h"
|
||||
#include "VTableUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <utility>
|
||||
|
||||
#include <cassert>
|
||||
#include SDBUS_HEADER
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
namespace sdbus::internal {
|
||||
|
||||
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
|
||||
Object::Object(sdbus::internal::IConnection& connection, ObjectPath objectPath)
|
||||
: connection_(connection), objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback )
|
||||
void Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
||||
auto slot = Object::addVTable(std::move(interfaceName), std::move(vtable), return_slot);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback)};
|
||||
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
vtables_.push_back(std::move(slot));
|
||||
}
|
||||
|
||||
void Object::registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature )
|
||||
Slot Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t)
|
||||
{
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName.c_str());
|
||||
|
||||
InterfaceData::SignalData signalData{signature};
|
||||
auto inserted = interface.signals_.emplace(signalName, std::move(signalData)).second;
|
||||
// 1st pass -- create vtable structure for internal sdbus-c++ purposes
|
||||
auto internalVTable = std::make_unique<VTable>(createInternalVTable(std::move(interfaceName), std::move(vtable)));
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
|
||||
// 2nd pass -- from internal sdbus-c++ vtable, create vtable structure in format expected by underlying sd-bus library
|
||||
internalVTable->sdbusVTable = createInternalSdBusVTable(*internalVTable);
|
||||
|
||||
// 3rd step -- register the vtable with sd-bus
|
||||
internalVTable->slot = connection_.addObjectVTable( objectPath_
|
||||
, internalVTable->interfaceName
|
||||
, &internalVTable->sdbusVTable[0]
|
||||
, internalVTable.get()
|
||||
, return_slot );
|
||||
|
||||
// Return vtable wrapped in a Slot object
|
||||
return {internalVTable.release(), [](void *ptr){ delete static_cast<VTable*>(ptr); }};
|
||||
}
|
||||
|
||||
void Object::registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback )
|
||||
void Object::unregister()
|
||||
{
|
||||
registerProperty( interfaceName
|
||||
, propertyName
|
||||
, signature
|
||||
, getCallback
|
||||
, property_set_callback{} );
|
||||
vtables_.clear();
|
||||
objectManagerSlot_.reset();
|
||||
}
|
||||
|
||||
void Object::registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback )
|
||||
Signal Object::createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
|
||||
InterfaceData::PropertyData propertyData{signature, std::move(getCallback), std::move(setCallback)};
|
||||
auto inserted = interface.properties_.emplace(propertyName, std::move(propertyData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::finishRegistration()
|
||||
{
|
||||
for (auto& item : interfaces_)
|
||||
{
|
||||
const auto& interfaceName = item.first;
|
||||
auto& interfaceData = item.second;
|
||||
|
||||
const auto& vtable = createInterfaceVTable(interfaceData);
|
||||
activateInterfaceVTable(interfaceName, interfaceData, vtable);
|
||||
}
|
||||
}
|
||||
|
||||
sdbus::Message Object::createSignal(const std::string& interfaceName, const std::string& signalName)
|
||||
{
|
||||
// Tell, don't ask
|
||||
return connection_.createSignal(objectPath_, interfaceName, signalName);
|
||||
}
|
||||
|
||||
void Object::emitSignal(const sdbus::Message& message)
|
||||
Signal Object::createSignal(const char* interfaceName, const char* signalName) const
|
||||
{
|
||||
return connection_.createSignal(objectPath_.c_str(), interfaceName, signalName);
|
||||
}
|
||||
|
||||
void Object::emitSignal(const sdbus::Signal& message)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
|
||||
|
||||
message.send();
|
||||
}
|
||||
|
||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||
void Object::emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames)
|
||||
{
|
||||
auto& vtable = interfaceData.vtable_;
|
||||
assert(vtable.empty());
|
||||
|
||||
vtable.push_back(createVTableStartItem());
|
||||
registerMethodsToVTable(interfaceData, vtable);
|
||||
registerSignalsToVTable(interfaceData, vtable);
|
||||
registerPropertiesToVTable(interfaceData, vtable);
|
||||
vtable.push_back(createVTableEndItem());
|
||||
|
||||
return vtable;
|
||||
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
|
||||
}
|
||||
|
||||
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
void Object::emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames)
|
||||
{
|
||||
for (const auto& item : interfaceData.methods_)
|
||||
connection_.emitPropertiesChangedSignal(objectPath_.c_str(), interfaceName, propNames);
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const InterfaceName& interfaceName)
|
||||
{
|
||||
Object::emitPropertiesChangedSignal(interfaceName, {});
|
||||
}
|
||||
|
||||
void Object::emitPropertiesChangedSignal(const char* interfaceName)
|
||||
{
|
||||
Object::emitPropertiesChangedSignal(interfaceName, {});
|
||||
}
|
||||
|
||||
void Object::emitInterfacesAddedSignal()
|
||||
{
|
||||
connection_.emitInterfacesAddedSignal(objectPath_);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces)
|
||||
{
|
||||
connection_.emitInterfacesAddedSignal(objectPath_, interfaces);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesRemovedSignal()
|
||||
{
|
||||
connection_.emitInterfacesRemovedSignal(objectPath_);
|
||||
}
|
||||
|
||||
void Object::emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
|
||||
{
|
||||
connection_.emitInterfacesRemovedSignal(objectPath_, interfaces);
|
||||
}
|
||||
|
||||
void Object::addObjectManager()
|
||||
{
|
||||
objectManagerSlot_ = connection_.addObjectManager(objectPath_, return_slot);
|
||||
}
|
||||
|
||||
Slot Object::addObjectManager(return_slot_t)
|
||||
{
|
||||
return connection_.addObjectManager(objectPath_, return_slot);
|
||||
}
|
||||
|
||||
sdbus::IConnection& Object::getConnection() const
|
||||
{
|
||||
return connection_;
|
||||
}
|
||||
|
||||
const ObjectPath& Object::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
Message Object::getCurrentlyProcessedMessage() const
|
||||
{
|
||||
return connection_.getCurrentlyProcessedMessage();
|
||||
}
|
||||
|
||||
Object::VTable Object::createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
|
||||
{
|
||||
VTable internalVTable;
|
||||
|
||||
internalVTable.interfaceName = std::move(interfaceName);
|
||||
|
||||
for (auto& vtableItem : vtable)
|
||||
{
|
||||
const auto& methodName = item.first;
|
||||
const auto& methodData = item.second;
|
||||
|
||||
vtable.push_back(createVTableMethodItem( methodName.c_str()
|
||||
, methodData.inputArgs_.c_str()
|
||||
, methodData.outputArgs_.c_str()
|
||||
, &Object::sdbus_method_callback ));
|
||||
std::visit( overload{ [&](InterfaceFlagsVTableItem&& interfaceFlags){ writeInterfaceFlagsToVTable(std::move(interfaceFlags), internalVTable); }
|
||||
, [&](MethodVTableItem&& method){ writeMethodRecordToVTable(std::move(method), internalVTable); }
|
||||
, [&](SignalVTableItem&& signal){ writeSignalRecordToVTable(std::move(signal), internalVTable); }
|
||||
, [&](PropertyVTableItem&& property){ writePropertyRecordToVTable(std::move(property), internalVTable); } }
|
||||
, std::move(vtableItem) );
|
||||
}
|
||||
|
||||
// Sort arrays so we can do fast searching for an item in sd-bus callback handlers
|
||||
std::sort(internalVTable.methods.begin(), internalVTable.methods.end(), [](const auto& a, const auto& b){ return a.name < b.name; });
|
||||
std::sort(internalVTable.signals.begin(), internalVTable.signals.end(), [](const auto& a, const auto& b){ return a.name < b.name; });
|
||||
std::sort(internalVTable.properties.begin(), internalVTable.properties.end(), [](const auto& a, const auto& b){ return a.name < b.name; });
|
||||
|
||||
internalVTable.object = this;
|
||||
|
||||
return internalVTable;
|
||||
}
|
||||
|
||||
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
void Object::writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.signals_)
|
||||
vtable.interfaceFlags = std::move(flags.flags);
|
||||
}
|
||||
|
||||
void Object::writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable)
|
||||
{
|
||||
SDBUS_CHECK_MEMBER_NAME(method.name.c_str());
|
||||
SDBUS_THROW_ERROR_IF(!method.callbackHandler, "Invalid method callback provided", EINVAL);
|
||||
|
||||
vtable.methods.push_back({ std::move(method.name)
|
||||
, std::move(method.inputSignature)
|
||||
, std::move(method.outputSignature)
|
||||
, paramNamesToString(method.inputParamNames) + paramNamesToString(method.outputParamNames)
|
||||
, std::move(method.callbackHandler)
|
||||
, std::move(method.flags) });
|
||||
}
|
||||
|
||||
void Object::writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable)
|
||||
{
|
||||
SDBUS_CHECK_MEMBER_NAME(signal.name.c_str());
|
||||
|
||||
vtable.signals.push_back({ std::move(signal.name)
|
||||
, std::move(signal.signature)
|
||||
, paramNamesToString(signal.paramNames)
|
||||
, std::move(signal.flags) });
|
||||
}
|
||||
|
||||
void Object::writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable)
|
||||
{
|
||||
SDBUS_CHECK_MEMBER_NAME(property.name.c_str());
|
||||
SDBUS_THROW_ERROR_IF(!property.getter && !property.setter, "Invalid property callbacks provided", EINVAL);
|
||||
|
||||
vtable.properties.push_back({ std::move(property.name)
|
||||
, std::move(property.signature)
|
||||
, std::move(property.getter)
|
||||
, std::move(property.setter)
|
||||
, std::move(property.flags) });
|
||||
}
|
||||
|
||||
std::vector<sd_bus_vtable> Object::createInternalSdBusVTable(const VTable& vtable)
|
||||
{
|
||||
std::vector<sd_bus_vtable> sdbusVTable;
|
||||
|
||||
startSdBusVTable(vtable.interfaceFlags, sdbusVTable);
|
||||
for (const auto& methodItem : vtable.methods)
|
||||
writeMethodRecordToSdBusVTable(methodItem, sdbusVTable);
|
||||
for (const auto& signalItem : vtable.signals)
|
||||
writeSignalRecordToSdBusVTable(signalItem, sdbusVTable);
|
||||
for (const auto& propertyItem : vtable.properties)
|
||||
writePropertyRecordToSdBusVTable(propertyItem, sdbusVTable);
|
||||
finalizeSdBusVTable(sdbusVTable);
|
||||
|
||||
return sdbusVTable;
|
||||
}
|
||||
|
||||
void Object::startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = createSdBusVTableStartItem(interfaceFlags.toSdBusInterfaceFlags());
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = createSdBusVTableMethodItem( method.name.c_str()
|
||||
, method.inputSignature.c_str()
|
||||
, method.outputSignature.c_str()
|
||||
, method.paramNames.c_str()
|
||||
, &Object::sdbus_method_callback
|
||||
, method.flags.toSdBusMethodFlags() );
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = createSdBusVTableSignalItem( signal.name.c_str()
|
||||
, signal.signature.c_str()
|
||||
, signal.paramNames.c_str()
|
||||
, signal.flags.toSdBusSignalFlags() );
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
auto vtableItem = !property.setCallback
|
||||
? createSdBusVTableReadOnlyPropertyItem( property.name.c_str()
|
||||
, property.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, property.flags.toSdBusPropertyFlags() )
|
||||
: createSdBusVTableWritablePropertyItem( property.name.c_str()
|
||||
, property.signature.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, &Object::sdbus_property_set_callback
|
||||
, property.flags.toSdBusWritablePropertyFlags() );
|
||||
vtable.push_back(std::move(vtableItem));
|
||||
}
|
||||
|
||||
void Object::finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
vtable.push_back(createSdBusVTableEndItem());
|
||||
}
|
||||
|
||||
const Object::VTable::MethodItem* Object::findMethod(const VTable& vtable, std::string_view methodName)
|
||||
{
|
||||
auto it = std::lower_bound(vtable.methods.begin(), vtable.methods.end(), methodName, [](const auto& methodItem, const auto& methodName)
|
||||
{
|
||||
const auto& signalName = item.first;
|
||||
const auto& signalData = item.second;
|
||||
return methodItem.name < methodName;
|
||||
});
|
||||
|
||||
vtable.push_back(createVTableSignalItem( signalName.c_str()
|
||||
, signalData.signature_.c_str() ));
|
||||
}
|
||||
return it != vtable.methods.end() && it->name == methodName ? &*it : nullptr;
|
||||
}
|
||||
|
||||
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
const Object::VTable::PropertyItem* Object::findProperty(const VTable& vtable, std::string_view propertyName)
|
||||
{
|
||||
for (const auto& item : interfaceData.properties_)
|
||||
auto it = std::lower_bound(vtable.properties.begin(), vtable.properties.end(), propertyName, [](const auto& propertyItem, const auto& propertyName)
|
||||
{
|
||||
const auto& propertyName = item.first;
|
||||
const auto& propertyData = item.second;
|
||||
return propertyItem.name < propertyName;
|
||||
});
|
||||
|
||||
if (!propertyData.setCallback_)
|
||||
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, &Object::sdbus_property_get_callback ));
|
||||
else
|
||||
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, &Object::sdbus_property_set_callback ));
|
||||
}
|
||||
return it != vtable.properties.end() && it->name == propertyName ? &*it : nullptr;
|
||||
}
|
||||
|
||||
void Object::activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable )
|
||||
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
|
||||
{
|
||||
// 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); };
|
||||
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)
|
||||
{
|
||||
Message message(sdbusMessage, Message::Type::eMethodCall);
|
||||
auto* vtable = static_cast<VTable*>(userData);
|
||||
assert(vtable != nullptr);
|
||||
assert(vtable->object != nullptr);
|
||||
|
||||
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_;
|
||||
assert(callback);
|
||||
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &vtable->object->connection_);
|
||||
|
||||
auto reply = message.createReply();
|
||||
const auto* methodItem = findMethod(*vtable, message.getMemberName());
|
||||
assert(methodItem != nullptr);
|
||||
assert(methodItem->callback);
|
||||
|
||||
try
|
||||
{
|
||||
callback(message, reply);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ methodItem->callback(std::move(message)); }, retError);
|
||||
|
||||
reply.send();
|
||||
|
||||
return 1;
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
|
||||
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, Message::Type::ePlainMessage);
|
||||
auto* vtable = static_cast<VTable*>(userData);
|
||||
assert(vtable != nullptr);
|
||||
assert(vtable->object != nullptr);
|
||||
|
||||
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_;
|
||||
// Getter can be empty - the case of "write-only" property
|
||||
if (!callback)
|
||||
const auto* propertyItem = findProperty(*vtable, property);
|
||||
assert(propertyItem != nullptr);
|
||||
|
||||
// Getter may be empty - the case of "write-only" property
|
||||
if (!propertyItem->getCallback)
|
||||
{
|
||||
sd_bus_error_set(retError, "org.freedesktop.DBus.Error.Failed", "Cannot read property as it is write-only");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
callback(reply);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &vtable->object->connection_);
|
||||
|
||||
return 1;
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->getCallback(reply); }, retError);
|
||||
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
|
||||
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, Message::Type::ePlainMessage);
|
||||
auto* vtable = static_cast<VTable*>(userData);
|
||||
assert(vtable != nullptr);
|
||||
assert(vtable->object != nullptr);
|
||||
|
||||
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_;
|
||||
assert(callback);
|
||||
const auto* propertyItem = findProperty(*vtable, property);
|
||||
assert(propertyItem != nullptr);
|
||||
assert(propertyItem->setCallback);
|
||||
|
||||
try
|
||||
{
|
||||
callback(value);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &vtable->object->connection_);
|
||||
|
||||
return 1;
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->setCallback(std::move(value)); }, retError);
|
||||
|
||||
return ok ? 1 : -1;
|
||||
}
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath)
|
||||
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, ObjectPath objectPath)
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
174
src/Object.h
174
src/Object.h
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Object.h
|
||||
*
|
||||
@ -26,88 +27,121 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_OBJECT_H_
|
||||
#define SDBUS_CXX_INTERNAL_OBJECT_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include "IConnection.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
#include "sdbus-c++/IObject.h"
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
#include "IConnection.h"
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include SDBUS_HEADER
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Object
|
||||
: public IObject
|
||||
{
|
||||
public:
|
||||
Object(sdbus::internal::IConnection& connection, std::string objectPath);
|
||||
Object(sdbus::internal::IConnection& connection, ObjectPath objectPath);
|
||||
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback ) override;
|
||||
void addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) override;
|
||||
Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) override;
|
||||
void unregister() override;
|
||||
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) override;
|
||||
Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const override;
|
||||
Signal createSignal(const char* interfaceName, const char* signalName) const override;
|
||||
void emitSignal(const sdbus::Signal& message) override;
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames) override;
|
||||
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames) override;
|
||||
void emitPropertiesChangedSignal(const InterfaceName& interfaceName) override;
|
||||
void emitPropertiesChangedSignal(const char* interfaceName) override;
|
||||
void emitInterfacesAddedSignal() override;
|
||||
void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces) override;
|
||||
void emitInterfacesRemovedSignal() override;
|
||||
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces) override;
|
||||
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback ) override;
|
||||
void addObjectManager() override;
|
||||
[[nodiscard]] Slot addObjectManager(return_slot_t) override;
|
||||
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback ) override;
|
||||
|
||||
void finishRegistration() override;
|
||||
|
||||
sdbus::Message createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Message& message) override;
|
||||
[[nodiscard]] sdbus::IConnection& getConnection() const override;
|
||||
[[nodiscard]] const ObjectPath& getObjectPath() const override;
|
||||
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
|
||||
|
||||
private:
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
// A vtable record comprising methods, signals, properties, flags.
|
||||
// Once created, it cannot be modified. Only new vtables records can be added.
|
||||
// An interface can have any number of vtables attached to it, not only one.
|
||||
struct VTable
|
||||
{
|
||||
using MethodName = std::string;
|
||||
struct MethodData
|
||||
{
|
||||
std::string inputArgs_;
|
||||
std::string outputArgs_;
|
||||
method_callback callback_;
|
||||
};
|
||||
std::map<MethodName, MethodData> methods_;
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
{
|
||||
std::string signature_;
|
||||
};
|
||||
std::map<SignalName, SignalData> signals_;
|
||||
using PropertyName = std::string;
|
||||
struct PropertyData
|
||||
{
|
||||
std::string signature_;
|
||||
property_get_callback getCallback_;
|
||||
property_set_callback setCallback_;
|
||||
};
|
||||
std::map<PropertyName, PropertyData> properties_;
|
||||
std::vector<sd_bus_vtable> vtable_;
|
||||
InterfaceName interfaceName;
|
||||
Flags interfaceFlags;
|
||||
|
||||
std::unique_ptr<void, std::function<void(void*)>> slot_;
|
||||
struct MethodItem
|
||||
{
|
||||
MethodName name;
|
||||
Signature inputSignature;
|
||||
Signature outputSignature;
|
||||
std::string paramNames;
|
||||
method_callback callback;
|
||||
Flags flags;
|
||||
};
|
||||
// Array of method records sorted by method name
|
||||
std::vector<MethodItem> methods;
|
||||
|
||||
struct SignalItem
|
||||
{
|
||||
SignalName name;
|
||||
Signature signature;
|
||||
std::string paramNames;
|
||||
Flags flags;
|
||||
};
|
||||
// Array of signal records sorted by signal name
|
||||
std::vector<SignalItem> signals;
|
||||
|
||||
struct PropertyItem
|
||||
{
|
||||
PropertyName name;
|
||||
Signature signature;
|
||||
property_get_callback getCallback;
|
||||
property_set_callback setCallback;
|
||||
Flags flags;
|
||||
};
|
||||
// Array of signal records sorted by signal name
|
||||
std::vector<PropertyItem> properties;
|
||||
|
||||
// VTable structure in format required by sd-bus API
|
||||
std::vector<sd_bus_vtable> sdbusVTable;
|
||||
|
||||
// Back-reference to the owning object from sd-bus callback handlers
|
||||
Object* object{};
|
||||
|
||||
// This is intentionally the last member, because it must be destructed first,
|
||||
// releasing callbacks above before the callbacks themselves are destructed.
|
||||
Slot slot;
|
||||
};
|
||||
|
||||
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData);
|
||||
static void registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
static void registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
static void registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
void activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable );
|
||||
VTable createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable);
|
||||
void writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable);
|
||||
void writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable);
|
||||
void writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable);
|
||||
void writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable);
|
||||
|
||||
std::vector<sd_bus_vtable> createInternalSdBusVTable(const VTable& vtable);
|
||||
static void startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable);
|
||||
static void writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable);
|
||||
static void writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable);
|
||||
static void writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable);
|
||||
static void finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable);
|
||||
|
||||
static const VTable::MethodItem* findMethod(const VTable& vtable, std::string_view methodName);
|
||||
static const VTable::PropertyItem* findProperty(const VTable& vtable, std::string_view propertyName);
|
||||
|
||||
static std::string paramNamesToString(const std::vector<std::string>& paramNames);
|
||||
|
||||
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_property_get_callback( sd_bus *bus
|
||||
@ -127,11 +161,11 @@ namespace internal {
|
||||
|
||||
private:
|
||||
sdbus::internal::IConnection& connection_;
|
||||
std::string objectPath_;
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
ObjectPath objectPath_;
|
||||
std::vector<Slot> vtables_;
|
||||
Slot objectManagerSlot_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
Message ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
|
||||
{
|
||||
// Tell, don't ask
|
||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||
}
|
||||
|
||||
Message ObjectProxy::callMethod(const Message& 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*/)
|
||||
{
|
||||
Message message(sdbusMessage, Message::Type::eSignal);
|
||||
|
||||
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();
|
||||
|
||||
Message createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
Message callMethod(const Message& 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_ */
|
443
src/Proxy.cpp
Normal file
443
src/Proxy.cpp
Normal file
@ -0,0 +1,443 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 "sdbus-c++/Error.h"
|
||||
#include "sdbus-c++/IConnection.h"
|
||||
#include "sdbus-c++/Message.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
#include "MessageUtils.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include SDBUS_HEADER
|
||||
#include <utility>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
Proxy::Proxy(sdbus::internal::IConnection& connection, ServiceName destination, ObjectPath objectPath)
|
||||
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
|
||||
// The connection is not ours only, it is owned and managed by the user and we just reference
|
||||
// it here, so we expect the client to manage the event loop upon this connection themselves.
|
||||
}
|
||||
|
||||
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
: connection_(std::move(connection))
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
|
||||
// The connection is ours only, i.e. it's us who has to manage the event loop upon this connection,
|
||||
// in order that we get and process signals, async call replies, and other messages from D-Bus.
|
||||
connection_->enterEventLoopAsync();
|
||||
}
|
||||
|
||||
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t )
|
||||
: connection_(std::move(connection))
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
|
||||
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
|
||||
|
||||
// Even though the connection is ours only, we don't start an event loop thread.
|
||||
// This proxy is meant to be created, used for simple synchronous D-Bus call(s) and then dismissed.
|
||||
}
|
||||
|
||||
MethodCall Proxy::createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const
|
||||
{
|
||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||
}
|
||||
|
||||
MethodCall Proxy::createMethodCall(const char* interfaceName, const char* methodName) const
|
||||
{
|
||||
return connection_->createMethodCall(destination_.c_str(), objectPath_.c_str(), interfaceName, methodName);
|
||||
}
|
||||
|
||||
MethodReply Proxy::callMethod(const MethodCall& message)
|
||||
{
|
||||
return Proxy::callMethod(message, /*timeout*/ 0);
|
||||
}
|
||||
|
||||
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
|
||||
|
||||
return message.send(timeout);
|
||||
}
|
||||
|
||||
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback)
|
||||
{
|
||||
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0);
|
||||
}
|
||||
|
||||
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, return_slot_t)
|
||||
{
|
||||
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0, return_slot);
|
||||
}
|
||||
|
||||
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
|
||||
|
||||
auto asyncCallInfo = std::make_shared<AsyncCallInfo>(AsyncCallInfo{ .callback = std::move(asyncReplyCallback)
|
||||
, .proxy = *this
|
||||
, .floating = false });
|
||||
|
||||
asyncCallInfo->slot = message.send((void*)&Proxy::sdbus_async_reply_handler, asyncCallInfo.get(), timeout, return_slot);
|
||||
|
||||
auto asyncCallInfoWeakPtr = std::weak_ptr{asyncCallInfo};
|
||||
|
||||
floatingAsyncCallSlots_.push_back(std::move(asyncCallInfo));
|
||||
|
||||
return {asyncCallInfoWeakPtr};
|
||||
}
|
||||
|
||||
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout, return_slot_t)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
|
||||
|
||||
auto asyncCallInfo = std::make_unique<AsyncCallInfo>(AsyncCallInfo{ .callback = std::move(asyncReplyCallback)
|
||||
, .proxy = *this
|
||||
, .floating = true });
|
||||
|
||||
asyncCallInfo->slot = message.send((void*)&Proxy::sdbus_async_reply_handler, asyncCallInfo.get(), timeout, return_slot);
|
||||
|
||||
return {asyncCallInfo.release(), [](void *ptr){ delete static_cast<AsyncCallInfo*>(ptr); }};
|
||||
}
|
||||
|
||||
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, with_future_t)
|
||||
{
|
||||
return Proxy::callMethodAsync(message, {}, with_future);
|
||||
}
|
||||
|
||||
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t)
|
||||
{
|
||||
auto promise = std::make_shared<std::promise<MethodReply>>();
|
||||
auto future = promise->get_future();
|
||||
|
||||
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, std::optional<Error> error) noexcept
|
||||
{
|
||||
if (!error)
|
||||
promise->set_value(std::move(reply));
|
||||
else
|
||||
promise->set_exception(std::make_exception_ptr(*std::move(error)));
|
||||
};
|
||||
|
||||
(void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout);
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
void Proxy::registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler )
|
||||
{
|
||||
Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler));
|
||||
}
|
||||
|
||||
void Proxy::registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler )
|
||||
{
|
||||
auto slot = Proxy::registerSignalHandler(interfaceName, signalName, std::move(signalHandler), return_slot);
|
||||
|
||||
floatingSignalSlots_.push_back(std::move(slot));
|
||||
}
|
||||
|
||||
Slot Proxy::registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t )
|
||||
{
|
||||
return Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler), return_slot);
|
||||
}
|
||||
|
||||
Slot Proxy::registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t )
|
||||
{
|
||||
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
|
||||
SDBUS_CHECK_MEMBER_NAME(signalName);
|
||||
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
|
||||
|
||||
auto signalInfo = std::make_unique<SignalInfo>(SignalInfo{std::move(signalHandler), *this, {}});
|
||||
|
||||
signalInfo->slot = connection_->registerSignalHandler( destination_.c_str()
|
||||
, objectPath_.c_str()
|
||||
, interfaceName
|
||||
, signalName
|
||||
, &Proxy::sdbus_signal_handler
|
||||
, signalInfo.get()
|
||||
, return_slot );
|
||||
|
||||
return {signalInfo.release(), [](void *ptr){ delete static_cast<SignalInfo*>(ptr); }};
|
||||
}
|
||||
|
||||
void Proxy::unregister()
|
||||
{
|
||||
floatingAsyncCallSlots_.clear();
|
||||
floatingSignalSlots_.clear();
|
||||
}
|
||||
|
||||
sdbus::IConnection& Proxy::getConnection() const
|
||||
{
|
||||
return *connection_;
|
||||
}
|
||||
|
||||
const ObjectPath& Proxy::getObjectPath() const
|
||||
{
|
||||
return objectPath_;
|
||||
}
|
||||
|
||||
Message Proxy::getCurrentlyProcessedMessage() const
|
||||
{
|
||||
return connection_->getCurrentlyProcessedMessage();
|
||||
}
|
||||
|
||||
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* asyncCallInfo = static_cast<AsyncCallInfo*>(userData);
|
||||
assert(asyncCallInfo != nullptr);
|
||||
assert(asyncCallInfo->callback);
|
||||
auto& proxy = asyncCallInfo->proxy;
|
||||
|
||||
// We are removing the CallData item at the complete scope exit, after the callback has been invoked.
|
||||
// We can't do it earlier (before callback invocation for example), because CallBack data (slot release)
|
||||
// is the synchronization point between callback invocation and Proxy::unregister.
|
||||
SCOPE_EXIT
|
||||
{
|
||||
proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
|
||||
};
|
||||
|
||||
auto message = Message::Factory::create<MethodReply>(sdbusMessage, proxy.connection_.get());
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&]
|
||||
{
|
||||
const auto* error = sd_bus_message_get_error(sdbusMessage);
|
||||
if (error == nullptr)
|
||||
{
|
||||
asyncCallInfo->callback(std::move(message), {});
|
||||
}
|
||||
else
|
||||
{
|
||||
Error exception(Error::Name{error->name}, error->message);
|
||||
asyncCallInfo->callback(std::move(message), std::move(exception));
|
||||
}
|
||||
}, retError);
|
||||
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
auto* signalInfo = static_cast<SignalInfo*>(userData);
|
||||
assert(signalInfo != nullptr);
|
||||
assert(signalInfo->callback);
|
||||
|
||||
auto message = Message::Factory::create<Signal>(sdbusMessage, signalInfo->proxy.connection_.get());
|
||||
|
||||
auto ok = invokeHandlerAndCatchErrors([&](){ signalInfo->callback(std::move(message)); }, retError);
|
||||
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
Proxy::FloatingAsyncCallSlots::~FloatingAsyncCallSlots()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void Proxy::FloatingAsyncCallSlots::push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo)
|
||||
{
|
||||
std::lock_guard lock(mutex_);
|
||||
if (!asyncCallInfo->finished) // The call may have finished in the meantime
|
||||
slots_.emplace_back(std::move(asyncCallInfo));
|
||||
}
|
||||
|
||||
void Proxy::FloatingAsyncCallSlots::erase(AsyncCallInfo* info)
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
info->finished = true;
|
||||
auto it = std::find_if(slots_.begin(), slots_.end(), [info](auto const& entry){ return entry.get() == info; });
|
||||
if (it != slots_.end())
|
||||
{
|
||||
auto callInfo = std::move(*it);
|
||||
slots_.erase(it);
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `removeCall` is called by some
|
||||
// thread and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
}
|
||||
|
||||
void Proxy::FloatingAsyncCallSlots::clear()
|
||||
{
|
||||
std::unique_lock lock(mutex_);
|
||||
auto asyncCallSlots = std::move(slots_);
|
||||
slots_ = {};
|
||||
lock.unlock();
|
||||
|
||||
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
|
||||
// out of the `mutex_' critical section here, because if the `clear` is called by some thread
|
||||
// and at the same time Proxy's async reply handler (which already holds global sd-bus
|
||||
// mutex) is in progress in a different thread, we get double-mutex deadlock.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callInfo)
|
||||
: callInfo_(std::move(callInfo))
|
||||
{
|
||||
}
|
||||
|
||||
void PendingAsyncCall::cancel()
|
||||
{
|
||||
if (auto ptr = callInfo_.lock(); ptr != nullptr)
|
||||
{
|
||||
auto* asyncCallInfo = static_cast<internal::Proxy::AsyncCallInfo*>(ptr.get());
|
||||
asyncCallInfo->proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
|
||||
|
||||
// At this point, the callData item is being deleted, leading to the release of the
|
||||
// sd-bus slot pointer. This release locks the global sd-bus mutex. If the async
|
||||
// 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 !callInfo_.expired();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath 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
|
||||
, ServiceName destination
|
||||
, ObjectPath 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::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
connection.release();
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath)
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createLightWeightProxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
return createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
|
||||
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) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t )
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
|
||||
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
|
||||
assert(sdbusConnection != nullptr);
|
||||
|
||||
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath)
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath)
|
||||
{
|
||||
return createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
}
|
147
src/Proxy.h
Normal file
147
src/Proxy.h
Normal file
@ -0,0 +1,147 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 "sdbus-c++/Types.h"
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include SDBUS_HEADER
|
||||
#include <vector>
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
class Proxy
|
||||
: public IProxy
|
||||
{
|
||||
public:
|
||||
Proxy( sdbus::internal::IConnection& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const override;
|
||||
MethodCall createMethodCall(const char* interfaceName, const char* methodName) const override;
|
||||
MethodReply callMethod(const MethodCall& message) override;
|
||||
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
|
||||
PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) override;
|
||||
Slot callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, return_slot_t ) override;
|
||||
PendingAsyncCall callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, uint64_t timeout ) override;
|
||||
Slot callMethodAsync( const MethodCall& message
|
||||
, async_reply_handler asyncReplyCallback
|
||||
, uint64_t timeout
|
||||
, return_slot_t ) override;
|
||||
std::future<MethodReply> callMethodAsync(const MethodCall& message, with_future_t) override;
|
||||
std::future<MethodReply> callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t) override;
|
||||
|
||||
void registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler ) override;
|
||||
void registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler ) override;
|
||||
Slot registerSignalHandler( const InterfaceName& interfaceName
|
||||
, const SignalName& signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t ) override;
|
||||
Slot registerSignalHandler( const char* interfaceName
|
||||
, const char* signalName
|
||||
, signal_handler signalHandler
|
||||
, return_slot_t ) override;
|
||||
void unregister() override;
|
||||
|
||||
[[nodiscard]] sdbus::IConnection& getConnection() const override;
|
||||
[[nodiscard]] const ObjectPath& getObjectPath() const override;
|
||||
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
|
||||
|
||||
private:
|
||||
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
friend PendingAsyncCall;
|
||||
|
||||
std::unique_ptr< sdbus::internal::IConnection
|
||||
, std::function<void(sdbus::internal::IConnection*)>
|
||||
> connection_;
|
||||
ServiceName destination_;
|
||||
ObjectPath objectPath_;
|
||||
|
||||
std::vector<Slot> floatingSignalSlots_;
|
||||
|
||||
struct SignalInfo
|
||||
{
|
||||
signal_handler callback;
|
||||
Proxy& proxy;
|
||||
Slot slot;
|
||||
};
|
||||
|
||||
struct AsyncCallInfo
|
||||
{
|
||||
async_reply_handler callback;
|
||||
Proxy& proxy;
|
||||
Slot slot{};
|
||||
bool finished{false};
|
||||
bool floating;
|
||||
};
|
||||
|
||||
// Container keeping track of pending async calls
|
||||
class FloatingAsyncCallSlots
|
||||
{
|
||||
public:
|
||||
~FloatingAsyncCallSlots();
|
||||
void push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo);
|
||||
void erase(AsyncCallInfo* info);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::deque<std::shared_ptr<AsyncCallInfo>> slots_;
|
||||
};
|
||||
|
||||
FloatingAsyncCallSlots floatingAsyncCallSlots_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_PROXY_H_ */
|
129
src/ScopeGuard.h
129
src/ScopeGuard.h
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file ScopeGuard.h
|
||||
*
|
||||
@ -26,6 +27,7 @@
|
||||
#ifndef SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
|
||||
#define SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
|
||||
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
|
||||
// Straightforward, modern, easy-to-use RAII utility to perform work on scope exit in an exception-safe manner.
|
||||
@ -54,78 +56,115 @@
|
||||
// return; // exiting scope normally
|
||||
// }
|
||||
|
||||
#define SCOPE_EXIT \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::skybase::utils::detail::ScopeGuardOnExit() + [&]() \
|
||||
#define SCOPE_EXIT \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::sdbus::internal::ScopeGuardOnExitTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::sdbus::internal::ScopeGuardOnExitTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_SUCCESS \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::sdbus::internal::ScopeGuardOnExitSuccessTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_SUCCESS_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::sdbus::internal::ScopeGuardOnExitSuccessTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_FAILURE \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::sdbus::internal::ScopeGuardOnExitFailureTag{} + [&]() \
|
||||
/**/
|
||||
#define SCOPE_EXIT_FAILURE_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::sdbus::internal::ScopeGuardOnExitFailureTag{} + [&]() \
|
||||
/**/
|
||||
|
||||
#define SCOPE_EXIT_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::skybase::utils::detail::ScopeGuardOnExit() + [&]() \
|
||||
/**/
|
||||
namespace sdbus::internal {
|
||||
|
||||
namespace skybase {
|
||||
namespace utils {
|
||||
struct ScopeGuardOnExitTag
|
||||
{
|
||||
static bool holds(int /*originalExceptions*/)
|
||||
{
|
||||
return true; // Always holds
|
||||
}
|
||||
};
|
||||
struct ScopeGuardOnExitSuccessTag
|
||||
{
|
||||
static bool holds(int originalExceptions)
|
||||
{
|
||||
return originalExceptions == std::uncaught_exceptions(); // Only holds when no exception occurred within the scope
|
||||
}
|
||||
};
|
||||
struct ScopeGuardOnExitFailureTag
|
||||
{
|
||||
static bool holds(int originalExceptions)
|
||||
{
|
||||
return originalExceptions != std::uncaught_exceptions(); // Only holds when an exception occurred within the scope
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Fun>
|
||||
template <class _Fun, typename _Tag>
|
||||
class ScopeGuard
|
||||
{
|
||||
_Fun fnc_;
|
||||
bool active_;
|
||||
|
||||
public:
|
||||
ScopeGuard(_Fun f)
|
||||
: fnc_(std::move(f))
|
||||
, active_(true)
|
||||
ScopeGuard(_Fun f) : fnc_(std::move(f))
|
||||
{
|
||||
}
|
||||
~ScopeGuard()
|
||||
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
ScopeGuard(ScopeGuard&& rhs) : fnc_(std::move(rhs.fnc_)), active_(rhs.active_), exceptions_(rhs.exceptions_)
|
||||
{
|
||||
if (active_)
|
||||
fnc_();
|
||||
rhs.dismiss();
|
||||
}
|
||||
|
||||
void dismiss()
|
||||
{
|
||||
active_ = false;
|
||||
}
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
ScopeGuard(ScopeGuard&& rhs)
|
||||
: fnc_(std::move(rhs.fnc_))
|
||||
, active_(rhs.active_)
|
||||
|
||||
~ScopeGuard()
|
||||
{
|
||||
rhs.dismiss();
|
||||
if (active_ && _Tag::holds(exceptions_))
|
||||
fnc_();
|
||||
}
|
||||
|
||||
private:
|
||||
_Fun fnc_;
|
||||
int exceptions_{std::uncaught_exceptions()};
|
||||
bool active_{true};
|
||||
};
|
||||
|
||||
namespace detail
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitTag> operator+(ScopeGuardOnExitTag, _Fun&& fnc)
|
||||
{
|
||||
enum class ScopeGuardOnExit
|
||||
{
|
||||
};
|
||||
|
||||
// Helper function to auto-deduce type of the callable entity
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun> operator+(ScopeGuardOnExit, _Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitTag>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
|
||||
}}
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag> operator+(ScopeGuardOnExitSuccessTag, _Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun, ScopeGuardOnExitFailureTag> operator+(ScopeGuardOnExitFailureTag, _Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun, ScopeGuardOnExitFailureTag>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define CONCATENATE_IMPL(s1, s2) s1##s2
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
|
||||
#ifdef __COUNTER__
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str, __COUNTER__) \
|
||||
/**/
|
||||
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __COUNTER__)
|
||||
#else
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str, __LINE__) \
|
||||
/**/
|
||||
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
|
||||
#endif
|
||||
|
||||
#endif /* SDBUS_CPP_INTERNAL_SCOPEGUARD_H_ */
|
||||
|
514
src/SdBus.cpp
Normal file
514
src/SdBus.cpp
Normal file
@ -0,0 +1,514 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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>
|
||||
#include <algorithm>
|
||||
|
||||
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_);
|
||||
|
||||
auto r = ::sd_bus_send(bus, m, cookie);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
|
||||
{
|
||||
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_);
|
||||
|
||||
auto r = ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
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 Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Setting general method call timeout not supported by underlying version of libsystemd");
|
||||
#endif
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_get_method_call_timeout(bus, ret);
|
||||
#else
|
||||
(void)bus;
|
||||
(void)ret;
|
||||
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Getting general method call timeout not supported by underlying version of libsystemd");
|
||||
#endif
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
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_system(sd_bus **ret)
|
||||
{
|
||||
return ::sd_bus_open_system(ret);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_user(sd_bus **ret)
|
||||
{
|
||||
return ::sd_bus_open_user(ret);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_user_with_address(sd_bus **ret, const char* address)
|
||||
{
|
||||
sd_bus* bus = nullptr;
|
||||
|
||||
int r = ::sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_address(bus, address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_bus_client(bus, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// Copying behavior from
|
||||
// https://github.com/systemd/systemd/blob/fee6441601c979165ebcbb35472036439f8dad5f/src/libsystemd/sd-bus/sd-bus.c#L1381
|
||||
// Here, we make the bus as trusted
|
||||
r = ::sd_bus_set_trusted(bus, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_start(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_direct(sd_bus **ret, const char* address)
|
||||
{
|
||||
sd_bus* bus = nullptr;
|
||||
|
||||
int r = ::sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_address(bus, address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_start(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_direct(sd_bus **ret, int fd)
|
||||
{
|
||||
sd_bus* bus = nullptr;
|
||||
|
||||
int r = ::sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_fd(bus, fd, fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_start(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
|
||||
{
|
||||
sd_bus* bus = nullptr;
|
||||
|
||||
int r = ::sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_fd(bus, fd, fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
sd_id128_t id;
|
||||
r = ::sd_id128_randomize(&id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_set_server(bus, true, id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = ::sd_bus_start(bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
|
||||
{
|
||||
#ifndef SDBUS_basu
|
||||
return ::sd_bus_open_system_remote(ret, host);
|
||||
#else
|
||||
(void)ret;
|
||||
(void)host;
|
||||
// https://git.sr.ht/~emersion/basu/commit/01d33b244eb6
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_add_match_async(bus, slot, match, callback, install_callback, userdata);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_match_signal(bus, ret, sender, path, interface, member, callback, userdata);
|
||||
}
|
||||
|
||||
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_slot_unref(slot);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_new(sd_bus **ret)
|
||||
{
|
||||
return ::sd_bus_new(ret);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_start(sd_bus *bus)
|
||||
{
|
||||
return ::sd_bus_start(bus);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_process(bus, r);
|
||||
}
|
||||
|
||||
sd_bus_message* SdBus::sd_bus_get_current_message(sd_bus *bus)
|
||||
{
|
||||
return ::sd_bus_get_current_message(bus);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
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_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
auto r1 = ::sd_bus_get_n_queued_read(bus, read);
|
||||
auto r2 = ::sd_bus_get_n_queued_write(bus, write);
|
||||
|
||||
return std::min(r1, r2);
|
||||
}
|
||||
|
||||
int SdBus::sd_bus_flush(sd_bus *bus)
|
||||
{
|
||||
return ::sd_bus_flush(bus);
|
||||
}
|
||||
|
||||
sd_bus* SdBus::sd_bus_flush_close_unref(sd_bus *bus)
|
||||
{
|
||||
return ::sd_bus_flush_close_unref(bus);
|
||||
}
|
||||
|
||||
sd_bus* SdBus::sd_bus_close_unref(sd_bus *bus)
|
||||
{
|
||||
#if LIBSYSTEMD_VERSION>=241
|
||||
return ::sd_bus_close_unref(bus);
|
||||
#else
|
||||
::sd_bus_close(bus);
|
||||
return ::sd_bus_unref(bus);
|
||||
#endif
|
||||
}
|
||||
|
||||
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_ref(sd_bus_creds *c)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
return ::sd_bus_creds_ref(c);
|
||||
}
|
||||
|
||||
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *c)
|
||||
{
|
||||
std::lock_guard lock(sdbusMutex_);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
110
src/SdBus.h
Normal file
110
src/SdBus.h
Normal file
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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_system(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_user(sd_bus **ret) override;
|
||||
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
|
||||
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override;
|
||||
virtual int sd_bus_open_direct(sd_bus **ret, const char* address) override;
|
||||
virtual int sd_bus_open_direct(sd_bus **ret, int fd) override;
|
||||
virtual int sd_bus_open_server(sd_bus **ret, int fd) override;
|
||||
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
|
||||
virtual int sd_bus_release_name(sd_bus *bus, const char *name) override;
|
||||
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
|
||||
virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
|
||||
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
|
||||
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
|
||||
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) override;
|
||||
virtual int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata) override;
|
||||
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;
|
||||
|
||||
virtual int sd_bus_new(sd_bus **ret) override;
|
||||
virtual int sd_bus_start(sd_bus *bus) override;
|
||||
|
||||
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override;
|
||||
virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) override;
|
||||
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
|
||||
virtual int sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) override;
|
||||
virtual int sd_bus_flush(sd_bus *bus) override;
|
||||
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override;
|
||||
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) override;
|
||||
|
||||
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) override;
|
||||
|
||||
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) override;
|
||||
virtual sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *c) override;
|
||||
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) override;
|
||||
|
||||
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) override;
|
||||
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 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file Types.cpp
|
||||
*
|
||||
@ -23,13 +24,18 @@
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
namespace sdbus { /*namespace internal {*/
|
||||
#include "sdbus-c++/Error.h"
|
||||
|
||||
#include "MessageUtils.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <system_error>
|
||||
#include SDBUS_HEADER
|
||||
#include <unistd.h>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
Variant::Variant()
|
||||
: msg_(createPlainMessage())
|
||||
@ -49,11 +55,10 @@ void Variant::deserializeFrom(Message& msg)
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
std::string Variant::peekValueType() const
|
||||
const char* Variant::peekValueType() const
|
||||
{
|
||||
std::string type;
|
||||
std::string contents;
|
||||
msg_.peekType(type, contents);
|
||||
msg_.rewind(false);
|
||||
auto [type, contents] = msg_.peekType();
|
||||
return contents;
|
||||
}
|
||||
|
||||
@ -62,4 +67,27 @@ bool Variant::isEmpty() const
|
||||
return msg_.isEmpty();
|
||||
}
|
||||
|
||||
void UnixFd::close() // NOLINT(readability-make-member-function-const)
|
||||
{
|
||||
if (fd_ >= 0)
|
||||
{
|
||||
::close(fd_);
|
||||
}
|
||||
}
|
||||
|
||||
int UnixFd::checkedDup(int fd)
|
||||
{
|
||||
if (fd < 0)
|
||||
{
|
||||
return fd;
|
||||
}
|
||||
|
||||
int ret = ::dup(fd); // NOLINT(android-cloexec-dup) // TODO: verify
|
||||
if (ret < 0)
|
||||
{
|
||||
throw std::system_error(errno, std::generic_category(), "dup failed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace sdbus
|
||||
|
98
src/Utils.h
Normal file
98
src/Utils.h
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 SDBUS_HEADER
|
||||
|
||||
#if LIBSYSTEMD_VERSION>=246
|
||||
#define SDBUS_CHECK_OBJECT_PATH(_PATH) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_object_path_is_valid(_PATH), std::string("Invalid object path '") + _PATH + "' provided", EINVAL) \
|
||||
/**/
|
||||
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME), std::string("Invalid interface name '") + _NAME + "' provided", EINVAL) \
|
||||
/**/
|
||||
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(*_NAME && !sd_bus_service_name_is_valid(_NAME), std::string("Invalid service name '") + _NAME + "' provided", EINVAL) \
|
||||
/**/
|
||||
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
|
||||
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME), std::string("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
|
||||
|
||||
namespace sdbus::internal {
|
||||
|
||||
template <typename _Callable>
|
||||
bool invokeHandlerAndCatchErrors(_Callable callable, sd_bus_error *retError)
|
||||
{
|
||||
try
|
||||
{
|
||||
callable();
|
||||
}
|
||||
catch (const Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), e.what());
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), "Unknown error occurred");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns time since epoch based of POSIX CLOCK_MONOTONIC,
|
||||
// so we use the very same clock as underlying sd-bus library.
|
||||
[[nodiscard]] inline auto now()
|
||||
{
|
||||
struct timespec ts{};
|
||||
auto r = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "clock_gettime failed: ", -errno);
|
||||
|
||||
return std::chrono::nanoseconds(ts.tv_nsec) + std::chrono::seconds(ts.tv_sec);
|
||||
}
|
||||
|
||||
// Implementation of the overload pattern for variant visitation
|
||||
template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
|
||||
template <class... Ts> overload(Ts...) -> overload<Ts...>;
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_UTILS_H_ */
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableUtils.c
|
||||
*
|
||||
@ -24,48 +25,80 @@
|
||||
*/
|
||||
|
||||
#include "VTableUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include SDBUS_HEADER
|
||||
|
||||
sd_bus_vtable createVTableStartItem()
|
||||
sd_bus_vtable createSdBusVTableStartItem(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 )
|
||||
sd_bus_vtable createSdBusVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags )
|
||||
{
|
||||
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 )
|
||||
sd_bus_vtable createSdBusVTableSignalItem( const char *member
|
||||
, 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_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, 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_vtable createSdBusVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, 0);
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, flags);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableEndItem()
|
||||
sd_bus_vtable createSdBusVTableEndItem()
|
||||
{
|
||||
struct sd_bus_vtable vtableEnd = SD_BUS_VTABLE_END;
|
||||
return vtableEnd;
|
||||
|
@ -1,5 +1,6 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
|
||||
*
|
||||
* @file VTableUtils.h
|
||||
*
|
||||
@ -26,27 +27,34 @@
|
||||
#ifndef SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
|
||||
#define SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <stdbool.h>
|
||||
#include SDBUS_HEADER
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
sd_bus_vtable createVTableStartItem();
|
||||
sd_bus_vtable createVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, sd_bus_message_handler_t handler );
|
||||
sd_bus_vtable createVTableSignalItem( const char *member
|
||||
, const char *signature );
|
||||
sd_bus_vtable createVTablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter );
|
||||
sd_bus_vtable createVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter );
|
||||
sd_bus_vtable createVTableEndItem();
|
||||
sd_bus_vtable createSdBusVTableStartItem(uint64_t flags);
|
||||
sd_bus_vtable createSdBusVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, const char *paramNames
|
||||
, sd_bus_message_handler_t handler
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableSignalItem( const char *member
|
||||
, const char *signature
|
||||
, const char *outnames
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter
|
||||
, uint64_t flags );
|
||||
sd_bus_vtable createSdBusVTableEndItem();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,244 +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");
|
||||
|
||||
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);
|
||||
|
||||
registrationSS << tab << tab << "object_.registerMethod(\""
|
||||
<< methodName << "\")"
|
||||
<< ".onInterface(interfaceName)"
|
||||
<< ".implementedAs("
|
||||
<< "[this]("
|
||||
<< argTypeStr
|
||||
<< "){ return this->" << methodName << "("
|
||||
<< argStr << "); });" << endl;
|
||||
|
||||
declarationSS << tab
|
||||
<< "virtual " << outArgsToType(outArgs) << " " << methodName
|
||||
<< "(" << 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.3)
|
||||
|
||||
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,68 +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++14
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
libsdbus_c___integrationtests_LDADD = \
|
||||
-lsdbus-c++ \
|
||||
@libsdbus_cpp_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
-lgmock \
|
||||
-lpthread
|
||||
|
||||
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,289 +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>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
|
||||
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);
|
||||
usleep(50000); // 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, 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,145 +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"
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,156 +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("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 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,46 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file defs.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_DEFS_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_
|
||||
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
const std::string INTERFACE_NAME{"com.kistler.testsdbuscpp"};
|
||||
const std::string OBJECT_PATH{"/"};
|
||||
|
||||
constexpr const uint8_t UINT8_VALUE{1};
|
||||
constexpr const int16_t INT16_VALUE{21};
|
||||
constexpr const uint32_t UINT32_VALUE{42};
|
||||
constexpr const int32_t INT32_VALUE{-42};
|
||||
constexpr const int32_t INT64_VALUE{-1024};
|
||||
|
||||
const std::string STRING_VALUE{"sdbus-c++-testing"};
|
||||
const sdbus::Signature SIGNATURE_VALUE{"a{is}"};
|
||||
const sdbus::ObjectPath OBJECT_PATH_VALUE{"/"};
|
||||
|
||||
constexpr const double DOUBLE_VALUE{3.24L};
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ */
|
@ -1,190 +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;
|
||||
}
|
||||
|
||||
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_ */
|
@ -1,230 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Message_test.cpp
|
||||
*
|
||||
* Created on: Dec 3, 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 <sdbus-c++/Types.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::DoubleEq;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string deserializeString(sdbus::Message& msg)
|
||||
{
|
||||
std::string str;
|
||||
msg >> str;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AMessage, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(sdbus::Message());
|
||||
}
|
||||
|
||||
TEST(AMessage, IsInvalidAfterDefaultConstructed)
|
||||
{
|
||||
sdbus::Message msg;
|
||||
|
||||
ASSERT_FALSE(msg.isValid());
|
||||
}
|
||||
|
||||
TEST(AMessage, IsValidWhenConstructedAsRealMessage)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
ASSERT_TRUE(msg.isValid());
|
||||
}
|
||||
|
||||
TEST(AMessage, CreatesShallowCopyWhenCopyConstructed)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
msg << "I am a string"s;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Message msgCopy = msg;
|
||||
|
||||
std::string str;
|
||||
msgCopy >> str;
|
||||
|
||||
ASSERT_THAT(str, Eq("I am a string"));
|
||||
ASSERT_THROW(msgCopy >> str, sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(AMessage, CreatesDeepCopyWhenEplicitlyCopied)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
msg << "I am a string"s;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Message msgCopy{sdbus::createPlainMessage()};
|
||||
msg.copyTo(msgCopy, true);
|
||||
msgCopy.seal(); // Seal to be able to read from it subsequently
|
||||
msg.rewind(true); // Rewind to the beginning after copying
|
||||
|
||||
ASSERT_THAT(deserializeString(msg), Eq("I am a string"));
|
||||
ASSERT_THAT(deserializeString(msgCopy), Eq("I am a string"));
|
||||
}
|
||||
|
||||
TEST(AMessage, IsEmptyWhenContainsNoValue)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
ASSERT_TRUE(msg.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AMessage, IsNotEmptyWhenContainsAValue)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
msg << "I am a string"s;
|
||||
|
||||
ASSERT_FALSE(msg.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AMessage, ReturnsItsTypeWhenAsked)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
ASSERT_THAT(msg.getType(), Eq(sdbus::Message::Type::ePlainMessage));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryASimpleInteger)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
int dataWritten = 5;
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
int dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryAVariant)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
auto dataWritten = sdbus::Variant((double)3.14);
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Variant dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead.get<double>(), Eq(dataWritten.get<double>()));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
auto value = std::vector<sdbus::Variant>{"hello"s, (double)3.14};
|
||||
auto dataWritten = sdbus::Variant{value};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Variant dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead.get<decltype(value)>()[0].get<std::string>(), Eq(value[0].get<std::string>()));
|
||||
ASSERT_THAT(dataRead.get<decltype(value)>()[1].get<double>(), Eq(value[1].get<double>()));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryAnArray)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
std::vector<int64_t> dataWritten{3545342, 43643532, 324325};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::vector<int64_t> dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryADictionary)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::map<int, std::string> dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryAComplexType)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
using ComplexType = std::map<
|
||||
uint64_t,
|
||||
sdbus::Struct<
|
||||
std::map<
|
||||
uint8_t,
|
||||
std::vector<
|
||||
sdbus::Struct<
|
||||
sdbus::ObjectPath,
|
||||
bool,
|
||||
int16_t,
|
||||
/*sdbus::Variant,*/
|
||||
std::map<int, std::string>
|
||||
>
|
||||
>
|
||||
>,
|
||||
sdbus::Signature,
|
||||
double
|
||||
>
|
||||
>;
|
||||
|
||||
ComplexType dataWritten = { {1, {{{5, {{"/some/object", true, 45, {{6, "hello"}, {7, "world"}}}}}}, "av", 3.14}}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
ComplexType dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file TypeTraits_test.cpp
|
||||
*
|
||||
* Created on: Nov 27, 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 <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
|
||||
using ::testing::Eq;
|
||||
|
||||
namespace
|
||||
{
|
||||
// ---
|
||||
// FIXTURE DEFINITION FOR TYPED TESTS
|
||||
// ----
|
||||
|
||||
template <typename _T>
|
||||
class Type2DBusTypeSignatureConversion
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
const std::string dbusTypeSignature_{getDBusTypeSignature()};
|
||||
private:
|
||||
static std::string getDBusTypeSignature();
|
||||
};
|
||||
|
||||
#define TYPE(...) \
|
||||
template <> \
|
||||
std::string Type2DBusTypeSignatureConversion<__VA_ARGS__>::getDBusTypeSignature() \
|
||||
/**/
|
||||
#define HAS_DBUS_TYPE_SIGNATURE(_SIG) \
|
||||
{ \
|
||||
return (_SIG); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
TYPE(bool)HAS_DBUS_TYPE_SIGNATURE("b")
|
||||
TYPE(uint8_t)HAS_DBUS_TYPE_SIGNATURE("y")
|
||||
TYPE(int16_t)HAS_DBUS_TYPE_SIGNATURE("n")
|
||||
TYPE(uint16_t)HAS_DBUS_TYPE_SIGNATURE("q")
|
||||
TYPE(int32_t)HAS_DBUS_TYPE_SIGNATURE("i")
|
||||
TYPE(uint32_t)HAS_DBUS_TYPE_SIGNATURE("u")
|
||||
TYPE(int64_t)HAS_DBUS_TYPE_SIGNATURE("x")
|
||||
TYPE(uint64_t)HAS_DBUS_TYPE_SIGNATURE("t")
|
||||
TYPE(double)HAS_DBUS_TYPE_SIGNATURE("d")
|
||||
TYPE(const char*)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(std::string)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(sdbus::ObjectPath)HAS_DBUS_TYPE_SIGNATURE("o")
|
||||
TYPE(sdbus::Signature)HAS_DBUS_TYPE_SIGNATURE("g")
|
||||
TYPE(sdbus::Variant)HAS_DBUS_TYPE_SIGNATURE("v")
|
||||
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
|
||||
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
|
||||
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
|
||||
TYPE(std::map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
|
||||
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,
|
||||
const char*
|
||||
>
|
||||
>;
|
||||
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}gs)}")
|
||||
|
||||
typedef ::testing::Types< bool
|
||||
, uint8_t
|
||||
, int16_t
|
||||
, uint16_t
|
||||
, int32_t
|
||||
, uint32_t
|
||||
, int64_t
|
||||
, uint64_t
|
||||
, double
|
||||
, const char*
|
||||
, std::string
|
||||
, sdbus::ObjectPath
|
||||
, sdbus::Signature
|
||||
, sdbus::Variant
|
||||
, sdbus::Struct<bool>
|
||||
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
|
||||
, std::vector<int16_t>
|
||||
, std::map<int32_t, int64_t>
|
||||
, ComplexType
|
||||
> DBusSupportedTypes;
|
||||
|
||||
TYPED_TEST_CASE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
|
||||
{
|
||||
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Types_test.cpp
|
||||
*
|
||||
* Created on: Dec 3, 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 <sdbus-c++/Types.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
|
||||
using ::testing::Eq;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const uint64_t ANY_UINT64 = 84578348354;
|
||||
constexpr const double ANY_DOUBLE = 3.14;
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AVariant, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(sdbus::Variant());
|
||||
}
|
||||
|
||||
TEST(AVariant, ContainsNoValueAfterDefaultConstructed)
|
||||
{
|
||||
sdbus::Variant v;
|
||||
|
||||
ASSERT_TRUE(v.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeConstructedFromASimpleValue)
|
||||
{
|
||||
int value = 5;
|
||||
|
||||
ASSERT_NO_THROW(sdbus::Variant{value});
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeConstructedFromAComplexValue)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
|
||||
ASSERT_NO_THROW(sdbus::Variant(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeCopied)
|
||||
{
|
||||
auto value = "hello"s;
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
auto variantCopy1{variant};
|
||||
auto variantCopy2 = variantCopy1;
|
||||
|
||||
ASSERT_THAT(variantCopy1.get<std::string>(), Eq(value));
|
||||
ASSERT_THAT(variantCopy2.get<std::string>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, IsNotEmptyWhenContainsAValue)
|
||||
{
|
||||
sdbus::Variant v("hello");
|
||||
|
||||
ASSERT_FALSE(v.isEmpty());
|
||||
}
|
||||
|
||||
TEST(ASimpleVariant, ReturnsTheSimpleValueWhenAsked)
|
||||
{
|
||||
int value = 5;
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<int>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<decltype(value)>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
|
||||
{
|
||||
std::string value{"I am a string"};
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_TRUE(variant.containsValueOfType<ComplexType>());
|
||||
}
|
||||
|
||||
TEST(ASimpleVariant, ReturnsFalseWhenAskedIfItContainsTypeItDoesntReallyContain)
|
||||
{
|
||||
int value = 5;
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_FALSE(variant.containsValueOfType<double>());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanContainOtherEmbeddedVariants)
|
||||
{
|
||||
using TypeWithVariants = std::vector<sdbus::Struct<sdbus::Variant, double>>;
|
||||
TypeWithVariants value;
|
||||
value.emplace_back(sdbus::make_struct(sdbus::Variant("a string"), ANY_DOUBLE));
|
||||
value.emplace_back(sdbus::make_struct(sdbus::Variant(ANY_UINT64), ANY_DOUBLE));
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_TRUE(variant.containsValueOfType<TypeWithVariants>());
|
||||
}
|
||||
|
||||
TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
|
||||
{
|
||||
sdbus::Variant variant("a string");
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
|
||||
ASSERT_NO_THROW(variant.serializeTo(msg));
|
||||
}
|
||||
|
||||
TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
|
||||
{
|
||||
sdbus::Variant variant;
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
|
||||
ASSERT_THROW(variant.serializeTo(msg), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
variant.serializeTo(msg);
|
||||
msg.seal();
|
||||
sdbus::Variant variant2;
|
||||
variant2.deserializeFrom(msg);
|
||||
|
||||
ASSERT_THAT(variant2.get<decltype(value)>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
sdbus::Variant variant(value);
|
||||
auto variantCopy1{variant};
|
||||
auto variantCopy2 = variant;
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
variant.serializeTo(msg);
|
||||
variantCopy1.serializeTo(msg);
|
||||
variantCopy2.serializeTo(msg);
|
||||
msg.seal();
|
||||
sdbus::Variant receivedVariant1, receivedVariant2, receivedVariant3;
|
||||
receivedVariant1.deserializeFrom(msg);
|
||||
receivedVariant2.deserializeFrom(msg);
|
||||
receivedVariant3.deserializeFrom(msg);
|
||||
|
||||
ASSERT_THAT(receivedVariant1.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant2.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant3.get<decltype(value)>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AStruct, CreatesStructFromTuple)
|
||||
{
|
||||
std::tuple<int32_t, std::string> value{1234, "abcd"};
|
||||
sdbus::Struct<int32_t, std::string> valueStruct{value};
|
||||
|
||||
ASSERT_THAT(std::get<0>(valueStruct), Eq(std::get<0>(value)));
|
||||
ASSERT_THAT(std::get<1>(valueStruct), Eq(std::get<1>(value)));
|
||||
}
|
175
tests/CMakeLists.txt
Normal file
175
tests/CMakeLists.txt
Normal file
@ -0,0 +1,175 @@
|
||||
#-------------------------------
|
||||
# DOWNLOAD AND BUILD OF GOOGLETEST
|
||||
#-------------------------------
|
||||
|
||||
find_package(GTest ${SDBUSCPP_GOOGLETEST_VERSION} CONFIG)
|
||||
if (NOT TARGET GTest::gmock)
|
||||
# Try pkg-config if GTest was not found through CMake config
|
||||
find_package(PkgConfig)
|
||||
if (PkgConfig_FOUND)
|
||||
pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${SDBUSCPP_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)
|
||||
|
||||
if (SDBUSCPP_GOOGLETEST_VERSION VERSION_GREATER_EQUAL 1.13.0)
|
||||
set(GOOGLETEST_TAG "v${SDBUSCPP_GOOGLETEST_VERSION}")
|
||||
else()
|
||||
set(GOOGLETEST_TAG "release-${SDBUSCPP_GOOGLETEST_VERSION}")
|
||||
endif()
|
||||
|
||||
message("Manually fetching & building googletest ${GOOGLETEST_TAG}...")
|
||||
FetchContent_Declare(googletest
|
||||
GIT_REPOSITORY ${SDBUSCPP_GOOGLETEST_GIT_REPO}
|
||||
GIT_TAG ${GOOGLETEST_TAG}
|
||||
GIT_SHALLOW 1
|
||||
UPDATE_COMMAND "")
|
||||
|
||||
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
|
||||
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
FetchContent_MakeAvailable(googletest)
|
||||
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
|
||||
add_library(GTest::gmock ALIAS gmock)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#-------------------------------
|
||||
# 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}/PollData_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/Types_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/TypeTraits_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/Connection_test.cpp
|
||||
${UNITTESTS_SOURCE_DIR}/mocks/SdBusMock.h)
|
||||
|
||||
set(INTEGRATIONTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/integrationtests)
|
||||
set(INTEGRATIONTESTS_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=${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
SDBUS_${SDBUS_IMPL}
|
||||
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
|
||||
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib GTest::gmock)
|
||||
|
||||
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
|
||||
target_compile_definitions(sdbus-c++-integration-tests PRIVATE
|
||||
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
|
||||
SDBUS_${SDBUS_IMPL})
|
||||
if(NOT SDBUS_IMPL STREQUAL "basu")
|
||||
# Systemd::Libsystemd is included because integration tests use sd-event. Otherwise sdbus-c++ encapsulates and hides libsystemd.
|
||||
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ Systemd::Libsystemd GTest::gmock)
|
||||
else()
|
||||
# sd-event implementation is not part of basu, so its integration tests will be skipped
|
||||
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
|
||||
endif()
|
||||
|
||||
if(SDBUSCPP_BUILD_PERF_TESTS OR SDBUSCPP_BUILD_STRESS_TESTS)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if(SDBUSCPP_BUILD_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(SDBUSCPP_BUILD_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
|
||||
#----------------------------------
|
||||
|
||||
if(SDBUSCPP_INSTALL)
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS sdbus-c++-unit-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(TARGETS sdbus-c++-integration-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
|
||||
COMPONENT sdbus-c++-test)
|
||||
if(SDBUSCPP_BUILD_PERF_TESTS)
|
||||
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
|
||||
COMPONENT sdbus-c++-test)
|
||||
endif()
|
||||
if(SDBUSCPP_BUILD_STRESS_TESTS)
|
||||
install(TARGETS sdbus-c++-stress-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
|
||||
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf
|
||||
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
|
||||
COMPONENT sdbus-c++-test)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#----------------------------------
|
||||
# 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()
|
301
tests/integrationtests/DBusAsyncMethodsTests.cpp
Normal file
301
tests/integrationtests/DBusAsyncMethodsTests.cpp
Normal file
@ -0,0 +1,301 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::steady_clock> start;
|
||||
try
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
this->m_proxy->doOperationClientSideAsyncWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
|
||||
future.get();
|
||||
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchronousMethodAsynchronously)
|
||||
{
|
||||
// Yeah, this is kinda timing-dependent test, but times should be safe...
|
||||
std::mutex mtx;
|
||||
std::vector<uint32_t> results;
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
auto call = [&](uint32_t param)
|
||||
{
|
||||
TestProxy proxy{SERVICE_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));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
{
|
||||
std::atomic<size_t> resultCount{};
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
auto call = [&]()
|
||||
{
|
||||
TestProxy proxy{SERVICE_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));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchronousMethodWithLargeMessage)
|
||||
{
|
||||
std::map<int32_t, std::string> largeMap;
|
||||
for (int32_t i = 0; i < 40'000; ++i)
|
||||
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
|
||||
|
||||
auto result1 = this->m_proxy->doOperationAsyncWithLargeData(0, largeMap); // Sends large map back in the context of the callback (event loop thread)
|
||||
auto result2 = this->m_proxy->doOperationAsyncWithLargeData(500, largeMap); // Sends large map back outside the context of the event loop thread
|
||||
|
||||
ASSERT_THAT(result1, Eq(largeMap));
|
||||
ASSERT_THAT(result2, Eq(largeMap));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(std::move(err)));
|
||||
});
|
||||
|
||||
this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(100));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
|
||||
{
|
||||
auto future = this->m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(100));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
|
||||
{
|
||||
auto future = this->m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
|
||||
|
||||
auto methodReply = future.get();
|
||||
uint32_t returnValue{};
|
||||
methodReply >> returnValue;
|
||||
|
||||
ASSERT_THAT(returnValue, Eq(100));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodWithLargeDataAsynchronouslyOnClientSideWithFuture)
|
||||
{
|
||||
std::map<int32_t, std::string> largeMap;
|
||||
for (int32_t i = 0; i < 40'000; ++i)
|
||||
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
|
||||
|
||||
auto future = this->m_proxy->doOperationWithLargeDataClientSideAsync(largeMap, sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(largeMap));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
|
||||
{
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){});
|
||||
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_TRUE(call.isPending());
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSide)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSideByDestroyingOwningSlot)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
|
||||
|
||||
{
|
||||
auto slot = this->m_proxy->doOperationClientSideAsync(100, sdbus::return_slot);
|
||||
// Now the slot is destroyed, cancelling the async call
|
||||
}
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
call.cancel();
|
||||
|
||||
ASSERT_FALSE(call.isPending());
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
|
||||
|
||||
auto call = this->m_proxy->doOperationClientSideAsync(0);
|
||||
(void) future.get(); // Wait for the call to finish
|
||||
|
||||
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
|
||||
{
|
||||
sdbus::PendingAsyncCall call;
|
||||
|
||||
ASSERT_FALSE(call.isPending());
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, SupportsAsyncCallCopyAssignment)
|
||||
{
|
||||
sdbus::PendingAsyncCall call;
|
||||
|
||||
call = this->m_proxy->doOperationClientSideAsync(100);
|
||||
|
||||
ASSERT_TRUE(call.isPending());
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
|
||||
{
|
||||
std::promise<uint32_t> promise;
|
||||
auto future = promise.get_future();
|
||||
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value(res);
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
this->m_proxy->doErroneousOperationClientSideAsync();
|
||||
|
||||
ASSERT_THROW(future.get(), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(AsyncSdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
|
||||
{
|
||||
auto future = this->m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
|
||||
|
||||
ASSERT_THROW(future.get(), sdbus::Error);
|
||||
}
|
96
tests/integrationtests/DBusConnectionTests.cpp
Normal file
96
tests/integrationtests/DBusConnectionTests.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(Connection, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(auto con = sdbus::createBusConnection());
|
||||
}
|
||||
|
||||
TEST(Connection, CanRequestName)
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
|
||||
// In case of system bus connection, requesting may throw as we need to allow that first through a config file in /etc/dbus-1/system.d
|
||||
ASSERT_NO_THROW(connection->requestName(SERVICE_NAME))
|
||||
<< "Perhaps you've forgotten to copy `org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory before running the tests?";
|
||||
}
|
||||
|
||||
TEST(SystemBusConnection, CannotRequestNonregisteredDbusName)
|
||||
{
|
||||
auto connection = sdbus::createSystemBusConnection();
|
||||
sdbus::ServiceName notSupportedBusName{"some.random.not.supported.dbus.name"};
|
||||
|
||||
ASSERT_THROW(connection->requestName(notSupportedBusName), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanReleaseRequestedName)
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
connection->requestName(SERVICE_NAME);
|
||||
|
||||
ASSERT_NO_THROW(connection->releaseName(SERVICE_NAME));
|
||||
}
|
||||
|
||||
TEST(Connection, CannotReleaseNonrequestedName)
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
sdbus::ServiceName notAcquiredBusName{"some.random.unacquired.name"};
|
||||
|
||||
ASSERT_THROW(connection->releaseName(notAcquiredBusName), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanEnterAndLeaveInternalEventLoop)
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
connection->requestName(SERVICE_NAME);
|
||||
|
||||
std::thread t([&](){ connection->enterEventLoop(); });
|
||||
connection->leaveEventLoop();
|
||||
|
||||
t.join();
|
||||
}
|
184
tests/integrationtests/DBusGeneralTests.cpp
Normal file
184
tests/integrationtests/DBusGeneralTests.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 "TestFixture.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>
|
||||
#include <variant>
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace sdbus::test;
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
using ADirectConnection = TestFixtureWithDirectConnection;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccessfully)
|
||||
{
|
||||
auto connection = sdbus::createBusConnection();
|
||||
connection->requestName(SERVICE_NAME);
|
||||
|
||||
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
|
||||
ASSERT_NO_THROW(TestProxy proxy(SERVICE_NAME, OBJECT_PATH));
|
||||
|
||||
connection->releaseName(SERVICE_NAME);
|
||||
}
|
||||
|
||||
TEST(AProxy, DoesNotSupportMoveSemantics)
|
||||
{
|
||||
static_assert(!std::is_move_constructible_v<DummyTestProxy>);
|
||||
static_assert(!std::is_move_assignable_v<DummyTestProxy>);
|
||||
}
|
||||
|
||||
TEST(AnAdaptor, DoesNotSupportMoveSemantics)
|
||||
{
|
||||
static_assert(!std::is_move_constructible_v<DummyTestAdaptor>);
|
||||
static_assert(!std::is_move_assignable_v<DummyTestAdaptor>);
|
||||
}
|
||||
|
||||
TYPED_TEST(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
|
||||
{
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
}, sdbus::return_slot);
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(matchingMessageReceived));
|
||||
}
|
||||
|
||||
TYPED_TEST(AConnection, CanInstallMatchRuleAsynchronously)
|
||||
{
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
std::atomic<bool> matchRuleInstalled{false};
|
||||
auto slot = this->s_proxyConnection->addMatchAsync( matchRule
|
||||
, [&](sdbus::Message msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
}
|
||||
, [&](sdbus::Message /*msg*/)
|
||||
{
|
||||
matchRuleInstalled = true;
|
||||
}
|
||||
, sdbus::return_slot );
|
||||
|
||||
EXPECT_TRUE(waitUntil(matchRuleInstalled));
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(matchingMessageReceived));
|
||||
}
|
||||
|
||||
TYPED_TEST(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
|
||||
{
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
}, sdbus::return_slot);
|
||||
slot.reset();
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
|
||||
}
|
||||
|
||||
TYPED_TEST(AConnection, CanAddFloatingMatchRule)
|
||||
{
|
||||
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
|
||||
std::atomic<bool> matchingMessageReceived{false};
|
||||
auto con = sdbus::createBusConnection();
|
||||
con->enterEventLoopAsync();
|
||||
auto callback = [&](sdbus::Message msg)
|
||||
{
|
||||
if(msg.getPath() == OBJECT_PATH)
|
||||
matchingMessageReceived = true;
|
||||
};
|
||||
con->addMatch(matchRule, std::move(callback));
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
[[maybe_unused]] auto gotMessage = waitUntil(matchingMessageReceived, 2s);
|
||||
assert(gotMessage);
|
||||
matchingMessageReceived = false;
|
||||
|
||||
con.reset();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
|
||||
}
|
||||
|
||||
TYPED_TEST(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
|
||||
{
|
||||
auto matchRule = "type='signal',interface='" + INTERFACE_NAME + "',member='simpleSignal'";
|
||||
std::atomic<size_t> numberOfMatchingMessages{};
|
||||
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message msg)
|
||||
{
|
||||
if(msg.getMemberName() == "simpleSignal"sv)
|
||||
numberOfMatchingMessages++;
|
||||
}, sdbus::return_slot);
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
this->m_adaptor->emitSignalWithMap({});
|
||||
adaptor2->emitSimpleSignal();
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
|
||||
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
|
||||
}
|
||||
|
||||
// A simple direct connection test similar in nature to https://github.com/systemd/systemd/blob/main/src/libsystemd/sd-bus/test-bus-server.c
|
||||
TEST_F(ADirectConnection, CanBeUsedBetweenClientAndServer)
|
||||
{
|
||||
auto val = m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
|
||||
m_adaptor->emitSimpleSignal();
|
||||
|
||||
// Make sure method call passes and emitted signal is received
|
||||
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
|
||||
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
|
||||
}
|
383
tests/integrationtests/DBusMethodsTests.cpp
Normal file
383
tests/integrationtests/DBusMethodsTests.cpp
Normal file
@ -0,0 +1,383 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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 std::string_literals;
|
||||
using namespace sdbus::test;
|
||||
|
||||
namespace my {
|
||||
struct Struct
|
||||
{
|
||||
int i;
|
||||
std::string s;
|
||||
std::vector<double> l;
|
||||
|
||||
friend bool operator==(const Struct &lhs, const Struct &rhs) = default;
|
||||
};
|
||||
}
|
||||
|
||||
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l);
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsEmptyMethodSuccessfully)
|
||||
{
|
||||
ASSERT_NO_THROW(this->m_proxy->noArgNoReturn());
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccessfully)
|
||||
{
|
||||
auto resInt = this->m_proxy->getInt();
|
||||
ASSERT_THAT(resInt, Eq(INT32_VALUE));
|
||||
|
||||
auto multiplyRes = this->m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithTuplesSuccessfully)
|
||||
{
|
||||
auto resTuple = this->m_proxy->getTuple();
|
||||
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
|
||||
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccessfully)
|
||||
{
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
|
||||
auto vectorRes = this->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 = this->m_proxy->getInts16FromStruct(b);
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithVariantSuccessfully)
|
||||
{
|
||||
sdbus::Variant v{DOUBLE_VALUE};
|
||||
sdbus::Variant variantRes = this->m_proxy->processVariant(v);
|
||||
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStdVariantSuccessfully)
|
||||
{
|
||||
std::variant<int32_t, double, std::string> v{DOUBLE_VALUE};
|
||||
auto variantRes = this->m_proxy->processVariant(v);
|
||||
ASSERT_THAT(std::get<int32_t>(variantRes), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccessfully)
|
||||
{
|
||||
std::vector<int32_t> x{-2, 0, 2};
|
||||
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
|
||||
std::map<int32_t, sdbus::Variant> mapOfVariants = this->m_proxy->getMapOfVariants(x, y);
|
||||
decltype(mapOfVariants) res{ {-2, sdbus::Variant{false}}
|
||||
, {0, sdbus::Variant{false}}
|
||||
, {2, sdbus::Variant{true}}};
|
||||
|
||||
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
|
||||
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
|
||||
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithStructInStructSuccessfully)
|
||||
{
|
||||
auto val = this->m_proxy->getStructInStruct();
|
||||
ASSERT_THAT(val.template get<0>(), Eq(STRING_VALUE));
|
||||
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoStructsSuccessfully)
|
||||
{
|
||||
auto val = this->m_proxy->sumStructItems({1, 2}, {3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoVectorsSuccessfully)
|
||||
{
|
||||
auto val = this->m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithSignatureSuccessfully)
|
||||
{
|
||||
auto resSignature = this->m_proxy->getSignature();
|
||||
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithObjectPathSuccessfully)
|
||||
{
|
||||
auto resObjectPath = this->m_proxy->getObjPath();
|
||||
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithUnixFdSuccessfully)
|
||||
{
|
||||
auto resUnixFd = this->m_proxy->getUnixFd();
|
||||
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithComplexTypeSuccessfully)
|
||||
{
|
||||
auto resComplex = this->m_proxy->getComplex();
|
||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
|
||||
{
|
||||
this->m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasMultiplyCalled));
|
||||
ASSERT_THAT(this->m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
|
||||
{
|
||||
auto res = this->m_proxy->doOperationWithTimeout(500ms, (20ms).count()); // The operation will take 20ms, but the timeout is 500ms, so we are fine
|
||||
ASSERT_THAT(res, Eq(20));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
try
|
||||
{
|
||||
this->m_proxy->doOperationWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
|
||||
FAIL() << "Expected sdbus::Error exception";
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
ASSERT_THAT(e.getName(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
|
||||
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Operation 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";
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodThatThrowsError)
|
||||
{
|
||||
try
|
||||
{
|
||||
this->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";
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
|
||||
{
|
||||
ASSERT_NO_THROW(this->m_proxy->throwErrorWithNoReply());
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasThrowErrorCalled));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||
{
|
||||
ASSERT_THROW(this->m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
|
||||
{
|
||||
ASSERT_THROW(this->m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
|
||||
{
|
||||
TestProxy proxy(sdbus::ServiceName{"sdbuscpp.destination.that.does.not.exist"}, OBJECT_PATH);
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
|
||||
{
|
||||
TestProxy proxy(SERVICE_NAME, sdbus::ObjectPath{"/sdbuscpp/path/that/does/not/exist"});
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
|
||||
{
|
||||
this->m_proxy->emitTwoSimpleSignals();
|
||||
|
||||
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanSendAndReceiveDictionariesAsCustomStructsImplicitly)
|
||||
{
|
||||
// This test demonstrates that sdbus-c++ can send a SDBUSCPP_REGISTER_STRUCT-described struct as a dictionary of strings to variants,
|
||||
// and that sdbus-c++ can automatically deserialize a dictionary of strings to variants into such a struct (instead of a map).
|
||||
const my::Struct structSent{3545342, "hello"s, {3.14, 2.4568546}};
|
||||
my::Struct structReceived;
|
||||
|
||||
this->m_proxy->getProxy().callMethod("returnDictionary")
|
||||
.onInterface("org.sdbuscpp.integrationtests")
|
||||
.withArguments(sdbus::as_dictionary(structSent))
|
||||
.storeResultsTo(structReceived);
|
||||
|
||||
ASSERT_THAT(structReceived, Eq(structSent));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
|
||||
{
|
||||
this->m_proxy->doOperation(10); // This will save pointer to method call message on server side
|
||||
|
||||
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
|
||||
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperation"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ProvidesSerialInMethodCallAndMethodReplyMessage)
|
||||
{
|
||||
auto reply = this->m_proxy->doOperationOnBasicAPILevel(ANY_UNSIGNED_NUMBER);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->m_methodCallMsg->getCookie(), Gt(0));
|
||||
ASSERT_THAT(reply.getReplyCookie(), Eq(this->m_proxy->m_methodCallMsg->getCookie())); // Pairing method reply with method call message
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
|
||||
{
|
||||
this->m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
|
||||
|
||||
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
|
||||
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperationAsync"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CallsMethodWithLargeArgument)
|
||||
{
|
||||
std::map<int, std::string> collection;
|
||||
//std::size_t totalSize{};
|
||||
for (int i = 0; i < 400'000; i++)
|
||||
{
|
||||
collection[i] = ("This is a string of fifty characters. This is a string of fifty " + std::to_string(i));
|
||||
//totalSize += sizeof(int) + collection[i].size();
|
||||
}
|
||||
//printf("Sending large message with collection of size %zu bytes\n", totalSize);
|
||||
this->m_proxy->sendLargeMessage(collection);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanSendCallsAndReceiveRepliesWithLargeData)
|
||||
{
|
||||
std::map<int32_t, std::string> largeMap;
|
||||
for (int32_t i = 0; i < 40'000; ++i)
|
||||
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
|
||||
|
||||
auto returnedMap = this->m_proxy->doOperationWithLargeData(largeMap);
|
||||
|
||||
ASSERT_THAT(returnedMap, Eq(largeMap));
|
||||
}
|
||||
|
||||
#if LIBSYSTEMD_VERSION>=240
|
||||
TYPED_TEST(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
|
||||
{
|
||||
this->s_adaptorConnection->setMethodCallTimeout(5000000);
|
||||
ASSERT_THAT(this->s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
|
||||
}
|
||||
#else
|
||||
TYPED_TEST(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
|
||||
{
|
||||
ASSERT_THROW(this->s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
|
||||
ASSERT_THROW(this->s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
|
||||
}
|
||||
#endif
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
|
||||
{
|
||||
auto proxy = std::make_unique<TestProxy>(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
|
||||
|
||||
auto multiplyRes = proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime)
|
||||
{
|
||||
auto& object = this->m_adaptor->getObject();
|
||||
sdbus::InterfaceName interfaceName{"org.sdbuscpp.integrationtests2"};
|
||||
auto vtableSlot = object.addVTable( interfaceName
|
||||
, { sdbus::registerMethod("add").implementedAs([](const int64_t& a, const double& b){ return a + b; })
|
||||
, sdbus::registerMethod("subtract").implementedAs([](const int& a, const int& b){ return a - b; }) }
|
||||
, sdbus::return_slot );
|
||||
|
||||
// The new remote vtable is registered as long as we keep vtableSlot, so remote method calls now should pass
|
||||
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
int result{};
|
||||
proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2).storeResultsTo(result);
|
||||
|
||||
ASSERT_THAT(result, Eq(8));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime)
|
||||
{
|
||||
auto& object = this->m_adaptor->getObject();
|
||||
sdbus::InterfaceName interfaceName{"org.sdbuscpp.integrationtests2"};
|
||||
|
||||
auto vtableSlot = object.addVTable( interfaceName
|
||||
, { sdbus::registerMethod("add").implementedAs([](const int64_t& a, const double& b){ return a + b; })
|
||||
, sdbus::registerMethod("subtract").implementedAs([](const int& a, const int& b){ return a - b; }) }
|
||||
, sdbus::return_slot );
|
||||
vtableSlot.reset(); // Letting the slot go means letting go the associated vtable registration
|
||||
|
||||
// No such remote D-Bus method under given interface exists anymore...
|
||||
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
ASSERT_THROW(proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2), sdbus::Error);
|
||||
}
|
92
tests/integrationtests/DBusPropertiesTests.cpp
Normal file
92
tests/integrationtests/DBusPropertiesTests.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ReadsReadOnlyPropertySuccessfully)
|
||||
{
|
||||
ASSERT_THAT(this->m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, FailsWritingToReadOnlyProperty)
|
||||
{
|
||||
ASSERT_THROW(this->m_proxy->setStateProperty("new_value"), sdbus::Error);
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWritePropertySuccessfully)
|
||||
{
|
||||
uint32_t newActionValue = 5678;
|
||||
|
||||
this->m_proxy->action(newActionValue);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
|
||||
{
|
||||
this->m_proxy->blocking(true); // This will save pointer to property get message on server side
|
||||
|
||||
ASSERT_THAT(this->m_adaptor->m_propertySetMsg, NotNull());
|
||||
ASSERT_THAT(this->m_adaptor->m_propertySetSender, Not(IsEmpty()));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWriteVariantPropertySuccessfully)
|
||||
{
|
||||
sdbus::Variant newActionValue{5678};
|
||||
|
||||
this->m_proxy->actionVariant(newActionValue);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->actionVariant().template get<int>(), Eq(5678));
|
||||
}
|
167
tests/integrationtests/DBusSignalsTests.cpp
Normal file
167
tests/integrationtests/DBusSignalsTests.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalSuccessfully)
|
||||
{
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccessfully)
|
||||
{
|
||||
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
|
||||
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
|
||||
{
|
||||
sdbus::ServiceName otherBusName{SERVICE_NAME + "2"};
|
||||
auto connection2 = sdbus::createBusConnection(otherBusName);
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*connection2, OBJECT_PATH);
|
||||
|
||||
adaptor2->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccessfully)
|
||||
{
|
||||
this->m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("zero"));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("one"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithLargeMapSuccessfully)
|
||||
{
|
||||
std::map<int32_t, std::string> largeMap;
|
||||
for (int32_t i = 0; i < 20'000; ++i)
|
||||
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
|
||||
this->m_adaptor->emitSignalWithMap(largeMap);
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
|
||||
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccessfully)
|
||||
{
|
||||
double d = 3.14;
|
||||
this->m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithVariant));
|
||||
ASSERT_THAT(this->m_proxy->m_variantFromSignal, DoubleEq(d));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccessfully)
|
||||
{
|
||||
this->m_adaptor->emitSignalWithoutRegistration({"platform", sdbus::Signature{"av"}});
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithSignature));
|
||||
ASSERT_THAT(this->m_proxy->m_signatureFromSignal["platform"], Eq(sdbus::Signature{"av"}));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
|
||||
{
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
waitUntil(this->m_proxy->m_gotSimpleSignal);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->m_signalMsg, NotNull());
|
||||
ASSERT_THAT(this->m_proxy->m_signalName, Eq(std::string("simpleSignal")));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, UnregistersSignalHandler)
|
||||
{
|
||||
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
|
||||
{
|
||||
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
|
||||
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
|
||||
|
||||
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
|
||||
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ReRegistersSignalHandler)
|
||||
{
|
||||
// unregister simple-signal handler
|
||||
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
|
||||
|
||||
// re-register simple-signal handler
|
||||
ASSERT_NO_THROW(this->m_proxy->reRegisterSimpleSignalHandler());
|
||||
|
||||
this->m_adaptor->emitSimpleSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
|
||||
}
|
412
tests/integrationtests/DBusStandardInterfacesTests.cpp
Normal file
412
tests/integrationtests/DBusStandardInterfacesTests.cpp
Normal file
@ -0,0 +1,412 @@
|
||||
/**
|
||||
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.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;
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(SdbusTestObject, PingsViaPeerInterface)
|
||||
{
|
||||
ASSERT_NO_THROW(this->m_proxy->Ping());
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
|
||||
{
|
||||
if (::access("/etc/machine-id", F_OK) == -1 &&
|
||||
::access("/var/lib/dbus/machine-id", F_OK) == -1)
|
||||
GTEST_SKIP() << "/etc/machine-id and /var/lib/dbus/machine-id files do not exist, GetMachineId() will not work";
|
||||
|
||||
ASSERT_NO_THROW(this->m_proxy->GetMachineId());
|
||||
}
|
||||
|
||||
// TODO: Adjust expected xml and uncomment this test
|
||||
//TYPED_TEST(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
|
||||
//{
|
||||
// ASSERT_THAT(this->m_proxy->Introspect(), Eq(this->m_adaptor->getExpectedXmlApiDescription()));
|
||||
//}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsPropertyViaPropertiesInterface)
|
||||
{
|
||||
ASSERT_THAT(this->m_proxy->Get(INTERFACE_NAME, "state").template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
{
|
||||
std::promise<std::string> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
this->m_proxy->GetAsync(INTERFACE_NAME, "state", [&](std::optional<sdbus::Error> err, sdbus::Variant value)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value(value.get<std::string>());
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
{
|
||||
auto future = this->m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get().template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, SetsPropertyViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2345;
|
||||
|
||||
this->m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
|
||||
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2346;
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value();
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW(future.get());
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CancelsAsynchronousPropertySettingViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2346;
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
{
|
||||
auto slot = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value();
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
}, sdbus::return_slot);
|
||||
// Now the slot is destroyed, cancelling the async call
|
||||
}
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
{
|
||||
uint32_t newActionValue = 2347;
|
||||
|
||||
auto future = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
|
||||
|
||||
ASSERT_NO_THROW(future.get());
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
|
||||
{
|
||||
const auto properties = this->m_proxy->GetAll(INTERFACE_NAME);
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(4));
|
||||
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
|
||||
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
|
||||
{
|
||||
std::promise<std::map<sdbus::PropertyName, sdbus::Variant>> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](std::optional<sdbus::Error> err, std::map<sdbus::PropertyName, sdbus::Variant> value)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value(std::move(value));
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
});
|
||||
const auto properties = future.get();
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(4));
|
||||
EXPECT_THAT(properties.at(STATE_PROPERTY).get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_PROPERTY).get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
|
||||
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
{
|
||||
auto future = this->m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
|
||||
|
||||
auto properties = future.get();
|
||||
|
||||
ASSERT_THAT(properties, SizeIs(4));
|
||||
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
|
||||
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
|
||||
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<sdbus::PropertyName>& /*invalidatedProperties*/ )
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at(BLOCKING_PROPERTY).get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
this->m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
|
||||
this->m_proxy->action(DEFAULT_ACTION_VALUE*2);
|
||||
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {BLOCKING_PROPERTY});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
|
||||
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
|
||||
, const std::vector<sdbus::PropertyName>& invalidatedProperties )
|
||||
{
|
||||
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
|
||||
EXPECT_THAT(changedProperties, SizeIs(1));
|
||||
EXPECT_THAT(changedProperties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
|
||||
ASSERT_THAT(invalidatedProperties, SizeIs(1));
|
||||
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
|
||||
{
|
||||
this->m_adaptor.reset();
|
||||
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
|
||||
|
||||
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsSuccessfully)
|
||||
{
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
|
||||
|
||||
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
|
||||
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH)
|
||||
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
|
||||
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2)
|
||||
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
|
||||
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronously)
|
||||
{
|
||||
std::promise<size_t> promise;
|
||||
auto future = promise.get_future();
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
this->m_objectManagerProxy->GetManagedObjectsAsync([&](std::optional<sdbus::Error> /*err*/, const std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>>& objectsInterfacesAndProperties)
|
||||
{
|
||||
promise.set_value(objectsInterfacesAndProperties.size());
|
||||
});
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronouslyViaSlotReturningOverload)
|
||||
{
|
||||
std::promise<size_t> promise;
|
||||
auto future = promise.get_future();
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
auto slot = this->m_objectManagerProxy->GetManagedObjectsAsync([&](std::optional<sdbus::Error> /*err*/, const std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>>& objectsInterfacesAndProperties)
|
||||
{
|
||||
promise.set_value(objectsInterfacesAndProperties.size());
|
||||
}, sdbus::return_slot);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronouslyViaFutureOverload)
|
||||
{
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
auto future = this->m_objectManagerProxy->GetManagedObjectsAsync(sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get().size(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
|
||||
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
|
||||
#if LIBSYSTEMD_VERSION<=244
|
||||
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(4));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
#else
|
||||
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
|
||||
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
|
||||
// So in this specific instance, `action' property is no more added to the list.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
this->m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
#if LIBSYSTEMD_VERSION<=250
|
||||
EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
|
||||
#else
|
||||
// Since systemd v251, ObjectManager standard interface is not listed among the interfaces
|
||||
// if the object does not have object manager functionality explicitly enabled.
|
||||
EXPECT_THAT(interfacesAndProperties, SizeIs(4)); // INTERFACE_NAME + 3 standard interfaces
|
||||
#endif
|
||||
#if LIBSYSTEMD_VERSION<=244
|
||||
// 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(4));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
#else
|
||||
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
|
||||
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
|
||||
// So in this specific instance, `action' property is no more added to the list.
|
||||
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
|
||||
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
this->m_adaptor->emitInterfacesAddedSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
ASSERT_THAT(interfaces, SizeIs(1));
|
||||
EXPECT_THAT(interfaces[0], Eq(INTERFACE_NAME));
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
this->m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
|
||||
, const std::vector<sdbus::InterfaceName>& interfaces )
|
||||
{
|
||||
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
|
||||
#if LIBSYSTEMD_VERSION<=250
|
||||
ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
|
||||
#else
|
||||
// Since systemd v251, ObjectManager standard interface is not listed among the interfaces
|
||||
// if the object does not have object manager functionality explicitly enabled.
|
||||
ASSERT_THAT(interfaces, SizeIs(4)); // INTERFACE_NAME + 3 standard interfaces
|
||||
#endif
|
||||
signalReceived = true;
|
||||
};
|
||||
|
||||
this->m_adaptor->emitInterfacesRemovedSignal();
|
||||
|
||||
ASSERT_TRUE(waitUntil(signalReceived));
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user