Compare commits

...

100 Commits

Author SHA1 Message Date
d591b69f92 Bump revision up to 0.8.0 2020-02-01 13:56:13 +01:00
49586001d6 Try to better explain the design and how to use connections in objects and proxies 2020-02-01 13:38:51 +01:00
aa8e9123de Update the tutorial for new method parameter names feature 2020-02-01 12:38:39 +01:00
eade6a0e44 Add support for method and signal parameter names in introspection 2020-02-01 12:38:39 +01:00
10977c6137 Bump revision up to 0.7.8 2020-01-25 23:38:54 +01:00
1e455b8ef3 Fix inline variable since in v0.7 version line we still support gcc 6 2020-01-25 22:31:41 +01:00
75709e31f1 Fix sporadic race condition between Variant and underlying bus
The underlying bus was thread_local, but the design assumption that Variants built on top of that instance won't outlive the thread was incorrect. In stress tests, Variants were moved (and this is completely legal) to a different thread.
2020-01-25 22:31:41 +01:00
245db893b8 Remove obsolete section on Yocto recipes for sdbus-c++ 2020-01-23 16:58:48 +01:00
477c5dd714 Update section on Yocto recipes for sdbus-c++ 2020-01-23 16:57:42 +01:00
b25534013f Fix lock_guard variable declaration (don't yet use newer C++17 features) 2020-01-19 19:17:04 +01:00
68b5eac9e9 Bump revision up to 0.7.7 2020-01-19 18:59:36 +01:00
4310a3bd17 Little fix of order of destruction in stress tests 2020-01-19 18:57:14 +01:00
f41d9bc395 Fix issue of event loop thread and synchronous method call thread polling on the same D-Bus connection
Synchronous D-Bus method calls are now done in terms of blocking asynchronous calls.
2020-01-19 18:51:19 +01:00
5121d46eed Bump revision up to 0.7.6 2020-01-05 22:49:26 +01:00
121ed1a975 Fix clang-8 compilation errors and warnings 2020-01-05 12:52:37 +01:00
cc495811f9 Bump revision up to 0.7.5 2019-11-10 17:43:14 +01:00
839bc13625 Make Connection const-correct and fix integration tests build error 2019-11-10 17:40:33 +01:00
5fe0f503ca Add a method to retrieve the unique name of a connection 2019-11-10 17:34:57 +01:00
d50a15b2a2 Move C++17 uncaught_exceptions to public API 2019-11-10 17:31:58 +01:00
3a76e9c120 Bump revision up to 0.7.4 2019-11-03 20:40:13 +01:00
304b69dd8b Use tag dispatching to construct various types of Connection, refactor Connection unit tests 2019-11-03 20:30:52 +01:00
Jay
099bc857ad Add support for opening a connection to a remote system bus using ssh (#77) 2019-11-03 20:21:39 +01:00
c139110112 Add support for custom timeout value for D-Bus method calls (#72) 2019-11-03 13:54:13 +01:00
e7155c5506 Bump revision up to 0.7.3 2019-10-13 15:39:32 +02:00
0f7de608ac Little code cleanup 2019-10-11 16:04:37 +02:00
c6d4d2710f Skip GetMachineId test when /etc/machine-id is not available 2019-10-11 15:56:00 +02:00
0440dcb15b Added ability to integrate with foreign event loops 2019-10-08 22:09:05 +02:00
e30ce194ab Add getSenderName method to Message 2019-10-06 11:28:16 +02:00
8dea11bac6 Add note on solving potential getent-related Yocto errors 2019-07-09 18:29:58 +02:00
750dab3927 Bump revision up to 0.7.2 2019-07-08 10:01:11 +02:00
bf35157a4a Comment out unused parameter 2019-07-08 09:58:22 +02:00
a09362f79a Switch from plain UnixFd to owning UnixFd (#69) 2019-07-08 09:53:53 +02:00
c264f83e83 Fix Yocto chapter level in the tutorial 2019-06-12 15:03:26 +02:00
71adb5cf30 Add notes on sdbus-c++ Yocto recipes to the tutorial 2019-06-12 15:02:01 +02:00
00177a7e4c Bump revision up to 0.7.1 2019-06-12 09:18:55 +02:00
9826d28f51 Add missing Factory friend to Message subclasses 2019-06-11 20:29:45 +02:00
ab34b0ae50 Update header doxy comments in source files 2019-06-11 20:18:37 +02:00
ff944c9e95 Add protected non-virtual destructor in generated classes 2019-06-10 22:54:16 +02:00
7049d00a78 Remove unnecessary std::move of parameters (thanks to @ardazishvili)
Fixes part of #52
2019-06-10 22:03:02 +02:00
236c10ff56 Resolve a few clang-tidy suggestions and warnings (thanks to @ardazishvili)
Fixes part of #52.
2019-06-10 21:54:02 +02:00
dcad208ffe Redesign inheritance from Message (#62)
... so that the code is more idiomatic, clear and expressive about its intended use
2019-06-10 21:38:30 +02:00
57c840637c Add support for Unix fd D-Bus type 2019-06-10 21:19:56 +02:00
efe799ef3f Update section on standard D-Bus interfaces in the tutorial 2019-06-05 12:34:43 +02:00
5c0a8d5ab4 Add object manager automatically in ObjectManager_adaptor constructor 2019-06-05 12:18:04 +02:00
65b3e7ba00 Update README 2019-06-05 11:43:06 +02:00
b2b0bddf02 Fix Variant signal test in integration tests 2019-06-04 23:45:45 +02:00
11f0edf7b8 Fix fragile time-based waiting in integration tests 2019-06-04 22:48:54 +02:00
946cc8d0cd Fix indentation of doxygen comments 2019-06-04 22:21:49 +02:00
07625a435b Bump revision up to 0.7.0 2019-06-04 21:39:29 +02:00
fbb5242729 Add emit prefix to generated signal emitting methods 2019-06-04 21:30:09 +02:00
dbeaf87208 Remove obsolete section from the tutorial 2019-06-03 23:58:29 +02:00
45176c9eb7 Update the tutorial with standard D-Bus interfaces info 2019-06-03 23:57:14 +02:00
38b51bddc6 Add support for ObjectManager's InterfaceAdded/Removed signals on server side 2019-06-03 23:47:27 +02:00
01e2a7a570 Add support for PropertyChanged signal on server side 2019-06-03 22:02:15 +02:00
38552483ca Update ChangeLog 2019-05-29 22:41:59 +02:00
91fa35140b Add support for ObjectManager and other standard D-Bus interfaces (#55)
Fixes #50
2019-05-29 22:28:15 +02:00
4b0c23204d Add object manager support to IConnection interface (#54) 2019-05-27 20:51:42 +02:00
c13ee60b7e Have no update step in external googletest build
This enables (re)building sdbus-c++ with already built googletest without the need for Internet connection
2019-05-26 15:38:58 +02:00
6ee66dfc47 Update ChangeLog for incoming new release 2019-05-26 15:37:32 +02:00
ed5c7a1fd5 Update INSTALL 2019-05-26 15:36:14 +02:00
6629d31733 Update CMake usage example in tutorial 2019-05-23 22:04:51 +02:00
0014bb0b6e Go a step further towards modern CMake and cleaner project structure 2019-05-23 21:40:59 +02:00
ad3749f2c2 Add libmount as dependency for libsystemd 2019-05-20 23:28:28 +02:00
0045e8fcdc Do shallow clone of googletest 2019-05-20 23:08:20 +02:00
e12a9c3914 Move the license to the parent tools directory 2019-05-14 14:42:00 +02:00
19d852e1b9 Add licence to the codegen tool 2019-05-14 14:38:22 +02:00
8da3e312bc Make tools a sdbus-c++-tools project, not codegen specifically 2019-05-14 14:38:05 +02:00
81b5a67f35 Use shallow clone for libsystemd external project 2019-05-13 19:21:23 +02:00
b87b0c9dd9 Fix the way of handling thread-local system bus 2019-05-12 10:23:26 +02:00
76414ff09e Add pkgconfig to Dependencies section in README (#51) 2019-05-09 19:37:56 +02:00
4998895f41 Hide some internal googletest-specific CMake options 2019-05-09 19:17:32 +02:00
7763c66513 Add support for libsystemd versions up to the newest 242, and set 242 as default version 2019-05-09 19:16:16 +02:00
7c3f91310f Improve docs on steps needed to build libsystemd 2019-05-08 23:54:32 +02:00
2256adf707 Add section on automatically provided standard D-Bus interfaces 2019-05-08 23:44:22 +02:00
2c218ab3ba Minor update of tutorial -- building libsystemd as part of sdbus-c++ 2019-04-28 17:20:11 +02:00
0cffed4574 Add option to build libsystemd automatically as integral part of sdbus-c++ 2019-04-28 17:09:37 +02:00
36269897fd Remove unwanted sdbus-c++-internal includes from integration tests 2019-04-28 15:59:29 +02:00
1b1b9ae8ae Move code generator to tools subdirectory for higher consistency with OSS standards 2019-04-26 00:03:46 +02:00
1b02c604d8 Rename test subdirectory to tests for higher consistency with OSS standards 2019-04-25 23:54:51 +02:00
981206fa8c Rename doc subdirectory to docs for higher consistency with OSS standards 2019-04-25 23:48:11 +02:00
b0dfea041d Use googletest from master because 1.8.1 causes THREADS_PTHREAD_ARG issue when cross-compiling 2019-04-25 17:15:35 +02:00
824aaa711e Separate doxygen doc building from other docs 2019-04-25 13:34:33 +02:00
882262bc2f Switch back to support also CMake 3.5 2019-04-25 12:51:50 +02:00
4ede37d6a3 Install additional documentation files 2019-04-25 12:42:08 +02:00
58647c6e7d Update AUTHORS file 2019-04-25 12:41:12 +02:00
a23d88a628 README: Add minimum versions of needed compilers 2019-04-23 20:35:52 +02:00
29c438b3bb README: Add link on solving systemd dependency on tutorial 2019-04-23 20:34:01 +02:00
8b7b9197eb Add info on solving libssystemd dependency 2019-04-23 20:30:57 +02:00
2d27f99b32 Add note on dependency on systemd to README 2019-04-23 17:21:27 +02:00
9bde7c7b68 Add more intro description of what sdbus-c++ is into README 2019-04-23 17:12:49 +02:00
62a546c9d3 Bump revision up to 0.6.0 2019-04-15 21:05:54 +02:00
5b99658f36 Turn on compiler warnings 2019-04-14 20:56:47 +02:00
d3749741d1 Add preliminary ChangeLog 2019-04-13 21:38:22 +02:00
1d44d8b37f Add loops in stress tests to test adaptor/proxy initialization/deinitialization 2019-04-13 21:28:43 +02:00
e3a74a3ff2 Add unregister function to IObject and IProxy API 2019-04-13 21:17:37 +02:00
99160156fe Fix all gcc warnings 2019-04-12 22:39:14 +02:00
ee30375cfc Use official release of googletest (v1.8.1) rather than master 2019-04-12 21:42:33 +02:00
06ca6539f3 Make sure googletest is always built as a static lib and never installed as part of sdbus-c++ 2019-04-12 21:30:07 +02:00
ed0745c83a Only install doxy docs if they were generated 2019-04-12 20:27:54 +02:00
93b6e5237a Clean up some names (rename classes, 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()
2019-04-09 21:28:07 +02:00
110 changed files with 6367 additions and 3350 deletions

6
.gitignore vendored
View File

@ -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

10
AUTHORS
View File

@ -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 :)

View File

@ -2,34 +2,45 @@
# PROJECT INFORMATION
#-------------------------------
cmake_minimum_required(VERSION 3.8)
cmake_minimum_required(VERSION 3.6)
project(sdbus-c++ VERSION 0.5.3 LANGUAGES C CXX)
project(sdbus-c++ VERSION 0.8.0 LANGUAGES C CXX)
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
#-------------------------------
# PERFORMING CHECKS
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES
#-------------------------------
find_package(PkgConfig REQUIRED)
pkg_check_modules(SYSTEMD REQUIRED libsystemd>=236)
option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into libsdbus-c++" OFF)
if(NOT BUILD_LIBSYSTEMD)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SYSTEMD libsystemd>=236)
if(NOT SYSTEMD_FOUND)
message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found "
"(you may turn BUILD_LIBSYSTEMD on for sdbus-c++ to try downloading "
"and building libsystemd in as part of sdbus-c++ during configuration)")
endif()
else()
# Build static libsystemd library as an external project
include(cmake/LibsystemdExternalProject.cmake)
endif()
#-------------------------------
# SOURCE FILES CONFIGURATION
#-------------------------------
set(SDBUSCPP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/src)
set(SDBUSCPP_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(SDBUSCPP_INCLUDE_SUBDIR sdbus-c++)
set(SDBUSCPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include/${SDBUSCPP_INCLUDE_SUBDIR})
set(SDBUSCPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include/${SDBUSCPP_INCLUDE_SUBDIR})
set(SDBUSCPP_CPP_SRCS
${SDBUSCPP_SOURCE_DIR}/Connection.cpp
${SDBUSCPP_SOURCE_DIR}/ConvenienceClasses.cpp
${SDBUSCPP_SOURCE_DIR}/Error.cpp
${SDBUSCPP_SOURCE_DIR}/Message.cpp
${SDBUSCPP_SOURCE_DIR}/Object.cpp
${SDBUSCPP_SOURCE_DIR}/ObjectProxy.cpp
${SDBUSCPP_SOURCE_DIR}/Proxy.cpp
${SDBUSCPP_SOURCE_DIR}/Types.cpp
${SDBUSCPP_SOURCE_DIR}/Flags.cpp
${SDBUSCPP_SOURCE_DIR}/VTableUtils.c
@ -40,21 +51,22 @@ set(SDBUSCPP_HDR_SRCS
${SDBUSCPP_SOURCE_DIR}/IConnection.h
${SDBUSCPP_SOURCE_DIR}/MessageUtils.h
${SDBUSCPP_SOURCE_DIR}/Object.h
${SDBUSCPP_SOURCE_DIR}/ObjectProxy.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}/ConvenienceClasses.h
${SDBUSCPP_INCLUDE_DIR}/ConvenienceClasses.inl
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.h
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.inl
${SDBUSCPP_INCLUDE_DIR}/Error.h
${SDBUSCPP_INCLUDE_DIR}/IConnection.h
${SDBUSCPP_INCLUDE_DIR}/Interfaces.h
${SDBUSCPP_INCLUDE_DIR}/Introspection.h
${SDBUSCPP_INCLUDE_DIR}/AdaptorInterfaces.h
${SDBUSCPP_INCLUDE_DIR}/ProxyInterfaces.h
${SDBUSCPP_INCLUDE_DIR}/StandardInterfaces.h
${SDBUSCPP_INCLUDE_DIR}/IObject.h
${SDBUSCPP_INCLUDE_DIR}/IObjectProxy.h
${SDBUSCPP_INCLUDE_DIR}/IProxy.h
${SDBUSCPP_INCLUDE_DIR}/Message.h
${SDBUSCPP_INCLUDE_DIR}/MethodResult.h
${SDBUSCPP_INCLUDE_DIR}/Types.h
@ -68,9 +80,11 @@ set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HD
# GENERAL COMPILER CONFIGURATION
#-------------------------------
set(CMAKE_CXX_STANDARD 17)
include_directories("${CMAKE_SOURCE_DIR}/include")
include_directories("${CMAKE_SOURCE_DIR}/src")
if(${CMAKE_VERSION} VERSION_LESS "3.8.0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
else()
set(CMAKE_CXX_STANDARD 17) # Supported in CMake>=3.8
endif()
#----------------------------------
# LIBRARY BUILD INFORMATION
@ -81,27 +95,35 @@ set(SDBUSCPP_VERSION "${PROJECT_VERSION}")
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
add_library(sdbus-cpp OBJECT ${SDBUSCPP_SRCS})
target_include_directories(sdbus-cpp PUBLIC ${SYSTEMD_INCLUDE_DIRS})
target_compile_definitions(sdbus-cpp PRIVATE BUILDLIB=1)
# 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=${SYSTEMD_VERSION})
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<BUILD_INTERFACE:${SYSTEMD_INCLUDE_DIRS}>)
if(BUILD_SHARED_LIBS)
set_target_properties(sdbus-cpp PROPERTIES POSITION_INDEPENDENT_CODE ON)
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
if(BUILD_LIBSYSTEMD)
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
endif()
add_library(sdbus-c++ $<TARGET_OBJECTS:sdbus-cpp>)
add_library(sdbus-c++ $<TARGET_OBJECTS:sdbus-c++-objlib>)
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++ ${SYSTEMD_LIBRARIES})
PROPERTIES PUBLIC_HEADER "${SDBUSCPP_PUBLIC_HDRS}"
VERSION "${SDBUSCPP_VERSION}"
SOVERSION "${SDBUSCPP_VERSION_MAJOR}"
OUTPUT_NAME "sdbus-c++")
target_link_libraries(sdbus-c++ PRIVATE ${SYSTEMD_LIBRARIES})
#----------------------------------
# INSTALLATION
#----------------------------------
install(TARGETS sdbus-c++
EXPORT sdbus-c++-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT static_libraries
@ -115,7 +137,7 @@ option(BUILD_TESTS "Build and install tests (default OFF)" OFF)
if(BUILD_TESTS)
enable_testing()
add_subdirectory("${CMAKE_SOURCE_DIR}/test")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
endif()
#----------------------------------
@ -125,17 +147,19 @@ endif()
option(BUILD_CODE_GEN "Build and install interface stub code generator (default OFF)" OFF)
if(BUILD_CODE_GEN)
add_subdirectory("${CMAKE_SOURCE_DIR}/stub-generator")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools")
endif()
#----------------------------------
# DOCUMENTATION
#----------------------------------
option(BUILD_DOC "Build doxygen documentation for sdbus-c++ API" ON)
option(BUILD_DOC "Build documentation for sdbus-c++" ON)
if(BUILD_DOC)
add_subdirectory("${CMAKE_SOURCE_DIR}/doc")
option(BUILD_DOXYGEN_DOC "Build doxygen documentation for sdbus-c++ API" OFF)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/docs")
install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR})
endif()
#----------------------------------
@ -143,15 +167,20 @@ endif()
#----------------------------------
include(CMakePackageConfigHelpers)
set(SDBUSCPP_CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++)
configure_file(sdbus-c++-config.cmake.in sdbus-c++-config.cmake @ONLY)
install(FILES ${CMAKE_BINARY_DIR}/sdbus-c++-config.cmake DESTINATION ${SDBUSCPP_CONFIG_INSTALL_DIR} COMPONENT dev)
install(EXPORT sdbus-c++-targets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
NAMESPACE SDBusCpp::
COMPONENT dev)
write_basic_package_version_file(sdbus-c++-config-version.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion)
install(FILES ${CMAKE_BINARY_DIR}/sdbus-c++-config-version.cmake DESTINATION ${SDBUSCPP_CONFIG_INSTALL_DIR} COMPONENT dev)
configure_package_config_file(cmake/sdbus-c++-config.cmake.in cmake/sdbus-c++-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++)
write_basic_package_version_file(cmake/sdbus-c++-config-version.cmake COMPATIBILITY SameMajorVersion)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
COMPONENT dev)
configure_file(sdbus-c++.pc.in sdbus-c++.pc @ONLY)
install(FILES ${CMAKE_BINARY_DIR}/sdbus-c++.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
configure_file(pkgconfig/sdbus-c++.pc.in pkgconfig/sdbus-c++.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)

View File

@ -54,7 +54,7 @@ v0.4.3
v0.5.0
- Redesign of the Connection model for scalability, thread-safety, simplicity and clarity
- [[Breaking ABI change]] Introduce support asynchronous invocation of D-Bus methods on client-side
- [[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
@ -73,3 +73,72 @@ 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

View File

@ -1,7 +1,7 @@
Building:
$ mkdir build
$ cd build
$ cmake .. ${CONFIGURE_FLAGS_IF_NECESSARY}
$ cmake .. -DCMAKE_BUILD_TYPE=Release ${OTHER_CONFIG_FLAGS}
$ make
Installing:

View File

@ -1,7 +1,11 @@
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
-----------------------------------
@ -11,12 +15,12 @@ The library is built using CMake:
```bash
$ mkdir build
$ cd build
$ cmake .. ${CONFIGURE_FLAGS_IF_NECESSARY}
$ cmake .. -DCMAKE_BUILD_TYPE=Release ${OTHER_CONFIG_FLAGS}
$ make
$ sudo make install
```
### CMake configuration flags
### CMake configuration flags for sdbus-c++
* `BUILD_CODE_GEN` [boolean]
@ -24,7 +28,11 @@ $ sudo make install
* `BUILD_DOC` [boolean]
Option for building Doxygen documentation of sdbus-c++ API. If `BUILD_DOC` is enabled, the documentation must still be built explicitly through `make doc`. Default value: `ON`. Use `-DBUILD_DOC=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
Option for including sdbus-c++ documentation files and tutorials. Default value: `ON`. With this option turned on, you may also enable/disable the following option:
* `BUILD_DOXYGEN_DOC` [boolean]
Option for building Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `make doc`. Default value: `OFF`. Use `-DBUILD_DOXYGEN_DOC=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
* `BUILD_TESTS` [boolean]
@ -42,12 +50,25 @@ $ sudo make install
Path where the test binaries shall get installed. Default value: `/opt/test/bin`.
* `BUILD_LIBSYSTEMD` [boolean]
Option for building libsystemd dependency library automatically when sdbus-c++ is built, and making libsystemd an integral part of sdbus-c++ library. Default value: `OFF`. Might be very helpful in non-systemd environments where libsystemd shared library is unavailable (see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information). With this option turned on, you may also provide the following configuration flag:
* `LIBSYSTEMD_VERSION` [string]
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`.
* `CMAKE_BUILD_TYPE` [string]
This is a CMake-builtin option. Set to `Release` to build sdbus-c++ for production use. Set to `Debug` if you want to help further develop (and debug) the library :)
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, will be downloaded and built automatically
* `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 (gcc >= 6, clang >= 3.7)
* `libsystemd` - systemd library containing sd-bus implementation. This library is part of systemd. Systemd at least v236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
Licensing
---------
@ -57,10 +78,10 @@ The library is distributed under LGPLv2.1 license.
References/documentation
------------------------
* [D-Bus Specification](https://dbus.freedesktop.org/doc/dbus-specification.html)
* [Using sdbus-c++](docs/using-sdbus-c++.md) - *the* main, comprehensive tutorial on sdbus-c++
* [Systemd and dbus configuration](docs/systemd-dbus-config.md)
* [D-Bus Specification](https://dbus.freedesktop.org/docs/dbus-specification.html)
* [sd-bus Overview](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html)
* [Tutorial: Using sdbus-c++](doc/using-sdbus-c++.md)
* [Systemd and dbus configuration](doc/systemd-dbus-config.md)
Contributing
------------

View File

@ -0,0 +1,57 @@
find_program(MESON meson)
find_program(NINJA ninja)
if((NOT MESON) OR (NOT NINJA))
message(FATAL_ERROR "Meson and Ninja are required to build libsystemd")
endif()
find_library(GLIBC_RT_LIBRARY rt)
find_package(PkgConfig REQUIRED)
pkg_check_modules(MOUNT mount)
pkg_check_modules(CAP REQUIRED libcap)
if (NOT CAP_FOUND)
find_library(CAP_LIBRARIES cap) # Compat with Ubuntu 14.04 which ships libcap w/o .pc file
endif()
set(LIBSYSTEMD_VERSION "242" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
if(NOT CMAKE_BUILD_TYPE)
set(LIBSYSTEMD_BUILD_TYPE "plain")
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(LIBSYSTEMD_BUILD_TYPE "debug")
else()
set(LIBSYSTEMD_BUILD_TYPE "release")
endif()
if(LIBSYSTEMD_VERSION LESS "239")
message(FATAL_ERROR "Only libsystemd version >=239 can be built as static part of sdbus-c++")
endif()
if(LIBSYSTEMD_VERSION GREATER "240")
set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h)
endif()
include(ExternalProject)
ExternalProject_Add(LibsystemdBuildProject
PREFIX libsystemd-v${LIBSYSTEMD_VERSION}
GIT_REPOSITORY https://github.com/systemd/systemd.git
GIT_TAG v${LIBSYSTEMD_VERSION}
GIT_SHALLOW 1
UPDATE_COMMAND ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
COMMAND ${MESON} --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Dstatic-libsystemd=pic <SOURCE_DIR> <BINARY_DIR>
BUILD_COMMAND ${BUILD_VERSION_H}
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
BUILD_ALWAYS 1
INSTALL_COMMAND ""
LOG_DOWNLOAD 1 LOG_UPDATE 1 LOG_CONFIGURE 1 LOG_BUILD 1)
ExternalProject_Get_property(LibsystemdBuildProject SOURCE_DIR)
set(SYSTEMD_INCLUDE_DIRS ${SOURCE_DIR}/src)
ExternalProject_Get_property(LibsystemdBuildProject BINARY_DIR)
set(SYSTEMD_LIBRARY_DIRS ${BINARY_DIR})
set(SYSTEMD_VERSION ${LIBSYSTEMD_VERSION})
add_library(Systemd::Libsystemd STATIC IMPORTED)
set_target_properties(Systemd::Libsystemd PROPERTIES IMPORTED_LOCATION ${SYSTEMD_LIBRARY_DIRS}/libsystemd.a)
set(SYSTEMD_LIBRARIES Systemd::Libsystemd ${CAP_LIBRARIES} ${GLIBC_RT_LIBRARY} ${MOUNT_LIBRARIES})

View File

@ -0,0 +1,11 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake")
check_required_components("@PROJECT_NAME@")
# This is here for backwards-compatibility. Please use more modern target-based approach.
set(SDBUSCPP_VERSION "@SDBUSCPP_VERSION@")
set(SDBUSCPP_FOUND "TRUE")
set_and_check(SDBUSCPP_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
set_and_check(SDBUSCPP_LIBRARY_DIR "@CMAKE_INSTALL_FULL_LIBDIR@")
set(SDBUSCPP_LIBRARIES sdbus-c++)

View File

@ -1,17 +0,0 @@
# Building doxygen documentation
find_package(Doxygen)
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)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR})
else()
message(WARNING "Documentation enabled, but Doxygen cannot be found")
endif()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

25
docs/CMakeLists.txt Normal file
View File

@ -0,0 +1,25 @@
# Building doxygen documentation
find_package(Doxygen)
if(BUILD_DOXYGEN_DOC)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL)
else()
message(WARNING "Documentation enabled, but Doxygen cannot be found")
endif()
endif()
install(FILES sdbus-c++-class-diagram.png
sdbus-c++-class-diagram.uml
systemd-dbus-config.md
using-sdbus-c++.md
DESTINATION ${CMAKE_INSTALL_DOCDIR})

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -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

View File

@ -5,46 +5,101 @@ Using sdbus-c++ library
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. [Design of sdbus-c++](#design-of-sdbus-c)
6. [Multiple layers of sdbus-c++ API](#multiple-layers-of-sdbus-c-api)
7. [An example: Number concatenator](#an-example-number-concatenator)
8. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
9. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
10. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs)
11. [Asynchronous server-side methods](#asynchronous-server-side-methods)
12. [Asynchronous client-side methods](#asynchronous-client-side-methods)
13. [Using D-Bus properties](#using-d-bus-properties)
14. [Conclusion](#conclusion)
3. [Solving libsystemd dependency](#solving-libsystemd-dependency)
4. [Distributing sdbus-c++](#distributing-sdbus-c)
5. [Header files and namespaces](#header-files-and-namespaces)
6. [Error signalling and propagation](#error-signalling-and-propagation)
7. [Design of sdbus-c++](#design-of-sdbus-c)
8. [Multiple layers of sdbus-c++ API](#multiple-layers-of-sdbus-c-api)
9. [An example: Number concatenator](#an-example-number-concatenator)
10. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
11. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
12. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs)
13. [Asynchronous server-side methods](#asynchronous-server-side-methods)
14. [Asynchronous client-side methods](#asynchronous-client-side-methods)
15. [Using D-Bus properties](#using-d-bus-properties)
16. [Standard D-Bus interfaces](#standard-d-bus-interfaces)
17. [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++ is a C++ D-Bus 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](https://github.com/systemd/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 CMake. The library provides a config file, so integrating it into your CMake project is rather straight-forward:
The library build system is based on CMake. The library provides a config and an export file, so integrating it into your CMake project is sooo simple:
```bash
```cmake
# First, find sdbus-c++
find_package(sdbus-c++ REQUIRED)
# Use the sdbus-c++ target in SDBusCpp namespace
add_executable(exe exe.cpp)
target_link_libraries(exe PRIVATE SDBusCpp::sdbus-c++)
```
The library also supports `pkg-config`, so it easily be integrated into e.g. an Autotools project:
```bash
PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.4],,
AC_MSG_ERROR([You need the sdbus-c++ library (version 0.4 or newer)]
PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.6],,
AC_MSG_ERROR([You need the sdbus-c++ library (version 0.6 or newer)]
[http://www.kistler.com/])
)
```
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.
Solving libsystemd dependency
-----------------------------
sdbus-c++ depends on libsystemd, a C library that is part of [systemd](https://github.com/systemd/systemd) and that contains underlying sd-bus implementation.
Minimum required libsystemd shared library version is 0.20.0 (which corresponds to minimum systemd version 236).
If your target Linux distribution is already based on systemd ecosystem of version 236 and higher, then there is no additional effort, just make sure you have corresponding systemd header files available (provided by `libsystemd-dev` package on Debian/Ubuntu, for example), and you may go on building sdbus-c++ seamlessly.
However, sdbus-c++ can perfectly be used in non-systemd environments as well. There are two ways to approach this:
### Building and distributing libsystemd as a shared library yourself
Fortunately, libsystemd is rather self-contained and can be built and used independently of the rest of systemd ecosystem. To build libsystemd shared library for sdbus-c++:
```shell
$ git clone https://github.com/systemd/systemd
$ cd systemd
$ git checkout v242 # or any other recent stable version
$ mkdir build
$ cd build
$ meson --buildtype=release .. # solve systemd dependencies if any pop up, e.g. libmount-dev, libcap, librt...
$ ninja version.h # building version.h target is only necessary in systemd version >= 241
$ ninja libsystemd.so.0.26.0 # or another version number depending which systemd version you have
# finally, manually install the library, header files and libsystemd.pc pkgconfig file
```
### Building and distributing libsystemd as part of sdbus-c++
sdbus-c++ provides `BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd enviroments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes `meson`, `ninja`, `git` programs and mainly libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Also when distributing, make sure these dependency libraries are installed on the production machine.
You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242).
Distributing sdbus-c++
----------------------
### Yocto
There are Yocto recipes for sdbus-c++ available in the [`meta-oe`](https://github.com/openembedded/meta-openembedded/tree/master/meta-oe/recipes-core/sdbus-c%2B%2B) layer of the `meta-openembedded` project. There are two recipes:
* One for sdbus-c++ itself. It detects whether systemd feature is turned on in the poky linux configuration. If so, it simply depends on systemd and makes use of libsystemd shared library available in the target system. Otherwise it automatically downloads and builds libsystemd static library and links it into the sdbus-c++ shared library. The recipe also supports ptest.
* One for sdbus-c++ native tools, namely sdbus-c++ code generator to generate C++ adaptor and proxy binding classes.
Tip: If you get `ERROR: Program or command 'getent' not found or not executable` when building sdbus-c++ in Yocto, please make sure you've added `getent` to `HOSTTOOLS`. For example, you can add `HOSTTOOLS_NONFATAL += "getent"` into your local.conf file.
Contributors willing to help with bringing sdbus-c++ to other popular package systems are welcome.
Header files and namespaces
---------------------------
@ -52,7 +107,7 @@ All sdbus-c++ header files reside in the `sdbus-c++` subdirectory within the sta
```cpp
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IObjectProxy.h>
#include <sdbus-c++/IProxy.h>
```
or just include the global header file that pulls in everything:
@ -87,25 +142,32 @@ The following diagram illustrates the major entities in sdbus-c++.
* 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:
`IProxy` 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, in both synchronous and asynchronous way,
* registering handlers for signals,
`Message` class represents a message, which is the fundamental DBus concept. There are three distinctive types of message that derive from the `Message` class:
`Message` class represents a message, which is the fundamental DBus concept. There are three distinctive types of message that are derived from the `Message` class:
* `MethodCall` (with serialized parameters),
* `MethodCall` (be it synchronous or asynchronous method call, with serialized parameters),
* `MethodReply` (with serialized return values),
* or a `Signal` (with serialized parameters).
* `Signal` (with serialized parameters),
* `PropertySetCall` (with serialized parameter value to be set)
* `PropertyGetReply` (where property value shall be stored)
* `PlainMessage` (for internal purposes).
### Thread safety in sdbus-c++
sdbus-c++ is thread-aware by design. But, in general, it's not thread-safe. At least not in all places. There are situations where sdbus-c++ provides and guarantees API-level thread safety by design. It is safe to do these operations from multiple threads at the same time:
sdbus-c++ is completely thread-aware by design. Though sdbus-c++ is not thread-safe in general, there are situations where sdbus-c++ provides and guarantees API-level thread safety by design. It is safe to do these operations (operations within the bullet points, not across them) from multiple threads at the same time:
* Making and destroying `Object`s and `ObjectProxy`s, even on a shared connection that is already running an event loop. Under *making* here is meant a complete atomic sequence of construction, registration of method/signal/property callbacks and export of the `Object`/`ObjectProxy` so it is ready to issue/receive messages. This sequence must be done in one thread.
* Making or destroying distinct `Object`/`Proxy` instances simultaneously (even on a shared connection that is running an event loop already, see below). Under *making* here is meant a complete sequence of construction, registration of method/signal/property callbacks and export of the `Object`/`Proxy` so it is ready to issue/receive messages. This sequence must be completely done within the context of one thread.
* Creating and sending asynchronous method replies on an `Object` instance.
* Creating and emitting signals on an `Object` instance.
* Creating and sending method calls (both synchronously and asynchronously) on an `ObjectProxy` instance. (But it's generally better that our threads use their own exclusive instances of object proxy, to minimize shared state and contention.)
* Creating and sending method calls (both synchronously and asynchronously) on an `Proxy` instance. (But it's generally better that our threads use their own exclusive instances of proxy, to minimize shared state and contention.)
sdbus-c++ is designed such that all the above operations are thread-safe also on a connection that is running an event loop (usually in a separate thread) at that time. It's an internal thread safety. For example, a signal arrives and is processed by sdbus-c++ even loop at an appropriate `Proxy` instance, while the user is going to destroy that instance in their application thread. The user cannot explicitly control these situations (or they could, but that would be very limiting and cubersome on the API level).
However, other combinations, that the user invokes explicitly from within more threads are NOT thread-safe in sdbus-c++ by design, and the user should make sure by their design that these cases never occur. For example, destroying an `Object` instance in one thread while emitting a signal on it in another thread is not thread-safe. In this specific case, the user should make sure in their application that all threads stop working with a specific instance before a thread proceeds with deleting that instance.
Multiple layers of sdbus-c++ API
-------------------------------
@ -115,7 +177,7 @@ sdbus-c++ API comes in two layers:
* [the basic layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer), which is a simple wrapper layer on top of sd-bus, using mechanisms that are native to C++ (e.g. serialization/deserialization of data from messages),
* [the 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.
sdbus-c++ also ships with a stub generator tool that converts D-Bus IDL in XML format into stub code for the adaptor as well as the proxy part. Hierarchically, these stubs provide yet another layer of convenience (the "stubs layer"), making it possible for D-Bus RPC calls to completely look like native C++ calls on a local object.
An example: Number concatenator
-------------------------------
@ -130,7 +192,7 @@ In the following sections, we will elaborate on the ways of implementing such an
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 their interface classes (`IConnection`, `IObject`, `IObjectProxy`), but, analogously to the underlying sd-bus C library, we still work on the level of D-Bus messages. We need to
In the basic API layer, we already have abstractions for D-Bus connections, objects and object proxies, with which we can interact via their interface classes (`IConnection`, `IObject`, `IProxy`), but, analogously to the underlying sd-bus C library, we still work on the level of D-Bus messages. We need to
* create them,
* serialize/deserialize arguments to/from them (thanks to many overloads of C++ insertion/extraction operators, this is very simple),
@ -235,7 +297,7 @@ int main(int argc, char *argv[])
// will create its own connection automatically, and it will be system bus connection.
const char* destinationName = "org.sdbuscpp.concatenator";
const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenatorProxy = sdbus::createObjectProxy(destinationName, objectPath);
auto concatenatorProxy = sdbus::createProxy(destinationName, objectPath);
// Let's subscribe for the 'concatenated' signals
const char* interfaceName = "org.sdbuscpp.Concatenator";
@ -277,7 +339,7 @@ int main(int argc, char *argv[])
}
```
In simple cases, we don't need to create D-Bus connection explicitly for our proxies. Unless a connection is provided to an object proxy explicitly via factory parameter, the proxy will create a connection of his own, and it will be a system bus connection. This is the case in the example above. (This approach is not scalable and resource-saving if we have plenty of proxies; see section [Working with D-Bus connections](#working-with-d-bus-connections-in-sdbus-c) for elaboration.) So, in the example, we create a proxy for object `/org/sdbuscpp/concatenator` publicly available at bus `org.sdbuscpp.concatenator`. We register signal handlers, if any, and finish the registration, making the object proxy ready for use.
In simple cases, we don't need to create D-Bus connection explicitly for our proxies. Unless a connection is provided to a proxy object explicitly via factory parameter, the proxy will create a connection of his own, and it will be a system bus connection. This is the case in the example above. (This approach is not scalable and resource-saving if we have plenty of proxies; see section [Working with D-Bus connections](#working-with-d-bus-connections-in-sdbus-c) for elaboration.) So, in the example, we create a proxy for object `/org/sdbuscpp/concatenator` publicly available at bus `org.sdbuscpp.concatenator`. We register signal handlers, if any, and finish the registration, making the proxy ready for use.
The callback for a D-Bus signal handler on this level is any callable of signature `void(sdbus::Signal& signal)`. The one and only parameter `signal` is the incoming signal message. We need to deserialize arguments from it, and then we can do our business logic with it.
@ -291,27 +353,33 @@ The design of D-Bus connections in sdbus-c++ allows for certain flexibility and
How shall we use connections in relation to D-Bus objects and object proxies?
A D-Bus connection is represented by a `Connection` instance. Each connection needs an event loop being run upon it. So it needs a thread handling the event loop. This thread serves all incoming and outgoing messages and all communication towards D-Bus daemon.
A D-Bus connection is represented by a `IConnection` instance. Each connection needs an event loop being run upon it. So it needs a thread handling the event loop. This thread serves all incoming and outgoing messages and all communication towards D-Bus daemon. One process can have one but also multiple D-Bus connections (we just have to make certain that the connections with assigned bus names don't share a common name; the name must be unique).
One process can have multiple D-Bus connections, with assigned unique bus names or without, as long as those with assigned bus names do not share a common bus name.
A typical use case for most services is **one** D-Bus connection in the application. The application runs event loop on that connection. When creating objects or proxies, the application provides reference of that connection to those objects and proxies. This means all these objects and proxies share the same connection. This is nicely scalable, because with whatever number of objects or proxies, there is only one connection and one event loop thread. Yet, services that provide objects at various bus names have to create and maintain multiple D-Bus connections, each with the unique bus name.
A D-Bus connection can be created for and used exclusively by one D-Bus object (represented by one `Object` instance) or D-Bus object proxy (represented by one `ObjectProxy` instance), but can very well be used by and shared across multiple objects, multiple proxies or even both multiple objects and proxies at the same time. When shared, one must bear in mind that the access to the connection is mutually exclusive and is serialized. This means, for example, that if an object's callback is going to be invoked for an incoming remote method call and in another thread we use a proxy to call remote method in another process, the threads are contending and only one can go on while the other must wait and can only proceed after the first one has finished, because both are using a shared resource -- the connection.
The connection is thread-safe and objects and proxies can invoke operations on it from multiple threads simultaneously, but the operations are serialized. This means, for example, that if an object's callback for an incoming remote method call is going to be invoked in an event loop thread, and in another thread we use a proxy to call remote method in another process, the threads are contending and only one can go on while the other must wait and can only proceed after the first one has finished, because both are using a shared resource -- the connection.
The former case (1:1) is one extreme; it's usually simple, has zero resource contention, but hurts scalability (for example, 50 proxies in our program need 50 D-Bus connections and 50 event loop threads upon them). The latter case (1:N) is the other extreme -- all D-Bus objects and proxies share one single connection. This is the most scalable solution (since, for example, 5 or 200 objects/proxies use always one single connection), but may increase contention and hurt concurrency (since only one of all those objects/proxies can work with the connection at a time). And then there are limitless options between the two (for example, we can use one connection for all objects, and another connection for all proxies in our service...). sdbus-c++ gives its users freedom to choose whatever approach is more suitable to them in their application at fine granularity.
We should bear that in mind when designing more complex, multi-threaded services with high parallelism. If we have undesired contention on a connection, creating a specific, dedicated connection for a hot spot helps to increase concurrency. sdbus-c++ provides us freedom to create as many connections as we want and assign objects and proxies to those connections at our will. We, as application developers, choose whatever approach is more suitable to us at quite a fine granularity.
How can we use connections from the server and the client perspective?
So, more technically, how can we use connections from the server and the client perspective?
* On the *server* side, we generally need to create D-Bus objects and publish their APIs. For that we first need a connection with a unique bus name. We need to create the D-Bus connection manually ourselves, request bus name on it, and manually launch its event loop (in a blocking way, through `enterProcessingLoop()`, or non-blocking async way, through `enterProcessingLoopAsync()`). At any time before or after running the event loop on the connection, we can create and "hook", as well as remove, objects and proxies upon that connection.
On the **server** side, we generally need to create D-Bus objects and publish their APIs. For that we first need a connection with a unique bus name. We need to create the D-Bus connection manually ourselves, request bus name on it, and manually launch its event loop:
* On the *client* side, for our D-Bus object proxies, we have more options (corresponding to three overloads of the `createObjectProxy()` factory):
* either in a blocking way, through `enterProcessingLoop()`,
* or in a non-blocking async way, through `enterProcessingLoopAsync()`),
* or, when we have our own implementation of an event loop (e.g. we are using sd-event event loop), we can ask the connection for its underlying fd and use that fd in our loop.
* We don't bother about any connection when creating a proxy. For each proxy instance sdbus-c++ also creates an internal connection instance to be used just by this proxy, and it will be a *system bus* connection. Additionally, an event loop thread for that connection is created and run internally.
Of course, at any time before or after running the event loop on the connection, we can create and "hook", as well as remove, objects and proxies upon that connection.
This hurts scalability (see discussion above), but our code is simpler, and since each proxy has its own connection, there is zero contention.
On the **client** side we have more options when creating D-Bus proxies. That corresponds to three overloads of the `createProxy()` factory:
* We create a connection explicitly by ourselves and `std::move` it to the object proxy factory. The proxy becomes an owner of this connection, and will run the event loop on that connection. This is the same as in the above bullet point, but with a flexibility that we can choose the bus type (system, session bus).
* In case we (the application) already maintain a D-Bus connection, e.g. because we a D-Bus service anyway, the simple and typical approach is to create proxy upon that connection. The proxy will share the connection with others. So we pass connection reference to proxy factory. With this approach we must of course ensure that the connection exists as long as the proxy exists.
* Or -- and this is typical when we have a simple D-Bus client application -- we have another option: we let proxy maintain its own connection (and an associated thread):
* We either create the connection ourselves and `std::move` it to the proxy object factory. The proxy becomes an owner of this connection, and will run the event loop on that connection. This had the advantage that we may choose the type of connection (system, session, remote).
* We are always full owners of the connection. We create the connection, and a proxy only takes and keeps a reference to it. We take care of the event loop upon that connection (and we must ensure the connection exists as long as all its users exist).
* Or we don't bother about any connection at all when creating a proxy (the factory overload with no connection parameter). Under the hood, the proxy creates its own *system bus* connection, creates a separate thread and runs an event loop in it. This is **the simplest approach** for non-complex D-Bus clients. For more complex ones, with big number of proxies, this hurts scalability but may improve concurrency (see discussion higher above), so we should make a conscious choice.
Implementing the Concatenator example using convenience sdbus-c++ API layer
---------------------------------------------------------------------------
@ -323,11 +391,11 @@ The convenience API layer abstracts the concept of underlying D-Bus messages awa
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,
- at a higher level of abstraction (closer to the abstraction level of the problem being solved),
- significantly shorter,
- almost as fast as one written using the basic API layer.
The code written using this layer expresses *what* it does, rather then *how*. Let's look at code samples.
The code written using this layer expresses in a declarative way *what* it does, rather then *how*. Let's look at code samples.
### Server side
@ -402,7 +470,7 @@ 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);
auto concatenatorProxy = sdbus::createProxy(destinationName, objectPath);
// Let's subscribe for the 'concatenated' signals
const char* interfaceName = "org.sdbuscpp.Concatenator";
@ -443,18 +511,25 @@ When registering methods, calling methods or emitting signals, multiple lines of
sdbus-c++ users shall prefer the convenience API to the lower level, basic API. When feasible, using generated adaptor and proxy stubs is even better. These stubs provide yet another, higher API level built on top of the convenience API. They are described in the following section.
Tip: When registering a D-Bus object, we can additionally provide names of input and output parameters of its methods and names of parameters of its signals. When the object is introspected, these names are listed in the resulting introspection XML, which improves the description of object's interfaces:
```c++
concatenator->registerMethod("concatenate").onInterface(interfaceName).withInputParamNames("numbers", "separator").withOutputParamNames("concatenatedString").implementedAs(&concatenate);
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>("concatenatedString");
```
Implementing the Concatenator example using sdbus-c++-generated stubs
---------------------------------------------------------------------
sdbus-c++ ships with the native stub generator tool called `sdbus-c++-xml2cpp`. The tool is very similar to `dbusxx-xml2cpp` tool that comes from dbus-c++ project.
sdbus-c++ ships with the native stub generator tool called `sdbus-c++-xml2cpp`. The tool is very similar to `dbusxx-xml2cpp` tool that comes with the dbus-c++ library.
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:
The generator tool takes D-Bus XML IDL description of D-Bus interfaces on its input, and can be instructed to generate one or both of these: an adaptor header file for use on the server side, and a proxy header file for use on the client side. Like this:
```bash
sdbus-c++-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.
The adaptor header file contains classes that can be used to implement interfaces described in the IDL (these classes represent object interfaces). The proxy header file contains classes that can be used to make calls to remote objects (these classes represent remote object interfaces).
### XML description of the Concatenator interface
@ -481,7 +556,7 @@ After running this through the stubs generator, we get the stub code that is des
### 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.
For each interface in the XML IDL file the generator creates one class that represents it. The class is de facto an interface which shall be implemented by the class inheriting it. The class' constructor takes care of registering all methods, signals and properties. For each D-Bus method there is a pure virtual member function. These pure virtual functions must be implemented in the child class. For each signal, there is a public function member that emits this signal.
```cpp
/*
@ -501,20 +576,22 @@ namespace sdbuscpp {
class Concatenator_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.Concatenator";
static constexpr const char* INTERFACE_NAME = "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>();
object_.registerMethod("concatenate").onInterface(INTERFACE_NAME).withInputParamNames("numbers", "separator").withOutputParamNames("concatenatedString").implementedAs([this](const std::vector<int32_t>& numbers, const std::string& separator){ return this->concatenate(numbers, separator); });
object_.registerSignal("concatenated").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
}
~Concatenator_adaptor() = default;
public:
void concatenated(const std::string& concatenatedString)
void emitConcatenated(const std::string& concatenatedString)
{
object_.emitSignal("concatenated").onInterface(interfaceName).withArguments(concatenatedString);
object_.emitSignal("concatenated").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
}
private:
@ -531,7 +608,7 @@ private:
### 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.
Analogously to the adaptor classes described above, there is one class generated for one interface in the XML IDL file. The class is de facto a proxy to the concrete single interface of a remote object. For each D-Bus signal there is a pure virtual member function whose body must be provided in a child class. For each method, there is a public function member that calls the method remotely.
```cpp
/*
@ -551,27 +628,29 @@ namespace sdbuscpp {
class Concatenator_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.Concatenator";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.Concatenator";
protected:
Concatenator_proxy(sdbus::IObjectProxy& object)
: object_(object)
Concatenator_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
object_.uponSignal("concatenated").onInterface(interfaceName).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
proxy_.uponSignal("concatenated").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
}
~Concatenator_proxy() = default;
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);
proxy_.callMethod("concatenate").onInterface(INTERFACE_NAME).withArguments(numbers, separator).storeResultsTo(result);
return result;
}
private:
sdbus::IObjectProxy& object_;
sdbus::IProxy& proxy_;
};
}} // namespaces
@ -581,18 +660,34 @@ private:
### 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.
To implement a D-Bus object that implements all its D-Bus interfaces, we now need to create a class representing the D-Bus object. This class must inherit from all corresponding `*_adaptor` classes (a-ka object interfaces, because these classes are as-if interfaces) and implement all pure virtual member functions.
How do we do that technically? Simply, our object class just needs to inherit from `AdaptorInterfaces` variadic template class. We fill its template arguments with a list of all generated interface classes. The `AdaptorInterfaces` is a convenience class that hides a few boiler-plate details. For example, in its constructor, it creates an `Object` instance, and it takes care of proper initialization of all adaptor superclasses.
In our object class we need to:
* Give an implementation to the D-Bus object's methods by overriding corresponding virtual functions,
* call `registerAdaptor()` in the constructor, which makes the adaptor (the D-Bus object underneath it) available for remote calls,
* call `unregisterAdaptor()`, which, conversely, unregisters the adaptor from the bus.
Calling `registerAdaptor()` and `unregisterAdaptor()` was not necessary in previous sdbus-c++ versions, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders.
```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*/>
class Concatenator : public sdbus::AdaptorInterfaces<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))
: sdbus::AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
}
~Concatenator()
{
unregisterAdaptor();
}
protected:
@ -610,7 +705,7 @@ protected:
}
// Emit the 'concatenated' signal with the resulting string
concatenated(result);
emitConcatenated(result);
// Return the resulting string
return result;
@ -638,11 +733,21 @@ int main(int argc, char *argv[])
}
```
It's that simple :)
Now we have a service with a unique bus name and a D-Bus object available on it. Let's write a client.
### 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.
To implement a proxy for a remote D-Bus object, we shall create a class representing the proxy object. This class must inherit from all corresponding `*_proxy` classes (a-ka remote object interfaces, because these classes are as-if interfaces) and -- if applicable -- implement all pure virtual member functions.
How do we do that technically? Simply, our proxy class just needs to inherit from `ProxyInterfaces` variadic template class. We fill its template arguments with a list of all generated interface classes. The `ProxyInterfaces` is a convenience class that hides a few boiler-plate details. For example, in its constructor, it can create a `Proxy` instance for us, and it takes care of proper initialization of all generated interface superclasses.
In our proxy class we need to:
* Give an implementation to signal handlers and asynchronous method reply handlers (if any) by overriding corresponding virtual functions,
* call `registerProxy()` in the constructor, which makes the proxy (the D-Bus proxy object underneath it) ready to receive signals and async call replies,
* call `unregisterProxy()`, which, conversely, unregisters the proxy from the bus.
Calling `registerProxy()` and `unregisterProxy()` was not necessary in previous versions of sdbus-c++, as it was handled by the parent class. This was convenient, but suffered from a potential pure virtual function call issue. Only the class that implements virtual functions can do the registration, hence this slight inconvenience on user's shoulders.
```cpp
#include <sdbus-c++/sdbus-c++.h>
@ -652,8 +757,14 @@ class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::Concatena
{
public:
ConcatenatorProxy(std::string destination, std::string objectPath)
: sdbus::ProxyInterfaces<org::sdbuscpp::Concatenator_proxy>(std::move(destination), std::move(objectPath))
: sdbus::ProxyInterfaces(std::move(destination), std::move(objectPath))
{
registerProxy();
}
~Concatenator()
{
unregisterProxy();
}
protected:
@ -664,9 +775,9 @@ protected:
};
```
In the above example, a proxy is created that creates and maintains its own system bus connection. However, there are `ProxyInterfaces` class template constructor overloads that also take the connection from the user as the first parameter, and pass that connection over to the underlying proxy. The connection instance is used for all D-Bus proxy interfaces listed in the `ProxyInterfaces` template parameter list.
In the above example, a proxy is created that creates and maintains its own system bus connection. However, there are `ProxyInterfaces` class template constructor overloads that also take the connection from the user as the first parameter, and pass that connection over to the underlying proxy. The connection instance is used by all interfaces listed in the `ProxyInterfaces` template parameter list.
Note however that there are multiple `ProxyInterfaces` constructor overloads, and they differ in how the proxy behaves towards the D-Bus connection. These overloads precisely map the `sdbus::createObjectProxy` overloads, as they are actually implemented on top of them. See [Proxy and D-Bus connection](#Proxy-and-D-Bus-connection) for more info.
Note however that there are multiple `ProxyInterfaces` constructor overloads, and they differ in how the proxy behaves towards the D-Bus connection. These overloads precisely map the `sdbus::createProxy` overloads, as they are actually implemented on top of them. See [Proxy and D-Bus connection](#Proxy-and-D-Bus-connection) for more info. We can even create a `IProxy` instance on our own, and inject it into our proxy class -- there is a constructor overload for it in `ProxyInterfaces`. This can help if we need to provide mocked implementations in our unit tests.
Now let's use this proxy to make remote calls and listen to signals in a real application.
@ -797,7 +908,7 @@ void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbe
methodResult.returnReply(result);
// Emit the 'concatenated' signal with the resulting string
this->concatenated(result);
this->emitConcatenated(result);
}).detach();
}
```
@ -828,7 +939,7 @@ sdbus-c++ stub generator can generate stub code for server-side async methods. W
</node>
```
For a real example of a server-side asynchronous D-Bus method, please look at sdbus-c++ [stress tests](/test/stresstests).
For a real example of a server-side asynchronous D-Bus method, please look at sdbus-c++ [stress tests](/tests/stresstests).
Asynchronous client-side methods
--------------------------------
@ -945,14 +1056,14 @@ For each client-side async method, a corresponding `on<MethodName>Reply` pure vi
So in the specific example above, the stub generator will generate a `Concatenator_proxy` class similar to one shown in a [dedicated section above](#concatenator-client-glueh), with the difference that it will also generate an additional `virtual void onConcatenateReply(const sdbus::Error* error, const std::string& concatenatedString);` method, which we shall override in derived `ConcatenatorProxy`.
For a real example of a client-side asynchronous D-Bus method, please look at sdbus-c++ [stress tests](/test/stresstests).
For a real example of a client-side asynchronous D-Bus method, please look at sdbus-c++ [stress tests](/tests/stresstests).
Using D-Bus properties
----------------------
Defining and working with D-Bus properties using XML description is quite easy.
### Defining a property in the XML
### Defining a property in the IDL
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.
@ -986,12 +1097,14 @@ public:
object_.registerProperty("status").onInterface(INTERFACE_NAME).withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); });
}
~PropertyProvider_adaptor() = default;
private:
// property getter
virtual uint32_t status() = 0;
// property setter
virtual void status(const uint32_t& value) = 0;
/*...*/
};
#endif
@ -1003,7 +1116,7 @@ The proxy:
class PropertyProvider_proxy
{
/*...*/
public:
// getting the property value
uint32_t status()
@ -1016,13 +1129,31 @@ public:
{
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.
Standard D-Bus interfaces
-------------------------
sdbus-c++ provides support for standard D-Bus interfaces. These are:
* `org.freedesktop.DBus.Peer`
* `org.freedesktop.DBus.Introspectable`
* `org.freedesktop.DBus.Properties`
* `org.freedesktop.DBus.ObjectManager`
The implementation of methods that these interfaces define is provided by the library. `Peer`, `Introspectable` and `Properties` are automatically part of interfaces of every D-Bus object. `ObjectManager` is not automatically present and has to be enabled by the client when using `IObject` API. When using generated `ObjectManager_adaptor`, `ObjectManager` is enabled automatically in its constructor.
Pre-generated `*_proxy` and `*_adaptor` convenience classes for these standard interfaces are located in `sdbus-c++/StandardInterfaces.h`. We add them simply as additional parameters of `sdbus::ProxyInterfaces` or `sdbus::AdaptorInterfaces` class template, and our proxy or adaptor class inherits convenience functions from those interface classes.
For example, to conveniently emit a `PropertyChanged` signal under `org.freedesktop.DBus.Properties` interface, we just issue `emitPropertiesChangedSignal` function of our adaptor object.
Note that signals of afore-mentioned standard D-Bus interfaces are not emitted by the library automatically. It's clients who are supposed to emit them.
Conclusion
----------

View File

@ -0,0 +1,140 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorInterfaces.h
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_ADAPTORINTERFACES_H_
#define SDBUS_CXX_ADAPTORINTERFACES_H_
#include <sdbus-c++/IObject.h>
#include <cassert>
#include <string>
#include <memory>
// Forward declarations
namespace sdbus {
class IConnection;
}
namespace sdbus {
/********************************************//**
* @class ObjectHolder
*
* ObjectHolder is a helper that simply owns and provides
* access to an object to other classes in the inheritance
* hierarchy of an object based on generated interface classes.
*
***********************************************/
class ObjectHolder
{
protected:
ObjectHolder(std::unique_ptr<IObject>&& object)
: object_(std::move(object))
{
}
const IObject& getObject() const
{
assert(object_ != nullptr);
return *object_;
}
IObject& getObject()
{
assert(object_ != nullptr);
return *object_;
}
private:
std::unique_ptr<IObject> object_;
};
/********************************************//**
* @class AdaptorInterfaces
*
* AdaptorInterfaces is a helper template class that joins all interface classes of a remote
* D-Bus object generated by sdbus-c++-xml2cpp to be used on the server (the adaptor) side,
* including some auxiliary classes. AdaptorInterfaces is the class that native-like object
* implementation classes written by users should inherit from and implement all pure virtual
* methods. So the _Interfaces template parameter is a list of sdbus-c++-xml2cpp-generated
* adaptor-side interface classes representing interfaces (with methods, signals and properties)
* of the D-Bus object.
*
* In the final adaptor class inherited from AdaptorInterfaces, it is necessary to finish
* adaptor registration in class constructor (finishRegistration();`), and, conversely,
* unregister the adaptor in class destructor (`unregister();`).
*
***********************************************/
template <typename... _Interfaces>
class AdaptorInterfaces
: protected ObjectHolder
, public _Interfaces...
{
public:
/*!
* @brief Creates object instance
*
* @param[in] connection D-Bus connection where the object will publish itself
* @param[in] objectPath Path of the D-Bus object
*
* For more information, consult @ref createObject(sdbus::IConnection&,std::string)
*/
AdaptorInterfaces(IConnection& connection, std::string objectPath)
: ObjectHolder(createObject(connection, std::move(objectPath)))
, _Interfaces(getObject())...
{
}
/*!
* @brief Finishes adaptor API registration and publishes the adaptor on the bus
*
* This function must be called in the constructor of the final adaptor class that implements AdaptorInterfaces.
*
* For more information, see underlying @ref IObject::finishRegistration()
*/
void registerAdaptor()
{
getObject().finishRegistration();
}
/*!
* @brief Unregisters adaptors's API and removes it from the bus
*
* This function must be called in the destructor of the final adaptor class that implements AdaptorInterfaces.
*
* For more information, see underlying @ref IObject::unregister()
*/
void unregisterAdaptor()
{
getObject().unregister();
}
protected:
using base_type = AdaptorInterfaces;
};
}
#endif /* SDBUS_CXX_ADAPTORINTERFACES_H_ */

View File

@ -1,7 +1,8 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ConvenienceClasses.h
* @file ConvenienceApiClasses.h
*
* Created on: Jan 19, 2017
* Project: sdbus-c++
@ -23,38 +24,49 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_CONVENIENCECLASSES_H_
#define SDBUS_CXX_CONVENIENCECLASSES_H_
#ifndef SDBUS_CXX_CONVENIENCEAPICLASSES_H_
#define SDBUS_CXX_CONVENIENCEAPICLASSES_H_
#include <sdbus-c++/Message.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Flags.h>
#include <string>
#include <vector>
#include <type_traits>
#include <chrono>
#include <cstdint>
// Forward declarations
namespace sdbus {
class IObject;
class IObjectProxy;
class IProxy;
class Variant;
class Error;
}
namespace sdbus {
template <typename... _Args>
inline constexpr bool are_strings_v = std::conjunction<std::is_convertible<_Args, std::string>...>::value;
class MethodRegistrator
{
public:
MethodRegistrator(IObject& object, const std::string& methodName);
MethodRegistrator(MethodRegistrator&& other) = default;
MethodRegistrator& operator=(MethodRegistrator&& other) = default;
~MethodRegistrator() noexcept(false);
MethodRegistrator& onInterface(const std::string& interfaceName);
MethodRegistrator& onInterface(std::string interfaceName);
template <typename _Function>
std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&> implementedAs(_Function&& callback);
template <typename _Function>
std::enable_if_t<is_async_method_v<_Function>, MethodRegistrator&> implementedAs(_Function&& callback);
MethodRegistrator& withInputParamNames(std::vector<std::string> paramNames);
template <typename... _String>
std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> withInputParamNames(_String... paramNames);
MethodRegistrator& withOutputParamNames(std::vector<std::string> paramNames);
template <typename... _String>
std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> withOutputParamNames(_String... paramNames);
MethodRegistrator& markAsDeprecated();
MethodRegistrator& markAsPrivileged();
MethodRegistrator& withNoReply();
@ -64,7 +76,9 @@ namespace sdbus {
const std::string& methodName_;
std::string interfaceName_;
std::string inputSignature_;
std::vector<std::string> inputParamNames_;
std::string outputSignature_;
std::vector<std::string> outputParamNames_;
method_callback methodCallback_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
@ -75,11 +89,13 @@ namespace sdbus {
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> SignalRegistrator& withParameters();
template <typename... _Args> SignalRegistrator& withParameters(std::vector<std::string> paramNames);
template <typename... _Args, typename... _String>
std::enable_if_t<are_strings_v<_String...>, SignalRegistrator&> withParameters(_String... paramNames);
SignalRegistrator& markAsDeprecated();
private:
@ -87,6 +103,7 @@ namespace sdbus {
const std::string& signalName_;
std::string interfaceName_;
std::string signalSignature_;
std::vector<std::string> paramNames_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
};
@ -96,10 +113,9 @@ namespace sdbus {
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);
PropertyRegistrator& onInterface(std::string interfaceName);
template <typename _Function> PropertyRegistrator& withGetter(_Function&& callback);
template <typename _Function> PropertyRegistrator& withSetter(_Function&& callback);
PropertyRegistrator& markAsDeprecated();
@ -122,7 +138,6 @@ namespace sdbus {
public:
InterfaceFlagsSetter(IObject& object, const std::string& interfaceName);
InterfaceFlagsSetter(InterfaceFlagsSetter&& other) = default;
InterfaceFlagsSetter& operator=(InterfaceFlagsSetter&& other) = default;
~InterfaceFlagsSetter() noexcept(false);
InterfaceFlagsSetter& markAsDeprecated();
@ -142,7 +157,6 @@ namespace sdbus {
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);
@ -157,20 +171,23 @@ namespace sdbus {
class MethodInvoker
{
public:
MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName);
MethodInvoker(IProxy& proxy, const std::string& methodName);
MethodInvoker(MethodInvoker&& other) = default;
MethodInvoker& operator=(MethodInvoker&& other) = default;
~MethodInvoker() noexcept(false);
MethodInvoker& onInterface(const std::string& interfaceName);
MethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
MethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
template <typename... _Args> void storeResultsTo(_Args&... args);
void dontExpectReply();
private:
IObjectProxy& objectProxy_;
IProxy& proxy_;
const std::string& methodName_;
uint64_t timeout_{};
MethodCall method_;
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
bool methodCalled_{};
@ -179,54 +196,59 @@ namespace sdbus {
class AsyncMethodInvoker
{
public:
AsyncMethodInvoker(IObjectProxy& objectProxy, const std::string& methodName);
AsyncMethodInvoker(IProxy& proxy, const std::string& methodName);
AsyncMethodInvoker& onInterface(const std::string& interfaceName);
AsyncMethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
template <typename _Function> void uponReplyInvoke(_Function&& callback);
private:
IObjectProxy& objectProxy_;
IProxy& proxy_;
const std::string& methodName_;
AsyncMethodCall method_;
uint64_t timeout_{};
MethodCall method_;
};
class SignalSubscriber
{
public:
SignalSubscriber(IObjectProxy& objectProxy, const std::string& signalName);
SignalSubscriber& onInterface(const std::string& interfaceName);
SignalSubscriber(IProxy& proxy, const std::string& signalName);
SignalSubscriber& onInterface(std::string interfaceName);
template <typename _Function> void call(_Function&& callback);
private:
IObjectProxy& objectProxy_;
std::string signalName_;
IProxy& proxy_;
const std::string& signalName_;
std::string interfaceName_;
};
class PropertyGetter
{
public:
PropertyGetter(IObjectProxy& objectProxy, const std::string& propertyName);
PropertyGetter(IProxy& proxy, const std::string& propertyName);
sdbus::Variant onInterface(const std::string& interfaceName);
private:
IObjectProxy& objectProxy_;
std::string propertyName_;
IProxy& proxy_;
const std::string& propertyName_;
};
class PropertySetter
{
public:
PropertySetter(IObjectProxy& objectProxy, const std::string& propertyName);
PropertySetter& onInterface(const std::string& interfaceName);
PropertySetter(IProxy& proxy, const std::string& propertyName);
PropertySetter& onInterface(std::string interfaceName);
template <typename _Value> void toValue(const _Value& value);
void toValue(const sdbus::Variant& value);
private:
IObjectProxy& objectProxy_;
IProxy& proxy_;
const std::string& propertyName_;
std::string interfaceName_;
};
}
#endif /* SDBUS_CXX_CONVENIENCECLASSES_H_ */
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */

View File

@ -1,7 +1,8 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ConvenienceClasses.inl
* @file ConvenienceApiClasses.inl
*
* Created on: Dec 19, 2016
* Project: sdbus-c++
@ -23,11 +24,11 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CPP_CONVENIENCECLASSES_INL_
#define SDBUS_CPP_CONVENIENCECLASSES_INL_
#ifndef SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
#define SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/IObjectProxy.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/Types.h>
@ -35,16 +36,19 @@
#include <sdbus-c++/Error.h>
#include <string>
#include <tuple>
/*#include <exception>*/
#include <exception>
#include <cassert>
namespace sdbus {
// Moved into the library to isolate from C++17 dependency
/*
/*** ----------------- ***/
/*** MethodRegistrator ***/
/*** ----------------- ***/
inline MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
: object_(object)
, methodName_(methodName)
, exceptions_(std::uncaught_exceptions()) // Needs C++17
, exceptions_(std::uncaught_exceptions())
{
}
@ -54,8 +58,8 @@ namespace sdbus {
if (std::uncaught_exceptions() != exceptions_)
return;
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(methodCallback_); // implementedAs() must be placed/called prior to this function
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
@ -66,19 +70,26 @@ namespace sdbus {
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
object_.registerMethod( interfaceName_
, std::move(methodName_)
, std::move(inputSignature_)
, std::move(inputParamNames_)
, std::move(outputSignature_)
, std::move(outputParamNames_)
, std::move(methodCallback_)
, std::move(flags_));
}
*/
inline MethodRegistrator& MethodRegistrator::onInterface(const std::string& interfaceName)
inline MethodRegistrator& MethodRegistrator::onInterface(std::string interfaceName)
{
interfaceName_ = interfaceName;
interfaceName_ = std::move(interfaceName);
return *this;
}
template <typename _Function>
inline std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&> MethodRegistrator::implementedAs(_Function&& callback)
inline std::enable_if_t<!is_async_method_v<_Function>, MethodRegistrator&>
MethodRegistrator::implementedAs(_Function&& callback)
{
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
@ -107,7 +118,8 @@ namespace sdbus {
}
template <typename _Function>
inline std::enable_if_t<is_async_method_v<_Function>, MethodRegistrator&> MethodRegistrator::implementedAs(_Function&& callback)
inline std::enable_if_t<is_async_method_v<_Function>, MethodRegistrator&>
MethodRegistrator::implementedAs(_Function&& callback)
{
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
@ -127,6 +139,32 @@ namespace sdbus {
return *this;
}
inline MethodRegistrator& MethodRegistrator::withInputParamNames(std::vector<std::string> paramNames)
{
inputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> MethodRegistrator::withInputParamNames(_String... paramNames)
{
return withInputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(std::vector<std::string> paramNames)
{
outputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline std::enable_if_t<are_strings_v<_String...>, MethodRegistrator&> MethodRegistrator::withOutputParamNames(_String... paramNames)
{
return withOutputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
@ -148,12 +186,13 @@ namespace sdbus {
return *this;
}
/*** ----------------- ***/
/*** SignalRegistrator ***/
/*** ----------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
inline SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)
: object_(object)
, signalName_(std::move(signalName))
, signalName_(signalName)
, exceptions_(std::uncaught_exceptions())
{
}
@ -164,8 +203,7 @@ namespace sdbus {
if (std::uncaught_exceptions() != exceptions_)
return;
if (interfaceName_.empty())
throw sdbus::Exception("DBus interface not specified when registering a DBus signal");
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
@ -176,9 +214,12 @@ namespace sdbus {
// 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_);
object_.registerSignal( interfaceName_
, std::move(signalName_)
, std::move(signalSignature_)
, std::move(paramNames_)
, std::move(flags_) );
}
*/
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
{
@ -195,6 +236,23 @@ namespace sdbus {
return *this;
}
template <typename... _Args>
inline SignalRegistrator& SignalRegistrator::withParameters(std::vector<std::string> paramNames)
{
paramNames_ = std::move(paramNames);
return withParameters<_Args...>();
}
template <typename... _Args, typename... _String>
inline std::enable_if_t<are_strings_v<_String...>, SignalRegistrator&>
SignalRegistrator::withParameters(_String... paramNames)
{
static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
return withParameters<_Args...>({paramNames...});
}
inline SignalRegistrator& SignalRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
@ -202,12 +260,13 @@ namespace sdbus {
return *this;
}
/*** ------------------- ***/
/*** PropertyRegistrator ***/
/*** ------------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
inline PropertyRegistrator::PropertyRegistrator(IObject& object, std::string propertyName)
inline PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
: object_(object)
, propertyName_(std::move(propertyName))
, propertyName_(propertyName)
, exceptions_(std::uncaught_exceptions())
{
}
@ -218,7 +277,7 @@ namespace sdbus {
if (std::uncaught_exceptions() != exceptions_)
return;
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus property", EINVAL);
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
@ -229,17 +288,17 @@ namespace sdbus {
// 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_)
object_.registerProperty( interfaceName_
, propertyName_
, propertySignature_
, std::move(getter_)
, std::move(setter_) );
, std::move(setter_)
, flags_ );
}
*/
inline PropertyRegistrator& PropertyRegistrator::onInterface(const std::string& interfaceName)
inline PropertyRegistrator& PropertyRegistrator::onInterface(std::string interfaceName)
{
interfaceName_ = interfaceName;
interfaceName_ = std::move(interfaceName);
return *this;
}
@ -253,10 +312,10 @@ namespace sdbus {
if (propertySignature_.empty())
propertySignature_ = signature_of_function_output_arguments<_Function>::str();
getter_ = [callback = std::forward<_Function>(callback)](Message& msg)
getter_ = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply)
{
// Get the propety value and serialize it into the message
msg << callback();
// Get the propety value and serialize it into the pre-constructed reply message
reply << callback();
};
return *this;
@ -271,14 +330,14 @@ namespace sdbus {
if (propertySignature_.empty())
propertySignature_ = signature_of_function_input_arguments<_Function>::str();
setter_ = [callback = std::forward<_Function>(callback)](Message& msg)
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 message
msg >> property;
// Deserialize property value from the incoming call message
call >> property;
// Invoke setter with the value
callback(property);
@ -308,9 +367,10 @@ namespace sdbus {
return *this;
}
/*** -------------------- ***/
/*** InterfaceFlagsSetter ***/
/*** -------------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
inline InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
: object_(object)
, interfaceName_(interfaceName)
@ -324,8 +384,6 @@ namespace sdbus {
if (std::uncaught_exceptions() != exceptions_)
return;
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when setting its flags", EINVAL);
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
@ -335,10 +393,8 @@ namespace sdbus {
// Therefore, we can allow setInterfaceFlags() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.setInterfaceFlags( std::move(interfaceName_)
, std::move(flags_) );
object_.setInterfaceFlags(interfaceName_, std::move(flags_));
}
*/
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsDeprecated()
{
@ -368,9 +424,10 @@ namespace sdbus {
return *this;
}
/*** ------------- ***/
/*** SignalEmitter ***/
/*** ------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
inline SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
: object_(object)
, signalName_(signalName)
@ -384,9 +441,6 @@ namespace sdbus {
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
@ -398,7 +452,6 @@ namespace sdbus {
// to the exception thrown from here if the caller is a destructor itself.
object_.emitSignal(signal_);
}
*/
inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
{
@ -410,16 +463,17 @@ namespace sdbus {
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);
assert(signal_.isValid()); // onInterface() must be placed/called prior to withArguments()
detail::serialize_pack(signal_, std::forward<_Args>(args)...);
}
/*** ------------- ***/
/*** MethodInvoker ***/
/*** ------------- ***/
// Moved into the library to isolate from C++17 dependency
/*
inline MethodInvoker::MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName)
: objectProxy_(objectProxy)
inline MethodInvoker::MethodInvoker(IProxy& proxy, const std::string& methodName)
: proxy_(proxy)
, methodName_(methodName)
, exceptions_(std::uncaught_exceptions())
{
@ -432,9 +486,6 @@ namespace sdbus {
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
@ -444,21 +495,34 @@ namespace sdbus {
// 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_);
proxy_.callMethod(method_, timeout_);
}
*/
inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
{
method_ = objectProxy_.createMethodCall(interfaceName, methodName_);
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)
{
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
detail::serialize_pack(method_, std::forward<_Args>(args)...);
@ -468,9 +532,9 @@ namespace sdbus {
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);
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
auto reply = objectProxy_.callMethod(method_);
auto reply = proxy_.callMethod(method_, timeout_);
methodCalled_ = true;
detail::deserialize_pack(reply, args...);
@ -478,29 +542,46 @@ namespace sdbus {
inline void MethodInvoker::dontExpectReply()
{
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
method_.dontExpectReply();
}
/*** ------------------ ***/
/*** AsyncMethodInvoker ***/
/*** ------------------ ***/
inline AsyncMethodInvoker::AsyncMethodInvoker(IObjectProxy& objectProxy, const std::string& methodName)
: objectProxy_(objectProxy)
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const std::string& methodName)
: proxy_(proxy)
, methodName_(methodName)
{
}
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const std::string& interfaceName)
{
method_ = objectProxy_.createAsyncMethodCall(interfaceName, methodName_);
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)
{
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
detail::serialize_pack(method_, std::forward<_Args>(args)...);
@ -510,9 +591,9 @@ namespace sdbus {
template <typename _Function>
void AsyncMethodInvoker::uponReplyInvoke(_Function&& callback)
{
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
objectProxy_.callMethod(method_, [callback = std::forward<_Function>(callback)](MethodReply& reply, const Error* error)
auto asyncReplyHandler = [callback = std::forward<_Function>(callback)](MethodReply& reply, const Error* error)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the message.
@ -524,19 +605,24 @@ namespace sdbus {
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, error, args); // TODO: Use std::apply when switching to full C++17 support
});
};
proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
}
/*** ---------------- ***/
/*** SignalSubscriber ***/
/*** ---------------- ***/
inline SignalSubscriber::SignalSubscriber(IObjectProxy& objectProxy, const std::string& signalName)
: objectProxy_(objectProxy)
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const std::string& signalName)
: proxy_(proxy)
, signalName_(signalName)
{
}
inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName)
inline SignalSubscriber& SignalSubscriber::onInterface(std::string interfaceName)
{
interfaceName_ = interfaceName;
interfaceName_ = std::move(interfaceName);
return *this;
}
@ -544,11 +630,11 @@ namespace sdbus {
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);
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
objectProxy_.registerSignalHandler( interfaceName_
, signalName_
, [callback = std::forward<_Function>(callback)](Signal& signal)
proxy_.registerSignalHandler( interfaceName_
, signalName_
, [callback = std::forward<_Function>(callback)](Signal& signal)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the signal message.
@ -562,9 +648,12 @@ namespace sdbus {
});
}
/*** -------------- ***/
/*** PropertyGetter ***/
/*** -------------- ***/
inline PropertyGetter::PropertyGetter(IObjectProxy& objectProxy, const std::string& propertyName)
: objectProxy_(objectProxy)
inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
, propertyName_(propertyName)
{
}
@ -572,7 +661,7 @@ namespace sdbus {
inline sdbus::Variant PropertyGetter::onInterface(const std::string& interfaceName)
{
sdbus::Variant var;
objectProxy_
proxy_
.callMethod("Get")
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(interfaceName, propertyName_)
@ -580,16 +669,19 @@ namespace sdbus {
return var;
}
/*** -------------- ***/
/*** PropertySetter ***/
/*** -------------- ***/
inline PropertySetter::PropertySetter(IObjectProxy& objectProxy, const std::string& propertyName)
: objectProxy_(objectProxy)
inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
, propertyName_(propertyName)
{
}
inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
inline PropertySetter& PropertySetter::onInterface(std::string interfaceName)
{
interfaceName_ = interfaceName;
interfaceName_ = std::move(interfaceName);
return *this;
}
@ -597,14 +689,19 @@ namespace sdbus {
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);
PropertySetter::toValue(sdbus::Variant{value});
}
objectProxy_
inline void PropertySetter::toValue(const sdbus::Variant& value)
{
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
proxy_
.callMethod("Set")
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(interfaceName_, propertyName_, sdbus::Variant{value});
.withArguments(interfaceName_, propertyName_, value);
}
}
#endif /* SDBUS_CPP_CONVENIENCECLASSES_INL_ */
#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */

View File

@ -1,7 +1,8 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ConvenienceClasses.h
* @file Error.h
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
@ -26,7 +27,9 @@
#ifndef SDBUS_CXX_ERROR_H_
#define SDBUS_CXX_ERROR_H_
#include <errno.h>
#include <stdexcept>
#include <string>
namespace sdbus {

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Flags.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IConnection.h
*
@ -26,9 +27,10 @@
#ifndef SDBUS_CXX_ICONNECTION_H_
#define SDBUS_CXX_ICONNECTION_H_
//#include <cstdint>
#include <string>
#include <memory>
#include <chrono>
#include <cstdint>
namespace sdbus {
@ -45,113 +47,218 @@ namespace sdbus {
class IConnection
{
public:
struct PollData
{
int fd;
short int events;
uint64_t timeout_usec;
};
virtual ~IConnection() = default;
/*!
* @brief Requests D-Bus name on the connection
*
* @param[in] name Name to request
*
* @throws sdbus::Error in case of failure
*/
* @brief Requests D-Bus name on the connection
*
* @param[in] name Name to request
*
* @throws sdbus::Error in case of failure
*/
virtual void requestName(const std::string& name) = 0;
/*!
* @brief Releases D-Bus name on the connection
*
* @param[in] name Name to release
*
* @throws sdbus::Error in case of failure
*/
* @brief Releases D-Bus name on the connection
*
* @param[in] name Name to release
*
* @throws sdbus::Error in case of failure
*/
virtual void releaseName(const std::string& name) = 0;
/*!
* @brief Enters the D-Bus processing loop
*
* The incoming D-Bus messages are processed in the loop. The method
* blocks indefinitely, until unblocked via leaveProcessingLoop.
*
* @throws sdbus::Error in case of failure
*/
* @brief Retrieve the unique name of a connection. E.g. ":1.xx"
*
* @throws sdbus::Error in case of failure
*/
virtual std::string getUniqueName() const = 0;
/*!
* @brief Enters the D-Bus processing loop
*
* 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 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.
*/
* @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 D-Bus processing loop
*
* Ends the previously started processing loop.
*
* @throws sdbus::Error in case of failure
*/
* @brief Leaves the D-Bus processing loop
*
* Ends the previously started processing loop.
*
* @throws sdbus::Error in case of failure
*/
virtual void leaveProcessingLoop() = 0;
inline virtual ~IConnection() = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* @throws sdbus::Error in case of failure
*/
virtual void addObjectManager(const std::string& objectPath) = 0;
/*!
* @brief Returns parameters you can pass to poll
*
* To integrate sdbus with your app's own custom event handling system
* (without the requirement of an extra thread), you can use this
* method to query which file descriptors, poll events and timeouts you
* should add to your app's poll call in your main event loop. If these
* file descriptors signal, then you should call processPendingRequest
* to process the event. This means that all of sdbus's callbacks will
* arrive on your app's main event thread (opposed to on a thread created
* by sdbus-c++). If you are unsure what this all means then use
* enterProcessingLoop() or enterProcessingLoopAsync() instead.
*
* To integrate sdbus-c++ into a gtk app, pass the file descriptor returned
* by this method to g_main_context_add_poll.
*
* @throws sdbus::Error in case of failure
*/
virtual PollData getProcessLoopPollData() const = 0;
/*!
* @brief Process a pending request
*
* Processes a single dbus event. All of sdbus-c++'s callbacks will be called
* from within this method. This method should ONLY be used in conjuction
* with getProcessLoopPollData(). enterProcessingLoop() and
* enterProcessingLoopAsync() will call this method for you, so there is no
* need to call it when using these. If you are unsure what this all means then
* don't use this method.
*
* @returns true if an event was processed
* @throws sdbus::Error in case of failure
*/
virtual bool processPendingRequest() = 0;
/*!
* @brief Sets general method call timeout
*
* @param[in] timeout Timeout value in microseconds
*
* General method call timeout is used for all method calls upon this connection.
* Method call-specific timeout overrides this general setting.
*
* Supported by libsystemd>=v240.
*
* @throws sdbus::Error in case of failure
*/
virtual void setMethodCallTimeout(uint64_t timeout) = 0;
/*!
* @copydoc IConnection::setMethodCallTimeout(uint64_t)
*/
template <typename _Rep, typename _Period>
void setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
/*!
* @brief Gets general method call timeout
*
* @return Timeout value in microseconds
*
* Supported by libsystemd>=v240.
*
* @throws sdbus::Error in case of failure
*/
virtual uint64_t getMethodCallTimeout() const = 0;
};
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
*/
* @brief Creates/opens D-Bus system connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createConnection();
/*!
* @brief Creates/opens D-Bus system connection 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
*/
* @brief Creates/opens D-Bus system connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus system connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @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 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
*/
* @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 session connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
* @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 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
*/
* @brief Creates/opens D-Bus session connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus system connection 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
*/
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
}
#endif /* SDBUS_CXX_ICONNECTION_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IObject.h
*
@ -26,12 +27,13 @@
#ifndef SDBUS_CXX_IOBJECT_H_
#define SDBUS_CXX_IOBJECT_H_
#include <sdbus-c++/ConvenienceClasses.h>
#include <sdbus-c++/ConvenienceApiClasses.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Flags.h>
#include <functional>
#include <string>
#include <memory>
#include <vector>
// Forward declarations
namespace sdbus {
@ -44,241 +46,399 @@ 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.
* In general, the class is thread-aware, but not thread-safe.
* However, the operation of creating and sending asynchronous
* method replies, as well as creating and emitting
* signals, is 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:
virtual ~IObject() = default;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the method will belong to
* @param[in] methodName Name of the method
* @param[in] inputSignature D-Bus signature of method input parameters
* @param[in] outputSignature D-Bus signature of method output parameters
* @param[in] methodCallback Callback that implements the body of the method
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the method will belong to
* @param[in] methodName Name of the method
* @param[in] inputSignature D-Bus signature of method input parameters
* @param[in] outputSignature D-Bus signature of method output parameters
* @param[in] methodCallback Callback that implements the body of the method
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags = {} ) = 0;
/*!
* @brief Registers signal that the object will emit on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] flags D-Bus signal flags (deprecated)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the method will belong to
* @param[in] methodName Name of the method
* @param[in] inputSignature D-Bus signature of method input parameters
* @param[in] inputNames Names of input parameters
* @param[in] outputSignature D-Bus signature of method output parameters
* @param[in] outputNames Names of output parameters
* @param[in] methodCallback Callback that implements the body of the method
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
*
* Provided names of input and output parameters will be included in the introspection
* description (given that at least version 242 of underlying libsystemd library is
* used; otherwise, names of parameters are ignored). This usually helps better describe
* the API to the introspector.
*
* @throws sdbus::Error in case of failure
*/
virtual void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags = {} ) = 0;
/*!
* @brief Registers read-only property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers signal that the object will emit on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] flags D-Bus signal flags (deprecated)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, Flags flags = {} ) = 0;
/*!
* @brief Registers signal that the object will emit on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] paramNames Names of parameters of the signal
* @param[in] flags D-Bus signal flags (deprecated)
*
* Provided names of signal output parameters will be included in the introspection
* description (given that at least version 242 of underlying libsystemd library is
* used; otherwise, names of parameters are ignored). This usually helps better describe
* the API to the introspector.
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags = {} ) = 0;
/*!
* @brief Registers read-only property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags = {} ) = 0;
/*!
* @brief Registers read/write property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] setCallback Callback that implements the body of the property setter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers read/write property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] setCallback Callback that implements the body of the property setter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags = {} ) = 0;
/*!
* @brief Sets flags for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @param[in] flags Flags to be set
*
* @throws sdbus::Error in case of failure
*/
* @brief Sets flags for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @param[in] flags Flags to be set
*
* @throws sdbus::Error in case of failure
*/
virtual void setInterfaceFlags(const std::string& interfaceName, Flags flags) = 0;
/*!
* @brief Finishes the registration and exports object API on D-Bus
*
* The method exports all up to now registered methods, signals and properties on D-Bus.
* Must be called only once, after all methods, signals and properties have been registered.
*
* @throws sdbus::Error in case of failure
*/
* @brief Finishes object API registration and publishes the object on the bus
*
* The method exports all up to now registered methods, signals and properties on D-Bus.
* Must be called after all methods, signals and properties have been registered.
*
* @throws sdbus::Error in case of failure
*/
virtual void finishRegistration() = 0;
/*!
* @brief Creates a signal message
*
* @param[in] interfaceName Name of an interface that the signal belongs under
* @param[in] signalName Name of the signal
* @return A signal message
*
* Serialize signal arguments into the returned message and emit the signal by passing
* the message with serialized arguments to the @c emitSignal function.
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
*
* @throws sdbus::Error in case of failure
*/
* @brief Unregisters object's API and removes object from the bus
*
* This method unregisters the object, its interfaces, methods, signals and properties
* from the bus. Unregistration is done automatically also in object's destructor. This
* method makes sense if, in the process of object removal, we need to make sure that
* callbacks are unregistered explicitly before the final destruction of the object instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
/*!
* @brief Creates a signal message
*
* @param[in] interfaceName Name of an interface that the signal belongs under
* @param[in] signalName Name of the signal
* @return A signal message
*
* Serialize signal arguments into the returned message and emit the signal by passing
* the message with serialized arguments to the @c emitSignal function.
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual Signal createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
/*!
* @brief Emits signal on D-Bus
*
* @param[in] message Signal message to be sent out
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
* @brief Emits signal for this object path
*
* @param[in] message Signal message to be sent out
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitSignal(const sdbus::Signal& message) = 0;
/*!
* @brief Provides D-Bus connection used by the object
*
* @return Reference to the D-Bus connection
*/
* @brief Emits PropertyChanged signal for specified properties under a given interface of this object path
*
* @param[in] interfaceName Name of an interface that properties belong to
* @param[in] propNames Names of properties that will be included in the PropertiesChanged signal
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) = 0;
/*!
* @brief Emits PropertyChanged signal for all properties on a given interface of this object path
*
* @param[in] interfaceName Name of an interface
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const std::string& interfaceName) = 0;
/*!
* @brief Emits InterfacesAdded signal on this object path
*
* This emits an InterfacesAdded signal on this object path, by iterating all registered
* interfaces on the path. All properties are queried and included in the signal.
* This call is equivalent to emitInterfacesAddedSignal() with an explicit list of
* registered interfaces. However, unlike emitInterfacesAddedSignal(interfaces), this
* call can figure out the list of supported interfaces itself. Furthermore, it properly
* adds the builtin org.freedesktop.DBus.* interfaces.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitInterfacesAddedSignal() = 0;
/*!
* @brief Emits InterfacesAdded signal on this object path
*
* This emits an InterfacesAdded signal on this object path with explicitly provided list
* of registered interfaces. As sdbus-c++ does currently not supported adding/removing
* interfaces of an existing object at run time (an object has a fixed set of interfaces
* registered by the time of invoking finishRegistration()), emitInterfacesAddedSignal(void)
* is probably what you are looking for.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) = 0;
/*!
* @brief Emits InterfacesRemoved signal on this object path
*
* This is like sd_bus_emit_object_added(), but emits an InterfacesRemoved signal on this
* object path. This only includes any registered interfaces but skips the properties.
* This function shall be called (just) before destroying the object.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitInterfacesRemovedSignal() = 0;
/*!
* @brief Emits InterfacesRemoved signal on this object path
*
* This emits an InterfacesRemoved signal on the given path with explicitly provided list
* of registered interfaces. As sdbus-c++ does currently not supported adding/removing
* interfaces of an existing object at run time (an object has a fixed set of interfaces
* registered by the time of invoking finishRegistration()), emitInterfacesRemovedSignal(void)
* is probably what you are looking for.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) = 0;
/*!
* @brief Adds an ObjectManager interface at the path of this D-Bus object
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* @throws sdbus::Error in case of failure
*/
virtual void addObjectManager() = 0;
/*!
* @brief Removes an ObjectManager interface from the path of this D-Bus object
*
* @throws sdbus::Error in case of failure
*/
virtual void removeObjectManager() = 0;
/*!
* @brief Tests whether ObjectManager interface is added at the path of this D-Bus object
* @return True if ObjectManager interface is there, false otherwise
*/
virtual bool hasObjectManager() const = 0;
/*!
* @brief Provides D-Bus connection used by the object
*
* @return Reference to the D-Bus connection
*/
virtual sdbus::IConnection& getConnection() const = 0;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] methodName Name of the method
* @return A helper object for convenient registration of the method
*
* This is a high-level, convenience way of registering D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the parameters and return type
* of the provided native method implementation callback.
*
* Example of use:
* @code
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] methodName Name of the method
* @return A helper object for convenient registration of the method
*
* This is a high-level, convenience way of registering D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the parameters and return type
* of the provided native method implementation callback.
*
* Example of use:
* @code
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
MethodRegistrator registerMethod(const std::string& methodName);
/*!
* @brief 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
*/
* @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 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
*/
* @brief Registers property that the object will provide on D-Bus
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient registration of the property
*
* This is a high-level, convenience way of registering D-Bus properties that abstracts
* from the D-Bus message concept. Property arguments are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native callbacks.
*
* Example of use:
* @code
* object_.registerProperty("state").onInterface("com.kistler.foo").withGetter([this](){ return this->state(); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
PropertyRegistrator registerProperty(const std::string& propertyName);
/*!
* @brief Sets flags (annotations) for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @return A helper object for convenient setting of Interface flags
*
* This is a high-level, convenience alternative to the other setInterfaceFlags overload.
*
* Example of use:
* @code
* object_.setInterfaceFlags("com.kistler.foo").markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @brief Sets flags (annotations) for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @return A helper object for convenient setting of Interface flags
*
* This is a high-level, convenience alternative to the other setInterfaceFlags overload.
*
* Example of use:
* @code
* object_.setInterfaceFlags("com.kistler.foo").markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
/*!
* @brief Emits signal on D-Bus
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient emission of signals
*
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
*
* Example of use:
* @code
* int arg1 = ...;
* double arg2 = ...;
* object_.emitSignal("fooSignal").onInterface("com.kistler.foo").withArguments(arg1, arg2);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
* @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);
virtual ~IObject() = 0;
};
// Out-of-line member definitions
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
{
return MethodRegistrator(*this, methodName);
@ -286,17 +446,17 @@ namespace sdbus {
inline SignalRegistrator IObject::registerSignal(const std::string& signalName)
{
return SignalRegistrator(*this, std::move(signalName));
return SignalRegistrator(*this, signalName);
}
inline PropertyRegistrator IObject::registerProperty(const std::string& propertyName)
{
return PropertyRegistrator(*this, std::move(propertyName));
return PropertyRegistrator(*this, propertyName);
}
inline InterfaceFlagsSetter IObject::setInterfaceFlags(const std::string& interfaceName)
{
return InterfaceFlagsSetter(*this, std::move(interfaceName));
return InterfaceFlagsSetter(*this, interfaceName);
}
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
@ -304,30 +464,28 @@ namespace sdbus {
return SignalEmitter(*this, signalName);
}
inline IObject::~IObject() {}
/*!
* @brief Creates instance representing a D-Bus object
*
* @param[in] connection D-Bus connection to be used by the object
* @param[in] objectPath Path of the D-Bus object
* @return Pointer to the object representation instance
*
* The provided connection will be used by the object to export methods,
* issue signals and provide properties.
*
* Creating a D-Bus object instance is (thread-)safe even upon the connection
* which is already running its processing loop.
*
* Code example:
* @code
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
* @endcode
*/
* @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 processing loop.
*
* Code example:
* @code
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
}
#include <sdbus-c++/ConvenienceClasses.inl>
#include <sdbus-c++/ConvenienceApiClasses.inl>
#endif /* SDBUS_CXX_IOBJECT_H_ */

View File

@ -1,350 +0,0 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file IObjectProxy.h
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_IOBJECTPROXY_H_
#define SDBUS_CXX_IOBJECTPROXY_H_
#include <sdbus-c++/ConvenienceClasses.h>
#include <string>
#include <memory>
#include <functional>
// Forward declarations
namespace sdbus {
class MethodCall;
class AsyncMethodCall;
class MethodReply;
class IConnection;
}
namespace sdbus {
/********************************************//**
* @class IObjectProxy
*
* An interface to D-Bus object proxy. Provides API for calling
* methods, getting/setting properties, and for registering to signals.
*
* All methods throw @c sdbus::Error in case of failure.
* In general, the class is thread-aware, but not thread-safe.
* However, the operation of creating and sending method calls
* (both synchronously and asynchronously) is 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
*
* Serialize method arguments into the returned message and invoke the method by passing
* the message with serialized arguments to the @c callMethod function.
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
/*!
* @brief Creates an asynchronous 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
*
* 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 callMethodAsync(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual AsyncMethodCall createAsyncMethodCall(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
* @return A method reply message
*
* Normally, the call is blocking, i.e. it waits for the remote method to finish with either
* a return value or an error.
*
* If the method call argument is set to not expect reply, the call will not wait for the remote
* method to finish, i.e. the call will be non-blocking, and the function will return an empty,
* invalid MethodReply object (representing void).
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual MethodReply callMethod(const MethodCall& message) = 0;
/*!
* @brief Calls method on the proxied D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
*
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the connection
* event loop processing thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback) = 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 Calls method on the proxied D-Bus object asynchronously
*
* @param[in] methodName Name of the method
* @return A helper object for convenient asynchronous invocation of the method
*
* This is a high-level, convenience way of calling D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments
* and return values.
*
* Example of use:
* @code
* int a = ..., b = ...;
* object_.callMethodAsync("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result)
* {
* std::cout << "Got result of multiplying " << a << " and " << b << ": " << result << std::endl;
* });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
AsyncMethodInvoker callMethodAsync(const std::string& methodName);
/*!
* @brief Registers signal handler for a given signal of the proxied D-Bus object
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient registration of the signal handler
*
* This is a high-level, convenience way of registering to D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the parameters
* of the provided native signal callback.
*
* Example of use:
* @code
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
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 AsyncMethodInvoker IObjectProxy::callMethodAsync(const std::string& methodName)
{
return AsyncMethodInvoker(*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. The caller still
* remains the owner of the connection (the proxy just keeps a reference to it), and
* should make sure that a processing loop is running on that connection, so the proxy
* may receive incoming signals and asynchronous method replies.
*
* 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. The Object proxy becomes
* an exclusive owner of this connection, and will automatically start a procesing loop
* upon that connection in a separate internal thread. Handlers for incoming signals and
* asynchronous method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::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
*
* No D-Bus connection is provided here, so the object proxy will create and manage
* his own connection, and will automatically start a procesing loop upon that connection
* in a separate internal thread. Handlers for incoming signals and asynchronous
* method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::createObjectProxy("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_ */

377
include/sdbus-c++/IProxy.h Normal file
View File

@ -0,0 +1,377 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IProxy.h
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_IPROXY_H_
#define SDBUS_CXX_IPROXY_H_
#include <sdbus-c++/ConvenienceApiClasses.h>
#include <string>
#include <memory>
#include <functional>
#include <chrono>
// Forward declarations
namespace sdbus {
class MethodCall;
class MethodReply;
class IConnection;
}
namespace sdbus {
/********************************************//**
* @class IProxy
*
* IProxy class represents a proxy object, which is a convenient local object created
* to represent a remote D-Bus object in another process.
* The proxy enables calling methods on remote objects, receiving signals from remote
* objects, and getting/setting properties of remote objects.
*
* All IProxy member methods throw @c sdbus::Error in case of D-Bus or sdbus-c++ error.
* The IProxy class has been designed as thread-aware. However, the operation of
* creating and sending method calls (both synchronously and asynchronously) is
* thread-safe by design.
*
***********************************************/
class IProxy
{
public:
virtual ~IProxy() = default;
/*!
* @brief Creates a method call message
*
* @param[in] interfaceName Name of an interface that provides a given method
* @param[in] methodName Name of the method
* @return A method call message
*
* Serialize method arguments into the returned message and invoke the method by passing
* the message with serialized arguments to the @c callMethod function.
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
/*!
* @brief Calls method on the proxied D-Bus object
*
* @param[in] message Message representing a method call
* @param[in] timeout Timeout for dbus call in microseconds
* @return A method reply message
*
* Normally, the call is blocking, i.e. it waits for the remote method to finish with either
* a return value or an error.
*
* If the method call argument is set to not expect reply, the call will not wait for the remote
* method to finish, i.e. the call will be non-blocking, and the function will return an empty,
* invalid MethodReply object (representing void).
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout = 0) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,uint64_t)
*/
template <typename _Rep, typename _Period>
MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout);
/*!
* @brief Calls method on the proxied D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @param[in] timeout Timeout for dbus call in microseconds
*
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the connection
* event loop processing thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout = 0) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t)
*/
template <typename _Rep, typename _Period>
void callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
/*!
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
* @param[in] signalHandler Callback that implements the body of the signal handler
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler ) = 0;
/*!
* @brief Finishes the registration of signal handlers
*
* The method physically subscribes to the desired signals.
* Must be called only once, after all signals have been registered already.
*
* @throws sdbus::Error in case of failure
*/
virtual void finishRegistration() = 0;
/*!
* @brief Unregisters proxy's signal handlers and stops receving replies to pending async calls
*
* Unregistration is done automatically also in proxy's destructor. This method makes
* sense if, in the process of proxy removal, we need to make sure that callbacks
* are unregistered explicitly before the final destruction of the proxy instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
/*!
* @brief Calls method on the proxied D-Bus object
*
* @param[in] methodName Name of the method
* @return A helper object for convenient invocation of the method
*
* This is a high-level, convenience way of calling D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments
* and return values.
*
* Example of use:
* @code
* int result, a = ..., b = ...;
* object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
MethodInvoker callMethod(const std::string& methodName);
/*!
* @brief Calls method on the proxied D-Bus object asynchronously
*
* @param[in] methodName Name of the method
* @return A helper object for convenient asynchronous invocation of the method
*
* This is a high-level, convenience way of calling D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments
* and return values.
*
* Example of use:
* @code
* int a = ..., b = ...;
* object_.callMethodAsync("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result)
* {
* std::cout << "Got result of multiplying " << a << " and " << b << ": " << result << std::endl;
* });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
AsyncMethodInvoker callMethodAsync(const std::string& methodName);
/*!
* @brief Registers signal handler for a given signal of the proxied D-Bus object
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient registration of the signal handler
*
* This is a high-level, convenience way of registering to D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the parameters
* of the provided native signal callback.
*
* Example of use:
* @code
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
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);
};
// 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 void IProxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
callMethod(message, std::move(asyncReplyCallback), microsecs.count());
}
inline MethodInvoker IProxy::callMethod(const std::string& methodName)
{
return MethodInvoker(*this, methodName);
}
inline AsyncMethodInvoker IProxy::callMethodAsync(const std::string& methodName)
{
return AsyncMethodInvoker(*this, methodName);
}
inline SignalSubscriber IProxy::uponSignal(const std::string& signalName)
{
return SignalSubscriber(*this, signalName);
}
inline PropertyGetter IProxy::getProperty(const std::string& propertyName)
{
return PropertyGetter(*this, propertyName);
}
inline PropertySetter IProxy::setProperty(const std::string& propertyName)
{
return PropertySetter(*this, propertyName);
}
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the proxy object instance
*
* The provided connection will be used by the proxy to issue calls against the object,
* and signals, if any, will be subscribed to on this connection. The caller still
* remains the owner of the connection (the proxy just keeps a reference to it), and
* should make sure that a processing loop is running on that connection, so the proxy
* may receive incoming signals and asynchronous method replies.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy(connection, "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* The provided connection will be used by the proxy to issue calls against the object,
* and signals, if any, will be subscribed to on this connection. The Object proxy becomes
* an exclusive owner of this connection, and will automatically start a procesing loop
* upon that connection in a separate internal thread. Handlers for incoming signals and
* asynchronous method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
*
* @param[in] destination Bus name that provides the remote D-Bus object
* @param[in] objectPath Path of the remote D-Bus object
* @return Pointer to the object proxy instance
*
* No D-Bus connection is provided here, so the object proxy will create and manage
* his own connection, and will automatically start a procesing loop upon that connection
* in a separate internal thread. Handlers for incoming signals and asynchronous
* method replies will be executed in the context of that thread.
*
* Code example:
* @code
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath );
}
#include <sdbus-c++/ConvenienceApiClasses.inl>
#endif /* SDBUS_CXX_IPROXY_H_ */

View File

@ -1,156 +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
: protected 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
: protected ObjectHolder<IObjectProxy>
, public _Interfaces...
{
public:
/*!
* @brief Creates fully working object proxy 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 createObjectProxy(std::string, std::string)
*/
ProxyInterfaces(std::string destination, std::string objectPath)
: ObjectHolder<IObjectProxy>(createObjectProxy(std::move(destination), std::move(objectPath)))
, _Interfaces(getObject())...
{
getObject().finishRegistration();
}
/*!
* @brief Creates fully working 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
*
* 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 createObjectProxy(IConnection&,std::string, std::string)
*/
ProxyInterfaces(IConnection& connection, std::string destination, std::string objectPath)
: ObjectHolder<IObjectProxy>(createObjectProxy(connection, std::move(destination), std::move(objectPath)))
, _Interfaces(getObject())...
{
getObject().finishRegistration();
}
/*!
* @brief Creates fully working 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
*
* The proxy created this way becomes an owner of the connection.
* For more information on its behavior, consult @ref createObjectProxy(std::unique_ptr<sdbus::IConnection>&&,std::string, std::string)
*/
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath)
: ObjectHolder<IObjectProxy>(createObjectProxy(std::move(connection), std::move(destination), std::move(objectPath)))
, _Interfaces(getObject())...
{
getObject().finishRegistration();
}
};
}
#endif /* SDBUS_CXX_INTERFACES_H_ */

View File

@ -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_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Message.h
*
@ -35,8 +36,7 @@
#include <utility>
#include <cstdint>
#include <cassert>
#include <iostream>
#include <functional>
// Forward declarations
namespace sdbus {
@ -44,6 +44,7 @@ namespace sdbus {
class ObjectPath;
class Signature;
template <typename... _ValueTypes> class Struct;
struct UnixFd;
class MethodReply;
namespace internal {
class ISdBus;
@ -71,22 +72,12 @@ namespace sdbus {
* by D-Bus.
*
* You don't need to work with this class directly if you use high-level APIs
* of @c IObject and @c IObjectProxy.
* of @c IObject and @c IProxy.
*
***********************************************/
class Message
{
public:
Message() = default;
Message(internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
Message(const Message&) noexcept;
Message& operator=(const Message&) noexcept;
Message(Message&& other) noexcept;
Message& operator=(Message&& other) noexcept;
~Message();
Message& operator<<(bool item);
Message& operator<<(int16_t item);
Message& operator<<(int32_t item);
@ -101,6 +92,7 @@ namespace sdbus {
Message& operator<<(const Variant &item);
Message& operator<<(const ObjectPath &item);
Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item);
Message& operator>>(bool& item);
Message& operator>>(int16_t& item);
@ -116,6 +108,7 @@ namespace sdbus {
Message& operator>>(Variant &item);
Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item);
Message& openContainer(const std::string& signature);
Message& closeContainer();
@ -135,11 +128,12 @@ namespace sdbus {
Message& enterStruct(const std::string& signature);
Message& exitStruct();
operator bool() const;
explicit operator bool() const;
void clearFlags();
std::string getInterfaceName() const;
std::string getMemberName() const;
std::string getSender() const;
void peekType(std::string& type, std::string& contents) const;
bool isValid() const;
bool isEmpty() const;
@ -148,50 +142,108 @@ namespace sdbus {
void seal();
void rewind(bool complete);
class Factory;
protected:
Message() = default;
explicit Message(internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
Message(const Message&) noexcept;
Message& operator=(const Message&) noexcept;
Message(Message&& other) noexcept;
Message& operator=(Message&& other) noexcept;
~Message();
friend Factory;
protected:
void* msg_{};
internal::ISdBus* sdbus_{};
mutable bool ok_{true};
};
struct dont_request_slot_t { explicit dont_request_slot_t() = default; };
#ifdef __cpp_inline_variables
inline constexpr dont_request_slot_t dont_request_slot{};
#else
constexpr dont_request_slot_t dont_request_slot{};
#endif
class MethodCall : public Message
{
public:
using Message::Message;
MethodReply send() const;
friend Factory;
public:
using Slot = std::unique_ptr<void, std::function<void(void*)>>;
MethodCall() = default;
MethodReply send(uint64_t timeout) const;
void send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const;
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout) const;
MethodReply createReply() const;
MethodReply createErrorReply(const sdbus::Error& error) const;
void dontExpectReply();
bool doesntExpectReply() const;
private:
MethodReply sendWithReply() const;
MethodReply sendWithReply(uint64_t timeout = 0) const;
MethodReply sendWithNoReply() const;
};
class AsyncMethodCall : public Message
{
public:
using Message::Message;
AsyncMethodCall() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
AsyncMethodCall(MethodCall&& call) noexcept;
void send(void* callback, void* userData) const;
};
class MethodReply : public Message
{
public:
using Message::Message;
friend Factory;
public:
MethodReply() = default;
void send() const;
};
class Signal : public Message
{
public:
using Message::Message;
friend Factory;
public:
Signal() = default;
void send() const;
};
class PropertySetCall : public Message
{
using Message::Message;
friend Factory;
public:
PropertySetCall() = default;
};
class PropertyGetReply : public Message
{
using Message::Message;
friend Factory;
public:
PropertyGetReply() = default;
};
class PlainMessage : public Message
{
using Message::Message;
friend Factory;
public:
PlainMessage() = default;
};
template <typename _Element>
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file MethodResult.h
*

View File

@ -0,0 +1,174 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ProxyInterfaces.h
*
* Created on: Apr 8, 2019
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_PROXYINTERFACES_H_
#define SDBUS_CXX_PROXYINTERFACES_H_
#include <sdbus-c++/IProxy.h>
#include <cassert>
#include <string>
#include <memory>
// Forward declarations
namespace sdbus {
class IConnection;
}
namespace sdbus {
/********************************************//**
* @class ProxyObjectHolder
*
* ProxyObjectHolder is a helper that simply owns and provides
* access to a proxy object to other classes in the inheritance
* hierarchy of a native-like proxy object based on generated
* interface classes.
*
***********************************************/
class ProxyObjectHolder
{
protected:
ProxyObjectHolder(std::unique_ptr<IProxy>&& proxy)
: proxy_(std::move(proxy))
{
assert(proxy_ != nullptr);
}
const IProxy& getProxy() const
{
assert(proxy_ != nullptr);
return *proxy_;
}
IProxy& getProxy()
{
assert(proxy_ != nullptr);
return *proxy_;
}
private:
std::unique_ptr<IProxy> proxy_;
};
/********************************************//**
* @class ProxyInterfaces
*
* ProxyInterfaces is a helper template class that joins all interface classes of a remote
* D-Bus object generated by sdbus-c++-xml2cpp to be used on the client (the proxy) side,
* including some auxiliary classes. ProxyInterfaces is the class that native-like proxy
* implementation classes written by users should inherit from and implement all pure virtual
* methods. So the _Interfaces template parameter is a list of sdbus-c++-xml2cpp-generated
* proxy-side interface classes representing interfaces of the corresponding remote D-Bus object.
*
* In the final proxy class inherited from ProxyInterfaces, it is necessary to finish proxy
* registration in class constructor (`finishRegistration();`), and, conversely, unregister
* the proxy in class destructor (`unregister();`).
*
***********************************************/
template <typename... _Interfaces>
class ProxyInterfaces
: protected ProxyObjectHolder
, public _Interfaces...
{
public:
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
* For more information on its behavior, consult @ref createProxy(std::string,std::string)
*/
ProxyInterfaces(std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
{
}
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* The proxy created this way just references a D-Bus connection owned and managed by the user.
* For more information on its behavior, consult @ref createProxy(IConnection&,std::string,std::string)
*/
ProxyInterfaces(IConnection& connection, std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(connection, std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
{
}
/*!
* @brief Creates native-like proxy object instance
*
* @param[in] connection D-Bus connection to be used by the proxy object
* @param[in] destination Bus name that provides a D-Bus object
* @param[in] objectPath Path of the D-Bus object
*
* The proxy created this way becomes an owner of the connection.
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string)
*/
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
{
}
/*!
* @brief Finishes proxy registration and makes the proxy ready for use
*
* This function must be called in the constructor of the final proxy class that implements ProxyInterfaces.
*
* For more information, see underlying @ref IProxy::finishRegistration()
*/
void registerProxy()
{
getProxy().finishRegistration();
}
/*!
* @brief Unregisters the proxy so it no more receives signals and async call replies
*
* This function must be called in the destructor of the final proxy class that implements ProxyInterfaces.
*
* See underlying @ref IProxy::unregister()
*/
void unregisterProxy()
{
getProxy().unregister();
}
protected:
using base_type = ProxyInterfaces;
};
}
#endif /* SDBUS_CXX_INTERFACES_H_ */

View File

@ -0,0 +1,262 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file StandardInterfaces.h
*
* Created on: Dec 13, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_STANDARDINTERFACES_H_
#define SDBUS_CXX_STANDARDINTERFACES_H_
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#include <string>
#include <map>
#include <vector>
namespace sdbus {
// Proxy for peer
class Peer_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
protected:
Peer_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~Peer_proxy() = default;
public:
void Ping()
{
proxy_.callMethod("Ping").onInterface(INTERFACE_NAME);
}
std::string GetMachineId()
{
std::string machineUUID;
proxy_.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
return machineUUID;
}
private:
sdbus::IProxy& proxy_;
};
// Proxy for introspection
class Introspectable_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
protected:
Introspectable_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~Introspectable_proxy() = default;
public:
std::string Introspect()
{
std::string xml;
proxy_.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
return xml;
}
private:
sdbus::IProxy& proxy_;
};
// Proxy for properties
class Properties_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
protected:
Properties_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_
.uponSignal("PropertiesChanged")
.onInterface(INTERFACE_NAME)
.call([this]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
{
this->onPropertiesChanged(interfaceName, changedProperties, invalidatedProperties);
});
}
~Properties_proxy() = default;
virtual void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) = 0;
public:
sdbus::Variant Get(const std::string& interfaceName, const std::string& propertyName)
{
return proxy_.getProperty(propertyName).onInterface(interfaceName);
}
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
{
proxy_.setProperty(propertyName).onInterface(interfaceName).toValue(value);
}
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
{
std::map<std::string, sdbus::Variant> props;
proxy_.callMethod("GetAll").onInterface(INTERFACE_NAME).withArguments(interfaceName).storeResultsTo(props);
return props;
}
private:
sdbus::IProxy& proxy_;
};
// Proxy for object manager
class ObjectManager_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected:
ObjectManager_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_
.uponSignal("InterfacesAdded")
.onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
this->onInterfacesAdded(objectPath, interfacesAndProperties);
});
proxy_
.uponSignal("InterfacesRemoved")
.onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
this->onInterfacesRemoved(objectPath, interfaces);
});
}
~ObjectManager_proxy() = default;
virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) = 0;
virtual void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces) = 0;
public:
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects()
{
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
proxy_.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
return objectsInterfacesAndProperties;
}
private:
sdbus::IProxy& proxy_;
};
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
// is provided by underlying libsystemd implementation. The exception is Properties_adaptor and
// ObjectManager_adaptor, which provide convenience functionality to emit signals.
// Adaptor for properties
class Properties_adaptor
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
protected:
Properties_adaptor(sdbus::IObject& object)
: object_(object)
{
}
~Properties_adaptor() = default;
public:
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
{
object_.emitPropertiesChangedSignal(interfaceName, properties);
}
void emitPropertiesChangedSignal(const std::string& interfaceName)
{
object_.emitPropertiesChangedSignal(interfaceName);
}
private:
sdbus::IObject& object_;
};
// Adaptor for object manager
class ObjectManager_adaptor
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected:
ObjectManager_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.addObjectManager();
}
~ObjectManager_adaptor() = default;
public:
void emitInterfacesAddedSignal()
{
object_.emitInterfacesAddedSignal();
}
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
{
object_.emitInterfacesAddedSignal(interfaces);
}
void emitInterfacesRemovedSignal()
{
object_.emitInterfacesRemovedSignal();
}
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
{
object_.emitInterfacesRemovedSignal(interfaces);
}
private:
sdbus::IObject& object_;
};
}
#endif /* SDBUS_CXX_STANDARDINTERFACES_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TypeTraits.h
*
@ -40,10 +41,12 @@ namespace sdbus {
template <typename... _ValueTypes> class Struct;
class ObjectPath;
class Signature;
class Message;
struct UnixFd;
class MethodCall;
class MethodReply;
class Signal;
class PropertySetCall;
class PropertyGetReply;
template <typename... _Results> class Result;
class Error;
}
@ -53,8 +56,8 @@ namespace sdbus {
using method_callback = std::function<void(MethodCall msg)>;
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
using signal_handler = std::function<void(Signal& signal)>;
using property_set_callback = std::function<void(Message& msg)>;
using property_get_callback = std::function<void(Message& reply)>;
using property_set_callback = std::function<void(PropertySetCall& msg)>;
using property_get_callback = std::function<void(PropertyGetReply& reply)>;
template <typename _T>
struct signature_of
@ -285,6 +288,17 @@ namespace sdbus {
}
};
template <>
struct signature_of<UnixFd>
{
static constexpr bool is_valid = true;
static const std::string str()
{
return "h";
}
};
template <typename _Element>
struct signature_of<std::vector<_Element>>
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Types.h
*
@ -33,6 +34,7 @@
#include <typeinfo>
#include <memory>
#include <tuple>
#include <unistd.h>
namespace sdbus {
@ -94,9 +96,15 @@ namespace sdbus {
std::string peekValueType() const;
private:
mutable Message msg_{};
mutable PlainMessage msg_{};
};
/********************************************//**
* @class Struct
*
* Representation of struct D-Bus type
*
***********************************************/
template <typename... _ValueTypes>
class Struct
: public std::tuple<_ValueTypes...>
@ -135,6 +143,12 @@ namespace sdbus {
return result_type(std::forward<_Elements>(args)...);
}
/********************************************//**
* @class ObjectPath
*
* Representation of object path D-Bus type
*
***********************************************/
class ObjectPath : public std::string
{
public:
@ -146,6 +160,12 @@ namespace sdbus {
using std::string::operator=;
};
/********************************************//**
* @class Signature
*
* Representation of Signature D-Bus type
*
***********************************************/
class Signature : public std::string
{
public:
@ -157,6 +177,106 @@ namespace sdbus {
using std::string::operator=;
};
struct adopt_fd_t { explicit adopt_fd_t() = default; };
#ifdef __cpp_inline_variables
inline constexpr adopt_fd_t adopt_fd{};
#else
constexpr adopt_fd_t adopt_fd{};
#endif
/********************************************//**
* @struct UnixFd
*
* UnixFd is a representation of file descriptor D-Bus type that owns
* the underlying fd, provides access to it, and closes the fd when
* the UnixFd goes out of scope.
*
* UnixFd can be default constructed (owning invalid fd), or constructed from
* an explicitly provided fd by either duplicating or adopting that fd as-is.
*
***********************************************/
class UnixFd
{
public:
UnixFd() = default;
explicit UnixFd(int fd)
: fd_(::dup(fd))
{
}
UnixFd(int fd, adopt_fd_t)
: fd_(fd)
{
}
UnixFd(const UnixFd& other)
{
*this = other;
}
UnixFd& operator=(const UnixFd& other)
{
close();
fd_ = ::dup(other.fd_);
return *this;
}
UnixFd(UnixFd&& other)
{
*this = std::move(other);
}
UnixFd& operator=(UnixFd&& other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
~UnixFd()
{
close();
}
int get() const
{
return fd_;
}
void reset(int fd = -1)
{
*this = UnixFd{fd};
}
void reset(int fd, adopt_fd_t)
{
*this = UnixFd{fd, adopt_fd};
}
int release()
{
auto fd = fd_;
fd_ = -1;
return fd;
}
bool isValid() const
{
return fd_ >= 0;
}
private:
void close()
{
if (fd_ >= 0)
::close(fd_);
}
int fd_ = -1;
};
}
#endif /* SDBUS_CXX_TYPES_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file sdbus-c++.h
*
@ -25,11 +26,13 @@
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/IObjectProxy.h>
#include <sdbus-c++/Interfaces.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/Types.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Introspection.h>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/Flags.h>

View File

@ -1,14 +0,0 @@
# Config file for the sdbus-c++ package.
#
# It defines the following variables:
# SDBUSCPP_VERSION - version of sdbus-c++
# SDBUSCPP_FOUND - set to true
# SDBUSCPP_INCLUDE_DIRS - include directories for sdbus-c++
# SDBUSCPP_LIBRARY_DIR - library directories for sdbus-c++
# SDBUSCPP_LIBRARIES - libraries to link against
set(SDBUSCPP_VERSION "@SDBUSCPP_VERSION@")
set(SDBUSCPP_FOUND "TRUE")
set(SDBUSCPP_INCLUDE_DIRS "@CMAKE_INSTALL_FULL_INCLUDEDIR@")
set(SDBUSCPP_LIBRARY_DIR "@CMAKE_INSTALL_FULL_LIBDIR@")
set(SDBUSCPP_LIBRARIES sdbus-c++)

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection.cpp
*
@ -25,6 +26,7 @@
#include "Connection.h"
#include "SdBus.h"
#include "MessageUtils.h"
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Error.h>
#include "ScopeGuard.h"
@ -35,24 +37,31 @@
namespace sdbus { namespace internal {
Connection::Connection(Connection::BusType type, std::unique_ptr<ISdBus>&& interface)
Connection::Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory)
: iface_(std::move(interface))
, busType_(type)
, bus_(openBus(busFactory))
{
assert(iface_ != nullptr);
}
auto bus = openBus(busType_);
bus_.reset(bus);
Connection::Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t)
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open_system(bus); })
{
}
finishHandshake(bus);
Connection::Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t)
: Connection(std::move(interface), [this](sd_bus** bus){ return iface_->sd_bus_open_user(bus); })
{
}
loopExitFd_ = createProcessingLoopExitDescriptor();
Connection::Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host)
: Connection(std::move(interface), [this, &host](sd_bus** bus){ return iface_->sd_bus_open_system_remote(bus, host.c_str()); })
{
}
Connection::~Connection()
{
leaveProcessingLoop();
closeProcessingLoopExitDescriptor(loopExitFd_);
}
void Connection::requestName(const std::string& name)
@ -67,8 +76,20 @@ void Connection::releaseName(const std::string& name)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to release bus name", -r);
}
std::string Connection::getUniqueName() const
{
const char* unique = nullptr;
auto r = iface_->sd_bus_get_unique_name(bus_.get(), &unique);
SDBUS_THROW_ERROR_IF(r < 0 || unique == nullptr, "Failed to get unique bus name", -r);
return unique;
}
void Connection::enterProcessingLoop()
{
loopThreadId_ = std::this_thread::get_id();
std::lock_guard guard(loopMutex_);
while (true)
{
auto processed = processPendingRequest();
@ -79,6 +100,8 @@ void Connection::enterProcessingLoop()
if (!success)
break; // Exit processing loop
}
loopThreadId_ = std::thread::id{};
}
void Connection::enterProcessingLoopAsync()
@ -93,6 +116,15 @@ void Connection::leaveProcessingLoop()
joinWithProcessingLoop();
}
sdbus::IConnection::PollData Connection::getProcessLoopPollData() const
{
ISdBus::PollData pollData;
auto r = iface_->sd_bus_get_poll_data(bus_.get(), &pollData);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r);
return {pollData.fd, pollData.events, pollData.timeout_usec};
}
const ISdBus& Connection::getSdBusInterface() const
{
return *iface_.get();
@ -103,10 +135,46 @@ ISdBus& Connection::getSdBusInterface()
return *iface_.get();
}
sd_bus_slot* Connection::addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData )
void Connection::addObjectManager(const std::string& objectPath)
{
Connection::addObjectManager(objectPath, nullptr);
}
SlotPtr Connection::addObjectManager(const std::string& objectPath, void* /*dummy*/)
{
sd_bus_slot *slot{};
auto r = iface_->sd_bus_add_object_manager( bus_.get()
, &slot
, objectPath.c_str() );
SDBUS_THROW_ERROR_IF(r < 0, "Failed to add object manager", -r);
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
void Connection::setMethodCallTimeout(uint64_t timeout)
{
auto r = iface_->sd_bus_set_method_call_timeout(bus_.get(), timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set method call timeout", -r);
}
uint64_t Connection::getMethodCallTimeout() const
{
uint64_t timeout;
auto r = iface_->sd_bus_get_method_call_timeout(bus_.get(), &timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get method call timeout", -r);
return timeout;
}
SlotPtr Connection::addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData )
{
sd_bus_slot *slot{};
@ -119,12 +187,18 @@ sd_bus_slot* Connection::addObjectVTable( const std::string& objectPath
SDBUS_THROW_ERROR_IF(r < 0, "Failed to register object vtable", -r);
return slot;
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
void Connection::removeObjectVTable(sd_bus_slot* vtableHandle)
PlainMessage Connection::createPlainMessage() const
{
iface_->sd_bus_slot_unref(vtableHandle);
sd_bus_message* sdbusMsg{};
auto r = iface_->sd_bus_message_new(bus_.get(), &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a plain message", -r);
return Message::Factory::create<PlainMessage>(sdbusMsg, iface_.get(), adopt_message);
}
MethodCall Connection::createMethodCall( const std::string& destination
@ -143,31 +217,83 @@ MethodCall Connection::createMethodCall( const std::string& destination
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
return MethodCall{sdbusMsg, iface_.get(), adopt_message};
return Message::Factory::create<MethodCall>(sdbusMsg, iface_.get(), adopt_message);
}
Signal Connection::createSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName ) const
{
sd_bus_message *sdbusSignal{};
sd_bus_message *sdbusMsg{};
auto r = iface_->sd_bus_message_new_signal( bus_.get()
, &sdbusSignal
, &sdbusMsg
, objectPath.c_str()
, interfaceName.c_str()
, signalName.c_str() );
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
return Signal{sdbusSignal, iface_.get(), adopt_message};
return Message::Factory::create<Signal>(sdbusMsg, iface_.get(), adopt_message);
}
sd_bus_slot* Connection::registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData )
void Connection::emitPropertiesChangedSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::vector<std::string>& propNames )
{
auto names = to_strv(propNames);
auto r = iface_->sd_bus_emit_properties_changed_strv( bus_.get()
, objectPath.c_str()
, interfaceName.c_str()
, propNames.empty() ? nullptr : &names[0] );
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit PropertiesChanged signal", -r);
}
void Connection::emitInterfacesAddedSignal(const std::string& objectPath)
{
auto r = iface_->sd_bus_emit_object_added(bus_.get(), objectPath.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesAdded signal for all registered interfaces", -r);
}
void Connection::emitInterfacesAddedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces )
{
auto names = to_strv(interfaces);
auto r = iface_->sd_bus_emit_interfaces_added_strv( bus_.get()
, objectPath.c_str()
, interfaces.empty() ? nullptr : &names[0] );
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesAdded signal", -r);
}
void Connection::emitInterfacesRemovedSignal(const std::string& objectPath)
{
auto r = iface_->sd_bus_emit_object_removed(bus_.get(), objectPath.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesRemoved signal for all registered interfaces", -r);
}
void Connection::emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces )
{
auto names = to_strv(interfaces);
auto r = iface_->sd_bus_emit_interfaces_removed_strv( bus_.get()
, objectPath.c_str()
, interfaces.empty() ? nullptr : &names[0] );
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit InterfacesRemoved signal", -r);
}
SlotPtr Connection::registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData )
{
sd_bus_slot *slot{};
@ -176,29 +302,47 @@ sd_bus_slot* Connection::registerSignalHandler( const std::string& objectPath
SDBUS_THROW_ERROR_IF(r < 0, "Failed to register signal handler", -r);
return slot;
return {slot, [this](void *slot){ iface_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
void Connection::unregisterSignalHandler(sd_bus_slot* handlerCookie)
MethodReply Connection::tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout)
{
iface_->sd_bus_slot_unref(handlerCookie);
auto loopThreadId = loopThreadId_.load(std::memory_order_relaxed);
// Is the loop not yet on? => Go make synchronous call
while (loopThreadId == std::thread::id{})
{
// Did the loop begin in the meantime? Or try_lock() failed spuriously?
if (!loopMutex_.try_lock())
{
loopThreadId = loopThreadId_.load(std::memory_order_relaxed);
continue;
}
// Synchronous D-Bus call
std::lock_guard guard(loopMutex_, std::adopt_lock);
return message.send(timeout);
}
// Is the loop on and we are in the same thread? => Go for synchronous call
if (loopThreadId == std::this_thread::get_id())
{
assert(!loopMutex_.try_lock());
return message.send(timeout);
}
return {};
}
sd_bus* Connection::openBus(Connection::BusType type)
Connection::BusPtr Connection::openBus(const BusFactory& busFactory)
{
sd_bus* bus{};
int r = 0;
if (type == BusType::eSystem)
r = iface_->sd_bus_open_system(&bus);
else if (type == BusType::eSession)
r = iface_->sd_bus_open_user(&bus);
else
assert(false);
int r = busFactory(&bus);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open bus", -r);
assert(bus != nullptr);
return bus;
BusPtr busPtr{bus, [this](sd_bus* bus){ return iface_->sd_bus_flush_close_unref(bus); }};
finishHandshake(busPtr.get());
return busPtr;
}
void Connection::finishHandshake(sd_bus* bus)
@ -214,33 +358,19 @@ void Connection::finishHandshake(sd_bus* bus)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
}
int Connection::createProcessingLoopExitDescriptor()
{
auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
return r;
}
void Connection::closeProcessingLoopExitDescriptor(int fd)
{
close(fd);
}
void Connection::notifyProcessingLoopToExit()
{
assert(loopExitFd_ >= 0);
assert(loopExitFd_.fd >= 0);
uint64_t value = 1;
auto r = write(loopExitFd_, &value, sizeof(value));
auto r = write(loopExitFd_.fd, &value, sizeof(value));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno);
}
void Connection::clearExitNotification()
{
uint64_t value{};
auto r = read(loopExitFd_, &value, sizeof(value));
auto r = read(loopExitFd_.fd, &value, sizeof(value));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
}
@ -253,11 +383,9 @@ void Connection::joinWithProcessingLoop()
bool Connection::processPendingRequest()
{
auto bus = bus_.get();
assert(bus != nullptr);
int r = iface_->sd_bus_process(bus, nullptr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to process bus requests", -r);
return r > 0;
@ -268,17 +396,14 @@ bool Connection::waitForNextRequest()
auto bus = bus_.get();
assert(bus != nullptr);
assert(loopExitFd_ != 0);
assert(loopExitFd_.fd != 0);
ISdBus::PollData sdbusPollData;
auto r = iface_->sd_bus_get_poll_data(bus, &sdbusPollData);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus poll data", -r);
struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_, POLLIN, 0}};
auto sdbusPollData = getProcessLoopPollData();
struct pollfd fds[] = {{sdbusPollData.fd, sdbusPollData.events, 0}, {loopExitFd_.fd, POLLIN, 0}};
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
auto timeout = sdbusPollData.timeout_usec == (uint64_t) -1 ? (uint64_t)-1 : (sdbusPollData.timeout_usec+999)/1000;
r = poll(fds, fdsCount, timeout);
auto r = poll(fds, fdsCount, timeout);
if (r < 0 && errno == EINTR)
return true; // Try again
@ -308,10 +433,41 @@ std::string Connection::composeSignalMatchFilter( const std::string& objectPath
return filter;
}
std::vector</*const */char*> Connection::to_strv(const std::vector<std::string>& strings)
{
std::vector</*const */char*> strv;
for (auto& str : strings)
strv.push_back(const_cast<char*>(str.c_str()));
strv.push_back(nullptr);
return strv;
}
Connection::LoopExitEventFd::LoopExitEventFd()
{
fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK);
SDBUS_THROW_ERROR_IF(fd < 0, "Failed to create event object", -errno);
}
Connection::LoopExitEventFd::~LoopExitEventFd()
{
assert(fd >= 0);
close(fd);
}
}}
namespace sdbus {
namespace internal {
std::unique_ptr<sdbus::internal::IConnection> createConnection()
{
auto connection = sdbus::createConnection();
SCOPE_EXIT{ connection.release(); };
auto connectionInternal = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
return std::unique_ptr<sdbus::internal::IConnection>(connectionInternal);
}
}
std::unique_ptr<sdbus::IConnection> createConnection()
{
return createSystemBusConnection();
@ -326,8 +482,8 @@ std::unique_ptr<sdbus::IConnection> createSystemBusConnection()
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSystem
, std::move(interface));
constexpr sdbus::internal::Connection::system_bus_t system_bus;
return std::make_unique<sdbus::internal::Connection>(std::move(interface), system_bus);
}
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name)
@ -341,8 +497,8 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection()
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr);
return std::make_unique<sdbus::internal::Connection>( sdbus::internal::Connection::BusType::eSession
, std::move(interface));
constexpr sdbus::internal::Connection::session_bus_t session_bus;
return std::make_unique<sdbus::internal::Connection>(std::move(interface), session_bus);
}
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name)
@ -352,4 +508,12 @@ std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string
return conn;
}
std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host)
{
auto interface = std::make_unique<sdbus::internal::SdBus>();
assert(interface != nullptr);
constexpr sdbus::internal::Connection::remote_system_bus_t remote_system_bus;
return std::make_unique<sdbus::internal::Connection>(std::move(interface), remote_system_bus, host);
}
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection.h
*
@ -34,6 +35,10 @@
#include <systemd/sd-bus.h>
#include <memory>
#include <thread>
#include <string>
#include <vector>
#include <atomic>
#include <mutex>
namespace sdbus { namespace internal {
@ -42,30 +47,40 @@ namespace sdbus { namespace internal {
, public sdbus::internal::IConnection // Internal, private interface
{
public:
enum class BusType
{
eSystem,
eSession
};
// Bus type tags
struct system_bus_t{};
struct session_bus_t{};
struct remote_system_bus_t{};
Connection(BusType type, std::unique_ptr<ISdBus>&& interface);
Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, session_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, remote_system_bus_t, const std::string& host);
~Connection() override;
void requestName(const std::string& name) override;
void releaseName(const std::string& name) override;
std::string getUniqueName() const override;
void enterProcessingLoop() override;
void enterProcessingLoopAsync() override;
void leaveProcessingLoop() override;
sdbus::IConnection::PollData getProcessLoopPollData() const override;
bool processPendingRequest() override;
void addObjectManager(const std::string& objectPath) override;
SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/) override;
void setMethodCallTimeout(uint64_t timeout) override;
uint64_t getMethodCallTimeout() const override;
const ISdBus& getSdBusInterface() const override;
ISdBus& getSdBusInterface() override;
sd_bus_slot* addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) override;
void removeObjectVTable(sd_bus_slot* vtableHandle) override;
SlotPtr addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) override;
PlainMessage createPlainMessage() const override;
MethodCall createMethodCall( const std::string& destination
, const std::string& objectPath
, const std::string& interfaceName
@ -74,19 +89,31 @@ namespace sdbus { namespace internal {
, const std::string& interfaceName
, const std::string& signalName ) const override;
sd_bus_slot* registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData ) override;
void unregisterSignalHandler(sd_bus_slot* handlerCookie) override;
void emitPropertiesChangedSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::vector<std::string>& propNames ) override;
void emitInterfacesAddedSignal(const std::string& objectPath) override;
void emitInterfacesAddedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) override;
void emitInterfacesRemovedSignal(const std::string& objectPath) override;
void emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) override;
SlotPtr registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData ) override;
MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) override;
private:
sd_bus* openBus(Connection::BusType type);
using BusFactory = std::function<int(sd_bus**)>;
using BusPtr = std::unique_ptr<sd_bus, std::function<sd_bus*(sd_bus*)>>;
Connection(std::unique_ptr<ISdBus>&& interface, const BusFactory& busFactory);
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
void finishHandshake(sd_bus* bus);
static int createProcessingLoopExitDescriptor();
static void closeProcessingLoopExitDescriptor(int fd);
bool processPendingRequest();
bool waitForNextRequest();
static std::string composeSignalMatchFilter( const std::string& objectPath
, const std::string& interfaceName
@ -94,17 +121,22 @@ namespace sdbus { namespace internal {
void notifyProcessingLoopToExit();
void clearExitNotification();
void joinWithProcessingLoop();
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
struct LoopExitEventFd
{
LoopExitEventFd();
~LoopExitEventFd();
int fd;
};
private:
std::unique_ptr<ISdBus> iface_;
std::unique_ptr<sd_bus, std::function<sd_bus*(sd_bus*)>> bus_ {nullptr, [this](sd_bus* bus)
{
return iface_->sd_bus_flush_close_unref(bus);
}};
BusType busType_;
BusPtr bus_;
std::thread asyncLoopThread_;
int loopExitFd_{-1};
std::atomic<std::thread::id> loopThreadId_;
std::mutex loopMutex_;
LoopExitEventFd loopExitFd_;
};
}}

View File

@ -1,208 +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 {
MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
: object_(object)
, methodName_(methodName)
, exceptions_(std::uncaught_exceptions()) // Needs C++17
{
}
MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't register the method if MethodRegistrator threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
}
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_, flags_);
}
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_)
, flags_ );
}
InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
: object_(object)
, interfaceName_(interfaceName)
, exceptions_(std::uncaught_exceptions())
{
}
InterfaceFlagsSetter::~InterfaceFlagsSetter() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't set any flags if InterfaceFlagsSetter threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when setting its flags", EINVAL);
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow setInterfaceFlags() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.setInterfaceFlags( std::move(interfaceName_)
, std::move(flags_) );
}
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_);
}
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Error.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Flags.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file IConnection.h
*
@ -29,13 +30,15 @@
#include <systemd/sd-bus.h>
#include <string>
#include <memory>
#include <functional>
#include <vector>
// Forward declaration
namespace sdbus {
class MethodCall;
class AsyncMethodCall;
class MethodReply;
class Signal;
class PlainMessage;
namespace internal {
class ISdBus;
}
@ -44,40 +47,56 @@ namespace sdbus {
namespace sdbus {
namespace internal {
using SlotPtr = std::unique_ptr<void, std::function<void(void*)>>;
class IConnection
{
public:
virtual ~IConnection() = default;
virtual const ISdBus& getSdBusInterface() const = 0;
virtual ISdBus& getSdBusInterface() = 0;
virtual sd_bus_slot* addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) = 0;
virtual void removeObjectVTable(sd_bus_slot* vtableHandle) = 0;
virtual SlotPtr addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) = 0;
virtual PlainMessage createPlainMessage() const = 0;
virtual MethodCall createMethodCall( const std::string& destination
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& methodName ) const = 0;
virtual Signal createSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName ) const = 0;
virtual sd_bus_slot* 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(sd_bus_slot* handlerCookie) = 0;
virtual void emitPropertiesChangedSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::vector<std::string>& propNames ) = 0;
virtual void emitInterfacesAddedSignal(const std::string& objectPath) = 0;
virtual void emitInterfacesAddedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) = 0;
virtual void emitInterfacesRemovedSignal(const std::string& objectPath) = 0;
virtual void emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) = 0;
virtual SlotPtr addObjectManager(const std::string& objectPath, void* /*dummy*/ = nullptr) = 0;
virtual SlotPtr registerSignalHandler( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData ) = 0;
virtual void enterProcessingLoopAsync() = 0;
virtual void leaveProcessingLoop() = 0;
virtual ~IConnection() = default;
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0;
};
std::unique_ptr<sdbus::internal::IConnection> createConnection();
}
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ISdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -41,6 +42,8 @@ namespace sdbus { namespace internal {
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;
@ -48,16 +51,29 @@ namespace sdbus { namespace internal {
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_user(sd_bus **ret) = 0;
virtual int sd_bus_open_system(sd_bus **ret) = 0;
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* host) = 0;
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) = 0;
virtual int sd_bus_release_name(sd_bus *bus, const char *name) = 0;
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) = 0;
virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) = 0;
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) = 0;
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0;
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
@ -66,8 +82,6 @@ namespace sdbus { namespace internal {
virtual int sd_bus_flush(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
virtual ~ISdBus() = default;
};
}}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Message.cpp
*
@ -27,7 +28,8 @@
#include <sdbus-c++/Types.h>
#include <sdbus-c++/Error.h>
#include "MessageUtils.h"
#include "SdBus.h"
#include "ISdBus.h"
#include "IConnection.h"
#include "ScopeGuard.h"
#include <systemd/sd-bus.h>
#include <cassert>
@ -210,7 +212,16 @@ Message& Message::operator<<(const ObjectPath &item)
Message& Message::operator<<(const Signature &item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, item.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an Signature value", -r);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a Signature value", -r);
return *this;
}
Message& Message::operator<<(const UnixFd &item)
{
auto fd = item.get();
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a UnixFd value", -r);
return *this;
}
@ -383,6 +394,20 @@ Message& Message::operator>>(Signature &item)
return *this;
}
Message& Message::operator>>(UnixFd &item)
{
int fd = -1;
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
if (r == 0)
ok_ = false;
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a UnixFd value", -r);
item.reset(fd);
return *this;
}
Message& Message::openContainer(const std::string& signature)
{
@ -566,6 +591,11 @@ std::string Message::getMemberName() const
return sd_bus_message_get_member((sd_bus_message*)msg_);
}
std::string Message::getSender() const
{
return sd_bus_message_get_sender((sd_bus_message*)msg_);
}
void Message::peekType(std::string& type, std::string& contents) const
{
char typeSig;
@ -583,7 +613,7 @@ bool Message::isValid() const
bool Message::isEmpty() const
{
return sd_bus_message_is_empty((sd_bus_message*)msg_);
return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0;
}
void MethodCall::dontExpectReply()
@ -596,31 +626,31 @@ 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 ? false : true;
return r == 0;
}
MethodReply MethodCall::send() const
MethodReply MethodCall::send(uint64_t timeout) const
{
if (!doesntExpectReply())
return sendWithReply();
return sendWithReply(timeout);
else
return sendWithNoReply();
}
MethodReply MethodCall::sendWithReply() const
MethodReply MethodCall::sendWithReply(uint64_t timeout) const
{
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
sd_bus_message* sdbusReply{};
auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, 0, &sdbusError, &sdbusReply);
auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, timeout, &sdbusError, &sdbusReply);
if (sd_bus_error_is_set(&sdbusError))
throw sdbus::Error(sdbusError.name, sdbusError.message);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
return MethodReply{sdbusReply, sdbus_, adopt_message};
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
}
MethodReply MethodCall::sendWithNoReply() const
@ -628,7 +658,23 @@ MethodReply MethodCall::sendWithNoReply() const
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method with no reply", -r);
return MethodReply{}; // No reply
return Factory::create<MethodReply>(); // No reply
}
void MethodCall::send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const
{
auto r = sdbus_->sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
}
MethodCall::Slot MethodCall::send(void* callback, void* userData, uint64_t timeout) const
{
sd_bus_slot* slot;
auto r = sdbus_->sd_bus_call_async(nullptr, &slot, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r);
return Slot{slot, [sdbus_ = sdbus_](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
MethodReply MethodCall::createReply() const
@ -637,7 +683,7 @@ MethodReply MethodCall::createReply() const
auto r = sdbus_->sd_bus_message_new_method_return((sd_bus_message*)msg_, &sdbusReply);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
return MethodReply{sdbusReply, sdbus_, adopt_message};
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
}
MethodReply MethodCall::createErrorReply(const Error& error) const
@ -650,18 +696,7 @@ MethodReply MethodCall::createErrorReply(const Error& error) const
auto r = sdbus_->sd_bus_message_new_method_error((sd_bus_message*)msg_, &sdbusErrorReply, &sdbusError);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
return MethodReply{sdbusErrorReply, sdbus_, adopt_message};
}
AsyncMethodCall::AsyncMethodCall(MethodCall&& call) noexcept
: Message(std::move(call))
{
}
void AsyncMethodCall::send(void* callback, void* userData) const
{
auto r = sdbus_->sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, 0);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r);
return Factory::create<MethodReply>(sdbusErrorReply, sdbus_, adopt_message);
}
void MethodReply::send() const
@ -676,29 +711,10 @@ void Signal::send() const
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
}
Message createPlainMessage()
PlainMessage createPlainMessage()
{
int r;
sd_bus* bus{};
SCOPE_EXIT{ sd_bus_unref(bus); };
r = sd_bus_default_system(&bus);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get default system bus", -r);
thread_local struct BusReferenceKeeper
{
BusReferenceKeeper(sd_bus* bus) : bus_(bus) {}
~BusReferenceKeeper() { sd_bus_unref(bus_); }
sd_bus* bus_{};
} busReferenceKeeper{bus};
sd_bus_message* 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);
thread_local internal::SdBus sdbus;
return Message{sdbusMsg, &sdbus, adopt_message};
static auto connection = internal::createConnection();
return connection->createPlainMessage();
}
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file MessageUtils.h
*
@ -30,7 +31,35 @@
namespace sdbus
{
Message createPlainMessage();
class Message::Factory
{
public:
template<typename _Msg>
static _Msg create()
{
return _Msg{};
}
template<typename _Msg>
static _Msg create(void *msg)
{
return _Msg{msg};
}
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus)
{
return _Msg{msg, sdbus};
}
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus, adopt_message_t)
{
return _Msg{msg, sdbus, adopt_message};
}
};
PlainMessage createPlainMessage();
}
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Object.cpp
*
@ -24,6 +25,7 @@
*/
#include "Object.h"
#include "MessageUtils.h"
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Error.h>
@ -43,51 +45,83 @@ Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
}
void Object::registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags )
{
registerMethod( interfaceName
, std::move(methodName)
, std::move(inputSignature)
, {}
, std::move(outputSignature)
, {}
, std::move(methodCallback)
, std::move(flags) );
}
void Object::registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags )
{
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
auto& interface = interfaces_[interfaceName];
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback), flags};
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
InterfaceData::MethodData methodData{ std::move(inputSignature)
, std::move(outputSignature)
, paramNamesToString(inputNames) + paramNamesToString(outputNames)
, std::move(methodCallback)
, std::move(flags) };
auto inserted = interface.methods.emplace(std::move(methodName), std::move(methodData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
}
void Object::registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
, std::string signalName
, std::string signature
, Flags flags )
{
registerSignal(interfaceName, std::move(signalName), std::move(signature), {}, std::move(flags));
}
void Object::registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags )
{
auto& interface = interfaces_[interfaceName];
InterfaceData::SignalData signalData{signature, flags};
auto inserted = interface.signals_.emplace(signalName, std::move(signalData)).second;
InterfaceData::SignalData signalData{std::move(signature), paramNamesToString(paramNames), std::move(flags)};
auto inserted = interface.signals.emplace(std::move(signalName), std::move(signalData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
}
void Object::registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags )
{
registerProperty( interfaceName
, propertyName
, signature
, getCallback
, property_set_callback{}
, flags );
, std::move(propertyName)
, std::move(signature)
, std::move(getCallback)
, {}
, std::move(flags) );
}
void Object::registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags )
@ -96,8 +130,11 @@ void Object::registerProperty( const std::string& interfaceName
auto& interface = interfaces_[interfaceName];
InterfaceData::PropertyData propertyData{signature, std::move(getCallback), std::move(setCallback), flags};
auto inserted = interface.properties_.emplace(propertyName, std::move(propertyData)).second;
InterfaceData::PropertyData propertyData{ std::move(signature)
, std::move(getCallback)
, std::move(setCallback)
, std::move(flags)};
auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
}
@ -105,7 +142,7 @@ void Object::registerProperty( const std::string& interfaceName
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
{
auto& interface = interfaces_[interfaceName];
interface.flags_ = flags;
interface.flags = flags;
}
void Object::finishRegistration()
@ -120,6 +157,12 @@ void Object::finishRegistration()
}
}
void Object::unregister()
{
interfaces_.clear();
removeObjectManager();
}
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
{
return connection_.createSignal(objectPath_, interfaceName, signalName);
@ -127,9 +170,56 @@ sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::
void Object::emitSignal(const sdbus::Signal& message)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
message.send();
}
void Object::emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames)
{
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
}
void Object::emitPropertiesChangedSignal(const std::string& interfaceName)
{
Object::emitPropertiesChangedSignal(interfaceName, {});
}
void Object::emitInterfacesAddedSignal()
{
connection_.emitInterfacesAddedSignal(objectPath_);
}
void Object::emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
{
connection_.emitInterfacesAddedSignal(objectPath_, interfaces);
}
void Object::emitInterfacesRemovedSignal()
{
connection_.emitInterfacesRemovedSignal(objectPath_);
}
void Object::emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
{
connection_.emitInterfacesRemovedSignal(objectPath_, interfaces);
}
void Object::addObjectManager()
{
objectManagerSlot_ = connection_.addObjectManager(objectPath_);
}
void Object::removeObjectManager()
{
objectManagerSlot_.reset();
}
bool Object::hasObjectManager() const
{
return objectManagerSlot_ != nullptr;
}
sdbus::IConnection& Object::getConnection() const
{
return dynamic_cast<sdbus::IConnection&>(connection_);
@ -137,10 +227,10 @@ sdbus::IConnection& Object::getConnection() const
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
{
auto& vtable = interfaceData.vtable_;
auto& vtable = interfaceData.vtable;
assert(vtable.empty());
vtable.push_back(createVTableStartItem(interfaceData.flags_.toSdBusInterfaceFlags()));
vtable.push_back(createVTableStartItem(interfaceData.flags.toSdBusInterfaceFlags()));
registerMethodsToVTable(interfaceData, vtable);
registerSignalsToVTable(interfaceData, vtable);
registerPropertiesToVTable(interfaceData, vtable);
@ -151,14 +241,15 @@ const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& i
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.methods_)
for (const auto& item : interfaceData.methods)
{
const auto& methodName = item.first;
const auto& methodData = item.second;
vtable.push_back(createVTableMethodItem( methodName.c_str()
, methodData.inputArgs_.c_str()
, methodData.outputArgs_.c_str()
, methodData.inputArgs.c_str()
, methodData.outputArgs.c_str()
, methodData.paramNames.c_str()
, &Object::sdbus_method_callback
, methodData.flags_.toSdBusMethodFlags() ));
}
@ -166,35 +257,36 @@ void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::ve
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.signals_)
for (const auto& item : interfaceData.signals)
{
const auto& signalName = item.first;
const auto& signalData = item.second;
vtable.push_back(createVTableSignalItem( signalName.c_str()
, signalData.signature_.c_str()
, signalData.flags_.toSdBusSignalFlags() ));
, signalData.signature.c_str()
, signalData.paramNames.c_str()
, signalData.flags.toSdBusSignalFlags() ));
}
}
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.properties_)
for (const auto& item : interfaceData.properties)
{
const auto& propertyName = item.first;
const auto& propertyData = item.second;
if (!propertyData.setCallback_)
if (!propertyData.setCallback)
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
, propertyData.signature_.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, propertyData.flags_.toSdBusPropertyFlags() ));
, propertyData.flags.toSdBusPropertyFlags() ));
else
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
, propertyData.signature_.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, &Object::sdbus_property_set_callback
, propertyData.flags_.toSdBusWritablePropertyFlags() ));
, propertyData.flags.toSdBusWritablePropertyFlags() ));
}
}
@ -202,10 +294,15 @@ void Object::activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable )
{
// Tell, don't ask
auto slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
interfaceData.slot_.reset(slot);
interfaceData.slot_.get_deleter() = [this](sd_bus_slot *slot){ connection_.removeObjectVTable(slot); };
interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
}
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
{
std::string names;
for (const auto& name : paramNames)
names += name + '\0';
return names;
}
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
@ -213,10 +310,10 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
auto* object = static_cast<Object*>(userData);
assert(object != nullptr);
MethodCall message{sdbusMessage, &object->connection_.getSdBusInterface()};
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object->connection_.getSdBusInterface());
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
auto& callback = object->interfaces_[message.getInterfaceName()].methods[message.getMemberName()].callback;
assert(callback);
try
@ -243,7 +340,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
assert(object != nullptr);
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties_[property].getCallback_;
auto& callback = object->interfaces_[interface].properties[property].getCallback;
// Getter can be empty - the case of "write-only" property
if (!callback)
{
@ -251,7 +348,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
return 1;
}
Message reply{sdbusReply, &object->connection_.getSdBusInterface()};
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object->connection_.getSdBusInterface());
try
{
@ -277,10 +374,10 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
assert(object != nullptr);
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = object->interfaces_[interface].properties_[property].setCallback_;
auto& callback = object->interfaces_[interface].properties[property].setCallback;
assert(callback);
Message value{sdbusValue, &object->connection_.getSdBusInterface()};
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object->connection_.getSdBusInterface());
try
{

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Object.h
*
@ -46,26 +47,38 @@ namespace internal {
Object(sdbus::internal::IConnection& connection, std::string objectPath);
void registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags ) override;
void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags ) override;
void registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
, std::string signalName
, std::string signature
, Flags flags ) override;
void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags ) override;
void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags ) override;
void registerProperty( const std::string& interfaceName
, const std::string& propertyName
, const std::string& signature
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags ) override;
@ -73,9 +86,20 @@ namespace internal {
void setInterfaceFlags(const std::string& interfaceName, Flags flags) override;
void finishRegistration() override;
void unregister() override;
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
void emitSignal(const sdbus::Signal& message) override;
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) override;
void emitPropertiesChangedSignal(const std::string& interfaceName) override;
void emitInterfacesAddedSignal() override;
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) override;
void emitInterfacesRemovedSignal() override;
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) override;
void addObjectManager() override;
void removeObjectManager() override;
bool hasObjectManager() const override;
sdbus::IConnection& getConnection() const override;
@ -86,32 +110,34 @@ namespace internal {
using MethodName = std::string;
struct MethodData
{
std::string inputArgs_;
std::string outputArgs_;
method_callback callback_;
const std::string inputArgs;
const std::string outputArgs;
const std::string paramNames;
method_callback callback;
Flags flags_;
};
std::map<MethodName, MethodData> methods_;
std::map<MethodName, MethodData> methods;
using SignalName = std::string;
struct SignalData
{
std::string signature_;
Flags flags_;
const std::string signature;
const std::string paramNames;
Flags flags;
};
std::map<SignalName, SignalData> signals_;
std::map<SignalName, SignalData> signals;
using PropertyName = std::string;
struct PropertyData
{
std::string signature_;
property_get_callback getCallback_;
property_set_callback setCallback_;
Flags flags_;
const std::string signature;
property_get_callback getCallback;
property_set_callback setCallback;
Flags flags;
};
std::map<PropertyName, PropertyData> properties_;
std::vector<sd_bus_vtable> vtable_;
Flags flags_;
std::map<PropertyName, PropertyData> properties;
std::vector<sd_bus_vtable> vtable;
Flags flags;
std::unique_ptr<sd_bus_slot, std::function<void(sd_bus_slot*)>> slot_;
SlotPtr slot;
};
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData);
@ -121,6 +147,7 @@ namespace internal {
void activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable );
static std::string paramNamesToString(const std::vector<std::string>& paramNames);
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_property_get_callback( sd_bus *bus
@ -142,6 +169,7 @@ namespace internal {
sdbus::internal::IConnection& connection_;
std::string objectPath_;
std::map<InterfaceName, InterfaceData> interfaces_;
SlotPtr objectManagerSlot_;
};
}

View File

@ -1,205 +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>
#include <chrono>
#include <thread>
namespace sdbus { namespace internal {
ObjectProxy::ObjectProxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
// The connection is not ours only, it is managed by the client and we just reference it here,
// so we expect the client to manage the event loop upon this connection themselves.
}
ObjectProxy::ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath )
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
// The connection is ours only, so we have to manage event loop upon this connection,
// so we get signals, async replies, and other messages from D-Bus.
connection_->enterProcessingLoopAsync();
}
MethodCall ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
{
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
}
AsyncMethodCall ObjectProxy::createAsyncMethodCall(const std::string& interfaceName, const std::string& methodName)
{
return AsyncMethodCall{ObjectProxy::createMethodCall(interfaceName, methodName)};
}
MethodReply ObjectProxy::callMethod(const MethodCall& message)
{
return message.send();
}
void ObjectProxy::callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback)
{
auto callback = (void*)&ObjectProxy::sdbus_async_reply_handler;
// Allocated userData gets deleted in the sdbus_async_reply_handler
auto userData = new AsyncReplyUserData{*this, std::move(asyncReplyCallback)};
message.send(callback, userData);
}
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()
{
registerSignalHandlers(*connection_);
}
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](sd_bus_slot *slot){ connection.unregisterSignalHandler(slot); };
}
}
}
int ObjectProxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
// We are assuming the ownership of the async reply handler pointer passed here
std::unique_ptr<AsyncReplyUserData> asyncReplyUserData{static_cast<AsyncReplyUserData*>(userData)};
assert(asyncReplyUserData != nullptr);
assert(asyncReplyUserData->callback);
MethodReply message{sdbusMessage, &asyncReplyUserData->proxy.connection_->getSdBusInterface()};
const auto* error = sd_bus_message_get_error(sdbusMessage);
if (error == nullptr)
{
asyncReplyUserData->callback(message, nullptr);
}
else
{
sdbus::Error exception(error->name, error->message);
asyncReplyUserData->callback(message, &exception);
}
}
int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
{
auto* proxy = static_cast<ObjectProxy*>(userData);
assert(proxy != nullptr);
Signal message{sdbusMessage, &proxy->connection_->getSdBusInterface()};
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = proxy->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) );
}
}

View File

@ -1,98 +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 );
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
AsyncMethodCall createAsyncMethodCall(const std::string& interfaceName, const std::string& methodName) override;
MethodReply callMethod(const MethodCall& message) override;
void callMethod(const AsyncMethodCall& message, async_reply_handler asyncReplyCallback) override;
void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler ) override;
void finishRegistration() override;
private:
struct AsyncReplyUserData
{
ObjectProxy& proxy;
async_reply_handler callback;
};
void registerSignalHandlers(sdbus::internal::IConnection& connection);
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_signal_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_;
std::string destination_;
std::string objectPath_;
using InterfaceName = std::string;
struct InterfaceData
{
using SignalName = std::string;
struct SignalData
{
signal_handler callback_;
std::unique_ptr<sd_bus_slot, std::function<void(sd_bus_slot*)>> slot_;
};
std::map<SignalName, SignalData> signals_;
};
std::map<InterfaceName, InterfaceData> interfaces_;
};
}}
#endif /* SDBUS_CXX_INTERNAL_OBJECTPROXY_H_ */

287
src/Proxy.cpp Normal file
View File

@ -0,0 +1,287 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Proxy.cpp
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Proxy.h"
#include "IConnection.h"
#include "MessageUtils.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Error.h"
#include "ScopeGuard.h"
#include <systemd/sd-bus.h>
#include <cassert>
#include <chrono>
#include <thread>
namespace sdbus { namespace internal {
Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
// The connection is not ours only, it is owned and managed by the user and we just reference
// it here, so we expect the client to manage the event loop upon this connection themselves.
}
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath )
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
// 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_->enterProcessingLoopAsync();
}
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
{
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
}
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
{
// Sending method call synchronously is the only operation that blocks, waiting for the method
// reply message among the incoming message on the sd-bus connection socket. But typically there
// already is somebody that generally handles incoming D-Bus messages -- the connection event loop
// running typically in its own thread. We have to avoid polling on socket from several threads.
// So we have to branch here: either we are within the context of the event loop thread, then we
// can send the message simply via sd_bus_call, which blocks. Or we are in another thread, then
// we can perform the send operation of the method call message from here (because that is thread-
// safe like other sd-bus API accesses), but the incoming reply we have to get through the event
// loop thread, because this is be the only rightful listener on the sd-bus connection socket.
// So, technically, we use async means to wait here for reply received by the event loop thread.
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
// If we don't need to wait for any reply, we can send the message now irrespective of the context
if (message.doesntExpectReply())
return message.send(timeout);
// If we are in the context of event loop thread, we can send the D-Bus call synchronously
// and wait blockingly for the reply, because we are the exclusive listeners on the socket
auto reply = connection_->tryCallMethodSynchronously(message, timeout);
if (reply.isValid())
return reply;
// Otherwise we send the call asynchronously and do blocking wait for the reply from the event loop thread
return sendMethodCallMessageAndWaitForReply(message, timeout);
}
void Proxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
auto callData = std::make_unique<AsyncCalls::CallData>(AsyncCalls::CallData{*this, std::move(asyncReplyCallback), {}});
callData->slot = message.send(callback, callData.get(), timeout);
pendingAsyncCalls_.addCall(callData->slot.get(), std::move(callData));
}
MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout)
{
/*thread_local*/ SyncCallReplyData syncCallReplyData;
async_reply_handler asyncReplyCallback = [&syncCallReplyData](MethodReply& reply, const Error* error)
{
syncCallReplyData.sendMethodReplyToWaitingThread(reply, error);
};
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
AsyncCalls::CallData callData{*this, std::move(asyncReplyCallback), {}};
message.send(callback, &callData, timeout, dont_request_slot);
return syncCallReplyData.waitForMethodReply();
}
void Proxy::SyncCallReplyData::sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error)
{
SCOPE_EXIT{ cond_.notify_one(); };
std::unique_lock<std::mutex> lock{mutex_};
SCOPE_EXIT{ arrived_ = true; };
//error_ = nullptr; // Necessary if SyncCallReplyData instance is thread_local
if (error == nullptr)
reply_ = std::move(reply);
else
error_ = std::make_unique<Error>(*error);
}
MethodReply Proxy::SyncCallReplyData::waitForMethodReply()
{
std::unique_lock<std::mutex> lock{mutex_};
cond_.wait(lock, [this](){ return arrived_; });
//arrived_ = false; // Necessary if SyncCallReplyData instance is thread_local
if (error_)
throw *error_;
return std::move(reply_);
}
void Proxy::registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler )
{
SDBUS_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 Proxy::finishRegistration()
{
registerSignalHandlers(*connection_);
}
void Proxy::registerSignalHandlers(sdbus::internal::IConnection& connection)
{
for (auto& interfaceItem : interfaces_)
{
const auto& interfaceName = interfaceItem.first;
auto& signalsOnInterface = interfaceItem.second.signals_;
for (auto& signalItem : signalsOnInterface)
{
const auto& signalName = signalItem.first;
auto& slot = signalItem.second.slot_;
slot = connection.registerSignalHandler( objectPath_
, interfaceName
, signalName
, &Proxy::sdbus_signal_handler
, this );
}
}
}
void Proxy::unregister()
{
pendingAsyncCalls_.clear();
interfaces_.clear();
}
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
{
auto* asyncCallData = static_cast<AsyncCalls::CallData*>(userData);
assert(asyncCallData != nullptr);
assert(asyncCallData->callback);
auto& proxy = asyncCallData->proxy;
SCOPE_EXIT
{
// Slot may be null if we're doing blocking synchronous call implemented by means of asynchronous call,
// because in that case the call data is still alive on the stack, we don't need to manage it separately.
if (asyncCallData->slot)
proxy.pendingAsyncCalls_.removeCall(asyncCallData->slot.get());
};
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
const auto* error = sd_bus_message_get_error(sdbusMessage);
if (error == nullptr)
{
asyncCallData->callback(message, nullptr);
}
else
{
sdbus::Error exception(error->name, error->message);
asyncCallData->callback(message, &exception);
}
return 1;
}
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
{
auto* proxy = static_cast<Proxy*>(userData);
assert(proxy != nullptr);
auto message = Message::Factory::create<Signal>(sdbusMessage, &proxy->connection_->getSdBusInterface());
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
auto& callback = proxy->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_;
assert(callback);
callback(message);
return 1;
}
}}
namespace sdbus {
std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
, std::string destination
, std::string objectPath )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
return std::make_unique<sdbus::internal::Proxy>( *sdbusConnection
, std::move(destination)
, std::move(objectPath) );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, std::string destination
, std::string objectPath )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
connection.release();
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath )
{
auto connection = sdbus::createConnection();
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
assert(sdbusConnection != nullptr);
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
}
}

152
src/Proxy.h Normal file
View File

@ -0,0 +1,152 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Proxy.h
*
* Created on: Nov 8, 2016
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_INTERNAL_PROXY_H_
#define SDBUS_CXX_INTERNAL_PROXY_H_
#include <sdbus-c++/IProxy.h>
#include "IConnection.h"
#include <systemd/sd-bus.h>
#include <string>
#include <memory>
#include <map>
#include <unordered_map>
#include <mutex>
#include <condition_variable>
namespace sdbus {
namespace internal {
class Proxy
: public IProxy
{
public:
Proxy( sdbus::internal::IConnection& connection
, std::string destination
, std::string objectPath );
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath );
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
void callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler ) override;
void finishRegistration() override;
void unregister() override;
private:
class SyncCallReplyData
{
public:
void sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error);
MethodReply waitForMethodReply();
private:
std::mutex mutex_;
std::condition_variable cond_;
bool arrived_{};
MethodReply reply_;
std::unique_ptr<Error> error_;
};
MethodReply sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout);
void registerSignalHandlers(sdbus::internal::IConnection& connection);
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
private:
std::unique_ptr< sdbus::internal::IConnection
, std::function<void(sdbus::internal::IConnection*)>
> connection_;
std::string destination_;
std::string objectPath_;
using InterfaceName = std::string;
struct InterfaceData
{
using SignalName = std::string;
struct SignalData
{
signal_handler callback_;
SlotPtr slot_;
};
std::map<SignalName, SignalData> signals_;
};
std::map<InterfaceName, InterfaceData> interfaces_;
// We need to keep track of pending async calls. When the proxy is being destructed, we must
// remove all slots of these pending calls, otherwise in case when the connection outlives
// the proxy, we might get async reply handlers invoked for pending async calls after the proxy
// has been destroyed, which is a free ticket into the realm of undefined behavior.
class AsyncCalls
{
public:
struct CallData
{
Proxy& proxy;
async_reply_handler callback;
MethodCall::Slot slot;
};
~AsyncCalls()
{
clear();
}
bool addCall(void* slot, std::unique_ptr<CallData>&& asyncCallData)
{
std::lock_guard<std::mutex> lock(mutex_);
return calls_.emplace(slot, std::move(asyncCallData)).second;
}
bool removeCall(void* slot)
{
std::lock_guard<std::mutex> lock(mutex_);
return calls_.erase(slot) > 0;
}
void clear()
{
std::unique_lock<std::mutex> lock(mutex_);
auto asyncCallSlots = std::move(calls_);
// Perform releasing of sd-bus slots outside of the calls_ critical section which avoids
// double mutex dead lock when the async reply handler is invoked at the same time.
lock.unlock();
}
private:
std::unordered_map<void*, std::unique_ptr<CallData>> calls_;
std::mutex mutex_;
} pendingAsyncCalls_;
};
}}
#endif /* SDBUS_CXX_INTERNAL_PROXY_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file ScopeGuard.h
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file SdBus.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -25,6 +26,7 @@
*/
#include "SdBus.h"
#include <sdbus-c++/Error.h>
namespace sdbus { namespace internal {
@ -63,6 +65,13 @@ int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m,
return ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
}
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type)
{
std::unique_lock<std::recursive_mutex> 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::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
@ -91,6 +100,67 @@ int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message
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::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_set_method_call_timeout(bus, usec);
#else
(void)bus;
(void)usec;
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Setting general method call timeout not supported by underlying version of libsystemd");
#endif
}
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
{
#if LIBSYSTEMD_VERSION>=240
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_get_method_call_timeout(bus, ret);
#else
(void)bus;
(void)ret;
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Getting general method call timeout not supported by underlying version of libsystemd");
#endif
}
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
{
std::unique_lock<std::recursive_mutex> 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::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_emit_object_added(bus, path);
}
int SdBus::sd_bus_emit_object_removed(sd_bus *bus, const char *path)
{
std::unique_lock<std::recursive_mutex> 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::unique_lock<std::recursive_mutex> 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::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
}
int SdBus::sd_bus_open_user(sd_bus **ret)
{
return ::sd_bus_open_user(ret);
@ -101,6 +171,11 @@ int SdBus::sd_bus_open_system(sd_bus **ret)
return ::sd_bus_open_system(ret);
}
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
{
return ::sd_bus_open_system_remote(ret, host);
}
int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags)
{
std::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
@ -115,6 +190,12 @@ int SdBus::sd_bus_release_name(sd_bus *bus, const char *name)
return ::sd_bus_release_name(bus, name);
}
int SdBus::sd_bus_get_unique_name(sd_bus *bus, const char **name)
{
std::unique_lock<std::recursive_mutex> 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::unique_lock<std::recursive_mutex> lock(sdbusMutex_);
@ -122,6 +203,13 @@ int SdBus::sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char
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::unique_lock<std::recursive_mutex> 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::unique_lock<std::recursive_mutex> lock(sdbusMutex_);

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file SdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -42,16 +43,29 @@ public:
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_user(sd_bus **ret) override;
virtual int sd_bus_open_system(sd_bus **ret) override;
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override;
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
virtual int sd_bus_release_name(sd_bus *bus, const char *name) override;
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Types.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file VTableUtils.c
*
@ -35,18 +36,46 @@ sd_bus_vtable createVTableStartItem(uint64_t flags)
sd_bus_vtable createVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags )
{
#if LIBSYSTEMD_VERSION>=242
// We have to expand macro SD_BUS_METHOD_WITH_NAMES manually here, because the macro expects literal char strings
/*struct sd_bus_vtable vtableItem = SD_BUS_METHOD_WITH_NAMES(member, signature, innames, result, outnames, handler, flags);*/
struct sd_bus_vtable vtableItem =
{
.type = _SD_BUS_VTABLE_METHOD,
.flags = flags,
.x = {
.method = {
.member = member,
.signature = signature,
.result = result,
.handler = handler,
.offset = 0,
.names = paramNames,
},
},
};
#else
(void)paramNames;
struct sd_bus_vtable vtableItem = SD_BUS_METHOD(member, signature, result, handler, flags);
#endif
return vtableItem;
}
sd_bus_vtable createVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags )
{
#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;
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file VTableUtils.h
*
@ -37,10 +38,12 @@ sd_bus_vtable createVTableStartItem(uint64_t flags);
sd_bus_vtable createVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags );
sd_bus_vtable createVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags );
sd_bus_vtable createVTablePropertyItem( const char *member
, const char *signature

View File

@ -1,428 +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 "SdBus.h"
#include "TestingAdaptor.h"
#include "TestingProxy.h"
// sdbus
#include "sdbus-c++/sdbus-c++.h"
// gmock
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// STL
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
using ::testing::Eq;
using ::testing::Gt;
using ::testing::ElementsAre;
using namespace std::chrono_literals;
namespace
{
class AdaptorAndProxyFixture : public ::testing::Test
{
public:
static void SetUpTestCase()
{
s_connection->requestName(INTERFACE_NAME);
s_connection->enterProcessingLoopAsync();
}
static void TearDownTestCase()
{
s_connection->leaveProcessingLoop();
s_connection->releaseName(INTERFACE_NAME);
}
private:
void SetUp() override
{
m_adaptor = std::make_unique<TestingAdaptor>(*s_connection);
m_proxy = std::make_unique<TestingProxy>(INTERFACE_NAME, OBJECT_PATH);
std::this_thread::sleep_for(50ms); // Give time for the proxy to start listening to signals
}
void TearDown() override
{
m_proxy.reset();
m_adaptor.reset();
}
public:
static std::unique_ptr<sdbus::IConnection> s_connection;
std::unique_ptr<TestingAdaptor> m_adaptor;
std::unique_ptr<TestingProxy> m_proxy;
};
std::unique_ptr<sdbus::IConnection> AdaptorAndProxyFixture::s_connection = sdbus::createSystemBusConnection();
}
/*-------------------------------------*/
/* -- 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, CallsMultiplyMethodWithNoReplyFlag)
{
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
for (auto i = 0; i < 100; ++i)
{
if (m_adaptor->wasMultiplyCalled())
break;
std::this_thread::sleep_for(10ms);
}
ASSERT_TRUE(m_adaptor->wasMultiplyCalled());
ASSERT_THAT(m_adaptor->getMultiplyResult(), Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
{
try
{
m_proxy->throwError();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), Eq("org.freedesktop.DBus.Error.AccessDenied"));
ASSERT_THAT(e.getMessage(), Eq("A test error occurred (Operation not permitted)"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
for (auto i = 0; i < 100; ++i)
{
if (m_adaptor->wasThrowErrorCalled())
break;
std::this_thread::sleep_for(10ms);
}
ASSERT_TRUE(m_adaptor->wasThrowErrorCalled());
}
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
{
// Yeah, this is kinda timing-dependent test, but times should be safe...
std::mutex mtx;
std::vector<uint32_t> results;
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&](uint32_t param)
{
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
auto result = proxy.doOperationAsync(param);
std::lock_guard<std::mutex> guard(mtx);
results.push_back(result);
};
std::thread invocations[]{std::thread{call, 1500}, std::thread{call, 1000}, std::thread{call, 500}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
}
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
{
std::atomic<size_t> resultCount{};
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&]()
{
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
size_t localResultCount{};
for (size_t i = 0; i < 500; ++i)
{
auto result = proxy.doOperationAsync(i % 2);
if (result == (i % 2)) // Correct return value?
localResultCount++;
}
resultCount += localResultCount;
};
std::thread invocations[]{std::thread{call}, std::thread{call}, std::thread{call}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(resultCount, Eq(1500));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doOperationClientSideAsync(100);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}
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("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
{
TestingProxy proxy(INTERFACE_NAME, "/sdbuscpp/path/that/does/not/exist");
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"));
}
// Properties
TEST_F(SdbusTestObject, ReadsReadPropertySuccesfully)
{
ASSERT_THAT(m_proxy->state(), Eq(STRING_VALUE));
}
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
{
uint32_t 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);
}
TEST_F(SdbusTestObject, AnswersXmlApiDescriptionOnIntrospection)
{
ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
}

View File

@ -1,78 +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, sdbus::introspectable_proxy>
{
public:
using sdbus::ProxyInterfaces<::testing_proxy, sdbus::introspectable_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; }
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
{
m_DoOperationClientSideAsyncReplyHandler = handler;
}
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)));
}
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error) override
{
if (m_DoOperationClientSideAsyncReplyHandler)
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
}
private:
int m_simpleCallCounter{};
std::map<int32_t, std::string> m_map;
double m_variantValue;
std::map<std::string, std::string> m_signature;
std::function<void(uint32_t res, const sdbus::Error* err)> m_DoOperationClientSideAsyncReplyHandler;
};
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ */

View File

@ -1,46 +0,0 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__perftest_adaptor_h__adaptor__H__
#define __sdbuscpp__perftest_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class perftest_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.perftests";
protected:
perftest_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("sendDataSignals").onInterface(interfaceName).implementedAs([this](const uint32_t& numberOfSignals, const uint32_t& signalMsgSize){ return this->sendDataSignals(numberOfSignals, signalMsgSize); });
object_.registerMethod("concatenateTwoStrings").onInterface(interfaceName).implementedAs([this](const std::string& string1, const std::string& string2){ return this->concatenateTwoStrings(string1, string2); });
object_.registerSignal("dataSignal").onInterface(interfaceName).withParameters<std::string>();
}
public:
void dataSignal(const std::string& data)
{
object_.emitSignal("dataSignal").onInterface(interfaceName).withArguments(data);
}
private:
virtual void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) = 0;
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) = 0;
private:
sdbus::IObject& object_;
};
}} // namespaces
#endif

View File

@ -1,49 +0,0 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__perftest_proxy_h__proxy__H__
#define __sdbuscpp__perftest_proxy_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class perftest_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.perftests";
protected:
perftest_proxy(sdbus::IObjectProxy& object)
: object_(object)
{
object_.uponSignal("dataSignal").onInterface(interfaceName).call([this](const std::string& data){ this->onDataSignal(data); });
}
virtual void onDataSignal(const std::string& data) = 0;
public:
void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize)
{
object_.callMethod("sendDataSignals").onInterface(interfaceName).withArguments(numberOfSignals, signalMsgSize);
}
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2)
{
std::string result;
object_.callMethod("concatenateTwoStrings").onInterface(interfaceName).withArguments(string1, string2).storeResultsTo(result);
return result;
}
private:
sdbus::IObjectProxy& object_;
};
}} // namespaces
#endif

View File

@ -1,424 +0,0 @@
/**
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file sdbus-c++-stress-tests.cpp
*
* Created on: Jan 25, 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 "celsius-thermometer-adaptor.h"
#include "celsius-thermometer-proxy.h"
#include "fahrenheit-thermometer-adaptor.h"
#include "fahrenheit-thermometer-proxy.h"
#include "concatenator-adaptor.h"
#include "concatenator-proxy.h"
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include <string>
#include <iostream>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <cassert>
#include <atomic>
#include <sstream>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std::chrono_literals;
using namespace std::string_literals;
#define SERVICE_1_BUS_NAME "org.sdbuscpp.stresstests.service1"s
#define SERVICE_2_BUS_NAME "org.sdbuscpp.stresstests.service2"s
#define CELSIUS_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/celsius/thermometer"s
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"s
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"s
class CelsiusThermometerAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>
{
public:
using sdbus::Interfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>::Interfaces;
protected:
virtual uint32_t getCurrentTemperature() override
{
return m_currentTemperature++;
}
private:
uint32_t m_currentTemperature{};
};
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>
{
public:
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>::ProxyInterfaces;
};
class FahrenheitThermometerAdaptor : public sdbus::Interfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_adaptor
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >
{
public:
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
: sdbus::Interfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_adaptor
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >(connection, std::move(objectPath))
, celsiusProxy_(connection, SERVICE_2_BUS_NAME, CELSIUS_THERMOMETER_OBJECT_PATH)
{
unsigned int workers = std::thread::hardware_concurrency();
if (workers < 4)
workers = 4;
for (unsigned int i = 0; i < workers; ++i)
workers_.emplace_back([this]()
{
//std::cout << "Created FTA worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
while(!exit_)
{
// Pop a work item from the queue
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this]{return !requests_.empty() || exit_;});
if (exit_)
break;
auto request = std::move(requests_.front());
requests_.pop();
lock.unlock();
// Either create or destroy a delegate object
if (request.delegateObjectPath.empty())
{
// Create new delegate object
auto& connection = getObject().getConnection();
sdbus::ObjectPath newObjectPath = FAHRENHEIT_THERMOMETER_OBJECT_PATH + "/" + std::to_string(request.objectNr);
// Here we are testing dynamic creation of a D-Bus object in an async way
auto adaptor = std::make_unique<FahrenheitThermometerAdaptor>(connection, newObjectPath);
std::unique_lock<std::mutex> lock{childrenMutex_};
children_.emplace(newObjectPath, std::move(adaptor));
lock.unlock();
request.result.returnResults(newObjectPath);
}
else
{
// Destroy existing delegate object
// Here we are testing dynamic removal of a D-Bus object in an async way
std::lock_guard<std::mutex> lock{childrenMutex_};
children_.erase(request.delegateObjectPath);
}
}
});
}
~FahrenheitThermometerAdaptor()
{
exit_ = true;
cond_.notify_all();
for (auto& worker : workers_)
worker.join();
}
protected:
virtual uint32_t getCurrentTemperature() override
{
// In this D-Bus call, make yet another D-Bus call to another service over the same connection
return static_cast<uint32_t>(celsiusProxy_.getCurrentTemperature() * 1.8 + 32.);
}
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) override
{
static size_t objectCounter{};
objectCounter++;
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{objectCounter, std::string{}, std::move(result)});
lock.unlock();
cond_.notify_one();
}
virtual void destroyDelegateObject(sdbus::Result<>&& /*result*/, sdbus::ObjectPath delegate) override
{
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{0, std::move(delegate), {}});
lock.unlock();
cond_.notify_one();
}
private:
CelsiusThermometerProxy celsiusProxy_;
std::map<std::string, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
std::mutex childrenMutex_;
struct WorkItem
{
size_t objectNr;
sdbus::ObjectPath delegateObjectPath;
sdbus::Result<sdbus::ObjectPath> result;
};
std::mutex mutex_;
std::condition_variable cond_;
std::queue<WorkItem> requests_;
std::vector<std::thread> workers_;
std::atomic<bool> exit_{};
};
class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_proxy
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >
{
public:
using sdbus::ProxyInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_proxy
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >::ProxyInterfaces;
};
class ConcatenatorAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstests::concatenator_adaptor>
{
public:
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
: sdbus::Interfaces<org::sdbuscpp::stresstests::concatenator_adaptor>(connection, std::move(objectPath))
{
unsigned int workers = std::thread::hardware_concurrency();
if (workers < 4)
workers = 4;
for (unsigned int i = 0; i < workers; ++i)
workers_.emplace_back([this]()
{
//std::cout << "Created CA worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
while(!exit_)
{
// Pop a work item from the queue
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this]{return !requests_.empty() || exit_;});
if (exit_)
break;
auto request = std::move(requests_.front());
requests_.pop();
lock.unlock();
// Do concatenation work, return results and fire signal
auto aString = request.input.at("key1").get<std::string>();
auto aNumber = request.input.at("key2").get<uint32_t>();
auto resultString = aString + " " + std::to_string(aNumber);
request.result.returnResults(resultString);
concatenatedSignal(resultString);
}
});
}
~ConcatenatorAdaptor()
{
exit_ = true;
cond_.notify_all();
for (auto& worker : workers_)
worker.join();
}
protected:
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) override
{
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{std::move(params), std::move(result)});
lock.unlock();
cond_.notify_one();
}
private:
struct WorkItem
{
std::map<std::string, sdbus::Variant> input;
sdbus::Result<std::string> result;
};
std::mutex mutex_;
std::condition_variable cond_;
std::queue<WorkItem> requests_;
std::vector<std::thread> workers_;
std::atomic<bool> exit_{};
};
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>
{
public:
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>::ProxyInterfaces;
private:
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) override
{
assert(error == nullptr);
std::stringstream str(result);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
str >> aNumber;
assert(aNumber >= 0);
++repliesReceived_;
}
virtual void onConcatenatedSignal(const std::string& concatenatedString) override
{
std::stringstream str(concatenatedString);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
str >> aNumber;
assert(aNumber >= 0);
++signalsReceived_;
}
public:
std::atomic<uint32_t> repliesReceived_{};
std::atomic<uint32_t> signalsReceived_{};
};
//-----------------------------------------
int main(int /*argc*/, char */*argv*/[])
{
auto service2Connection = sdbus::createSystemBusConnection(SERVICE_2_BUS_NAME);
std::thread service2Thread([&con = *service2Connection]()
{
CelsiusThermometerAdaptor thermometer(con, CELSIUS_THERMOMETER_OBJECT_PATH);
con.enterProcessingLoop();
});
auto service1Connection = sdbus::createSystemBusConnection(SERVICE_1_BUS_NAME);
std::thread service1Thread([&con = *service1Connection]()
{
ConcatenatorAdaptor concatenator(con, CONCATENATOR_OBJECT_PATH);
FahrenheitThermometerAdaptor thermometer(con, FAHRENHEIT_THERMOMETER_OBJECT_PATH);
con.enterProcessingLoop();
});
std::this_thread::sleep_for(100ms);
std::atomic<uint32_t> concatenationCallsMade{0};
std::atomic<uint32_t> concatenationRepliesReceived{0};
std::atomic<uint32_t> concatenationSignalsReceived{0};
std::atomic<uint32_t> thermometerCallsMade{0};
auto clientConnection = sdbus::createSystemBusConnection();
std::thread clientThread([&, &con = *clientConnection]()
{
std::atomic<bool> stopClients{false};
std::thread concatenatorThread([&]()
{
ConcatenatorProxy concatenator(con, SERVICE_1_BUS_NAME, CONCATENATOR_OBJECT_PATH);
uint32_t localCounter{};
// Issue async concatenate calls densely one after another
while (!stopClients)
{
std::map<std::string, sdbus::Variant> param;
param["key1"] = "sdbus-c++-stress-tests";
param["key2"] = localCounter++;
concatenator.concatenate(param);
if ((localCounter % 10) == 0)
{
// Make sure the system is catching up with our async requests,
// otherwise sleep a bit to slow down flooding the server.
assert(localCounter >= concatenator.repliesReceived_);
while ((localCounter - concatenator.repliesReceived_) > 40 && !stopClients)
std::this_thread::sleep_for(1ms);
// Update statistics
concatenationCallsMade = localCounter;
concatenationRepliesReceived = (uint32_t)concatenator.repliesReceived_;
concatenationSignalsReceived = (uint32_t)concatenator.signalsReceived_;
}
}
});
std::thread thermometerThread([&]()
{
// Here we continuously remotely call getCurrentTemperature(). We have one proxy object,
// first we use it's factory interface to create another proxy object, call getCurrentTemperature()
// on that one, and then destroy that proxy object. All that continously in a loop.
// This tests dynamic creation and destruction of remote D-Bus objects and local object proxies.
FahrenheitThermometerProxy thermometer(con, SERVICE_1_BUS_NAME, FAHRENHEIT_THERMOMETER_OBJECT_PATH);
uint32_t localCounter{};
uint32_t previousTemperature{};
while (!stopClients)
{
localCounter++;
auto newObjectPath = thermometer.createDelegateObject();
FahrenheitThermometerProxy proxy{con, SERVICE_1_BUS_NAME, newObjectPath};
auto temperature = proxy.getCurrentTemperature();
assert(temperature >= previousTemperature); // The temperature shall rise continually
previousTemperature = temperature;
//std::this_thread::sleep_for(1ms);
if ((localCounter % 10) == 0)
thermometerCallsMade = localCounter;
proxy.~FahrenheitThermometerProxy();
thermometer.destroyDelegateObject(newObjectPath);
}
});
con.enterProcessingLoop();
stopClients = true;
concatenatorThread.join();
thermometerThread.join();
});
std::atomic<bool> exitLogger{};
std::thread loggerThread([&]()
{
while (!exitLogger)
{
std::this_thread::sleep_for(1s);
std::cout << "Made " << concatenationCallsMade << " concatenation calls, received " << concatenationRepliesReceived << " replies and " << concatenationSignalsReceived << " signals so far." << std::endl;
std::cout << "Made " << thermometerCallsMade << " thermometer calls so far." << std::endl << std::endl;
}
});
getchar();
exitLogger = true;
loggerThread.join();
clientConnection->leaveProcessingLoop();
clientThread.join();
service1Connection->leaveProcessingLoop();
service1Thread.join();
service2Connection->leaveProcessingLoop();
service2Thread.join();
return 0;
}

View File

@ -1,148 +0,0 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file Connection_test.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
*
* Created on: Feb 4, 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 "Connection.h"
#include "unittests/mocks/SdBusMock.h"
#include <gtest/gtest.h>
using ::testing::_;
using ::testing::DoAll;
using ::testing::SetArgPointee;
using ::testing::Return;
using ::testing::NiceMock;
using BusType = sdbus::internal::Connection::BusType;
class ConnectionCreationTest : public ::testing::Test
{
protected:
ConnectionCreationTest() = default;
std::unique_ptr<NiceMock<SdBusMock>> mock_ { std::make_unique<NiceMock<SdBusMock>>() };
sd_bus* STUB_ { reinterpret_cast<sd_bus*>(1) };
};
using ASystemBusConnection = ConnectionCreationTest;
using ASessionBusConnection = ConnectionCreationTest;
TEST_F(ASystemBusConnection, OpensAndFlushesBusWhenCreated)
{
EXPECT_CALL(*mock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush(_)).Times(1);
sdbus::internal::Connection(BusType::eSystem, std::move(mock_));
}
TEST_F(ASessionBusConnection, OpensAndFlushesBusWhenCreated)
{
EXPECT_CALL(*mock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush(_)).Times(1);
sdbus::internal::Connection(BusType::eSession, std::move(mock_));
}
TEST_F(ASystemBusConnection, ClosesAndUnrefsBusWhenDestructed)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush_close_unref(_)).Times(1);
sdbus::internal::Connection(BusType::eSession, std::move(mock_));
}
TEST_F(ASessionBusConnection, ClosesAndUnrefsBusWhenDestructed)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
EXPECT_CALL(*mock_, sd_bus_flush_close_unref(_)).Times(1);
sdbus::internal::Connection(BusType::eSession, std::move(mock_));
}
TEST_F(ASystemBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(-1)));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSystem, std::move(mock_)), sdbus::Error);
}
TEST_F(ASessionBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(-1)));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSession, std::move(mock_)), sdbus::Error);
}
TEST_F(ASystemBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSystem, std::move(mock_)), sdbus::Error);
}
TEST_F(ASessionBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{
ON_CALL(*mock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(STUB_), Return(1)));
ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(sdbus::internal::Connection(BusType::eSession, std::move(mock_)), sdbus::Error);
}
class ConnectionRequestTest : public ::testing::TestWithParam<BusType>
{
protected:
ConnectionRequestTest() = default;
void SetUp() override
{
switch (GetParam())
{
case BusType::eSystem:
EXPECT_CALL(*mock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
break;
case BusType::eSession:
EXPECT_CALL(*mock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(STUB_), Return(1)));
break;
default:
break;
}
ON_CALL(*mock_, sd_bus_flush(_)).WillByDefault(Return(1));
ON_CALL(*mock_, sd_bus_flush_close_unref(_)).WillByDefault(Return(STUB_));
}
std::unique_ptr<NiceMock<SdBusMock>> mock_ { std::make_unique<NiceMock<SdBusMock>>() };
sd_bus* STUB_ { reinterpret_cast<sd_bus*>(1) };
};
using AConnectionNameRequest = ConnectionRequestTest;
TEST_P(AConnectionNameRequest, DoesNotThrowOnSuccess)
{
EXPECT_CALL(*mock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1));
sdbus::internal::Connection(GetParam(), std::move(mock_)).requestName("");
}
TEST_P(AConnectionNameRequest, ThrowsOnFail)
{
EXPECT_CALL(*mock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1));
sdbus::internal::Connection conn_(GetParam(), std::move(mock_));
ASSERT_THROW(conn_.requestName(""), sdbus::Error);
}
INSTANTIATE_TEST_SUITE_P(Request, AConnectionNameRequest, ::testing::Values(BusType::eSystem, BusType::eSession));

View File

@ -21,11 +21,16 @@ if(result)
message(FATAL_ERROR "Build step for googletest failed: ${result}")
endif()
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
#-------------------------------
# SOURCE FILES CONFIGURATION
@ -54,10 +59,10 @@ set(INTEGRATIONTESTS_SRCS
set(PERFTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/perftests)
set(STRESSTESTS_CLIENT_SRCS
${PERFTESTS_SOURCE_DIR}/client.cpp
${PERFTESTS_SOURCE_DIR}/perftest-proxy.h)
${PERFTESTS_SOURCE_DIR}/perftests-proxy.h)
set(STRESSTESTS_SERVER_SRCS
${PERFTESTS_SOURCE_DIR}/server.cpp
${PERFTESTS_SOURCE_DIR}/perftest-adaptor.h)
${PERFTESTS_SOURCE_DIR}/perftests-adaptor.h)
set(STRESSTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stresstests)
set(STRESSTESTS_SRCS
@ -79,14 +84,15 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
# BUILD INFORMATION
#----------------------------------
# Turn off -isystem gcc option that CMake uses for imported
# targets even when INTERFACE_INCLUDE_DIRECTORIES is used.
set(CMAKE_NO_SYSTEM_FROM_IMPORTED "1")
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS} $<TARGET_OBJECTS:sdbus-cpp>)
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS} $<TARGET_OBJECTS:sdbus-c++-objlib>)
target_compile_definitions(sdbus-c++-unit-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_include_directories(sdbus-c++-unit-tests PRIVATE ${SYSTEMD_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include)
target_link_libraries(sdbus-c++-unit-tests ${SYSTEMD_LIBRARIES} gmock gmock_main)
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${SYSTEMD_VERSION})
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ gmock gmock_main)
# Manual performance and stress tests

View File

@ -1,6 +1,6 @@
# Taken from https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project
cmake_minimum_required(VERSION 2.8.2)
cmake_minimum_required(VERSION 3.6)
project(googletest-download NONE)
@ -9,8 +9,10 @@ include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG master
GIT_SHALLOW 1
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""

View File

@ -0,0 +1,682 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorAndProxy_test.cpp
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// Own
#include "TestingAdaptor.h"
#include "TestingProxy.h"
// sdbus
#include "sdbus-c++/sdbus-c++.h"
// gmock
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// STL
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#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;
namespace
{
class AdaptorAndProxyFixture : public ::testing::Test
{
public:
static void SetUpTestCase()
{
s_connection->requestName(INTERFACE_NAME);
s_connection->enterProcessingLoopAsync();
}
static void TearDownTestCase()
{
s_connection->leaveProcessingLoop();
s_connection->releaseName(INTERFACE_NAME);
}
static bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout = 5s)
{
std::chrono::milliseconds elapsed{};
std::chrono::milliseconds step{5ms};
do {
std::this_thread::sleep_for(step);
elapsed += step;
if (elapsed > timeout)
return false;
} while (!flag);
return true;
}
private:
void SetUp() override
{
m_adaptor = std::make_unique<TestingAdaptor>(*s_connection);
m_proxy = std::make_unique<TestingProxy>(INTERFACE_NAME, OBJECT_PATH);
std::this_thread::sleep_for(50ms); // Give time for the proxy to start listening to signals
}
void TearDown() override
{
m_proxy.reset();
m_adaptor.reset();
}
public:
static std::unique_ptr<sdbus::IConnection> s_connection;
std::unique_ptr<TestingAdaptor> m_adaptor;
std::unique_ptr<TestingProxy> m_proxy;
};
std::unique_ptr<sdbus::IConnection> AdaptorAndProxyFixture::s_connection = sdbus::createSystemBusConnection();
}
/*-------------------------------------*/
/* -- 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, CallsMethodWithUnixFdSuccesfully)
{
auto resUnixFd = m_proxy->getUnixFd();
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
{
auto resComplex = m_proxy->getComplex();
ASSERT_THAT(resComplex.count(0), Eq(1));
}
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
{
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_TRUE(waitUntil(m_adaptor->m_wasMultiplyCalled));
ASSERT_THAT(m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
{
auto res = m_proxy->doOperationWith500msTimeout(20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
ASSERT_THAT(res, Eq(20));
}
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
{
try
{
m_proxy->doOperationWith500msTimeout(1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), AnyOf("org.freedesktop.DBus.Error.Timeout", "org.freedesktop.DBus.Error.NoReply"));
ASSERT_THAT(e.getMessage(), AnyOf("Connection timed out", "Method call timed out"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
{
try
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doOperationClientSideAsyncWith500msTimeout(1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
future.get(), Eq(100);
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"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
{
try
{
m_proxy->throwError();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
{
ASSERT_THAT(e.getName(), Eq("org.freedesktop.DBus.Error.AccessDenied"));
ASSERT_THAT(e.getMessage(), Eq("A test error occurred (Operation not permitted)"));
}
catch(...)
{
FAIL() << "Expected sdbus::Error exception";
}
}
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
}
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
{
// Yeah, this is kinda timing-dependent test, but times should be safe...
std::mutex mtx;
std::vector<uint32_t> results;
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&](uint32_t param)
{
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
auto result = proxy.doOperationAsync(param);
std::lock_guard<std::mutex> guard(mtx);
results.push_back(result);
};
std::thread invocations[]{std::thread{call, 1500}, std::thread{call, 1000}, std::thread{call, 500}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
}
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
{
std::atomic<size_t> resultCount{};
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&]()
{
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
size_t localResultCount{};
for (size_t i = 0; i < 500; ++i)
{
auto result = proxy.doOperationAsync(i % 2);
if (result == (i % 2)) // Correct return value?
localResultCount++;
}
resultCount += localResultCount;
};
std::thread invocations[]{std::thread{call}, std::thread{call}, std::thread{call}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
ASSERT_THAT(resultCount, Eq(1500));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doOperationClientSideAsync(100);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, InvokesErroneousMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
});
m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}
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("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
{
TestingProxy proxy(INTERFACE_NAME, "/sdbuscpp/path/that/does/not/exist");
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, ReceivesTwoSignalsWhileMakingMethodCall)
{
m_proxy->emitTwoSimpleSignals();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
}
#if LIBSYSTEMD_VERSION>=240
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
{
s_connection->setMethodCallTimeout(5000000);
ASSERT_THAT(s_connection->getMethodCallTimeout(), Eq(5000000));
}
#else
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
{
ASSERT_THROW(s_connection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(s_connection->getMethodCallTimeout(), sdbus::Error);
}
#endif
// Signals
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
{
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
{
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("zero"));
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("one"));
}
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
{
double d = 3.14;
m_adaptor->emitSignalWithVariant(d);
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
}
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
{
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
}
// Properties
TEST_F(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
{
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
{
ASSERT_THROW(m_proxy->state("new_value"), sdbus::Error);
}
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
{
uint32_t newActionValue = 5678;
m_proxy->action(newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
// Standard D-Bus interfaces
TEST_F(SdbusTestObject, PingsViaPeerInterface)
{
ASSERT_NO_THROW(m_proxy->Ping());
}
TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
{
// If /etc/machine-id does not exist in your system (which is very likely because you have
// a non-systemd Linux), org.freedesktop.DBus.Peer.GetMachineId() will not work. To solve
// this, you can create /etc/machine-id yourself as symlink to /var/lib/dbus/machine-id,
// and then org.freedesktop.DBus.Peer.GetMachineId() will start to work.
if (::access("/etc/machine-id", F_OK) == -1)
GTEST_SKIP() << "/etc/machine-id file does not exist, GetMachineId() will not work";
ASSERT_NO_THROW(m_proxy->GetMachineId());
}
TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
{
ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
}
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
{
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
{
uint32_t newActionValue = 2345;
m_proxy->Set(INTERFACE_NAME, "action", newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& /*invalidatedProperties*/ )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
signalReceived = true;
};
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
m_proxy->action(DEFAULT_ACTION_VALUE*2);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
ASSERT_THAT(invalidatedProperties, SizeIs(1));
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
signalReceived = true;
};
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
{
const auto objectsInterfacesAndProperties = m_proxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
}
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
{
auto subObject1 = sdbus::createObject(*s_connection, "/sub/path1");
subObject1->registerProperty("aProperty1").onInterface("org.sdbuscpp.integrationtests.iface1").withGetter([]{return uint8_t{123};});
subObject1->finishRegistration();
auto subObject2 = sdbus::createObject(*s_connection, "/sub/path2");
subObject2->registerProperty("aProperty2").onInterface("org.sdbuscpp.integrationtests.iface2").withGetter([]{return "hi";});
subObject2->finishRegistration();
const auto objectsInterfacesAndProperties = m_proxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
EXPECT_THAT(objectsInterfacesAndProperties.at("/sub/path1").at("org.sdbuscpp.integrationtests.iface1").at("aProperty1").get<uint8_t>(), Eq(123));
EXPECT_THAT(objectsInterfacesAndProperties.at("/sub/path2").at("org.sdbuscpp.integrationtests.iface2").at("aProperty2").get<std::string>(), Eq("hi"));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3)); // 3 properties under INTERFACE_NAME
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(1));
EXPECT_THAT(interfaces[0], Eq(INTERFACE_NAME));
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(5)); // INTERFACE_NAME + 4 standard interfaces
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection_test.cpp
*

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TestingAdaptor.h
*
@ -31,32 +32,47 @@
#include <chrono>
#include <atomic>
class TestingAdaptor : public sdbus::Interfaces<testing_adaptor>
class TestingAdaptor : public sdbus::AdaptorInterfaces< testing_adaptor
, sdbus::Properties_adaptor
, sdbus::ObjectManager_adaptor >
{
public:
TestingAdaptor(sdbus::IConnection& connection) :
sdbus::Interfaces<::testing_adaptor>(connection, OBJECT_PATH) { }
AdaptorInterfaces(connection, OBJECT_PATH)
{
registerAdaptor();
}
virtual ~TestingAdaptor() { }
bool wasMultiplyCalled() const { return m_multiplyCalled; }
double getMultiplyResult() const { return m_multiplyResult; }
bool wasThrowErrorCalled() const { return m_throwErrorCalled; }
~TestingAdaptor()
{
unregisterAdaptor();
}
protected:
void noArgNoReturn() const {}
void noArgNoReturn() const
{
}
int32_t getInt() const { return INT32_VALUE; }
int32_t getInt() const
{
return INT32_VALUE;
}
std::tuple<uint32_t, std::string> getTuple() const { return std::make_tuple(UINT32_VALUE, STRING_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; }
double multiply(const int64_t& a, const double& b) const
{
return a * b;
}
void multiplyWithNoReply(const int64_t& a, const double& b) const
{
m_multiplyResult = a * b;
m_multiplyCalled = true;
m_wasMultiplyCalled = true;
}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const
@ -134,8 +150,18 @@ protected:
}
}
sdbus::Signature getSignature() const { return SIGNATURE_VALUE; }
sdbus::ObjectPath getObjectPath() const { return OBJECT_PATH_VALUE; }
sdbus::Signature getSignature() const
{
return SIGNATURE_VALUE;
}
sdbus::ObjectPath getObjectPath() const
{
return OBJECT_PATH_VALUE;
}
sdbus::UnixFd getUnixFd() const
{
return sdbus::UnixFd{UNIX_FD_VALUE};
}
ComplexType getComplex() const
{
@ -167,24 +193,52 @@ protected:
void throwError() const
{
m_throwErrorCalled = true;
m_wasThrowErrorCalled = true;
throw sdbus::createError(1, "A test error occurred");
}
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; }
void emitTwoSimpleSignals() override
{
emitSimpleSignal();
emitSignalWithMap({});
}
std::string state()
{
return m_state;
}
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;
const std::string m_state{DEFAULT_STATE_VALUE};
uint32_t m_action{DEFAULT_ACTION_VALUE};
bool m_blocking{DEFAULT_BLOCKING_VALUE};
public: // for tests
// For dont-expect-reply method call verifications
mutable std::atomic<bool> m_multiplyCalled{};
mutable std::atomic<bool> m_wasMultiplyCalled{false};
mutable double m_multiplyResult{};
mutable std::atomic<bool> m_throwErrorCalled{};
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
};

View File

@ -0,0 +1,126 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @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"
#include <atomic>
class TestingProxy : public sdbus::ProxyInterfaces< ::testing_proxy
, sdbus::Peer_proxy
, sdbus::Introspectable_proxy
, sdbus::Properties_proxy
, sdbus::ObjectManager_proxy >
{
public:
TestingProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
registerProxy();
}
~TestingProxy()
{
unregisterProxy();
}
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
{
m_DoOperationClientSideAsyncReplyHandler = handler;
}
protected:
void onSimpleSignal() override
{
m_gotSimpleSignal = true;
}
void onSignalWithMap(const std::map<int32_t, std::string>& m) override
{
m_mapFromSignal = m;
m_gotSignalWithMap = true;
}
void onSignalWithVariant(const sdbus::Variant& v) override
{
m_variantFromSignal = v.get<double>();
m_gotSignalWithVariant = true;
}
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) override
{
m_signatureFromSignal[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
m_gotSignalWithSignature = true;
}
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error) override
{
if (m_DoOperationClientSideAsyncReplyHandler)
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
}
// Signals of standard D-Bus interfaces
void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) override
{
if (m_onPropertiesChangedHandler)
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
}
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
{
if (m_onInterfacesAddedHandler)
m_onInterfacesAddedHandler(objectPath, interfacesAndProperties);
}
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces) override
{
if (m_onInterfacesRemovedHandler)
m_onInterfacesRemovedHandler(objectPath, interfaces);
}
//private:
public: // for tests
int m_SimpleSignals = 0;
std::atomic<bool> m_gotSimpleSignal{false};
std::atomic<bool> m_gotSignalWithMap{false};
std::map<int32_t, std::string> m_mapFromSignal;
std::atomic<bool> m_gotSignalWithVariant{false};
double m_variantFromSignal;
std::atomic<bool> m_gotSignalWithSignature{false};
std::map<std::string, std::string> m_signatureFromSignal;
std::function<void(uint32_t res, const sdbus::Error* err)> m_DoOperationClientSideAsyncReplyHandler;
std::function<void(const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>&)> m_onPropertiesChangedHandler;
std::function<void(const sdbus::ObjectPath&, const std::map<std::string, std::map<std::string, sdbus::Variant>>&)> m_onInterfacesAddedHandler;
std::function<void(const sdbus::ObjectPath&, const std::vector<std::string>&)> m_onInterfacesRemovedHandler;
};
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ */

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file adaptor-glue.h
*
@ -52,7 +53,6 @@ using ComplexType = std::map<
class testing_adaptor
{
protected:
testing_adaptor(sdbus::IObject& object) :
object_(object)
@ -60,17 +60,18 @@ protected:
object_.setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getInt(); });
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").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("multiply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
object_.registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
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](
object_.registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME)
.withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](
const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x ,y); });
object_.registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getStructInStruct(); });
@ -97,6 +98,7 @@ protected:
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
object_.registerMethod("getUnixFd").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getUnixFd(); });
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
@ -105,34 +107,39 @@ protected:
object_.registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([](){}).markAsPrivileged();
object_.registerMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME).implementedAs([this](){ this->emitTwoSimpleSignals(); });
// registration of signals is optional, it is useful because of introspection
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>();
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>();
// Note: sd-bus of libsystemd up to v244 has a bug where it doesn't generate signal parameter names in introspection XML. Signal param names commented temporarily.
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>(/*"aMap"*/);
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>(/*"aVariant"*/);
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
object_.registerProperty("blocking").onInterface(INTERFACE_NAME)./*withGetter([this](){ return this->blocking(); }).*/withSetter([this](const bool& value){ this->blocking(value); });
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL);
//object_.registerProperty("blocking").onInterface(INTERFACE_NAME)./*withGetter([this](){ return this->blocking(); }).*/withSetter([this](const bool& value){ this->blocking(value); });
object_.registerProperty("blocking").onInterface(INTERFACE_NAME).withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); });
}
~testing_adaptor() = default;
public:
void simpleSignal()
void emitSimpleSignal()
{
object_.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void signalWithMap(const std::map<int32_t, std::string>& map)
void emitSignalWithMap(const std::map<int32_t, std::string>& map)
{
object_.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(map);
}
void signalWithVariant(const sdbus::Variant& v)
void emitSignalWithVariant(const sdbus::Variant& v)
{
object_.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(v);
}
void signalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
{
object_.emitSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).withArguments(s);
}
@ -162,8 +169,10 @@ protected:
virtual void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) = 0;
virtual sdbus::Signature getSignature() const = 0;
virtual sdbus::ObjectPath getObjectPath() const = 0;
virtual sdbus::UnixFd getUnixFd() const = 0;
virtual ComplexType getComplex() const = 0;
virtual void throwError() const = 0;
virtual void emitTwoSimpleSignals() = 0;
virtual std::string state() = 0;
virtual uint32_t action() = 0;
@ -210,6 +219,19 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<arg type="as" name="invalidated_properties"/>
</signal>
</interface>
<interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg type="a{oa{sa{sv}}}" name="object_paths_interfaces_and_properties" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg type="o" name="object_path"/>
<arg type="a{sa{sv}}" name="interfaces_and_properties"/>
</signal>
<signal name="InterfacesRemoved">
<arg type="o" name="object_path"/>
<arg type="as" name="interfaces"/>
</signal>
</interface>
<interface name="org.sdbuscpp.integrationtests">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<method name="doOperation">
@ -223,21 +245,23 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<method name="doPrivilegedStuff">
<annotation name="org.freedesktop.systemd1.Privileged" value="true"/>
</method>
<method name="emitTwoSimpleSignals">
</method>
<method name="getComplex">
<arg type="a{t(a{ya(obva{is})}gs)}" direction="out"/>
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</method>
<method name="getInt">
<arg type="i" direction="out"/>
<arg type="i" name="anInt" direction="out"/>
</method>
<method name="getInts16FromStruct">
<arg type="(yndsan)" direction="in"/>
<arg type="an" direction="out"/>
</method>
<method name="getMapOfVariants">
<arg type="ai" direction="in"/>
<arg type="(vv)" direction="in"/>
<arg type="a{iv}" direction="out"/>
<arg type="ai" name="x" direction="in"/>
<arg type="(vv)" name="y" direction="in"/>
<arg type="a{iv}" name="aMapOfVariants" direction="out"/>
</method>
<method name="getObjectPath">
<arg type="o" direction="out"/>
@ -252,10 +276,13 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<arg type="u" direction="out"/>
<arg type="s" direction="out"/>
</method>
<method name="getUnixFd">
<arg type="h" direction="out"/>
</method>
<method name="multiply">
<arg type="x" direction="in"/>
<arg type="d" direction="in"/>
<arg type="d" direction="out"/>
<arg type="x" name="a" direction="in"/>
<arg type="d" name="b" direction="in"/>
<arg type="d" name="result" direction="out"/>
</method>
<method name="multiplyWithNoReply">
<arg type="x" direction="in"/>
@ -294,7 +321,7 @@ R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspectio
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</signal>
<property name="action" type="u" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates"/>
</property>
<property name="blocking" type="b" access="readwrite">
</property>

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file defs.h
*
@ -40,6 +41,11 @@ constexpr const int32_t INT64_VALUE{-1024};
const std::string STRING_VALUE{"sdbus-c++-testing"};
const sdbus::Signature SIGNATURE_VALUE{"a{is}"};
const sdbus::ObjectPath OBJECT_PATH_VALUE{"/"};
const int UNIX_FD_VALUE = 0;
const std::string DEFAULT_STATE_VALUE{"default-state-value"};
const uint32_t DEFAULT_ACTION_VALUE{999};
const bool DEFAULT_BLOCKING_VALUE{true};
constexpr const double DOUBLE_VALUE{3.24L};

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file proxy-glue.h
*
@ -34,7 +35,7 @@
class testing_proxy
{
protected:
testing_proxy(sdbus::IObjectProxy& object) :
testing_proxy(sdbus::IProxy& object) :
object_(object)
{
object_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
@ -45,6 +46,8 @@ protected:
{ this->onSignalWithoutRegistration(s); });
}
~testing_proxy() = default;
virtual void onSimpleSignal() = 0;
virtual void onSignalWithMap(const std::map<int32_t, std::string>& map) = 0;
virtual void onSignalWithVariant(const sdbus::Variant& v) = 0;
@ -53,6 +56,11 @@ protected:
virtual void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error) = 0;
public:
void emitTwoSimpleSignals()
{
object_.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
}
void noArgNoReturn()
{
object_.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
@ -133,6 +141,14 @@ public:
return result;
}
uint32_t doOperationWith500msTimeout(uint32_t param)
{
using namespace std::chrono_literals;
uint32_t result;
object_.callMethod("doOperation").onInterface(INTERFACE_NAME).withTimeout(500000us).withArguments(param).storeResultsTo(result);
return result;
}
uint32_t doOperationAsync(uint32_t param)
{
uint32_t result;
@ -140,23 +156,38 @@ public:
return result;
}
uint32_t doOperationClientSideAsync(uint32_t param)
void doOperationClientSideAsync(uint32_t param)
{
object_.callMethodAsync("doOperation")
.onInterface(INTERFACE_NAME)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
});
{
this->onDoOperationReply(returnValue, error);
});
}
uint32_t doErroneousOperationClientSideAsync()
void doErroneousOperationClientSideAsync()
{
object_.callMethodAsync("throwError").onInterface(INTERFACE_NAME).uponReplyInvoke([this](const sdbus::Error* error)
{
this->onDoOperationReply(0, error);
});
object_.callMethodAsync("throwError")
.onInterface(INTERFACE_NAME)
.uponReplyInvoke([this](const sdbus::Error* error)
{
this->onDoOperationReply(0, error);
});
}
void doOperationClientSideAsyncWith500msTimeout(uint32_t param)
{
using namespace std::chrono_literals;
object_.callMethodAsync("doOperation")
.onInterface(INTERFACE_NAME)
.withTimeout(500000us)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
});
}
sdbus::Signature getSignature()
@ -173,6 +204,13 @@ public:
return result;
}
sdbus::UnixFd getUnixFd()
{
sdbus::UnixFd result;
object_.callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
ComplexType getComplex()
{
ComplexType result;
@ -209,6 +247,11 @@ public:
return object_.getProperty("state").onInterface(INTERFACE_NAME);
}
void state(const std::string& value)
{
object_.setProperty("state").onInterface(INTERFACE_NAME).toValue(value);
}
uint32_t action()
{
return object_.getProperty("action").onInterface(INTERFACE_NAME);
@ -231,7 +274,7 @@ public:
private:
sdbus::IObjectProxy& object_;
sdbus::IProxy& object_;
};

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file sdbus-c++-integration-tests.cpp
*

View File

@ -23,7 +23,7 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "perftest-proxy.h"
#include "perftests-proxy.h"
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include <string>
@ -33,15 +33,24 @@
#include <chrono>
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace std::chrono_literals;
class PerftestClient : public sdbus::ProxyInterfaces<org::sdbuscpp::perftest_proxy>
uint64_t totalDuration = 0;
class PerftestProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::perftests_proxy>
{
public:
PerftestClient(std::string destination, std::string objectPath)
: sdbus::ProxyInterfaces<org::sdbuscpp::perftest_proxy>(std::move(destination), std::move(objectPath))
PerftestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
registerProxy();
}
~PerftestProxy()
{
unregisterProxy();
}
protected:
@ -59,7 +68,9 @@ protected:
else if (counter == m_msgCount)
{
auto stopTime = std::chrono::steady_clock::now();
std::cout << "Received " << m_msgCount << " signals in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl;
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
totalDuration += duration;
std::cout << "Received " << m_msgCount << " signals in: " << duration << " ms" << std::endl;
counter = 0;
}
}
@ -91,7 +102,7 @@ int main(int /*argc*/, char */*argv*/[])
{
const char* destinationName = "org.sdbuscpp.perftests";
const char* objectPath = "/org/sdbuscpp/perftests";
PerftestClient client(destinationName, objectPath);
PerftestProxy client(destinationName, objectPath);
const unsigned int repetitions{20};
unsigned int msgCount = 1000;
@ -107,6 +118,9 @@ int main(int /*argc*/, char */*argv*/[])
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
totalDuration = 0;
msgSize = 1000;
std::cout << std::endl << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
client.m_msgCount = msgCount; client.m_msgSize = msgSize;
@ -117,6 +131,9 @@ int main(int /*argc*/, char */*argv*/[])
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
totalDuration = 0;
msgSize = 20;
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
for (unsigned int r = 0; r < repetitions; ++r)
@ -133,11 +150,16 @@ int main(int /*argc*/, char */*argv*/[])
assert(result.size() == msgSize);
}
auto stopTime = std::chrono::steady_clock::now();
std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl;
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
totalDuration += duration;
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << std::endl;
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
totalDuration = 0;
msgSize = 1000;
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
for (unsigned int r = 0; r < repetitions; ++r)
@ -154,10 +176,15 @@ int main(int /*argc*/, char */*argv*/[])
assert(result.size() == msgSize);
}
auto stopTime = std::chrono::steady_clock::now();
std::cout << "Called " << msgCount << " methods in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count() << " ms" << std::endl;
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
totalDuration += duration;
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << std::endl;
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
totalDuration = 0;
return 0;
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/sdbuscpp/perftest">
<interface name="org.sdbuscpp.perftest">
<interface name="org.sdbuscpp.perftests">
<method name="sendDataSignals">
<arg type="u" name="numberOfSignals" direction="in" />
<arg type="u" name="signalMsgSize" direction="in" />

View File

@ -0,0 +1,48 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__perftests_adaptor_h__adaptor__H__
#define __sdbuscpp__perftests_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class perftests_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.perftests";
protected:
perftests_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("sendDataSignals").onInterface(INTERFACE_NAME).withInputParamNames("numberOfSignals", "signalMsgSize").implementedAs([this](const uint32_t& numberOfSignals, const uint32_t& signalMsgSize){ return this->sendDataSignals(numberOfSignals, signalMsgSize); });
object_.registerMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withInputParamNames("string1", "string2").withOutputParamNames("result").implementedAs([this](const std::string& string1, const std::string& string2){ return this->concatenateTwoStrings(string1, string2); });
object_.registerSignal("dataSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("data");
}
~perftests_adaptor() = default;
public:
void emitDataSignal(const std::string& data)
{
object_.emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
}
private:
virtual void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) = 0;
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) = 0;
private:
sdbus::IObject& object_;
};
}} // namespaces
#endif

View File

@ -0,0 +1,51 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__perftests_proxy_h__proxy__H__
#define __sdbuscpp__perftests_proxy_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class perftests_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.perftests";
protected:
perftests_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_.uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); });
}
~perftests_proxy() = default;
virtual void onDataSignal(const std::string& data) = 0;
public:
void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize)
{
proxy_.callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
}
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2)
{
std::string result;
proxy_.callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif

View File

@ -23,42 +23,48 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "perftest-adaptor.h"
#include "perftests-adaptor.h"
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include <string>
#include <thread>
#include <chrono>
#include <algorithm>
#include <iostream>
using namespace std::chrono_literals;
std::string createRandomString(size_t length);
class PerftestServer : public sdbus::Interfaces<org::sdbuscpp::perftest_adaptor>
class PerftestAdaptor : public sdbus::AdaptorInterfaces<org::sdbuscpp::perftests_adaptor>
{
public:
PerftestServer(sdbus::IConnection& connection, std::string objectPath)
: sdbus::Interfaces<org::sdbuscpp::perftest_adaptor>(connection, std::move(objectPath))
PerftestAdaptor(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
}
~PerftestAdaptor()
{
unregisterAdaptor();
}
protected:
virtual void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) override
{
auto data = createRandomString(signalMsgSize);
char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
auto start_time = std::chrono::steady_clock::now();
for (uint32_t i = 0; i < numberOfSignals; ++i)
{
// Emit signal
dataSignal(data);
emitDataSignal(data);
}
auto stop_time = std::chrono::steady_clock::now();
std::cout << "Server sent " << numberOfSignals << " signals in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count() << " ms" << std::endl;
}
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) override
{
return string1 + string2;
@ -89,7 +95,7 @@ int main(int /*argc*/, char */*argv*/[])
auto connection = sdbus::createSystemBusConnection(serviceName);
const char* objectPath = "/org/sdbuscpp/perftests";
PerftestServer server(*connection, objectPath);
PerftestAdaptor server(*connection, objectPath);
connection->enterProcessingLoop();
}

View File

@ -18,15 +18,17 @@ namespace celsius {
class thermometer_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.celsius.thermometer";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.celsius.thermometer";
protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("getCurrentTemperature").onInterface(interfaceName).implementedAs([this](){ return this->getCurrentTemperature(); });
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
}
~thermometer_adaptor() = default;
private:
virtual uint32_t getCurrentTemperature() = 0;

View File

@ -18,24 +18,26 @@ namespace celsius {
class thermometer_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.celsius.thermometer";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.celsius.thermometer";
protected:
thermometer_proxy(sdbus::IObjectProxy& object)
: object_(object)
thermometer_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~thermometer_proxy() = default;
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
object_.callMethod("getCurrentTemperature").onInterface(interfaceName).storeResultsTo(result);
proxy_.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
private:
sdbus::IObjectProxy& object_;
sdbus::IProxy& proxy_;
};
}}}} // namespaces

View File

@ -17,20 +17,22 @@ namespace stresstests {
class concatenator_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.concatenator";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.concatenator";
protected:
concatenator_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("concatenate").onInterface(interfaceName).implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); });
object_.registerSignal("concatenatedSignal").onInterface(interfaceName).withParameters<std::string>();
object_.registerMethod("concatenate").onInterface(INTERFACE_NAME).withInputParamNames("params").withOutputParamNames("result").implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); });
object_.registerSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
}
~concatenator_adaptor() = default;
public:
void concatenatedSignal(const std::string& concatenatedString)
void emitConcatenatedSignal(const std::string& concatenatedString)
{
object_.emitSignal("concatenatedSignal").onInterface(interfaceName).withArguments(concatenatedString);
object_.emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
}
private:

View File

@ -17,15 +17,17 @@ namespace stresstests {
class concatenator_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.concatenator";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.concatenator";
protected:
concatenator_proxy(sdbus::IObjectProxy& object)
: object_(object)
concatenator_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
object_.uponSignal("concatenatedSignal").onInterface(interfaceName).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
proxy_.uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
}
~concatenator_proxy() = default;
virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0;
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) = 0;
@ -33,11 +35,11 @@ protected:
public:
void concatenate(const std::map<std::string, sdbus::Variant>& params)
{
object_.callMethodAsync("concatenate").onInterface(interfaceName).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
proxy_.callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
}
private:
sdbus::IObjectProxy& object_;
sdbus::IProxy& proxy_;
};
}}} // namespaces

View File

@ -18,15 +18,17 @@ namespace fahrenheit {
class thermometer_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.fahrenheit.thermometer";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.fahrenheit.thermometer";
protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("getCurrentTemperature").onInterface(interfaceName).implementedAs([this](){ return this->getCurrentTemperature(); });
object_.registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
}
~thermometer_adaptor() = default;
private:
virtual uint32_t getCurrentTemperature() = 0;
@ -45,16 +47,18 @@ namespace thermometer {
class factory_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.fahrenheit.thermometer.factory";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.fahrenheit.thermometer.factory";
protected:
factory_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("createDelegateObject").onInterface(interfaceName).implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); });
object_.registerMethod("destroyDelegateObject").onInterface(interfaceName).implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
object_.registerMethod("createDelegateObject").onInterface(INTERFACE_NAME).withOutputParamNames("delegate").implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); });
object_.registerMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withInputParamNames("delegate").implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
}
~factory_adaptor() = default;
private:
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) = 0;
virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0;

View File

@ -18,24 +18,26 @@ namespace fahrenheit {
class thermometer_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.fahrenheit.thermometer";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.fahrenheit.thermometer";
protected:
thermometer_proxy(sdbus::IObjectProxy& object)
: object_(object)
thermometer_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~thermometer_proxy() = default;
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
object_.callMethod("getCurrentTemperature").onInterface(interfaceName).storeResultsTo(result);
proxy_.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
private:
sdbus::IObjectProxy& object_;
sdbus::IProxy& proxy_;
};
}}}} // namespaces
@ -49,29 +51,31 @@ namespace thermometer {
class factory_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstests.fahrenheit.thermometer.factory";
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.stresstests.fahrenheit.thermometer.factory";
protected:
factory_proxy(sdbus::IObjectProxy& object)
: object_(object)
factory_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~factory_proxy() = default;
public:
sdbus::ObjectPath createDelegateObject()
{
sdbus::ObjectPath result;
object_.callMethod("createDelegateObject").onInterface(interfaceName).storeResultsTo(result);
proxy_.callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void destroyDelegateObject(const sdbus::ObjectPath& delegate)
{
object_.callMethod("destroyDelegateObject").onInterface(interfaceName).withArguments(delegate).dontExpectReply();
proxy_.callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
}
private:
sdbus::IObjectProxy& object_;
sdbus::IProxy& proxy_;
};
}}}}} // namespaces

View File

@ -0,0 +1,514 @@
/**
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file sdbus-c++-stress-tests.cpp
*
* Created on: Jan 25, 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 "celsius-thermometer-adaptor.h"
#include "celsius-thermometer-proxy.h"
#include "fahrenheit-thermometer-adaptor.h"
#include "fahrenheit-thermometer-proxy.h"
#include "concatenator-adaptor.h"
#include "concatenator-proxy.h"
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include <string>
#include <iostream>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <cassert>
#include <cstdlib>
#include <atomic>
#include <sstream>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std::chrono_literals;
using namespace std::string_literals;
#define SERVICE_1_BUS_NAME "org.sdbuscpp.stresstests.service1"s
#define SERVICE_2_BUS_NAME "org.sdbuscpp.stresstests.service2"s
#define CELSIUS_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/celsius/thermometer"s
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"s
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"s
class CelsiusThermometerAdaptor : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>
{
public:
CelsiusThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
}
~CelsiusThermometerAdaptor()
{
unregisterAdaptor();
}
protected:
virtual uint32_t getCurrentTemperature() override
{
return m_currentTemperature++;
}
private:
uint32_t m_currentTemperature{};
};
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>
{
public:
CelsiusThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
~CelsiusThermometerProxy()
{
unregisterProxy();
}
};
class FahrenheitThermometerAdaptor : public sdbus::AdaptorInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_adaptor
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >
{
public:
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath, bool isDelegate)
: AdaptorInterfaces(connection, std::move(objectPath))
, celsiusProxy_(connection, SERVICE_2_BUS_NAME, CELSIUS_THERMOMETER_OBJECT_PATH)
{
if (!isDelegate)
{
unsigned int workers = std::thread::hardware_concurrency();
if (workers < 4)
workers = 4;
for (unsigned int i = 0; i < workers; ++i)
workers_.emplace_back([this]()
{
//std::cout << "Created FTA worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
while(!exit_)
{
// Pop a work item from the queue
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this]{return !requests_.empty() || exit_;});
if (exit_)
break;
auto request = std::move(requests_.front());
requests_.pop();
lock.unlock();
// Either create or destroy a delegate object
if (request.delegateObjectPath.empty())
{
// Create new delegate object
auto& connection = getObject().getConnection();
sdbus::ObjectPath newObjectPath = FAHRENHEIT_THERMOMETER_OBJECT_PATH + "/" + std::to_string(request.objectNr);
// Here we are testing dynamic creation of a D-Bus object in an async way
auto adaptor = std::make_unique<FahrenheitThermometerAdaptor>(connection, newObjectPath, true);
std::unique_lock<std::mutex> lock{childrenMutex_};
children_.emplace(newObjectPath, std::move(adaptor));
lock.unlock();
request.result.returnResults(newObjectPath);
}
else
{
// Destroy existing delegate object
// Here we are testing dynamic removal of a D-Bus object in an async way
std::lock_guard<std::mutex> lock{childrenMutex_};
children_.erase(request.delegateObjectPath);
}
}
});
}
registerAdaptor();
}
~FahrenheitThermometerAdaptor()
{
exit_ = true;
cond_.notify_all();
for (auto& worker : workers_)
worker.join();
unregisterAdaptor();
}
protected:
virtual uint32_t getCurrentTemperature() override
{
// In this D-Bus call, make yet another D-Bus call to another service over the same connection
return static_cast<uint32_t>(celsiusProxy_.getCurrentTemperature() * 1.8 + 32.);
}
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) override
{
static size_t objectCounter{};
objectCounter++;
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{objectCounter, std::string{}, std::move(result)});
lock.unlock();
cond_.notify_one();
}
virtual void destroyDelegateObject(sdbus::Result<>&& /*result*/, sdbus::ObjectPath delegate) override
{
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{0, std::move(delegate), {}});
lock.unlock();
cond_.notify_one();
}
private:
CelsiusThermometerProxy celsiusProxy_;
std::map<std::string, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
std::mutex childrenMutex_;
struct WorkItem
{
size_t objectNr;
sdbus::ObjectPath delegateObjectPath;
sdbus::Result<sdbus::ObjectPath> result;
};
std::mutex mutex_;
std::condition_variable cond_;
std::queue<WorkItem> requests_;
std::vector<std::thread> workers_;
std::atomic<bool> exit_{};
};
class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_proxy
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >
{
public:
FahrenheitThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
~FahrenheitThermometerProxy()
{
unregisterProxy();
}
};
class ConcatenatorAdaptor : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::concatenator_adaptor>
{
public:
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
unsigned int workers = std::thread::hardware_concurrency();
if (workers < 4)
workers = 4;
for (unsigned int i = 0; i < workers; ++i)
workers_.emplace_back([this]()
{
//std::cout << "Created CA worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
while(!exit_)
{
// Pop a work item from the queue
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this]{return !requests_.empty() || exit_;});
if (exit_)
break;
auto request = std::move(requests_.front());
requests_.pop();
lock.unlock();
// Do concatenation work, return results and fire signal
auto aString = request.input.at("key1").get<std::string>();
auto aNumber = request.input.at("key2").get<uint32_t>();
auto resultString = aString + " " + std::to_string(aNumber);
request.result.returnResults(resultString);
emitConcatenatedSignal(resultString);
}
});
registerAdaptor();
}
~ConcatenatorAdaptor()
{
exit_ = true;
cond_.notify_all();
for (auto& worker : workers_)
worker.join();
unregisterAdaptor();
}
protected:
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) override
{
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{std::move(params), std::move(result)});
lock.unlock();
cond_.notify_one();
}
private:
struct WorkItem
{
std::map<std::string, sdbus::Variant> input;
sdbus::Result<std::string> result;
};
std::mutex mutex_;
std::condition_variable cond_;
std::queue<WorkItem> requests_;
std::vector<std::thread> workers_;
std::atomic<bool> exit_{};
};
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>
{
public:
ConcatenatorProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
~ConcatenatorProxy()
{
unregisterProxy();
}
private:
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) override
{
assert(error == nullptr);
std::stringstream str(result);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
str >> aNumber;
assert(aNumber > 0);
++repliesReceived_;
}
virtual void onConcatenatedSignal(const std::string& concatenatedString) override
{
std::stringstream str(concatenatedString);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
str >> aNumber;
assert(aNumber > 0);
++signalsReceived_;
}
public:
std::atomic<uint32_t> repliesReceived_{};
std::atomic<uint32_t> signalsReceived_{};
};
//-----------------------------------------
int main(int argc, char *argv[])
{
long loops;
long loopDuration;
if (argc == 1)
{
loops = 1;
loopDuration = 30000;
}
else if (argc == 3)
{
loops = std::atol(argv[1]);
loopDuration = std::atol(argv[2]);
}
else
throw std::runtime_error("Wrong program options");
std::cout << "Going on with " << loops << " loops and " << loopDuration << "ms loop duration" << std::endl;
std::atomic<uint32_t> concatenationCallsMade{0};
std::atomic<uint32_t> concatenationRepliesReceived{0};
std::atomic<uint32_t> concatenationSignalsReceived{0};
std::atomic<uint32_t> thermometerCallsMade{0};
std::atomic<bool> exitLogger{};
std::thread loggerThread([&]()
{
while (!exitLogger)
{
std::this_thread::sleep_for(1s);
std::cout << "Made " << concatenationCallsMade << " concatenation calls, received " << concatenationRepliesReceived << " replies and " << concatenationSignalsReceived << " signals so far." << std::endl;
std::cout << "Made " << thermometerCallsMade << " thermometer calls so far." << std::endl << std::endl;
}
});
for (long loop = 0; loop < loops; ++loop)
{
std::cout << "Entering loop " << loop+1 << std::endl;
auto service2Connection = sdbus::createSystemBusConnection(SERVICE_2_BUS_NAME);
std::atomic<bool> service2ThreadReady{};
std::thread service2Thread([&con = *service2Connection, &service2ThreadReady]()
{
CelsiusThermometerAdaptor thermometer(con, CELSIUS_THERMOMETER_OBJECT_PATH);
service2ThreadReady = true;
con.enterProcessingLoop();
});
auto service1Connection = sdbus::createSystemBusConnection(SERVICE_1_BUS_NAME);
std::atomic<bool> service1ThreadReady{};
std::thread service1Thread([&con = *service1Connection, &service1ThreadReady]()
{
ConcatenatorAdaptor concatenator(con, CONCATENATOR_OBJECT_PATH);
FahrenheitThermometerAdaptor thermometer(con, FAHRENHEIT_THERMOMETER_OBJECT_PATH, false);
service1ThreadReady = true;
con.enterProcessingLoop();
});
// Wait for both services to export their D-Bus objects
while (!service2ThreadReady || !service1ThreadReady)
std::this_thread::sleep_for(1ms);
auto clientConnection = sdbus::createSystemBusConnection();
std::mutex clientThreadExitMutex;
std::condition_variable clientThreadExitCond;
bool clientThreadExit{};
std::thread clientThread([&, &con = *clientConnection]()
{
std::atomic<bool> stopClients{false};
std::thread concatenatorThread([&]()
{
ConcatenatorProxy concatenator(con, SERVICE_1_BUS_NAME, CONCATENATOR_OBJECT_PATH);
uint32_t localCounter{};
// Issue async concatenate calls densely one after another
while (!stopClients)
{
std::map<std::string, sdbus::Variant> param;
param["key1"] = "sdbus-c++-stress-tests";
param["key2"] = ++localCounter;
concatenator.concatenate(param);
if ((localCounter % 10) == 0)
{
// Make sure the system is catching up with our async requests,
// otherwise sleep a bit to slow down flooding the server.
assert(localCounter >= concatenator.repliesReceived_);
while ((localCounter - concatenator.repliesReceived_) > 40 && !stopClients)
std::this_thread::sleep_for(1ms);
// Update statistics
concatenationCallsMade = localCounter;
concatenationRepliesReceived = (uint32_t)concatenator.repliesReceived_;
concatenationSignalsReceived = (uint32_t)concatenator.signalsReceived_;
}
}
});
std::thread thermometerThread([&]()
{
// Here we continuously remotely call getCurrentTemperature(). We have one proxy object,
// first we use it's factory interface to create another proxy object, call getCurrentTemperature()
// on that one, and then destroy that proxy object. All that continously in a loop.
// This tests dynamic creation and destruction of remote D-Bus objects and local object proxies.
FahrenheitThermometerProxy thermometer(con, SERVICE_1_BUS_NAME, FAHRENHEIT_THERMOMETER_OBJECT_PATH);
uint32_t localCounter{};
uint32_t previousTemperature{};
while (!stopClients)
{
localCounter++;
auto newObjectPath = thermometer.createDelegateObject();
FahrenheitThermometerProxy proxy{con, SERVICE_1_BUS_NAME, newObjectPath};
auto temperature = proxy.getCurrentTemperature();
assert(temperature >= previousTemperature); // The temperature shall rise continually
previousTemperature = temperature;
//std::this_thread::sleep_for(1ms);
if ((localCounter % 10) == 0)
thermometerCallsMade = localCounter;
thermometer.destroyDelegateObject(newObjectPath);
}
});
// We could run the loop in a sync way, but we want it to run also when proxies are destroyed for better
// coverage of multi-threaded scenarios, so we run it async and use condition variable for exit notification
//con.enterProcessingLoop();
con.enterProcessingLoopAsync();
std::unique_lock<std::mutex> lock(clientThreadExitMutex);
clientThreadExitCond.wait(lock, [&]{return clientThreadExit;});
stopClients = true;
thermometerThread.join();
concatenatorThread.join();
});
std::this_thread::sleep_for(std::chrono::milliseconds(loopDuration));
//clientConnection->leaveProcessingLoop();
std::unique_lock<std::mutex> lock(clientThreadExitMutex);
clientThreadExit = true;
lock.unlock();
clientThreadExitCond.notify_one();
clientThread.join();
service1Connection->leaveProcessingLoop();
service1Thread.join();
service2Connection->leaveProcessingLoop();
service2Thread.join();
}
exitLogger = true;
loggerThread.join();
return 0;
}

View File

@ -0,0 +1,172 @@
/**
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Connection_test.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
*
* Created on: Feb 4, 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 "Connection.h"
#include "unittests/mocks/SdBusMock.h"
#include <gtest/gtest.h>
using ::testing::_;
using ::testing::DoAll;
using ::testing::SetArgPointee;
using ::testing::Return;
using ::testing::NiceMock;
using sdbus::internal::Connection;
constexpr sdbus::internal::Connection::system_bus_t system_bus;
constexpr sdbus::internal::Connection::session_bus_t session_bus;
constexpr sdbus::internal::Connection::remote_system_bus_t remote_system_bus;
class ConnectionCreationTest : public ::testing::Test
{
protected:
ConnectionCreationTest() = default;
std::unique_ptr<NiceMock<SdBusMock>> sdBusIntfMock_ = std::make_unique<NiceMock<SdBusMock>>();
sd_bus* fakeBusPtr_ = reinterpret_cast<sd_bus*>(1);
};
using ASystemBusConnection = ConnectionCreationTest;
using ASessionBusConnection = ConnectionCreationTest;
TEST_F(ASystemBusConnection, OpensAndFlushesBusWhenCreated)
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1);
Connection(std::move(sdBusIntfMock_), system_bus);
}
TEST_F(ASessionBusConnection, OpensAndFlushesBusWhenCreated)
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus);
}
TEST_F(ASystemBusConnection, ClosesAndUnrefsBusWhenDestructed)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus);
}
TEST_F(ASessionBusConnection, ClosesAndUnrefsBusWhenDestructed)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
EXPECT_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).Times(1);
Connection(std::move(sdBusIntfMock_), session_bus);
}
TEST_F(ASystemBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1)));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), system_bus), sdbus::Error);
}
TEST_F(ASessionBusConnection, ThrowsErrorWhenOpeningTheBusFailsDuringConstruction)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(-1)));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), session_bus), sdbus::Error);
}
TEST_F(ASystemBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), system_bus), sdbus::Error);
}
TEST_F(ASessionBusConnection, ThrowsErrorWhenFlushingTheBusFailsDuringConstruction)
{
ON_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillByDefault(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(-1));
ASSERT_THROW(Connection(std::move(sdBusIntfMock_), session_bus), sdbus::Error);
}
namespace
{
template <typename _BusTypeTag>
class AConnectionNameRequest : public ::testing::Test
{
protected:
void setUpBusOpenExpectation();
std::unique_ptr<Connection> makeConnection();
void SetUp() override
{
setUpBusOpenExpectation();
ON_CALL(*sdBusIntfMock_, sd_bus_flush(_)).WillByDefault(Return(1));
ON_CALL(*sdBusIntfMock_, sd_bus_flush_close_unref(_)).WillByDefault(Return(fakeBusPtr_));
con_ = makeConnection();
}
NiceMock<SdBusMock>* sdBusIntfMock_ = new NiceMock<SdBusMock>(); // con_ below will assume ownership
sd_bus* fakeBusPtr_ = reinterpret_cast<sd_bus*>(1);
std::unique_ptr<Connection> con_;
};
template<> void AConnectionNameRequest<Connection::system_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template<> void AConnectionNameRequest<Connection::session_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_user(_)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template<> void AConnectionNameRequest<Connection::remote_system_bus_t>::setUpBusOpenExpectation()
{
EXPECT_CALL(*sdBusIntfMock_, sd_bus_open_system_remote(_, _)).WillOnce(DoAll(SetArgPointee<0>(fakeBusPtr_), Return(1)));
}
template <typename _BusTypeTag>
std::unique_ptr<Connection> AConnectionNameRequest<_BusTypeTag>::makeConnection()
{
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), _BusTypeTag{});
}
template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::remote_system_bus_t>::makeConnection()
{
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), remote_system_bus, "some host");
}
typedef ::testing::Types< Connection::system_bus_t
, Connection::session_bus_t
, Connection::remote_system_bus_t
> BusTypeTags;
TYPED_TEST_SUITE(AConnectionNameRequest, BusTypeTags);
}
TYPED_TEST(AConnectionNameRequest, DoesNotThrowOnSuccess)
{
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1));
this->con_->requestName("org.sdbuscpp.somename");
}
TYPED_TEST(AConnectionNameRequest, ThrowsOnFail)
{
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1));
ASSERT_THROW(this->con_->requestName("org.sdbuscpp.somename"), sdbus::Error);
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Message_test.cpp
*
@ -30,12 +31,13 @@
#include <cstdint>
using ::testing::Eq;
using ::testing::Gt;
using ::testing::DoubleEq;
using namespace std::string_literals;
namespace
{
std::string deserializeString(sdbus::Message& msg)
std::string deserializeString(sdbus::PlainMessage& msg)
{
std::string str;
msg >> str;
@ -49,29 +51,29 @@ namespace
TEST(AMessage, CanBeDefaultConstructed)
{
ASSERT_NO_THROW(sdbus::Message());
ASSERT_NO_THROW(sdbus::PlainMessage());
}
TEST(AMessage, IsInvalidAfterDefaultConstructed)
{
sdbus::Message msg;
sdbus::PlainMessage msg;
ASSERT_FALSE(msg.isValid());
}
TEST(AMessage, IsValidWhenConstructedAsRealMessage)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
ASSERT_TRUE(msg.isValid());
}
TEST(AMessage, CreatesShallowCopyWhenCopyConstructed)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
msg << "I am a string"s;
msg.seal();
sdbus::Message msgCopy = msg;
sdbus::PlainMessage msgCopy = msg;
std::string str;
msgCopy >> str;
@ -82,11 +84,11 @@ TEST(AMessage, CreatesShallowCopyWhenCopyConstructed)
TEST(AMessage, CreatesDeepCopyWhenEplicitlyCopied)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
msg << "I am a string"s;
msg.seal();
sdbus::Message msgCopy{sdbus::createPlainMessage()};
auto 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
@ -97,14 +99,14 @@ TEST(AMessage, CreatesDeepCopyWhenEplicitlyCopied)
TEST(AMessage, IsEmptyWhenContainsNoValue)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
ASSERT_TRUE(msg.isEmpty());
}
TEST(AMessage, IsNotEmptyWhenContainsAValue)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
msg << "I am a string"s;
ASSERT_FALSE(msg.isEmpty());
@ -112,7 +114,7 @@ TEST(AMessage, IsNotEmptyWhenContainsAValue)
TEST(AMessage, CanCarryASimpleInteger)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
int dataWritten = 5;
@ -125,9 +127,24 @@ TEST(AMessage, CanCarryASimpleInteger)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryAUnixFd)
{
auto msg = sdbus::createPlainMessage();
sdbus::UnixFd dataWritten{0};
msg << dataWritten;
msg.seal();
sdbus::UnixFd dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead.get(), Gt(dataWritten.get()));
}
TEST(AMessage, CanCarryAVariant)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
auto dataWritten = sdbus::Variant((double)3.14);
@ -142,7 +159,7 @@ TEST(AMessage, CanCarryAVariant)
TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
auto value = std::vector<sdbus::Variant>{"hello"s, (double)3.14};
auto dataWritten = sdbus::Variant{value};
@ -159,7 +176,7 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
TEST(AMessage, CanCarryAnArray)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
std::vector<int64_t> dataWritten{3545342, 43643532, 324325};
@ -174,7 +191,7 @@ TEST(AMessage, CanCarryAnArray)
TEST(AMessage, CanCarryADictionary)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
@ -189,7 +206,7 @@ TEST(AMessage, CanCarryADictionary)
TEST(AMessage, CanCarryAComplexType)
{
sdbus::Message msg{sdbus::createPlainMessage()};
auto msg = sdbus::createPlainMessage();
using ComplexType = std::map<
uint64_t,

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file TypeTraits_test.cpp
*
@ -72,6 +73,7 @@ namespace
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::UnixFd)HAS_DBUS_TYPE_SIGNATURE("h")
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
@ -91,10 +93,11 @@ namespace
>
>,
sdbus::Signature,
sdbus::UnixFd,
const char*
>
>;
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}gs)}")
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}ghs)}")
typedef ::testing::Types< bool
, uint8_t
@ -110,6 +113,7 @@ namespace
, sdbus::ObjectPath
, sdbus::Signature
, sdbus::Variant
, sdbus::UnixFd
, sdbus::Struct<bool>
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
, std::vector<int16_t>
@ -117,7 +121,7 @@ namespace
, ComplexType
> DBusSupportedTypes;
TYPED_TEST_CASE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
TYPED_TEST_SUITE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
}
/*-------------------------------------*/

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file Types_test.cpp
*
@ -28,8 +29,10 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cstdint>
#include <sys/eventfd.h>
using ::testing::Eq;
using ::testing::Gt;
using namespace std::string_literals;
namespace
@ -64,7 +67,7 @@ TEST(AVariant, CanBeConstructedFromASimpleValue)
TEST(AVariant, CanBeConstructedFromAComplexValue)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello"s, ANY_DOUBLE), sdbus::make_struct("world"s, ANY_DOUBLE)}} };
ASSERT_NO_THROW(sdbus::Variant(value));
}
@ -100,7 +103,7 @@ TEST(ASimpleVariant, ReturnsTheSimpleValueWhenAsked)
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)}} };
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello"s, ANY_DOUBLE), sdbus::make_struct("world"s, ANY_DOUBLE)}} };
sdbus::Variant variant(value);
@ -120,7 +123,7 @@ TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello"s, ANY_DOUBLE), sdbus::make_struct("world"s, ANY_DOUBLE)}} };
sdbus::Variant variant(value);
@ -152,7 +155,7 @@ TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
{
sdbus::Variant variant("a string");
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
ASSERT_NO_THROW(variant.serializeTo(msg));
}
@ -161,7 +164,7 @@ TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
{
sdbus::Variant variant;
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
ASSERT_THROW(variant.serializeTo(msg), sdbus::Error);
}
@ -169,10 +172,10 @@ TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello"s, ANY_DOUBLE), sdbus::make_struct("world"s, ANY_DOUBLE)}} };
sdbus::Variant variant(value);
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
variant.serializeTo(msg);
msg.seal();
sdbus::Variant variant2;
@ -184,12 +187,12 @@ TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
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)}} };
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello"s, ANY_DOUBLE), sdbus::make_struct("world"s, ANY_DOUBLE)}} };
sdbus::Variant variant(value);
auto variantCopy1{variant};
auto variantCopy2 = variant;
sdbus::Message msg = sdbus::createPlainMessage();
auto msg = sdbus::createPlainMessage();
variant.serializeTo(msg);
variantCopy1.serializeTo(msg);
variantCopy2.serializeTo(msg);
@ -240,3 +243,104 @@ TEST(ASignature, CanBeConstructedFromStdString)
ASSERT_THAT(sdbus::Signature{aSignature}, Eq(aSignature));
}
TEST(AUnixFd, DuplicatesAndOwnsFdUponStandardConstruction)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
EXPECT_THAT(sdbus::UnixFd{fd}.get(), Gt(fd));
EXPECT_THAT(::close(fd), Eq(0));
}
TEST(AUnixFd, AdoptsAndOwnsFdAsIsUponAdoptionConstruction)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
EXPECT_THAT(sdbus::UnixFd(fd, sdbus::adopt_fd).get(), Eq(fd));
EXPECT_THAT(::close(fd), Eq(-1));
}
TEST(AUnixFd, DuplicatesFdUponCopyConstruction)
{
sdbus::UnixFd unixFd(::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK));
sdbus::UnixFd unixFdCopy{unixFd};
EXPECT_THAT(unixFdCopy.get(), Gt(unixFd.get()));
}
TEST(AUnixFd, TakesOverFdUponMoveConstruction)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
sdbus::UnixFd unixFdNew{std::move(unixFd)};
EXPECT_FALSE(unixFd.isValid());
EXPECT_THAT(unixFdNew.get(), Eq(fd));
}
TEST(AUnixFd, ClosesFdProperlyUponDestruction)
{
int fd, fdCopy;
{
fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
auto unixFdNew = std::move(unixFd);
auto unixFdCopy = unixFdNew;
fdCopy = unixFdCopy.get();
}
EXPECT_THAT(::close(fd), Eq(-1));
EXPECT_THAT(::close(fdCopy), Eq(-1));
}
TEST(AUnixFd, DoesNotCloseReleasedFd)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
int fdReleased;
{
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
fdReleased = unixFd.release();
EXPECT_FALSE(unixFd.isValid());
}
EXPECT_THAT(fd, Eq(fdReleased));
EXPECT_THAT(::close(fd), Eq(0));
}
TEST(AUnixFd, ClosesFdOnReset)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
unixFd.reset();
EXPECT_FALSE(unixFd.isValid());
EXPECT_THAT(::close(fd), Eq(-1));
}
TEST(AUnixFd, DuplicatesNewFdAndClosesOriginalFdOnReset)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
auto newFd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
unixFd.reset(newFd);
EXPECT_THAT(unixFd.get(), Gt(newFd));
EXPECT_THAT(::close(fd), Eq(-1));
EXPECT_THAT(::close(newFd), Eq(0));
}
TEST(AUnixFd, TakesOverNewFdAndClosesOriginalFdOnAdoptingReset)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
auto newFd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
unixFd.reset(newFd, sdbus::adopt_fd);
EXPECT_THAT(unixFd.get(), Eq(newFd));
EXPECT_THAT(::close(fd), Eq(-1));
}

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file SdBusMock.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@ -41,16 +42,29 @@ public:
MOCK_METHOD5(sd_bus_call, int(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply));
MOCK_METHOD6(sd_bus_call_async, int(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec));
MOCK_METHOD3(sd_bus_message_new, int(sd_bus *bus, sd_bus_message **m, uint8_t type));
MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member));
MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member));
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **m));
MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e));
MOCK_METHOD2(sd_bus_set_method_call_timeout, int(sd_bus *bus, uint64_t usec));
MOCK_METHOD2(sd_bus_get_method_call_timeout, int(sd_bus *bus, uint64_t *ret));
MOCK_METHOD4(sd_bus_emit_properties_changed_strv, int(sd_bus *bus, const char *path, const char *interface, char **names));
MOCK_METHOD2(sd_bus_emit_object_added, int(sd_bus *bus, const char *path));
MOCK_METHOD2(sd_bus_emit_object_removed, int(sd_bus *bus, const char *path));
MOCK_METHOD3(sd_bus_emit_interfaces_added_strv, int(sd_bus *bus, const char *path, char **interfaces));
MOCK_METHOD3(sd_bus_emit_interfaces_removed_strv, int(sd_bus *bus, const char *path, char **interfaces));
MOCK_METHOD1(sd_bus_open_user, int(sd_bus **ret));
MOCK_METHOD1(sd_bus_open_system, int(sd_bus **ret));
MOCK_METHOD2(sd_bus_open_system_remote, int(sd_bus **ret, const char *host));
MOCK_METHOD3(sd_bus_request_name, int(sd_bus *bus, const char *name, uint64_t flags));
MOCK_METHOD2(sd_bus_release_name, int(sd_bus *bus, const char *name));
MOCK_METHOD2(sd_bus_get_unique_name, int(sd_bus *bus, const char **name));
MOCK_METHOD6(sd_bus_add_object_vtable, int(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata));
MOCK_METHOD3(sd_bus_add_object_manager, int(sd_bus *bus, sd_bus_slot **slot, const char *path));
MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata));
MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot));

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file sdbus-c++-unit-tests.cpp
*

View File

@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.5)
project(sdbus-c++-xml2cpp)
project(sdbus-c++-tools)
include(GNUInstallDirs)
@ -19,17 +19,17 @@ find_package(EXPAT REQUIRED)
#-------------------------------
set(SDBUSCPP_XML2CPP_SRCS
xml2cpp.cpp
xml.h
xml.cpp
generator_utils.h
generator_utils.cpp
BaseGenerator.h
BaseGenerator.cpp
AdaptorGenerator.h
AdaptorGenerator.cpp
ProxyGenerator.h
ProxyGenerator.cpp)
xml2cpp-codegen/xml2cpp.cpp
xml2cpp-codegen/xml.h
xml2cpp-codegen/xml.cpp
xml2cpp-codegen/generator_utils.h
xml2cpp-codegen/generator_utils.cpp
xml2cpp-codegen/BaseGenerator.h
xml2cpp-codegen/BaseGenerator.cpp
xml2cpp-codegen/AdaptorGenerator.h
xml2cpp-codegen/AdaptorGenerator.cpp
xml2cpp-codegen/ProxyGenerator.h
xml2cpp-codegen/ProxyGenerator.cpp)
#-------------------------------
# GENERAL COMPILER CONFIGURATION
@ -41,12 +41,11 @@ set(CMAKE_CXX_STANDARD 14)
# EXECUTABLE BUILD INFORMATION
#----------------------------------
add_executable(${PROJECT_NAME} ${SDBUSCPP_XML2CPP_SRCS})
target_link_libraries (${PROJECT_NAME} ${EXPAT_LIBRARIES})
add_executable(sdbus-c++-xml2cpp ${SDBUSCPP_XML2CPP_SRCS})
target_link_libraries (sdbus-c++-xml2cpp ${EXPAT_LIBRARIES})
#----------------------------------
# INSTALLATION
#----------------------------------
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
install(TARGETS sdbus-c++-xml2cpp EXPORT sdbus-c++-xml2cpp DESTINATION ${CMAKE_INSTALL_BINDIR})

504
tools/COPYING Normal file
View File

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library 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.
This library 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 this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -1,5 +1,6 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
*
* @file AdaptorGenerator.cpp
*
@ -33,6 +34,7 @@
#include <cstdlib>
#include <algorithm>
#include <iterator>
#include <cctype>
using std::endl;
@ -80,7 +82,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
body << "class " << className << endl
<< "{" << endl
<< "public:" << endl
<< tab << "static constexpr const char* interfaceName = \"" << ifaceName << "\";" << endl << endl
<< tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl
<< tab << className << "(sdbus::IObject& object)" << endl
<< tab << tab << ": object_(object)" << endl;
@ -109,7 +111,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
if(!annotationRegistration.empty())
{
std::stringstream str;
str << tab << tab << "object_.setInterfaceFlags(interfaceName)" << annotationRegistration << ";" << endl;
str << tab << tab << "object_.setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl;
annotationRegistration = str.str();
}
@ -129,6 +131,8 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
<< propertyRegistration
<< tab << "}" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
if (!signalMethods.empty())
{
body << "public:" << endl << signalMethods;
@ -189,7 +193,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
if (annotationValue == "true")
annotationRegistration += ".markAsPrivileged()";
}
else
else if (annotationName != "org.freedesktop.DBus.Method.Timeout") // Whatever else...
{
std::cerr << "Node: " << methodName << ": "
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
@ -200,14 +204,17 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
Nodes inArgs = args.select("direction" , "in");
Nodes outArgs = args.select("direction" , "out");
std::string argStr, argTypeStr;
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs, async);
std::string argStr, argTypeStr, argStringsStr, outArgStringsStr;
std::tie(argStr, argTypeStr, std::ignore, argStringsStr) = argsToNamesAndTypes(inArgs, async);
std::tie(std::ignore, std::ignore, std::ignore, outArgStringsStr) = argsToNamesAndTypes(outArgs);
using namespace std::string_literals;
registrationSS << tab << tab << "object_.registerMethod(\""
<< methodName << "\")"
<< ".onInterface(interfaceName)"
<< ".onInterface(INTERFACE_NAME)"
<< (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
<< (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "")
<< ".implementedAs("
<< "[this]("
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
@ -255,25 +262,28 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
Nodes args = (*signal)["arg"];
std::string argStr, argTypeStr, typeStr;;
std::tie(argStr, argTypeStr, typeStr) = argsToNamesAndTypes(args);
std::string argStr, argTypeStr, typeStr, argStringsStr;
std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
signalRegistrationSS << tab << tab
<< "object_.registerSignal(\"" << name << "\")"
".onInterface(interfaceName)";
".onInterface(INTERFACE_NAME)";
if (args.size() > 0)
{
signalRegistrationSS << ".withParameters<" << typeStr << ">()";
signalRegistrationSS << ".withParameters<" << typeStr << ">(" << argStringsStr << ")";
}
signalRegistrationSS << annotationRegistration;
signalRegistrationSS << ";" << endl;
signalMethodSS << tab << "void " << name << "(" << argTypeStr << ")" << endl
auto nameWithCapFirstLetter = name;
nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]);
signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl
<< tab << "{" << endl
<< tab << tab << "object_.emitSignal(\"" << name << "\")"
".onInterface(interfaceName)";
".onInterface(INTERFACE_NAME)";
if (!argStr.empty())
{
@ -322,7 +332,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
registrationSS << tab << tab << "object_.registerProperty(\""
<< propertyName << "\")"
<< ".onInterface(interfaceName)";
<< ".onInterface(INTERFACE_NAME)";
if (propertyAccess == "read" || propertyAccess == "readwrite")
{

Some files were not shown because too many files have changed in this diff Show More