Compare commits

..
1 Commits
Author SHA1 Message Date
Stanislav Angelovič e75337bd23 test 2024-01-08 08:00:39 +01:00
92 changed files with 4174 additions and 5979 deletions
+12 -33
View File
@@ -4,9 +4,7 @@ on:
push:
branches: [ master ]
pull_request:
branches:
- master
- release/v2.0
branches: [ master ]
jobs:
build:
@@ -15,18 +13,12 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-22.04]
compiler: [g++]
compiler: [g++, clang]
build: [shared-libsystemd]
include:
- os: ubuntu-22.04
compiler: clang
build: shared-libsystemd
- os: ubuntu-22.04
- os: ubuntu-20.04
compiler: g++
build: embedded-static-libsystemd
- os: ubuntu-22.04
compiler: clang
build: embedded-static-libsystemd
steps:
- uses: actions/checkout@v3
- name: install-libsystemd-toolchain
@@ -47,41 +39,28 @@ jobs:
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
sudo update-alternatives --remove-all c++
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
# We need to use libc++ with Clang because there is a bug in libstdc++ chrono headers that Clang has issues with
echo "SDBUSCPP_EXTRA_CXX_FLAGS=-stdlib=libc++" >> $GITHUB_ENV
# We don't install googletest but we let it be built within sdbus-c++ builds below, since it needs to be built against libc++ for Clang jobs to pass
# - name: install-googletest
# if: matrix.os == 'ubuntu-22.04'
# run: |
# sudo apt-get install -y libgmock-dev
# - name: install-googletest
# if: matrix.os == 'ubuntu-20.04' # On older ubuntus the libgmock-dev package is either unavailable or has faulty pkg-config file, so we build & install manually
# run: |
# git clone https://github.com/google/googletest.git
# cd googletest
# mkdir build
# cd build
# cmake .. -DCMAKE_CXX_FLAGS="$SDBUSCPP_EXTRA_CXX_FLAGS"
# cmake --build . -j4
# sudo cmake --build . --target install
- name: install-googletest
if: matrix.os == 'ubuntu-22.04' # On older ubuntus the libgmock-dev package is either unavailable or has faulty pkg-config file
run: |
sudo apt-get install -y libgmock-dev
- name: configure-debug
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON ..
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
- name: configure-release
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O3 -DNDEBUG -W -Wextra -Wall -Wnon-virtual-dtor -Werror" -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON ..
- name: configure-with-embedded-libsystemd
if: matrix.build == 'embedded-static-libsystemd'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="$SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_BUILD_LIBSYSTEMD=ON -DSDBUSCPP_LIBSYSTEMD_VERSION=252 -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_VERBOSE_MAKEFILE=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON -DBUILD_CODE_GEN=ON -DBUILD_LIBSYSTEMD=ON -DLIBSYSTEMD_VERSION=244 ..
- name: make
run: |
cd build
@@ -97,7 +76,7 @@ jobs:
cd build
cpack -G DEB
- name: 'Upload Artifact'
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04' && matrix.compiler == 'g++'
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04' && matrix.compiler == 'g++'
uses: actions/upload-artifact@v3
with:
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"
@@ -118,7 +97,7 @@ jobs:
prepare: |
pkg install -y cmake ninja pkgconf basu expat googletest
run: |
cmake -B _build -G Ninja -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON
cmake -B _build -G Ninja -DBUILD_CODE_GEN=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON
cmake --build _build
cmake --install _build
pkg install -y dbus
+91 -154
View File
@@ -2,128 +2,55 @@
# PROJECT INFORMATION
#-------------------------------
cmake_minimum_required(VERSION 3.14)
cmake_minimum_required(VERSION 3.13)
project(sdbus-c++ VERSION 2.0.0 LANGUAGES CXX C)
project(sdbus-c++ VERSION 1.4.0 LANGUAGES C CXX)
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
# -------------------------------
# CONFIGURATION OPTIONS
# -------------------------------
option(SDBUSCPP_BUILD_LIBSYSTEMD "Fetch & build libsystemd static library and make it part of libsdbus-c++, instead of searching for it in the system" OFF)
if(NOT SDBUSCPP_BUILD_LIBSYSTEMD)
set(SDBUSCPP_SDBUS_LIB "default" CACHE STRING "sd-bus implementation library to search for and use (default, systemd, elogind, or basu)")
set_property(CACHE SDBUSCPP_SDBUS_LIB PROPERTY STRINGS default systemd elogind basu)
else()
set(SDBUSCPP_LIBSYSTEMD_VERSION "242" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
set(SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS "" CACHE STRING "Additional configuration options to be passed as-is to libsystemd build system")
endif()
option(SDBUSCPP_INSTALL "Enable installation of sdbus-c++ (downstream projects embedding sdbus-c++ may want to turn this OFF)" ON)
option(SDBUSCPP_BUILD_TESTS "Build tests" OFF)
if (SDBUSCPP_BUILD_TESTS)
option(SDBUSCPP_BUILD_PERF_TESTS "Build also sdbus-c++ performance tests" OFF)
option(SDBUSCPP_BUILD_STRESS_TESTS "Build also sdbus-c++ stress tests" OFF)
set(SDBUSCPP_TESTS_INSTALL_PATH "tests/${PROJECT_NAME}" CACHE STRING "Specifies where the test binaries will be installed")
set(SDBUSCPP_GOOGLETEST_VERSION 1.10.0 CACHE STRING "Version of gmock library to use")
set(SDBUSCPP_GOOGLETEST_GIT_REPO "https://github.com/google/googletest.git" CACHE STRING "A git repo to clone and build googletest from if gmock is not found in the system")
endif()
option(SDBUSCPP_BUILD_CODEGEN "Build generator tool for C++ native bindings" OFF)
option(SDBUSCPP_BUILD_EXAMPLES "Build example programs" OFF)
option(SDBUSCPP_BUILD_DOCS "Build documentation for sdbus-c++" ON)
if(SDBUSCPP_BUILD_DOCS)
option(SDBUSCPP_BUILD_DOXYGEN_DOCS "Build doxygen documentation for sdbus-c++ API" OFF)
endif()
#option(SDBUSCPP_CLANG_TIDY "Co-compile with clang-tidy static analyzer" OFF)
#option(SDBUSCPP_COVERAGE "Build sdbus-c++ with code coverage instrumentation" OFF)
# We promote the BUILD_SHARED_LIBS flag to a (global) option only if we are the main project
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
endif()
message(STATUS "")
message(STATUS " *******************************************")
message(STATUS " * SDBUS-C++ CONFIGURATION *")
message(STATUS " *******************************************")
message(STATUS "")
message(STATUS " SDBUSCPP_BUILD_LIBSYSTEMD: ${SDBUSCPP_BUILD_LIBSYSTEMD}")
if(SDBUSCPP_BUILD_LIBSYSTEMD)
message(STATUS " SDBUSCPP_LIBSYSTEMD_VERSION: ${SDBUSCPP_LIBSYSTEMD_VERSION}")
message(STATUS " SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS: ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}")
endif()
message(STATUS " SDBUSCPP_INSTALL: ${SDBUSCPP_INSTALL}")
message(STATUS " SDBUSCPP_BUILD_TESTS: ${SDBUSCPP_BUILD_TESTS}")
if(SDBUSCPP_BUILD_TESTS)
message(STATUS " SDBUSCPP_BUILD_PERF_TESTS: ${SDBUSCPP_BUILD_PERF_TESTS}")
message(STATUS " SDBUSCPP_BUILD_STRESS_TESTS: ${SDBUSCPP_BUILD_STRESS_TESTS}")
message(STATUS " SDBUSCPP_TESTS_INSTALL_PATH: ${SDBUSCPP_TESTS_INSTALL_PATH}")
message(STATUS " SDBUSCPP_GOOGLETEST_VERSION: ${SDBUSCPP_GOOGLETEST_VERSION}")
message(STATUS " SDBUSCPP_GOOGLETEST_GIT_REPO: ${SDBUSCPP_GOOGLETEST_GIT_REPO}")
endif()
message(STATUS " SDBUSCPP_BUILD_CODEGEN: ${SDBUSCPP_BUILD_CODEGEN}")
message(STATUS " SDBUSCPP_BUILD_EXAMPLES: ${SDBUSCPP_BUILD_EXAMPLES}")
message(STATUS " SDBUSCPP_BUILD_DOCS: ${SDBUSCPP_BUILD_DOCS}")
if(SDBUSCPP_BUILD_DOCS)
message(STATUS " SDBUSCPP_BUILD_DOXYGEN_DOCS: ${SDBUSCPP_BUILD_DOXYGEN_DOCS}")
endif()
message(STATUS " BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}")
message(STATUS "")
#-------------------------------
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES
#-------------------------------
if(SDBUSCPP_BUILD_LIBSYSTEMD)
# Build static libsystemd library as an external project
include(cmake/LibsystemdExternalProject.cmake)
set(SDBUS_IMPL "systemd")
set(SDBUS_LIB "libsystemd")
else()
# Search for sd-bus implementations in the system as per user's configuration
set(SDBUS_LIBS ${SDBUSCPP_SDBUS_LIB})
if(SDBUSCPP_SDBUS_LIB STREQUAL "default")
set(SDBUS_LIBS systemd elogind basu) # This is the default search order
endif()
set(LIBSYSTEMD_IMPL "systemd")
set(LIBSYSTEMD_LIB "libsystemd")
option(BUILD_LIBSYSTEMD "Build libsystemd static library and incorporate it into libsdbus-c++" OFF)
if(NOT BUILD_LIBSYSTEMD)
find_package(PkgConfig REQUIRED)
foreach(LIB ${SDBUS_LIBS})
if(LIB STREQUAL "systemd")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=238)
if(TARGET PkgConfig::Systemd)
set(SDBUS_IMPL "systemd")
set(SDBUS_LIB "libsystemd")
break()
endif()
elseif(LIB STREQUAL "elogind")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=238)
if(TARGET PkgConfig::Systemd)
set(SDBUS_IMPL "elogind")
set(SDBUS_LIB "libelogind")
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
list(GET VERSION_LIST 0 Systemd_VERSION)
break()
endif()
elseif(LIB STREQUAL "basu")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL basu)
if(TARGET PkgConfig::Systemd)
set(SDBUS_IMPL "basu")
set(SDBUS_LIB "basu")
set(Systemd_VERSION "240") # https://git.sr.ht/~emersion/basu/commit/d4d185d29a26
break()
endif()
else()
message(FATAL_ERROR "Unrecognized sd-bus implementation library ${LIB}")
endif()
endforeach()
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236)
if(NOT TARGET PkgConfig::Systemd)
message(FATAL_ERROR "None of ${SDBUS_LIBS} libraries implementing sd-bus was found (Are their dev packages installed?)")
message(WARNING "libsystemd not found, checking for libelogind instead")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=236)
if(TARGET PkgConfig::Systemd)
set(LIBSYSTEMD_IMPL "elogind")
set(LIBSYSTEMD_LIB "libelogind")
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
list(GET VERSION_LIST 0 Systemd_VERSION)
else()
message(WARNING "libelogind not found, checking for basu instead")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL basu)
set(LIBSYSTEMD_IMPL "basu")
set(LIBSYSTEMD_LIB "basu")
# https://git.sr.ht/~emersion/basu/commit/d4d185d29a26
set(Systemd_VERSION "240")
endif()
endif()
if(NOT TARGET PkgConfig::Systemd)
message(FATAL_ERROR "libsystemd of version at least 236 is required, but was not found "
"(if you have systemd in your OS, you may want to install package containing pkgconfig "
" files for libsystemd library. On Ubuntu, that is libsystemd-dev. "
" Alternatively, you may turn BUILD_LIBSYSTEMD on for sdbus-c++ to download, build "
"and incorporate libsystemd as embedded library within sdbus-c++)")
endif()
add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd)
string(REGEX MATCHALL "([0-9]+)" SYSTEMD_VERSION_LIST "${Systemd_VERSION}")
list(GET SYSTEMD_VERSION_LIST 0 SDBUSCPP_LIBSYSTEMD_VERSION)
message(STATUS "Building with libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
list(GET SYSTEMD_VERSION_LIST 0 LIBSYSTEMD_VERSION)
message(STATUS "Building with libsystemd v${LIBSYSTEMD_VERSION}")
else()
# Build static libsystemd library as an external project
include(cmake/LibsystemdExternalProject.cmake)
endif()
find_package(Threads REQUIRED)
@@ -162,8 +89,6 @@ set(SDBUSCPP_HDR_SRCS
set(SDBUSCPP_PUBLIC_HDRS
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.h
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.inl
${SDBUSCPP_INCLUDE_DIR}/VTableItems.h
${SDBUSCPP_INCLUDE_DIR}/VTableItems.inl
${SDBUSCPP_INCLUDE_DIR}/Error.h
${SDBUSCPP_INCLUDE_DIR}/IConnection.h
${SDBUSCPP_INCLUDE_DIR}/AdaptorInterfaces.h
@@ -184,7 +109,7 @@ set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HD
# GENERAL COMPILER CONFIGURATION
#-------------------------------
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
#----------------------------------
# LIBRARY BUILD INFORMATION
@@ -193,19 +118,24 @@ set(CMAKE_CXX_STANDARD 20)
set(SDBUSCPP_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(SDBUSCPP_VERSION "${PROJECT_VERSION}")
# We promote the BUILD_SHARED_LIBS flag to a (global) option only if we are the main project
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
endif()
# Having an object target allows unit tests to reuse already built sources without re-building
add_library(sdbus-c++-objlib OBJECT ${SDBUSCPP_SRCS})
target_compile_definitions(sdbus-c++-objlib PRIVATE
BUILD_LIB=1
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
SDBUS_${SDBUS_IMPL}
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
SDBUS_${LIBSYSTEMD_IMPL}
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
if(BUILD_SHARED_LIBS)
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
if(SDBUSCPP_BUILD_LIBSYSTEMD)
if(BUILD_LIBSYSTEMD)
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
endif()
target_link_libraries(sdbus-c++-objlib
@@ -223,11 +153,28 @@ set_target_properties(sdbus-c++
OUTPUT_NAME "sdbus-c++")
target_link_libraries(sdbus-c++ PRIVATE sdbus-c++-objlib)
#----------------------------------
# INSTALLATION
#----------------------------------
set(EXPORT_SET sdbus-c++)
if(NOT BUILD_SHARED_LIBS)
list(APPEND EXPORT_SET "sdbus-c++-objlib")
endif()
install(TARGETS ${EXPORT_SET}
EXPORT sdbus-c++-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT runtime NAMELINK_COMPONENT dev
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT dev)
#----------------------------------
# TESTS
#----------------------------------
if(SDBUSCPP_BUILD_TESTS)
option(BUILD_TESTS "Build tests (default OFF)" OFF)
if(BUILD_TESTS)
message(STATUS "Building with tests")
enable_testing()
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
@@ -237,7 +184,9 @@ endif()
# UTILS
#----------------------------------
if(SDBUSCPP_BUILD_CODEGEN)
option(BUILD_CODE_GEN "Build and install interface stub code generator (default OFF)" OFF)
if(BUILD_CODE_GEN)
message(STATUS "Building with code generator tool")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools")
endif()
@@ -246,7 +195,9 @@ endif()
# EXAMPLES
#----------------------------------
if(SDBUSCPP_BUILD_EXAMPLES)
option(BUILD_EXAMPLES "Build example programs (default OFF)" OFF)
if(BUILD_EXAMPLES)
message(STATUS "Building with examples")
add_subdirectory(examples)
endif()
@@ -255,9 +206,13 @@ endif()
# DOCUMENTATION
#----------------------------------
if(SDBUSCPP_BUILD_DOCS)
option(BUILD_DOC "Build documentation for sdbus-c++" ON)
if(BUILD_DOC)
message(STATUS "Building with documentation")
option(BUILD_DOXYGEN_DOC "Build doxygen documentation for sdbus-c++ API" OFF)
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/docs")
install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc)
endif()
#----------------------------------
@@ -266,58 +221,40 @@ endif()
include(CMakePackageConfigHelpers)
install(EXPORT sdbus-c++-targets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
NAMESPACE SDBusCpp::
COMPONENT dev)
configure_package_config_file(cmake/sdbus-c++-config.cmake.in cmake/sdbus-c++-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++)
write_basic_package_version_file(cmake/sdbus-c++-config-version.cmake COMPATIBILITY SameMajorVersion)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
COMPONENT dev)
if(BUILD_SHARED_LIBS AND (SDBUSCPP_BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
if(BUILD_SHARED_LIBS AND (BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
set(PKGCONFIG_REQS ".private")
else()
set(PKGCONFIG_REQS "")
endif()
set(PKGCONFIG_DEPS ${SDBUS_LIB})
set(PKGCONFIG_DEPS ${LIBSYSTEMD_LIB})
configure_file(pkgconfig/sdbus-c++.pc.in pkgconfig/sdbus-c++.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
#----------------------------------
# INSTALLATION
# CPack
#----------------------------------
if(SDBUSCPP_INSTALL)
set(EXPORT_SET sdbus-c++)
if(NOT BUILD_SHARED_LIBS)
list(APPEND EXPORT_SET "sdbus-c++-objlib")
endif()
install(TARGETS ${EXPORT_SET}
EXPORT sdbus-c++-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sdbus-c++-runtime NAMELINK_COMPONENT sdbus-c++-dev
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT sdbus-c++-dev
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDBUSCPP_INCLUDE_SUBDIR} COMPONENT sdbus-c++-dev)
install(EXPORT sdbus-c++-targets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
NAMESPACE SDBusCpp::
COMPONENT sdbus-c++-dev)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++
COMPONENT sdbus-c++-dev)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT sdbus-c++-dev)
if(SDBUSCPP_BUILD_DOCS)
install(FILES README README.md NEWS COPYING ChangeLog AUTHORS DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
endif()
endif()
#----------------------------------
# CPACK
#----------------------------------
set(CPACK_PACKAGE_VENDOR "Kistler")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High-level C++ D-Bus library")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "high-level C++ D-Bus library")
set(CPACK_PACKAGE_CONTACT "info@kistler.com")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_COMPONENTS_ALL runtime dev doc)
set(CPACK_COMPONENT_DEV_DEPENDS "runtime")
# specific for DEB generator
set(CPACK_DEB_COMPONENT_INSTALL ON)
set(CPACK_DEBIAN_RUNTIME_DEBUGINFO_PACKAGE ON)
-52
View File
@@ -239,55 +239,3 @@ v1.4.0
- Add support for direct, peer-to-peer connections
- Add option to create IConnection directly from an underlying sd_bus instance
- Some additional fixes
v1.5.0
- Improve handling of exceptions from callback handlers
- Add support for async registration of matches
- Correctly add libsystemd dependency to pkgconfi
- Fix request name signal handling issue
- Add INSTALL_TESTS CMake option
- Minor UnixFd cleanups
- Additional little fixes and updates in code, build system, CI, and documentation
v1.6.0
- Add support for enums in D-Bus serialization and signatures
- Add support for std::variant as an alternative C++ type for D-Bus Variant
- Add support for implicit conversions between std::variant and sdbus::Variant
- Fix missing includes
v2.0.0
- A new major version with revamped API, bringing numerous new features, simplifications, fixes and breaking changes improving not only API consistency, safety and expressiveness, but also performance
- Add section 'Migrating to sdbus-c++ v2' to the 'Using sdbus-c++' document providing the complete list of breaking API/ABI/behavior changes and migration notes
- Switch to C++20 standard (but the API is backwards-compatible with C++17, skipping C++20 features in such a case)
- Add strong types to public API
- Add support for string_view as a D-Bus type representation
- Enable compile-time D-Bus signature generation
- Redesign Object vtable registration API
- Improve Proxy signal subscription API
- Refactor object manager API for consistency
- Introduce native sd-event integration
- Let callbacks take message objects by value
- Simplify async D-Bus connection handling
- Simplify async call state flag
- Make Variant constructor explicit
- Use optional for passing potential call errors
- Rename and re-organize CMake options
- Rename connection creation methods
- Have all async function names finish with *Async for consistency
- Implement more flexible searching for sd-bus libs
- Add new `SDBUSCPP_SDBUS_LIB` CMake configuration variable determining which sd-bus library shall be picked
- Make Variant conversion operator explicit
- Use sd-bus API to get the current message
- Use sd_bus_match_signal() for signal registration
- Provide also const char* overloads for convenience functions
- Disable move in generated adaptor and proxy classes
- Improve and make more efficient some Message API
- Add Slot-returning overloads of async method calls
- Remove floating_slot_t tag and use return_slot_t instead
- Add [[nodiscard]] to many relevant API functions
- Add proper fix for timeout handling
- Fix for external event loops in which the event loop thread ID was not correctly initialized (now fixed and simplified by not needing the thread ID anymore)
- Remove deprecated stuff
- Add `SDBUSCPP_` prefix to CMake configuration variables to avoid conflicts with downstream projects
- Require systemd of at least v238
- Many other fixes and updates in code, tests, build system, CI, and documentation
+18 -24
View File
@@ -26,11 +26,11 @@ $ sudo cmake --build . --target install
### CMake configuration flags for sdbus-c++
* `SDBUSCPP_BUILD_CODEGEN` [boolean]
* `BUILD_CODE_GEN` [boolean]
Option for building the stub code generator `sdbus-c++-xml2cpp` for generating the adaptor and proxy interfaces out of the D-Bus IDL XML description. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_CODEGEN=ON` flag to turn on building the code gen.
Option for building the stub code generator `sdbus-c++-xml2cpp` for generating the adaptor and proxy interfaces out of the D-Bus IDL XML description. Default value: `OFF`. Use `-DBUILD_CODE_GEN=ON` flag to turn on building the code gen.
* `SDBUSCPP_BUILD_DOCS` [boolean]
* `BUILD_DOC` [boolean]
Option for including sdbus-c++ documentation files and tutorials. Default value: `ON`. With this option turned on, you may also enable/disable the following option:
@@ -38,41 +38,35 @@ $ sudo cmake --build . --target install
Option for building Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `cmake --build . --target doc`. Default value: `OFF`. Use `-DBUILD_DOXYGEN_DOC=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
* `SDBUSCPP_BUILD_TESTS` [boolean]
* `BUILD_TESTS` [boolean]
Option for building sdbus-c++ unit and integration tests, invokable by `cmake --build . --target test` (Note: before invoking `cmake --build . --target test`, make sure you copy `tests/integrationtests/files/org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory). That incorporates downloading and building static libraries of Google Test. Default value: `OFF`. Use `-DBUILD_TESTS=ON` to enable building the tests. With this option turned on, you may also enable/disable the following options:
* `SDBUSCPP_BUILD_PERF_TESTS` [boolean]
* `ENABLE_PERF_TESTS` [boolean]
Option for building sdbus-c++ performance tests. Default value: `OFF`.
* `SDBUSCPP_BUILD_STRESS_TESTS` [boolean]
* `ENABLE_STRESS_TESTS` [boolean]
Option for building sdbus-c++ stress tests. Default value: `OFF`.
* `SDBUSCPP_TESTS_INSTALL_PATH` [string]
* `INSTALL_TESTS` [boolean]
Option for installing tests that were built. Default value: `OFF`.
* `TESTS_INSTALL_PATH` [string]
Path where the test binaries shall get installed. Default value: `${CMAKE_INSTALL_PREFIX}/tests/sdbus-c++` (previously: `/opt/test/bin`).
* `SDBUSCPP_BUILD_LIBSYSTEMD` [boolean]
* `BUILD_LIBSYSTEMD` [boolean]
Option for building libsystemd as a sd-bus implementation when sdbus-c++ is built, and making libsystemd an integral part of sdbus-c++ library. Default value: `OFF`, which means that the sd-bus implementation library (`libsystemd`, `libelogind`, or `basu`) will be searched via `pkg-config` in the system.
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:
This option may be very helpful in environments where sd-bus implementation library is unavailable (see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information).
With this option turned off, you may provide the following additional configuration flag:
* `SDBUSCPP_SDBUS_LIB` [string]
Defines which sd-bus implementation library to search for and use. Allowed values: `default`, `systemd`, `elogind`, `basu`. Default value: `default`, which means that sdbus-c++ will try to find any of `systemd`, `elogind`, `basu` in the order as listed here.
With this option turned on, you may provide the following additional configuration flag:
* `SDBUSCPP_LIBSYSTEMD_VERSION` [string]
* `LIBSYSTEMD_VERSION` [string]
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`.
* `SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
* `LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
Additional options to be passed as-is to the libsystemd build system (meson for systemd v242) in its configure step. Can be used for passing e.g. toolchain file path in case of cross builds. Default value: empty.
@@ -84,7 +78,7 @@ $ sudo cmake --build . --target install
This is a global CMake flag, promoted in sdbus-c++ project to a CMake option. Use this to control whether sdbus-c++ is built as either a shared or static library. Default value: `ON`.
* `SDBUSCPP_BUILD_EXAMPLES` [boolean]
* `BUILD_EXAMPLES` [boolean]
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`
@@ -92,10 +86,10 @@ Dependencies
------------
* `C++17` - the library uses C++17 features.
* `libsystemd`/`libelogind`/`basu` - libraries containing sd-bus implementation that sdbus-c++ is written around. In case of `libsystemd` and `libelogind`, version >= 236 is needed. (In case you have you're missing any of those sd-bus implementations, don't worry, see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information.)
* `libsystemd`/`libelogind`/`basu` - libraries containing sd-bus implementation that sdbus-c++ is written around. In case of `libsystemd` and `libelogind`, version >= 236 is needed. (In case you have a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
* `expat` - necessary when building the xml2cpp binding code generator (`SDBUSCPP_BUILD_CODEGEN` option is `ON`).
* `expat` - necessary when building xml2cpp code generator (`BUILD_CODE_GEN` option is ON).
Licensing
---------
+9 -6
View File
@@ -18,6 +18,9 @@ if (NOT CAP_FOUND)
find_library(CAP_LIBRARIES cap) # Compat with Ubuntu 14.04 which ships libcap w/o .pc file
endif()
set(LIBSYSTEMD_VERSION "242" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
set(LIBSYSTEMD_EXTRA_CONFIG_OPTS "" CACHE STRING "Additional configuration options to be passed as-is to libsystemd build system")
if(NOT CMAKE_BUILD_TYPE)
set(LIBSYSTEMD_BUILD_TYPE "plain")
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
@@ -26,24 +29,24 @@ else()
set(LIBSYSTEMD_BUILD_TYPE "release")
endif()
if(SDBUSCPP_LIBSYSTEMD_VERSION LESS "239")
if(LIBSYSTEMD_VERSION LESS "239")
message(FATAL_ERROR "Only libsystemd version >=239 can be built as static part of sdbus-c++")
endif()
if(SDBUSCPP_LIBSYSTEMD_VERSION GREATER "240")
if(LIBSYSTEMD_VERSION GREATER "240")
set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h)
endif()
message(STATUS "Building with embedded libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
message(STATUS "Building with embedded libsystemd v${LIBSYSTEMD_VERSION}")
include(ExternalProject)
ExternalProject_Add(LibsystemdBuildProject
PREFIX libsystemd-v${SDBUSCPP_LIBSYSTEMD_VERSION}
PREFIX libsystemd-v${LIBSYSTEMD_VERSION}
GIT_REPOSITORY https://github.com/systemd/systemd-stable.git
GIT_TAG v${SDBUSCPP_LIBSYSTEMD_VERSION}-stable
GIT_TAG v${LIBSYSTEMD_VERSION}-stable
GIT_SHALLOW 1
UPDATE_COMMAND ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Drootprefix=<INSTALL_DIR> -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Drootprefix=<INSTALL_DIR> -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${LIBSYSTEMD_EXTRA_CONFIG_OPTS}
BUILD_COMMAND ${BUILD_VERSION_H}
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
BUILD_ALWAYS 0
+7 -11
View File
@@ -15,19 +15,15 @@ if(BUILD_DOXYGEN_DOC)
# workaround bug https://github.com/doxygen/doxygen/pull/6787
add_custom_command(TARGET doc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/sdbus-c++-class-diagram.png ${CMAKE_CURRENT_BINARY_DIR}/html/.)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT doc)
else()
message(WARNING "Documentation enabled, but Doxygen cannot be found")
endif()
endif()
if(SDBUSCPP_INSTALL)
install(FILES sdbus-c++-class-diagram.png
sdbus-c++-class-diagram.uml
systemd-dbus-config.md
using-sdbus-c++.md
DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
if (BUILD_DOXYGEN_DOC AND DOXYGEN_FOUND)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT sdbus-c++-doc)
endif()
endif()
install(FILES sdbus-c++-class-diagram.png
sdbus-c++-class-diagram.uml
systemd-dbus-config.md
using-sdbus-c++.md
DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT doc)
+157 -270
View File
@@ -5,7 +5,7 @@ Using sdbus-c++ library
1. [Introduction](#introduction)
2. [Integrating sdbus-c++ into your project](#integrating-sdbus-c-into-your-project)
3. [Solving sd-bus dependency](#solving-sd-bus-dependency)
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)
@@ -22,9 +22,7 @@ Using sdbus-c++ library
17. [Representing D-Bus Types in sdbus-c++](#representing-d-bus-types-in-sdbus-c)
18. [Support for match rules](#support-for-match-rules)
19. [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections)
20. [Using sdbus-c++ in external event loops](#using-sdbus-c-in-external-event-loops)
21. [Migrating to sdbus-c++ v2](#migrating-to-sdbus-c-v2)
22. [Conclusion](#conclusion)
20. [Conclusion](#conclusion)
Introduction
------------
@@ -56,7 +54,7 @@ PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.6],,
)
```
> **_Note_:** sdbus-c++ library uses a number of modern C++17 (and, conditionally, C++20) features. Please make sure you have a recent compiler (best gcc >= 10, clang >= 11).
> **_Note_:** sdbus-c++ library uses a number of modern C++17 features. Please make certain you have a recent compiler (gcc >= 7, clang >= 6).
If you intend to use xml-to-c++ generator tool (explained later) in your project to generate interface headers from XML, you can integrate that too with CMake or `pkg-config`:
@@ -74,14 +72,14 @@ add_custom_command(
)
```
Solving sd-bus dependency
-------------------------
Solving libsystemd dependency
-----------------------------
sdbus-c++ is a wrapper around sd-bus, a C library that has been written as part of [systemd](https://github.com/systemd/systemd) project.
sdbus-c++ depends on sd-bus API, which is implemented in libsystemd, a C library that is part of [systemd](https://github.com/systemd/systemd).
Within systemd, sd-bus is implemented as part of `libsystemd` shared library. At least version 0.22.0 (which corresponds to the minimum systemd version 238) of `libsystemd` is needed.
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 238 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.
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. If `libsystemd` is not found in the system when configuring sdbus-c++, then
@@ -89,9 +87,7 @@ However, sdbus-c++ can perfectly be used in non-systemd environments as well. If
2. sdbus-c++ will try to find `basu`, which is just sd-bus implementation extracted from systemd.
Alternatively to this fallback search sequence, you may explicitly instruct sdbus-c++ to use a specified sd-bus implementation through the `SDBUSCPP_SDBUS_LIB` CMake configuration option.
On systems where neither of those libraries is available, we can build sd-bus manually, or we can (conveniently) instruct sdbus-c++ to build and integrate sd-bus into itself for us.
On systems where neither of these is available, we can build sd-bus as a shared lib manually or we can (conveniently) instruct sdbus-c++ to build and integrate sd-bus into itself for us.
### Building and distributing libsystemd as a shared library yourself
@@ -111,9 +107,9 @@ $ ninja libsystemd.so.0.26.0 # or another version number depending which system
### Building and distributing libsystemd as part of sdbus-c++
sdbus-c++ provides `SDBUSCPP_BUILD_LIBSYSTEMD` configuration option. When turned on, sdbus-c++ will automatically download and build libsystemd as a static library and make it an opaque part of sdbus-c++ shared library for you. This is the most convenient and effective approach to build, distribute and use sdbus-c++ as a self-contained, systemd-independent library in non-systemd environments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Also, when distributing, make sure these dependency libraries are installed on the production machine.
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 environments. Just make sure your build machine has all dependencies needed by libsystemd build process. That includes, among others, `meson`, `ninja`, `git`, `gperf`, and -- primarily -- libraries and library headers for `libmount`, `libcap` and `librt` (part of glibc). Also, when distributing, make sure these dependency libraries are installed on the production machine.
You may additionally set the `SDBUSCPP_LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242).
You may additionally set the `LIBSYSTEMD_VERSION` configuration flag to fine-tune the version of systemd to be taken in. (The default value is 242).
Distributing sdbus-c++
----------------------
@@ -170,10 +166,10 @@ All public types and functions of sdbus-c++ reside in the `sdbus` namespace.
Error signalling and propagation
--------------------------------
`sdbus::Error` type is used as an exception to signal errors in sdbus-c++. There are two types of errors:
`sdbus::Error` exception is used to signal errors in sdbus-c++. There are two types of errors:
* D-Bus related errors, like call timeouts, failed socket allocation, etc. These are raised by the D-Bus library or D-Bus daemon itself.
* user-defined errors, i.e. errors signalled and propagated from remote methods back to the caller. So these are issued by sdbus-c++ clients.
* user-defined errors, i.e. errors signalled and propagated from remote methods back to the caller. So these are issued by sdbus-c++ users.
`sdbus::Error` is a carrier for both types of errors, carrying the error name and error message with it.
@@ -184,7 +180,7 @@ The following diagram illustrates the major entities in sdbus-c++.
![class](sdbus-c++-class-diagram.png)
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign well-known service names to those connections. An I/O event loop should be run on the bus connection.
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign unique service names to those connections. An I/O event loop should be run on the bus connection.
`IObject` represents the concept of an object that exposes its methods, signals and properties. Its responsibilities are:
@@ -238,7 +234,7 @@ Let's have an object `/org/sdbuscpp/concatenator` that implements the `org.sdbus
In the following sections, we will elaborate on the ways of implementing such an object on both the server and the client side.
> **Before running Concatenator example in your system:** In order for your service to be allowed to provide a D-Bus API on ** the system bus**, a D-Bus security policy file has to be put in place for that service. Otherwise the service will fail to start (you'll get `[org.freedesktop.DBus.Error.AccessDenied] Failed to request bus name (Permission denied)`, for example). To make the Concatenator example work in your system, [look in this section of systemd configuration](systemd-dbus-config.md#dbus-configuration) for how to name the file, where to place it, how to populate it. For further information, consult [dbus-daemon documentation](https://dbus.freedesktop.org/doc/dbus-daemon.1.html), sections *INTEGRATING SYSTEM SERVICES* and *CONFIGURATION FILE*. As an example used for sdbus-c++ integration tests, you may look at the [policy file for sdbus-c++ integration tests](/tests/integrationtests/files/org.sdbuscpp.integrationtests.conf).
> **Before running Concatenator example in your system:** In order for your service to be allowed to provide a D-Bus API on system bus, a D-Bus security policy file has to be put in place for that service. Otherwise the service will fail to start (you'll get `[org.freedesktop.DBus.Error.AccessDenied] Failed to request bus name (Permission denied)`, for example). To make the Concatenator example work in your system, [look in this section of systemd configuration](systemd-dbus-config.md#dbus-configuration) for how to name the file, where to place it, how to populate it. For further information, consult [dbus-daemon documentation](https://dbus.freedesktop.org/doc/dbus-daemon.1.html), sections *INTEGRATING SYSTEM SERVICES* and *CONFIGURATION FILE*. As an example used for sdbus-c++ integration tests, you may look at the [policy file for sdbus-c++ integration tests](/tests/integrationtests/files/org.sdbuscpp.integrationtests.conf).
Implementing the Concatenator example using basic sdbus-c++ API layer
---------------------------------------------------------------------
@@ -289,41 +285,36 @@ void concatenate(sdbus::MethodCall call)
reply.send();
// Emit 'concatenated' signal
sdbus::InterfaceName interfaceName{"org.sdbuscpp.Concatenator"};
sdbus::SignalName signalName{"concatenated"};
auto signal = g_concatenator->createSignal(interfaceName, signalName);
const char* interfaceName = "org.sdbuscpp.Concatenator";
auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
signal << result;
g_concatenator->emitSignal(signal);
}
int main(int argc, char *argv[])
{
// Create D-Bus connection to (either the session or system) bus and requests a well-known name on it.
sdbus::ServiceName serviceName{"org.sdbuscpp.concatenator"};
auto connection = sdbus::createBusConnection(serviceName);
// Create D-Bus connection to the system bus and requests name on it.
const char* serviceName = "org.sdbuscpp.concatenator";
auto connection = sdbus::createSystemBusConnection(serviceName);
// Create concatenator D-Bus object.
sdbus::ObjectPath objectPath{"/org/sdbuscpp/concatenator"};
auto concatenator = sdbus::createObject(*connection, std::move(objectPath));
const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenator = sdbus::createObject(*connection, objectPath);
g_concatenator = concatenator.get();
// Register D-Bus methods and signals on the concatenator object, and exports the object.
sdbus::InterfaceName interfaceName{"org.sdbuscpp.Concatenator"};
concatenator->addVTable( sdbus::MethodVTableItem{"concatenate", sdbus::Signature{"ais"}, {}, sdbus::Signature{"s"}, {}, &concatenate, {}}
, sdbus::SignalVTableItem{"concatenated", sdbus::Signature{"s"}, {}, {}} )
.forInterface(interfaceName);
const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->registerMethod(interfaceName, "concatenate", "ais", "s", &concatenate);
concatenator->registerSignal(interfaceName, "concatenated", "s");
concatenator->finishRegistration();
// Run the I/O event loop on the bus connection.
connection->enterEventLoop();
}
```
We establish a D-Bus system connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We add a so-called object vtable, where we declare and describe its D-Bus API, i.e. its interface, methods, signals, properties (if any) that the object provides. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests.
> **_Tip_:** There's also an overload of `addVTable()` method with `return_slot_t` tag parameter which returns a `Slot` object. The slot is a simple RAII-based handle of the associated vtable registration. As long as you keep the slot object, the vtable registration is active. When you let go of the slot, the vtable is automatically removed from the D-Bus object. This gives you the ability to implement "dynamic" D-Bus object API that is addable as well as removable at any time during object lifetime.
> **_Note_:** A D-Bus object can have any number of vtables attached to it. Even a D-Bus interface of an object can have multiple vtables attached to it.
We establish a D-Bus system connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We register interfaces, its methods, signals that the object provides, and, through `finishRegistration()`, export the object (i.e., make it visible to clients) on the bus. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests.
The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall call)`. The `call` parameter is the incoming method call message. We need to deserialize our method input arguments from it. Then we can invoke the logic of the method and get the results. Then for the given `call`, we create a `reply` message, pack results into it and send it back to the caller through `send()`. (If we had a void-returning method, we'd just send an empty `reply` back.) We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`.
@@ -338,7 +329,7 @@ Please note that we can create and destroy D-Bus objects on a connection dynamic
#include <iostream>
#include <unistd.h>
void onConcatenated(sdbus::Signal signal)
void onConcatenated(sdbus::Signal& signal)
{
std::string concatenatedString;
signal >> concatenatedString;
@@ -350,24 +341,22 @@ int main(int argc, char *argv[])
{
// Create proxy object for the concatenator object on the server side. Since here
// we are creating the proxy instance without passing connection to it, the proxy
// will create its own connection automatically (to either system bus or session bus,
// depending on the context).
sdbus::ServiceName destination{"org.sdbuscpp.concatenator"};
sdbus::ObjectPath objectPath{"/org/sdbuscpp/concatenator"};
auto concatenatorProxy = sdbus::createProxy(std::move(destination), std::move(objectPath));
// 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::createProxy(destinationName, objectPath);
// Let's subscribe for the 'concatenated' signals
sdbus::InterfaceName interfaceName{"org.sdbuscpp.Concatenator"};
sdbus::SignalName signalName{"concatenated"};
concatenatorProxy->registerSignalHandler(interfaceName, signalName, &onConcatenated);
const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenatorProxy->registerSignalHandler(interfaceName, "concatenated", &onConcatenated);
concatenatorProxy->finishRegistration();
std::vector<int> numbers = {1, 2, 3};
std::string separator = ":";
MethodName concatenate{"concatenate"};
// Invoke concatenate on given interface of the object
{
auto method = concatenatorProxy->createMethodCall(interfaceName, concatenate);
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
method << numbers << separator;
auto reply = concatenatorProxy->callMethod(method);
std::string result;
@@ -375,9 +364,9 @@ int main(int argc, char *argv[])
assert(result == "1:2:3");
}
/ // Invoke concatenate again, this time with no numbers and we shall get an error
// Invoke concatenate again, this time with no numbers and we shall get an error
{
auto method = concatenatorProxy->createMethodCall(interfaceName, concatenate);
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
method << std::vector<int>() << separator;
try
{
@@ -397,11 +386,9 @@ int main(int argc, char *argv[])
}
```
In simple cases, we don't need to create D-Bus connection explicitly for our proxies. We either pass a connection object to the proxy upon creation, or otherwise the proxy will create a connection of his own (to either the session bus or the system bus, depending on the context, see `man sd_bus_open`). 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 handlers for signals we are interested in (if any).
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.
> **_Tip_:** There's also an overload of `registerSignalHandler()` with `return_slot_t` tag which returns a `Slot` object. The slot is a simple RAII-based handle of the subscription. As long as you keep the slot object, the signal subscription is active. When you let go of the object, the signal handler is automatically unregistered. This gives you finer control over the lifetime of signal subscription.
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.
Subsequently, we invoke two RPC calls to object's `concatenate()` method. We create a method call message by invoking proxy's `createMethodCall()`. We serialize method input arguments into it, and make a synchronous call via proxy's `callMethod()`. As a return value we get the reply message as soon as it arrives. We deserialize return values from that message, and further use it in our program. The second `concatenate()` RPC call is done with invalid arguments, so we get a D-Bus error reply from the service, which as we can see is manifested via `sdbus::Error` exception being thrown.
@@ -411,12 +398,14 @@ Please note that we can create and destroy D-Bus object proxies dynamically, at
There are several factory methods to create a bus connection object in sdbus-c++:
* `createBusConnection()` - opens a connection to the session bus when in a user context, and a connection to the system bus, otherwise
* `createBusConnection(const sdbus::ServiceName& name)` - opens a connection to the session bus when in a user context, and a connection with the given name to the system bus, otherwise, and requests the given well-known service name on the bus
* `createConnection()` - opens a connection to the system bus
* `createConnection(const std::string& name)` - opens a connection with the given name to the system bus
* `createDefaultBusConnection()` - opens a connection to the session bus when in a user context, and a connection to the system bus, otherwise
* `createDefaultBusConnection(const std::string& name)` - opens a connection with the given name to the session bus when in a user context, and a connection with the given name to the system bus, otherwise
* `createSystemBusConnection()` - opens a connection to the system bus
* `createSystemBusConnection(const sdbus::ServiceName& name)` - opens a connection to the system bus, and requests the given well-known service name on the bus
* `createSystemBusConnection(const std::string& name)` - opens a connection with the given name to the system bus
* `createSessionBusConnection()` - opens a connection to the session bus
* `createSessionBusConnection(const sdbus::ServiceName& name)` - opens a connection to the session bus, and requests the given well-known service name on the bus.
* `createSessionBusConnection(const std::string& name)` - opens a connection with the given name to the session bus
* `createSessionBusConnectionWithAddress(const std::string& address)` - opens a connection to the session bus at a custom address
* `createRemoteSystemBusConnection(const std::string& host)` - opens a connection to the system bus on a remote host using ssh
* `createDirectBusConnection(const std::string& address)` - opens direct D-Bus connection at a custom address (see [Using direct (peer-to-peer) D-Bus connections](#using-direct-peer-to-peer-d-bus-connections))
@@ -434,51 +423,41 @@ How shall we use connections in relation to D-Bus objects and object proxies?
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).
A typical use case for most services is **one** D-Bus connection in the application. The application runs an (internal or external, see below) 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 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.
The connection is thread-safe and objects and proxies can invoke operations on it from multiple threads simultaneously, but the operations are serialized. Access to the connection is mutually exclusive. 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 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.
When a `poll()` sleeps upon the connection, the connection can be used by other threads without blocking. When calling a D-Bus method through a proxy synchronously, the proxy blocks the connection from concurrent use until it gets from the peer a reply (or an error, the call times out). Async D-Bus method calls don't block the connection while the call is pending (the connection is only "locked" while the call message is sent out and while the reply handler is executed for an already arrived reply message, but not in between while the call is pending). See doxygen documentation for `IProxy::callMethod()` overloads for more details.
We should bear these design aspects of sdbus-c++ in mind when designing more complex, multi-threaded services with high parallelism. If we have undesired contention on a connection, creating a separate, 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.
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.
So, more technically, how can we use connections from the server and the client perspective?
#### Using D-Bus connections on the server side
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:
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:
* its internal event loop
* either in a blocking way, through `enterEventLoop()`,
* or in a non-blocking async way, through `enterEventLoopAsync()`,
* or an external event loop. This is suitable if we use in our application an event loop implementation of our choice (e.g., sd-event, GLib Event Loop, boost::asio, ...) and we want to hook up our sdbus-c++ connections with it. See [Using sdbus-c++ in external event loops](#using-sdbus-c-in-external-event-loops) section for more information.
* either in a blocking way, through `enterEventLoop()`,
* or in a non-blocking async way, through `enterEventLoopAsync()`,
* 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, I/O events and timeouts through `getEventLoopPollData()` and use that data in our event loop mechanism.
The object takes the D-Bus connection as a reference in its constructor. This is the only way to wire the connection and the object together. We must make sure the connection exists as long as objects using it exist.
The object takes the D-Bus connection as a reference in its constructor. This is the only way to wire connection and object together. We must make sure the connection exists as long as objects using it exist.
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.
*Note:* There may be both objects and proxies hooked to a single connection, of course. A D-Bus server application may also be a client to another D-Bus server application, and share one D-Bus connection for the D-Bus interface it exports as well as for the proxies towards other D-Bus interfaces.
#### Using D-Bus connections on the client side
On the **client** side we likewise need a connection -- just that unlike on the server side, we don't need to request a unique bus name on it. We have more options here when creating a proxy:
* Pass an already existing connection as a reference. This is the typical approach when the application already maintains a D-Bus connection (maybe it provide D-Bus API on it, and/or it already has some proxies hooked on it). The proxy will share the connection with others. With this approach we must of course ensure that the connection exists as long as the proxy exists. For discussion on options for running an event loop on that connection, see above section [Using D-Bus connections on the server side](#using-d-bus-connections-on-the-server-side).
* Pass an already existing connection as a reference. This is the typical approach when the application already maintains a D-Bus connection (maybe it provide D-Bus API on it, and/or it already has some proxies hooked on it). The proxy will share the connection with others. With this approach we must of course ensure that the connection exists as long as the proxy exists.
* Or -- and this is a simpler approach for simple D-Bus client applications -- we have another option: we let the proxy maintain its own connection (and potentially an associated event loop thread, see below). We have two options here:
* Or -- and this is typical when we have a simple D-Bus client application -- we have another option: we let the proxy maintain its own connection (and potentially an associated event loop thread, see below):
* We either create the connection ourselves and `std::move` it to the proxy object factory. The proxy becomes an owner of this connection, and it will be his dedicated connection. This has the advantage that we may choose the type of connection (system, session, remote). Additionally,
* 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).
* when created **without** `dont_run_event_loop_thread_t` tag, the proxy **will start** a dedicated event loop thread on that connection;
* or, when created **with** `dont_run_event_loop_thread_t` tag, the proxy will start **no** event loop thread on that connection.
* Or we don't 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. Quite **simple**, but as you can see, this hurts scalability in case of many proxies, as each would spawn and maintain its own event loop thread (see discussion higher above). But we don't necessarily need an event loop thread, in case our proxy doesn't need to listen to signals or async method call replies. Read on.
* Or we don't care about connnections at all (proxy factory overloads with no connection parameter). Under the hood, the proxy creates its own connection, to either the session bus (when in a user context) or the system bus otherwise. Additionally:
* when created **without** `dont_run_event_loop_thread_t` tag, the proxy **will start** a dedicated event loop thread on that connection;
* or, when created **with** `dont_run_event_loop_thread_t` tag, the proxy will start **no** event loop thread on that connection.
It's also possible in this case to instruct the proxy to **not spawn an event loop thread** for its connection. There are many situations that we want to quickly create a proxy, carry out one or a few (synchronous) D-Bus calls, and let go of proxy. We call them light-weight proxies. For that purpose, spawning a new event loop thread comes with time and resource penalty, for nothing. To create such **a light-weight proxy**, use the factory/constructor overload with `dont_run_event_loop_thread_t`. All in above two bullet sub-points holds; the proxy just won't spawn a thread with an event loop in it. Note that such a proxy can be used only for synchronous D-Bus calls; it may not receive signals or async call replies.
A proxy needs an event loop if it's a "**long-lived**" proxy that listens on incoming messages like signals, async call replies, atc. Sharing one connection with its one event loop is more scalable. Starting a dedicated event loop in a proxy is simpler from API perspective, but comes at a performance and resource cost for each proxy creation/destruction, and it hurts scalability. A simple and scalable option are "**short-lived, light-weight**" proxies. Quite a typical use case is that we occasionally need to carry out one or a few D-Bus calls and that's it. We may create a proxy, do the calls, and let go of proxy. Such a light-weight proxy is created when `dont_run_event_loop_thread_t` tag is passed to the proxy factory. Such a proxy **does not spawn** an event loop thread. It only support synchronous D-Bus calls (no signals, no async calls...), and is meant to be created, used right away, and then destroyed immediately.
#### Stopping internal I/O event loops graciously
#### Stopping I/O event loops graciously
A connection with an asynchronous event loop (i.e. one initiated through `enterEventLoopAsync()`) will stop and join its event loop thread automatically in its destructor. An event loop that blocks in the synchronous `enterEventLoop()` call can be unblocked through `leaveEventLoop()` call on the respective bus connection issued from a different thread or from an OS signal handler.
@@ -507,13 +486,13 @@ The code written using this layer expresses in a declarative way *what* it does,
int main(int argc, char *argv[])
{
// Create D-Bus connection to the (either system or session) bus and requests a well-known name on it.
sdbus::ServiceName serviceName{"org.sdbuscpp.concatenator"};
auto connection = sdbus::createBusConnection(serviceName);
// Create D-Bus connection to the system bus and requests name on it.
const char* serviceName = "org.sdbuscpp.concatenator";
auto connection = sdbus::createSystemBusConnection(serviceName);
// Create concatenator D-Bus object.
sdbus::ObjectPath objectPath{"/org/sdbuscpp/concatenator"};
auto concatenator = sdbus::createObject(*connection, std::move(objectPath));
const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenator = sdbus::createObject(*connection, objectPath);
auto concatenate = [&concatenator](const std::vector<int> numbers, const std::string& separator)
{
@@ -528,25 +507,23 @@ int main(int argc, char *argv[])
}
// Emit 'concatenated' signal
concatenator->emitSignal("concatenated").onInterface("org.sdbuscpp.Concatenator").withArguments(result);
const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->emitSignal("concatenated").onInterface(interfaceName).withArguments(result);
return result;
};
// Register D-Bus methods and signals on the concatenator object, and exports the object.
concatenator->addVTable( sdbus::registerMethod("concatenate").implementedAs(std::move(concatenate))
, sdbus::registerSignal{"concatenated").withParameters<std::string>() )
.forInterface("org.sdbuscpp.Concatenator");
const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->registerMethod("concatenate").onInterface(interfaceName).implementedAs(std::move(concatenate));
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>();
concatenator->finishRegistration();
// Run the loop on the connection.
connection->enterEventLoop();
}
```
> **_Tip_:** There's also an overload of `addVTable(...).forInterface()` method with `return_slot_t` tag parameter which returns a `Slot` object. The slot is a simple RAII-based handle of the associated vtable registration. As long as you keep the slot object, the vtable registration is active. When you let go of the slot, the vtable is automatically removed from the D-Bus object. This gives you the ability to implement "dynamic" D-Bus object API that is addable as well as removable at any time during object lifetime.
> **_Note_:** A D-Bus object can have any number of vtables attached to it. Even a D-Bus interface of an object can have multiple vtables attached to it.
### Client side
```c++
@@ -564,13 +541,14 @@ void onConcatenated(const std::string& concatenatedString)
int main(int argc, char *argv[])
{
// Create proxy object for the concatenator object on the server side
sdbus::ServiceName destination{"org.sdbuscpp.concatenator"};
sdbus::ObjectPath objectPath{"/org/sdbuscpp/concatenator"};
auto concatenatorProxy = sdbus::createProxy(std::move(destination), std::move(objectPath));
const char* destinationName = "org.sdbuscpp.concatenator";
const char* objectPath = "/org/sdbuscpp/concatenator";
auto concatenatorProxy = sdbus::createProxy(destinationName, objectPath);
// Let's subscribe for the 'concatenated' signals
sdbus::InterfaceName interfaceName{"org.sdbuscpp.Concatenator"};
const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenatorProxy->uponSignal("concatenated").onInterface(interfaceName).call([](const std::string& str){ onConcatenated(str); });
concatenatorProxy->finishRegistration();
std::vector<int> numbers = {1, 2, 3};
std::string separator = ":";
@@ -604,19 +582,17 @@ int main(int argc, char *argv[])
When registering methods, calling methods or emitting signals, multiple lines of code have shrunk into simple one-liners. Signatures of provided callbacks are introspected and types of provided arguments are deduced at compile time, so the D-Bus signatures as well as serialization and deserialization of arguments to and from D-Bus messages are generated for us completely by the compiler.
> **_Tip_:** There's also an overload of `uponSignal(...).call()` with `return_slot_t` tag which returns a `Slot` object. The slot is a simple RAII-based handle of the subscription. As long as you keep the slot object, the signal subscription is active. When you let go of the object, the signal handler is automatically unregistered. This gives you finer control over the lifetime of signal subscription.
We recommend that sdbus-c++ users prefer the convenience API to the lower level, basic API. When feasible, using generated adaptor and proxy C++ bindings is even better as it provides yet slightly higher abstraction built on top of the convenience API, where remote calls look simply like local, native calls of object methods. They are described in the following section.
> **_Note_:** By default, signal callback handlers are not invoked (i.e., the signal is silently dropped) if there is a signal signature mismatch. If you want to be informed of such situations, you can add `std::optional<sdbus::Error>` parameter to the beginning of your signal callback handler's parameter list. When sdbus-c++ invokes the handler, it will set this argument either to be empty (in normal cases), or to carry a corresponding `sdbus::Error` object (in case of deserialization failures, like type mismatches). An example of a handler with the signature (`int`) different from the real signal contents (`string`):
> **_Note_:** By default, signal callback handlers are not invoked (i.e., the signal is silently dropped) if there is a signal signature mismatch. If clients want to be informed of such situations, they can prepend `const sdbus::Error*` parameter to their signal callback handler's parameter list. This argument will be `nullptr` in normal cases, and will provide access to the corresponding `sdbus::Error` object in case of deserialization failures. An example of a handler with the signature (`int`) different from the real signal contents (`string`):
> ```c++
> void onConcatenated(std::optional<sdbus::Error> e, int wrongParameter)
> void onConcatenated(const sdbus::Error* e, int wrongParameter)
> {
> assert(e.has_value());
> assert(e);
> assert(e->getMessage() == "Failed to deserialize a int32 value");
> }
> ```
> Signature mismatch in signal handlers is probably the most common reason why signals are not received in the client, while we can see them on the bus with `dbus-monitor`. Use `std::optional<sdbus::Error>`-based callback variant and inspect the error to check if that's the cause of your problems.
> Signature mismatch in signal handlers is probably the most common reason why signals are not received in the client, while we can see them on the bus with `dbus-monitor`. Use `const sdbus::Error*`-based callback variant and inspect the error to check if that's the cause of such problems.
> **_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++
@@ -694,7 +670,7 @@ After running this through the code generator, we get the generated code that is
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.
Generated adaptor classes are not copyable and not moveable by design. One can create them on the heap and manage them in e.g. a `std::unique_ptr` if move semantics is needed (for example, when they are stored in a container).
Generated adaptor classes support move semantics. They are moveable but not copyable.
```cpp
/*
@@ -718,35 +694,30 @@ public:
protected:
Concatenator_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
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(const Concatenator_adaptor&) = delete;
Concatenator_adaptor& operator=(const Concatenator_adaptor&) = delete;
Concatenator_adaptor(Concatenator_adaptor&&) = delete;
Concatenator_adaptor& operator=(Concatenator_adaptor&&) = delete;
Concatenator_adaptor(Concatenator_adaptor&&) = default;
Concatenator_adaptor& operator=(Concatenator_adaptor&&) = default;
~Concatenator_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable( sdbus::registerMethod("concatenate").withInputParamNames("numbers", "separator").withOutputParamNames("concatenatedString").implementedAs([this](const std::vector<int32_t>& numbers, const std::string& separator){ return this->concatenate(numbers, separator); })
, sdbus::registerSignal("concatenated").withParameters<std::string>("concatenatedString")
).forInterface(INTERFACE_NAME);
}
public:
void emitConcatenated(const std::string& concatenatedString)
{
m_object.emitSignal("concatenated").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
object_->emitSignal("concatenated").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
}
private:
virtual std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}} // namespaces
@@ -758,7 +729,7 @@ private:
Analogously to the adaptor classes described above, there is one proxy 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.
Generated proxy classes are not copyable and not moveable by design. One can create them on the heap and manage them in e.g. a `std::unique_ptr` if move semantics is needed (for example, when they are stored in a container).
Generated proxy classes support move semantics. They are moveable but not copyable.
```cpp
/*
@@ -782,34 +753,30 @@ public:
protected:
Concatenator_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
proxy_->uponSignal("concatenated").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
}
Concatenator_proxy(const Concatenator_proxy&) = delete;
Concatenator_proxy& operator=(const Concatenator_proxy&) = delete;
Concatenator_proxy(Concatenator_proxy&&) = delete;
Concatenator_proxy& operator=(Concatenator_proxy&&) = delete;
Concatenator_proxy(Concatenator_proxy&&) = default;
Concatenator_proxy& operator=(Concatenator_proxy&&) = default;
~Concatenator_proxy() = default;
void registerProxy()
{
m_proxy.uponSignal("concatenated").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
}
virtual void onConcatenated(const std::string& concatenatedString) = 0;
public:
std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)
{
std::string result;
m_proxy.callMethod("concatenate").onInterface(INTERFACE_NAME).withArguments(numbers, separator).storeResultsTo(result);
proxy_->callMethod("concatenate").onInterface(INTERFACE_NAME).withArguments(numbers, separator).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
}} // namespaces
@@ -838,7 +805,7 @@ Calling `registerAdaptor()` and `unregisterAdaptor()` was not necessary in previ
class Concatenator : public sdbus::AdaptorInterfaces<org::sdbuscpp::Concatenator_adaptor /*, more adaptor classes if there are more interfaces*/>
{
public:
Concatenator(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
Concatenator(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
@@ -881,13 +848,13 @@ That's it. We now have an implementation of a D-Bus object implementing `org.sdb
int main(int argc, char *argv[])
{
// Create D-Bus connection to (either the system or session) bus and request a well-known name on it.
sdbus::ServiceName serviceName{"org.sdbuscpp.concatenator"};
auto connection = sdbus::createBusConnection(serviceName);
// Create D-Bus connection to the system bus and requests name on it.
const char* serviceName = "org.sdbuscpp.concatenator";
auto connection = sdbus::createSystemBusConnection(serviceName);
// Create concatenator D-Bus object.
sdbus::ObjectPath objectPath{"/org/sdbuscpp/concatenator"};
Concatenator concatenator(*connection, std::move(objectPath));
const char* objectPath = "/org/sdbuscpp/concatenator";
Concatenator concatenator(*connection, objectPath);
// Run the loop on the connection.
connection->enterEventLoop();
@@ -917,7 +884,7 @@ Calling `registerProxy()` and `unregisterProxy()` was not necessary in previous
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::Concatenator_proxy /*, more proxy classes if there are more interfaces*/>
{
public:
ConcatenatorProxy(sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
ConcatenatorProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
registerProxy();
@@ -938,7 +905,7 @@ protected:
> **_Tip_:** By inheriting from `sdbus::ProxyInterfaces`, we get access to the protected `getProxy()` method. We can call this method inside our proxy implementation class to access the underlying `IProxy` object.
In the above example, a proxy is created that creates and maintains its own bus connection (the bus is either session bus or system bus depending on the context, see `man sd_bus_open`). 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.
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::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.
@@ -951,9 +918,9 @@ Now let's use this proxy to make remote calls and listen to signals in a real ap
int main(int argc, char *argv[])
{
// Create proxy object for the concatenator object on the server side
sdbus::ServiceName destination{"org.sdbuscpp.concatenator"};
sdbus::ObjectPath objectPath{"/org/sdbuscpp/concatenator"};
ConcatenatorProxy concatenatorProxy(std::move(destination), std::move(objectPath));
const char* destinationName = "org.sdbuscpp.concatenator";
const char* objectPath = "/org/sdbuscpp/concatenator";
ConcatenatorProxy concatenatorProxy(destinationName, objectPath);
std::vector<int> numbers = {1, 2, 3};
std::string separator = ":";
@@ -1045,9 +1012,8 @@ void concatenate(sdbus::MethodCall call)
reply.send();
// Emit 'concatenated' signal (creating and emitting signals is thread-safe)
sdbus::InterfaceName interfaceName{"org.sdbuscpp.Concatenator"};
sdbus::SignalName signalName{"concatenated"};
auto signal = g_concatenator->createSignal(interfaceName, signalName);
const char* interfaceName = "org.sdbuscpp.Concatenator";
auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
signal << result;
g_concatenator->emitSignal(signal);
}).detach();
@@ -1140,15 +1106,15 @@ int main(int argc, char *argv[])
{
/* ... */
auto callback = [](MethodReply reply, std::optional<sdbus::Error> error)
auto callback = [](MethodReply& reply, const sdbus::Error* error)
{
if (!error) // No error
if (error == nullptr) // No error
{
std::string result;
reply >> result;
std::cout << "Got concatenate result: " << result << std::endl;
}
else // We've got a D-Bus error...
else // We got a D-Bus error...
{
std::cerr << "Got concatenate error " << error->getName() << " with message " << error->getMessage() << std::endl;
}
@@ -1156,7 +1122,7 @@ int main(int argc, char *argv[])
// Invoke concatenate on given interface of the object
{
auto method = concatenatorProxy->createMethodCall(interfaceName, concatenate);
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
method << numbers << separator;
concatenatorProxy->callMethod(method, callback);
// When the reply comes, we shall get "Got concatenate result 1:2:3" on the standard output
@@ -1164,7 +1130,7 @@ int main(int argc, char *argv[])
// Invoke concatenate again, this time with no numbers and we shall get an error
{
auto method = concatenatorProxy->createMethodCall(interfaceName, concatenate);
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
method << std::vector<int>() << separator;
concatenatorProxy->callMethod(method, callback);
// When the reply comes, we shall get concatenation error message on the standard error output
@@ -1176,7 +1142,7 @@ int main(int argc, char *argv[])
}
```
The callback is a void-returning function taking two arguments: a reference to the reply message, and a pointer to the prospective `sdbus::Error` instance. Empty `error` optional argument means that no D-Bus error occurred while making the call, and the reply message contains a valid reply. A non-empty `error` argument means that an error occurred during the call, and we can access the error name and message from the `Error` value inside the argument.
The callback is a void-returning function taking two arguments: a reference to the reply message, and a pointer to the prospective `sdbus::Error` instance. Zero `Error` pointer means that no D-Bus error occurred while making the call, and the reply message contains valid reply. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred. Error name and message can then be read out by the client from that instance.
There is also an overload of this `IProxy::callMethod()` function taking method call timeout argument.
@@ -1186,7 +1152,7 @@ Another option is to use `std::future`-based overload of the `IProxy::callMethod
...
// Invoke concatenate on given interface of the object
{
auto method = concatenatorProxy->createMethodCall(interfaceName, concatenate);
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
method << numbers << separator;
auto future = concatenatorProxy->callMethod(method, sdbus::with_future);
try
@@ -1205,16 +1171,16 @@ Another option is to use `std::future`-based overload of the `IProxy::callMethod
### Convenience API
On the convenience API level, the call statement starts with `callMethodAsync()`, and one option is to finish the statement with `uponReplyInvoke()` that takes a callback handler. The callback is a void-returning function that takes at least one argument: `std::optional<sdbus::Error>`. All subsequent arguments shall exactly reflect the D-Bus method output arguments. A concatenator example:
On the convenience API level, the call statement starts with `callMethodAsync()`, and one option is to finish the statement with `uponReplyInvoke()` that takes a callback handler. The callback is a void-returning function that takes at least one argument: pointer to the `sdbus::Error` instance. All subsequent arguments shall exactly reflect the D-Bus method output arguments. A concatenator example:
```c++
int main(int argc, char *argv[])
{
/* ... */
auto callback = [](std::optional<sdbus::Error> error, const std::string& concatenatedString)
auto callback = [](const sdbus::Error* error, const std::string& concatenatedString)
{
if (!error) // No error
if (error == nullptr) // No error
std::cout << "Got concatenate result: " << concatenatedString << std::endl;
else // We got a D-Bus error...
std::cerr << "Got concatenate error " << error->getName() << " with message " << error->getMessage() << std::endl;
@@ -1238,11 +1204,7 @@ int main(int argc, char *argv[])
}
```
Empty `error` parameter means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. However, `error` parameter containing a value means that an error occurred during the call (and subsequent arguments are simply default-constructed), and the underlying `Error` instance provides us with the error name and message.
> **_Tip_:** The function returns the `sdbus::PendingAsyncCall` object, a non-owning, observing handle to the async call. It can be used to query whether the call is still in progress, and to cancel the call.
> **_Tip_:** There is also the `.uponReplyInvoke(callback, sdbus::return_slot);` variant with the `return_slot` tag, which returns `Slot` object, an owning RAII handle to the async call. This makes the client an owner of the pending async call. Letting go of the handle means cancelling the call.
When the `Error` pointer is zero, it means that no D-Bus error occurred while making the call, and subsequent arguments are valid D-Bus method return values. Non-zero `Error` pointer, however, points to the valid `Error` instance, meaning that an error occurred during the call (and subsequent arguments are simply default-constructed). Error name and message can then be read out by the client from `Error` instance.
Another option is to finish the async call statement with `getResultAsFuture()`, which is a template function which takes the list of types returned by the D-Bus method (empty list in case of `void`-returning method) which returns a `std::future` object, which will later, when the reply arrives, be set to contain the return value(s). Or if the call returns an error, `sdbus::Error` will be thrown by `std::future::get()`.
@@ -1291,7 +1253,7 @@ An asynchronous method can be generated as a callback-based method or `std::futu
For each client-side async method, a corresponding `on<MethodName>Reply` pure virtual function, where `<MethodName>` is the capitalized D-Bus method name, is generated in the generated proxy class. This function is the callback invoked when the D-Bus method reply arrives, and must be provided a body by overriding it in the implementation class.
So in the specific example above, the tool 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(std::optional<sdbus::Error> error, const std::string& concatenatedString);` method, which we shall override in the derived `ConcatenatorProxy`.
So in the specific example above, the tool 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`.
#### Generating std:future-based async methods
@@ -1341,7 +1303,7 @@ Getting a property in asynchronous manner is also possible, in both callback-bas
```c++
// Callback-based method:
auto callback = [](std::optional<sdbus::Error> /*error*/, sdbus::Variant value)
auto callback = [](const sdbus::Error* err, sdbus::Variant value)
{
std::cout << "Got property value: " << value.get<uint32_t>() << std::endl;
};
@@ -1352,7 +1314,7 @@ std::future<sdbus::Variant> statusFuture = object.getPropertyAsync("status").onI
std::cout << "Got property value: " << statusFuture.get().get<uint32_t>() << std::endl;
```
More information on an `error` callback handler parameter, on behavior of `future` in erroneous situations, can be found in section [Asynchronous client-side methods](#asynchronous-client-side-methods).
More information on `error` callback handler parameter, on behavior of `future` in erroneous situations, can be found in section [Asynchronous client-side methods](#asynchronous-client-side-methods).
#### Writing a property
@@ -1367,7 +1329,7 @@ Setting a property in asynchronous manner is also possible, in both callback-bas
```c++
// Callback-based method:
auto callback = [](std::optional<sdbus::Error> error { /*... Error handling in case error contains a value...*/ };
auto callback = [](const sdbus::Error* err) { /*... Error handling in case err is non-null...*/ };
uint32_t status = proxy->setPropertyAsync("status").onInterface("org.sdbuscpp.Concatenator").toValue(status).uponReplyInvoke(std::move(callback));
// Future-based method:
std::future<void> statusFuture = object.setPropertyAsync("status").onInterface("org.sdbuscpp.Concatenator").getResultAsFuture();
@@ -1414,17 +1376,12 @@ class PropertyProvider_adaptor
public:
PropertyProvider_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
object_->registerProperty("status").onInterface(INTERFACE_NAME).withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); });
}
/*...*/
void registerAdaptor()
{
m_object.addVTable( sdbus::registerProperty("status").withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); })
).forInterface(INTERFACE_NAME);
}
~PropertyProvider_adaptor() = default;
private:
// property getter
@@ -1448,13 +1405,13 @@ public:
// getting the property value
uint32_t status()
{
return m_object.getProperty("status").onInterface(INTERFACE_NAME).get<uint32_t>();
return object_->getProperty("status").onInterface(INTERFACE_NAME);
}
// setting the property value
void status(const uint32_t& value)
{
m_object.setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
object_->setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
}
/*...*/
@@ -1495,19 +1452,19 @@ class PropertyProvider_proxy
{
/*...*/
virtual void onStatusPropertyGetReply(const uint32_t& value, std::optional<sdbus::Error> error) = 0;
virtual void onStatusPropertyGetReply(const uint32_t& value, const sdbus::Error* error) = 0;
public:
// getting the property value
sdbus::PendingAsyncCall status()
{
return m_object.getPropertyAsync("status").onInterface(INTERFACE_NAME).uponReplyInvoke([this](std::optional<sdbus::Error> error, const sdbus::Variant& value){ this->onStatusPropertyGetReply(value.get<uint32_t>(), std::move(error)); });
return object_->getPropertyAsync("status").onInterface(INTERFACE_NAME).uponReplyInvoke([this](const sdbus::Error* error, const sdbus::Variant& value){ this->onActionPropertyGetReply(value.get<uint32_t>(), error); });
}
// setting the property value
void status(const uint32_t& value)
{
m_object.setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
object_->setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
}
/*...*/
@@ -1532,7 +1489,7 @@ Pre-generated `*_proxy` and `*_adaptor` convenience classes for these standard i
For example, for our `Concatenator` example above in this tutorial, we may want to conveniently emit a `PropertyChanged` signal under `org.freedesktop.DBus.Properties` interface. First, we must augment our `Concatenator` class to also inherit from `org.freedesktop.DBus.Properties` interface: `class Concatenator : public sdbus::AdaptorInterfaces<org::sdbuscpp::Concatenator_adaptor, sdbus::Properties_adaptor> {...};`, and then 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 you, the user of sdbus-c++, who are supposed to emit them.
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.
Working examples of using standard D-Bus interfaces can be found in [sdbus-c++ integration tests](/tests/integrationtests/DBusStandardInterfacesTests.cpp) or the [examples](/examples) directory.
@@ -1545,23 +1502,23 @@ sdbus-c++ provides many default, pre-defined C++ type representations for D-Bus
| Category | Code | Code ASCII | Conventional Name | C++ Type |
|---------------------|-------------|------------|--------------------|---------------------------------|
| reserved | 0 | NUL | INVALID | - |
| fixed, basic | 121 | y | BYTE | `uint8_t` |
| fixed, basic | 98 | b | BOOLEAN | `bool` |
| fixed, basic | 110 | n | INT16 | `int16_t` |
| fixed, basic | 113 | q | UINT16 | `uint16_t` |
| fixed, basic | 105 | i | INT32 | `int32_t` |
| fixed, basic | 117 | u | UINT32 | `uint32_t` |
| fixed, basic | 120 | x | INT64 | `int64_t` |
| fixed, basic | 116 | t | UINT64 | `uint64_t` |
| fixed, basic | 100 | d | DOUBLE | `double` |
| string-like, basic | 115 | s | STRING | `const char*`, `std::string` |
| string-like, basic | 111 | o | OBJECT_PATH | `sdbus::ObjectPath` |
| string-like, basic | 103 | g | SIGNATURE | `sdbus::Signature` |
| fixed, basic | 121 | y | BYTE | `uint8_t` |
| fixed, basic | 98 | b | BOOLEAN | `bool` |
| fixed, basic | 110 | n | INT16 | `int16_t` |
| fixed, basic | 113 | q | UINT16 | `uint16_t` |
| fixed, basic | 105 | i | INT32 | `int32_t` |
| fixed, basic | 117 | u | UINT32 | `uint32_t` |
| fixed, basic | 120 | x | INT64 | `int64_t` |
| fixed, basic | 116 | t | UINT64 | `uint64_t` |
| fixed, basic | 100 | d | DOUBLE | `double` |
| string-like, basic | 115 | s | STRING | `const char*`, `std::string` |
| string-like, basic | 111 | o | OBJECT_PATH | `sdbus::ObjectPath` |
| string-like, basic | 103 | g | SIGNATURE | `sdbus::Signature` |
| container | 97 | a | ARRAY | `std::vector<T>`, `std::array<T>`, `std::span<T>` - if used as an array followed by a single complete type `T` <br /> `std::map<T1, T2>`, `std::unordered_map<T1, T2>` - if used as an array of dict entries |
| container | 114,40,41 | r() | STRUCT | `sdbus::Struct<T1, T2, ...>` variadic class template |
| container | 118 | v | VARIANT | `sdbus::Variant`, `std::variant<T1, ...>` |
| container | 118 | v | VARIANT | `sdbus::Variant` |
| container | 101,123,125 | e{} | DICT_ENTRY | - |
| fixed, basic | 104 | h | UNIX_FD | `sdbus::UnixFd` |
| fixed, basic | 104 | h | UNIX_FD | `sdbus::UnixFd` |
| reserved | 109 | m | (reserved) | - |
| reserved | 42 | * | (reserved) | - |
| reserved | 63 | ? | (reserved) | - |
@@ -1579,14 +1536,14 @@ For more information on basic D-Bus types, D-Bus container types, and D-Bus type
### Extending sdbus-c++ type system
The above mapping between D-Bus and C++ types is what sdbus-c++ provides by default. However, the mapping can be extended. You can implement additional mapping between a D-Bus type and their custom type.
The above mapping between D-Bus and C++ types is what sdbus-c++ provides by default. However, the mapping can be extended. Clients can implement additional mapping between a D-Bus type and their custom type.
We need two things to do that:
* implement `sdbus::Message` insertion (serialization) and extraction (deserialization) operators, so sdbus-c++ knows how to serialize/deserialize our custom type,
* specialize `sdbus::signature_of` template for our custom type, so sdbus-c++ knows the mapping to D-Bus type and other necessary information about our type.
Say, we would like to represent D-Bus arrays as `std::list`s in our application. Since sdbus-c++ comes with pre-defined support for `std::vector`s, `std::array`s and `std::span`s as D-Bus array representations, we have to provide an extension. To implement message serialization and deserialization functions for `std::list`, we can simply copy the sdbus-c++ implementation of these functions for `std::vector`, and simply adjust for `std::list`. Then we provide `signature_of` specialization, again written in terms of one specialized for `std::vector`:
Say, we would like to represent D-Bus arrays as `std::list`s in our application. Since sdbus-c++ comes with pre-defined support for `std::vector`s, `std::array`s and `std::span`s as D-Bus array representations, we have to provide an extension. To implement message serialization and deserialization functions for `std::list`, we can simply copy the sdbus-c++ implementation of these functions for `std::vector`, and simply adjust for `std::list`. Then we provide `signature_of` specialization, again written on terms of one specialized for `std::vector`:
```c++
#include <list>
@@ -1598,7 +1555,7 @@ namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
{
msg.openContainer<_ElementType>();
msg.openContainer(sdbus::signature_of<_ElementType>::str());
for (const auto& item : items)
msg << item;
@@ -1612,7 +1569,7 @@ sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& i
template <typename _ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
{
if(!msg.enterContainer<_ElementType>())
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
return msg;
while (true)
@@ -1633,18 +1590,16 @@ sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
} // namespace sdbus
// Implementing type traits for std::list -- we re-use by inheriting
// from type traits already provided by sdbus-c++ for D-Bus arrays
// Implementing type traits for std::list, and since we map it to D-Bus array,
// we can re-use std::vector type traits because it's the same stuff.
template <typename _Element, typename _Allocator>
struct sdbus::signature_of<std::list<_Element, _Allocator>>
: sdbus::signature_of<std::vector<_Element>>
: sdbus::signature_of<std::vector<_Element, _Allocator>>
{};
```
Then we can simply use `std::list`s, serialize/deserialize them in a D-Bus message, in D-Bus method calls or return values... and they will be simply transmitted as D-Bus arrays.
Similarly, say we have our own `lockfree_map` which we would like to use natively with sdbus-c++ as a C++ type for D-Bus dictionary -- we can copy or build on top of `std::map` specializations.
As another example, say we have our custom type `my::Struct` which we'd like to use as a D-Bus structure representation (sdbus-c++ provides `sdbus::Struct` type for that, but we don't want to use it because using our custom type directly is more convenient). Again, we have to provide type traits and message serialization/deserialization functions for our custom type. We build our functions and specializations on top of `sdbus::Struct`, so we don't have to copy and write a lot of boiler-plate. Serialization/deserialization functions can be placed in the same namespace as our custom type, and will be found thanks to the ADR lookup. The `signature_of` specialization must always be in either `sdbus` namespace or in a global namespace:
```c++
@@ -1725,9 +1680,10 @@ int main(int argc, char *argv[])
// We can now use connection objects in a familiar way, e.g. create adaptor and proxy objects on them, and exchange messages.
// Here, using Concatenator IDL-generated bindings example from chapters above:
Concatenator concatenator(*serverConnection, sdbus::ObjectPath{"/org/sdbuscpp/concatenator"});
sdbus::ServiceName emptyDestination; // Destination may be empty in case of direct connections
ConcatenatorProxy concatenatorProxy(*clientConnection, std::move(emptyDestination), sdbus::ObjectPath{"/org/sdbuscpp/concatenator"});
const char* objectPath = "/org/sdbuscpp/concatenator";
Concatenator concatenator(*serverConnection, objectPath);
const char* emptyDestinationName = ""; // Destination may be empty in case of direct connections
ConcatenatorProxy concatenatorProxy(*clientConnection, emptyDestinationName, objectPath);
// Perform call of concatenate D-Bus method
std::vector<int> numbers = {1, 2, 3};
@@ -1743,75 +1699,6 @@ int main(int argc, char *argv[])
> **_Note_:** The example above explicitly stops the event loops on both sides, before the connection objects are destroyed. This avoids potential `Connection reset by peer` errors caused when one side closes its socket while the other side is still working on the counterpart socket. This is a recommended workflow for closing direct D-Bus connections.
Using sdbus-c++ in external event loops
---------------------------------------
sdbus-c++ connections can be hooked up with an external (like GMainLoop, boost::asio, etc.) or manual event loop involving `poll()` or a similar I/O polling call. The following describes how to integrate it correctly:
Before **each** invocation of the I/O polling call, `IConnection::getEventLoopPollData()` function should be invoked. Returned `PollData::fd` file descriptor should be polled for the events indicated by `PollData::events`, and the I/O call should block up to the returned `PollData::timeout`. Additionally, returned `PollData::eventFd` should be polled for POLLIN events.
After each I/O polling call (for both `PollData::fd` and `PollData::eventFd` events), the `IConnection::processPendingEvent()` method should be invoked. This enables the bus connection to process any incoming or outgoing D-Bus messages.
Note that the returned timeout should be considered only a maximum sleeping time. It is permissible (and even expected) that shorter timeouts are used by the calling program, in case other event sources are polled in the same event loop. Note that the returned time-value is absolute, based of `CLOCK_MONOTONIC` and specified in microseconds. Use `PollData::getPollTimeout()` to have the timeout value converted into a form that can be passed to `poll()`.
`PollData::fd` is a bus I/O fd. `PollData::eventFd` is an sdbus-c++ internal fd for communicating important changes from other threads to the event loop thread, so the event loop retrieves new poll data (with updated timeout, for example) and, potentially, processes pending D-Bus messages (like signals that came in during a blocking synchronous call from other thread, or queued outgoing messages that are very big to be able to have been sent in one shot from another thread), before the next poll.
Consult `IConnection::PollData` and `IConnection::getEventLoopPollData()` documentation for potentially more information.
### Integration of sd-event event loop
sdbus-c++ provides built-in integration of sd-event, which makes it very convenient to hook sdbus-c++ connection up with an sd-event event loop.
See documentation of `IConnection::attachSdEventLoop()`, `IConnection::detachSdEventLoop()`, and `IConnection::getSdEventLoop()` methods, or sdbus-c++ integration tests for an example of use. These methods are sdbus-c++ counterparts to and mimic the behavior of these underlying sd-bus functions: `sd_bus_attach_event()`, `sd_bus_detach_event()`, and `sd_bus_get_event()`. Their manual pages provide much more details about their behavior.
Migrating to sdbus-c++ v2
-------------------------
sdbus-c++ v2 is a major release that comes with a number of breaking API/ABI/behavior changes compared to v1. The following list describes the changes:
* Change in behavior: In *synchronous* D-Bus calls, the proxy object now keeps the connection instance blocked for the entire duration of the method call. Incoming messages like signals will be queued and processed after the call. Access to the connection from other threads is blocked. To avoid this (in case this hurts you):
* either use short-lived, light-weight proxies for such synchronous calls,
* or call the method in an asynchronous way.
* Strong types were introduced for safer, less error-prone and more expressive API. What previously was `auto proxy = createProxy("org.sdbuscpp.concatenator", "/org/sdbuscpp/concatenator");` is now written like `auto proxy = createProxy(ServiceName{"org.sdbuscpp.concatenator"}, ObjectPath{"/org/sdbuscpp/concatenator"});`. These types are:
* `ObjectPath` type for the object path (the type has been around already but now is also used consistently in sdbus-c++ API for object path strings)
* `InterfaceName` type for D-Bus interface names
* `BusName` (and its aliases `ServiceName` and `ConnectionName`) type for bus/service/connection names
* `MemberName` (and its aliases `MethodName`, `SignalName` and `PropertyName`) type for D-Bus method, signal and property names
* `Signature` type for the D-Bus signature (the type has been around already but now is also used consistently in sdbus-c++ API for signature strings)
* `Error::Name` type for D-Bus error names
* Signatures of callbacks `async_reply_handler`, `signal_handler`, `message_handler` and `property_set_callback` were modified to take input message objects by value instead of non-const ref to a message. The callback handler assumes ownership of the message. This API is cleaner and more self-explaining.
* The `PollData` struct has been extended with a new data member: `eventFd`. All hooks with external event loops shall be modified to poll on this `eventFd` in addition to the `fd`.
* `PollData::timeout_usec` was renamed to `PollData::timeout` and its type has been changed to `std::chrono::microseconds`. This member now holds directly what before had to be obtained through `PollData::getAbsoluteTimeout()` call.
* `PollData::getRelativeTimeout()` return type was changed to `std::chrono::microseconds`.
* `IConnection::processPendingRequest()` was renamed to `IConnection::processPendingEvent()`.
* `Variant` constructor is now explicit.
* `Variant`'s conversion operator to the underlying type is now explicit.
* `IProxy::getCurrentlyProcessedMessage()` now returns `Message` by value instead of a raw pointer to it. The caller assumes ownership of the message.
* Object D-Bus API registration is now done through `IObject::addVTable()` method. The vtable gets active immediately. No `finishRegistration()` call is needed anymore. vtables can be added and removed dynamically at run time. In addition to API simplification this brings consistency with sd-bus API and increases flexibility.
* Subscription to signals has been simplified. The subscription is active right after the `registerSignalHandler`/`uponSignal()` call. No need for the final call to `finishRegistration()`.
* `IProxy::muteSignal()` and `IProxy::unregisterSignal()` have been removed. When subscribing to a signal, we can ask sdbus-c++ to give us a RAII-based slot object. As long as we keep the slot, the subscription is active. Destroying the slot object implies unsubscribing from the signal.
* `request_slot` tag was renamed to `return_slot`.
* Deprecated `dont_request_slot` was removed. It shall be replaced with `floating_slot`.
* `ProxyInterfaces::getObjectPath()` was removed. It shall be replaced with `ProxyInterfaces::getProxy().getObjectPath()`.
* `AdaptorInterfaces::getObjectPath()` was removed. It can be replaced with `AdaptorInterfaces::getObject().getObjectPath()`.
* `createConnection()` has been removed. To create a connection to the system bus use `createSystemConnection()` instead.
* `createDefaultBusConnection()` has been renamed to `createBusConnection()`.
* `IObject::removeObjectManager()` and `IObject::hasObjectManager()` were removed. Clients should now use the slot-returning `IObject::addObjectManager()` to control the `ObjectManager` interface lifetime.
* `floating_slot_t` tag was removed from `IConnection::addObjectManager()`, the function is now by default floating-slot-based.
* Slot-returning `IConnection::addMatch()` has gotten the `return_slot_t` tag parameter, while `floating_slot_t` was removed from the floating slot-based overload of the method.
* Slot-returning `IConnection::addMatchAsync()` has gotten the `return_slot_t` tag parameter, while `floating_slot_t` was removed from the floating slot-based overload of the method.
* Change in behavior: `Proxy`s now by default call `createBusConnection()` to get a connection when the connection is not provided explicitly by the caller, so they connect to either the session bus or the system bus depending on the context (as opposed to always to the system bus like before).
* Callbacks taking `const sdbus::Error* error` were changed to take `std::optional<sdbus::Error>`, which better expresses the intent and meaning.
* `getInterfaceName()`, `getMemberName()`, `getSender()`, `getPath()` and `getDestination()` methods of `Message` class now return `const char*` instead of `std::string`, for efficiency reasons.
* `peekType()` method of `Message` class now returns a pair of `char` (type signature) and `const char*` (contents signature), for expressiveness and efficiency reasons.
* D-Bus signatures when using high-level API are now assembled at compile time. There are breaking changes inside `signature_of` type traits and `Message` serialization/deserialization methods. This only interests you if you extend sdbus-c++ type system with your own types. See the updated tutorial on extending sdbus-c++ type system.
* Generated adaptor and proxy classes are not moveable anymore.
* Types and methods marked deprecated in sdbus-c++ v1 were removed completely.
* CMake options got `SDBUSCPP_` prefix for better usability and minimal risk of conflicts in downstream CMake projects. `SDBUSCPP_INSTALL` CMake option was added.
* CMake components got `sdbus-c++-` prefix.
An important note: **C++ bindings generated from XML files must be re-generated** with the new `sdbus-c++-xml2cpp` when migrating to sdbus-c++ v2.0.
Conclusion
----------
+1 -7
View File
@@ -1,12 +1,6 @@
# Building examples
add_executable(obj-manager-server org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp)
target_link_libraries(obj-manager-server sdbus-c++)
add_executable(obj-manager-client org.freedesktop.DBus.ObjectManager/obj-manager-client.cpp)
target_link_libraries(obj-manager-client sdbus-c++)
if(SDBUSCPP_INSTALL)
install(TARGETS obj-manager-server DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-examples)
install(TARGETS obj-manager-client DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-examples)
endif()
target_link_libraries(obj-manager-client sdbus-c++)
@@ -21,37 +21,28 @@ public:
protected:
Planet1_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(proxy)
{
}
Planet1_proxy(const Planet1_proxy&) = delete;
Planet1_proxy& operator=(const Planet1_proxy&) = delete;
Planet1_proxy(Planet1_proxy&&) = delete;
Planet1_proxy& operator=(Planet1_proxy&&) = delete;
~Planet1_proxy() = default;
void registerProxy()
{
}
public:
uint64_t GetPopulation()
{
uint64_t result;
m_proxy.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
public:
std::string Name()
{
return m_proxy.getProperty("Name").onInterface(INTERFACE_NAME).get<std::string>();
return proxy_.getProperty("Name").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy& proxy_;
};
}}} // namespaces
@@ -21,24 +21,14 @@ public:
protected:
Planet1_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(object)
{
object_.registerMethod("GetPopulation").onInterface(INTERFACE_NAME).withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); });
object_.registerProperty("Name").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Name(); });
}
Planet1_adaptor(const Planet1_adaptor&) = delete;
Planet1_adaptor& operator=(const Planet1_adaptor&) = delete;
Planet1_adaptor(Planet1_adaptor&&) = delete;
Planet1_adaptor& operator=(Planet1_adaptor&&) = delete;
~Planet1_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable( sdbus::registerMethod("GetPopulation").withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); })
, sdbus::registerProperty("Name").withGetter([this](){ return this->Name(); })
).forInterface(INTERFACE_NAME);
}
private:
virtual uint64_t GetPopulation() = 0;
@@ -46,7 +36,7 @@ private:
virtual std::string Name() = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject& object_;
};
}}} // namespaces
@@ -17,7 +17,7 @@
class PlanetProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::ExampleManager::Planet1_proxy >
{
public:
PlanetProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath path)
PlanetProxy(sdbus::IConnection& connection, std::string destination, std::string path)
: ProxyInterfaces(connection, std::move(destination), std::move(path))
{
registerProxy();
@@ -29,13 +29,13 @@ public:
}
};
class ManagerProxy final : public sdbus::ProxyInterfaces<sdbus::ObjectManager_proxy>
class ManagerProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
{
public:
ManagerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath path)
: ProxyInterfaces(connection, destination, std::move(path))
, m_connection(connection)
, m_destination(destination)
ManagerProxy(sdbus::IConnection& connection, const std::string& destination, std::string path)
: ProxyInterfaces(connection, destination, std::move(path))
, m_connection(connection)
, m_destination(destination)
{
registerProxy();
}
@@ -47,7 +47,8 @@ public:
void handleExistingObjects()
{
auto objectsInterfacesAndProperties = GetManagedObjects();
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
objectsInterfacesAndProperties = GetManagedObjects();
for (const auto& [object, interfacesAndProperties] : objectsInterfacesAndProperties) {
onInterfacesAdded(object, interfacesAndProperties);
}
@@ -55,7 +56,7 @@ public:
private:
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties) override
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
{
std::cout << objectPath << " added:\t";
for (const auto& [interface, _] : interfacesAndProperties) {
@@ -64,20 +65,20 @@ private:
std::cout << std::endl;
// Parse and print some more info
auto planetInterface = interfacesAndProperties.find(sdbus::InterfaceName{org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME});
auto planetInterface = interfacesAndProperties.find(org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME);
if (planetInterface == interfacesAndProperties.end()) {
return;
}
const auto& properties = planetInterface->second;
// get a property which was passed as part of the signal.
const auto& name = properties.at(sdbus::PropertyName{"Name"}).get<std::string>();
const auto& name = properties.at("Name").get<std::string>();
// or create a proxy instance to the newly added object.
PlanetProxy planet(m_connection, m_destination, objectPath);
std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << std::endl;
}
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<sdbus::InterfaceName>& interfaces) override
, const std::vector<std::string>& interfaces) override
{
std::cout << objectPath << " removed:\t";
for (const auto& interface : interfaces) {
@@ -87,16 +88,14 @@ private:
}
sdbus::IConnection& m_connection;
sdbus::ServiceName m_destination;
std::string m_destination;
};
int main()
{
auto connection = sdbus::createSessionBusConnection();
sdbus::ServiceName destination{"org.sdbuscpp.examplemanager"};
sdbus::ObjectPath objectPath{"/org/sdbuscpp/examplemanager"};
auto managerProxy = std::make_unique<ManagerProxy>(*connection, std::move(destination), std::move(objectPath));
auto managerProxy = std::make_unique<ManagerProxy>(*connection, "org.sdbuscpp.examplemanager", "/org/sdbuscpp/examplemanager");
try {
managerProxy->handleExistingObjects();
}
@@ -108,4 +107,4 @@ int main()
connection->enterEventLoop();
return 0;
}
}
@@ -19,13 +19,11 @@
#include <thread>
#include <chrono>
using sdbus::ObjectPath;
class ManagerAdaptor : public sdbus::AdaptorInterfaces<sdbus::ObjectManager_adaptor>
class ManagerAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
{
public:
ManagerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path)
: AdaptorInterfaces(connection, std::move(path))
ManagerAdaptor(sdbus::IConnection& connection, std::string path)
: AdaptorInterfaces(connection, std::move(path))
{
registerAdaptor();
}
@@ -36,23 +34,23 @@ public:
}
};
class PlanetAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor
, sdbus::ManagedObject_adaptor
, sdbus::Properties_adaptor >
class PlanetAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor,
sdbus::ManagedObject_adaptor,
sdbus::Properties_adaptor >
{
public:
PlanetAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path, std::string name, uint64_t population)
: AdaptorInterfaces(connection, std::move(path))
, m_name(std::move(name))
, m_population(population)
PlanetAdaptor(sdbus::IConnection& connection, std::string path, std::string name, uint64_t poulation)
: AdaptorInterfaces(connection, std::move(path))
, m_name(std::move(name))
, m_population(poulation)
{
registerAdaptor();
emitInterfacesAddedSignal({sdbus::InterfaceName{org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME}});
emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
}
~PlanetAdaptor()
{
emitInterfacesRemovedSignal({sdbus::InterfaceName{org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME}});
emitInterfacesRemovedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
unregisterAdaptor();
}
@@ -84,19 +82,18 @@ void printCountDown(const std::string& message, int seconds)
int main()
{
auto connection = sdbus::createSessionBusConnection();
sdbus::ServiceName serviceName{"org.sdbuscpp.examplemanager"};
connection->requestName(serviceName);
connection->requestName("org.sdbuscpp.examplemanager");
connection->enterEventLoopAsync();
auto manager = std::make_unique<ManagerAdaptor>(*connection, ObjectPath{"/org/sdbuscpp/examplemanager"});
auto manager = std::make_unique<ManagerAdaptor>(*connection, "/org/sdbuscpp/examplemanager");
while (true)
{
printCountDown("Creating PlanetAdaptor in ", 5);
auto earth = std::make_unique<PlanetAdaptor>(*connection, ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Earth"}, "Earth", 7'874'965'825);
auto earth = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Earth", "Earth", 7'874'965'825);
printCountDown("Creating PlanetAdaptor in ", 5);
auto trantor = std::make_unique<PlanetAdaptor>(*connection, ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Trantor"}, "Trantor", 40'000'000'000);
auto trantor = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Trantor", "Trantor", 40'000'000'000);
printCountDown("Creating PlanetAdaptor in ", 5);
auto laconia = std::make_unique<PlanetAdaptor>(*connection, ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Laconia"}, "Laconia", 231'721);
auto laconia = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Laconia", "Laconia", 231'721);
printCountDown("Removing PlanetAdaptor in ", 5);
earth.reset();
printCountDown("Removing PlanetAdaptor in ", 5);
@@ -105,7 +102,7 @@ int main()
laconia.reset();
}
connection->releaseName(serviceName);
connection->releaseName("org.sdbuscpp.examplemanager");
connection->leaveEventLoop();
return 0;
}
}
+15 -13
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorInterfaces.h
*
@@ -82,10 +82,9 @@ namespace sdbus {
* adaptor-side interface classes representing interfaces (with methods, signals and properties)
* of the D-Bus object.
*
* In the final adaptor class inherited from AdaptorInterfaces, one needs to make sure:
* 1. to call `registerAdaptor();` in the class constructor, and, conversely,
* 2. to call `unregisterAdaptor();` in the class destructor,
* so that the object API vtable is registered and unregistered at the proper time.
* 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>
@@ -102,22 +101,22 @@ namespace sdbus {
*
* For more information, consult @ref createObject(sdbus::IConnection&,std::string)
*/
AdaptorInterfaces(IConnection& connection, ObjectPath objectPath)
AdaptorInterfaces(IConnection& connection, std::string objectPath)
: ObjectHolder(createObject(connection, std::move(objectPath)))
, _Interfaces(getObject())...
{
}
/*!
* @brief Adds object vtable (i.e. D-Bus API) definitions for all interfaces it implements
* @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.
*
* See also @ref IObject::addVTable()
* For more information, see underlying @ref IObject::finishRegistration()
*/
void registerAdaptor()
{
(_Interfaces::registerAdaptor(), ...);
getObject().finishRegistration();
}
/*!
@@ -133,17 +132,20 @@ namespace sdbus {
}
/*!
* @brief Returns reference to the underlying IObject instance
* @brief Returns object path of the underlying DBus object
*/
using ObjectHolder::getObject;
const std::string& getObjectPath() const
{
return getObject().getObjectPath();
}
protected:
using base_type = AdaptorInterfaces;
AdaptorInterfaces(const AdaptorInterfaces&) = delete;
AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete;
AdaptorInterfaces(AdaptorInterfaces&&) = delete;
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = delete;
AdaptorInterfaces(AdaptorInterfaces&&) = default;
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = default;
~AdaptorInterfaces() = default;
};
+143 -115
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ConvenienceApiClasses.h
*
@@ -30,15 +30,13 @@
#include <sdbus-c++/Message.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Types.h>
#include <sdbus-c++/VTableItems.h>
#include <chrono>
#include <cstdint>
#include <future>
#include <map>
#include <sdbus-c++/Flags.h>
#include <string>
#include <string_view>
#include <vector>
#include <type_traits>
#include <chrono>
#include <future>
#include <cstdint>
// Forward declarations
namespace sdbus {
@@ -50,42 +48,115 @@ namespace sdbus {
namespace sdbus {
class VTableAdder
class MethodRegistrator
{
public:
void forInterface(InterfaceName interfaceName);
void forInterface(std::string interfaceName);
[[nodiscard]] Slot forInterface(InterfaceName interfaceName, return_slot_t);
[[nodiscard]] Slot forInterface(std::string interfaceName, return_slot_t);
MethodRegistrator(IObject& object, std::string methodName);
MethodRegistrator(MethodRegistrator&& other) = default;
~MethodRegistrator() noexcept(false);
private:
friend IObject;
VTableAdder(IObject& object, std::vector<VTableItem> vtable);
MethodRegistrator& onInterface(std::string interfaceName);
template <typename _Function> MethodRegistrator& implementedAs(_Function&& callback);
MethodRegistrator& withInputParamNames(std::vector<std::string> paramNames);
template <typename... _String> MethodRegistrator& withInputParamNames(_String... paramNames);
MethodRegistrator& withOutputParamNames(std::vector<std::string> paramNames);
template <typename... _String> MethodRegistrator& withOutputParamNames(_String... paramNames);
MethodRegistrator& markAsDeprecated();
MethodRegistrator& markAsPrivileged();
MethodRegistrator& withNoReply();
private:
IObject& object_;
std::vector<VTableItem> vtable_;
std::string methodName_;
std::string interfaceName_;
std::string inputSignature_;
std::vector<std::string> inputParamNames_;
std::string outputSignature_;
std::vector<std::string> outputParamNames_;
method_callback methodCallback_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
};
class SignalRegistrator
{
public:
SignalRegistrator(IObject& object, std::string signalName);
SignalRegistrator(SignalRegistrator&& other) = default;
~SignalRegistrator() noexcept(false);
SignalRegistrator& onInterface(std::string interfaceName);
template <typename... _Args> SignalRegistrator& withParameters();
template <typename... _Args> SignalRegistrator& withParameters(std::vector<std::string> paramNames);
template <typename... _Args, typename... _String> SignalRegistrator& withParameters(_String... paramNames);
SignalRegistrator& markAsDeprecated();
private:
IObject& object_;
std::string signalName_;
std::string interfaceName_;
std::string signalSignature_;
std::vector<std::string> paramNames_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
};
class PropertyRegistrator
{
public:
PropertyRegistrator(IObject& object, const std::string& propertyName);
PropertyRegistrator(PropertyRegistrator&& other) = default;
~PropertyRegistrator() noexcept(false);
PropertyRegistrator& onInterface(std::string interfaceName);
template <typename _Function> PropertyRegistrator& withGetter(_Function&& callback);
template <typename _Function> PropertyRegistrator& withSetter(_Function&& callback);
PropertyRegistrator& markAsDeprecated();
PropertyRegistrator& markAsPrivileged();
PropertyRegistrator& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
private:
IObject& object_;
const std::string& propertyName_;
std::string interfaceName_;
std::string propertySignature_;
property_get_callback getter_;
property_set_callback setter_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when PropertyRegistrator is constructed
};
class InterfaceFlagsSetter
{
public:
InterfaceFlagsSetter(IObject& object, const std::string& interfaceName);
InterfaceFlagsSetter(InterfaceFlagsSetter&& other) = default;
~InterfaceFlagsSetter() noexcept(false);
InterfaceFlagsSetter& markAsDeprecated();
InterfaceFlagsSetter& markAsPrivileged();
InterfaceFlagsSetter& withNoReplyMethods();
InterfaceFlagsSetter& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
private:
IObject& object_;
const std::string& interfaceName_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when InterfaceFlagsSetter is constructed
};
class SignalEmitter
{
public:
SignalEmitter& onInterface(const InterfaceName& interfaceName);
SignalEmitter& onInterface(const std::string& interfaceName);
SignalEmitter& onInterface(const char* interfaceName);
template <typename... _Args> void withArguments(_Args&&... args);
SignalEmitter(IObject& object, const std::string& signalName);
SignalEmitter(SignalEmitter&& other) = default;
~SignalEmitter() noexcept(false);
private:
friend IObject;
SignalEmitter(IObject& object, const SignalName& signalName);
SignalEmitter(IObject& object, const char* signalName);
SignalEmitter& onInterface(const std::string& interfaceName);
template <typename... _Args> void withArguments(_Args&&... args);
private:
IObject& object_;
const char* signalName_;
const std::string& signalName_;
Signal signal_;
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
};
@@ -93,27 +164,22 @@ namespace sdbus {
class MethodInvoker
{
public:
MethodInvoker& onInterface(const InterfaceName& interfaceName);
MethodInvoker(IProxy& proxy, const std::string& methodName);
MethodInvoker(MethodInvoker&& other) = default;
~MethodInvoker() noexcept(false);
MethodInvoker& onInterface(const std::string& interfaceName);
MethodInvoker& onInterface(const char* interfaceName);
MethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
MethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
template <typename... _Args> void storeResultsTo(_Args&... args);
void dontExpectReply();
MethodInvoker(MethodInvoker&& other) = default;
~MethodInvoker() noexcept(false);
private:
friend IProxy;
MethodInvoker(IProxy& proxy, const MethodName& methodName);
MethodInvoker(IProxy& proxy, const char* methodName);
private:
IProxy& proxy_;
const char* methodName_;
const std::string& methodName_;
uint64_t timeout_{};
MethodCall method_;
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
@@ -123,29 +189,21 @@ namespace sdbus {
class AsyncMethodInvoker
{
public:
AsyncMethodInvoker& onInterface(const InterfaceName& interfaceName);
AsyncMethodInvoker(IProxy& proxy, const std::string& methodName);
AsyncMethodInvoker& onInterface(const std::string& interfaceName);
AsyncMethodInvoker& onInterface(const char* interfaceName);
AsyncMethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
// Returned future will be std::future<void> for no (void) D-Bus method return value
// or std::future<T> for single D-Bus method return value
// or std::future<std::tuple<...>> for multiple method return values
template <typename... _Args> std::future<future_return_t<_Args...>> getResultAsFuture();
private:
friend IProxy;
AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName);
AsyncMethodInvoker(IProxy& proxy, const char* methodName);
template <typename _Function> async_reply_handler makeAsyncReplyHandler(_Function&& callback);
private:
IProxy& proxy_;
const char* methodName_;
const std::string& methodName_;
uint64_t timeout_{};
MethodCall method_;
};
@@ -153,114 +211,90 @@ namespace sdbus {
class SignalSubscriber
{
public:
SignalSubscriber& onInterface(const InterfaceName& interfaceName);
SignalSubscriber& onInterface(const std::string& interfaceName);
SignalSubscriber& onInterface(const char* interfaceName);
SignalSubscriber(IProxy& proxy, const std::string& signalName);
SignalSubscriber& onInterface(std::string interfaceName);
template <typename _Function> void call(_Function&& callback);
template <typename _Function> [[nodiscard]] Slot call(_Function&& callback, return_slot_t);
private:
friend IProxy;
SignalSubscriber(IProxy& proxy, const SignalName& signalName);
SignalSubscriber(IProxy& proxy, const char* signalName);
template <typename _Function> signal_handler makeSignalHandler(_Function&& callback);
private:
IProxy& proxy_;
const char* signalName_;
const char* interfaceName_{};
const std::string& signalName_;
std::string interfaceName_;
};
class SignalUnsubscriber
{
public:
SignalUnsubscriber(IProxy& proxy, const std::string& signalName);
void onInterface(const std::string& interfaceName);
private:
IProxy& proxy_;
const std::string& signalName_;
};
class PropertyGetter
{
public:
Variant onInterface(std::string_view interfaceName);
private:
friend IProxy;
PropertyGetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
PropertyGetter(IProxy& proxy, const std::string& propertyName);
Variant onInterface(const std::string& interfaceName);
private:
IProxy& proxy_;
std::string_view propertyName_;
const std::string& propertyName_;
};
class AsyncPropertyGetter
{
public:
AsyncPropertyGetter& onInterface(std::string_view interfaceName);
AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName);
AsyncPropertyGetter& onInterface(const std::string& interfaceName);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
std::future<Variant> getResultAsFuture();
private:
friend IProxy;
AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
private:
IProxy& proxy_;
std::string_view propertyName_;
std::string_view interfaceName_;
const std::string& propertyName_;
const std::string* interfaceName_{};
};
class PropertySetter
{
public:
PropertySetter& onInterface(std::string_view interfaceName);
PropertySetter(IProxy& proxy, const std::string& propertyName);
PropertySetter& onInterface(const std::string& interfaceName);
template <typename _Value> void toValue(const _Value& value);
template <typename _Value> void toValue(const _Value& value, dont_expect_reply_t);
void toValue(const Variant& value);
void toValue(const Variant& value, dont_expect_reply_t);
private:
friend IProxy;
PropertySetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
private:
IProxy& proxy_;
std::string_view propertyName_;
std::string_view interfaceName_;
const std::string& propertyName_;
const std::string* interfaceName_{};
};
class AsyncPropertySetter
{
public:
AsyncPropertySetter& onInterface(std::string_view interfaceName);
AsyncPropertySetter(IProxy& proxy, const std::string& propertyName);
AsyncPropertySetter& onInterface(const std::string& interfaceName);
template <typename _Value> AsyncPropertySetter& toValue(_Value&& value);
AsyncPropertySetter& toValue(Variant value);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
std::future<void> getResultAsFuture();
private:
friend IProxy;
AsyncPropertySetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
private:
IProxy& proxy_;
std::string_view propertyName_;
std::string_view interfaceName_;
const std::string& propertyName_;
const std::string* interfaceName_{};
Variant value_;
};
class AllPropertiesGetter
{
public:
std::map<PropertyName, Variant> onInterface(std::string_view interfaceName);
private:
friend IProxy;
AllPropertiesGetter(IProxy& proxy);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
std::map<std::string, Variant> onInterface(const std::string& interfaceName);
private:
IProxy& proxy_;
@@ -269,22 +303,16 @@ namespace sdbus {
class AsyncAllPropertiesGetter
{
public:
AsyncAllPropertiesGetter& onInterface(std::string_view interfaceName);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
template <typename _Function> [[nodiscard]] Slot uponReplyInvoke(_Function&& callback, return_slot_t);
std::future<std::map<PropertyName, Variant>> getResultAsFuture();
private:
friend IProxy;
AsyncAllPropertiesGetter(IProxy& proxy);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
AsyncAllPropertiesGetter& onInterface(const std::string& interfaceName);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
std::future<std::map<std::string, Variant>> getResultAsFuture();
private:
IProxy& proxy_;
std::string_view interfaceName_;
const std::string* interfaceName_{};
};
} // namespace sdbus
}
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */
+453 -236
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ConvenienceApiClasses.inl
*
@@ -27,62 +27,395 @@
#ifndef SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
#define SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Types.h>
#include <cassert>
#include <exception>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Error.h>
#include <string>
#include <tuple>
#include <type_traits>
#include <exception>
#include <cassert>
namespace sdbus {
/*** ------------- ***/
/*** VTableAdder ***/
/*** ------------- ***/
/*** ----------------- ***/
/*** MethodRegistrator ***/
/*** ----------------- ***/
inline VTableAdder::VTableAdder(IObject& object, std::vector<VTableItem> vtable)
inline MethodRegistrator::MethodRegistrator(IObject& object, std::string methodName)
: object_(object)
, vtable_(std::move(vtable))
, methodName_(std::move(methodName))
, exceptions_(std::uncaught_exceptions())
{
}
inline void VTableAdder::forInterface(InterfaceName interfaceName)
{
object_.addVTable(std::move(interfaceName), std::move(vtable_));
inline MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't register the method if MethodRegistrator threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(methodCallback_); // implementedAs() must be placed/called prior to this function
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerMethod( interfaceName_
, std::move(methodName_)
, std::move(inputSignature_)
, inputParamNames_
, std::move(outputSignature_)
, outputParamNames_
, std::move(methodCallback_)
, std::move(flags_));
}
inline void VTableAdder::forInterface(std::string interfaceName)
inline MethodRegistrator& MethodRegistrator::onInterface(std::string interfaceName)
{
forInterface(InterfaceName{std::move(interfaceName)});
interfaceName_ = std::move(interfaceName);
return *this;
}
[[nodiscard]] inline Slot VTableAdder::forInterface(InterfaceName interfaceName, return_slot_t)
template <typename _Function>
MethodRegistrator& MethodRegistrator::implementedAs(_Function&& callback)
{
return object_.addVTable(std::move(interfaceName), std::move(vtable_), return_slot);
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
methodCallback_ = [callback = std::forward<_Function>(callback)](MethodCall call)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the message.
tuple_of_function_input_arg_types_t<_Function> inputArgs;
// Deserialize input arguments from the message into the tuple.
call >> inputArgs;
if constexpr (!is_async_method_v<_Function>)
{
// Invoke callback with input arguments from the tuple.
auto ret = sdbus::apply(callback, inputArgs);
// Store output arguments to the reply message and send it back.
auto reply = call.createReply();
reply << ret;
reply.send();
}
else
{
// Invoke callback with input arguments from the tuple and with result object to be set later
using AsyncResult = typename function_traits<_Function>::async_result_t;
sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
}
};
return *this;
}
[[nodiscard]] inline Slot VTableAdder::forInterface(std::string interfaceName, return_slot_t)
inline MethodRegistrator& MethodRegistrator::withInputParamNames(std::vector<std::string> paramNames)
{
return forInterface(InterfaceName{std::move(interfaceName)}, return_slot);
inputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline MethodRegistrator& MethodRegistrator::withInputParamNames(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withInputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(std::vector<std::string> paramNames)
{
outputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withOutputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
inline MethodRegistrator& MethodRegistrator::markAsPrivileged()
{
flags_.set(Flags::PRIVILEGED);
return *this;
}
inline MethodRegistrator& MethodRegistrator::withNoReply()
{
flags_.set(Flags::METHOD_NO_REPLY);
return *this;
}
/*** ----------------- ***/
/*** SignalRegistrator ***/
/*** ----------------- ***/
inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
: object_(object)
, signalName_(std::move(signalName))
, exceptions_(std::uncaught_exceptions())
{
}
inline SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerSignal( interfaceName_
, std::move(signalName_)
, std::move(signalSignature_)
, paramNames_
, std::move(flags_) );
}
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
{
interfaceName_ = std::move(interfaceName);
return *this;
}
template <typename... _Args>
inline SignalRegistrator& SignalRegistrator::withParameters()
{
signalSignature_ = signature_of_function_input_arguments<void(_Args...)>::str();
return *this;
}
template <typename... _Args>
inline SignalRegistrator& SignalRegistrator::withParameters(std::vector<std::string> paramNames)
{
paramNames_ = std::move(paramNames);
return withParameters<_Args...>();
}
template <typename... _Args, typename... _String>
inline SignalRegistrator& SignalRegistrator::withParameters(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
return withParameters<_Args...>({paramNames...});
}
inline SignalRegistrator& SignalRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
/*** ------------------- ***/
/*** PropertyRegistrator ***/
/*** ------------------- ***/
inline PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
: object_(object)
, propertyName_(propertyName)
, exceptions_(std::uncaught_exceptions())
{
}
inline PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerProperty() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerProperty( interfaceName_
, propertyName_
, propertySignature_
, std::move(getter_)
, std::move(setter_)
, flags_ );
}
inline PropertyRegistrator& PropertyRegistrator::onInterface(std::string interfaceName)
{
interfaceName_ = std::move(interfaceName);
return *this;
}
template <typename _Function>
inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
{
static_assert(function_argument_count_v<_Function> == 0, "Property getter function must not take any arguments");
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
if (propertySignature_.empty())
propertySignature_ = signature_of_function_output_arguments<_Function>::str();
getter_ = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply)
{
// Get the propety value and serialize it into the pre-constructed reply message
reply << callback();
};
return *this;
}
template <typename _Function>
inline PropertyRegistrator& PropertyRegistrator::withSetter(_Function&& callback)
{
static_assert(function_argument_count_v<_Function> == 1, "Property setter function must take one parameter - the property value");
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
if (propertySignature_.empty())
propertySignature_ = signature_of_function_input_arguments<_Function>::str();
setter_ = [callback = std::forward<_Function>(callback)](PropertySetCall& call)
{
// Default-construct property value
using property_type = function_argument_t<_Function, 0>;
std::decay_t<property_type> property;
// Deserialize property value from the incoming call message
call >> property;
// Invoke setter with the value
callback(property);
};
return *this;
}
inline PropertyRegistrator& PropertyRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
inline PropertyRegistrator& PropertyRegistrator::markAsPrivileged()
{
flags_.set(Flags::PRIVILEGED);
return *this;
}
inline PropertyRegistrator& PropertyRegistrator::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
{
flags_.set(behavior);
return *this;
}
/*** -------------------- ***/
/*** InterfaceFlagsSetter ***/
/*** -------------------- ***/
inline InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
: object_(object)
, interfaceName_(interfaceName)
, exceptions_(std::uncaught_exceptions())
{
}
inline InterfaceFlagsSetter::~InterfaceFlagsSetter() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't set any flags if InterfaceFlagsSetter threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow setInterfaceFlags() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.setInterfaceFlags(interfaceName_, std::move(flags_));
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsPrivileged()
{
flags_.set(Flags::PRIVILEGED);
return *this;
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::withNoReplyMethods()
{
flags_.set(Flags::METHOD_NO_REPLY);
return *this;
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
{
flags_.set(behavior);
return *this;
}
/*** ------------- ***/
/*** SignalEmitter ***/
/*** ------------- ***/
inline SignalEmitter::SignalEmitter(IObject& object, const SignalName& signalName)
: SignalEmitter(object, signalName.c_str())
{
}
inline SignalEmitter::SignalEmitter(IObject& object, const char* signalName)
inline SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
: object_(object)
, signalName_(signalName)
, exceptions_(std::uncaught_exceptions())
@@ -107,17 +440,7 @@ namespace sdbus {
object_.emitSignal(signal_);
}
inline SignalEmitter& SignalEmitter::onInterface(const InterfaceName& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline SignalEmitter& SignalEmitter::onInterface(const char* interfaceName)
{
signal_ = object_.createSignal(interfaceName, signalName_);
@@ -136,12 +459,7 @@ namespace sdbus {
/*** MethodInvoker ***/
/*** ------------- ***/
inline MethodInvoker::MethodInvoker(IProxy& proxy, const MethodName& methodName)
: MethodInvoker(proxy, methodName.c_str())
{
}
inline MethodInvoker::MethodInvoker(IProxy& proxy, const char* methodName)
inline MethodInvoker::MethodInvoker(IProxy& proxy, const std::string& methodName)
: proxy_(proxy)
, methodName_(methodName)
, exceptions_(std::uncaught_exceptions())
@@ -167,17 +485,7 @@ namespace sdbus {
proxy_.callMethod(method_, timeout_);
}
inline MethodInvoker& MethodInvoker::onInterface(const InterfaceName& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline MethodInvoker& MethodInvoker::onInterface(const char* interfaceName)
{
method_ = proxy_.createMethodCall(interfaceName, methodName_);
@@ -230,28 +538,13 @@ namespace sdbus {
/*** AsyncMethodInvoker ***/
/*** ------------------ ***/
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName)
: AsyncMethodInvoker(proxy, methodName.c_str())
{
}
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const char* methodName)
inline AsyncMethodInvoker::AsyncMethodInvoker(IProxy& proxy, const std::string& methodName)
: proxy_(proxy)
, methodName_(methodName)
{
}
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const InterfaceName& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const std::string& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline AsyncMethodInvoker& AsyncMethodInvoker::onInterface(const char* interfaceName)
{
method_ = proxy_.createMethodCall(interfaceName, methodName_);
@@ -287,31 +580,14 @@ namespace sdbus {
{
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync(method_, makeAsyncReplyHandler(std::forward<_Function>(callback)), timeout_);
}
template <typename _Function>
[[nodiscard]] Slot AsyncMethodInvoker::uponReplyInvoke(_Function&& callback, return_slot_t)
{
assert(method_.isValid()); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync( method_
, makeAsyncReplyHandler(std::forward<_Function>(callback))
, timeout_
, return_slot );
}
template <typename _Function>
inline async_reply_handler AsyncMethodInvoker::makeAsyncReplyHandler(_Function&& callback)
{
return [callback = std::forward<_Function>(callback)](MethodReply reply, std::optional<Error> error)
auto asyncReplyHandler = [callback = std::forward<_Function>(callback)](MethodReply& reply, const Error* error)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the message.
tuple_of_function_input_arg_types_t<_Function> args;
// Deserialize input arguments from the message into the tuple (if no error occurred).
if (!error)
if (error == nullptr)
{
try
{
@@ -321,14 +597,16 @@ namespace sdbus {
{
// Pass message deserialization exceptions to the client via callback error parameter,
// instead of propagating them up the message loop call stack.
sdbus::apply(callback, e, args);
sdbus::apply(callback, &e, args);
return;
}
}
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, std::move(error), args);
sdbus::apply(callback, error, args);
};
return proxy_.callMethod(method_, std::move(asyncReplyHandler), timeout_);
}
template <typename... _Args>
@@ -337,15 +615,15 @@ namespace sdbus {
auto promise = std::make_shared<std::promise<future_return_t<_Args...>>>();
auto future = promise->get_future();
uponReplyInvoke([promise = std::move(promise)](std::optional<Error> error, _Args... args)
uponReplyInvoke([promise = std::move(promise)](const Error* error, _Args... args)
{
if (!error)
if (error == nullptr)
if constexpr (!std::is_void_v<future_return_t<_Args...>>)
promise->set_value({std::move(args)...});
else
promise->set_value();
else
promise->set_exception(std::make_exception_ptr(*std::move(error)));
promise->set_exception(std::make_exception_ptr(*error));
});
// Will be std::future<void> for no D-Bus method return value
@@ -358,28 +636,13 @@ namespace sdbus {
/*** SignalSubscriber ***/
/*** ---------------- ***/
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const SignalName& signalName)
: SignalSubscriber(proxy, signalName.c_str())
{
}
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const char* signalName)
inline SignalSubscriber::SignalSubscriber(IProxy& proxy, const std::string& signalName)
: proxy_(proxy)
, signalName_(signalName)
{
}
inline SignalSubscriber& SignalSubscriber::onInterface(const InterfaceName& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName)
{
return onInterface(interfaceName.c_str());
}
inline SignalSubscriber& SignalSubscriber::onInterface(const char* interfaceName)
inline SignalSubscriber& SignalSubscriber::onInterface(std::string interfaceName)
{
interfaceName_ = std::move(interfaceName);
@@ -389,37 +652,20 @@ namespace sdbus {
template <typename _Function>
inline void SignalSubscriber::call(_Function&& callback)
{
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
proxy_.registerSignalHandler( interfaceName_
, signalName_
, makeSignalHandler(std::forward<_Function>(callback)) );
}
template <typename _Function>
[[nodiscard]] inline Slot SignalSubscriber::call(_Function&& callback, return_slot_t)
{
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
return proxy_.registerSignalHandler( interfaceName_
, signalName_
, makeSignalHandler(std::forward<_Function>(callback))
, return_slot );
}
template <typename _Function>
inline signal_handler SignalSubscriber::makeSignalHandler(_Function&& callback)
{
return [callback = std::forward<_Function>(callback)](Signal signal)
, [callback = std::forward<_Function>(callback)](Signal& signal)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the signal message.
tuple_of_function_input_arg_types_t<_Function> signalArgs;
// The signal handler can take pure signal parameters only, or an additional `std::optional<Error>` as its first
// The signal handler can take pure signal parameters only, or an additional `const Error*` as its first
// parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
// the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
// will be communicated to the client's signal handler as a valid Error object inside the std::optional parameter.
// will be communicated as a non-zero Error pointer to the client's signal handler.
if constexpr (has_error_param_v<_Function>)
{
// Deserialize input arguments from the signal message into the tuple
@@ -431,12 +677,12 @@ namespace sdbus {
{
// Pass message deserialization exceptions to the client via callback error parameter,
// instead of propagating them up the message loop call stack.
sdbus::apply(callback, e, signalArgs);
sdbus::apply(callback, &e, signalArgs);
return;
}
// Invoke callback with no error and input arguments from the tuple.
sdbus::apply(callback, {}, signalArgs);
sdbus::apply(callback, nullptr, signalArgs);
}
else
{
@@ -446,24 +692,39 @@ namespace sdbus {
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, signalArgs);
}
};
});
}
/*** ------------------ ***/
/*** SignalUnsubscriber ***/
/*** ------------------ ***/
inline SignalUnsubscriber::SignalUnsubscriber(IProxy& proxy, const std::string& signalName)
: proxy_(proxy)
, signalName_(signalName)
{
}
inline void SignalUnsubscriber::onInterface(const std::string& interfaceName)
{
proxy_.unregisterSignalHandler(interfaceName, signalName_);
}
/*** -------------- ***/
/*** PropertyGetter ***/
/*** -------------- ***/
inline PropertyGetter::PropertyGetter(IProxy& proxy, std::string_view propertyName)
inline PropertyGetter::PropertyGetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
, propertyName_(std::move(propertyName))
, propertyName_(propertyName)
{
}
inline Variant PropertyGetter::onInterface(std::string_view interfaceName)
inline Variant PropertyGetter::onInterface(const std::string& interfaceName)
{
Variant var;
proxy_.callMethod("Get")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(interfaceName, propertyName_)
.storeResultsTo(var);
return var;
@@ -473,15 +734,15 @@ namespace sdbus {
/*** AsyncPropertyGetter ***/
/*** ------------------- ***/
inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName)
: proxy_(proxy)
, propertyName_(std::move(propertyName))
inline AsyncPropertyGetter::AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
, propertyName_(propertyName)
{
}
inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(std::string_view interfaceName)
inline AsyncPropertyGetter& AsyncPropertyGetter::onInterface(const std::string& interfaceName)
{
interfaceName_ = std::move(interfaceName);
interfaceName_ = &interfaceName;
return *this;
}
@@ -489,38 +750,23 @@ namespace sdbus {
template <typename _Function>
PendingAsyncCall AsyncPropertyGetter::uponReplyInvoke(_Function&& callback)
{
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, Variant>
, "Property get callback function must accept std::optional<Error> and property value as Variant" );
static_assert(std::is_invocable_r_v<void, _Function, const Error*, Variant>, "Property get callback function must accept Error* and property value as Variant");
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("Get")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_)
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_, propertyName_)
.uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
[[nodiscard]] Slot AsyncPropertyGetter::uponReplyInvoke(_Function&& callback, return_slot_t)
{
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, Variant>
, "Property get callback function must accept std::optional<Error> and property value as Variant" );
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("Get")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_)
.uponReplyInvoke(std::forward<_Function>(callback), return_slot);
}
inline std::future<Variant> AsyncPropertyGetter::getResultAsFuture()
{
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("Get")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_)
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_, propertyName_)
.getResultAsFuture<Variant>();
}
@@ -528,15 +774,15 @@ namespace sdbus {
/*** PropertySetter ***/
/*** -------------- ***/
inline PropertySetter::PropertySetter(IProxy& proxy, std::string_view propertyName)
inline PropertySetter::PropertySetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
, propertyName_(std::move(propertyName))
, propertyName_(propertyName)
{
}
inline PropertySetter& PropertySetter::onInterface(std::string_view interfaceName)
inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
{
interfaceName_ = std::move(interfaceName);
interfaceName_ = &interfaceName;
return *this;
}
@@ -555,36 +801,36 @@ namespace sdbus {
inline void PropertySetter::toValue(const Variant& value)
{
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
proxy_.callMethod("Set")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_, value);
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_, propertyName_, value);
}
inline void PropertySetter::toValue(const Variant& value, dont_expect_reply_t)
{
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
proxy_.callMethod("Set")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_, value)
.dontExpectReply();
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_, propertyName_, value)
.dontExpectReply();
}
/*** ------------------- ***/
/*** AsyncPropertySetter ***/
/*** ------------------- ***/
inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, std::string_view propertyName)
: proxy_(proxy)
, propertyName_(propertyName)
inline AsyncPropertySetter::AsyncPropertySetter(IProxy& proxy, const std::string& propertyName)
: proxy_(proxy)
, propertyName_(propertyName)
{
}
inline AsyncPropertySetter& AsyncPropertySetter::onInterface(std::string_view interfaceName)
inline AsyncPropertySetter& AsyncPropertySetter::onInterface(const std::string& interfaceName)
{
interfaceName_ = std::move(interfaceName);
interfaceName_ = &interfaceName;
return *this;
}
@@ -605,38 +851,23 @@ namespace sdbus {
template <typename _Function>
PendingAsyncCall AsyncPropertySetter::uponReplyInvoke(_Function&& callback)
{
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>>
, "Property set callback function must accept std::optional<Error> only" );
static_assert(std::is_invocable_r_v<void, _Function, const Error*>, "Property set callback function must accept Error* only");
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("Set")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_, std::move(value_))
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_, propertyName_, std::move(value_))
.uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
[[nodiscard]] Slot AsyncPropertySetter::uponReplyInvoke(_Function&& callback, return_slot_t)
{
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>>
, "Property set callback function must accept std::optional<Error> only" );
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("Set")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_, std::move(value_))
.uponReplyInvoke(std::forward<_Function>(callback), return_slot);
}
inline std::future<void> AsyncPropertySetter::getResultAsFuture()
{
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("Set")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_, propertyName_, std::move(value_))
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_, propertyName_, std::move(value_))
.getResultAsFuture<>();
}
@@ -645,17 +876,17 @@ namespace sdbus {
/*** ------------------- ***/
inline AllPropertiesGetter::AllPropertiesGetter(IProxy& proxy)
: proxy_(proxy)
: proxy_(proxy)
{
}
inline std::map<PropertyName, Variant> AllPropertiesGetter::onInterface(std::string_view interfaceName)
inline std::map<std::string, Variant> AllPropertiesGetter::onInterface(const std::string& interfaceName)
{
std::map<PropertyName, Variant> props;
std::map<std::string, Variant> props;
proxy_.callMethod("GetAll")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(std::move(interfaceName))
.storeResultsTo(props);
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(interfaceName)
.storeResultsTo(props);
return props;
}
@@ -668,9 +899,9 @@ namespace sdbus {
{
}
inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(std::string_view interfaceName)
inline AsyncAllPropertiesGetter& AsyncAllPropertiesGetter::onInterface(const std::string& interfaceName)
{
interfaceName_ = std::move(interfaceName);
interfaceName_ = &interfaceName;
return *this;
}
@@ -678,41 +909,27 @@ namespace sdbus {
template <typename _Function>
PendingAsyncCall AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback)
{
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, std::map<PropertyName, Variant>>
, "All properties get callback function must accept std::optional<Error> and a map of property names to their values" );
static_assert( std::is_invocable_r_v<void, _Function, const Error*, std::map<std::string, Variant>>
, "All properties get callback function must accept Error* and a map of property names to their values" );
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("GetAll")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_)
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_)
.uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
[[nodiscard]] Slot AsyncAllPropertiesGetter::uponReplyInvoke(_Function&& callback, return_slot_t)
inline std::future<std::map<std::string, Variant>> AsyncAllPropertiesGetter::getResultAsFuture()
{
static_assert( std::is_invocable_r_v<void, _Function, std::optional<Error>, std::map<PropertyName, Variant>>
, "All properties get callback function must accept std::optional<Error> and a map of property names to their values" );
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(interfaceName_ != nullptr); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("GetAll")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_)
.uponReplyInvoke(std::forward<_Function>(callback), return_slot);
.onInterface("org.freedesktop.DBus.Properties")
.withArguments(*interfaceName_)
.getResultAsFuture<std::map<std::string, Variant>>();
}
inline std::future<std::map<PropertyName, Variant>> AsyncAllPropertiesGetter::getResultAsFuture()
{
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
return proxy_.callMethodAsync("GetAll")
.onInterface(DBUS_PROPERTIES_INTERFACE_NAME)
.withArguments(interfaceName_)
.getResultAsFuture<std::map<PropertyName, Variant>>();
}
} // namespace sdbus
}
#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */
+12 -27
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Error.h
*
@@ -43,56 +43,41 @@ namespace sdbus {
: public std::runtime_error
{
public:
// Strong type representing the D-Bus error name
class Name : public std::string
{
public:
Name() = default;
explicit Name(std::string value)
: std::string(std::move(value))
{}
explicit Name(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
explicit Error(Name name, const char* message = nullptr)
: Error(std::move(name), std::string(message ? message : ""))
explicit Error(const std::string& name, const char* message = nullptr)
: Error(name, std::string(message ? message : ""))
{
}
Error(Name name, std::string message)
Error(const std::string& name, const std::string& message)
: std::runtime_error("[" + name + "] " + message)
, name_(std::move(name))
, message_(std::move(message))
, name_(name)
, message_(message)
{
}
[[nodiscard]] const Name& getName() const
const std::string& getName() const
{
return name_;
}
[[nodiscard]] const std::string& getMessage() const
const std::string& getMessage() const
{
return message_;
}
[[nodiscard]] bool isValid() const
bool isValid() const
{
return !getName().empty();
}
private:
Name name_;
std::string name_;
std::string message_;
};
Error createError(int errNo, std::string customMsg);
sdbus::Error createError(int errNo, const std::string& customMsg);
inline const Error::Name SDBUSCPP_ERROR_NAME{"org.sdbuscpp.Error"};
inline const char* SDBUSCPP_ERROR_NAME = "org.sdbuscpp.Error";
}
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
+8 -8
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Flags.h
*
@@ -74,21 +74,21 @@ namespace sdbus {
flags_.set(flag, value);
}
[[nodiscard]] bool test(GeneralFlags flag) const
bool test(GeneralFlags flag) const
{
return flags_.test(flag);
}
[[nodiscard]] bool test(PropertyUpdateBehaviorFlags flag) const
bool test(PropertyUpdateBehaviorFlags flag) const
{
return flags_.test(flag);
}
[[nodiscard]] uint64_t toSdBusInterfaceFlags() const;
[[nodiscard]] uint64_t toSdBusMethodFlags() const;
[[nodiscard]] uint64_t toSdBusSignalFlags() const;
[[nodiscard]] uint64_t toSdBusPropertyFlags() const;
[[nodiscard]] uint64_t toSdBusWritablePropertyFlags() const;
uint64_t toSdBusInterfaceFlags() const;
uint64_t toSdBusMethodFlags() const;
uint64_t toSdBusSignalFlags() const;
uint64_t toSdBusPropertyFlags() const;
uint64_t toSdBusWritablePropertyFlags() const;
private:
std::bitset<FLAG_COUNT> flags_;
+205 -217
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file IConnection.h
*
@@ -28,22 +28,13 @@
#define SDBUS_CXX_ICONNECTION_H_
#include <sdbus-c++/TypeTraits.h>
#include <string>
#include <memory>
#include <chrono>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
// Forward declarations
struct sd_bus;
struct sd_event;
namespace sdbus {
class Message;
class ObjectPath;
class BusName;
using ServiceName = BusName;
}
namespace sdbus {
@@ -60,10 +51,90 @@ namespace sdbus {
class IConnection
{
public:
struct PollData;
/*!
* Poll Data for external event loop implementations.
*
* To integrate sdbus with your app's own custom event handling system
* you can use this method to query which file descriptors, poll events
* and timeouts you should add to your app's poll(2), or select(2)
* call in your main event loop.
*
* If you are unsure what this all means then use
* enterEventLoop() or enterEventLoopAsync() instead.
*
* See: getEventLoopPollData()
*/
struct PollData
{
/*!
* The read fd to be monitored by the event loop.
*/
int fd;
/*!
* The events to use for poll(2) alongside fd.
*/
short int events;
/*!
* Absolute timeout value in micro seconds and based of CLOCK_MONOTONIC.
*/
uint64_t timeout_usec;
/*!
* Get the event poll timeout.
*
* The timeout is an absolute value based of CLOCK_MONOTONIC.
*
* @return a duration since the CLOCK_MONOTONIC epoch started.
*/
[[nodiscard]] std::chrono::microseconds getAbsoluteTimeout() const
{
return std::chrono::microseconds(timeout_usec);
}
/*!
* Get the timeout as relative value from now
*
* @return std::nullopt if the timeout is indefinite. A duration otherwise.
*/
[[nodiscard]] std::optional<std::chrono::microseconds> getRelativeTimeout() const;
/*!
* Get a converted, relative timeout which can be passed as argument 'timeout' to poll(2)
*
* @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block. An integer in milli
* seconds otherwise.
*/
[[nodiscard]] int getPollTimeout() const;
};
virtual ~IConnection() = default;
/*!
* @brief Requests D-Bus name on the connection
*
* @param[in] name Name to request
*
* @throws sdbus::Error in case of failure
*/
virtual void requestName(const std::string& name) = 0;
/*!
* @brief Releases D-Bus name on the connection
*
* @param[in] name Name to release
*
* @throws sdbus::Error in case of failure
*/
virtual void releaseName(const std::string& name) = 0;
/*!
* @brief Retrieve the unique name of a connection. E.g. ":1.xx"
*
* @throws sdbus::Error in case of failure
*/
virtual std::string getUniqueName() const = 0;
/*!
* @brief Enters I/O event loop on this bus connection
*
@@ -92,103 +163,61 @@ namespace sdbus {
virtual void leaveEventLoop() = 0;
/*!
* @brief Attaches the bus connection to an sd-event event loop
* @brief Adds an ObjectManager at the specified D-Bus object path
*
* @param[in] event sd-event event loop object
* @param[in] priority Specified priority
* 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
* This call creates a floating registration. The ObjectManager will
* be there for the object path until the connection is destroyed.
*
* See `man sd_bus_attach_event'.
*/
virtual void attachSdEventLoop(sd_event *event, int priority = 0) = 0;
/*!
* @brief Detaches the bus connection from an sd-event event loop
* Another, recommended way to add object managers is directly through
* IObject API.
*
* @throws sdbus::Error in case of failure
*/
virtual void detachSdEventLoop() = 0;
[[deprecated("Use one of other addObjectManager overloads")]] virtual void addObjectManager(const std::string& objectPath) = 0;
/*!
* @brief Gets current sd-event event loop for the bus connection
* @brief Returns fd, I/O events and timeout data you can pass to poll
*
* @return Pointer to event loop object if attached, nullptr otherwise
*/
virtual sd_event *getSdEventLoop() = 0;
/*!
* @brief Returns fd's, I/O events and timeout data to be used in an external event loop
* To integrate sdbus with your app's own custom event handling system
* (without the requirement of an extra thread), you can use this
* method to query which file descriptors, poll events and timeouts you
* should add to your app's poll call in your main event loop. If these
* file descriptors signal, then you should call processPendingRequest
* to process the event. This means that all of sdbus's callbacks will
* arrive on your app's main event thread (opposed to on a thread created
* by sdbus-c++). If you are unsure what this all means then use
* enterEventLoop() or enterEventLoopAsync() instead.
*
* This function is useful to hook up a bus connection object with an
* external (like GMainLoop, boost::asio, etc.) or manual event loop
* involving poll() or a similar I/O polling call.
*
* Before **each** invocation of the I/O polling call, this function
* should be invoked. Returned PollData::fd file descriptor should
* be polled for the events indicated by PollData::events, and the I/O
* call should block for that up to the returned PollData::timeout.
*
* Additionally, returned PollData::eventFd should be polled for POLLIN
* events.
*
* After each I/O polling call the bus connection needs to process
* incoming or outgoing data, by invoking processPendingEvent().
*
* Note that the returned timeout should be considered only a maximum
* sleeping time. It is permissible (and even expected) that shorter
* timeouts are used by the calling program, in case other event sources
* are polled in the same event loop. Note that the returned time-value
* is absolute, based of CLOCK_MONOTONIC and specified in microseconds.
* Use PollData::getPollTimeout() to have the timeout value converted
* in a form that can be passed to poll(2).
*
* The bus connection conveniently integrates sd-event event loop.
* To attach the bus connection to an sd-event event loop, use
* attachSdEventLoop() function.
* 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
*/
[[nodiscard]] virtual PollData getEventLoopPollData() const = 0;
virtual PollData getEventLoopPollData() const = 0;
/*!
* @brief Processes a pending event
* @brief Process a pending request
*
* @returns True if an event was processed, false if no operations were pending
* @returns true if an event was processed, false if poll should be called
*
* This function drives the D-Bus connection. It processes pending I/O events.
* Queued outgoing messages (or parts thereof) are sent out. Queued incoming
* messages are dispatched to registered callbacks. Timeouts are recalculated.
*
* It returns false when no operations were pending and true if a message was
* processed. When false is returned the caller should synchronously poll for
* I/O events before calling into processPendingEvent() again.
* Don't forget to call getEventLoopPollData() each time before the next poll.
*
* You don't need to directly call this method or getEventLoopPollData() method
* when using convenient, internal bus connection event loops through
* enterEventLoop() or enterEventLoopAsync() calls, or when the bus is
* connected to an sd-event event loop through attachSdEventLoop().
* It is invoked automatically when necessary.
* Processes a single dbus event. All of sdbus-c++'s callbacks will be called
* from within this method. This method should ONLY be used in conjuction
* with getEventLoopPollData().
* This method returns true if an I/O message was processed. This you can try
* to call this method again before going to poll on I/O events. The method
* returns false if no operations were pending, and the caller should then
* poll for I/O events before calling this method again.
* enterEventLoop() and enterEventLoopAsync() will call this method for you,
* so there is no need to call it when using these. If you are unsure what
* this all means then don't use this method.
*
* @throws sdbus::Error in case of failure
*/
virtual bool processPendingEvent() = 0;
/*!
* @brief Provides access to the currently processed D-Bus message
*
* This method provides access to the currently processed incoming D-Bus message.
* "Currently processed" means that the registered callback handler(s) for that message
* are being invoked. This method is meant to be called from within a callback handler
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
* guaranteed to return a valid D-Bus message instance for which the handler is called.
* If called from other contexts/threads, it may return a valid or invalid message, depending
* on whether a message was processed or not at the time of the call.
*
* @return Currently processed D-Bus message
*/
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
virtual bool processPendingRequest() = 0;
/*!
* @brief Sets general method call timeout
@@ -219,11 +248,10 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual uint64_t getMethodCallTimeout() const = 0;
virtual uint64_t getMethodCallTimeout() const = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
@@ -232,52 +260,12 @@ namespace sdbus {
* This call creates a floating registration. The ObjectManager will
* be there for the object path until the connection is destroyed.
*
* Another, recommended way to add object managers is directly through IObject API.
* Another, recommended way to add object managers is directly through
* IObject API.
*
* @throws sdbus::Error in case of failure
*/
virtual void addObjectManager(const ObjectPath& objectPath) = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
* @return Slot handle owning the registration
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* This call returns an owning slot. The lifetime of the ObjectManager
* interface is bound to the lifetime of the returned slot instance.
*
* Another, recommended way to add object managers is directly through IObject API.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) = 0;
/*!
* @brief Installs a floating match rule for messages received on this bus connection
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
*
* The method installs a match rule for messages received on the specified bus connection.
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
* The specified handler function callback is called for each incoming message matching the specified
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
* sends a control message requesting the match to be added to the broker and waits until the broker
* confirms the match has been installed successfully.
*
* The method installs a floating match rule for messages received on the specified bus connection.
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
* is bound to the lifetime of the bus connection.
*
* For more information, consult `man sd_bus_add_match`.
*
* @throws sdbus::Error in case of failure
*/
virtual void addMatch(const std::string& match, message_handler callback) = 0;
virtual void addObjectManager(const std::string& objectPath, floating_slot_t) = 0;
/*!
* @brief Installs a match rule for messages received on this bus connection
@@ -290,40 +278,34 @@ namespace sdbus {
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
* The specified handler function callback is called for each incoming message matching the specified
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
* sends a control message requesting the match to be added to the broker and waits until the broker
* sends a control message requested the match to be added to the broker and waits until the broker
* confirms the match has been installed successfully.
*
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
* the slot instance implies uninstalling of the match rule from the bus connection.
* Simply let go of the slot instance to uninstall the match rule from the bus connection. The slot
* must not outlive the connection for the slot is associated with it.
*
* For more information, consult `man sd_bus_add_match`.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback, return_slot_t) = 0;
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback) = 0;
/*!
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
* @brief Installs a floating match rule for messages received on this bus connection
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
*
* This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously,
* in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response.
* The `installCallback' callable is called when the response is later received, with the response message
* from the broker as parameter. If it's an empty function object, a default implementation is used that
* terminates the bus connection should installing the match fail.
*
* The method installs a floating match rule for messages received on the specified bus connection.
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
* is bound to the lifetime of the bus connection.
*
* For more information, consult `man sd_bus_add_match_async`.
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation for more
* information.
*
* @throws sdbus::Error in case of failure
*/
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
virtual void addMatch(const std::string& match, message_handler callback, floating_slot_t) = 0;
/*!
* @brief Asynchronously installs a match rule for messages received on this bus connection
@@ -339,91 +321,58 @@ namespace sdbus {
* from the broker as parameter. If it's an empty function object, a default implementation is used that
* terminates the bus connection should installing the match fail.
*
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
* the slot instance implies the uninstalling of the match rule from the bus connection.
*
* For more information, consult `man sd_bus_add_match_async`.
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation, and consult
* `man sd_bus_add_match`, for more information.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addMatchAsync( const std::string& match
, message_handler callback
, message_handler installCallback
, return_slot_t ) = 0;
[[nodiscard]] virtual Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
/*!
* @brief Retrieves the unique name of a connection. E.g. ":1.xx"
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
*
* The method installs a floating match rule for messages received on the specified bus connection.
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
* is bound to the lifetime of the bus connection.
*
* Refer to the @c addMatch(const std::string& match, message_handler callback, message_handler installCallback)
* documentation for more information.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual BusName getUniqueName() const = 0;
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) = 0;
/*!
* @brief Requests a well-known D-Bus service name on a bus
* @copydoc IConnection::enterEventLoop()
*
* @param[in] name Name to request
*
* @throws sdbus::Error in case of failure
* @deprecated This function has been replaced by enterEventLoop()
*/
virtual void requestName(const ServiceName& name) = 0;
[[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop();
/*!
* @brief Releases an acquired well-known D-Bus service name on a bus
* @copydoc IConnection::enterEventLoopAsync()
*
* @param[in] name Name to release
*
* @throws sdbus::Error in case of failure
* @deprecated This function has been replaced by enterEventLoopAsync()
*/
virtual void releaseName(const ServiceName& name) = 0;
[[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync();
/*!
* @struct PollData
* @copydoc IConnection::leaveEventLoop()
*
* Carries poll data needed for integration with external event loop implementations.
*
* See getEventLoopPollData() for more info.
* @deprecated This function has been replaced by leaveEventLoop()
*/
struct PollData
{
/*!
* The read fd to be monitored by the event loop.
*/
int fd;
[[deprecated("This function has been replaced by leaveEventLoop()")]] void leaveProcessingLoop();
/*!
* The events to use for poll(2) alongside fd.
*/
short int events;
/*!
* Absolute timeout value in microseconds, based of CLOCK_MONOTONIC.
*
* Call getPollTimeout() to get timeout recalculated to relative timeout that can be passed to poll(2).
*/
std::chrono::microseconds timeout;
/*!
* An additional event fd to be monitored by the event loop for POLLIN events.
*/
int eventFd;
/*!
* Returns the timeout as relative value from now.
*
* Returned value is std::chrono::microseconds::max() if the timeout is indefinite.
*
* @return Relative timeout as a time duration
*/
[[nodiscard]] std::chrono::microseconds getRelativeTimeout() const;
/*!
* Returns relative timeout in the form which can be passed as argument 'timeout' to poll(2)
*
* @return -1 if the timeout is indefinite. 0 if the poll(2) shouldn't block.
* An integer in milliseconds otherwise.
*/
[[nodiscard]] int getPollTimeout() const;
};
/*!
* @copydoc IConnection::getEventLoopPollData()
*
* @deprecated This function has been replaced by getEventLoopPollData()
*/
[[deprecated("This function has been replaced by getEventLoopPollData()")]] PollData getProcessLoopPollData() const;
};
template <typename _Rep, typename _Period>
@@ -433,6 +382,45 @@ namespace sdbus {
return setMethodCallTimeout(microsecs.count());
}
inline void IConnection::enterProcessingLoop()
{
enterEventLoop();
}
inline void IConnection::enterProcessingLoopAsync()
{
enterEventLoopAsync();
}
inline void IConnection::leaveProcessingLoop()
{
leaveEventLoop();
}
inline IConnection::PollData IConnection::getProcessLoopPollData() const
{
return getEventLoopPollData();
}
/*!
* @brief Creates/opens D-Bus system bus connection
*
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection();
/*!
* @brief Creates/opens D-Bus system bus connection with a name
*
* @param[in] name Name to request on the connection after its opening
* @return Connection instance
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus session bus connection when in a user context, and a system bus connection, otherwise.
*
@@ -440,7 +428,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createBusConnection();
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection();
/*!
* @brief Creates/opens D-Bus session bus connection with a name when in a user context, and a system bus connection with a name, otherwise.
@@ -450,7 +438,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createBusConnection(const ServiceName& name);
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus system bus connection
@@ -469,7 +457,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const ServiceName& name);
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus session bus connection
@@ -488,7 +476,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const ServiceName& name);
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
/*!
* @brief Creates/opens D-Bus session bus connection at a custom address
+301 -273
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file IObject.h
*
@@ -28,20 +28,17 @@
#define SDBUS_CXX_IOBJECT_H_
#include <sdbus-c++/ConvenienceApiClasses.h>
#include <sdbus-c++/Flags.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/VTableItems.h>
#include <sdbus-c++/Flags.h>
#include <functional>
#include <memory>
#include <string>
#include <memory>
#include <vector>
// Forward declarations
namespace sdbus {
class Signal;
class IConnection;
class ObjectPath;
}
namespace sdbus {
@@ -61,108 +58,186 @@ namespace sdbus {
***********************************************/
class IObject
{
public: // High-level, convenience API
public:
virtual ~IObject() = default;
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] vtable Individual instances of VTable item structures stored in a vector
* @return VTableAdder high-level helper class
*
* This method is used to declare attributes for the object under the given interface.
* Parameter `vtable' represents a vtable definition that may contain method declarations
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
* struct), signal declarations (using SignalVTableItem struct), or global interface
* flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime.
*
* When called like `addVTable(vtable).forInterface(interface)`, then an internal registration
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
* can be added or removed by the user at runtime.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
* @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
*/
[[nodiscard]] VTableAdder addVTable(std::vector<VTableItem> vtable);
virtual void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags = {} ) = 0;
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] items Individual instances of VTable item structures
* @return VTableAdder high-level helper class
* @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)
*
* This method is used to declare attributes for the object under the given interface.
* Parameter pack contains vtable definition that may contain method declarations
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
* struct), signal declarations (using SignalVTableItem struct), or global interface
* flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime.
*
* When called like `addVTable(items...).forInterface(interface)`, then an internal registration
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
* can be added or removed by the user at runtime.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
* 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
*/
template < typename... VTableItems
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
[[nodiscard]] VTableAdder addVTable(VTableItems&&... items);
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 Emits signal on D-Bus
* @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
* @return A helper object for convenient emission of signals
*
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
*
* Example of use:
* @code
* int arg1 = ...;
* double arg2 = ...;
* SignalName fooSignal{"fooSignal"};
* object_.emitSignal(fooSignal).onInterface("com.kistler.foo").withArguments(arg1, arg2);
* @endcode
* @param[in] signature D-Bus signature of signal parameters
* @param[in] flags D-Bus signal flags (deprecated)
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalEmitter emitSignal(const SignalName& signalName);
virtual void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, Flags flags = {} ) = 0;
/*!
* @copydoc IObject::emitSignal(const SignalName&)
* @brief Registers signal that the object will emit on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] paramNames Names of parameters of the signal
* @param[in] flags D-Bus signal flags (deprecated)
*
* Provided names of signal output parameters will be included in the introspection
* description (given that at least version 242 of underlying libsystemd library is
* used; otherwise, names of parameters are ignored). This usually helps better describe
* the API to the introspector.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalEmitter emitSignal(const std::string& signalName);
virtual void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags = {} ) = 0;
/*!
* @copydoc IObject::emitSignal(const SignalName&)
* @brief Registers read-only property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalEmitter emitSignal(const char* signalName);
virtual void registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags = {} ) = 0;
/*!
* @brief Registers read/write property that the object will provide on D-Bus
*
* @param[in] interfaceName Name of an interface that the property will fall under
* @param[in] propertyName Name of the property
* @param[in] signature D-Bus signature of property parameters
* @param[in] getCallback Callback that implements the body of the property getter
* @param[in] setCallback Callback that implements the body of the property setter
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
*
* @throws sdbus::Error in case of failure
*/
virtual void registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags = {} ) = 0;
/*!
* @brief Sets flags for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @param[in] flags Flags to be set
*
* @throws sdbus::Error in case of failure
*/
virtual void setInterfaceFlags(const std::string& interfaceName, Flags flags) = 0;
/*!
* @brief Finishes object API registration and publishes the object on the bus
*
* The method exports all up to now registered methods, signals and properties on D-Bus.
* Must be called after all methods, signals and properties have been registered.
*
* @throws sdbus::Error in case of failure
*/
virtual void finishRegistration() = 0;
/*!
* @brief Unregisters object's API and removes object from the bus
*
* This method unregisters the object, its interfaces, methods, signals and properties
* from the bus. Unregistration is done automatically also in object's destructor. This
* method makes sense if, in the process of object removal, we need to make sure that
* callbacks are unregistered explicitly before the final destruction of the object instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
/*!
* @brief Creates a signal message
*
* @param[in] interfaceName Name of an interface that the signal belongs under
* @param[in] signalName Name of the signal
* @return A signal message
*
* Serialize signal arguments into the returned message and emit the signal by passing
* the message with serialized arguments to the @c emitSignal function.
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual Signal createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
/*!
* @brief Emits signal for this object path
*
* @param[in] message Signal message to be sent out
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitSignal(const sdbus::Signal& message) = 0;
/*!
* @brief Emits PropertyChanged signal for specified properties under a given interface of this object path
@@ -172,12 +247,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames) = 0;
/*!
* @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&,const std::vector<PropertyName>&)
*/
virtual void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames) = 0;
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
@@ -186,12 +256,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName) = 0;
/*!
* @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&)
*/
virtual void emitPropertiesChangedSignal(const char* interfaceName) = 0;
virtual void emitPropertiesChangedSignal(const std::string& interfaceName) = 0;
/*!
* @brief Emits InterfacesAdded signal on this object path
@@ -211,12 +276,14 @@ namespace sdbus {
* @brief Emits InterfacesAdded signal on this object path
*
* This emits an InterfacesAdded signal on this object path with explicitly provided list
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
* object interfaces and their vtables, so this method now makes more sense.
* 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<InterfaceName>& interfaces) = 0;
virtual void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) = 0;
/*!
* @brief Emits InterfacesRemoved signal on this object path
@@ -233,12 +300,14 @@ namespace sdbus {
* @brief Emits InterfacesRemoved signal on this object path
*
* This emits an InterfacesRemoved signal on the given path with explicitly provided list
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
* object interfaces and their vtables, so this method now makes more sense.
* 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<InterfaceName>& interfaces) = 0;
virtual void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) = 0;
/*!
* @brief Adds an ObjectManager interface at the path of this D-Bus object
@@ -247,215 +316,175 @@ namespace sdbus {
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* This call creates a so-called floating registration. This means that
* the ObjectManager interface stays there for the lifetime of the object.
*
* @throws sdbus::Error in case of failure
*/
virtual void addObjectManager() = 0;
/*!
* @brief Adds an ObjectManager interface at the path of this D-Bus object
*
* @return Slot handle owning the registration
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* The lifetime of the ObjectManager interface is bound to the lifetime
* of the returned slot instance.
* @brief Removes an ObjectManager interface from the path of this D-Bus object
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addObjectManager(return_slot_t) = 0;
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
*/
[[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0;
virtual sdbus::IConnection& getConnection() const = 0;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] methodName Name of the method
* @return A helper object for convenient registration of the method
*
* This is a high-level, convenience way of registering D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the parameters and return type
* of the provided native method implementation callback.
*
* Example of use:
* @code
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] MethodRegistrator registerMethod(const std::string& methodName);
/*!
* @brief Registers signal that the object will provide on D-Bus
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient registration of the signal
*
* This is a high-level, convenience way of registering D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native parameters.
*
* Example of use:
* @code
* object.registerSignal("paramChange").onInterface("com.kistler.foo").withParameters<std::map<int32_t, std::string>>();
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalRegistrator registerSignal(const std::string& signalName);
/*!
* @brief Registers property that the object will provide on D-Bus
*
* @param[in] propertyName Name of the property
* @return A helper object for convenient registration of the property
*
* This is a high-level, convenience way of registering D-Bus properties that abstracts
* from the D-Bus message concept. Property arguments are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the provided native callbacks.
*
* Example of use:
* @code
* object_.registerProperty("state").onInterface("com.kistler.foo").withGetter([this](){ return this->state(); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] PropertyRegistrator registerProperty(const std::string& propertyName);
/*!
* @brief Sets flags (annotations) for a given interface
*
* @param[in] interfaceName Name of an interface whose flags will be set
* @return A helper object for convenient setting of Interface flags
*
* This is a high-level, convenience alternative to the other setInterfaceFlags overload.
*
* Example of use:
* @code
* object_.setInterfaceFlags("com.kistler.foo").markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] InterfaceFlagsSetter setInterfaceFlags(const std::string& interfaceName);
/*!
* @brief Emits signal on D-Bus
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient emission of signals
*
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
*
* Example of use:
* @code
* int arg1 = ...;
* double arg2 = ...;
* object_.emitSignal("fooSignal").onInterface("com.kistler.foo").withArguments(arg1, arg2);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalEmitter emitSignal(const std::string& signalName);
/*!
* @brief Returns object path of the underlying DBus object
*/
[[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0;
virtual const std::string& getObjectPath() const = 0;
/*!
* @brief Provides access to the currently processed D-Bus message
* @brief Provides currently processed D-Bus message
*
* This method provides access to the currently processed incoming D-Bus message.
* This method provides immutable access to the currently processed incoming D-Bus message.
* "Currently processed" means that the registered callback handler(s) for that message
* are being invoked. This method is meant to be called from within a callback handler
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
* guaranteed to return a valid D-Bus message instance for which the handler is called.
* If called from other contexts/threads, it may return a valid or invalid message, depending
* on whether a message was processed or not at the time of the call.
* (e.g. D-Bus method implementation handler). In such a case it is guaranteed to return
* a valid pointer to the D-Bus message for which the handler is called. If called from other
* contexts/threads, it may return a nonzero pointer or a nullptr, depending on whether a message
* was processed at the time of call or not, but the value is nondereferencable, since the pointed-to
* message may have gone in the meantime.
*
* @return Currently processed D-Bus message
* @return A pointer to the currently processed D-Bus message
*/
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
/*!
* @brief Unregisters object's API and removes object from the bus
*
* This method unregisters the object, its interfaces, methods, signals and properties
* from the bus. Unregistration is done automatically also in object's destructor. This
* method makes sense if, in the process of object removal, we need to make sure that
* callbacks are unregistered explicitly before the final destruction of the object instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
public: // Lower-level, message-based API
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @param[in] interfaceName Name of an interface the the vtable is registered for
* @param[in] items Individual instances of VTable item structures
*
* This method is used to declare attributes for the object under the given interface.
* Parameter `items' represents a vtable definition that may contain method declarations
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
* struct), signal declarations (using SignalVTableItem struct), or global interface
* flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime. For each vtable an internal
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
template < typename... VTableItems
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
void addVTable(InterfaceName interfaceName, VTableItems&&... items);
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @param[in] interfaceName Name of an interface the the vtable is registered for
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
*
* This method is used to declare attributes for the object under the given interface.
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
* property declarations (using PropertyVTableItem struct), signal declarations (using
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime. For each vtable an internal
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
virtual void addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) = 0;
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @param[in] interfaceName Name of an interface the the vtable is registered for
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
*
* This method is used to declare attributes for the object under the given interface.
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
* property declarations (using PropertyVTableItem struct), signal declarations (using
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime. For each vtable an internal
* registration slot is created and is returned to the caller. The returned slot should be destroyed
* when the vtable is not needed anymore. This allows for "dynamic" object API where vtables
* can be added or removed by the user at runtime.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) = 0;
/*!
* @brief Creates a signal message
*
* @param[in] interfaceName Name of an interface that the signal belongs under
* @param[in] signalName Name of the signal
* @return A signal message
*
* Serialize signal arguments into the returned message and emit the signal by passing
* the message with serialized arguments to the @c emitSignal function.
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) = 0;
/*!
* @brief Emits signal for this object path
*
* @param[in] message Signal message to be sent out
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitSignal(const sdbus::Signal& message) = 0;
protected: // Internal API for efficiency reasons used by high-level API helper classes
friend SignalEmitter;
[[nodiscard]] virtual Signal createSignal(const char* interfaceName, const char* signalName) = 0;
virtual const Message* getCurrentlyProcessedMessage() const = 0;
};
// Out-of-line member definitions
inline SignalEmitter IObject::emitSignal(const SignalName& signalName)
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
{
return SignalEmitter(*this, signalName);
return MethodRegistrator(*this, methodName);
}
inline SignalRegistrator IObject::registerSignal(const std::string& signalName)
{
return SignalRegistrator(*this, signalName);
}
inline PropertyRegistrator IObject::registerProperty(const std::string& propertyName)
{
return PropertyRegistrator(*this, propertyName);
}
inline InterfaceFlagsSetter IObject::setInterfaceFlags(const std::string& interfaceName)
{
return InterfaceFlagsSetter(*this, interfaceName);
}
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
{
return SignalEmitter(*this, signalName.c_str());
}
inline SignalEmitter IObject::emitSignal(const char* signalName)
{
return SignalEmitter(*this, signalName);
}
template <typename... VTableItems, typename>
void IObject::addVTable(InterfaceName interfaceName, VTableItems&&... items)
{
addVTable(std::move(interfaceName), {std::forward<VTableItems>(items)...});
}
template <typename... VTableItems, typename>
VTableAdder IObject::addVTable(VTableItems&&... items)
{
return addVTable(std::vector<VTableItem>{std::forward<VTableItems>(items)...});
}
inline VTableAdder IObject::addVTable(std::vector<VTableItem> vtable)
{
return VTableAdder(*this, std::move(vtable));
}
/*!
* @brief Creates instance representing a D-Bus object
*
@@ -474,11 +503,10 @@ namespace sdbus {
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, ObjectPath objectPath);
[[nodiscard]] std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
}
#include <sdbus-c++/ConvenienceApiClasses.inl>
#include <sdbus-c++/VTableItems.inl>
#endif /* SDBUS_CXX_IOBJECT_H_ */
+187 -475
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file IProxy.h
*
@@ -29,20 +29,17 @@
#include <sdbus-c++/ConvenienceApiClasses.h>
#include <sdbus-c++/TypeTraits.h>
#include <chrono>
#include <functional>
#include <future>
#include <memory>
#include <string>
#include <string_view>
#include <memory>
#include <functional>
#include <chrono>
#include <future>
// Forward declarations
namespace sdbus {
class MethodCall;
class MethodReply;
class IConnection;
class ObjectPath;
class PendingAsyncCall;
namespace internal {
class Proxy;
@@ -67,9 +64,119 @@ namespace sdbus {
***********************************************/
class IProxy
{
public: // High-level, convenience API
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 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 D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @param[in] timeout Timeout for dbus call in microseconds
* @return Cookie for the the pending asynchronous call
*
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the connection
* I/O event loop thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout = 0) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t)
*/
template <typename _Rep, typename _Period>
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout);
/*!
* @brief Registers a handler for the desired signal emitted by the D-Bus object
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
* @param[in] signalHandler Callback that implements the body of the signal handler
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler ) = 0;
/*!
* @brief Unregisters the handler of the desired signal
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
*
* @throws sdbus::Error in case of failure
*/
virtual void unregisterSignalHandler( const std::string& interfaceName
, const std::string& signalName ) = 0;
/*!
* @brief Finishes the registration of signal handlers
*
* The method physically subscribes to the desired signals.
* Must be called only once, after all signals have been registered already.
*
* @throws sdbus::Error in case of failure
*/
virtual void finishRegistration() = 0;
/*!
* @brief Unregisters proxy's signal handlers and stops receving replies to pending async calls
*
* Unregistration is done automatically also in proxy's destructor. This method makes
* sense if, in the process of proxy removal, we need to make sure that callbacks
* are unregistered explicitly before the final destruction of the proxy instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
/*!
* @brief Calls method on the D-Bus object
*
@@ -84,24 +191,13 @@ namespace sdbus {
* Example of use:
* @code
* int result, a = ..., b = ...;
* MethodName multiply{"multiply"};
* object_.callMethod(multiply).onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
* object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] MethodInvoker callMethod(const MethodName& methodName);
/*!
* @copydoc IProxy::callMethod(const MethodName&)
*/
[[nodiscard]] MethodInvoker callMethod(const std::string& methodName);
/*!
* @copydoc IProxy::callMethod(const MethodName&)
*/
[[nodiscard]] MethodInvoker callMethod(const char* methodName);
/*!
* @brief Calls method on the D-Bus object asynchronously
*
@@ -116,8 +212,7 @@ namespace sdbus {
* Example of use:
* @code
* int a = ..., b = ...;
* MethodName multiply{"multiply"};
* object_.callMethodAsync(multiply).onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result)
* object_.callMethodAsync("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).uponReplyInvoke([](int result)
* {
* std::cout << "Got result of multiplying " << a << " and " << b << ": " << result << std::endl;
* });
@@ -125,18 +220,8 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const MethodName& methodName);
/*!
* @copydoc IProxy::callMethodAsync(const MethodName&)
*/
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const std::string& methodName);
/*!
* @copydoc IProxy::callMethodAsync(const MethodName&)
*/
[[nodiscard]] AsyncMethodInvoker callMethodAsync(const char* methodName);
/*!
* @brief Registers signal handler for a given signal of the D-Bus object
*
@@ -148,30 +233,31 @@ namespace sdbus {
* in a message and D-Bus signatures automatically deduced from the parameters
* of the provided native signal callback.
*
* A signal can be subscribed to and unsubscribed from at any time during proxy
* lifetime. The subscription is active immediately after the call.
*
* Example of use:
* @code
* object_.uponSignal("stateChanged").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onStateChanged(arg1, arg2); });
* sdbus::InterfaceName foo{"com.kistler.foo"};
* sdbus::SignalName levelChanged{"levelChanged"};
* object_.uponSignal(levelChanged).onInterface(foo).call([this](uint16_t level){ this->onLevelChanged(level); });
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalSubscriber uponSignal(const SignalName& signalName);
/*!
* @copydoc IProxy::uponSignal(const SignalName&)
*/
[[nodiscard]] SignalSubscriber uponSignal(const std::string& signalName);
/*!
* @copydoc IProxy::uponSignal(const SignalName&)
* @brief Unregisters signal handler of a given signal of the D-Bus object
*
* @param[in] signalName Name of the signal
* @return A helper object for convenient unregistration of the signal handler
*
* This is a high-level, convenience way of unregistering a D-Bus signal's handler.
*
* Example of use:
* @code
* object_.muteSignal("fooSignal").onInterface("com.kistler.foo");
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] SignalSubscriber uponSignal(const char* signalName);
[[nodiscard]] SignalUnsubscriber muteSignal(const std::string& signalName);
/*!
* @brief Gets value of a property of the D-Bus object
@@ -186,19 +272,11 @@ namespace sdbus {
* Example of use:
* @code
* int state = object.getProperty("state").onInterface("com.kistler.foo");
* sdbus::InterfaceName foo{"com.kistler.foo"};
* sdbus::PropertyName level{"level"};
* int level = object.getProperty(level).onInterface(foo);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] PropertyGetter getProperty(const PropertyName& propertyName);
/*!
* @copydoc IProxy::getProperty(const PropertyName&)
*/
[[nodiscard]] PropertyGetter getProperty(std::string_view propertyName);
[[nodiscard]] PropertyGetter getProperty(const std::string& propertyName);
/*!
* @brief Gets value of a property of the D-Bus object asynchronously
@@ -212,18 +290,13 @@ namespace sdbus {
* Example of use:
* @code
* std::future<sdbus::Variant> state = object.getPropertyAsync("state").onInterface("com.kistler.foo").getResultAsFuture();
* auto callback = [](std::optional<sdbus::Error> err, const sdbus::Variant& value){ ... };
* auto callback = [](const sdbus::Error* err, const sdbus::Variant& value){ ... };
* object.getPropertyAsync("state").onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] AsyncPropertyGetter getPropertyAsync(const PropertyName& propertyName);
/*!
* @copydoc IProxy::getPropertyAsync(const PropertyName&)
*/
[[nodiscard]] AsyncPropertyGetter getPropertyAsync(std::string_view propertyName);
[[nodiscard]] AsyncPropertyGetter getPropertyAsync(const std::string& propertyName);
/*!
* @brief Sets value of a property of the D-Bus object
@@ -245,12 +318,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] PropertySetter setProperty(const PropertyName& propertyName);
/*!
* @copydoc IProxy::setProperty(const PropertyName&)
*/
[[nodiscard]] PropertySetter setProperty(std::string_view propertyName);
[[nodiscard]] PropertySetter setProperty(const std::string& propertyName);
/*!
* @brief Sets value of a property of the D-Bus object asynchronously
@@ -270,12 +338,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] AsyncPropertySetter setPropertyAsync(const PropertyName& propertyName);
/*!
* @copydoc IProxy::setPropertyAsync(const PropertyName&)
*/
[[nodiscard]] AsyncPropertySetter setPropertyAsync(std::string_view propertyName);
[[nodiscard]] AsyncPropertySetter setPropertyAsync(const std::string& propertyName);
/*!
* @brief Gets values of all properties of the D-Bus object
@@ -304,7 +367,7 @@ namespace sdbus {
*
* Example of use:
* @code
* auto callback = [](std::optional<sdbus::Error> err, const std::map<PropertyName, Variant>>& properties){ ... };
* auto callback = [](const sdbus::Error* err, const std::map<std::string, Variant>>& properties){ ... };
* auto props = object.getAllPropertiesAsync().onInterface("com.kistler.foo").uponReplyInvoke(std::move(callback));
* @endcode
*
@@ -317,349 +380,55 @@ namespace sdbus {
*
* @return Reference to the D-Bus connection
*/
[[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0;
virtual sdbus::IConnection& getConnection() const = 0;
/*!
* @brief Returns object path of the underlying DBus object
*/
[[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0;
virtual const std::string& getObjectPath() const = 0;
/*!
* @brief Provides access to the currently processed D-Bus message
* @brief Provides currently processed D-Bus message
*
* This method provides access to the currently processed incoming D-Bus message.
* This method provides immutable access to the currently processed incoming D-Bus message.
* "Currently processed" means that the registered callback handler(s) for that message
* are being invoked. This method is meant to be called from within a callback handler
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
* guaranteed to return a valid D-Bus message instance for which the handler is called.
* If called from other contexts/threads, it may return a valid or invalid message, depending
* on whether a message was processed or not at the time of the call.
* guaranteed to return a valid pointer to the D-Bus message for which the handler is called.
* If called from other contexts/threads, it may return a nonzero pointer or a nullptr, depending
* on whether a message was processed at the time of call or not, but the value is nondereferencable,
* since the pointed-to message may have gone in the meantime.
*
* @return Currently processed D-Bus message
* @return A pointer to the currently processed D-Bus message
*/
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
/*!
* @brief Unregisters proxy's signal handlers and stops receiving replies to pending async calls
*
* Unregistration is done automatically also in proxy's destructor. This method makes
* sense if, in the process of proxy removal, we need to make sure that callbacks
* are unregistered explicitly before the final destruction of the proxy instance.
*
* @throws sdbus::Error in case of failure
*/
virtual void unregister() = 0;
public: // Lower-level, message-based API
/*!
* @brief Creates a method call message
*
* @param[in] interfaceName Name of an interface that provides a given method
* @param[in] methodName Name of the method
* @return A method call message
*
* Serialize method arguments into the returned message and invoke the method by passing
* the message with serialized arguments to the @c callMethod function.
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) = 0;
/*!
* @brief Calls method on the remote D-Bus object
*
* @param[in] message Message representing a method call
* @return A method reply message
*
* The call does not block if the method call has dont-expect-reply flag set. In that case,
* the call returns immediately and the return value is an empty, invalid method reply.
*
* The call blocks otherwise, waiting for the remote peer to send back a reply or an error,
* or until the call times out.
*
* While blocking, other concurrent operations (in other threads) on the underlying bus
* connection are stalled until the call returns. This is not an issue in vast majority of
* (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving
* shared bus connections, this may be an issue. It is advised to instead use an asynchronous
* callMethod() function overload, which does not block the bus connection, or do the synchronous
* call from another Proxy instance created just before the call and then destroyed (which is
* anyway quite a typical approach in D-Bus implementations). Such proxy instance must have
* its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread`
* tag are designed for exactly that purpose.
*
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
*
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
*
* @throws sdbus::Error in case of failure (also in case the remote function returned an error)
*/
virtual MethodReply callMethod(const MethodCall& message) = 0;
/*!
* @brief Calls method on the remote D-Bus object
*
* @param[in] message Message representing a method call
* @param[in] timeout Method call timeout (in microseconds)
* @return A method reply message
*
* The call does not block if the method call has dont-expect-reply flag set. In that case,
* the call returns immediately and the return value is an empty, invalid method reply.
*
* The call blocks otherwise, waiting for the remote peer to send back a reply or an error,
* or until the call times out.
*
* While blocking, other concurrent operations (in other threads) on the underlying bus
* connection are stalled until the call returns. This is not an issue in vast majority of
* (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving
* shared bus connections, this may be an issue. It is advised to instead use an asynchronous
* callMethod() function overload, which does not block the bus connection, or do the synchronous
* call from another Proxy instance created just before the call and then destroyed (which is
* anyway quite a typical approach in D-Bus implementations). Such proxy instance must have
* its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread`
* tag are designed for exactly that purpose.
*
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
*
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
*
* @throws sdbus::Error in case of failure (also in case the remote function returned an error)
*/
virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,uint64_t)
*/
template <typename _Rep, typename _Period>
MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout);
virtual const Message* getCurrentlyProcessedMessage() const = 0;
/*!
* @brief Calls method on the D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @return Observing handle for the the pending asynchronous call
* @param[in] timeout Timeout for dbus call in microseconds
* @return Cookie for the the pending asynchronous call
*
* This is a callback-based way of asynchronously calling a remote D-Bus method.
*
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the bus
* connection I/O event loop thread.
*
* An non-owning, observing async call handle is returned that can be used to query call status or cancel the call.
*
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
*
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) = 0;
/*!
* @brief Calls method on the D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @return RAII-style slot handle representing the ownership of the async call
*
* This is a callback-based way of asynchronously calling a remote D-Bus method.
*
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the bus
* connection I/O event loop thread.
*
* A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot.
* The slot can be used to cancel the method call at a later time by simply destroying it.
*
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
*
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, return_slot_t ) = 0;
/*!
* @brief Calls method on the D-Bus object asynchronously, with custom timeout
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @param[in] timeout Method call timeout (in microseconds)
* @return Observing handle for the the pending asynchronous call
*
* This is a callback-based way of asynchronously calling a remote D-Bus method.
*
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the bus
* connection I/O event loop thread.
*
* An non-owning, observing async call handle is returned that can be used to query call status or cancel the call.
*
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
*
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual PendingAsyncCall callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, uint64_t timeout ) = 0;
/*!
* @brief Calls method on the D-Bus object asynchronously, with custom timeout
*
* @param[in] message Message representing an async method call
* @param[in] asyncReplyCallback Handler for the async reply
* @param[in] timeout Method call timeout (in microseconds)
* @return RAII-style slot handle representing the ownership of the async call
*
* This is a callback-based way of asynchronously calling a remote D-Bus method.
*
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the bus
* connection I/O event loop thread.
*
* A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot.
* The slot can be used to cancel the method call at a later time by simply destroying it.
*
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
*
* Note: To avoid messing with messages, use API on a higher level of abstraction defined below.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, uint64_t timeout
, return_slot_t ) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t)
*/
template <typename _Rep, typename _Period>
PendingAsyncCall callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, const std::chrono::duration<_Rep, _Period>& timeout );
/*!
* @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t,return_slot_t)
*/
template <typename _Rep, typename _Period>
[[nodiscard]] Slot callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, const std::chrono::duration<_Rep, _Period>& timeout
, return_slot_t );
/*!
* @brief Calls method on the D-Bus object asynchronously
*
* @param[in] message Message representing an async method call
* @param[in] Tag denoting a std::future-based overload
* @return Future object providing access to the future method reply message
*
* This is a std::future-based way of asynchronously calling a remote D-Bus method.
*
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided future object will be set to contain the reply (or sdbus::Error
* in case the remote method threw an exception).
*
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
* The call is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided async reply handler will get invoked from the context of the connection
* I/O event loop thread.
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual std::future<MethodReply> callMethodAsync(const MethodCall& message, with_future_t) = 0;
/*!
* @brief Calls method on the D-Bus object asynchronously, with custom timeout
*
* @param[in] message Message representing an async method call
* @param[in] timeout Method call timeout
* @param[in] Tag denoting a std::future-based overload
* @return Future object providing access to the future method reply message
*
* This is a std::future-based way of asynchronously calling a remote D-Bus method.
*
* The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives,
* the provided future object will be set to contain the reply (or sdbus::Error
* in case the remote method threw an exception, or the call timed out).
*
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual std::future<MethodReply> callMethodAsync( const MethodCall& message
, uint64_t timeout
, with_future_t ) = 0;
virtual std::future<MethodReply> callMethod(const MethodCall& message, with_future_t) = 0;
virtual std::future<MethodReply> callMethod(const MethodCall& message, uint64_t timeout, with_future_t) = 0;
/*!
* @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t)
*/
template <typename _Rep, typename _Period>
std::future<MethodReply> callMethodAsync( const MethodCall& message
, const std::chrono::duration<_Rep, _Period>& timeout
, with_future_t );
/*!
* @brief Registers a handler for the desired signal emitted by the D-Bus object
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
* @param[in] signalHandler Callback that implements the body of the signal handler
*
* A signal can be subscribed to at any time during proxy lifetime. The subscription
* is active immediately after the call, and stays active for the entire lifetime
* of the Proxy object.
*
* To be able to unsubscribe from the signal at a later time, use the registerSignalHandler()
* overload with request_slot tag.
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler ) = 0;
/*!
* @brief Registers a handler for the desired signal emitted by the D-Bus object
*
* @param[in] interfaceName Name of an interface that the signal belongs to
* @param[in] signalName Name of the signal
* @param[in] signalHandler Callback that implements the body of the signal handler
*
* @return RAII-style slot handle representing the ownership of the subscription
*
* A signal can be subscribed to and unsubscribed from at any time during proxy
* lifetime. The subscription is active immediately after the call. The lifetime
* of the subscription is bound to the lifetime of the slot object. The subscription
* is unregistered by letting go of the slot object.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler
, return_slot_t ) = 0;
protected: // Internal API for efficiency reasons used by high-level API helper classes
friend MethodInvoker;
friend AsyncMethodInvoker;
friend SignalSubscriber;
[[nodiscard]] virtual MethodCall createMethodCall(const char* interfaceName, const char* methodName) = 0;
virtual void registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler ) = 0;
[[nodiscard]] virtual Slot registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler
, return_slot_t ) = 0;
std::future<MethodReply> callMethod( const MethodCall& message
, const std::chrono::duration<_Rep, _Period>& timeout
, with_future_t );
};
/********************************************//**
@@ -694,14 +463,14 @@ namespace sdbus {
* Pending call in this context means a call whose results have not arrived, or
* have arrived and are currently being processed by the callback handler.
*/
[[nodiscard]] bool isPending() const;
bool isPending() const;
private:
friend internal::Proxy;
PendingAsyncCall(std::weak_ptr<void> callInfo);
PendingAsyncCall(std::weak_ptr<void> callData);
private:
std::weak_ptr<void> callInfo_;
std::weak_ptr<void> callData_;
};
// Out-of-line member definitions
@@ -714,118 +483,61 @@ namespace sdbus {
}
template <typename _Rep, typename _Period>
inline PendingAsyncCall IProxy::callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, const std::chrono::duration<_Rep, _Period>& timeout )
inline PendingAsyncCall IProxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, const std::chrono::duration<_Rep, _Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return callMethodAsync(message, std::move(asyncReplyCallback), microsecs.count());
return callMethod(message, std::move(asyncReplyCallback), microsecs.count());
}
template <typename _Rep, typename _Period>
inline Slot IProxy::callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, const std::chrono::duration<_Rep, _Period>& timeout
, return_slot_t )
inline std::future<MethodReply> IProxy::callMethod( const MethodCall& message
, const std::chrono::duration<_Rep, _Period>& timeout
, with_future_t )
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return callMethodAsync(message, std::move(asyncReplyCallback), microsecs.count(), return_slot);
}
template <typename _Rep, typename _Period>
inline std::future<MethodReply> IProxy::callMethodAsync( const MethodCall& message
, const std::chrono::duration<_Rep, _Period>& timeout
, with_future_t )
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return callMethodAsync(message, microsecs.count(), with_future);
}
inline MethodInvoker IProxy::callMethod(const MethodName& methodName)
{
return MethodInvoker(*this, methodName);
return callMethod(message, microsecs.count(), with_future);
}
inline MethodInvoker IProxy::callMethod(const std::string& methodName)
{
return MethodInvoker(*this, methodName.c_str());
}
inline MethodInvoker IProxy::callMethod(const char* methodName)
{
return MethodInvoker(*this, methodName);
}
inline AsyncMethodInvoker IProxy::callMethodAsync(const MethodName& methodName)
{
return AsyncMethodInvoker(*this, methodName);
}
inline AsyncMethodInvoker IProxy::callMethodAsync(const std::string& methodName)
{
return AsyncMethodInvoker(*this, methodName.c_str());
}
inline AsyncMethodInvoker IProxy::callMethodAsync(const char* methodName)
{
return AsyncMethodInvoker(*this, methodName);
}
inline SignalSubscriber IProxy::uponSignal(const SignalName& signalName)
{
return SignalSubscriber(*this, signalName);
}
inline SignalSubscriber IProxy::uponSignal(const std::string& signalName)
{
return SignalSubscriber(*this, signalName.c_str());
}
inline SignalSubscriber IProxy::uponSignal(const char* signalName)
{
return SignalSubscriber(*this, signalName);
}
inline PropertyGetter IProxy::getProperty(const PropertyName& propertyName)
inline SignalUnsubscriber IProxy::muteSignal(const std::string& signalName)
{
return SignalUnsubscriber(*this, signalName);
}
inline PropertyGetter IProxy::getProperty(const std::string& propertyName)
{
return PropertyGetter(*this, propertyName);
}
inline PropertyGetter IProxy::getProperty(std::string_view propertyName)
{
return PropertyGetter(*this, std::move(propertyName));
}
inline AsyncPropertyGetter IProxy::getPropertyAsync(const PropertyName& propertyName)
inline AsyncPropertyGetter IProxy::getPropertyAsync(const std::string& propertyName)
{
return AsyncPropertyGetter(*this, propertyName);
}
inline AsyncPropertyGetter IProxy::getPropertyAsync(std::string_view propertyName)
{
return AsyncPropertyGetter(*this, std::move(propertyName));
}
inline PropertySetter IProxy::setProperty(const PropertyName& propertyName)
inline PropertySetter IProxy::setProperty(const std::string& propertyName)
{
return PropertySetter(*this, propertyName);
}
inline PropertySetter IProxy::setProperty(std::string_view propertyName)
{
return PropertySetter(*this, std::move(propertyName));
}
inline AsyncPropertySetter IProxy::setPropertyAsync(const PropertyName& propertyName)
inline AsyncPropertySetter IProxy::setPropertyAsync(const std::string& propertyName)
{
return AsyncPropertySetter(*this, propertyName);
}
inline AsyncPropertySetter IProxy::setPropertyAsync(std::string_view propertyName)
{
return AsyncPropertySetter(*this, std::move(propertyName));
}
inline AllPropertiesGetter IProxy::getAllProperties()
{
return AllPropertiesGetter(*this);
@@ -859,8 +571,8 @@ namespace sdbus {
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( sdbus::IConnection& connection
, ServiceName destination
, ObjectPath objectPath );
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
@@ -885,8 +597,8 @@ namespace sdbus {
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath );
, std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
@@ -912,8 +624,8 @@ namespace sdbus {
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<sdbus::IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t );
/*!
@@ -933,8 +645,8 @@ namespace sdbus {
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo");
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
, ObjectPath objectPath );
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath );
/*!
* @brief Creates a proxy object for a specific remote D-Bus object
@@ -954,8 +666,8 @@ namespace sdbus {
* auto proxy = sdbus::createProxy("com.kistler.foo", "/com/kistler/foo", sdbus::dont_run_event_loop_thread );
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
, ObjectPath objectPath
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t );
}
+77 -202
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Message.h
*
@@ -27,27 +27,22 @@
#ifndef SDBUS_CXX_MESSAGE_H_
#define SDBUS_CXX_MESSAGE_H_
#include <sdbus-c++/Error.h>
#include <sdbus-c++/TypeTraits.h>
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <functional>
#include <map>
#ifdef __has_include
# if __has_include(<span>)
# include <span>
# endif
#endif
#include <sdbus-c++/Error.h>
#include <string>
#include <sys/types.h>
#include <vector>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <map>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include <cstdint>
#include <cassert>
#include <functional>
#include <sys/types.h>
#include <algorithm>
// Forward declarations
namespace sdbus {
@@ -59,6 +54,7 @@ namespace sdbus {
class MethodReply;
namespace internal {
class ISdBus;
class IConnection;
}
}
@@ -73,19 +69,13 @@ namespace sdbus {
* Serialization and deserialization functions are provided for types supported
* by D-Bus.
*
* You mostly don't need to work with this class directly if you use high-level
* APIs of @c IObject and @c IProxy.
* You don't need to work with this class directly if you use high-level APIs
* of @c IObject and @c IProxy.
*
***********************************************/
class [[nodiscard]] Message
{
public:
Message(const Message&) noexcept;
Message& operator=(const Message&) noexcept;
Message(Message&& other) noexcept;
Message& operator=(Message&& other) noexcept;
~Message();
Message& operator<<(bool item);
Message& operator<<(int16_t item);
Message& operator<<(int32_t item);
@@ -97,23 +87,19 @@ namespace sdbus {
Message& operator<<(double item);
Message& operator<<(const char *item);
Message& operator<<(const std::string &item);
Message& operator<<(std::string_view item);
Message& operator<<(const Variant &item);
template <typename ...Elements>
Message& operator<<(const std::variant<Elements...>& value);
Message& operator<<(const ObjectPath &item);
Message& operator<<(const Signature &item);
Message& operator<<(const UnixFd &item);
template <typename _Element, typename _Allocator>
Message& operator<<(const std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator<<(const std::array<_Element, _Size>& items);
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator<<(const std::span<_Element, _Extent>& items);
#endif
template <typename _Enum, typename = std::enable_if_t<std::is_enum_v<_Enum>>>
Message& operator<<(const _Enum& item);
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
@@ -135,8 +121,6 @@ namespace sdbus {
Message& operator>>(char*& item);
Message& operator>>(std::string &item);
Message& operator>>(Variant &item);
template <typename ...Elements>
Message& operator>>(std::variant<Elements...>& value);
Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item);
@@ -144,12 +128,10 @@ namespace sdbus {
Message& operator>>(std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator>>(std::array<_Element, _Size>& items);
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator>>(std::span<_Element, _Extent>& items);
#endif
template <typename _Enum, typename = std::enable_if_t<std::is_enum_v<_Enum>>>
Message& operator>>(_Enum& item);
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
@@ -159,38 +141,22 @@ namespace sdbus {
template <typename... _ValueTypes>
Message& operator>>(std::tuple<_ValueTypes...>& item);
template <typename _ElementType>
Message& openContainer();
Message& openContainer(const char* signature);
Message& openContainer(const std::string& signature);
Message& closeContainer();
template <typename _KeyType, typename _ValueType>
Message& openDictEntry();
Message& openDictEntry(const char* signature);
Message& openDictEntry(const std::string& signature);
Message& closeDictEntry();
template <typename _ValueType>
Message& openVariant();
Message& openVariant(const char* signature);
Message& openVariant(const std::string& signature);
Message& closeVariant();
template <typename... _ValueTypes>
Message& openStruct();
Message& openStruct(const char* signature);
Message& openStruct(const std::string& signature);
Message& closeStruct();
template <typename _ElementType>
Message& enterContainer();
Message& enterContainer(const char* signature);
Message& enterContainer(const std::string& signature);
Message& exitContainer();
template <typename _KeyType, typename _ValueType>
Message& enterDictEntry();
Message& enterDictEntry(const char* signature);
Message& enterDictEntry(const std::string& signature);
Message& exitDictEntry();
template <typename _ValueType>
Message& enterVariant();
Message& enterVariant(const char* signature);
Message& enterVariant(const std::string& signature);
Message& exitVariant();
template <typename... _ValueTypes>
Message& enterStruct();
Message& enterStruct(const char* signature);
Message& enterStruct(const std::string& signature);
Message& exitStruct();
Message& appendArray(char type, const void *ptr, size_t size);
@@ -199,13 +165,12 @@ namespace sdbus {
explicit operator bool() const;
void clearFlags();
const char* getInterfaceName() const;
const char* getMemberName() const;
const char* getSender() const;
const char* getPath() const;
const char* getDestination() const;
// TODO: short docs in whole Message API
std::pair<char, const char*> peekType() const;
std::string getInterfaceName() const;
std::string getMemberName() const;
std::string getSender() const;
std::string getPath() const;
std::string getDestination() const;
void peekType(std::string& type, std::string& contents) const;
bool isValid() const;
bool isEmpty() const;
bool isAtEnd(bool complete) const;
@@ -249,6 +214,13 @@ namespace sdbus {
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:
@@ -266,7 +238,9 @@ namespace sdbus {
MethodCall() = default;
MethodReply send(uint64_t timeout) const;
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout, return_slot_t) const;
[[deprecated("Use send overload with floating_slot instead")]] void send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const;
void send(void* callback, void* userData, uint64_t timeout, floating_slot_t) const;
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout) const;
MethodReply createReply() const;
MethodReply createErrorReply(const sdbus::Error& error) const;
@@ -275,11 +249,12 @@ namespace sdbus {
bool doesntExpectReply() const;
protected:
MethodCall(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
MethodCall(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t) noexcept;
private:
MethodReply sendWithReply(uint64_t timeout = 0) const;
MethodReply sendWithNoReply() const;
const internal::IConnection* connection_{};
};
class MethodReply : public Message
@@ -300,7 +275,6 @@ namespace sdbus {
public:
Signal() = default;
void setDestination(const std::string& destination);
void setDestination(const char* destination);
void send() const;
};
@@ -332,19 +306,6 @@ namespace sdbus {
PlainMessage() = default;
};
template <typename ...Elements>
inline Message& Message::operator<<(const std::variant<Elements...>& value)
{
std::visit([this](const auto& inner)
{
openVariant<decltype(inner)>();
*this << inner;
closeVariant();
}, value);
return *this;
}
template <typename _Element, typename _Allocator>
inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
{
@@ -361,7 +322,7 @@ namespace sdbus {
return *this;
}
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator<<(const std::span<_Element, _Extent>& items)
{
@@ -371,12 +332,6 @@ namespace sdbus {
}
#endif
template <typename _Enum, typename>
inline Message& Message::operator<<(const _Enum &item)
{
return operator<<(static_cast<std::underlying_type_t<_Enum>>(item));
}
template <typename _Array>
inline void Message::serializeArray(const _Array& items)
{
@@ -386,12 +341,11 @@ namespace sdbus {
// otherwise use step-by-step serialization of individual elements.
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType));
appendArray(*signature_of<ElementType>::str().c_str(), items.data(), items.size() * sizeof(ElementType));
}
else
{
openContainer<ElementType>();
openContainer(signature_of<ElementType>::str());
for (const auto& item : items)
*this << item;
@@ -420,13 +374,16 @@ namespace sdbus {
inline void Message::serializeDictionary(const _Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using MappedType = typename _Dictionary::mapped_type;
using ValueType = typename _Dictionary::mapped_type;
openContainer<DictEntry<KeyType, MappedType>>();
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
openContainer(arraySignature);
for (const auto& item : items)
{
openDictEntry<KeyType, MappedType>();
openDictEntry(dictEntrySignature);
*this << item.first;
*this << item.second;
closeDictEntry();
@@ -455,7 +412,12 @@ namespace sdbus {
template <typename... _ValueTypes>
inline Message& Message::operator<<(const Struct<_ValueTypes...>& item)
{
openStruct<_ValueTypes...>();
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
assert(structSignature.size() > 2);
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size() - 2);
openStruct(structContentSignature);
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
closeStruct();
@@ -469,33 +431,6 @@ namespace sdbus {
return *this;
}
namespace detail
{
template <typename _Element, typename... _Elements>
bool deserialize_variant(Message& msg, std::variant<_Elements...>& value, const char* signature)
{
constexpr auto elemSignature = as_null_terminated(sdbus::signature_of_v<_Element>);
if (std::strcmp(signature, elemSignature.data()) != 0)
return false;
_Element temp;
msg.enterVariant(signature);
msg >> temp;
msg.exitVariant();
value = std::move(temp);
return true;
}
}
template <typename... Elements>
inline Message& Message::operator>>(std::variant<Elements...>& value)
{
auto [type, contents] = peekType();
bool result = (detail::deserialize_variant<Elements>(*this, value, contents) || ...);
SDBUS_THROW_ERROR_IF(!result, "Failed to deserialize variant: signature did not match any of the variant types", EINVAL);
return *this;
}
template <typename _Element, typename _Allocator>
inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
{
@@ -512,7 +447,7 @@ namespace sdbus {
return *this;
}
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator>>(std::span<_Element, _Extent>& items)
{
@@ -522,15 +457,6 @@ namespace sdbus {
}
#endif
template <typename _Enum, typename>
inline Message& Message::operator>>(_Enum& item)
{
std::underlying_type_t<_Enum> val;
*this >> val;
item = static_cast<_Enum>(val);
return *this;
}
template <typename _Array>
inline void Message::deserializeArray(_Array& items)
{
@@ -556,8 +482,7 @@ namespace sdbus {
size_t arraySize{};
const ElementType* arrayPtr{};
constexpr auto signature = as_null_terminated(sdbus::signature_of_v<ElementType>);
readArray(*signature.data(), (const void**)&arrayPtr, &arraySize);
readArray(*signature_of<ElementType>::str().c_str(), (const void**)&arrayPtr, &arraySize);
size_t elementsInMsg = arraySize / sizeof(ElementType);
bool notEnoughSpace = items.size() < elementsInMsg;
@@ -572,8 +497,7 @@ namespace sdbus {
size_t arraySize{};
const _Element* arrayPtr{};
constexpr auto signature = as_null_terminated(sdbus::signature_of_v<_Element>);
readArray(*signature.data(), (const void**)&arrayPtr, &arraySize);
readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
}
@@ -583,7 +507,7 @@ namespace sdbus {
{
using ElementType = typename _Array::value_type;
if(!enterContainer<ElementType>())
if(!enterContainer(signature_of<ElementType>::str()))
return;
for (auto& elem : items)
@@ -600,7 +524,7 @@ namespace sdbus {
template <typename _Element, typename _Allocator>
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
{
if(!enterContainer<_Element>())
if(!enterContainer(signature_of<_Element>::str()))
return;
while (true)
@@ -637,18 +561,21 @@ namespace sdbus {
inline void Message::deserializeDictionary(_Dictionary& items)
{
using KeyType = typename _Dictionary::key_type;
using MappedType = typename _Dictionary::mapped_type;
using ValueType = typename _Dictionary::mapped_type;
if (!enterContainer<DictEntry<KeyType, MappedType>>())
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
if (!enterContainer(arraySignature))
return;
while (true)
{
if (!enterDictEntry<KeyType, MappedType>())
if (!enterDictEntry(dictEntrySignature))
break;
KeyType key;
MappedType value;
ValueType value;
*this >> key >> value;
items.emplace(std::move(key), std::move(value));
@@ -681,7 +608,11 @@ namespace sdbus {
template <typename... _ValueTypes>
inline Message& Message::operator>>(Struct<_ValueTypes...>& item)
{
if (!enterStruct<_ValueTypes...>())
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
if (!enterStruct(structContentSignature))
return *this;
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
@@ -698,62 +629,6 @@ namespace sdbus {
return *this;
}
template <typename _ElementType>
inline Message& Message::openContainer()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>);
return openContainer(signature.data());
}
template <typename _KeyType, typename _ValueType>
inline Message& Message::openDictEntry()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_KeyType, _ValueType>>);
return openDictEntry(signature.data());
}
template <typename _ValueType>
inline Message& Message::openVariant()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>);
return openVariant(signature.data());
}
template <typename... _ValueTypes>
inline Message& Message::openStruct()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_ValueTypes...>>);
return openStruct(signature.data());
}
template <typename _ElementType>
inline Message& Message::enterContainer()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ElementType>);
return enterContainer(signature.data());
}
template <typename _KeyType, typename _ValueType>
inline Message& Message::enterDictEntry()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_KeyType, _ValueType>>);
return enterDictEntry(signature.data());
}
template <typename _ValueType>
inline Message& Message::enterVariant()
{
constexpr auto signature = as_null_terminated(signature_of_v<_ValueType>);
return enterVariant(signature.data());
}
template <typename... _ValueTypes>
inline Message& Message::enterStruct()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<_ValueTypes...>>);
return enterStruct(signature.data());
}
}
#endif /* SDBUS_CXX_MESSAGE_H_ */
+2 -3
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file MethodResult.h
*
@@ -28,10 +28,9 @@
#define SDBUS_CXX_METHODRESULT_H_
#include <sdbus-c++/Message.h>
#include <cassert>
// Forward declarations
// Forward declaration
namespace sdbus {
class Error;
}
+19 -17
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ProxyInterfaces.h
*
@@ -83,10 +83,9 @@ namespace sdbus {
* methods. So the _Interfaces template parameter is a list of sdbus-c++-xml2cpp-generated
* proxy-side interface classes representing interfaces of the corresponding remote D-Bus object.
*
* In the final adaptor class inherited from ProxyInterfaces, one needs to make sure:
* 1. to call `registerProxy();` in the class constructor, and, conversely,
* 2. to call `unregisterProxy();` in the class destructor,
* so that the signals are subscribed to and unsubscribed from at a proper time.
* 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>
@@ -104,7 +103,7 @@ namespace sdbus {
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
* For more information on its behavior, consult @ref createProxy(std::string,std::string)
*/
ProxyInterfaces(ServiceName destination, ObjectPath objectPath)
ProxyInterfaces(std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
{
@@ -119,7 +118,7 @@ namespace sdbus {
* This constructor overload creates a proxy that manages its own D-Bus connection(s).
* For more information on its behavior, consult @ref createProxy(std::string,std::string,sdbus::dont_run_event_loop_thread_t)
*/
ProxyInterfaces(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
ProxyInterfaces(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
, _Interfaces(getProxy())...
{
@@ -135,7 +134,7 @@ namespace sdbus {
* The proxy created this way just references a D-Bus connection owned and managed by the user.
* For more information on its behavior, consult @ref createProxy(IConnection&,std::string,std::string)
*/
ProxyInterfaces(IConnection& connection, ServiceName destination, ObjectPath objectPath)
ProxyInterfaces(IConnection& connection, std::string destination, std::string objectPath)
: ProxyObjectHolder(createProxy(connection, std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
{
@@ -151,7 +150,7 @@ namespace sdbus {
* The proxy created this way becomes an owner of the connection.
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string)
*/
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, ServiceName destination, ObjectPath objectPath)
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())...
{
@@ -167,22 +166,22 @@ namespace sdbus {
* The proxy created this way becomes an owner of the connection.
* For more information on its behavior, consult @ref createProxy(std::unique_ptr<sdbus::IConnection>&&,std::string,std::string,sdbus::dont_run_event_loop_thread_t)
*/
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
ProxyInterfaces(std::unique_ptr<sdbus::IConnection>&& connection, std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
, _Interfaces(getProxy())...
{
}
/*!
* @brief Registers handlers for D-Bus signals of the remote object
* @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.
*
* See also @ref IProxy::registerSignalHandler()
* For more information, see underlying @ref IProxy::finishRegistration()
*/
void registerProxy()
{
(_Interfaces::registerProxy(), ...);
getProxy().finishRegistration();
}
/*!
@@ -198,17 +197,20 @@ namespace sdbus {
}
/*!
* @brief Returns reference to the underlying IProxy instance
* @brief Returns object path of the underlying DBus object
*/
using ProxyObjectHolder::getProxy;
const std::string& getObjectPath() const
{
return getProxy().getObjectPath();
}
protected:
using base_type = ProxyInterfaces;
ProxyInterfaces(const ProxyInterfaces&) = delete;
ProxyInterfaces& operator=(const ProxyInterfaces&) = delete;
ProxyInterfaces(ProxyInterfaces&&) = delete;
ProxyInterfaces& operator=(ProxyInterfaces&&) = delete;
ProxyInterfaces(ProxyInterfaces&&) = default;
ProxyInterfaces& operator=(ProxyInterfaces&&) = default;
~ProxyInterfaces() = default;
};
+114 -241
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file StandardInterfaces.h
*
@@ -31,7 +31,6 @@
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#include <string>
#include <string_view>
#include <map>
#include <vector>
@@ -40,312 +39,206 @@ namespace sdbus {
// Proxy for peer
class Peer_proxy
{
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
protected:
Peer_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
}
Peer_proxy(const Peer_proxy&) = delete;
Peer_proxy& operator=(const Peer_proxy&) = delete;
Peer_proxy(Peer_proxy&&) = delete;
Peer_proxy& operator=(Peer_proxy&&) = delete;
Peer_proxy(Peer_proxy&&) = default;
Peer_proxy& operator=(Peer_proxy&&) = default;
~Peer_proxy() = default;
void registerProxy()
{
}
public:
void Ping()
{
m_proxy.callMethod("Ping").onInterface(INTERFACE_NAME);
proxy_->callMethod("Ping").onInterface(INTERFACE_NAME);
}
std::string GetMachineId()
{
std::string machineUUID;
m_proxy.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
proxy_->callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
return machineUUID;
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
// Proxy for introspection
class Introspectable_proxy
{
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
protected:
Introspectable_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
}
Introspectable_proxy(const Introspectable_proxy&) = delete;
Introspectable_proxy& operator=(const Introspectable_proxy&) = delete;
Introspectable_proxy(Introspectable_proxy&&) = delete;
Introspectable_proxy& operator=(Introspectable_proxy&&) = delete;
Introspectable_proxy(Introspectable_proxy&&) = default;
Introspectable_proxy& operator=(Introspectable_proxy&&) = default;
~Introspectable_proxy() = default;
void registerProxy()
{
}
public:
std::string Introspect()
{
std::string xml;
m_proxy.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
proxy_->callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
return xml;
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
// Proxy for properties
class Properties_proxy
{
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
protected:
Properties_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
}
Properties_proxy(const Properties_proxy&) = delete;
Properties_proxy& operator=(const Properties_proxy&) = delete;
Properties_proxy(Properties_proxy&&) = delete;
Properties_proxy& operator=(Properties_proxy&&) = delete;
~Properties_proxy() = default;
void registerProxy()
{
m_proxy
.uponSignal("PropertiesChanged")
proxy_
->uponSignal("PropertiesChanged")
.onInterface(INTERFACE_NAME)
.call([this]( const InterfaceName& interfaceName
, const std::map<PropertyName, sdbus::Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties )
.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);
});
}
virtual void onPropertiesChanged( const InterfaceName& interfaceName
, const std::map<PropertyName, sdbus::Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties ) = 0;
Properties_proxy(const Properties_proxy&) = delete;
Properties_proxy& operator=(const Properties_proxy&) = delete;
Properties_proxy(Properties_proxy&&) = default;
Properties_proxy& operator=(Properties_proxy&&) = default;
~Properties_proxy() = default;
virtual void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) = 0;
public:
sdbus::Variant Get(const InterfaceName& interfaceName, const PropertyName& propertyName)
sdbus::Variant Get(const std::string& interfaceName, const std::string& propertyName)
{
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
}
sdbus::Variant Get(std::string_view interfaceName, std::string_view propertyName)
{
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
return proxy_->getProperty(propertyName).onInterface(interfaceName);
}
template <typename _Function>
PendingAsyncCall GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, _Function&& callback)
PendingAsyncCall GetAsync(const std::string& interfaceName, const std::string& propertyName, _Function&& callback)
{
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
}
std::future<sdbus::Variant> GetAsync(const std::string& interfaceName, const std::string& propertyName, with_future_t)
{
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
}
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
{
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value);
}
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, dont_expect_reply_t)
{
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
}
template <typename _Function>
[[nodiscard]] Slot GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, _Function&& callback, return_slot_t)
PendingAsyncCall SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, _Function&& callback)
{
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
}
std::future<void> SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, with_future_t)
{
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
}
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
{
return proxy_->getAllProperties().onInterface(interfaceName);
}
template <typename _Function>
PendingAsyncCall GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback)
PendingAsyncCall GetAllAsync(const std::string& interfaceName, _Function&& callback)
{
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
[[nodiscard]] Slot GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback, return_slot_t)
std::future<std::map<std::string, sdbus::Variant>> GetAllAsync(const std::string& interfaceName, with_future_t)
{
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
}
std::future<sdbus::Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t)
{
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
}
std::future<sdbus::Variant> GetAsync(std::string_view interfaceName, std::string_view propertyName, with_future_t)
{
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
}
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value)
{
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
}
void Set(std::string_view interfaceName, const std::string_view propertyName, const sdbus::Variant& value)
{
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
}
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, dont_expect_reply_t)
{
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
}
void Set(std::string_view interfaceName, const std::string_view propertyName, const sdbus::Variant& value, dont_expect_reply_t)
{
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
}
template <typename _Function>
PendingAsyncCall SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
PendingAsyncCall SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
}
template <typename _Function>
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
}
std::future<void> SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, with_future_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
}
std::future<void> SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, with_future_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
}
std::map<PropertyName, sdbus::Variant> GetAll(const InterfaceName& interfaceName)
{
return m_proxy.getAllProperties().onInterface(interfaceName);
}
std::map<PropertyName, sdbus::Variant> GetAll(std::string_view interfaceName)
{
return m_proxy.getAllProperties().onInterface(interfaceName);
}
template <typename _Function>
PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, _Function&& callback)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, _Function&& callback, return_slot_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
}
template <typename _Function>
PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
}
template <typename _Function>
PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback, return_slot_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
}
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(const InterfaceName& interfaceName, with_future_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
}
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(std::string_view interfaceName, with_future_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
// Proxy for object manager
class ObjectManager_proxy
{
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected:
ObjectManager_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
}
ObjectManager_proxy(const ObjectManager_proxy&) = delete;
ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete;
ObjectManager_proxy(ObjectManager_proxy&&) = delete;
ObjectManager_proxy& operator=(ObjectManager_proxy&&) = delete;
~ObjectManager_proxy() = default;
void registerProxy()
{
m_proxy
.uponSignal("InterfacesAdded")
proxy_
->uponSignal("InterfacesAdded")
.onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath
, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>& interfacesAndProperties )
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
this->onInterfacesAdded(objectPath, interfacesAndProperties);
});
m_proxy
.uponSignal("InterfacesRemoved")
proxy_->uponSignal("InterfacesRemoved")
.onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath
, const std::vector<sdbus::InterfaceName>& interfaces )
, const std::vector<std::string>& interfaces )
{
this->onInterfacesRemoved(objectPath, interfaces);
});
}
ObjectManager_proxy(const ObjectManager_proxy&) = delete;
ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete;
ObjectManager_proxy(ObjectManager_proxy&&) = default;
ObjectManager_proxy& operator=(ObjectManager_proxy&&) = default;
~ObjectManager_proxy() = default;
virtual void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>& interfacesAndProperties) = 0;
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) = 0;
virtual void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<sdbus::InterfaceName>& interfaces) = 0;
, const std::vector<std::string>& interfaces) = 0;
public:
std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>> GetManagedObjects()
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects()
{
std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>> objectsInterfacesAndProperties;
m_proxy.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
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& m_proxy;
sdbus::IProxy* proxy_;
};
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
@@ -355,47 +248,34 @@ namespace sdbus {
// Adaptor for properties
class Properties_adaptor
{
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
protected:
Properties_adaptor(sdbus::IObject& object) : m_object(object)
Properties_adaptor(sdbus::IObject& object)
: object_(&object)
{
}
Properties_adaptor(const Properties_adaptor&) = delete;
Properties_adaptor& operator=(const Properties_adaptor&) = delete;
Properties_adaptor(Properties_adaptor&&) = delete;
Properties_adaptor& operator=(Properties_adaptor&&) = delete;
Properties_adaptor(Properties_adaptor&&) = default;
Properties_adaptor& operator=(Properties_adaptor&&) = default;
~Properties_adaptor() = default;
void registerAdaptor()
{
}
public:
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& properties)
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
{
m_object.emitPropertiesChangedSignal(interfaceName, properties);
object_->emitPropertiesChangedSignal(interfaceName, properties);
}
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& properties)
void emitPropertiesChangedSignal(const std::string& interfaceName)
{
m_object.emitPropertiesChangedSignal(interfaceName, properties);
}
void emitPropertiesChangedSignal(const InterfaceName& interfaceName)
{
m_object.emitPropertiesChangedSignal(interfaceName);
}
void emitPropertiesChangedSignal(const char* interfaceName)
{
m_object.emitPropertiesChangedSignal(interfaceName);
object_->emitPropertiesChangedSignal(interfaceName);
}
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
/*!
@@ -410,27 +290,24 @@ namespace sdbus {
*/
class ObjectManager_adaptor
{
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected:
explicit ObjectManager_adaptor(sdbus::IObject& object) : m_object(object)
explicit ObjectManager_adaptor(sdbus::IObject& object)
: object_(&object)
{
object_->addObjectManager();
}
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor(ObjectManager_adaptor&&) = delete;
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = delete;
ObjectManager_adaptor(ObjectManager_adaptor&&) = default;
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = default;
~ObjectManager_adaptor() = default;
void registerAdaptor()
{
m_object.addObjectManager();
}
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
/*!
@@ -448,21 +325,17 @@ namespace sdbus {
{
protected:
explicit ManagedObject_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
}
ManagedObject_adaptor(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor(ManagedObject_adaptor&&) = delete;
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = delete;
ManagedObject_adaptor(ManagedObject_adaptor&&) = default;
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = default;
~ManagedObject_adaptor() = default;
void registerAdaptor()
{
}
public:
/*!
* @brief Emits InterfacesAdded signal for this object path
@@ -471,7 +344,7 @@ namespace sdbus {
*/
void emitInterfacesAddedSignal()
{
m_object.emitInterfacesAddedSignal();
object_->emitInterfacesAddedSignal();
}
/*!
@@ -479,9 +352,9 @@ namespace sdbus {
*
* See IObject::emitInterfacesAddedSignal().
*/
void emitInterfacesAddedSignal(const std::vector<sdbus::InterfaceName>& interfaces)
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
{
m_object.emitInterfacesAddedSignal(interfaces);
object_->emitInterfacesAddedSignal(interfaces);
}
/*!
@@ -491,7 +364,7 @@ namespace sdbus {
*/
void emitInterfacesRemovedSignal()
{
m_object.emitInterfacesRemovedSignal();
object_->emitInterfacesRemovedSignal();
}
/*!
@@ -499,13 +372,13 @@ namespace sdbus {
*
* See IObject::emitInterfacesRemovedSignal().
*/
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
{
m_object.emitInterfacesRemovedSignal(interfaces);
object_->emitInterfacesRemovedSignal(interfaces);
}
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}
+252 -197
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TypeTraits.h
*
@@ -27,27 +27,19 @@
#ifndef SDBUS_CXX_TYPETRAITS_H_
#define SDBUS_CXX_TYPETRAITS_H_
#include <sdbus-c++/Error.h>
#include <type_traits>
#include <string>
#include <vector>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <map>
#include <unordered_map>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#ifdef __has_include
# if __has_include(<span>)
# include <span>
# endif
#endif
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
// Forward declarations
namespace sdbus {
@@ -56,10 +48,6 @@ namespace sdbus {
class ObjectPath;
class Signature;
class UnixFd;
template<typename _T1, typename _T2> using DictEntry = std::pair<_T1, _T2>;
class BusName;
class InterfaceName;
class MemberName;
class MethodCall;
class MethodReply;
class Signal;
@@ -68,28 +56,30 @@ namespace sdbus {
class PropertyGetReply;
template <typename... _Results> class Result;
class Error;
template <typename _T, typename _Enable = void> struct signature_of;
}
namespace sdbus {
// Callbacks from sdbus-c++
using method_callback = std::function<void(MethodCall msg)>;
using async_reply_handler = std::function<void(MethodReply reply, std::optional<Error> error)>;
using signal_handler = std::function<void(Signal signal)>;
using message_handler = std::function<void(Message msg)>;
using property_set_callback = std::function<void(PropertySetCall msg)>;
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
using signal_handler = std::function<void(Signal& signal)>;
using message_handler = std::function<void(Message& msg)>;
using property_set_callback = std::function<void(PropertySetCall& msg)>;
using property_get_callback = std::function<void(PropertyGetReply& reply)>;
// Type-erased RAII-style handle to callbacks/subscriptions registered to sdbus-c++
using Slot = std::unique_ptr<void, std::function<void(void*)>>;
// Tag specifying that an owning handle (so-called slot) of the logical resource shall be provided to the client
struct return_slot_t { explicit return_slot_t() = default; };
inline constexpr return_slot_t return_slot{};
// Tag specifying that an owning slot handle shall be returned from the function
struct request_slot_t { explicit request_slot_t() = default; };
inline constexpr request_slot_t request_slot{};
// Tag specifying that the library shall own the slot resulting from the call of the function (so-called floating slot)
struct floating_slot_t { explicit floating_slot_t() = default; };
inline constexpr floating_slot_t floating_slot{};
// Deprecated name for the above -- a floating slot
struct dont_request_slot_t { explicit dont_request_slot_t() = default; };
[[deprecated("Replaced by floating_slot")]] inline constexpr dont_request_slot_t dont_request_slot{};
// Tag denoting the assumption that the caller has already obtained message ownership
struct adopt_message_t { explicit adopt_message_t() = default; };
inline constexpr adopt_message_t adopt_message{};
@@ -107,284 +97,354 @@ namespace sdbus {
struct dont_expect_reply_t { explicit dont_expect_reply_t() = default; };
inline constexpr dont_expect_reply_t dont_expect_reply{};
// Helper for static assert
template <class... _T> constexpr bool always_false = false;
// Helper operator+ for concatenation of `std::array`s
template <typename _T, std::size_t _N1, std::size_t _N2>
constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs);
// Template specializations for getting D-Bus signatures from C++ types
template <typename _T>
constexpr auto signature_of_v = signature_of<_T>::value;
template <typename _T, typename _Enable>
struct signature_of
{
static constexpr bool is_valid = false;
static constexpr bool is_trivial_dbus_type = false;
static constexpr void* value = []
static const std::string str()
{
// See using-sdbus-c++.md, section "Extending sdbus-c++ type system",
// on how to teach sdbus-c++ about your custom types
static_assert(always_false<_T>, "Unsupported D-Bus type (specialize `signature_of` for your custom types)");
};
// sizeof(_T) < 0 is here to make compiler not being able to figure out
// the assertion expression before the template instantiation takes place.
static_assert(sizeof(_T) < 0, "Unknown DBus type");
return "";
}
};
template <typename _T>
struct signature_of<const _T> : signature_of<_T>
struct signature_of<const _T>
: public signature_of<_T>
{};
template <typename _T>
struct signature_of<volatile _T> : signature_of<_T>
{};
template <typename _T>
struct signature_of<_T&> : signature_of<_T>
struct signature_of<_T&>
: public signature_of<_T>
{};
template <>
struct signature_of<void>
{
static constexpr std::array<char, 0> value{};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "";
}
};
template <>
struct signature_of<bool>
{
static constexpr std::array value{'b'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "b";
}
};
template <>
struct signature_of<uint8_t>
{
static constexpr std::array value{'y'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "y";
}
};
template <>
struct signature_of<int16_t>
{
static constexpr std::array value{'n'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "n";
}
};
template <>
struct signature_of<uint16_t>
{
static constexpr std::array value{'q'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "q";
}
};
template <>
struct signature_of<int32_t>
{
static constexpr std::array value{'i'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "i";
}
};
template <>
struct signature_of<uint32_t>
{
static constexpr std::array value{'u'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "u";
}
};
template <>
struct signature_of<int64_t>
{
static constexpr std::array value{'x'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "x";
}
};
template <>
struct signature_of<uint64_t>
{
static constexpr std::array value{'t'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "t";
}
};
template <>
struct signature_of<double>
{
static constexpr std::array value{'d'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = true;
static const std::string str()
{
return "d";
}
};
template <>
struct signature_of<char*>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <>
struct signature_of<const char*>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <std::size_t _N>
struct signature_of<char[_N]>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <std::size_t _N>
struct signature_of<const char[_N]>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <>
struct signature_of<std::string>
{
static constexpr std::array value{'s'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "s";
}
};
template <>
struct signature_of<std::string_view> : signature_of<std::string>
{};
template <>
struct signature_of<char*> : signature_of<std::string>
{};
template <>
struct signature_of<const char*> : signature_of<std::string>
{};
template <std::size_t _N>
struct signature_of<char[_N]> : signature_of<std::string>
{};
template <std::size_t _N>
struct signature_of<const char[_N]> : signature_of<std::string>
{};
template <>
struct signature_of<BusName> : signature_of<std::string>
{};
template <>
struct signature_of<InterfaceName> : signature_of<std::string>
{};
template <>
struct signature_of<MemberName> : signature_of<std::string>
{};
template <typename... _ValueTypes>
struct signature_of<Struct<_ValueTypes...>>
{
static constexpr std::array contents = (signature_of_v<_ValueTypes> + ...);
static constexpr std::array value = std::array{'('} + contents + std::array{')'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
std::string signature;
signature += "(";
(signature += ... += signature_of<_ValueTypes>::str());
signature += ")";
return signature;
}
};
template <>
struct signature_of<Variant>
{
static constexpr std::array value{'v'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
};
template <typename... Elements>
struct signature_of<std::variant<Elements...>> : signature_of<Variant>
{};
static const std::string str()
{
return "v";
}
};
template <>
struct signature_of<ObjectPath>
{
static constexpr std::array value{'o'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "o";
}
};
template <>
struct signature_of<Signature>
{
static constexpr std::array value{'g'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "g";
}
};
template <>
struct signature_of<UnixFd>
{
static constexpr std::array value{'h'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
};
template <typename _T1, typename _T2>
struct signature_of<DictEntry<_T1, _T2>>
{
static constexpr std::array value = std::array{'{'} + signature_of_v<std::tuple<_T1, _T2>> + std::array{'}'};
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "h";
}
};
template <typename _Element, typename _Allocator>
struct signature_of<std::vector<_Element, _Allocator>>
{
static constexpr std::array value = std::array{'a'} + signature_of_v<_Element>;
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a" + signature_of<_Element>::str();
}
};
template <typename _Element, std::size_t _Size>
struct signature_of<std::array<_Element, _Size>> : signature_of<std::vector<_Element>>
struct signature_of<std::array<_Element, _Size>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a" + signature_of<_Element>::str();
}
};
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
struct signature_of<std::span<_Element, _Extent>> : signature_of<std::vector<_Element>>
struct signature_of<std::span<_Element, _Extent>>
{
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a" + signature_of<_Element>::str();
}
};
#endif
template <typename _Enum>
struct signature_of<_Enum, typename std::enable_if_t<std::is_enum_v<_Enum>>>
: public signature_of<std::underlying_type_t<_Enum>>
{};
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
struct signature_of<std::map<_Key, _Value, _Compare, _Allocator>>
{
static constexpr std::array contents = signature_of_v<std::tuple<_Key, _Value>>;
static constexpr std::array dict_entry = std::array{'{'} + contents + std::array{'}'};
static constexpr std::array value = std::array{'a'} + dict_entry;
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a{" + signature_of<_Key>::str() + signature_of<_Value>::str() + "}";
}
};
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
struct signature_of<std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>>
: signature_of<std::map<_Key, _Value>>
{
};
template <typename... _Types>
struct signature_of<std::tuple<_Types...>> // A simple concatenation of signatures of _Types
{
static constexpr std::array value = (std::array<char, 0>{} + ... + signature_of_v<_Types>);
static constexpr bool is_valid = false;
static constexpr bool is_valid = true;
static constexpr bool is_trivial_dbus_type = false;
static const std::string str()
{
return "a{" + signature_of<_Key>::str() + signature_of<_Value>::str() + "}";
}
};
// To simplify conversions of arrays to C strings
template <typename _T, std::size_t _N>
constexpr auto as_null_terminated(std::array<_T, _N> arr)
{
return arr + std::array<_T, 1>{0};
}
// Function traits implementation inspired by (c) kennytm,
// https://github.com/kennytm/utils/blob/master/traits.hpp
template <typename _Type>
struct function_traits : function_traits<decltype(&_Type::operator())>
struct function_traits
: public function_traits<decltype(&_Type::operator())>
{};
template <typename _Type>
struct function_traits<const _Type> : function_traits<_Type>
struct function_traits<const _Type>
: public function_traits<_Type>
{};
template <typename _Type>
struct function_traits<_Type&> : function_traits<_Type>
struct function_traits<_Type&>
: public function_traits<_Type>
{};
template <typename _ReturnType, typename... _Args>
@@ -424,62 +484,72 @@ namespace sdbus {
};
template <typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_Args...)> : function_traits_base<_ReturnType, _Args...>
struct function_traits<_ReturnType(_Args...)>
: public function_traits_base<_ReturnType, _Args...>
{
static constexpr bool is_async = false;
static constexpr bool has_error_param = false;
};
template <typename... _Args>
struct function_traits<void(std::optional<Error>, _Args...)> : function_traits_base<void, _Args...>
struct function_traits<void(const Error*, _Args...)>
: public function_traits_base<void, _Args...>
{
static constexpr bool has_error_param = true;
};
template <typename... _Args, typename... _Results>
struct function_traits<void(Result<_Results...>, _Args...)> : function_traits_base<std::tuple<_Results...>, _Args...>
struct function_traits<void(Result<_Results...>, _Args...)>
: public function_traits_base<std::tuple<_Results...>, _Args...>
{
static constexpr bool is_async = true;
using async_result_t = Result<_Results...>;
};
template <typename... _Args, typename... _Results>
struct function_traits<void(Result<_Results...>&&, _Args...)> : function_traits_base<std::tuple<_Results...>, _Args...>
struct function_traits<void(Result<_Results...>&&, _Args...)>
: public function_traits_base<std::tuple<_Results...>, _Args...>
{
static constexpr bool is_async = true;
using async_result_t = Result<_Results...>;
};
template <typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(*)(_Args...)> : function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(*)(_Args...)>
: public function_traits<_ReturnType(_Args...)>
{};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...)> : function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...)>
: public function_traits<_ReturnType(_Args...)>
{
typedef _ClassType& owner_type;
};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const> : function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const>
: public function_traits<_ReturnType(_Args...)>
{
typedef const _ClassType& owner_type;
};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile> : function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile>
: public function_traits<_ReturnType(_Args...)>
{
typedef volatile _ClassType& owner_type;
};
template <typename _ClassType, typename _ReturnType, typename... _Args>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile> : function_traits<_ReturnType(_Args...)>
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile>
: public function_traits<_ReturnType(_Args...)>
{
typedef const volatile _ClassType& owner_type;
};
template <typename FunctionType>
struct function_traits<std::function<FunctionType>> : function_traits<FunctionType>
struct function_traits<std::function<FunctionType>>
: public function_traits<FunctionType>
{};
template <class _Function>
@@ -518,33 +588,45 @@ namespace sdbus {
template <typename _Function>
using tuple_of_function_output_arg_types_t = typename tuple_of_function_output_arg_types<_Function>::type;
template <typename _Function>
struct signature_of_function_input_arguments : signature_of<tuple_of_function_input_arg_types_t<_Function>>
template <typename _Type>
struct aggregate_signature
{
static std::string value_as_string()
static const std::string str()
{
constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_input_arg_types_t<_Function>>);
return signature.data();
return signature_of<std::decay_t<_Type>>::str();
}
};
template <typename... _Types>
struct aggregate_signature<std::tuple<_Types...>>
{
static const std::string str()
{
std::string signature;
(void)(signature += ... += signature_of<std::decay_t<_Types>>::str());
return signature;
}
};
template <typename _Function>
inline auto signature_of_function_input_arguments_v = signature_of_function_input_arguments<_Function>::value_as_string();
template <typename _Function>
struct signature_of_function_output_arguments : signature_of<tuple_of_function_output_arg_types_t<_Function>>
struct signature_of_function_input_arguments
{
static std::string value_as_string()
static const std::string str()
{
constexpr auto signature = as_null_terminated(signature_of_v<tuple_of_function_output_arg_types_t<_Function>>);
return signature.data();
return aggregate_signature<tuple_of_function_input_arg_types_t<_Function>>::str();
}
};
template <typename _Function>
inline auto signature_of_function_output_arguments_v = signature_of_function_output_arguments<_Function>::value_as_string();
struct signature_of_function_output_arguments
{
static const std::string str()
{
return aggregate_signature<tuple_of_function_output_arg_types_t<_Function>>::str();
}
};
// std::future stuff for return values of async calls
template <typename... _Args> struct future_return
{
typedef std::tuple<_Args...> type;
@@ -563,13 +645,6 @@ namespace sdbus {
template <typename... _Args>
using future_return_t = typename future_return<_Args...>::type;
// Credit: Piotr Skotnicki (https://stackoverflow.com/a/57639506)
template <typename, typename>
constexpr bool is_one_of_variants_types = false;
template <typename... _VariantTypes, typename _QueriedType>
constexpr bool is_one_of_variants_types<std::variant<_VariantTypes...>, _QueriedType>
= (std::is_same_v<_QueriedType, _VariantTypes> || ...);
namespace detail
{
@@ -583,12 +658,12 @@ namespace sdbus {
}
template <class _Function, class _Tuple, std::size_t... _I>
decltype(auto) apply_impl( _Function&& f
, std::optional<Error> e
, _Tuple&& t
, std::index_sequence<_I...> )
constexpr decltype(auto) apply_impl( _Function&& f
, const Error* e
, _Tuple&& t
, std::index_sequence<_I...> )
{
return std::forward<_Function>(f)(std::move(e), std::get<_I>(std::forward<_Tuple>(t))...);
return std::forward<_Function>(f)(e, std::get<_I>(std::forward<_Tuple>(t))...);
}
// For non-void returning functions, apply_impl simply returns function return value (a tuple of values).
@@ -629,33 +704,13 @@ namespace sdbus {
// Convert tuple `t' of values into a list of arguments
// and invoke function `f' with those arguments.
template <class _Function, class _Tuple>
decltype(auto) apply(_Function&& f, std::optional<Error> e, _Tuple&& t)
constexpr decltype(auto) apply(_Function&& f, const Error* e, _Tuple&& t)
{
return detail::apply_impl( std::forward<_Function>(f)
, std::move(e)
, e
, std::forward<_Tuple>(t)
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
}
// Convenient concatenation of arrays
template <typename _T, std::size_t _N1, std::size_t _N2>
constexpr std::array<_T, _N1 + _N2> operator+(std::array<_T, _N1> lhs, std::array<_T, _N2> rhs)
{
std::array<_T, _N1 + _N2> result{};
std::size_t index = 0;
for (auto& el : lhs) {
result[index] = std::move(el);
++index;
}
for (auto& el : rhs) {
result[index] = std::move(el);
++index;
}
return result;
}
}
#endif /* SDBUS_CXX_TYPETRAITS_H_ */
+33 -125
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Types.h
*
@@ -29,13 +29,11 @@
#include <sdbus-c++/Message.h>
#include <sdbus-c++/TypeTraits.h>
#include <cstring>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <memory>
#include <tuple>
#include <utility>
namespace sdbus {
@@ -58,19 +56,12 @@ namespace sdbus {
Variant();
template <typename _ValueType>
explicit Variant(const _ValueType& value) : Variant()
{
msg_.openVariant<_ValueType>();
msg_ << value;
msg_.closeVariant();
msg_.seal();
}
template <typename... _Elements>
Variant(const std::variant<_Elements...>& value)
Variant(const _ValueType& value)
: Variant()
{
msg_.openVariant(signature_of<_ValueType>::str());
msg_ << value;
msg_.closeVariant();
msg_.seal();
}
@@ -79,8 +70,7 @@ namespace sdbus {
{
_ValueType val;
msg_.rewind(false);
msg_.enterVariant<_ValueType>();
msg_.enterVariant(signature_of<_ValueType>::str());
msg_ >> val;
msg_.exitVariant();
return val;
@@ -88,32 +78,22 @@ namespace sdbus {
// Only allow conversion operator for true D-Bus type representations in C++
template <typename _ValueType, typename = std::enable_if_t<signature_of<_ValueType>::is_valid>>
explicit operator _ValueType() const
operator _ValueType() const
{
return get<_ValueType>();
}
template <typename... _Elements>
operator std::variant<_Elements...>() const
{
std::variant<_Elements...> result;
msg_.rewind(false);
msg_ >> result;
return result;
}
template <typename _Type>
bool containsValueOfType() const
{
constexpr auto signature = as_null_terminated(signature_of_v<_Type>);
return std::strcmp(signature.data(), peekValueType()) == 0;
return signature_of<_Type>::str() == peekValueType();
}
bool isEmpty() const;
void serializeTo(Message& msg) const;
void deserializeFrom(Message& msg);
const char* peekValueType() const;
std::string peekValueType() const;
private:
mutable PlainMessage msg_{};
@@ -136,12 +116,15 @@ namespace sdbus {
public:
using std::tuple<_ValueTypes...>::tuple;
// Disable constructor if an older then 7.1.0 version of GCC is used
#if !((defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !(__GNUC__ > 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 0)))))
Struct() = default;
explicit Struct(const std::tuple<_ValueTypes...>& t)
: std::tuple<_ValueTypes...>(t)
{
}
#endif
template <std::size_t _I>
auto& get()
@@ -170,107 +153,42 @@ namespace sdbus {
/********************************************//**
* @class ObjectPath
*
* Strong type representing the D-Bus object path
* Representation of object path D-Bus type
*
***********************************************/
class ObjectPath : public std::string
{
public:
ObjectPath() = default;
explicit ObjectPath(std::string value)
: std::string(std::move(value))
using std::string::string;
ObjectPath() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
ObjectPath(const ObjectPath&) = default; // Fixes gcc 8.3 error (deleted copy constructor)
ObjectPath(ObjectPath&&) = default; // Enable move - user-declared copy ctor prevents implicit creation
ObjectPath& operator = (const ObjectPath&) = default; // Fixes gcc 8.3 error (deleted copy assignment)
ObjectPath& operator = (ObjectPath&&) = default; // Enable move - user-declared copy assign prevents implicit creation
ObjectPath(std::string path)
: std::string(std::move(path))
{}
explicit ObjectPath(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
/********************************************//**
* @class BusName
*
* Strong type representing the D-Bus bus/service/connection name
*
***********************************************/
class BusName : public std::string
{
public:
BusName() = default;
explicit BusName(std::string value)
: std::string(std::move(value))
{}
explicit BusName(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
using ServiceName = BusName;
using ConnectionName = BusName;
/********************************************//**
* @class InterfaceName
*
* Strong type representing the D-Bus interface name
*
***********************************************/
class InterfaceName : public std::string
{
public:
InterfaceName() = default;
explicit InterfaceName(std::string value)
: std::string(std::move(value))
{}
explicit InterfaceName(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
/********************************************//**
* @class MemberName
*
* Strong type representing the D-Bus member name
*
***********************************************/
class MemberName : public std::string
{
public:
MemberName() = default;
explicit MemberName(std::string value)
: std::string(std::move(value))
{}
explicit MemberName(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
using MethodName = MemberName;
using SignalName = MemberName;
using PropertyName = MemberName;
/********************************************//**
* @class Signature
*
* Strong type representing the D-Bus object path
* Representation of Signature D-Bus type
*
***********************************************/
class Signature : public std::string
{
public:
Signature() = default;
explicit Signature(std::string value)
: std::string(std::move(value))
using std::string::string;
Signature() = default; // Fixes gcc 6.3 error (default c-tor is not imported in above using declaration)
Signature(const Signature&) = default; // Fixes gcc 8.3 error (deleted copy constructor)
Signature(Signature&&) = default; // Enable move - user-declared copy ctor prevents implicit creation
Signature& operator = (const Signature&) = default; // Fixes gcc 8.3 error (deleted copy assignment)
Signature& operator = (Signature&&) = default; // Enable move - user-declared copy assign prevents implicit creation
Signature(std::string path)
: std::string(std::move(path))
{}
explicit Signature(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
@@ -337,7 +255,7 @@ namespace sdbus {
close();
}
[[nodiscard]] int get() const
int get() const
{
return fd_;
}
@@ -357,7 +275,7 @@ namespace sdbus {
return std::exchange(fd_, -1);
}
[[nodiscard]] bool isValid() const
bool isValid() const
{
return fd_ >= 0;
}
@@ -373,16 +291,6 @@ namespace sdbus {
int fd_ = -1;
};
/********************************************//**
* @typedef DictEntry
*
* DictEntry is implemented as std::pair, a standard
* value_type in STL(-like) associative containers.
*
***********************************************/
template<typename _T1, typename _T2>
using DictEntry = std::pair<_T1, _T2>;
}
template <size_t _I, typename... _ValueTypes>
-112
View File
@@ -1,112 +0,0 @@
/**
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file VTableItems.h
*
* Created on: Dec 14, 2023
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CXX_VTABLEITEMS_H_
#define SDBUS_CXX_VTABLEITEMS_H_
#include <sdbus-c++/Flags.h>
#include <sdbus-c++/Types.h>
#include <sdbus-c++/TypeTraits.h>
#include <string>
#include <variant>
#include <vector>
namespace sdbus {
struct MethodVTableItem
{
template <typename _Function> MethodVTableItem& implementedAs(_Function&& callback);
MethodVTableItem& withInputParamNames(std::vector<std::string> names);
template <typename... _String> MethodVTableItem& withInputParamNames(_String... names);
MethodVTableItem& withOutputParamNames(std::vector<std::string> names);
template <typename... _String> MethodVTableItem& withOutputParamNames(_String... names);
MethodVTableItem& markAsDeprecated();
MethodVTableItem& markAsPrivileged();
MethodVTableItem& withNoReply();
MethodName name;
Signature inputSignature;
std::vector<std::string> inputParamNames;
Signature outputSignature;
std::vector<std::string> outputParamNames;
method_callback callbackHandler;
Flags flags;
};
MethodVTableItem registerMethod(MethodName methodName);
MethodVTableItem registerMethod(std::string methodName);
struct SignalVTableItem
{
template <typename... _Args> SignalVTableItem& withParameters();
template <typename... _Args> SignalVTableItem& withParameters(std::vector<std::string> names);
template <typename... _Args, typename... _String> SignalVTableItem& withParameters(_String... names);
SignalVTableItem& markAsDeprecated();
SignalName name;
Signature signature;
std::vector<std::string> paramNames;
Flags flags;
};
SignalVTableItem registerSignal(SignalName signalName);
SignalVTableItem registerSignal(std::string signalName);
struct PropertyVTableItem
{
template <typename _Function> PropertyVTableItem& withGetter(_Function&& callback);
template <typename _Function> PropertyVTableItem& withSetter(_Function&& callback);
PropertyVTableItem& markAsDeprecated();
PropertyVTableItem& markAsPrivileged();
PropertyVTableItem& withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
PropertyName name;
Signature signature;
property_get_callback getter;
property_set_callback setter;
Flags flags;
};
PropertyVTableItem registerProperty(PropertyName propertyName);
PropertyVTableItem registerProperty(std::string propertyName);
struct InterfaceFlagsVTableItem
{
InterfaceFlagsVTableItem& markAsDeprecated();
InterfaceFlagsVTableItem& markAsPrivileged();
InterfaceFlagsVTableItem& withNoReplyMethods();
InterfaceFlagsVTableItem& withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior);
Flags flags;
};
InterfaceFlagsVTableItem setInterfaceFlags();
using VTableItem = std::variant<MethodVTableItem, SignalVTableItem, PropertyVTableItem, InterfaceFlagsVTableItem>;
} // namespace sdbus
#endif /* SDBUS_CXX_VTABLEITEMS_H_ */
-301
View File
@@ -1,301 +0,0 @@
/**
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file VTableItems.inl
*
* Created on: Dec 14, 2023
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SDBUS_CPP_VTABLEITEMS_INL_
#define SDBUS_CPP_VTABLEITEMS_INL_
#include <sdbus-c++/Error.h>
#include <sdbus-c++/TypeTraits.h>
#include <string>
#include <type_traits>
#include <vector>
namespace sdbus {
/*** -------------------- ***/
/*** Method VTable Item ***/
/*** -------------------- ***/
template <typename _Function>
MethodVTableItem& MethodVTableItem::implementedAs(_Function&& callback)
{
inputSignature = signature_of_function_input_arguments_v<_Function>;
outputSignature = signature_of_function_output_arguments_v<_Function>;
callbackHandler = [callback = std::forward<_Function>(callback)](MethodCall call)
{
// Create a tuple of callback input arguments' types, which will be used
// as a storage for the argument values deserialized from the message.
tuple_of_function_input_arg_types_t<_Function> inputArgs;
// Deserialize input arguments from the message into the tuple.
call >> inputArgs;
if constexpr (!is_async_method_v<_Function>)
{
// Invoke callback with input arguments from the tuple.
auto ret = sdbus::apply(callback, inputArgs);
// Store output arguments to the reply message and send it back.
auto reply = call.createReply();
reply << ret;
reply.send();
}
else
{
// Invoke callback with input arguments from the tuple and with result object to be set later
using AsyncResult = typename function_traits<_Function>::async_result_t;
sdbus::apply(callback, AsyncResult{std::move(call)}, std::move(inputArgs));
}
};
return *this;
}
inline MethodVTableItem& MethodVTableItem::withInputParamNames(std::vector<std::string> names)
{
inputParamNames = std::move(names);
return *this;
}
template <typename... _String>
inline MethodVTableItem& MethodVTableItem::withInputParamNames(_String... names)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withInputParamNames({names...});
}
inline MethodVTableItem& MethodVTableItem::withOutputParamNames(std::vector<std::string> names)
{
outputParamNames = std::move(names);
return *this;
}
template <typename... _String>
inline MethodVTableItem& MethodVTableItem::withOutputParamNames(_String... names)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withOutputParamNames({names...});
}
inline MethodVTableItem& MethodVTableItem::markAsDeprecated()
{
flags.set(Flags::DEPRECATED);
return *this;
}
inline MethodVTableItem& MethodVTableItem::markAsPrivileged()
{
flags.set(Flags::PRIVILEGED);
return *this;
}
inline MethodVTableItem& MethodVTableItem::withNoReply()
{
flags.set(Flags::METHOD_NO_REPLY);
return *this;
}
inline MethodVTableItem registerMethod(MethodName methodName)
{
return {std::move(methodName), {}, {}, {}, {}, {}, {}};
}
inline MethodVTableItem registerMethod(std::string methodName)
{
return registerMethod(MethodName{std::move(methodName)});
}
/*** -------------------- ***/
/*** Signal VTable Item ***/
/*** -------------------- ***/
template <typename... _Args>
inline SignalVTableItem& SignalVTableItem::withParameters()
{
signature = signature_of_function_input_arguments_v<void(_Args...)>;
return *this;
}
template <typename... _Args>
inline SignalVTableItem& SignalVTableItem::withParameters(std::vector<std::string> names)
{
paramNames = std::move(names);
return withParameters<_Args...>();
}
template <typename... _Args, typename... _String>
inline SignalVTableItem& SignalVTableItem::withParameters(_String... names)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
static_assert(sizeof...(_Args) == sizeof...(_String), "Numbers of signal parameters and their names don't match");
return withParameters<_Args...>({names...});
}
inline SignalVTableItem& SignalVTableItem::markAsDeprecated()
{
flags.set(Flags::DEPRECATED);
return *this;
}
inline SignalVTableItem registerSignal(SignalName signalName)
{
return {std::move(signalName), {}, {}, {}};
}
inline SignalVTableItem registerSignal(std::string signalName)
{
return registerSignal(SignalName{std::move(signalName)});
}
/*** -------------------- ***/
/*** Property VTable Item ***/
/*** -------------------- ***/
template <typename _Function>
inline PropertyVTableItem& PropertyVTableItem::withGetter(_Function&& callback)
{
static_assert(function_argument_count_v<_Function> == 0, "Property getter function must not take any arguments");
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
if (signature.empty())
signature = signature_of_function_output_arguments_v<_Function>;
getter = [callback = std::forward<_Function>(callback)](PropertyGetReply& reply)
{
// Get the propety value and serialize it into the pre-constructed reply message
reply << callback();
};
return *this;
}
template <typename _Function>
inline PropertyVTableItem& PropertyVTableItem::withSetter(_Function&& callback)
{
static_assert(function_argument_count_v<_Function> == 1, "Property setter function must take one parameter - the property value");
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
if (signature.empty())
signature = signature_of_function_input_arguments_v<_Function>;
setter = [callback = std::forward<_Function>(callback)](PropertySetCall call)
{
// Default-construct property value
using property_type = function_argument_t<_Function, 0>;
std::decay_t<property_type> property;
// Deserialize property value from the incoming call message
call >> property;
// Invoke setter with the value
callback(property);
};
return *this;
}
inline PropertyVTableItem& PropertyVTableItem::markAsDeprecated()
{
flags.set(Flags::DEPRECATED);
return *this;
}
inline PropertyVTableItem& PropertyVTableItem::markAsPrivileged()
{
flags.set(Flags::PRIVILEGED);
return *this;
}
inline PropertyVTableItem& PropertyVTableItem::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
{
flags.set(behavior);
return *this;
}
inline PropertyVTableItem registerProperty(PropertyName propertyName)
{
return {std::move(propertyName), {}, {}, {}, {}};
}
inline PropertyVTableItem registerProperty(std::string propertyName)
{
return registerProperty(PropertyName{std::move(propertyName)});
}
/*** --------------------------- ***/
/*** Interface Flags VTable Item ***/
/*** --------------------------- ***/
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsDeprecated()
{
flags.set(Flags::DEPRECATED);
return *this;
}
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::markAsPrivileged()
{
flags.set(Flags::PRIVILEGED);
return *this;
}
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withNoReplyMethods()
{
flags.set(Flags::METHOD_NO_REPLY);
return *this;
}
inline InterfaceFlagsVTableItem& InterfaceFlagsVTableItem::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
{
flags.set(behavior);
return *this;
}
inline InterfaceFlagsVTableItem setInterfaceFlags()
{
return {};
}
} // namespace sdbus
#endif /* SDBUS_CPP_VTABLEITEMS_INL_ */
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file sdbus-c++.h
*
+259 -469
View File
File diff suppressed because it is too large Load Diff
+67 -123
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Connection.h
*
@@ -27,32 +27,18 @@
#ifndef SDBUS_CXX_INTERNAL_CONNECTION_H_
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Message.h"
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Message.h>
#include "IConnection.h"
#include "ISdBus.h"
#include "ScopeGuard.h"
#include <memory>
#include <string>
#include "ISdBus.h"
#include SDBUS_HEADER
#include <memory>
#include <thread>
#include <string>
#include <vector>
// Forward declarations
struct sd_event_source;
namespace sdbus {
class ObjectPath;
class InterfaceName;
class BusName;
using ServiceName = BusName;
class MemberName;
using MethodName = MemberName;
using SignalName = MemberName;
using PropertyName = MemberName;
}
#include <atomic>
#include <mutex>
namespace sdbus::internal {
@@ -92,82 +78,62 @@ namespace sdbus::internal {
Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t);
~Connection() override;
void requestName(const ServiceName & name) override;
void releaseName(const ServiceName& name) override;
[[nodiscard]] BusName getUniqueName() const override;
void requestName(const std::string& name) override;
void releaseName(const std::string& name) override;
std::string getUniqueName() const override;
void enterEventLoop() override;
void enterEventLoopAsync() override;
void leaveEventLoop() override;
[[nodiscard]] PollData getEventLoopPollData() const override;
bool processPendingEvent() override;
Message getCurrentlyProcessedMessage() const override;
PollData getEventLoopPollData() const override;
bool processPendingRequest() override;
void addObjectManager(const ObjectPath& objectPath) override;
Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) override;
void addObjectManager(const std::string& objectPath) override;
void addObjectManager(const std::string& objectPath, floating_slot_t) override;
Slot addObjectManager(const std::string& objectPath, request_slot_t) override;
void setMethodCallTimeout(uint64_t timeout) override;
[[nodiscard]] uint64_t getMethodCallTimeout() const override;
uint64_t getMethodCallTimeout() const override;
void addMatch(const std::string& match, message_handler callback) override;
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback, return_slot_t) override;
void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override;
[[nodiscard]] Slot addMatchAsync( const std::string& match
, message_handler callback
, message_handler installCallback
, return_slot_t ) override;
[[nodiscard]] Slot addMatch(const std::string& match, message_handler callback) override;
void addMatch(const std::string& match, message_handler callback, floating_slot_t) override;
[[nodiscard]] Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) override;
void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) override;
void attachSdEventLoop(sd_event *event, int priority) override;
void detachSdEventLoop() override;
sd_event *getSdEventLoop() override;
const ISdBus& getSdBusInterface() const override;
ISdBus& getSdBusInterface() override;
[[nodiscard]] const ISdBus& getSdBusInterface() const override;
[[nodiscard]] ISdBus& getSdBusInterface() override;
Slot addObjectVTable( const ObjectPath& objectPath
, const InterfaceName& interfaceName
Slot addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData
, return_slot_t ) override;
, void* userData ) override;
[[nodiscard]] PlainMessage createPlainMessage() const override;
[[nodiscard]] MethodCall createMethodCall( const ServiceName& destination
, const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const MethodName& methodName ) const override;
[[nodiscard]] MethodCall createMethodCall( const char* destination
, const char* objectPath
, const char* interfaceName
, const char* methodName ) const override;
[[nodiscard]] Signal createSignal( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const SignalName& signalName ) const override;
[[nodiscard]] Signal createSignal( const char* objectPath
, const char* interfaceName
, const char* signalName ) const override;
PlainMessage createPlainMessage() const override;
MethodCall createMethodCall( const std::string& destination
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& methodName ) const override;
Signal createSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName ) const override;
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
Slot callMethod(const MethodCall& message, void* callback, void* userData, uint64_t timeout, return_slot_t) override;
void emitPropertiesChangedSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::vector<std::string>& propNames ) override;
void emitInterfacesAddedSignal(const std::string& objectPath) override;
void emitInterfacesAddedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) override;
void emitInterfacesRemovedSignal(const std::string& objectPath) override;
void emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) override;
void emitPropertiesChangedSignal( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const std::vector<PropertyName>& propNames ) override;
void emitPropertiesChangedSignal( const char* objectPath
, const char* interfaceName
, const std::vector<PropertyName>& propNames ) override;
void emitInterfacesAddedSignal(const ObjectPath& objectPath) override;
void emitInterfacesAddedSignal( const ObjectPath& objectPath
, const std::vector<InterfaceName>& interfaces ) override;
void emitInterfacesRemovedSignal(const ObjectPath& objectPath) override;
void emitInterfacesRemovedSignal( const ObjectPath& objectPath
, const std::vector<InterfaceName>& interfaces ) override;
Slot registerSignalHandler( const char* sender
, const char* objectPath
, const char* interfaceName
, const char* signalName
Slot registerSignalHandler( const std::string& sender
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData
, return_slot_t ) override;
, void* userData ) override;
MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) override;
private:
using BusFactory = std::function<int(sd_bus**)>;
@@ -177,42 +143,27 @@ namespace sdbus::internal {
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
BusPtr openPseudoBus();
void finishHandshake(sd_bus* bus);
bool waitForNextEvent();
bool waitForNextRequest();
static std::string composeSignalMatchFilter( const std::string &sender
, const std::string &objectPath
, const std::string &interfaceName
, const std::string &signalName);
void notifyEventLoop(int fd) const;
void notifyEventLoopToExit() const;
void clearEventLoopNotification(int fd) const;
void notifyEventLoopNewTimeout() const override;
[[nodiscard]] bool arePendingMessagesInReadQueue() const;
void notifyEventLoopToExit();
void notifyEventLoopToWakeUpFromPoll();
void wakeUpEventLoopIfMessagesInQueue();
void joinWithEventLoop();
template <typename StringBasedType>
static std::vector</*const */char*> to_strv(const std::vector<StringBasedType>& strings);
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
static int sdbus_match_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_match_install_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
private:
#ifndef SDBUS_basu // sd_event integration is not supported if instead of libsystemd we are based on basu
Slot createSdEventSlot(sd_event *event);
Slot createSdTimeEventSourceSlot(sd_event *event, int priority);
Slot createSdIoEventSourceSlot(sd_event *event, int fd, int priority);
Slot createSdInternalEventSourceSlot(sd_event *event, int fd, int priority);
static void deleteSdEventSource(sd_event_source *s);
static int onSdTimerEvent(sd_event_source *s, uint64_t usec, void *userdata);
static int onSdIoEvent(sd_event_source *s, int fd, uint32_t revents, void *userdata);
static int onSdInternalEvent(sd_event_source *s, int fd, uint32_t revents, void *userdata);
static int onSdEventPrepare(sd_event_source *s, void *userdata);
#endif
struct EventFd
{
EventFd();
~EventFd();
void notify();
bool clear();
int fd{-1};
};
@@ -221,26 +172,19 @@ namespace sdbus::internal {
message_handler callback;
message_handler installCallback;
Connection& connection;
Slot slot;
};
// sd-event integration
struct SdEvent
{
Slot sdEvent;
Slot sdTimeEventSource;
Slot sdIoEventSource;
Slot sdInternalEventSource;
sd_bus_slot *slot;
};
private:
std::unique_ptr<ISdBus> sdbus_;
std::unique_ptr<ISdBus> iface_;
BusPtr bus_;
std::thread asyncLoopThread_;
EventFd loopExitFd_; // To wake up event loop I/O polling to exit
EventFd eventFd_; // To wake up event loop I/O polling to re-enter poll with fresh PollData values
std::atomic<std::thread::id> loopThreadId_;
std::mutex loopMutex_;
EventFd loopExitFd_;
EventFd eventFd_;
std::atomic<uint64_t> activeTimeout_{};
std::vector<Slot> floatingMatchRules_;
std::unique_ptr<SdEvent> sdEvent_; // Integration of systemd sd-event event loop implementation
};
}
+8 -14
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Error.cpp
*
@@ -24,26 +24,20 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sdbus-c++/Error.h"
#include "ScopeGuard.h"
#include <sdbus-c++/Error.h>
#include SDBUS_HEADER
#include "ScopeGuard.h"
namespace sdbus
{
sdbus::Error createError(int errNo, std::string customMsg)
sdbus::Error createError(int errNo, const std::string& customMsg)
{
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
sd_bus_error_set_errno(&sdbusError, errNo);
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
Error::Name name(sdbusError.name);
std::string message(std::move(customMsg));
message.append(" (");
message.append(sdbusError.message);
message.append(")");
return Error(std::move(name), std::move(message));
std::string name(sdbusError.name);
std::string message(customMsg + " (" + sdbusError.message + ")");
return sdbus::Error(name, message);
}
} // namespace sdbus
}
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Flags.cpp
*
+39 -62
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file IConnection.h
*
@@ -27,30 +27,19 @@
#ifndef SDBUS_CXX_INTERNAL_ICONNECTION_H_
#define SDBUS_CXX_INTERNAL_ICONNECTION_H_
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/TypeTraits.h"
#include <functional>
#include <memory>
#include <string>
#include <sdbus-c++/IConnection.h>
#include SDBUS_HEADER
#include <string>
#include <memory>
#include <functional>
#include <vector>
// Forward declarations
// Forward declaration
namespace sdbus {
class MethodCall;
class MethodReply;
class Signal;
class PlainMessage;
class ObjectPath;
class InterfaceName;
class BusName;
using ServiceName = BusName;
class MemberName;
using MethodName = MemberName;
using SignalName = MemberName;
using PropertyName = MemberName;
namespace internal {
class ISdBus;
}
@@ -64,60 +53,48 @@ namespace sdbus::internal {
public:
~IConnection() override = default;
[[nodiscard]] virtual const ISdBus& getSdBusInterface() const = 0;
[[nodiscard]] virtual ISdBus& getSdBusInterface() = 0;
virtual const ISdBus& getSdBusInterface() const = 0;
virtual ISdBus& getSdBusInterface() = 0;
[[nodiscard]] virtual Slot addObjectVTable( const ObjectPath& objectPath
, const InterfaceName& interfaceName
[[nodiscard]] virtual Slot addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
, const sd_bus_vtable* vtable
, void* userData
, return_slot_t ) = 0;
, void* userData ) = 0;
[[nodiscard]] virtual PlainMessage createPlainMessage() const = 0;
[[nodiscard]] virtual MethodCall createMethodCall( const ServiceName& destination
, const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const MethodName& methodName ) const = 0;
[[nodiscard]] virtual MethodCall createMethodCall( const char* destination
, const char* objectPath
, const char* interfaceName
, const char* methodName ) const = 0;
[[nodiscard]] virtual Signal createSignal( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const SignalName& signalName ) const = 0;
[[nodiscard]] virtual Signal createSignal( const char* objectPath
, const char* interfaceName
, const char* signalName ) const = 0;
virtual 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 MethodReply callMethod(const MethodCall& message, uint64_t timeout) = 0;
[[nodiscard]] virtual Slot callMethod( const MethodCall& message
, void* callback
, void* userData
, uint64_t timeout
, return_slot_t ) = 0;
virtual void emitPropertiesChangedSignal( const std::string& objectPath
, const std::string& interfaceName
, const std::vector<std::string>& propNames ) = 0;
virtual void emitInterfacesAddedSignal(const std::string& objectPath) = 0;
virtual void emitInterfacesAddedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) = 0;
virtual void emitInterfacesRemovedSignal(const std::string& objectPath) = 0;
virtual void emitInterfacesRemovedSignal( const std::string& objectPath
, const std::vector<std::string>& interfaces ) = 0;
virtual void emitPropertiesChangedSignal( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const std::vector<PropertyName>& propNames ) = 0;
virtual void emitPropertiesChangedSignal( const char* objectPath
, const char* interfaceName
, const std::vector<PropertyName>& propNames ) = 0;
virtual void emitInterfacesAddedSignal(const ObjectPath& objectPath) = 0;
virtual void emitInterfacesAddedSignal( const ObjectPath& objectPath
, const std::vector<InterfaceName>& interfaces ) = 0;
virtual void emitInterfacesRemovedSignal(const ObjectPath& objectPath) = 0;
virtual void emitInterfacesRemovedSignal( const ObjectPath& objectPath
, const std::vector<InterfaceName>& interfaces ) = 0;
using sdbus::IConnection::addObjectManager;
[[nodiscard]] virtual Slot addObjectManager(const std::string& objectPath, request_slot_t) = 0;
[[nodiscard]] virtual Slot registerSignalHandler( const char* sender
, const char* objectPath
, const char* interfaceName
, const char* signalName
[[nodiscard]] virtual Slot registerSignalHandler( const std::string& sender
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
, sd_bus_message_handler_t callback
, void* userData
, return_slot_t ) = 0;
, void* userData ) = 0;
virtual void notifyEventLoopNewTimeout() const = 0;
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0;
};
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createConnection();
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createPseudoConnection();
}
+2 -4
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ISdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -81,16 +81,14 @@ namespace sdbus::internal {
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) = 0;
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) = 0;
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) = 0;
virtual int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata) = 0;
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) = 0;
virtual int sd_bus_new(sd_bus **ret) = 0;
virtual int sd_bus_start(sd_bus *bus) = 0;
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) = 0;
virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) = 0;
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0;
virtual int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret) = 0;
virtual int sd_bus_flush(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) = 0;
+70 -65
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Message.cpp
*
@@ -24,19 +24,15 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sdbus-c++/Message.h"
#include "sdbus-c++/Error.h"
#include "sdbus-c++/Types.h"
#include "IConnection.h"
#include "ISdBus.h"
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Types.h>
#include <sdbus-c++/Error.h>
#include "MessageUtils.h"
#include "ISdBus.h"
#include "IConnection.h"
#include "ScopeGuard.h"
#include <cassert>
#include <cstring>
#include SDBUS_HEADER
#include <cassert>
namespace sdbus {
@@ -198,17 +194,6 @@ Message& Message::operator<<(const std::string& item)
return *this;
}
Message& Message::operator<<(std::string_view item)
{
char* destPtr{};
auto r = sd_bus_message_append_string_space((sd_bus_message*)msg_, item.length(), &destPtr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a string_view value", -r);
std::memcpy(destPtr, item.data(), item.length());
return *this;
}
Message& Message::operator<<(const Variant &item)
{
item.serializeTo(*this);
@@ -441,9 +426,10 @@ Message& Message::operator>>(UnixFd &item)
return *this;
}
Message& Message::openContainer(const char* signature)
Message& Message::openContainer(const std::string& signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature);
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a container", -r);
return *this;
@@ -457,9 +443,9 @@ Message& Message::closeContainer()
return *this;
}
Message& Message::openDictEntry(const char* signature)
Message& Message::openDictEntry(const std::string& signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature);
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a dictionary entry", -r);
return *this;
@@ -473,9 +459,9 @@ Message& Message::closeDictEntry()
return *this;
}
Message& Message::openVariant(const char* signature)
Message& Message::openVariant(const std::string& signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature);
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a variant", -r);
return *this;
@@ -489,9 +475,9 @@ Message& Message::closeVariant()
return *this;
}
Message& Message::openStruct(const char* signature)
Message& Message::openStruct(const std::string& signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature);
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a struct", -r);
return *this;
@@ -505,9 +491,10 @@ Message& Message::closeStruct()
return *this;
}
Message& Message::enterContainer(const char* signature)
Message& Message::enterContainer(const std::string& signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature);
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
if (r == 0)
ok_ = false;
@@ -524,9 +511,9 @@ Message& Message::exitContainer()
return *this;
}
Message& Message::enterDictEntry(const char* signature)
Message& Message::enterDictEntry(const std::string& signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature);
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
if (r == 0)
ok_ = false;
@@ -543,9 +530,9 @@ Message& Message::exitDictEntry()
return *this;
}
Message& Message::enterVariant(const char* signature)
Message& Message::enterVariant(const std::string& signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature);
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
if (r == 0)
ok_ = false;
@@ -562,9 +549,9 @@ Message& Message::exitVariant()
return *this;
}
Message& Message::enterStruct(const char* signature)
Message& Message::enterStruct(const std::string& signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature);
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
if (r == 0)
ok_ = false;
@@ -612,38 +599,43 @@ void Message::rewind(bool complete)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
}
const char* Message::getInterfaceName() const
std::string Message::getInterfaceName() const
{
return sd_bus_message_get_interface((sd_bus_message*)msg_);
auto interface = sd_bus_message_get_interface((sd_bus_message*)msg_);
return interface != nullptr ? interface : "";
}
const char* Message::getMemberName() const
std::string Message::getMemberName() const
{
return sd_bus_message_get_member((sd_bus_message*)msg_);
auto member = sd_bus_message_get_member((sd_bus_message*)msg_);
return member != nullptr ? member : "";
}
const char* Message::getSender() const
std::string Message::getSender() const
{
return sd_bus_message_get_sender((sd_bus_message*)msg_);
}
const char* Message::getPath() const
std::string Message::getPath() const
{
return sd_bus_message_get_path((sd_bus_message*)msg_);
auto path = sd_bus_message_get_path((sd_bus_message*)msg_);
return path != nullptr ? path : "";
}
const char* Message::getDestination() const
std::string Message::getDestination() const
{
return sd_bus_message_get_destination((sd_bus_message*)msg_);
auto destination = sd_bus_message_get_destination((sd_bus_message*)msg_);
return destination != nullptr ? destination : "";
}
std::pair<char, const char*> Message::peekType() const
void Message::peekType(std::string& type, std::string& contents) const
{
char typeSignature{};
const char* contentsSignature{};
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSignature, &contentsSignature);
char typeSig;
const char* contentsSig;
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r);
return {typeSignature, contentsSignature};
type = typeSig;
contents = contentsSig ? contentsSig : "";
}
bool Message::isValid() const
@@ -771,9 +763,12 @@ std::string Message::getSELinuxContext() const
MethodCall::MethodCall( void *msg
, internal::ISdBus *sdbus
, const internal::IConnection *connection
, adopt_message_t) noexcept
: Message(msg, sdbus, adopt_message)
, connection_(connection)
{
assert(connection_ != nullptr);
}
void MethodCall::dontExpectReply()
@@ -806,7 +801,7 @@ MethodReply MethodCall::sendWithReply(uint64_t timeout) const
auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, timeout, &sdbusError, &sdbusReply);
if (sd_bus_error_is_set(&sdbusError))
throw Error(Error::Name{sdbusError.name}, sdbusError.message);
throw sdbus::Error(sdbusError.name, sdbusError.message);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
@@ -821,13 +816,32 @@ MethodReply MethodCall::sendWithNoReply() const
return Factory::create<MethodReply>(); // No reply
}
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout, return_slot_t) const
void MethodCall::send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const
{
MethodCall::send(callback, userData, timeout, floating_slot);
}
void MethodCall::send(void* callback, void* userData, uint64_t timeout, floating_slot_t) const
{
auto r = sdbus_->sd_bus_call_async(nullptr, nullptr, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
// Force event loop to re-enter polling with the async call timeout if that is less than the one used in current poll
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
connection_->notifyEventLoopNewTimeout();
}
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout) const
{
sd_bus_slot* slot;
auto r = sdbus_->sd_bus_call_async(nullptr, &slot, (sd_bus_message*)msg_, (sd_bus_message_handler_t)callback, userData, timeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method asynchronously", -r);
// Force event loop to re-enter polling with the async call timeout if that is less than the one used in current poll
SDBUS_THROW_ERROR_IF(connection_ == nullptr, "Invalid use of MethodCall API", ENOTSUP);
connection_->notifyEventLoopNewTimeout();
return {slot, [sdbus_ = sdbus_](void *slot){ sdbus_->sd_bus_slot_unref((sd_bus_slot*)slot); }};
}
@@ -867,12 +881,7 @@ void Signal::send() const
void Signal::setDestination(const std::string& destination)
{
return setDestination(destination.c_str());
}
void Signal::setDestination(const char* destination)
{
auto r = sdbus_->sd_bus_message_set_destination((sd_bus_message*)msg_, destination);
auto r = sdbus_->sd_bus_message_set_destination((sd_bus_message*)msg_, destination.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
}
@@ -888,11 +897,7 @@ namespace {
// Please note that the solution is NOT thread-safe.
// Another common solution is global sdbus-c++ startup/shutdown functions, but that would be an intrusive change.
#ifdef __cpp_constinit
constinit static bool pseudoConnectionDestroyed{};
#else
static bool pseudoConnectionDestroyed{};
#endif
/*constinit (C++20 keyword) */ static bool pseudoConnectionDestroyed{};
std::unique_ptr<sdbus::internal::IConnection, void(*)(sdbus::internal::IConnection*)> createPseudoConnection()
{
+7 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file MessageUtils.h
*
@@ -57,6 +57,12 @@ namespace sdbus
{
return _Msg{msg, sdbus, adopt_message};
}
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t)
{
return _Msg{msg, sdbus, connection, adopt_message};
}
};
PlainMessage createPlainMessage();
+233 -215
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Object.cpp
*
@@ -25,74 +25,159 @@
*/
#include "Object.h"
#include "sdbus-c++/Error.h"
#include "sdbus-c++/Flags.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Message.h"
#include "IConnection.h"
#include "MessageUtils.h"
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/Flags.h>
#include "ScopeGuard.h"
#include "IConnection.h"
#include "Utils.h"
#include "VTableUtils.h"
#include <cassert>
#include SDBUS_HEADER
#include <utility>
#include <cassert>
namespace sdbus::internal {
Object::Object(sdbus::internal::IConnection& connection, ObjectPath objectPath)
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
: connection_(connection), objectPath_(std::move(objectPath))
{
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
SDBUS_CHECK_OBJECT_PATH(objectPath_);
}
void Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
void Object::registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags )
{
auto slot = Object::addVTable(std::move(interfaceName), std::move(vtable), return_slot);
vtables_.push_back(std::move(slot));
registerMethod( interfaceName
, std::move(methodName)
, std::move(inputSignature)
, {}
, std::move(outputSignature)
, {}
, std::move(methodCallback)
, std::move(flags) );
}
Slot Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t)
void Object::registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags )
{
SDBUS_CHECK_INTERFACE_NAME(interfaceName.c_str());
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(methodName);
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
// 1st pass -- create vtable structure for internal sdbus-c++ purposes
auto internalVTable = std::make_unique<VTable>(createInternalVTable(std::move(interfaceName), std::move(vtable)));
auto& interface = getInterface(interfaceName);
InterfaceData::MethodData methodData{ std::move(inputSignature)
, std::move(outputSignature)
, paramNamesToString(inputNames) + paramNamesToString(outputNames)
, std::move(methodCallback)
, std::move(flags) };
auto inserted = interface.methods.emplace(std::move(methodName), std::move(methodData)).second;
// 2nd pass -- from internal sdbus-c++ vtable, create vtable structure in format expected by underlying sd-bus library
internalVTable->sdbusVTable = createInternalSdBusVTable(*internalVTable);
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
}
// 3rd step -- register the vtable with sd-bus
internalVTable->slot = connection_.addObjectVTable( objectPath_
, internalVTable->interfaceName
, &internalVTable->sdbusVTable[0]
, internalVTable.get()
, return_slot );
void Object::registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, Flags flags )
{
registerSignal(interfaceName, std::move(signalName), std::move(signature), {}, std::move(flags));
}
// Return vtable wrapped in a Slot object
return {internalVTable.release(), [](void *ptr){ delete static_cast<VTable*>(ptr); }};
void Object::registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags )
{
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(signalName);
auto& interface = getInterface(interfaceName);
InterfaceData::SignalData signalData{std::move(signature), paramNamesToString(paramNames), std::move(flags)};
auto inserted = interface.signals.emplace(std::move(signalName), std::move(signalData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
}
void Object::registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags )
{
registerProperty( interfaceName
, std::move(propertyName)
, std::move(signature)
, std::move(getCallback)
, {}
, std::move(flags) );
}
void Object::registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags )
{
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(propertyName);
SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL);
auto& interface = getInterface(interfaceName);
InterfaceData::PropertyData propertyData{ std::move(signature)
, std::move(getCallback)
, std::move(setCallback)
, std::move(flags) };
auto inserted = interface.properties.emplace(std::move(propertyName), std::move(propertyData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
}
void Object::setInterfaceFlags(const std::string& interfaceName, Flags flags)
{
auto& interface = getInterface(interfaceName);
interface.flags = flags;
}
void Object::finishRegistration()
{
for (auto& item : interfaces_)
{
const auto& interfaceName = item.first;
auto& interfaceData = item.second;
const auto& vtable = createInterfaceVTable(interfaceData);
activateInterfaceVTable(interfaceName, interfaceData, vtable);
}
}
void Object::unregister()
{
vtables_.clear();
objectManagerSlot_.reset();
interfaces_.clear();
removeObjectManager();
}
sdbus::Signal Object::createSignal(const InterfaceName& interfaceName, const SignalName& signalName)
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
{
return connection_.createSignal(objectPath_, interfaceName, signalName);
}
sdbus::Signal Object::createSignal(const char* interfaceName, const char* signalName)
{
return connection_.createSignal(objectPath_.c_str(), interfaceName, signalName);
}
void Object::emitSignal(const sdbus::Signal& message)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
@@ -100,22 +185,12 @@ void Object::emitSignal(const sdbus::Signal& message)
message.send();
}
void Object::emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames)
void Object::emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames)
{
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
}
void Object::emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames)
{
connection_.emitPropertiesChangedSignal(objectPath_.c_str(), interfaceName, propNames);
}
void Object::emitPropertiesChangedSignal(const InterfaceName& interfaceName)
{
Object::emitPropertiesChangedSignal(interfaceName, {});
}
void Object::emitPropertiesChangedSignal(const char* interfaceName)
void Object::emitPropertiesChangedSignal(const std::string& interfaceName)
{
Object::emitPropertiesChangedSignal(interfaceName, {});
}
@@ -125,7 +200,7 @@ void Object::emitInterfacesAddedSignal()
connection_.emitInterfacesAddedSignal(objectPath_);
}
void Object::emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces)
void Object::emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
{
connection_.emitInterfacesAddedSignal(objectPath_, interfaces);
}
@@ -135,19 +210,24 @@ void Object::emitInterfacesRemovedSignal()
connection_.emitInterfacesRemovedSignal(objectPath_);
}
void Object::emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
void Object::emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
{
connection_.emitInterfacesRemovedSignal(objectPath_, interfaces);
}
void Object::addObjectManager()
{
objectManagerSlot_ = connection_.addObjectManager(objectPath_, return_slot);
objectManagerSlot_ = connection_.addObjectManager(objectPath_, request_slot);
}
Slot Object::addObjectManager(return_slot_t)
void Object::removeObjectManager()
{
return connection_.addObjectManager(objectPath_, return_slot);
objectManagerSlot_.reset();
}
bool Object::hasObjectManager() const
{
return objectManagerSlot_ != nullptr;
}
sdbus::IConnection& Object::getConnection() const
@@ -155,161 +235,91 @@ sdbus::IConnection& Object::getConnection() const
return connection_;
}
const ObjectPath& Object::getObjectPath() const
const std::string& Object::getObjectPath() const
{
return objectPath_;
}
Message Object::getCurrentlyProcessedMessage() const
const Message* Object::getCurrentlyProcessedMessage() const
{
return connection_.getCurrentlyProcessedMessage();
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
}
Object::VTable Object::createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
Object::InterfaceData& Object::getInterface(const std::string& interfaceName)
{
VTable internalVTable;
return interfaces_.emplace(interfaceName, *this).first->second;
}
internalVTable.interfaceName = std::move(interfaceName);
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
{
auto& vtable = interfaceData.vtable;
assert(vtable.empty());
for (auto& vtableItem : vtable)
vtable.push_back(createVTableStartItem(interfaceData.flags.toSdBusInterfaceFlags()));
registerMethodsToVTable(interfaceData, vtable);
registerSignalsToVTable(interfaceData, vtable);
registerPropertiesToVTable(interfaceData, vtable);
vtable.push_back(createVTableEndItem());
return vtable;
}
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
for (const auto& item : interfaceData.methods)
{
std::visit( overload{ [&](InterfaceFlagsVTableItem&& interfaceFlags){ writeInterfaceFlagsToVTable(std::move(interfaceFlags), internalVTable); }
, [&](MethodVTableItem&& method){ writeMethodRecordToVTable(std::move(method), internalVTable); }
, [&](SignalVTableItem&& signal){ writeSignalRecordToVTable(std::move(signal), internalVTable); }
, [&](PropertyVTableItem&& property){ writePropertyRecordToVTable(std::move(property), internalVTable); } }
, std::move(vtableItem) );
const auto& methodName = item.first;
const auto& methodData = item.second;
vtable.push_back(createVTableMethodItem( methodName.c_str()
, methodData.inputArgs.c_str()
, methodData.outputArgs.c_str()
, methodData.paramNames.c_str()
, &Object::sdbus_method_callback
, methodData.flags.toSdBusMethodFlags() ));
}
// Sort arrays so we can do fast searching for an item in sd-bus callback handlers
std::sort(internalVTable.methods.begin(), internalVTable.methods.end(), [](const auto& a, const auto& b){ return a.name < b.name; });
std::sort(internalVTable.signals.begin(), internalVTable.signals.end(), [](const auto& a, const auto& b){ return a.name < b.name; });
std::sort(internalVTable.properties.begin(), internalVTable.properties.end(), [](const auto& a, const auto& b){ return a.name < b.name; });
internalVTable.object = this;
return internalVTable;
}
void Object::writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable)
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
vtable.interfaceFlags = std::move(flags.flags);
}
void Object::writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable)
{
SDBUS_CHECK_MEMBER_NAME(method.name.c_str());
SDBUS_THROW_ERROR_IF(!method.callbackHandler, "Invalid method callback provided", EINVAL);
vtable.methods.push_back({ std::move(method.name)
, std::move(method.inputSignature)
, std::move(method.outputSignature)
, paramNamesToString(method.inputParamNames) + paramNamesToString(method.outputParamNames)
, std::move(method.callbackHandler)
, std::move(method.flags) });
}
void Object::writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable)
{
SDBUS_CHECK_MEMBER_NAME(signal.name.c_str());
vtable.signals.push_back({ std::move(signal.name)
, std::move(signal.signature)
, paramNamesToString(signal.paramNames)
, std::move(signal.flags) });
}
void Object::writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable)
{
SDBUS_CHECK_MEMBER_NAME(property.name.c_str());
SDBUS_THROW_ERROR_IF(!property.getter && !property.setter, "Invalid property callbacks provided", EINVAL);
vtable.properties.push_back({ std::move(property.name)
, std::move(property.signature)
, std::move(property.getter)
, std::move(property.setter)
, std::move(property.flags) });
}
std::vector<sd_bus_vtable> Object::createInternalSdBusVTable(const VTable& vtable)
{
std::vector<sd_bus_vtable> sdbusVTable;
startSdBusVTable(vtable.interfaceFlags, sdbusVTable);
for (const auto& methodItem : vtable.methods)
writeMethodRecordToSdBusVTable(methodItem, sdbusVTable);
for (const auto& signalItem : vtable.signals)
writeSignalRecordToSdBusVTable(signalItem, sdbusVTable);
for (const auto& propertyItem : vtable.properties)
writePropertyRecordToSdBusVTable(propertyItem, sdbusVTable);
finalizeSdBusVTable(sdbusVTable);
return sdbusVTable;
}
void Object::startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = createSdBusVTableStartItem(interfaceFlags.toSdBusInterfaceFlags());
vtable.push_back(std::move(vtableItem));
}
void Object::writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = createSdBusVTableMethodItem( method.name.c_str()
, method.inputSignature.c_str()
, method.outputSignature.c_str()
, method.paramNames.c_str()
, &Object::sdbus_method_callback
, method.flags.toSdBusMethodFlags() );
vtable.push_back(std::move(vtableItem));
}
void Object::writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = createSdBusVTableSignalItem( signal.name.c_str()
, signal.signature.c_str()
, signal.paramNames.c_str()
, signal.flags.toSdBusSignalFlags() );
vtable.push_back(std::move(vtableItem));
}
void Object::writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = !property.setCallback
? createSdBusVTableReadOnlyPropertyItem( property.name.c_str()
, property.signature.c_str()
, &Object::sdbus_property_get_callback
, property.flags.toSdBusPropertyFlags() )
: createSdBusVTableWritablePropertyItem( property.name.c_str()
, property.signature.c_str()
, &Object::sdbus_property_get_callback
, &Object::sdbus_property_set_callback
, property.flags.toSdBusWritablePropertyFlags() );
vtable.push_back(std::move(vtableItem));
}
void Object::finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable)
{
vtable.push_back(createSdBusVTableEndItem());
}
const Object::VTable::MethodItem* Object::findMethod(const VTable& vtable, std::string_view methodName)
{
auto it = std::lower_bound(vtable.methods.begin(), vtable.methods.end(), methodName, [](const auto& methodItem, const auto& methodName)
for (const auto& item : interfaceData.signals)
{
return methodItem.name < methodName;
});
const auto& signalName = item.first;
const auto& signalData = item.second;
return it != vtable.methods.end() && it->name == methodName ? &*it : nullptr;
vtable.push_back(createVTableSignalItem( signalName.c_str()
, signalData.signature.c_str()
, signalData.paramNames.c_str()
, signalData.flags.toSdBusSignalFlags() ));
}
}
const Object::VTable::PropertyItem* Object::findProperty(const VTable& vtable, std::string_view propertyName)
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
{
auto it = std::lower_bound(vtable.properties.begin(), vtable.properties.end(), propertyName, [](const auto& propertyItem, const auto& propertyName)
for (const auto& item : interfaceData.properties)
{
return propertyItem.name < propertyName;
});
const auto& propertyName = item.first;
const auto& propertyData = item.second;
return it != vtable.properties.end() && it->name == propertyName ? &*it : nullptr;
if (!propertyData.setCallback)
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, propertyData.flags.toSdBusPropertyFlags() ));
else
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, &Object::sdbus_property_set_callback
, propertyData.flags.toSdBusWritablePropertyFlags() ));
}
}
void Object::activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable )
{
interfaceData.slot = connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], &interfaceData);
}
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
@@ -322,17 +332,22 @@ std::string Object::paramNamesToString(const std::vector<std::string>& paramName
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
auto* vtable = static_cast<VTable*>(userData);
assert(vtable != nullptr);
assert(vtable->object != nullptr);
auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(interfaceData != nullptr);
auto& object = interfaceData->object;
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &vtable->object->connection_.getSdBusInterface());
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object.connection_.getSdBusInterface());
const auto* methodItem = findMethod(*vtable, message.getMemberName());
assert(methodItem != nullptr);
assert(methodItem->callback);
object.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
SCOPE_EXIT
{
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
auto ok = invokeHandlerAndCatchErrors([&](){ methodItem->callback(std::move(message)); }, retError);
auto& callback = interfaceData->methods[message.getMemberName()].callback;
assert(callback);
auto ok = invokeHandlerAndCatchErrors([&](){ callback(message); }, retError);
return ok ? 1 : -1;
}
@@ -345,23 +360,21 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
, void *userData
, sd_bus_error *retError )
{
auto* vtable = static_cast<VTable*>(userData);
assert(vtable != nullptr);
assert(vtable->object != nullptr);
auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(interfaceData != nullptr);
auto& object = interfaceData->object;
const auto* propertyItem = findProperty(*vtable, property);
assert(propertyItem != nullptr);
// Getter may be empty - the case of "write-only" property
if (!propertyItem->getCallback)
auto& callback = interfaceData->properties[property].getCallback;
// Getter can be empty - the case of "write-only" property
if (!callback)
{
sd_bus_error_set(retError, "org.freedesktop.DBus.Error.Failed", "Cannot read property as it is write-only");
return 1;
}
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &vtable->object->connection_.getSdBusInterface());
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object.connection_.getSdBusInterface());
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->getCallback(reply); }, retError);
auto ok = invokeHandlerAndCatchErrors([&](){ callback(reply); }, retError);
return ok ? 1 : -1;
}
@@ -374,17 +387,22 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
, void *userData
, sd_bus_error *retError )
{
auto* vtable = static_cast<VTable*>(userData);
assert(vtable != nullptr);
assert(vtable->object != nullptr);
auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(interfaceData != nullptr);
auto& object = interfaceData->object;
const auto* propertyItem = findProperty(*vtable, property);
assert(propertyItem != nullptr);
assert(propertyItem->setCallback);
auto& callback = interfaceData->properties[property].setCallback;
assert(callback);
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &vtable->object->connection_.getSdBusInterface());
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object.connection_.getSdBusInterface());
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->setCallback(std::move(value)); }, retError);
object.m_CurrentlyProcessedMessage.store(&value, std::memory_order_relaxed);
SCOPE_EXIT
{
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
auto ok = invokeHandlerAndCatchErrors([&](){ callback(value); }, retError);
return ok ? 1 : -1;
}
@@ -393,7 +411,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
namespace sdbus {
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, ObjectPath objectPath)
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, 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);
+91 -76
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Object.h
*
@@ -27,19 +27,16 @@
#ifndef SDBUS_CXX_INTERNAL_OBJECT_H_
#define SDBUS_CXX_INTERNAL_OBJECT_H_
#include "sdbus-c++/IObject.h"
#include <sdbus-c++/IObject.h>
#include "IConnection.h"
#include "sdbus-c++/Types.h"
#include <cassert>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <string_view>
#include SDBUS_HEADER
#include <string>
#include <map>
#include <vector>
#include <functional>
#include <memory>
#include <atomic>
#include <cassert>
namespace sdbus::internal {
@@ -47,100 +44,117 @@ namespace sdbus::internal {
: public IObject
{
public:
Object(sdbus::internal::IConnection& connection, ObjectPath objectPath);
Object(sdbus::internal::IConnection& connection, std::string objectPath);
void addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) override;
Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) override;
void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags ) override;
void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, const std::vector<std::string>& inputNames
, std::string outputSignature
, const std::vector<std::string>& outputNames
, method_callback methodCallback
, Flags flags ) override;
void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, Flags flags ) override;
void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags ) override;
void registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, Flags flags ) override;
void registerProperty( const std::string& interfaceName
, std::string propertyName
, std::string signature
, property_get_callback getCallback
, property_set_callback setCallback
, Flags flags ) override;
void setInterfaceFlags(const std::string& interfaceName, Flags flags) override;
void finishRegistration() override;
void unregister() override;
sdbus::Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) override;
sdbus::Signal createSignal(const char* interfaceName, const char* signalName) override;
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
void emitSignal(const sdbus::Signal& message) override;
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames) override;
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames) override;
void emitPropertiesChangedSignal(const InterfaceName& interfaceName) override;
void emitPropertiesChangedSignal(const char* interfaceName) override;
void 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<InterfaceName>& interfaces) override;
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) override;
void emitInterfacesRemovedSignal() override;
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces) override;
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) override;
void addObjectManager() override;
[[nodiscard]] Slot addObjectManager(return_slot_t) override;
void removeObjectManager() override;
bool hasObjectManager() const override;
[[nodiscard]] sdbus::IConnection& getConnection() const override;
[[nodiscard]] const ObjectPath& getObjectPath() const override;
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
sdbus::IConnection& getConnection() const override;
const std::string& getObjectPath() const override;
const Message* getCurrentlyProcessedMessage() const override;
private:
// A vtable record comprising methods, signals, properties, flags.
// Once created, it cannot be modified. Only new vtables records can be added.
// An interface can have any number of vtables attached to it, not only one.
struct VTable
using InterfaceName = std::string;
struct InterfaceData
{
InterfaceName interfaceName;
Flags interfaceFlags;
InterfaceData(Object& object) : object(object) {}
struct MethodItem
using MethodName = std::string;
struct MethodData
{
MethodName name;
Signature inputSignature;
Signature outputSignature;
std::string paramNames;
const std::string inputArgs;
const std::string outputArgs;
const std::string paramNames;
method_callback callback;
Flags flags;
};
// Array of method records sorted by method name
std::vector<MethodItem> methods;
struct SignalItem
std::map<MethodName, MethodData> methods;
using SignalName = std::string;
struct SignalData
{
SignalName name;
Signature signature;
std::string paramNames;
const std::string signature;
const std::string paramNames;
Flags flags;
};
// Array of signal records sorted by signal name
std::vector<SignalItem> signals;
struct PropertyItem
std::map<SignalName, SignalData> signals;
using PropertyName = std::string;
struct PropertyData
{
PropertyName name;
Signature signature;
const std::string signature;
property_get_callback getCallback;
property_set_callback setCallback;
Flags flags;
};
// Array of signal records sorted by signal name
std::vector<PropertyItem> properties;
// VTable structure in format required by sd-bus API
std::vector<sd_bus_vtable> sdbusVTable;
// Back-reference to the owning object from sd-bus callback handlers
Object* object{};
std::map<PropertyName, PropertyData> properties;
std::vector<sd_bus_vtable> vtable;
Flags flags;
Object& object;
// This is intentionally the last member, because it must be destructed first,
// releasing callbacks above before the callbacks themselves are destructed.
Slot slot;
};
VTable createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable);
void writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable);
void writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable);
void writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable);
void writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable);
std::vector<sd_bus_vtable> createInternalSdBusVTable(const VTable& vtable);
static void startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable);
static void writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable);
static void writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable);
static void writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable);
static void finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable);
static const VTable::MethodItem* findMethod(const VTable& vtable, std::string_view methodName);
static const VTable::PropertyItem* findProperty(const VTable& vtable, std::string_view propertyName);
InterfaceData& getInterface(const std::string& interfaceName);
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData);
static void registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
static void registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
static void registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
void activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData
, const std::vector<sd_bus_vtable>& vtable );
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);
@@ -161,9 +175,10 @@ namespace sdbus::internal {
private:
sdbus::internal::IConnection& connection_;
ObjectPath objectPath_;
std::vector<Slot> vtables_;
std::string objectPath_;
std::map<InterfaceName, InterfaceData> interfaces_;
Slot objectManagerSlot_;
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
};
}
+193 -197
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Proxy.cpp
*
@@ -25,44 +25,41 @@
*/
#include "Proxy.h"
#include "sdbus-c++/Error.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Message.h"
#include "IConnection.h"
#include "MessageUtils.h"
#include "ScopeGuard.h"
#include "Utils.h"
#include <cassert>
#include <cstring>
#include "sdbus-c++/Message.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Error.h"
#include "ScopeGuard.h"
#include SDBUS_HEADER
#include <cassert>
#include <chrono>
#include <utility>
namespace sdbus::internal {
Proxy::Proxy(sdbus::internal::IConnection& connection, ServiceName destination, ObjectPath objectPath)
Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
// The connection is not ours only, it is owned and managed by the user and we just reference
// it here, so we expect the client to manage the event loop upon this connection themselves.
}
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath )
, std::string destination
, std::string objectPath )
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
// The connection is ours only, i.e. it's us who has to manage the event loop upon this connection,
// in order that we get and process signals, async call replies, and other messages from D-Bus.
@@ -70,163 +67,191 @@ Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
}
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t )
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
// Even though the connection is ours only, we don't start an event loop thread.
// This proxy is meant to be created, used for simple synchronous D-Bus call(s) and then dismissed.
}
MethodCall Proxy::createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName)
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
{
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
}
MethodCall Proxy::createMethodCall(const char* interfaceName, const char* methodName)
{
return connection_->createMethodCall(destination_.c_str(), objectPath_.c_str(), interfaceName, methodName);
}
MethodReply Proxy::callMethod(const MethodCall& message)
{
return Proxy::callMethod(message, /*timeout*/ 0);
}
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
{
// Sending method call synchronously is the only operation that blocks, waiting for the method
// reply message among the incoming messages on the sd-bus connection socket. But typically there
// already is somebody that generally handles incoming D-Bus messages -- the connection event loop
// running typically in its own thread. We have to avoid polling on socket from several threads.
// So we have to branch here: either we are within the context of the event loop thread, then we
// can send the message simply via sd_bus_call, which blocks. Or we are in another thread, then
// we can perform the send operation of the method call message from here (because that is thread-
// safe like other sd-bus API accesses), but the incoming reply we have to get through the event
// loop thread, because this is the only rightful listener on the sd-bus connection socket.
// So, technically, we use async means to wait here for reply received by the event loop thread.
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
return connection_->callMethod(message, timeout);
// If we don't need to wait for any reply, we can send the message now irrespective of the context
if (message.doesntExpectReply())
return message.send(timeout);
// If we are in the context of event loop thread, we can send the D-Bus call synchronously
// and wait blockingly for the reply, because we are the exclusive listeners on the socket
auto reply = connection_->tryCallMethodSynchronously(message, timeout);
if (reply.isValid())
return reply;
// Otherwise we send the call asynchronously and do blocking wait for the reply from the event loop thread
return sendMethodCallMessageAndWaitForReply(message, timeout);
}
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback)
{
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0);
}
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, return_slot_t)
{
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0, return_slot);
}
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
PendingAsyncCall Proxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
auto asyncCallInfo = std::make_shared<AsyncCallInfo>(AsyncCallInfo{ .callback = std::move(asyncReplyCallback)
, .proxy = *this
, .floating = false });
auto callback = (void*)&Proxy::sdbus_async_reply_handler;
auto callData = std::make_shared<AsyncCalls::CallData>(AsyncCalls::CallData{*this, std::move(asyncReplyCallback), {}, AsyncCalls::CallData::State::RUNNING});
auto weakData = std::weak_ptr<AsyncCalls::CallData>{callData};
asyncCallInfo->slot = connection_->callMethod( message
, (void*)&Proxy::sdbus_async_reply_handler
, asyncCallInfo.get()
, timeout
, return_slot );
callData->slot = message.send(callback, callData.get(), timeout);
auto asyncCallInfoWeakPtr = std::weak_ptr{asyncCallInfo};
pendingAsyncCalls_.addCall(std::move(callData));
floatingAsyncCallSlots_.push_back(std::move(asyncCallInfo));
return {asyncCallInfoWeakPtr};
return {weakData};
}
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout, return_slot_t)
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, with_future_t)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
auto asyncCallInfo = std::make_unique<AsyncCallInfo>(AsyncCallInfo{ .callback = std::move(asyncReplyCallback)
, .proxy = *this
, .floating = true });
asyncCallInfo->slot = connection_->callMethod( message
, (void*)&Proxy::sdbus_async_reply_handler
, asyncCallInfo.get()
, timeout
, return_slot );
return {asyncCallInfo.release(), [](void *ptr){ delete static_cast<AsyncCallInfo*>(ptr); }};
return Proxy::callMethod(message, {}, with_future);
}
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, with_future_t)
{
return Proxy::callMethodAsync(message, {}, with_future);
}
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t)
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, uint64_t timeout, with_future_t)
{
auto promise = std::make_shared<std::promise<MethodReply>>();
auto future = promise->get_future();
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, std::optional<Error> error) noexcept
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply& reply, const Error* error) noexcept
{
if (!error)
promise->set_value(std::move(reply));
if (error == nullptr)
promise->set_value(reply);
else
promise->set_exception(std::make_exception_ptr(*std::move(error)));
promise->set_exception(std::make_exception_ptr(*error));
};
(void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout);
(void)Proxy::callMethod(message, std::move(asyncReplyCallback), timeout);
return future;
}
void Proxy::registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
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), {}, AsyncCalls::CallData::State::NOT_ASYNC};
message.send(callback, &callData, timeout, floating_slot);
return syncCallReplyData.waitForMethodReply();
}
void Proxy::SyncCallReplyData::sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error)
{
std::unique_lock lock{mutex_};
SCOPE_EXIT{ cond_.notify_one(); }; // This must happen before unlocking the mutex to avoid potential data race on spurious wakeup in the waiting thread
SCOPE_EXIT{ arrived_ = true; };
//error_ = nullptr; // Necessary if SyncCallReplyData instance is thread_local
if (error == nullptr)
reply_ = std::move(reply);
else
error_ = std::make_unique<Error>(*error);
}
MethodReply Proxy::SyncCallReplyData::waitForMethodReply()
{
std::unique_lock lock{mutex_};
cond_.wait(lock, [this](){ return arrived_; });
//arrived_ = false; // Necessary if SyncCallReplyData instance is thread_local
if (error_)
throw *error_;
return std::move(reply_);
}
void Proxy::registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler )
{
Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler));
}
void Proxy::registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler )
{
auto slot = Proxy::registerSignalHandler(interfaceName, signalName, std::move(signalHandler), return_slot);
floatingSignalSlots_.push_back(std::move(slot));
}
Slot Proxy::registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler
, return_slot_t )
{
return Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler), return_slot);
}
Slot Proxy::registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler
, return_slot_t )
{
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(signalName);
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
auto signalInfo = std::make_unique<SignalInfo>(SignalInfo{std::move(signalHandler), *this, {}});
auto& interface = interfaces_[interfaceName];
signalInfo->slot = connection_->registerSignalHandler( destination_.c_str()
, objectPath_.c_str()
, interfaceName
, signalName
, &Proxy::sdbus_signal_handler
, signalInfo.get()
, return_slot );
auto signalData = std::make_unique<InterfaceData::SignalData>(*this, std::move(signalHandler), nullptr);
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
return {signalInfo.release(), [](void *ptr){ delete static_cast<SignalInfo*>(ptr); }};
auto inserted = insertionResult.second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL);
}
void Proxy::unregisterSignalHandler( const std::string& interfaceName
, const std::string& signalName )
{
auto it = interfaces_.find(interfaceName);
if (it != interfaces_.end())
it->second.signals_.erase(signalName);
}
void Proxy::finishRegistration()
{
registerSignalHandlers(*connection_);
}
void Proxy::registerSignalHandlers(sdbus::internal::IConnection& connection)
{
for (auto& interfaceItem : interfaces_)
{
const auto& interfaceName = interfaceItem.first;
auto& signalsOnInterface = interfaceItem.second.signals_;
for (auto& signalItem : signalsOnInterface)
{
const auto& signalName = signalItem.first;
auto* signalData = signalItem.second.get();
signalData->slot = connection.registerSignalHandler( destination_
, objectPath_
, interfaceName
, signalName
, &Proxy::sdbus_signal_handler
, signalData);
}
}
}
void Proxy::unregister()
{
floatingAsyncCallSlots_.clear();
floatingSignalSlots_.clear();
pendingAsyncCalls_.clear();
interfaces_.clear();
}
sdbus::IConnection& Proxy::getConnection() const
@@ -234,44 +259,53 @@ sdbus::IConnection& Proxy::getConnection() const
return *connection_;
}
const ObjectPath& Proxy::getObjectPath() const
const std::string& Proxy::getObjectPath() const
{
return objectPath_;
}
Message Proxy::getCurrentlyProcessedMessage() const
const Message* Proxy::getCurrentlyProcessedMessage() const
{
return connection_->getCurrentlyProcessedMessage();
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
}
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
auto* asyncCallInfo = static_cast<AsyncCallInfo*>(userData);
assert(asyncCallInfo != nullptr);
assert(asyncCallInfo->callback);
auto& proxy = asyncCallInfo->proxy;
auto* asyncCallData = static_cast<AsyncCalls::CallData*>(userData);
assert(asyncCallData != nullptr);
assert(asyncCallData->callback);
auto& proxy = asyncCallData->proxy;
auto state = asyncCallData->state;
// We are removing the CallData item at the complete scope exit, after the callback has been invoked.
// We can't do it earlier (before callback invocation for example), because CallBack data (slot release)
// is the synchronization point between callback invocation and Proxy::unregister.
SCOPE_EXIT
{
proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
// Remove call meta-data if it's a real async call (a sync call done in terms of async has STATE_NOT_ASYNC)
if (state != AsyncCalls::CallData::State::NOT_ASYNC)
proxy.pendingAsyncCalls_.removeCall(asyncCallData);
};
auto message = Message::Factory::create<MethodReply>(sdbusMessage, &proxy.connection_->getSdBusInterface());
proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
SCOPE_EXIT
{
proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
auto ok = invokeHandlerAndCatchErrors([&]
{
const auto* error = sd_bus_message_get_error(sdbusMessage);
if (error == nullptr)
{
asyncCallInfo->callback(std::move(message), {});
asyncCallData->callback(message, nullptr);
}
else
{
Error exception(Error::Name{error->name}, error->message);
asyncCallInfo->callback(std::move(message), std::move(exception));
Error exception(error->name, error->message);
asyncCallData->callback(message, &exception);
}
}, retError);
@@ -280,76 +314,38 @@ int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userDat
int Proxy::sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
auto* signalInfo = static_cast<SignalInfo*>(userData);
assert(signalInfo != nullptr);
assert(signalInfo->callback);
auto* signalData = static_cast<InterfaceData::SignalData*>(userData);
assert(signalData != nullptr);
assert(signalData->callback);
// TODO: Hide Message factory invocation under Connection API (tell, don't ask principle), then we can remove getSdBusInterface()
auto message = Message::Factory::create<Signal>(sdbusMessage, &signalInfo->proxy.connection_->getSdBusInterface());
auto message = Message::Factory::create<Signal>(sdbusMessage, &signalData->proxy.connection_->getSdBusInterface());
auto ok = invokeHandlerAndCatchErrors([&](){ signalInfo->callback(std::move(message)); }, retError);
signalData->proxy.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
SCOPE_EXIT
{
signalData->proxy.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
auto ok = invokeHandlerAndCatchErrors([&](){ signalData->callback(message); }, retError);
return ok ? 0 : -1;
}
Proxy::FloatingAsyncCallSlots::~FloatingAsyncCallSlots()
{
clear();
}
void Proxy::FloatingAsyncCallSlots::push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo)
{
std::lock_guard lock(mutex_);
if (!asyncCallInfo->finished) // The call may have finished in the meantime
slots_.emplace_back(std::move(asyncCallInfo));
}
void Proxy::FloatingAsyncCallSlots::erase(AsyncCallInfo* info)
{
std::unique_lock lock(mutex_);
info->finished = true;
auto it = std::find_if(slots_.begin(), slots_.end(), [info](auto const& entry){ return entry.get() == info; });
if (it != slots_.end())
{
auto callInfo = std::move(*it);
slots_.erase(it);
lock.unlock();
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
// out of the `mutex_' critical section here, because if the `removeCall` is called by some
// thread and at the same time Proxy's async reply handler (which already holds global sd-bus
// mutex) is in progress in a different thread, we get double-mutex deadlock.
}
}
void Proxy::FloatingAsyncCallSlots::clear()
{
std::unique_lock lock(mutex_);
auto asyncCallSlots = std::move(slots_);
slots_ = {};
lock.unlock();
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
// out of the `mutex_' critical section here, because if the `clear` is called by some thread
// and at the same time Proxy's async reply handler (which already holds global sd-bus
// mutex) is in progress in a different thread, we get double-mutex deadlock.
}
}
namespace sdbus {
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callInfo)
: callInfo_(std::move(callInfo))
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callData)
: callData_(std::move(callData))
{
}
void PendingAsyncCall::cancel()
{
if (auto ptr = callInfo_.lock(); ptr != nullptr)
if (auto ptr = callData_.lock(); ptr != nullptr)
{
auto* asyncCallInfo = static_cast<internal::Proxy::AsyncCallInfo*>(ptr.get());
asyncCallInfo->proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
auto* callData = static_cast<internal::Proxy::AsyncCalls::CallData*>(ptr.get());
callData->proxy.pendingAsyncCalls_.removeCall(callData);
// At this point, the callData item is being deleted, leading to the release of the
// sd-bus slot pointer. This release locks the global sd-bus mutex. If the async
@@ -360,7 +356,7 @@ void PendingAsyncCall::cancel()
bool PendingAsyncCall::isPending() const
{
return !callInfo_.expired();
return !callData_.expired();
}
}
@@ -368,8 +364,8 @@ bool PendingAsyncCall::isPending() const
namespace sdbus {
std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
, ServiceName destination
, ObjectPath objectPath )
, 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);
@@ -380,8 +376,8 @@ std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
}
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath )
, 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);
@@ -394,8 +390,8 @@ std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& conne
}
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
@@ -409,10 +405,10 @@ std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& conne
, dont_run_event_loop_thread );
}
std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
, ObjectPath objectPath )
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath )
{
auto connection = sdbus::createBusConnection();
auto connection = sdbus::createConnection();
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
assert(sdbusConnection != nullptr);
@@ -422,11 +418,11 @@ std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
, std::move(objectPath) );
}
std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
, ObjectPath objectPath
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t )
{
auto connection = sdbus::createBusConnection();
auto connection = sdbus::createConnection();
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
assert(sdbusConnection != nullptr);
+130 -75
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Proxy.h
*
@@ -27,17 +27,16 @@
#ifndef SDBUS_CXX_INTERNAL_PROXY_H_
#define SDBUS_CXX_INTERNAL_PROXY_H_
#include "sdbus-c++/IProxy.h"
#include <sdbus-c++/IProxy.h>
#include "IConnection.h"
#include "sdbus-c++/Types.h"
#include <deque>
#include <memory>
#include <mutex>
#include <string>
#include SDBUS_HEADER
#include <vector>
#include <string>
#include <memory>
#include <map>
#include <deque>
#include <mutex>
#include <atomic>
#include <condition_variable>
namespace sdbus::internal {
@@ -46,57 +45,54 @@ namespace sdbus::internal {
{
public:
Proxy( sdbus::internal::IConnection& connection
, ServiceName destination
, ObjectPath objectPath );
, std::string destination
, std::string objectPath );
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath );
, std::string destination
, std::string objectPath );
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t );
MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) override;
MethodCall createMethodCall(const char* interfaceName, const char* methodName) override;
MethodReply callMethod(const MethodCall& message) override;
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) override;
Slot callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, return_slot_t ) override;
PendingAsyncCall callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, uint64_t timeout ) override;
Slot callMethodAsync( const MethodCall& message
, async_reply_handler asyncReplyCallback
, uint64_t timeout
, return_slot_t ) override;
std::future<MethodReply> callMethodAsync(const MethodCall& message, with_future_t) override;
std::future<MethodReply> callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t) override;
PendingAsyncCall callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout) override;
std::future<MethodReply> callMethod(const MethodCall& message, with_future_t) override;
std::future<MethodReply> callMethod(const MethodCall& message, uint64_t timeout, with_future_t) override;
void registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
, signal_handler signalHandler ) override;
void registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler ) override;
Slot registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler
, return_slot_t ) override;
Slot registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler
, return_slot_t ) override;
void unregisterSignalHandler( const std::string& interfaceName
, const std::string& signalName ) override;
void finishRegistration() override;
void unregister() override;
[[nodiscard]] sdbus::IConnection& getConnection() const override;
[[nodiscard]] const ObjectPath& getObjectPath() const override;
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
sdbus::IConnection& getConnection() const override;
const std::string& getObjectPath() const override;
const Message* getCurrentlyProcessedMessage() const override;
private:
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
class SyncCallReplyData
{
public:
void sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error);
MethodReply waitForMethodReply();
private:
std::mutex mutex_;
std::condition_variable cond_;
bool arrived_{};
MethodReply reply_;
std::unique_ptr<Error> error_;
};
MethodReply sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout);
void registerSignalHandlers(sdbus::internal::IConnection& connection);
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
private:
friend PendingAsyncCall;
@@ -104,42 +100,101 @@ namespace sdbus::internal {
std::unique_ptr< sdbus::internal::IConnection
, std::function<void(sdbus::internal::IConnection*)>
> connection_;
ServiceName destination_;
ObjectPath objectPath_;
std::string destination_;
std::string objectPath_;
std::vector<Slot> floatingSignalSlots_;
struct SignalInfo
using InterfaceName = std::string;
struct InterfaceData
{
signal_handler callback;
Proxy& proxy;
Slot slot;
using SignalName = std::string;
struct SignalData
{
SignalData(Proxy& proxy, signal_handler callback, Slot slot)
: proxy(proxy)
, callback(std::move(callback))
, slot(std::move(slot))
{}
Proxy& proxy;
signal_handler callback;
// slot must be listed after callback to ensure that slot is destructed first.
// Destructing the slot will sd_bus_slot_unref() the callback.
// Only after sd_bus_slot_unref(), we can safely delete the callback. The bus mutex (SdBus::sdbusMutex_)
// ensures that sd_bus_slot_unref() and the callback execute sequentially.
Slot slot;
};
std::map<SignalName, std::unique_ptr<SignalData>> signals_;
};
std::map<InterfaceName, InterfaceData> interfaces_;
struct AsyncCallInfo
{
async_reply_handler callback;
Proxy& proxy;
Slot slot{};
bool finished{false};
bool floating;
};
// Container keeping track of pending async calls
class FloatingAsyncCallSlots
// 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:
~FloatingAsyncCallSlots();
void push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo);
void erase(AsyncCallInfo* info);
void clear();
struct CallData
{
enum class State
{ NOT_ASYNC
, RUNNING
, FINISHED
};
Proxy& proxy;
async_reply_handler callback;
Slot slot;
State state;
};
~AsyncCalls()
{
clear();
}
void addCall(std::shared_ptr<CallData> asyncCallData)
{
std::lock_guard lock(mutex_);
if (asyncCallData->state != CallData::State::FINISHED) // The call may have finished in the meantime
calls_.emplace_back(std::move(asyncCallData));
}
void removeCall(CallData* data)
{
std::unique_lock lock(mutex_);
data->state = CallData::State::FINISHED;
if (auto it = std::find_if(calls_.begin(), calls_.end(), [data](auto const& entry){ return entry.get() == data; }); it != calls_.end())
{
auto callData = std::move(*it);
calls_.erase(it);
lock.unlock();
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
// out of the `mutex_' critical section here, because if the `removeCall` is called by some
// thread and at the same time Proxy's async reply handler (which already holds global sd-bus
// mutex) is in progress in a different thread, we get double-mutex deadlock.
}
}
void clear()
{
std::unique_lock lock(mutex_);
auto asyncCallSlots = std::move(calls_);
calls_ = {};
lock.unlock();
// Releasing call slot pointer acquires global sd-bus mutex. We have to perform the release
// out of the `mutex_' critical section here, because if the `clear` is called by some thread
// and at the same time Proxy's async reply handler (which already holds global sd-bus
// mutex) is in progress in a different thread, we get double-mutex deadlock.
}
private:
std::mutex mutex_;
std::deque<std::shared_ptr<AsyncCallInfo>> slots_;
};
std::deque<std::shared_ptr<CallData>> calls_;
} pendingAsyncCalls_;
FloatingAsyncCallSlots floatingAsyncCallSlots_;
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
};
}
+1 -2
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ScopeGuard.h
*
@@ -27,7 +27,6 @@
#ifndef SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
#define SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
#include <exception>
#include <utility>
// Straightforward, modern, easy-to-use RAII utility to perform work on scope exit in an exception-safe manner.
+4 -29
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file SdBus.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -53,8 +53,6 @@ int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
return r;
// Make sure long messages are not only stored in outgoing queues but also really sent out
// TODO: This is a workaround. We should not block here until everything is physically sent out.
// Refactor: if sd_bus_get_n_queued_write() > 0 then wake up event loop through event fd
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
return r;
@@ -76,8 +74,6 @@ int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m,
return r;
// Make sure long messages are not only stored in outgoing queues but also really sent out
// TODO: This is a workaround. We should not block here until everything is physically sent out.
// Refactor: if sd_bus_get_n_queued_write() > 0 then wake up event loop through event fd
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
return r;
@@ -300,13 +296,11 @@ int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
{
#ifndef SDBUS_basu
return ::sd_bus_open_system_remote(ret, host);
#else
(void)ret;
(void)host;
#ifdef SDBUS_basu
// https://git.sr.ht/~emersion/basu/commit/01d33b244eb6
return -EOPNOTSUPP;
#else
return ::sd_bus_open_system_remote(ret, host);
#endif
}
@@ -358,13 +352,6 @@ int SdBus::sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *m
return ::sd_bus_add_match_async(bus, slot, match, callback, install_callback, userdata);
}
int SdBus::sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_match_signal(bus, ret, sender, path, interface, member, callback, userdata);
}
sd_bus_slot* SdBus::sd_bus_slot_unref(sd_bus_slot *slot)
{
std::lock_guard lock(sdbusMutex_);
@@ -389,11 +376,6 @@ int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
return ::sd_bus_process(bus, r);
}
sd_bus_message* SdBus::sd_bus_get_current_message(sd_bus *bus)
{
return ::sd_bus_get_current_message(bus);
}
int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
{
std::lock_guard lock(sdbusMutex_);
@@ -413,13 +395,6 @@ int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
return r;
}
int SdBus::sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret)
{
std::lock_guard lock(sdbusMutex_);
return ::sd_bus_get_n_queued_read(bus, ret);
}
int SdBus::sd_bus_flush(sd_bus *bus)
{
return ::sd_bus_flush(bus);
+2 -4
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file SdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -73,16 +73,14 @@ public:
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) override;
virtual int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata) override;
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;
virtual int sd_bus_new(sd_bus **ret) override;
virtual int sd_bus_start(sd_bus *bus) override;
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override;
virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) override;
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
virtual int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret) override;
virtual int sd_bus_flush(sd_bus *bus) override;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override;
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) override;
+12 -12
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Types.cpp
*
@@ -24,15 +24,13 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sdbus-c++/Types.h"
#include "sdbus-c++/Error.h"
#include <sdbus-c++/Types.h>
#include <sdbus-c++/Error.h>
#include "MessageUtils.h"
#include SDBUS_HEADER
#include <cassert>
#include <cerrno>
#include <system_error>
#include SDBUS_HEADER
#include <unistd.h>
namespace sdbus {
@@ -55,10 +53,12 @@ void Variant::deserializeFrom(Message& msg)
msg_.seal();
}
const char* Variant::peekValueType() const
std::string Variant::peekValueType() const
{
msg_.rewind(false);
auto [type, contents] = msg_.peekType();
std::string type;
std::string contents;
msg_.peekType(type, contents);
return contents;
}
@@ -67,7 +67,7 @@ bool Variant::isEmpty() const
return msg_.isEmpty();
}
void UnixFd::close() // NOLINT(readability-make-member-function-const)
void UnixFd::close()
{
if (fd_ >= 0)
{
@@ -82,7 +82,7 @@ int UnixFd::checkedDup(int fd)
return fd;
}
int ret = ::dup(fd); // NOLINT(android-cloexec-dup) // TODO: verify
int ret = ::dup(fd);
if (ret < 0)
{
throw std::system_error(errno, std::generic_category(), "dup failed");
@@ -90,4 +90,4 @@ int UnixFd::checkedDup(int fd)
return ret;
}
} // namespace sdbus
}
+11 -22
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Utils.h
*
@@ -31,17 +31,17 @@
#include SDBUS_HEADER
#if LIBSYSTEMD_VERSION>=246
#define SDBUS_CHECK_OBJECT_PATH(_PATH) \
SDBUS_THROW_ERROR_IF(!sd_bus_object_path_is_valid(_PATH), std::string("Invalid object path '") + _PATH + "' provided", EINVAL) \
#define SDBUS_CHECK_OBJECT_PATH(_PATH) \
SDBUS_THROW_ERROR_IF(!sd_bus_object_path_is_valid(_PATH.c_str()), "Invalid object path '" + _PATH + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME), std::string("Invalid interface name '") + _NAME + "' provided", EINVAL) \
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME.c_str()), "Invalid interface name '" + _NAME + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(*_NAME && !sd_bus_service_name_is_valid(_NAME), std::string("Invalid service name '") + _NAME + "' provided", EINVAL) \
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!_NAME.empty() && !sd_bus_service_name_is_valid(_NAME.c_str()), "Invalid service name '" + _NAME + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME), std::string("Invalid member name '") + _NAME + "' provided", EINVAL) \
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME.c_str()), "Invalid member name '" + _NAME + "' provided", EINVAL) \
/**/
#else
#define SDBUS_CHECK_OBJECT_PATH(_PATH)
@@ -66,29 +66,18 @@ namespace sdbus::internal {
}
catch (const std::exception& e)
{
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), e.what());
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, e.what());
return false;
}
catch (...)
{
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), "Unknown error occurred");
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, "Unknown error occurred");
return false;
}
return true;
}
// Returns time since epoch based of POSIX CLOCK_MONOTONIC,
// so we use the very same clock as underlying sd-bus library.
[[nodiscard]] inline auto now()
{
struct timespec ts{};
auto r = clock_gettime(CLOCK_MONOTONIC, &ts);
SDBUS_THROW_ERROR_IF(r < 0, "clock_gettime failed: ", -errno);
return std::chrono::nanoseconds(ts.tv_nsec) + std::chrono::seconds(ts.tv_sec);
}
// Implementation of the overload pattern for variant visitation
template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
template <class... Ts> overload(Ts...) -> overload<Ts...>;
+22 -22
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file VTableUtils.c
*
@@ -27,18 +27,18 @@
#include "VTableUtils.h"
#include SDBUS_HEADER
sd_bus_vtable createSdBusVTableStartItem(uint64_t flags)
sd_bus_vtable createVTableStartItem(uint64_t flags)
{
struct sd_bus_vtable vtableStart = SD_BUS_VTABLE_START(flags);
return vtableStart;
}
sd_bus_vtable createSdBusVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags )
sd_bus_vtable 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
@@ -65,10 +65,10 @@ sd_bus_vtable createSdBusVTableMethodItem( const char *member
return vtableItem;
}
sd_bus_vtable createSdBusVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags )
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);
@@ -79,26 +79,26 @@ sd_bus_vtable createSdBusVTableSignalItem( const char *member
return vtableItem;
}
sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, uint64_t flags )
sd_bus_vtable createVTablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, uint64_t flags )
{
struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, flags);
return vtableItem;
}
sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, sd_bus_property_set_t setter
, uint64_t flags )
sd_bus_vtable createVTableWritablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, sd_bus_property_set_t setter
, uint64_t flags )
{
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, flags);
return vtableItem;
}
sd_bus_vtable createSdBusVTableEndItem()
sd_bus_vtable createVTableEndItem()
{
struct sd_bus_vtable vtableEnd = SD_BUS_VTABLE_END;
return vtableEnd;
+23 -23
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file VTableUtils.h
*
@@ -27,34 +27,34 @@
#ifndef SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
#define SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
#include <stdbool.h>
#include SDBUS_HEADER
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
sd_bus_vtable createSdBusVTableStartItem(uint64_t flags);
sd_bus_vtable createSdBusVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags );
sd_bus_vtable createSdBusVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags );
sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, uint64_t flags );
sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, sd_bus_property_set_t setter
, uint64_t flags );
sd_bus_vtable createSdBusVTableEndItem();
sd_bus_vtable createVTableStartItem(uint64_t flags);
sd_bus_vtable createVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags );
sd_bus_vtable createVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags );
sd_bus_vtable createVTablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, uint64_t flags );
sd_bus_vtable createVTableWritablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, sd_bus_property_set_t setter
, uint64_t flags );
sd_bus_vtable createVTableEndItem();
#ifdef __cplusplus
}
+49 -52
View File
@@ -2,12 +2,15 @@
# DOWNLOAD AND BUILD OF GOOGLETEST
#-------------------------------
find_package(GTest ${SDBUSCPP_GOOGLETEST_VERSION} CONFIG)
set(GOOGLETEST_VERSION 1.10.0 CACHE STRING "Version of gmock to use")
set(GOOGLETEST_GIT_REPO "https://github.com/google/googletest.git" CACHE STRING "A git repo to clone and build googletest from if gmock is not found in the system")
find_package(GTest ${GOOGLETEST_VERSION} CONFIG)
if (NOT TARGET GTest::gmock)
# Try pkg-config if GTest was not found through CMake config
find_package(PkgConfig)
if (PkgConfig_FOUND)
pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${SDBUSCPP_GOOGLETEST_VERSION})
pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${GOOGLETEST_VERSION})
if(TARGET PkgConfig::GMock)
add_library(GTest::gmock ALIAS PkgConfig::GMock)
endif()
@@ -16,26 +19,26 @@ if (NOT TARGET GTest::gmock)
if (NOT TARGET GTest::gmock)
include(FetchContent)
if (SDBUSCPP_GOOGLETEST_VERSION VERSION_GREATER_EQUAL 1.13.0)
set(GOOGLETEST_TAG "v${SDBUSCPP_GOOGLETEST_VERSION}")
else()
set(GOOGLETEST_TAG "release-${SDBUSCPP_GOOGLETEST_VERSION}")
endif()
message("Manually fetching & building googletest ${GOOGLETEST_TAG}...")
message("Fetching googletest v${GOOGLETEST_VERSION}...")
FetchContent_Declare(googletest
GIT_REPOSITORY ${SDBUSCPP_GOOGLETEST_GIT_REPO}
GIT_TAG ${GOOGLETEST_TAG}
GIT_REPOSITORY ${GOOGLETEST_GIT_REPO}
GIT_TAG release-${GOOGLETEST_VERSION}
GIT_SHALLOW 1
UPDATE_COMMAND "")
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
FetchContent_MakeAvailable(googletest)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
add_library(GTest::gmock ALIAS gmock)
#FetchContent_MakeAvailable(googletest) # Not available in CMake 3.13 :-( Let's do it manually:
FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
FetchContent_Populate(googletest)
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
set(BUILD_GMOCK ON CACHE INTERNAL "" FORCE)
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
add_library(GTest::gmock ALIAS gmock)
endif()
endif()
endif()
@@ -47,7 +50,6 @@ set(UNITTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/unittests)
set(UNITTESTS_SRCS
${UNITTESTS_SOURCE_DIR}/sdbus-c++-unit-tests.cpp
${UNITTESTS_SOURCE_DIR}/Message_test.cpp
${UNITTESTS_SOURCE_DIR}/PollData_test.cpp
${UNITTESTS_SOURCE_DIR}/Types_test.cpp
${UNITTESTS_SOURCE_DIR}/TypeTraits_test.cpp
${UNITTESTS_SOURCE_DIR}/Connection_test.cpp
@@ -103,28 +105,23 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
target_compile_definitions(sdbus-c++-unit-tests PRIVATE
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
SDBUS_${SDBUS_IMPL}
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib GTest::gmock)
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
SDBUS_${SDBUS_IMPL})
if(NOT SDBUS_IMPL STREQUAL "basu")
# Systemd::Libsystemd is included because integration tests use sd-event. Otherwise sdbus-c++ encapsulates and hides libsystemd.
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ Systemd::Libsystemd GTest::gmock)
else()
# sd-event implementation is not part of basu, so its integration tests will be skipped
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
endif()
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
if(SDBUSCPP_BUILD_PERF_TESTS OR SDBUSCPP_BUILD_STRESS_TESTS)
# Manual performance and stress tests
option(ENABLE_PERF_TESTS "Build and install manual performance tests (default OFF)" OFF)
option(ENABLE_STRESS_TESTS "Build and install manual stress tests (default OFF)" OFF)
if(ENABLE_PERF_TESTS OR ENABLE_STRESS_TESTS)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if(SDBUSCPP_BUILD_PERF_TESTS)
if(ENABLE_PERF_TESTS)
message(STATUS "Building with performance tests")
add_executable(sdbus-c++-perf-tests-client ${STRESSTESTS_CLIENT_SRCS})
target_link_libraries(sdbus-c++-perf-tests-client sdbus-c++ Threads::Threads)
@@ -132,7 +129,7 @@ if(SDBUSCPP_BUILD_PERF_TESTS OR SDBUSCPP_BUILD_STRESS_TESTS)
target_link_libraries(sdbus-c++-perf-tests-server sdbus-c++ Threads::Threads)
endif()
if(SDBUSCPP_BUILD_STRESS_TESTS)
if(ENABLE_STRESS_TESTS)
message(STATUS "Building with stress tests")
add_executable(sdbus-c++-stress-tests ${STRESSTESTS_SRCS})
target_link_libraries(sdbus-c++-stress-tests sdbus-c++ Threads::Threads)
@@ -143,25 +140,25 @@ endif()
# INSTALLATION
#----------------------------------
if(SDBUSCPP_INSTALL)
option(INSTALL_TESTS "Install tests (default OFF)" OFF)
if(INSTALL_TESTS)
include(GNUInstallDirs)
install(TARGETS sdbus-c++-unit-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(TARGETS sdbus-c++-integration-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
COMPONENT sdbus-c++-test)
if(SDBUSCPP_BUILD_PERF_TESTS)
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
COMPONENT sdbus-c++-test)
set(TESTS_INSTALL_PATH "tests/${PROJECT_NAME}" CACHE STRING "Specifies where the test binaries will be installed")
install(TARGETS sdbus-c++-unit-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(TARGETS sdbus-c++-integration-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
if(ENABLE_PERF_TESTS)
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
endif()
if(SDBUSCPP_BUILD_STRESS_TESTS)
install(TARGETS sdbus-c++-stress-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
COMPONENT sdbus-c++-test)
if(ENABLE_STRESS_TESTS)
install(TARGETS sdbus-c++-stress-tests DESTINATION ${TESTS_INSTALL_PATH} COMPONENT test)
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d COMPONENT test)
endif()
endif()
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusAsyncMethodsTests.cpp
*
@@ -49,27 +49,29 @@ using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
{
std::chrono::time_point<std::chrono::steady_clock> start;
try
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (!err)
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*std::move(err)));
promise.set_exception(std::make_exception_ptr(*err));
});
start = std::chrono::steady_clock::now();
this->m_proxy->doOperationClientSideAsyncWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
m_proxy->doOperationClientSideAsyncWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
future.get();
FAIL() << "Expected sdbus::Error exception";
@@ -87,7 +89,7 @@ TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTime
}
}
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
{
// Yeah, this is kinda timing-dependent test, but times should be safe...
std::mutex mtx;
@@ -96,7 +98,7 @@ TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously
std::atomic<int> startedCount{};
auto call = [&](uint32_t param)
{
TestProxy proxy{SERVICE_NAME, OBJECT_PATH};
TestProxy proxy{BUS_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
auto result = proxy.doOperationAsync(param);
@@ -112,14 +114,14 @@ TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
}
TYPED_TEST(AsyncSdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
{
std::atomic<size_t> resultCount{};
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
auto call = [&]()
{
TestProxy proxy{SERVICE_NAME, OBJECT_PATH};
TestProxy proxy{BUS_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
@@ -142,33 +144,33 @@ TYPED_TEST(AsyncSdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncM
ASSERT_THAT(resultCount, Eq(1500));
}
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (!err)
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(std::move(err)));
promise.set_exception(std::make_exception_ptr(*err));
});
this->m_proxy->doOperationClientSideAsync(100);
m_proxy->doOperationClientSideAsync(100);
ASSERT_THAT(future.get(), Eq(100));
}
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
{
auto future = this->m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
auto future = m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
ASSERT_THAT(future.get(), Eq(100));
}
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
{
auto future = this->m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
auto future = m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
auto methodReply = future.get();
uint32_t returnValue{};
@@ -177,101 +179,87 @@ TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutu
ASSERT_THAT(returnValue, Eq(100));
}
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
{
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){});
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
auto call = this->m_proxy->doOperationClientSideAsync(100);
auto call = m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSide)
TEST_F(SdbusTestObject, CancelsPendingAsyncCallOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
auto call = this->m_proxy->doOperationClientSideAsync(100);
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
}
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSideByDestroyingOwningSlot)
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
{
auto slot = this->m_proxy->doOperationClientSideAsync(100, sdbus::return_slot);
// Now the slot is destroyed, cancelling the async call
}
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
}
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
auto call = this->m_proxy->doOperationClientSideAsync(100);
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_FALSE(call.isPending());
}
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, std::optional<sdbus::Error> /*err*/){ promise.set_value(1); });
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = this->m_proxy->doOperationClientSideAsync(0);
auto call = m_proxy->doOperationClientSideAsync(0);
(void) future.get(); // Wait for the call to finish
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
}
TYPED_TEST(AsyncSdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
TEST_F(SdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
{
sdbus::PendingAsyncCall call;
ASSERT_FALSE(call.isPending());
}
TYPED_TEST(AsyncSdbusTestObject, SupportsAsyncCallCopyAssignment)
TEST_F(SdbusTestObject, SupportsAsyncCallCopyAssignment)
{
sdbus::PendingAsyncCall call;
call = this->m_proxy->doOperationClientSideAsync(100);
call = m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TYPED_TEST(AsyncSdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
TEST_F(SdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
{
if (!err)
if (err == nullptr)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*std::move(err)));
promise.set_exception(std::make_exception_ptr(*err));
});
this->m_proxy->doErroneousOperationClientSideAsync();
m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}
TYPED_TEST(AsyncSdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
TEST_F(SdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
{
auto future = this->m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
auto future = m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
ASSERT_THROW(future.get(), sdbus::Error);
}
+59 -22
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusConnectionTests.cpp
*
@@ -41,6 +41,7 @@
using ::testing::Eq;
using namespace sdbus::test;
using namespace std::chrono_literals;
/*-------------------------------------*/
/* -- TEST CASES -- */
@@ -48,49 +49,85 @@ using namespace sdbus::test;
TEST(Connection, CanBeDefaultConstructed)
{
ASSERT_NO_THROW(auto con = sdbus::createBusConnection());
ASSERT_NO_THROW(auto con = sdbus::createConnection());
}
TEST(Connection, CanRequestName)
TEST(Connection, CanRequestRegisteredDbusName)
{
auto connection = sdbus::createBusConnection();
auto connection = sdbus::createConnection();
// In case of system bus connection, requesting may throw as we need to allow that first through a config file in /etc/dbus-1/system.d
ASSERT_NO_THROW(connection->requestName(SERVICE_NAME))
ASSERT_NO_THROW(connection->requestName(BUS_NAME))
<< "Perhaps you've forgotten to copy `org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory before running the tests?";
}
TEST(SystemBusConnection, CannotRequestNonregisteredDbusName)
TEST(Connection, CannotRequestNonregisteredDbusName)
{
auto connection = sdbus::createSystemBusConnection();
sdbus::ServiceName notSupportedBusName{"some.random.not.supported.dbus.name"};
ASSERT_THROW(connection->requestName(notSupportedBusName), sdbus::Error);
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->requestName("some.random.not.supported.dbus.name"), sdbus::Error);
}
TEST(Connection, CanReleaseRequestedName)
TEST(Connection, CanReleasedRequestedName)
{
auto connection = sdbus::createBusConnection();
connection->requestName(SERVICE_NAME);
auto connection = sdbus::createConnection();
ASSERT_NO_THROW(connection->releaseName(SERVICE_NAME));
connection->requestName(BUS_NAME);
ASSERT_NO_THROW(connection->releaseName(BUS_NAME));
}
TEST(Connection, CannotReleaseNonrequestedName)
{
auto connection = sdbus::createBusConnection();
sdbus::ServiceName notAcquiredBusName{"some.random.unacquired.name"};
ASSERT_THROW(connection->releaseName(notAcquiredBusName), sdbus::Error);
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
}
TEST(Connection, CanEnterAndLeaveInternalEventLoop)
TEST(Connection, CanEnterAndLeaveEventLoop)
{
auto connection = sdbus::createBusConnection();
connection->requestName(SERVICE_NAME);
auto connection = sdbus::createConnection();
connection->requestName(BUS_NAME);
std::thread t([&](){ connection->enterEventLoop(); });
connection->leaveEventLoop();
t.join();
}
TEST(Connection, PollDataGetZeroTimeout)
{
sdbus::IConnection::PollData pd{};
pd.timeout_usec = 0;
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
EXPECT_THAT(pd.getRelativeTimeout().value(), Eq(std::chrono::microseconds::zero()));
EXPECT_THAT(pd.getPollTimeout(), Eq(0));
}
TEST(Connection, PollDataGetInfiniteTimeout)
{
sdbus::IConnection::PollData pd{};
pd.timeout_usec = UINT64_MAX;
ASSERT_FALSE(pd.getRelativeTimeout().has_value());
EXPECT_THAT(pd.getPollTimeout(), Eq(-1));
}
TEST(Connection, PollDataGetZeroRelativeTimeoutForPast)
{
sdbus::IConnection::PollData pd{};
auto past = std::chrono::steady_clock::now() - 10s;
pd.timeout_usec = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch()).count();
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
EXPECT_THAT(pd.getRelativeTimeout().value(), Eq(0us));
EXPECT_THAT(pd.getPollTimeout(), Eq(0));
}
TEST(Connection, PollDataGetRelativeTimeoutInTolerance)
{
sdbus::IConnection::PollData pd{};
constexpr auto TIMEOUT = 1s;
constexpr auto TOLERANCE = 100ms;
auto future = std::chrono::steady_clock::now() + TIMEOUT;
pd.timeout_usec = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch()).count();
ASSERT_TRUE(pd.getRelativeTimeout().has_value());
EXPECT_GE(pd.getRelativeTimeout().value(), TIMEOUT - TOLERANCE);
EXPECT_LE(pd.getRelativeTimeout().value(), TIMEOUT + TOLERANCE);
EXPECT_GE(pd.getPollTimeout(), 900);
EXPECT_LE(pd.getPollTimeout(), 1100);
}
+50 -53
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusGeneralTests.cpp
*
@@ -44,8 +44,8 @@ using ::testing::ElementsAre;
using ::testing::Eq;
using namespace std::chrono_literals;
using namespace sdbus::test;
using namespace std::string_view_literals;
using AConnection = TestFixture;
using ADirectConnection = TestFixtureWithDirectConnection;
/*-------------------------------------*/
@@ -54,119 +54,116 @@ using ADirectConnection = TestFixtureWithDirectConnection;
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
{
auto connection = sdbus::createBusConnection();
connection->requestName(SERVICE_NAME);
auto connection = sdbus::createConnection();
connection->requestName(BUS_NAME);
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
ASSERT_NO_THROW(TestProxy proxy(SERVICE_NAME, OBJECT_PATH));
connection->releaseName(SERVICE_NAME);
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
}
TEST(AProxy, DoesNotSupportMoveSemantics)
TEST(AProxy, SupportsMoveSemantics)
{
static_assert(!std::is_move_constructible_v<DummyTestProxy>);
static_assert(!std::is_move_assignable_v<DummyTestProxy>);
static_assert(std::is_move_constructible_v<DummyTestProxy>);
static_assert(std::is_move_assignable_v<DummyTestProxy>);
}
TEST(AnAdaptor, DoesNotSupportMoveSemantics)
TEST(AnAdaptor, SupportsMoveSemantics)
{
static_assert(!std::is_move_constructible_v<DummyTestAdaptor>);
static_assert(!std::is_move_assignable_v<DummyTestAdaptor>);
static_assert(std::is_move_constructible_v<DummyTestAdaptor>);
static_assert(std::is_move_assignable_v<DummyTestAdaptor>);
}
TYPED_TEST(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
{
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message msg)
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}, sdbus::return_slot);
});
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(matchingMessageReceived));
}
TYPED_TEST(AConnection, CanInstallMatchRuleAsynchronously)
TEST_F(AConnection, CanInstallMatchRuleAsynchronously)
{
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
std::atomic<bool> matchRuleInstalled{false};
auto slot = this->s_proxyConnection->addMatchAsync( matchRule
, [&](sdbus::Message msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}
, [&](sdbus::Message /*msg*/)
{
matchRuleInstalled = true;
}
, sdbus::return_slot );
auto slot = s_proxyConnection->addMatchAsync( matchRule
, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}
, [&](sdbus::Message& /*msg*/)
{
matchRuleInstalled = true;
} );
EXPECT_TRUE(waitUntil(matchRuleInstalled));
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(matchingMessageReceived));
}
TYPED_TEST(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
TEST_F(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
{
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message msg)
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}, sdbus::return_slot);
});
slot.reset();
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
}
TYPED_TEST(AConnection, CanAddFloatingMatchRule)
TEST_F(AConnection, CanAddFloatingMatchRule)
{
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto con = sdbus::createBusConnection();
auto con = sdbus::createSystemBusConnection();
con->enterEventLoopAsync();
auto callback = [&](sdbus::Message msg)
auto callback = [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
};
con->addMatch(matchRule, std::move(callback));
this->m_adaptor->emitSimpleSignal();
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
m_adaptor->emitSimpleSignal();
[[maybe_unused]] auto gotMessage = waitUntil(matchingMessageReceived, 2s);
assert(gotMessage);
matchingMessageReceived = false;
con.reset();
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
}
TYPED_TEST(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
TEST_F(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
{
auto matchRule = "type='signal',interface='" + INTERFACE_NAME + "',member='simpleSignal'";
std::atomic<size_t> numberOfMatchingMessages{};
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](sdbus::Message msg)
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
{
if(msg.getMemberName() == "simpleSignal"sv)
if(msg.getMemberName() == "simpleSignal")
numberOfMatchingMessages++;
}, sdbus::return_slot);
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
});
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
this->m_adaptor->emitSignalWithMap({});
m_adaptor->emitSignalWithMap({});
adaptor2->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
+81 -114
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusMethodsTests.cpp
*
@@ -50,135 +50,131 @@ using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TYPED_TEST(SdbusTestObject, CallsEmptyMethodSuccesfully)
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
{
ASSERT_NO_THROW(this->m_proxy->noArgNoReturn());
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
}
TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
{
auto resInt = this->m_proxy->getInt();
auto resInt = m_proxy->getInt();
ASSERT_THAT(resInt, Eq(INT32_VALUE));
auto multiplyRes = this->m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TYPED_TEST(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
{
auto resTuple = this->m_proxy->getTuple();
auto resTuple = m_proxy->getTuple();
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
}
TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccesfully)
TEST_F(SdbusTestObject, CallsMethodsWithStructSuccesfully)
{
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
auto vectorRes = this->m_proxy->getInts16FromStruct(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 = this->m_proxy->getInts16FromStruct(b);
vectorRes = m_proxy->getInts16FromStruct(b);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithVariantSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
{
sdbus::Variant v{DOUBLE_VALUE};
sdbus::Variant variantRes = this->m_proxy->processVariant(v);
auto variantRes = m_proxy->processVariant(v);
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithStdVariantSuccesfully)
{
std::variant<int32_t, double, std::string> v{DOUBLE_VALUE};
auto variantRes = this->m_proxy->processVariant(v);
ASSERT_THAT(std::get<int32_t>(variantRes), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
{
std::vector<int32_t> x{-2, 0, 2};
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
std::map<int32_t, sdbus::Variant> mapOfVariants = this->m_proxy->getMapOfVariants(x, y);
decltype(mapOfVariants) res{ {-2, sdbus::Variant{false}}
, {0, sdbus::Variant{false}}
, {2, sdbus::Variant{true}}};
auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
decltype(mapOfVariants) res{ {sdbus::Variant{-2}, sdbus::Variant{false}}
, {sdbus::Variant{0}, sdbus::Variant{false}}
, {sdbus::Variant{2}, sdbus::Variant{true}}};
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
{
auto val = this->m_proxy->getStructInStruct();
ASSERT_THAT(val.template get<0>(), Eq(STRING_VALUE));
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));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
{
auto val = this->m_proxy->sumStructItems({1, 2}, {3, 4});
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
{
auto val = this->m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
auto val = m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
{
auto resSignature = this->m_proxy->getSignature();
auto resSignature = m_proxy->getSignature();
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
{
auto resObjectPath = this->m_proxy->getObjPath();
auto resObjectPath = m_proxy->getObjPath();
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
{
auto resUnixFd = this->m_proxy->getUnixFd();
auto resUnixFd = m_proxy->getUnixFd();
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
{
auto resComplex = this->m_proxy->getComplex();
auto resComplex = m_proxy->getComplex();
ASSERT_THAT(resComplex.count(0), Eq(1));
}
TYPED_TEST(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
{
this->m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasMultiplyCalled));
ASSERT_THAT(this->m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
ASSERT_TRUE(waitUntil(m_adaptor->m_wasMultiplyCalled));
ASSERT_THAT(m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
TEST_F(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
{
auto res = this->m_proxy->doOperationWithTimeout(500ms, (20ms).count()); // The operation will take 20ms, but the timeout is 500ms, so we are fine
auto res = m_proxy->doOperationWithTimeout(500ms, 20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
ASSERT_THAT(res, Eq(20));
}
TYPED_TEST(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
{
auto start = std::chrono::steady_clock::now();
try
{
this->m_proxy->doOperationWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
m_proxy->doOperationWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 1us, so we should time out
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
@@ -194,11 +190,11 @@ TYPED_TEST(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
}
}
TYPED_TEST(SdbusTestObject, CallsMethodThatThrowsError)
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
{
try
{
this->m_proxy->throwError();
m_proxy->throwError();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
@@ -212,111 +208,82 @@ TYPED_TEST(SdbusTestObject, CallsMethodThatThrowsError)
}
}
TYPED_TEST(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(this->m_proxy->throwErrorWithNoReply());
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasThrowErrorCalled));
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
}
TYPED_TEST(SdbusTestObject, FailsCallingNonexistentMethod)
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
{
ASSERT_THROW(this->m_proxy->callNonexistentMethod(), sdbus::Error);
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
}
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
{
ASSERT_THROW(this->m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
}
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
{
TestProxy proxy(sdbus::ServiceName{"sdbuscpp.destination.that.does.not.exist"}, OBJECT_PATH);
TestProxy proxy("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
{
TestProxy proxy(SERVICE_NAME, sdbus::ObjectPath{"/sdbuscpp/path/that/does/not/exist"});
TestProxy proxy(BUS_NAME, "/sdbuscpp/path/that/does/not/exist");
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TYPED_TEST(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
TEST_F(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
{
this->m_proxy->emitTwoSimpleSignals();
m_proxy->emitTwoSimpleSignals();
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
}
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
{
this->m_proxy->doOperation(10); // This will save pointer to method call message on server side
m_proxy->doOperation(10); // This will save pointer to method call message on server side
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperation"));
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperation"));
}
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
{
this->m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperationAsync"));
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperationAsync"));
}
#if LIBSYSTEMD_VERSION>=240
TYPED_TEST(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
{
this->s_adaptorConnection->setMethodCallTimeout(5000000);
ASSERT_THAT(this->s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
s_adaptorConnection->setMethodCallTimeout(5000000);
ASSERT_THAT(s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
}
#else
TYPED_TEST(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
{
ASSERT_THROW(this->s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(this->s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
ASSERT_THROW(s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
}
#endif
TYPED_TEST(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
TEST_F(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
{
auto proxy = std::make_unique<TestProxy>(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
#if defined(__clang__) && defined(__FreeBSD__)
GTEST_SKIP() << "https://github.com/Kistler-Group/sdbus-cpp/issues/359";
#endif
auto proxy = std::make_unique<TestProxy>(BUS_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
auto multiplyRes = proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime)
{
auto& object = this->m_adaptor->getObject();
sdbus::InterfaceName interfaceName{"org.sdbuscpp.integrationtests2"};
auto vtableSlot = object.addVTable( interfaceName
, { sdbus::registerMethod("add").implementedAs([](const int64_t& a, const double& b){ return a + b; })
, sdbus::registerMethod("subtract").implementedAs([](const int& a, const int& b){ return a - b; }) }
, sdbus::return_slot );
// The new remote vtable is registered as long as we keep vtableSlot, so remote method calls now should pass
auto proxy = sdbus::createProxy(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
int result{};
proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2).storeResultsTo(result);
ASSERT_THAT(result, Eq(8));
}
TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime)
{
auto& object = this->m_adaptor->getObject();
sdbus::InterfaceName interfaceName{"org.sdbuscpp.integrationtests2"};
auto vtableSlot = object.addVTable( interfaceName
, { sdbus::registerMethod("add").implementedAs([](const int64_t& a, const double& b){ return a + b; })
, sdbus::registerMethod("subtract").implementedAs([](const int& a, const int& b){ return a - b; }) }
, sdbus::return_slot );
vtableSlot.reset(); // Letting the slot go means letting go the associated vtable registration
// No such remote D-Bus method under given interface exists anymore...
auto proxy = sdbus::createProxy(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
ASSERT_THROW(proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2), sdbus::Error);
}
+14 -12
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusPropertiesTests.cpp
*
@@ -51,33 +51,35 @@ using ::testing::IsEmpty;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TYPED_TEST(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
TEST_F(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
{
ASSERT_THAT(this->m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
}
TYPED_TEST(SdbusTestObject, FailsWritingToReadOnlyProperty)
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
{
ASSERT_THROW(this->m_proxy->setStateProperty("new_value"), sdbus::Error);
ASSERT_THROW(m_proxy->setStateProperty("new_value"), sdbus::Error);
}
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
{
uint32_t newActionValue = 5678;
this->m_proxy->action(newActionValue);
m_proxy->action(newActionValue);
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TYPED_TEST(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
TEST_F(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
{
this->m_proxy->blocking(true); // This will save pointer to property get message on server side
m_proxy->blocking(true); // This will save pointer to property get message on server side
ASSERT_THAT(this->m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_propertySetSender, Not(IsEmpty()));
ASSERT_THAT(m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(m_adaptor->m_propertySetSender, Not(IsEmpty()));
}
+55 -53
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorAndProxy_test.cpp
*
@@ -44,124 +44,126 @@ using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalSuccesfully)
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
{
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
}
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
{
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
}
TYPED_TEST(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
{
sdbus::ServiceName otherBusName{SERVICE_NAME + "2"};
auto connection2 = sdbus::createBusConnection(otherBusName);
auto otherBusName = BUS_NAME + "2";
auto connection2 = sdbus::createConnection(otherBusName);
auto adaptor2 = std::make_unique<TestAdaptor>(*connection2, OBJECT_PATH);
adaptor2->emitSimpleSignal();
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
}
TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccesfully)
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
{
this->m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("zero"));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("one"));
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"));
}
TYPED_TEST(SdbusTestObject, EmitsSignalWithLargeMapSuccesfully)
TEST_F(SdbusTestObject, EmitsSignalWithLargeMapSuccesfully)
{
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 20'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
this->m_adaptor->emitSignalWithMap(largeMap);
m_adaptor->emitSignalWithMap(largeMap);
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
ASSERT_THAT(this->m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("This is string nr. 1"));
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("This is string nr. 2"));
}
TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
{
double d = 3.14;
this->m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(this->m_proxy->m_variantFromSignal, DoubleEq(d));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
}
TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
{
this->m_adaptor->emitSignalWithoutRegistration({"platform", sdbus::Signature{"av"}});
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(this->m_proxy->m_signatureFromSignal["platform"], Eq(sdbus::Signature{"av"}));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
}
TYPED_TEST(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
TEST_F(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
{
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
waitUntil(this->m_proxy->m_gotSimpleSignal);
waitUntil(m_proxy->m_gotSimpleSignal);
ASSERT_THAT(this->m_proxy->m_signalMsg, NotNull());
ASSERT_THAT(this->m_proxy->m_signalName, Eq(std::string("simpleSignal")));
ASSERT_THAT(m_proxy->m_signalMsg, NotNull());
ASSERT_THAT(m_proxy->m_signalMemberName, Eq("simpleSignal"));
}
TYPED_TEST(SdbusTestObject, UnregistersSignalHandler)
TEST_F(SdbusTestObject, UnregistersSignalHandler)
{
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
}
TYPED_TEST(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
TEST_F(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
{
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
}
TYPED_TEST(SdbusTestObject, ReRegistersSignalHandler)
TEST_F(SdbusTestObject, ReRegistersSignalHandler)
{
// unregister simple-signal handler
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
// re-register simple-signal handler
ASSERT_NO_THROW(this->m_proxy->reRegisterSimpleSignalHandler());
ASSERT_NO_THROW(m_proxy->reRegisterSimpleSignalHandler());
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
}
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusStandardInterfacesTests.cpp
*
@@ -48,204 +48,206 @@ using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TYPED_TEST(SdbusTestObject, PingsViaPeerInterface)
TEST_F(SdbusTestObject, PingsViaPeerInterface)
{
ASSERT_NO_THROW(this->m_proxy->Ping());
ASSERT_NO_THROW(m_proxy->Ping());
}
TYPED_TEST(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
{
if (::access("/etc/machine-id", F_OK) == -1 &&
::access("/var/lib/dbus/machine-id", F_OK) == -1)
GTEST_SKIP() << "/etc/machine-id and /var/lib/dbus/machine-id files do not exist, GetMachineId() will not work";
ASSERT_NO_THROW(this->m_proxy->GetMachineId());
ASSERT_NO_THROW(m_proxy->GetMachineId());
}
// TODO: Adjust expected xml and uncomment this test
//TYPED_TEST(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
//TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
//{
// ASSERT_THAT(this->m_proxy->Introspect(), Eq(this->m_adaptor->getExpectedXmlApiDescription()));
// ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
//}
TYPED_TEST(SdbusTestObject, GetsPropertyViaPropertiesInterface)
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
{
ASSERT_THAT(this->m_proxy->Get(INTERFACE_NAME, "state").template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
{
std::promise<std::string> promise;
auto future = promise.get_future();
this->m_proxy->GetAsync(INTERFACE_NAME, "state", [&](std::optional<sdbus::Error> err, sdbus::Variant value)
m_proxy->GetAsync(INTERFACE_NAME, "state", [&](const sdbus::Error* err, sdbus::Variant value)
{
if (!err)
if (err == nullptr)
promise.set_value(value.get<std::string>());
else
promise.set_exception(std::make_exception_ptr(*std::move(err)));
promise.set_exception(std::make_exception_ptr(*err));
});
ASSERT_THAT(future.get(), Eq(DEFAULT_STATE_VALUE));
}
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
{
auto future = this->m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
auto future = m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
ASSERT_THAT(future.get().template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(future.get().get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TYPED_TEST(SdbusTestObject, SetsPropertyViaPropertiesInterface)
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
{
uint32_t newActionValue = 2345;
this->m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
{
uint32_t newActionValue = 2346;
std::promise<void> promise;
auto future = promise.get_future();
this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](const sdbus::Error* err)
{
if (!err)
if (err == nullptr)
promise.set_value();
else
promise.set_exception(std::make_exception_ptr(*std::move(err)));
promise.set_exception(std::make_exception_ptr(*err));
});
ASSERT_NO_THROW(future.get());
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
{
uint32_t newActionValue = 2347;
auto future = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
auto future = m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
ASSERT_NO_THROW(future.get());
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TYPED_TEST(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = this->m_proxy->GetAll(INTERFACE_NAME);
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
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));
}
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
{
std::promise<std::map<sdbus::PropertyName, sdbus::Variant>> promise;
std::promise<std::map<std::string, sdbus::Variant>> promise;
auto future = promise.get_future();
this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](std::optional<sdbus::Error> err, std::map<sdbus::PropertyName, sdbus::Variant> value)
m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map<std::string, sdbus::Variant> value)
{
if (!err)
if (err == nullptr)
promise.set_value(std::move(value));
else
promise.set_exception(std::make_exception_ptr(*std::move(err)));
promise.set_exception(std::make_exception_ptr(*err));
});
const auto properties = future.get();
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at(STATE_PROPERTY).get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
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));
}
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
{
auto future = this->m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
auto future = m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
auto properties = future.get();
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
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));
}
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
{
std::atomic<bool> signalReceived{false};
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
, const std::vector<sdbus::PropertyName>& /*invalidatedProperties*/ )
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_PROPERTY).get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
signalReceived = true;
};
this->m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
this->m_proxy->action(DEFAULT_ACTION_VALUE*2);
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {BLOCKING_PROPERTY});
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
m_proxy->action(DEFAULT_ACTION_VALUE*2);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
ASSERT_TRUE(waitUntil(signalReceived));
}
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
{
std::atomic<bool> signalReceived{false};
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
, const std::vector<sdbus::PropertyName>& invalidatedProperties )
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_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
ASSERT_THAT(invalidatedProperties, SizeIs(1));
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
signalReceived = true;
};
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
ASSERT_TRUE(waitUntil(signalReceived));
}
TYPED_TEST(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
{
this->m_adaptor.reset();
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
m_adaptor.reset();
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
}
TYPED_TEST(SdbusTestObject, GetsManagedObjectsSuccessfully)
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
{
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH)
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2)
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
}
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
@@ -253,30 +255,30 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterface
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#endif
signalReceived = true;
};
this->m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
@@ -289,30 +291,30 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
#endif
signalReceived = true;
};
this->m_adaptor->emitInterfacesAddedSignal();
m_adaptor->emitInterfacesAddedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<sdbus::InterfaceName>& interfaces )
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(1));
@@ -320,16 +322,16 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfa
signalReceived = true;
};
this->m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<sdbus::InterfaceName>& interfaces )
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
@@ -342,7 +344,7 @@ TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
signalReceived = true;
};
this->m_adaptor->emitInterfacesRemovedSignal();
m_adaptor->emitInterfacesRemovedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}
+9 -12
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Defs.h
*
@@ -34,15 +34,12 @@
namespace sdbus { namespace test {
const InterfaceName INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
const ServiceName SERVICE_NAME{"org.sdbuscpp.integrationtests"};
const ServiceName EMPTY_DESTINATION;
const ObjectPath MANAGER_PATH {"/org/sdbuscpp/integrationtests"};
const ObjectPath OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
const ObjectPath OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
const PropertyName STATE_PROPERTY{"state"};
const PropertyName ACTION_PROPERTY{"action"};
const PropertyName BLOCKING_PROPERTY{"blocking"};
const std::string INTERFACE_NAME{"org.sdbuscpp.integrationtests"};
const std::string BUS_NAME = INTERFACE_NAME;
const std::string EMPTY_DESTINATION;
const std::string MANAGER_PATH {"/org/sdbuscpp/integrationtests"};
const std::string OBJECT_PATH {"/org/sdbuscpp/integrationtests/ObjectA1"};
const std::string OBJECT_PATH_2{"/org/sdbuscpp/integrationtests/ObjectB1"};
const std::string DIRECT_CONNECTION_SOCKET_PATH{std::filesystem::temp_directory_path() / "sdbus-cpp-direct-connection-test"};
constexpr const uint8_t UINT8_VALUE{1};
@@ -52,8 +49,8 @@ constexpr const int32_t INT32_VALUE{-42};
constexpr const int32_t INT64_VALUE{-1024};
const std::string STRING_VALUE{"sdbus-c++-testing"};
const Signature SIGNATURE_VALUE{"a{is}"};
const ObjectPath OBJECT_PATH_VALUE{"/"};
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"};
+12 -12
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
*
@@ -31,8 +31,8 @@
namespace sdbus { namespace test {
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path) :
AdaptorInterfaces(connection, std::move(path))
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) :
AdaptorInterfaces(connection, path)
{
registerAdaptor();
}
@@ -75,9 +75,9 @@ std::vector<int16_t> TestAdaptor::getInts16FromStruct(const sdbus::Struct<uint8_
return res;
}
sdbus::Variant TestAdaptor::processVariant(const std::variant<int32_t, double, std::string>& v)
sdbus::Variant TestAdaptor::processVariant(const sdbus::Variant& v)
{
sdbus::Variant res{static_cast<int32_t>(std::get<double>(v))};
sdbus::Variant res{static_cast<int32_t>(v.get<double>())};
return res;
}
@@ -122,16 +122,16 @@ uint32_t TestAdaptor::doOperation(const uint32_t& param)
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_methodName = m_methodCallMsg->getMemberName();
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
m_methodCallMemberName = m_methodCallMsg->getMemberName();
return param;
}
void TestAdaptor::doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t param)
{
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_methodName = m_methodCallMsg->getMemberName();
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
m_methodCallMemberName = m_methodCallMsg->getMemberName();
if (param == 0)
{
@@ -173,7 +173,7 @@ std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::
23, // uint8_t
{ // vector
{ // struct
sdbus::ObjectPath{"/object/path"}, // object path
"/object/path", // object path
false,
Variant{3.14},
{ // map
@@ -183,7 +183,7 @@ std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::
}
}
},
sdbus::Signature{"a{t(a{ya(obva{is})}gs)}"}, // signature
"a{t(a{ya(obva{is})}gs)}", // signature
std::string{}
}
}
@@ -234,7 +234,7 @@ bool TestAdaptor::blocking()
void TestAdaptor::blocking(const bool& value)
{
m_propertySetMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_propertySetMsg = getObject().getCurrentlyProcessedMessage();
m_propertySetSender = m_propertySetMsg->getSender();
m_blocking = value;
+9 -12
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
*
@@ -33,14 +33,13 @@
#include <chrono>
#include <atomic>
#include <utility>
#include <memory>
namespace sdbus { namespace test {
class ObjectManagerTestAdaptor final : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
{
public:
ObjectManagerTestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path) :
ObjectManagerTestAdaptor(sdbus::IConnection& connection, std::string path) :
AdaptorInterfaces(connection, std::move(path))
{
registerAdaptor();
@@ -57,7 +56,7 @@ class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integr
, sdbus::ManagedObject_adaptor >
{
public:
TestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path);
TestAdaptor(sdbus::IConnection& connection, const std::string& path);
~TestAdaptor();
protected:
@@ -67,7 +66,7 @@ protected:
double multiply(const int64_t& a, const double& b) override;
void multiplyWithNoReply(const int64_t& a, const double& b) override;
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) override;
sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>& variant) override;
sdbus::Variant processVariant(const sdbus::Variant& variant) override;
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) override;
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override;
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) override;
@@ -104,9 +103,9 @@ public: // for tests
mutable double m_multiplyResult{};
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
std::unique_ptr<const Message> m_methodCallMsg;
MethodName m_methodName;
std::unique_ptr<const Message> m_propertySetMsg;
const Message* m_methodCallMsg{};
std::string m_methodCallMemberName;
const Message* m_propertySetMsg{};
std::string m_propertySetSender;
};
@@ -115,9 +114,7 @@ class DummyTestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::i
, sdbus::ManagedObject_adaptor >
{
public:
DummyTestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path)
: AdaptorInterfaces(connection, std::move(path))
{}
DummyTestAdaptor(sdbus::IConnection& connection, const std::string& path) : AdaptorInterfaces(connection, path) {}
protected:
void noArgNoReturn() override {}
@@ -126,7 +123,7 @@ protected:
double multiply(const int64_t&, const double&) override { return {}; }
void multiplyWithNoReply(const int64_t&, const double&) override {}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>&) override { return {}; }
sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>&) override { return {}; }
sdbus::Variant processVariant(const sdbus::Variant&) override { return {}; }
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>&, const sdbus::Struct<sdbus::Variant, sdbus::Variant>&) override { return {}; }
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() override { return {}; }
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>&, const sdbus::Struct<int32_t, int64_t>&) override { return {}; }
+4 -14
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestFixture.cpp
* @file TestAdaptor.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
@@ -28,17 +28,7 @@
namespace sdbus { namespace test {
std::unique_ptr<sdbus::IConnection> BaseTestFixture::s_adaptorConnection = sdbus::createBusConnection();
std::unique_ptr<sdbus::IConnection> BaseTestFixture::s_proxyConnection = sdbus::createBusConnection();
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
std::thread TestFixture<SdEventLoop>::s_adaptorEventLoopThread{};
std::thread TestFixture<SdEventLoop>::s_proxyEventLoopThread{};
sd_event *TestFixture<SdEventLoop>::s_adaptorSdEvent{};
sd_event *TestFixture<SdEventLoop>::s_proxySdEvent{};
int TestFixture<SdEventLoop>::s_eventExitFd{-1};
#endif // SDBUS_basu
std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection();
}}
+12 -113
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestFixture.h
* @file TestAdaptor.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
@@ -33,10 +33,6 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
#include <systemd/sd-event.h>
#endif // SDBUS_basu
#include <sys/eventfd.h>
#include <thread>
#include <chrono>
@@ -50,24 +46,29 @@
namespace sdbus { namespace test {
class BaseTestFixture : public ::testing::Test
class TestFixture : public ::testing::Test
{
public:
static void SetUpTestCase()
{
s_adaptorConnection->requestName(SERVICE_NAME);
s_proxyConnection->enterEventLoopAsync();
s_adaptorConnection->requestName(BUS_NAME);
s_adaptorConnection->enterEventLoopAsync();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
}
static void TearDownTestCase()
{
s_adaptorConnection->releaseName(SERVICE_NAME);
s_adaptorConnection->releaseName(BUS_NAME);
s_adaptorConnection->leaveEventLoop();
s_proxyConnection->leaveEventLoop();
}
private:
void SetUp() override
{
m_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, SERVICE_NAME, MANAGER_PATH);
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, SERVICE_NAME, OBJECT_PATH);
m_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, BUS_NAME, MANAGER_PATH);
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, BUS_NAME, OBJECT_PATH);
m_objectManagerAdaptor = std::make_unique<ObjectManagerTestAdaptor>(*s_adaptorConnection, MANAGER_PATH);
m_adaptor = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH);
@@ -88,108 +89,6 @@ public:
std::unique_ptr<TestProxy> m_proxy;
};
struct SdBusCppLoop{};
struct SdEventLoop{};
template <typename _EventLoop>
class TestFixture : public BaseTestFixture{};
// Fixture working upon internal sdbus-c++ event loop
template <>
class TestFixture<SdBusCppLoop> : public BaseTestFixture
{
public:
static void SetUpTestCase()
{
BaseTestFixture::SetUpTestCase();
s_proxyConnection->enterEventLoopAsync();
s_adaptorConnection->enterEventLoopAsync();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
}
static void TearDownTestCase()
{
BaseTestFixture::TearDownTestCase();
s_adaptorConnection->leaveEventLoop();
s_proxyConnection->leaveEventLoop();
}
};
#ifndef SDBUS_basu // sd_event integration is not supported in basu-based sdbus-c++
// Fixture working upon attached external sd-event loop
template <>
class TestFixture<SdEventLoop> : public BaseTestFixture
{
public:
static void SetUpTestCase()
{
sd_event_new(&s_adaptorSdEvent);
sd_event_new(&s_proxySdEvent);
s_adaptorConnection->attachSdEventLoop(s_adaptorSdEvent);
s_proxyConnection->attachSdEventLoop(s_proxySdEvent);
s_eventExitFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
auto exitHandler = [](sd_event_source *s, auto...){ return sd_event_exit(sd_event_source_get_event(s), 0); };
sd_event_add_io(s_adaptorSdEvent, nullptr, s_eventExitFd, EPOLLIN, exitHandler, nullptr);
sd_event_add_io(s_proxySdEvent, nullptr, s_eventExitFd, EPOLLIN, exitHandler, nullptr);
s_adaptorEventLoopThread = std::thread([]()
{
sd_event_loop(s_adaptorSdEvent);
});
s_proxyEventLoopThread = std::thread([]()
{
sd_event_loop(s_proxySdEvent);
});
BaseTestFixture::SetUpTestCase();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
}
static void TearDownTestCase()
{
(void)eventfd_write(s_eventExitFd, 1);
s_adaptorEventLoopThread.join();
s_proxyEventLoopThread.join();
sd_event_unref(s_adaptorSdEvent);
sd_event_unref(s_proxySdEvent);
close(s_eventExitFd);
BaseTestFixture::TearDownTestCase();
}
private:
static std::thread s_adaptorEventLoopThread;
static std::thread s_proxyEventLoopThread;
static sd_event *s_adaptorSdEvent;
static sd_event *s_proxySdEvent;
static int s_eventExitFd;
};
typedef ::testing::Types<SdBusCppLoop, SdEventLoop> EventLoopTags;
#else // SDBUS_basu
typedef ::testing::Types<SdBusCppLoop> EventLoopTags;
#endif // SDBUS_basu
TYPED_TEST_SUITE(TestFixture, EventLoopTags);
template <typename _EventLoop>
using SdbusTestObject = TestFixture<_EventLoop>;
TYPED_TEST_SUITE(SdbusTestObject, EventLoopTags);
template <typename _EventLoop>
using AsyncSdbusTestObject = TestFixture<_EventLoop>;
TYPED_TEST_SUITE(AsyncSdbusTestObject, EventLoopTags);
template <typename _EventLoop>
using AConnection = TestFixture<_EventLoop>;
TYPED_TEST_SUITE(AConnection, EventLoopTags);
class TestFixtureWithDirectConnection : public ::testing::Test
{
private:
+21 -34
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestProxy.cpp
* @file TestAdaptor.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
@@ -31,7 +31,7 @@
namespace sdbus { namespace test {
TestProxy::TestProxy(ServiceName destination, ObjectPath objectPath)
TestProxy::TestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s){ this->onSignalWithoutRegistration(s); });
@@ -39,14 +39,14 @@ TestProxy::TestProxy(ServiceName destination, ObjectPath objectPath)
registerProxy();
}
TestProxy::TestProxy(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
TestProxy::TestProxy(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
: ProxyInterfaces(std::move(destination), std::move(objectPath), dont_run_event_loop_thread)
{
// It doesn't make sense to register any signals here since proxy upon a D-Bus connection with no event loop thread
// will not receive any incoming messages except replies to synchronous D-Bus calls.
}
TestProxy::TestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath)
TestProxy::TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s){ this->onSignalWithoutRegistration(s); });
@@ -61,8 +61,8 @@ TestProxy::~TestProxy()
void TestProxy::onSimpleSignal()
{
m_signalMsg = std::make_unique<sdbus::Message>(getProxy().getCurrentlyProcessedMessage());
m_signalName = m_signalMsg->getMemberName();
m_signalMsg = getProxy().getCurrentlyProcessedMessage();
m_signalMemberName = m_signalMsg->getMemberName();
m_gotSimpleSignal = true;
}
@@ -81,26 +81,25 @@ void TestProxy::onSignalWithVariant(const sdbus::Variant& aVariant)
void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
{
// Static cast to std::string is a workaround for gcc 11.4 false positive warning (which later gcc versions nor Clang emit)
m_signatureFromSignal[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
m_gotSignalWithSignature = true;
}
void TestProxy::onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error)
void TestProxy::onDoOperationReply(uint32_t returnValue, const sdbus::Error* error)
{
if (m_DoOperationClientSideAsyncReplyHandler)
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
}
void TestProxy::onPropertiesChanged( const sdbus::InterfaceName& interfaceName
, const std::map<PropertyName, sdbus::Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties )
void TestProxy::onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
{
if (m_onPropertiesChangedHandler)
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
}
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler)
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
{
m_DoOperationClientSideAsyncReplyHandler = std::move(handler);
}
@@ -118,23 +117,12 @@ sdbus::PendingAsyncCall TestProxy::doOperationClientSideAsync(uint32_t param)
return getProxy().callMethodAsync("doOperation")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(param)
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, std::move(error));
this->onDoOperationReply(returnValue, error);
});
}
Slot TestProxy::doOperationClientSideAsync(uint32_t param, sdbus::return_slot_t)
{
return getProxy().callMethodAsync("doOperation")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(param)
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, std::move(error));
}, sdbus::return_slot);
}
std::future<uint32_t> TestProxy::doOperationClientSideAsync(uint32_t param, with_future_t)
{
return getProxy().callMethodAsync("doOperation")
@@ -145,19 +133,19 @@ std::future<uint32_t> TestProxy::doOperationClientSideAsync(uint32_t param, with
std::future<MethodReply> TestProxy::doOperationClientSideAsyncOnBasicAPILevel(uint32_t param)
{
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, sdbus::MethodName{"doOperation"});
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, "doOperation");
methodCall << param;
return getProxy().callMethodAsync(methodCall, sdbus::with_future);
return getProxy().callMethod(methodCall, sdbus::with_future);
}
void TestProxy::doErroneousOperationClientSideAsync()
{
getProxy().callMethodAsync("throwError")
.onInterface(sdbus::test::INTERFACE_NAME)
.uponReplyInvoke([this](std::optional<sdbus::Error> error)
.uponReplyInvoke([this](const sdbus::Error* error)
{
this->onDoOperationReply(0, std::move(error));
this->onDoOperationReply(0, error);
});
}
@@ -175,9 +163,9 @@ void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microse
.onInterface(sdbus::test::INTERFACE_NAME)
.withTimeout(timeout)
.withArguments(param)
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, std::move(error));
this->onDoOperationReply(returnValue, error);
});
}
@@ -190,9 +178,8 @@ int32_t TestProxy::callNonexistentMethod()
int32_t TestProxy::callMethodOnNonexistentInterface()
{
sdbus::InterfaceName nonexistentInterfaceName{"sdbuscpp.interface.that.does.not.exist"};
int32_t result;
getProxy().callMethod("someMethod").onInterface(nonexistentInterfaceName).storeResultsTo(result);
getProxy().callMethod("someMethod").onInterface("sdbuscpp.interface.that.does.not.exist").storeResultsTo(result);
return result;
}
+23 -25
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestProxy.h
* @file TestAdaptor.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
@@ -33,14 +33,13 @@
#include <chrono>
#include <atomic>
#include <future>
#include <memory>
namespace sdbus { namespace test {
class ObjectManagerTestProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
{
public:
ObjectManagerTestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath)
ObjectManagerTestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
@@ -51,21 +50,21 @@ public:
unregisterProxy();
}
protected:
void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>& interfacesAndProperties) override
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<sdbus::InterfaceName>& interfaces) override
void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector<std::string>& interfaces) override
{
if (m_onInterfacesRemovedHandler)
m_onInterfacesRemovedHandler(objectPath, interfaces);
}
public: // for tests
std::function<void(const sdbus::ObjectPath&, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>&)> m_onInterfacesAddedHandler;
std::function<void(const sdbus::ObjectPath&, const std::vector<sdbus::InterfaceName>&)> m_onInterfacesRemovedHandler;
std::function<void(const sdbus::ObjectPath&, const std::map<std::string, std::map<std::string, sdbus::Variant>>&)> m_onInterfacesAddedHandler;
std::function<void(const sdbus::ObjectPath&, const std::vector<std::string>&)> m_onInterfacesRemovedHandler;
};
class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
@@ -74,9 +73,9 @@ class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integratio
, sdbus::Properties_proxy >
{
public:
TestProxy(ServiceName destination, ObjectPath objectPath);
TestProxy(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t);
TestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath);
TestProxy(std::string destination, std::string objectPath);
TestProxy(std::string destination, std::string objectPath, dont_run_event_loop_thread_t);
TestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath);
~TestProxy();
protected:
@@ -85,18 +84,17 @@ protected:
void onSignalWithVariant(const sdbus::Variant& aVariant) override;
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
void onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error);
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error);
// Signals of standard D-Bus interfaces
void onPropertiesChanged( const sdbus::InterfaceName& interfaceName
, const std::map<PropertyName, sdbus::Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties ) override;
void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) override;
public:
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler);
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler);
uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param);
[[nodiscard]] sdbus::Slot doOperationClientSideAsync(uint32_t param, sdbus::return_slot_t);
std::future<uint32_t> doOperationClientSideAsync(uint32_t param, with_future_t);
std::future<MethodReply> doOperationClientSideAsyncOnBasicAPILevel(uint32_t param);
std::future<void> doErroneousOperationClientSideAsync(with_future_t);
@@ -115,13 +113,13 @@ public: // for tests
std::atomic<bool> m_gotSignalWithVariant{false};
double m_variantFromSignal;
std::atomic<bool> m_gotSignalWithSignature{false};
std::map<std::string, Signature> m_signatureFromSignal;
std::map<std::string, std::string> m_signatureFromSignal;
std::function<void(uint32_t res, std::optional<sdbus::Error> err)> m_DoOperationClientSideAsyncReplyHandler;
std::function<void(const sdbus::InterfaceName&, const std::map<PropertyName, sdbus::Variant>&, const std::vector<PropertyName>&)> m_onPropertiesChangedHandler;
std::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::unique_ptr<const Message> m_signalMsg;
SignalName m_signalName;
const Message* m_signalMsg{};
std::string m_signalMemberName;
};
class DummyTestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
@@ -130,7 +128,7 @@ class DummyTestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integ
, sdbus::Properties_proxy >
{
public:
DummyTestProxy(ServiceName destination, ObjectPath objectPath)
DummyTestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(destination, objectPath)
{
}
@@ -141,10 +139,10 @@ protected:
void onSignalWithVariant(const sdbus::Variant&) override {}
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>&) {}
void onDoOperationReply(uint32_t, std::optional<sdbus::Error>) {}
void onDoOperationReply(uint32_t, const sdbus::Error*) {}
// Signals of standard D-Bus interfaces
void onPropertiesChanged(const InterfaceName&, const std::map<PropertyName, sdbus::Variant>&, const std::vector<PropertyName>&) override {}
void onPropertiesChanged( const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>& ) override {}
};
}}
@@ -20,64 +20,59 @@ public:
protected:
integrationtests_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
object_->setInterfaceFlags(INTERFACE_NAME).markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL);
object_->registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
object_->registerMethod("getInt").onInterface(INTERFACE_NAME).withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); });
object_->registerMethod("getTuple").onInterface(INTERFACE_NAME).withOutputParamNames("arg0", "arg1").implementedAs([this](){ return this->getTuple(); });
object_->registerMethod("multiply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
object_->registerMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withInputParamNames("a", "b").implementedAs([this](const int64_t& a, const double& b){ return this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply();
object_->registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0){ return this->getInts16FromStruct(arg0); });
object_->registerMethod("processVariant").onInterface(INTERFACE_NAME).withInputParamNames("variant").withOutputParamNames("result").implementedAs([this](const sdbus::Variant& variant){ return this->processVariant(variant); });
object_->registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x, y); });
object_->registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); });
object_->registerMethod("sumStructItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1){ return this->sumStructItems(arg0, arg1); });
object_->registerMethod("sumArrayItems").onInterface(INTERFACE_NAME).withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1){ return this->sumArrayItems(arg0, arg1); });
object_->registerMethod("doOperation").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); });
object_->registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result<uint32_t>&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); });
object_->registerMethod("getSignature").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); });
object_->registerMethod("getObjPath").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getObjPath(); });
object_->registerMethod("getUnixFd").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getUnixFd(); });
object_->registerMethod("getComplex").onInterface(INTERFACE_NAME).withOutputParamNames("arg0").implementedAs([this](){ return this->getComplex(); }).markAsDeprecated();
object_->registerMethod("throwError").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwError(); });
object_->registerMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->throwErrorWithNoReply(); }).withNoReply();
object_->registerMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged();
object_->registerMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->emitTwoSimpleSignals(); });
object_->registerSignal("simpleSignal").onInterface(INTERFACE_NAME).markAsDeprecated();
object_->registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>("aMap");
object_->registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>("aVariant");
object_->registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL);
object_->registerProperty("blocking").onInterface(INTERFACE_NAME).withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); });
object_->registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE);
}
integrationtests_adaptor(const integrationtests_adaptor&) = delete;
integrationtests_adaptor& operator=(const integrationtests_adaptor&) = delete;
integrationtests_adaptor(integrationtests_adaptor&&) = delete;
integrationtests_adaptor& operator=(integrationtests_adaptor&&) = delete;
integrationtests_adaptor(integrationtests_adaptor&&) = default;
integrationtests_adaptor& operator=(integrationtests_adaptor&&) = default;
~integrationtests_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable( sdbus::setInterfaceFlags().markAsDeprecated().withPropertyUpdateBehavior(sdbus::Flags::EMITS_NO_SIGNAL)
, sdbus::registerMethod("noArgNoReturn").implementedAs([this](){ return this->noArgNoReturn(); })
, sdbus::registerMethod("getInt").withOutputParamNames("anInt").implementedAs([this](){ return this->getInt(); })
, sdbus::registerMethod("getTuple").withOutputParamNames("arg0", "arg1").implementedAs([this](){ return this->getTuple(); })
, sdbus::registerMethod("multiply").withInputParamNames("a", "b").withOutputParamNames("result").implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); })
, sdbus::registerMethod("multiplyWithNoReply").withInputParamNames("a", "b").implementedAs([this](const int64_t& a, const double& b){ return this->multiplyWithNoReply(a, b); }).markAsDeprecated().withNoReply()
, sdbus::registerMethod("getInts16FromStruct").withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0){ return this->getInts16FromStruct(arg0); })
, sdbus::registerMethod("processVariant").withInputParamNames("variant").withOutputParamNames("result").implementedAs([this](const std::variant<int32_t, double, std::string>& variant){ return this->processVariant(variant); })
, sdbus::registerMethod("getMapOfVariants").withInputParamNames("x", "y").withOutputParamNames("aMapOfVariants").implementedAs([this](const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x, y); })
, sdbus::registerMethod("getStructInStruct").withOutputParamNames("aMapOfVariants").implementedAs([this](){ return this->getStructInStruct(); })
, sdbus::registerMethod("sumStructItems").withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1){ return this->sumStructItems(arg0, arg1); })
, sdbus::registerMethod("sumArrayItems").withInputParamNames("arg0", "arg1").withOutputParamNames("arg0").implementedAs([this](const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1){ return this->sumArrayItems(arg0, arg1); })
, sdbus::registerMethod("doOperation").withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](const uint32_t& arg0){ return this->doOperation(arg0); })
, sdbus::registerMethod("doOperationAsync").withInputParamNames("arg0").withOutputParamNames("arg0").implementedAs([this](sdbus::Result<uint32_t>&& result, uint32_t arg0){ this->doOperationAsync(std::move(result), std::move(arg0)); })
, sdbus::registerMethod("getSignature").withOutputParamNames("arg0").implementedAs([this](){ return this->getSignature(); })
, sdbus::registerMethod("getObjPath").withOutputParamNames("arg0").implementedAs([this](){ return this->getObjPath(); })
, sdbus::registerMethod("getUnixFd").withOutputParamNames("arg0").implementedAs([this](){ return this->getUnixFd(); })
, sdbus::registerMethod("getComplex").withOutputParamNames("arg0").implementedAs([this](){ return this->getComplex(); }).markAsDeprecated()
, sdbus::registerMethod("throwError").implementedAs([this](){ return this->throwError(); })
, sdbus::registerMethod("throwErrorWithNoReply").implementedAs([this](){ return this->throwErrorWithNoReply(); }).withNoReply()
, sdbus::registerMethod("doPrivilegedStuff").implementedAs([this](){ return this->doPrivilegedStuff(); }).markAsPrivileged()
, sdbus::registerMethod("emitTwoSimpleSignals").implementedAs([this](){ return this->emitTwoSimpleSignals(); })
, sdbus::registerSignal("simpleSignal").markAsDeprecated()
, sdbus::registerSignal("signalWithMap").withParameters<std::map<int32_t, std::string>>("aMap")
, sdbus::registerSignal("signalWithVariant").withParameters<sdbus::Variant>("aVariant")
, sdbus::registerProperty("action").withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); }).withUpdateBehavior(sdbus::Flags::EMITS_INVALIDATION_SIGNAL)
, sdbus::registerProperty("blocking").withGetter([this](){ return this->blocking(); }).withSetter([this](const bool& value){ this->blocking(value); })
, sdbus::registerProperty("state").withGetter([this](){ return this->state(); }).markAsDeprecated().withUpdateBehavior(sdbus::Flags::CONST_PROPERTY_VALUE)
).forInterface(INTERFACE_NAME);
}
public:
void emitSimpleSignal()
{
m_object.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
object_->emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void emitSignalWithMap(const std::map<int32_t, std::string>& aMap)
{
m_object.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
object_->emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
}
void emitSignalWithVariant(const sdbus::Variant& aVariant)
{
m_object.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
object_->emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
}
private:
@@ -87,7 +82,7 @@ private:
virtual double multiply(const int64_t& a, const double& b) = 0;
virtual void multiplyWithNoReply(const int64_t& a, const double& b) = 0;
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0) = 0;
virtual sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>& variant) = 0;
virtual sdbus::Variant processVariant(const sdbus::Variant& variant) = 0;
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) = 0;
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() = 0;
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1) = 0;
@@ -111,7 +106,7 @@ private:
virtual std::string state() = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}} // namespaces
+36 -47
View File
@@ -20,24 +20,20 @@ public:
protected:
integrationtests_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
proxy_->uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
proxy_->uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& aMap){ this->onSignalWithMap(aMap); });
proxy_->uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& aVariant){ this->onSignalWithVariant(aVariant); });
}
integrationtests_proxy(const integrationtests_proxy&) = delete;
integrationtests_proxy& operator=(const integrationtests_proxy&) = delete;
integrationtests_proxy(integrationtests_proxy&&) = delete;
integrationtests_proxy& operator=(integrationtests_proxy&&) = delete;
integrationtests_proxy(integrationtests_proxy&&) = default;
integrationtests_proxy& operator=(integrationtests_proxy&&) = default;
~integrationtests_proxy() = default;
void registerProxy()
{
simpleSignalSlot_ = m_proxy.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }, sdbus::return_slot);
m_proxy.uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& aMap){ this->onSignalWithMap(aMap); });
m_proxy.uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& aVariant){ this->onSignalWithVariant(aVariant); });
}
virtual void onSimpleSignal() = 0;
virtual void onSignalWithMap(const std::map<int32_t, std::string>& aMap) = 0;
virtual void onSignalWithVariant(const sdbus::Variant& aVariant) = 0;
@@ -45,185 +41,178 @@ protected:
public:
void noArgNoReturn()
{
m_proxy.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
proxy_->callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
}
int32_t getInt()
{
int32_t result;
m_proxy.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::tuple<uint32_t, std::string> getTuple()
{
std::tuple<uint32_t, std::string> result;
m_proxy.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
double multiply(const int64_t& a, const double& b)
{
double result;
m_proxy.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
proxy_->callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
return result;
}
void multiplyWithNoReply(const int64_t& a, const double& b)
{
m_proxy.callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
proxy_->callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
}
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& arg0)
{
std::vector<int16_t> result;
m_proxy.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
proxy_->callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
sdbus::Variant processVariant(const sdbus::Variant& variant)
{
sdbus::Variant result;
m_proxy.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
return result;
}
std::variant<int32_t, double, std::string> processVariant(const std::variant<int32_t, double, std::string>& variant)
{
std::variant<int32_t, double, std::string> result;
m_proxy.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
proxy_->callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(result);
return result;
}
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
{
std::map<int32_t, sdbus::Variant> result;
m_proxy.callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
proxy_->callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
return result;
}
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct()
{
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> result;
m_proxy.callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& arg0, const sdbus::Struct<int32_t, int64_t>& arg1)
{
int32_t result;
m_proxy.callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
proxy_->callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
return result;
}
uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1)
{
uint32_t result;
m_proxy.callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
proxy_->callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
return result;
}
uint32_t doOperation(const uint32_t& arg0)
{
uint32_t result;
m_proxy.callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
proxy_->callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
uint32_t doOperationAsync(const uint32_t& arg0)
{
uint32_t result;
m_proxy.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
proxy_->callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
sdbus::Signature getSignature()
{
sdbus::Signature result;
m_proxy.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::ObjectPath getObjPath()
{
sdbus::ObjectPath result;
m_proxy.callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::UnixFd getUnixFd()
{
sdbus::UnixFd result;
m_proxy.callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex()
{
std::map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::unordered_map<int32_t, std::string>>>>, sdbus::Signature, std::string>> result;
m_proxy.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void throwError()
{
m_proxy.callMethod("throwError").onInterface(INTERFACE_NAME);
proxy_->callMethod("throwError").onInterface(INTERFACE_NAME);
}
void throwErrorWithNoReply()
{
m_proxy.callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
proxy_->callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
}
void doPrivilegedStuff()
{
m_proxy.callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
proxy_->callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
}
void emitTwoSimpleSignals()
{
m_proxy.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
proxy_->callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
}
void unregisterSimpleSignalHandler()
{
simpleSignalSlot_.reset();
proxy_->muteSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void reRegisterSimpleSignalHandler()
{
simpleSignalSlot_ = m_proxy.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }, sdbus::return_slot);
proxy_->uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
proxy_->finishRegistration();
}
public:
uint32_t action()
{
return m_proxy.getProperty("action").onInterface(INTERFACE_NAME).get<uint32_t>();
return proxy_->getProperty("action").onInterface(INTERFACE_NAME);
}
void action(const uint32_t& value)
{
m_proxy.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
proxy_->setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
}
bool blocking()
{
return m_proxy.getProperty("blocking").onInterface(INTERFACE_NAME).get<bool>();
return proxy_->getProperty("blocking").onInterface(INTERFACE_NAME);
}
void blocking(const bool& value)
{
m_proxy.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
proxy_->setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
}
std::string state()
{
return m_proxy.getProperty("state").onInterface(INTERFACE_NAME).get<std::string>();
return proxy_->getProperty("state").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& m_proxy;
sdbus::Slot simpleSignalSlot_;
sdbus::IProxy* proxy_;
};
}} // namespaces
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file sdbus-c++-integration-tests.cpp
*
+5 -6
View File
@@ -1,6 +1,5 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file client.cpp
*
@@ -43,7 +42,7 @@ uint64_t totalDuration = 0;
class PerftestProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::perftests_proxy>
{
public:
PerftestProxy(sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
PerftestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
registerProxy();
@@ -101,9 +100,9 @@ std::string createRandomString(size_t length)
//-----------------------------------------
int main(int /*argc*/, char */*argv*/[])
{
sdbus::ServiceName destination{"org.sdbuscpp.perftests"};
sdbus::ObjectPath objectPath{"/org/sdbuscpp/perftests"};
PerftestProxy client(std::move(destination), std::move(objectPath));
const char* destinationName = "org.sdbuscpp.perftests";
const char* objectPath = "/org/sdbuscpp/perftests";
PerftestProxy client(destinationName, objectPath);
const unsigned int repetitions{20};
unsigned int msgCount = 1000;
+8 -13
View File
@@ -20,29 +20,24 @@ public:
protected:
perftests_adaptor(sdbus::IObject& object)
: m_object(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(const perftests_adaptor&) = delete;
perftests_adaptor& operator=(const perftests_adaptor&) = delete;
perftests_adaptor(perftests_adaptor&&) = delete;
perftests_adaptor& operator=(perftests_adaptor&&) = delete;
perftests_adaptor(perftests_adaptor&&) = default;
perftests_adaptor& operator=(perftests_adaptor&&) = default;
~perftests_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable( sdbus::registerMethod("sendDataSignals").withInputParamNames("numberOfSignals", "signalMsgSize").implementedAs([this](const uint32_t& numberOfSignals, const uint32_t& signalMsgSize){ return this->sendDataSignals(numberOfSignals, signalMsgSize); })
, sdbus::registerMethod("concatenateTwoStrings").withInputParamNames("string1", "string2").withOutputParamNames("result").implementedAs([this](const std::string& string1, const std::string& string2){ return this->concatenateTwoStrings(string1, string2); })
, sdbus::registerSignal("dataSignal").withParameters<std::string>("data")
).forInterface(INTERFACE_NAME);
}
public:
void emitDataSignal(const std::string& data)
{
m_object.emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
object_->emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
}
private:
@@ -50,7 +45,7 @@ private:
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}} // namespaces
+7 -11
View File
@@ -20,39 +20,35 @@ public:
protected:
perftests_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
proxy_->uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); });
}
perftests_proxy(const perftests_proxy&) = delete;
perftests_proxy& operator=(const perftests_proxy&) = delete;
perftests_proxy(perftests_proxy&&) = delete;
perftests_proxy& operator=(perftests_proxy&&) = delete;
perftests_proxy(perftests_proxy&&) = default;
perftests_proxy& operator=(perftests_proxy&&) = default;
~perftests_proxy() = default;
void registerProxy()
{
m_proxy.uponSignal("dataSignal").onInterface(INTERFACE_NAME).call([this](const std::string& data){ this->onDataSignal(data); });
}
virtual void onDataSignal(const std::string& data) = 0;
public:
void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize)
{
m_proxy.callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
proxy_->callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
}
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2)
{
std::string result;
m_proxy.callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
proxy_->callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
}} // namespaces
+5 -6
View File
@@ -1,6 +1,5 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file server.cpp
*
@@ -40,7 +39,7 @@ std::string createRandomString(size_t length);
class PerftestAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::perftests_adaptor>
{
public:
PerftestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
PerftestAdaptor(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
@@ -92,11 +91,11 @@ std::string createRandomString(size_t length)
//-----------------------------------------
int main(int /*argc*/, char */*argv*/[])
{
sdbus::ServiceName serviceName{"org.sdbuscpp.perftests"};
const char* serviceName = "org.sdbuscpp.perftests";
auto connection = sdbus::createSystemBusConnection(serviceName);
sdbus::ObjectPath objectPath{"/org/sdbuscpp/perftests"};
PerftestAdaptor server(*connection, std::move(objectPath));
const char* objectPath = "/org/sdbuscpp/perftests";
PerftestAdaptor server(*connection, objectPath);
connection->enterEventLoop();
}
@@ -22,27 +22,23 @@ public:
protected:
thermometer_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
}
thermometer_adaptor(const thermometer_adaptor&) = delete;
thermometer_adaptor& operator=(const thermometer_adaptor&) = delete;
thermometer_adaptor(thermometer_adaptor&&) = delete;
thermometer_adaptor& operator=(thermometer_adaptor&&) = delete;
thermometer_adaptor(thermometer_adaptor&&) = default;
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
~thermometer_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable(sdbus::registerMethod("getCurrentTemperature").withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); })).forInterface(INTERFACE_NAME);
}
private:
virtual uint32_t getCurrentTemperature() = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}}}} // namespaces
@@ -22,31 +22,27 @@ public:
protected:
thermometer_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
}
thermometer_proxy(const thermometer_proxy&) = delete;
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
thermometer_proxy(thermometer_proxy&&) = delete;
thermometer_proxy& operator=(thermometer_proxy&&) = delete;
thermometer_proxy(thermometer_proxy&&) = default;
thermometer_proxy& operator=(thermometer_proxy&&) = default;
~thermometer_proxy() = default;
void registerProxy()
{
}
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
m_proxy.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
}}}} // namespaces
+7 -12
View File
@@ -21,35 +21,30 @@ public:
protected:
concatenator_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
object_->registerMethod("concatenate").onInterface(INTERFACE_NAME).withInputParamNames("params").withOutputParamNames("result").implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); });
object_->registerSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withParameters<std::string>("concatenatedString");
}
concatenator_adaptor(const concatenator_adaptor&) = delete;
concatenator_adaptor& operator=(const concatenator_adaptor&) = delete;
concatenator_adaptor(concatenator_adaptor&&) = delete;
concatenator_adaptor& operator=(concatenator_adaptor&&) = delete;
concatenator_adaptor(concatenator_adaptor&&) = default;
concatenator_adaptor& operator=(concatenator_adaptor&&) = default;
~concatenator_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable( sdbus::registerMethod("concatenate").withInputParamNames("params").withOutputParamNames("result").implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); })
, sdbus::registerSignal("concatenatedSignal").withParameters<std::string>("concatenatedString")
).forInterface(INTERFACE_NAME);
}
public:
void emitConcatenatedSignal(const std::string& concatenatedString)
{
m_object.emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
object_->emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
}
private:
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}}} // namespaces
+7 -11
View File
@@ -21,34 +21,30 @@ public:
protected:
concatenator_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
proxy_->uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
}
concatenator_proxy(const concatenator_proxy&) = delete;
concatenator_proxy& operator=(const concatenator_proxy&) = delete;
concatenator_proxy(concatenator_proxy&&) = delete;
concatenator_proxy& operator=(concatenator_proxy&&) = delete;
concatenator_proxy(concatenator_proxy&&) = default;
concatenator_proxy& operator=(concatenator_proxy&&) = default;
~concatenator_proxy() = default;
void registerProxy()
{
m_proxy.uponSignal("concatenatedSignal").onInterface(INTERFACE_NAME).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
}
virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0;
virtual void onConcatenateReply(const std::string& result, std::optional<sdbus::Error> error) = 0;
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) = 0;
public:
sdbus::PendingAsyncCall concatenate(const std::map<std::string, sdbus::Variant>& params)
{
return m_proxy.callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](std::optional<sdbus::Error> error, const std::string& result){ this->onConcatenateReply(result, std::move(error)); });
return proxy_->callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
}}} // namespaces
@@ -22,27 +22,23 @@ public:
protected:
thermometer_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
}
thermometer_adaptor(const thermometer_adaptor&) = delete;
thermometer_adaptor& operator=(const thermometer_adaptor&) = delete;
thermometer_adaptor(thermometer_adaptor&&) = delete;
thermometer_adaptor& operator=(thermometer_adaptor&&) = delete;
thermometer_adaptor(thermometer_adaptor&&) = default;
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
~thermometer_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable(sdbus::registerMethod("getCurrentTemperature").withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); })).forInterface(INTERFACE_NAME);
}
private:
virtual uint32_t getCurrentTemperature() = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}}}} // namespaces
@@ -60,30 +56,25 @@ public:
protected:
factory_adaptor(sdbus::IObject& object)
: m_object(object)
: object_(&object)
{
object_->registerMethod("createDelegateObject").onInterface(INTERFACE_NAME).withOutputParamNames("delegate").implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); });
object_->registerMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withInputParamNames("delegate").implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply();
}
factory_adaptor(const factory_adaptor&) = delete;
factory_adaptor& operator=(const factory_adaptor&) = delete;
factory_adaptor(factory_adaptor&&) = delete;
factory_adaptor& operator=(factory_adaptor&&) = delete;
factory_adaptor(factory_adaptor&&) = default;
factory_adaptor& operator=(factory_adaptor&&) = default;
~factory_adaptor() = default;
void registerAdaptor()
{
m_object.addVTable( sdbus::registerMethod("createDelegateObject").withOutputParamNames("delegate").implementedAs([this](sdbus::Result<sdbus::ObjectPath>&& result){ this->createDelegateObject(std::move(result)); })
, sdbus::registerMethod("destroyDelegateObject").withInputParamNames("delegate").implementedAs([this](sdbus::Result<>&& result, sdbus::ObjectPath delegate){ this->destroyDelegateObject(std::move(result), std::move(delegate)); }).withNoReply()
).forInterface(INTERFACE_NAME);
}
private:
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) = 0;
virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0;
private:
sdbus::IObject& m_object;
sdbus::IObject* object_;
};
}}}}} // namespaces
@@ -22,31 +22,27 @@ public:
protected:
thermometer_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
}
thermometer_proxy(const thermometer_proxy&) = delete;
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
thermometer_proxy(thermometer_proxy&&) = delete;
thermometer_proxy& operator=(thermometer_proxy&&) = delete;
thermometer_proxy(thermometer_proxy&&) = default;
thermometer_proxy& operator=(thermometer_proxy&&) = default;
~thermometer_proxy() = default;
void registerProxy()
{
}
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
m_proxy.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
}}}} // namespaces
@@ -64,36 +60,32 @@ public:
protected:
factory_proxy(sdbus::IProxy& proxy)
: m_proxy(proxy)
: proxy_(&proxy)
{
}
factory_proxy(const factory_proxy&) = delete;
factory_proxy& operator=(const factory_proxy&) = delete;
factory_proxy(factory_proxy&&) = delete;
factory_proxy& operator=(factory_proxy&&) = delete;
factory_proxy(factory_proxy&&) = default;
factory_proxy& operator=(factory_proxy&&) = default;
~factory_proxy() = default;
void registerProxy()
{
}
public:
sdbus::ObjectPath createDelegateObject()
{
sdbus::ObjectPath result;
m_proxy.callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
proxy_->callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void destroyDelegateObject(const sdbus::ObjectPath& delegate)
{
m_proxy.callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
proxy_->callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
}
private:
sdbus::IProxy& m_proxy;
sdbus::IProxy* proxy_;
};
}}}}} // namespaces
+18 -18
View File
@@ -1,6 +1,5 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file sdbus-c++-stress-tests.cpp
*
@@ -46,17 +45,18 @@
#include <queue>
using namespace std::chrono_literals;
using namespace std::string_literals;
const sdbus::ServiceName SERVICE_1_BUS_NAME{"org.sdbuscpp.stresstests.service1"};
const sdbus::ServiceName SERVICE_2_BUS_NAME{"org.sdbuscpp.stresstests.service2"};
const sdbus::ObjectPath CELSIUS_THERMOMETER_OBJECT_PATH{"/org/sdbuscpp/stresstests/celsius/thermometer"};
const sdbus::ObjectPath FAHRENHEIT_THERMOMETER_OBJECT_PATH{"/org/sdbuscpp/stresstests/fahrenheit/thermometer"};
const sdbus::ObjectPath CONCATENATOR_OBJECT_PATH{"/org/sdbuscpp/stresstests/concatenator"};
#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 final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>
{
public:
CelsiusThermometerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
CelsiusThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
@@ -80,7 +80,7 @@ private:
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>
{
public:
CelsiusThermometerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
CelsiusThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
@@ -96,7 +96,7 @@ class FahrenheitThermometerAdaptor final : public sdbus::AdaptorInterfaces< org:
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >
{
public:
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath, bool isDelegate)
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath, bool isDelegate)
: AdaptorInterfaces(connection, std::move(objectPath))
, celsiusProxy_(connection, SERVICE_2_BUS_NAME, CELSIUS_THERMOMETER_OBJECT_PATH)
{
@@ -127,7 +127,7 @@ public:
{
// Create new delegate object
auto& connection = getObject().getConnection();
sdbus::ObjectPath newObjectPath{FAHRENHEIT_THERMOMETER_OBJECT_PATH + "/" + std::to_string(request.objectNr)};
sdbus::ObjectPath newObjectPath = FAHRENHEIT_THERMOMETER_OBJECT_PATH + "/" + std::to_string(request.objectNr);
// Here we are testing dynamic creation of a D-Bus object in an async way
auto adaptor = std::make_unique<FahrenheitThermometerAdaptor>(connection, newObjectPath, true);
@@ -175,7 +175,7 @@ protected:
objectCounter++;
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{objectCounter, {}, std::move(result)});
requests_.push(WorkItem{objectCounter, std::string{}, std::move(result)});
lock.unlock();
cond_.notify_one();
}
@@ -190,7 +190,7 @@ protected:
private:
CelsiusThermometerProxy celsiusProxy_;
std::map<sdbus::ObjectPath, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
std::map<std::string, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
std::mutex childrenMutex_;
struct WorkItem
@@ -210,7 +210,7 @@ class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces< org::sdbuscpp:
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >
{
public:
FahrenheitThermometerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
FahrenheitThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
@@ -225,7 +225,7 @@ public:
class ConcatenatorAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::concatenator_adaptor>
{
public:
ConcatenatorAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
unsigned int workers = std::thread::hardware_concurrency();
@@ -297,7 +297,7 @@ private:
class ConcatenatorProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>
{
public:
ConcatenatorProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
ConcatenatorProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
@@ -309,9 +309,9 @@ public:
}
private:
virtual void onConcatenateReply(const std::string& result, [[maybe_unused]] std::optional<sdbus::Error> error) override
virtual void onConcatenateReply(const std::string& result, [[maybe_unused]] const sdbus::Error* error) override
{
assert(error == std::nullopt);
assert(error == nullptr);
std::stringstream str(result);
std::string aString;
+3 -8
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Connection_test.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -26,7 +26,6 @@
*/
#include "Connection.h"
#include "sdbus-c++/Types.h"
#include "unittests/mocks/SdBusMock.h"
#include <gtest/gtest.h>
@@ -208,16 +207,12 @@ TYPED_TEST_SUITE(AConnectionNameRequest, BusTypeTags);
TYPED_TEST(AConnectionNameRequest, DoesNotThrowOnSuccess)
{
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1));
sdbus::ConnectionName name{"org.sdbuscpp.somename"};
this->con_->requestName(name);
this->con_->requestName("org.sdbuscpp.somename");
}
TYPED_TEST(AConnectionNameRequest, ThrowsOnFail)
{
sdbus::ConnectionName name{"org.sdbuscpp.somename"};
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1));
ASSERT_THROW(this->con_->requestName(name), sdbus::Error);
ASSERT_THROW(this->con_->requestName("org.sdbuscpp.somename"), sdbus::Error);
}
+20 -89
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Message_test.cpp
*
@@ -32,10 +32,8 @@
#include <list>
using ::testing::Eq;
using ::testing::StrEq;
using ::testing::Gt;
using ::testing::DoubleEq;
using ::testing::IsNull;
using namespace std::string_literals;
namespace
@@ -53,7 +51,7 @@ namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
{
msg.openContainer<_ElementType>();
msg.openContainer(sdbus::signature_of<_ElementType>::str());
for (const auto& item : items)
msg << item;
@@ -66,7 +64,7 @@ namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
{
if(!msg.enterContainer<_ElementType>())
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
return msg;
while (true)
@@ -121,7 +119,7 @@ namespace my {
template <>
struct sdbus::signature_of<my::Struct>
: sdbus::signature_of<sdbus::Struct<int, std::string, std::list<double>>>
: sdbus::signature_of<sdbus::Struct<int, std::string, std::list<double>>>
{};
/*-------------------------------------*/
@@ -206,21 +204,6 @@ TEST(AMessage, CanCarryASimpleInteger)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryAStringAsAStringView)
{
auto msg = sdbus::createPlainMessage();
const std::string_view dataWritten = "Hello";
msg << dataWritten;
msg.seal();
std::string dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryAUnixFd)
{
auto msg = sdbus::createPlainMessage();
@@ -287,7 +270,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdVector)
{
auto msg = sdbus::createPlainMessage();
const std::vector dataWritten{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
const std::vector<sdbus::Signature> dataWritten{"s", "u", "b"};
msg << dataWritten;
msg.seal();
@@ -317,7 +300,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
{
auto msg = sdbus::createPlainMessage();
const std::array dataWritten{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
const std::array<sdbus::Signature, 3> dataWritten{"s", "u", "b"};
msg << dataWritten;
msg.seal();
@@ -328,7 +311,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdSpan)
{
auto msg = sdbus::createPlainMessage();
@@ -350,7 +333,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan)
{
auto msg = sdbus::createPlainMessage();
const std::array sourceArray{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
const std::array<sdbus::Signature, 3> sourceArray{"s", "u", "b"};
const std::span dataWritten{sourceArray};
msg << dataWritten;
@@ -364,24 +347,6 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan)
}
#endif
TEST(AMessage, CanCarryAnEnumValue)
{
auto msg = sdbus::createPlainMessage();
enum class EnumA : int16_t {X = 5} aWritten{EnumA::X};
enum EnumB {Y = 11} bWritten{EnumB::Y};
msg << aWritten << bWritten;
msg.seal();
EnumA aRead{};
EnumB bRead{};
msg >> aRead >> bRead;
ASSERT_THAT(aRead, Eq(aWritten));
ASSERT_THAT(bRead, Eq(bWritten));
}
TEST(AMessage, ThrowsWhenDestinationStdArrayIsTooSmallDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();
@@ -395,7 +360,7 @@ TEST(AMessage, ThrowsWhenDestinationStdArrayIsTooSmallDuringDeserialization)
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();
@@ -450,7 +415,7 @@ TEST(AMessage, CanCarryAComplexType)
>
>;
ComplexType dataWritten = { {1, {{{5, {{sdbus::ObjectPath{"/some/object"}, true, 45, {{6, "hello"}, {7, "world"}}}}}}, sdbus::Signature{"av"}, 3.14}}};
ComplexType dataWritten = { {1, {{{5, {{"/some/object", true, 45, {{6, "hello"}, {7, "world"}}}}}}, "av", 3.14}}};
msg << dataWritten;
msg.seal();
@@ -467,10 +432,11 @@ TEST(AMessage, CanPeekASimpleType)
msg << 123;
msg.seal();
auto [type, contents] = msg.peekType();
ASSERT_THAT(type, Eq('i'));
ASSERT_THAT(contents, IsNull());
std::string type;
std::string contents;
msg.peekType(type, contents);
ASSERT_THAT(type, "i");
ASSERT_THAT(contents, "");
}
TEST(AMessage, CanPeekContainerContents)
@@ -479,10 +445,11 @@ TEST(AMessage, CanPeekContainerContents)
msg << std::map<int, std::string>{{1, "one"}, {2, "two"}};
msg.seal();
auto [type, contents] = msg.peekType();
ASSERT_THAT(type, Eq('a'));
ASSERT_THAT(contents, StrEq("{is}"));
std::string type;
std::string contents;
msg.peekType(type, contents);
ASSERT_THAT(type, "a");
ASSERT_THAT(contents, "{is}");
}
TEST(AMessage, CanCarryDBusArrayGivenAsCustomType)
@@ -516,39 +483,3 @@ TEST(AMessage, CanCarryDBusStructGivenAsCustomType)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
class AMessage : public ::testing::TestWithParam<std::variant<int32_t, std::string, my::Struct>>
{
};
TEST_P(AMessage, CanCarryDBusVariantGivenAsStdVariant)
{
auto msg = sdbus::createPlainMessage();
const std::variant<int32_t, std::string, my::Struct> dataWritten{GetParam()};
msg << dataWritten;
msg.seal();
std::variant<int32_t, std::string, my::Struct> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST_P(AMessage, ThrowsWhenDestinationStdVariantHasWrongTypeDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();
const std::variant<int32_t, std::string, my::Struct> dataWritten{GetParam()};
msg << dataWritten;
msg.seal();
std::variant<std::vector<bool>> dataRead;
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
INSTANTIATE_TEST_SUITE_P( StringIntStruct
, AMessage
, ::testing::Values("hello"s, 1, my::Struct{}));
-125
View File
@@ -1,125 +0,0 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file PollData_test.cpp
*
* Created on: Jan 19, 2023
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sdbus-c++/IConnection.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <chrono>
using ::testing::Eq;
using ::testing::Ge;
using ::testing::Le;
using ::testing::AllOf;
using namespace std::string_literals;
using namespace std::chrono_literals;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST(PollData, ReturnsZeroRelativeTimeoutForZeroAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
pd.timeout = std::chrono::microseconds::zero();
auto relativeTimeout = pd.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, Eq(std::chrono::microseconds::zero()));
}
TEST(PollData, ReturnsZeroPollTimeoutForZeroAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
pd.timeout = std::chrono::microseconds::zero();
auto pollTimeout = pd.getPollTimeout();
EXPECT_THAT(pollTimeout, Eq(0));
}
TEST(PollData, ReturnsInfiniteRelativeTimeoutForInfiniteAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
pd.timeout = std::chrono::microseconds::max();
auto relativeTimeout = pd.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, Eq(std::chrono::microseconds::max()));
}
TEST(PollData, ReturnsNegativePollTimeoutForInfiniteAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
pd.timeout = std::chrono::microseconds::max();
auto pollTimeout = pd.getPollTimeout();
EXPECT_THAT(pollTimeout, Eq(-1));
}
TEST(PollData, ReturnsZeroRelativeTimeoutForPastAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
auto past = std::chrono::steady_clock::now() - 10s;
pd.timeout = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch());
auto relativeTimeout = pd.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, Eq(0us));
}
TEST(PollData, ReturnsZeroPollTimeoutForPastAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
auto past = std::chrono::steady_clock::now() - 10s;
pd.timeout = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch());
auto pollTimeout = pd.getPollTimeout();
EXPECT_THAT(pollTimeout, Eq(0));
}
TEST(PollData, ReturnsCorrectRelativeTimeoutForFutureAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
auto future = std::chrono::steady_clock::now() + 1s;
pd.timeout = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch());
auto relativeTimeout = pd.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, AllOf(Ge(900ms), Le(1100ms)));
}
TEST(PollData, ReturnsCorrectPollTimeoutForFutureAbsoluteTimeout)
{
sdbus::IConnection::PollData pd;
auto future = std::chrono::steady_clock::now() + 1s;
pd.timeout = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch());
auto pollTimeout = pd.getPollTimeout();
EXPECT_THAT(pollTimeout, AllOf(Ge(900), Le(1100)));
}
+7 -40
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TypeTraits_test.cpp
*
@@ -49,21 +49,6 @@ namespace
static std::string getDBusTypeSignature();
};
enum class SomeEnumClass : uint8_t
{
A, B, C
};
enum struct SomeEnumStruct : int64_t
{
A, B, C
};
enum SomeClassicEnum
{
A, B, C
};
#define TYPE(...) \
template <> \
std::string Type2DBusTypeSignatureConversion<__VA_ARGS__>::getDBusTypeSignature() \
@@ -85,25 +70,17 @@ namespace
TYPE(double)HAS_DBUS_TYPE_SIGNATURE("d")
TYPE(const char*)HAS_DBUS_TYPE_SIGNATURE("s")
TYPE(std::string)HAS_DBUS_TYPE_SIGNATURE("s")
TYPE(std::string_view)HAS_DBUS_TYPE_SIGNATURE("s")
TYPE(sdbus::BusName)HAS_DBUS_TYPE_SIGNATURE("s")
TYPE(sdbus::InterfaceName)HAS_DBUS_TYPE_SIGNATURE("s")
TYPE(sdbus::MemberName)HAS_DBUS_TYPE_SIGNATURE("s")
TYPE(sdbus::ObjectPath)HAS_DBUS_TYPE_SIGNATURE("o")
TYPE(sdbus::Signature)HAS_DBUS_TYPE_SIGNATURE("g")
TYPE(sdbus::Variant)HAS_DBUS_TYPE_SIGNATURE("v")
TYPE(std::variant<int16_t, std::string>)HAS_DBUS_TYPE_SIGNATURE("v")
TYPE(sdbus::UnixFd)HAS_DBUS_TYPE_SIGNATURE("h")
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
TYPE(std::array<int16_t, 3>)HAS_DBUS_TYPE_SIGNATURE("an")
#ifdef __cpp_lib_span
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
#if __cplusplus >= 202002L
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("ao")
#endif
TYPE(SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
TYPE(SomeEnumStruct)HAS_DBUS_TYPE_SIGNATURE("x")
TYPE(SomeClassicEnum)HAS_DBUS_TYPE_SIGNATURE("u")
TYPE(std::map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
TYPE(std::unordered_map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
using ComplexType = std::map<
@@ -123,11 +100,10 @@ namespace
>,
sdbus::Signature,
sdbus::UnixFd,
const char*,
std::string_view
const char*
>
>;
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghss)}")
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghs)}")
typedef ::testing::Types< bool
, uint8_t
@@ -140,25 +116,17 @@ namespace
, double
, const char*
, std::string
, std::string_view
, sdbus::BusName
, sdbus::InterfaceName
, sdbus::MemberName
, sdbus::ObjectPath
, sdbus::Signature
, sdbus::Variant
, std::variant<int16_t, std::string>
, sdbus::UnixFd
, sdbus::Struct<bool>
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
, std::vector<int16_t>
, std::array<int16_t, 3>
#ifdef __cpp_lib_span
#if __cplusplus >= 202002L
, std::span<int16_t>
#endif
, SomeEnumClass
, SomeEnumStruct
, SomeClassicEnum
, std::map<int32_t, int64_t>
, std::unordered_map<int32_t, int64_t>
, ComplexType
@@ -173,8 +141,7 @@ namespace
TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
{
constexpr auto signature = sdbus::as_null_terminated(sdbus::signature_of_v<TypeParam>);
ASSERT_THAT(signature.data(), Eq(this->dbusTypeSignature_));
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
}
TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)
+8 -44
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Types_test.cpp
*
@@ -72,19 +72,6 @@ TEST(AVariant, CanBeConstructedFromAComplexValue)
ASSERT_NO_THROW(sdbus::Variant{value});
}
TEST(AVariant, CanBeConstructedFromAnStdVariant)
{
using ComplexType = std::vector<sdbus::Struct<std::string, double>>;
using StdVariantType = std::variant<std::string, uint64_t, ComplexType>;
ComplexType value{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}};
StdVariantType stdVariant{value};
sdbus::Variant sdbusVariant{stdVariant};
ASSERT_TRUE(sdbusVariant.containsValueOfType<ComplexType>());
ASSERT_THAT(sdbusVariant.get<ComplexType>(), Eq(value));
}
TEST(AVariant, CanBeCopied)
{
auto value = "hello"s;
@@ -143,30 +130,6 @@ TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains)
ASSERT_TRUE(variant.containsValueOfType<ComplexType>());
}
TEST(AVariant, CanBeConvertedIntoAnStdVariant)
{
using ComplexType = std::vector<sdbus::Struct<std::string, double>>;
using StdVariantType = std::variant<std::string, uint64_t, ComplexType>;
ComplexType value{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}};
sdbus::Variant sdbusVariant{value};
StdVariantType stdVariant{sdbusVariant};
ASSERT_TRUE(std::holds_alternative<ComplexType>(stdVariant));
ASSERT_THAT(std::get<ComplexType>(stdVariant), Eq(value));
}
TEST(AVariant, IsImplicitlyInterchangeableWithStdVariant)
{
using ComplexType = std::vector<sdbus::Struct<std::string, double>>;
using StdVariantType = std::variant<std::string, uint64_t, ComplexType>;
ComplexType value{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}};
StdVariantType stdVariant{value};
auto stdVariantCopy = [](const sdbus::Variant &v) -> StdVariantType { return v; }(stdVariant);
ASSERT_THAT(stdVariantCopy, Eq(stdVariant));
}
TEST(ASimpleVariant, ReturnsFalseWhenAskedIfItContainsTypeItDoesntReallyContain)
{
int value = 5;
@@ -322,6 +285,7 @@ TEST(ASignature, CanBeMovedLikeAStdString)
sdbus::Signature oSignature{aSignature};
ASSERT_THAT(sdbus::Signature{std::move(oSignature)}, Eq(sdbus::Signature(std::move(aSignature))));
ASSERT_THAT(std::string(oSignature), Eq(aSignature));
}
TEST(AUnixFd, DuplicatesAndOwnsFdUponStandardConstruction)
@@ -427,17 +391,17 @@ TEST(AUnixFd, TakesOverNewFdAndClosesOriginalFdOnAdoptingReset)
TEST(AnError, CanBeConstructedFromANameAndAMessage)
{
auto error = sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.error"}, "message");
EXPECT_THAT(error.getName(), Eq<std::string>("org.sdbuscpp.error"));
auto error = sdbus::Error("name", "message");
EXPECT_THAT(error.getName(), Eq<std::string>("name"));
EXPECT_THAT(error.getMessage(), Eq<std::string>("message"));
}
TEST(AnError, CanBeConstructedFromANameOnly)
{
auto error1 = sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.error"});
auto error2 = sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.error"}, nullptr);
EXPECT_THAT(error1.getName(), Eq<std::string>("org.sdbuscpp.error"));
EXPECT_THAT(error2.getName(), Eq<std::string>("org.sdbuscpp.error"));
auto error1 = sdbus::Error("name");
auto error2 = sdbus::Error("name", nullptr);
EXPECT_THAT(error1.getName(), Eq<std::string>("name"));
EXPECT_THAT(error2.getName(), Eq<std::string>("name"));
EXPECT_THAT(error1.getMessage(), Eq<std::string>(""));
EXPECT_THAT(error2.getMessage(), Eq<std::string>(""));
+2 -4
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file SdBusMock.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -72,16 +72,14 @@ public:
MOCK_METHOD3(sd_bus_add_object_manager, int(sd_bus *bus, sd_bus_slot **slot, const char *path));
MOCK_METHOD5(sd_bus_add_match, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata));
MOCK_METHOD6(sd_bus_add_match_async, int(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata));
MOCK_METHOD8(sd_bus_match_signal, int(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata));
MOCK_METHOD1(sd_bus_slot_unref, sd_bus_slot*(sd_bus_slot *slot));
MOCK_METHOD1(sd_bus_new, int(sd_bus **ret));
MOCK_METHOD1(sd_bus_start, int(sd_bus *bus));
MOCK_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **r));
MOCK_METHOD1(sd_bus_get_current_message, sd_bus_message*(sd_bus *bus));
MOCK_METHOD2(sd_bus_get_poll_data, int(sd_bus *bus, PollData* data));
MOCK_METHOD2(sd_bus_get_n_queued_read, int(sd_bus *bus, uint64_t *ret));
MOCK_METHOD1(sd_bus_flush, int(sd_bus *bus));
MOCK_METHOD1(sd_bus_flush_close_unref, sd_bus *(sd_bus *bus));
MOCK_METHOD1(sd_bus_close_unref, sd_bus *(sd_bus *bus));
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file sdbus-c++-unit-tests.cpp
*
+18 -25
View File
@@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.5)
project(sdbus-c++-tools VERSION 2.0.0)
project(sdbus-c++-tools VERSION 1.4.0)
include(GNUInstallDirs)
@@ -45,38 +45,31 @@ add_executable(sdbus-c++-xml2cpp ${SDBUSCPP_XML2CPP_SRCS})
target_link_libraries (sdbus-c++-xml2cpp ${EXPAT_LIBRARIES})
target_include_directories(sdbus-c++-xml2cpp PRIVATE ${EXPAT_INCLUDE_DIRS})
#----------------------------------
# INSTALLATION
#----------------------------------
install(TARGETS sdbus-c++-xml2cpp EXPORT sdbus-c++-tools-targets DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT dev)
#----------------------------------
# CMAKE CONFIG & PACKAGE CONFIG
#----------------------------------
include(CMakePackageConfigHelpers)
install(EXPORT sdbus-c++-tools-targets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
NAMESPACE SDBusCpp::
COMPONENT dev)
configure_package_config_file(cmake/sdbus-c++-tools-config.cmake.in cmake/sdbus-c++-tools-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++)
write_basic_package_version_file(cmake/sdbus-c++-tools-config-version.cmake COMPATIBILITY SameMajorVersion)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
COMPONENT dev)
configure_file(pkgconfig/sdbus-c++-tools.pc.in pkgconfig/sdbus-c++-tools.pc @ONLY)
#----------------------------------
# INSTALLATION
#----------------------------------
if(NOT DEFINED SDBUSCPP_INSTALL)
set(SDBUSCPP_INSTALL ON)
endif()
if (SDBUSCPP_INSTALL)
install(TARGETS sdbus-c++-xml2cpp
EXPORT sdbus-c++-tools-targets
DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT sdbus-c++-dev)
install(EXPORT sdbus-c++-tools-targets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
NAMESPACE SDBusCpp::
COMPONENT sdbus-c++-dev)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/cmake/sdbus-c++-tools-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/sdbus-c++-tools
COMPONENT sdbus-c++-dev)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++-tools.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT sdbus-c++-dev)
endif()
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++-tools.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
+44 -92
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorGenerator.cpp
*
@@ -85,17 +85,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
<< tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl
<< tab << className << "(sdbus::IObject& object)" << endl
<< tab << tab << ": m_object(object)" << endl
<< tab << "{" << endl
<< tab << "}" << endl << endl;
// Rule of Five
body << tab << className << "(const " << className << "&) = delete;" << endl;
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
body << tab << className << "(" << className << "&&) = delete;" << endl;
body << tab << className << "& operator=(" << className << "&&) = delete;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
<< tab << tab << ": object_(&object)" << endl;
Nodes methods = interface["method"];
Nodes signals = interface["signal"];
@@ -121,29 +111,34 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
if(!annotationRegistration.empty())
{
std::stringstream str;
str << "sdbus::setInterfaceFlags()" << annotationRegistration << ";";
str << tab << tab << "object_->setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl;
annotationRegistration = str.str();
}
std::vector<std::string> methodRegistrations;
std::string methodDeclaration;
std::tie(methodRegistrations, methodDeclaration) = processMethods(methods);
std::string methodRegistration, methodDeclaration;
std::tie(methodRegistration, methodDeclaration) = processMethods(methods);
std::vector<std::string> signalRegistrations;
std::string signalMethods;
std::tie(signalRegistrations, signalMethods) = processSignals(signals);
std::string signalRegistration, signalMethods;
std::tie(signalRegistration, signalMethods) = processSignals(signals);
std::vector<std::string> propertyRegistrations;
std::string propertyAccessorDeclaration;
std::tie(propertyRegistrations, propertyAccessorDeclaration) = processProperties(properties);
std::string propertyRegistration, propertyAccessorDeclaration;
std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties);
auto vtableRegistration = createVTableRegistration(annotationRegistration, methodRegistrations, signalRegistrations, propertyRegistrations);
body << tab << "void registerAdaptor()" << endl
<< tab << "{" << endl
<< vtableRegistration << endl
body << tab << "{" << endl
<< annotationRegistration
<< methodRegistration
<< signalRegistration
<< propertyRegistration
<< tab << "}" << endl << endl;
// Rule of Five
body << tab << className << "(const " << className << "&) = delete;" << endl;
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
body << tab << className << "(" << className << "&&) = default;" << endl;
body << tab << className << "& operator=(" << className << "&&) = default;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
if (!signalMethods.empty())
{
body << "public:" << endl << signalMethods;
@@ -160,7 +155,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
}
body << "private:" << endl
<< tab << "sdbus::IObject& m_object;" << endl
<< tab << "sdbus::IObject* object_;" << endl
<< "};" << endl << endl
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
@@ -168,16 +163,12 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
}
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
{
std::ostringstream declarationSS;
std::vector<std::string> methodRegistrations;
std::ostringstream registrationSS, declarationSS;
for (const auto& method : methods)
{
std::ostringstream registrationSS;
auto methodName = method->get("name");
auto methodNameSafe = mangle_name(methodName);
@@ -226,8 +217,9 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processMetho
using namespace std::string_literals;
registrationSS << "sdbus::registerMethod(\""
registrationSS << tab << tab << "object_->registerMethod(\""
<< methodName << "\")"
<< ".onInterface(INTERFACE_NAME)"
<< (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
<< (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "")
<< ".implementedAs("
@@ -237,9 +229,7 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processMetho
<< "){ " << (async ? "" : "return ") << "this->" << methodNameSafe << "("
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
<< argStr << "); })"
<< annotationRegistration;
methodRegistrations.push_back(registrationSS.str());
<< annotationRegistration << ";" << endl;
declarationSS << tab
<< "virtual "
@@ -251,20 +241,16 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processMetho
<< ") = 0;" << endl;
}
return std::make_tuple(methodRegistrations, declarationSS.str());
return std::make_tuple(registrationSS.str(), declarationSS.str());
}
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
{
std::ostringstream signalMethodSS;
std::vector<std::string> signalRegistrations;
std::ostringstream signalRegistrationSS, signalMethodSS;
for (const auto& signal : signals)
{
std::stringstream signalRegistrationSS;
auto name = signal->get("name");
auto annotations = getAnnotations(*signal);
@@ -286,7 +272,9 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processSigna
std::string argStr, argTypeStr, typeStr, argStringsStr;
std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
signalRegistrationSS << "sdbus::registerSignal(\"" << name << "\")";
signalRegistrationSS << tab << tab
<< "object_->registerSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)";
if (args.size() > 0)
{
@@ -294,8 +282,7 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processSigna
}
signalRegistrationSS << annotationRegistration;
signalRegistrations.push_back(signalRegistrationSS.str());
signalRegistrationSS << ";" << endl;
auto nameWithCapFirstLetter = name;
nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]);
@@ -303,7 +290,7 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processSigna
signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl
<< tab << "{" << endl
<< tab << tab << "m_object.emitSignal(\"" << name << "\")"
<< tab << tab << "object_->emitSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)";
if (!argStr.empty())
@@ -315,20 +302,16 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processSigna
<< tab << "}" << endl << endl;
}
return std::make_tuple(signalRegistrations, signalMethodSS.str());
return std::make_tuple(signalRegistrationSS.str(), signalMethodSS.str());
}
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
{
std::ostringstream declarationSS;
std::vector<std::string> propertyRegistrations;
std::ostringstream registrationSS, declarationSS;
for (const auto& property : properties)
{
std::ostringstream registrationSS;
auto propertyName = property->get("name");
auto propertyNameSafe = mangle_name(propertyName);
auto propertyAccess = property->get("access");
@@ -356,8 +339,9 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processPrope
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
}
registrationSS << "sdbus::registerProperty(\""
<< propertyName << "\")";
registrationSS << tab << tab << "object_->registerProperty(\""
<< propertyName << "\")"
<< ".onInterface(INTERFACE_NAME)";
if (propertyAccess == "read" || propertyAccess == "readwrite")
{
@@ -372,8 +356,7 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processPrope
}
registrationSS << annotationRegistration;
propertyRegistrations.push_back(registrationSS.str());
registrationSS << ";" << endl;
if (propertyAccess == "read" || propertyAccess == "readwrite")
declarationSS << tab << "virtual " << propertyType << " " << propertyNameSafe << "() = 0;" << endl;
@@ -381,38 +364,7 @@ std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processPrope
declarationSS << tab << "virtual void " << propertyNameSafe << "(" << propertyTypeArg << ") = 0;" << endl;
}
return std::make_tuple(propertyRegistrations, declarationSS.str());
}
std::string AdaptorGenerator::createVTableRegistration(const std::string& annotationRegistration,
const std::vector<std::string>& methodRegistrations,
const std::vector<std::string>& signalRegistrations,
const std::vector<std::string>& propertyRegistrations) const
{
std::vector<std::string> allRegistrations;
if (!annotationRegistration.empty())
allRegistrations.push_back(annotationRegistration);
allRegistrations.insert(allRegistrations.end(), methodRegistrations.begin(), methodRegistrations.end());
allRegistrations.insert(allRegistrations.end(), signalRegistrations.begin(), signalRegistrations.end());
allRegistrations.insert(allRegistrations.end(), propertyRegistrations.begin(), propertyRegistrations.end());
if (allRegistrations.empty())
return {};
std::ostringstream registrationSS;
if (allRegistrations.size() == 1)
{
registrationSS << tab << tab << "m_object.addVTable(" << allRegistrations[0] << ").forInterface(INTERFACE_NAME);";
}
else
{
registrationSS << tab << tab << "m_object.addVTable( " << allRegistrations[0] << endl;
for (size_t i = 1; i < allRegistrations.size(); ++i)
registrationSS << tab << tab << " , " << allRegistrations[i] << endl;
registrationSS << tab << tab << " ).forInterface(INTERFACE_NAME);";
}
return registrationSS.str();
return std::make_tuple(registrationSS.str(), declarationSS.str());
}
std::map<std::string, std::string> AdaptorGenerator::getAnnotations( sdbuscpp::xml::Node& node) const
+4 -9
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorGenerator.h
*
@@ -59,26 +59,21 @@ private:
* @param methods
* @return tuple: registration of methods, declaration of abstract methods
*/
std::tuple<std::vector<std::string>, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
std::tuple<std::string, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
/**
* Generate source code for signals
* @param signals
* @return tuple: registration of signals, definition of signal methods
*/
std::tuple<std::vector<std::string>, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
std::tuple<std::string, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
/**
* Generate source code for properties
* @param properties
* @return tuple: registration of properties, declaration of property accessor virtual methods
*/
std::tuple<std::vector<std::string>, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
std::string createVTableRegistration(const std::string& annotationRegistration,
const std::vector<std::string>& methodRegistrations,
const std::vector<std::string>& signalRegistrations,
const std::vector<std::string>& propertyRegistrations) const;
std::tuple<std::string, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
/**
* Get annotations listed for a given node
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file BaseGenerator.cpp
*
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file BaseGenerator.h
*
+29 -36
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ProxyGenerator.cpp
*
@@ -84,17 +84,7 @@ std::string ProxyGenerator::processInterface(Node& interface) const
<< tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl
<< tab << className << "(sdbus::IProxy& proxy)" << endl
<< tab << tab << ": m_proxy(proxy)" << endl
<< tab << "{" << endl
<< tab << "}" << endl << endl;
// Rule of Five
body << tab << className << "(const " << className << "&) = delete;" << endl;
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
body << tab << className << "(" << className << "&&) = delete;" << endl;
body << tab << className << "& operator=(" << className << "&&) = delete;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
<< tab << tab << ": proxy_(&proxy)" << endl;
Nodes methods = interface["method"];
Nodes signals = interface["signal"];
@@ -103,10 +93,17 @@ std::string ProxyGenerator::processInterface(Node& interface) const
std::string registration, declaration;
std::tie(registration, declaration) = processSignals(signals);
body << tab << "void registerProxy()" << endl
<< tab << "{" << endl
<< registration
<< tab << "}" << endl << endl;
body << tab << "{" << endl
<< registration
<< tab << "}" << endl << endl;
// Rule of Five
body << tab << className << "(const " << className << "&) = delete;" << endl;
body << tab << className << "& operator=(const " << className << "&) = delete;" << endl;
body << tab << className << "(" << className << "&&) = default;" << endl;
body << tab << className << "& operator=(" << className << "&&) = default;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
if (!declaration.empty())
body << declaration << endl;
@@ -136,7 +133,7 @@ std::string ProxyGenerator::processInterface(Node& interface) const
}
body << "private:" << endl
<< tab << "sdbus::IProxy& m_proxy;" << endl
<< tab << "sdbus::IProxy* proxy_;" << endl
<< "};" << endl << endl
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
@@ -226,7 +223,7 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
}
definitionSS << tab << tab << (async && !dontExpectReply ? "return " : "")
<< "m_proxy.callMethod" << (async ? "Async" : "") << "(\"" << name << "\").onInterface(INTERFACE_NAME)";
<< "proxy_->callMethod" << (async ? "Async" : "") << "(\"" << name << "\").onInterface(INTERFACE_NAME)";
if (!timeoutValue.empty())
{
@@ -251,11 +248,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processMethods(const Nodes&
}
else // Async methods implemented through callbacks
{
definitionSS << ".uponReplyInvoke([this](std::optional<sdbus::Error> error" << (outArgTypeStr.empty() ? "" : ", ") << outArgTypeStr << ")"
"{ this->on" << nameBigFirst << "Reply(" << outArgStr << (outArgStr.empty() ? "" : ", ") << "std::move(error)); })";
definitionSS << ".uponReplyInvoke([this](const sdbus::Error* error" << (outArgTypeStr.empty() ? "" : ", ") << outArgTypeStr << ")"
"{ this->on" << nameBigFirst << "Reply(" << outArgStr << (outArgStr.empty() ? "" : ", ") << "error); })";
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "Reply("
<< outArgTypeStr << (outArgTypeStr.empty() ? "" : ", ") << "std::optional<sdbus::Error> error) = 0;" << endl;
<< outArgTypeStr << (outArgTypeStr.empty() ? "" : ", ") << "const sdbus::Error* error) = 0;" << endl;
}
}
else if (outArgs.size() > 0)
@@ -289,8 +286,8 @@ std::tuple<std::string, std::string> ProxyGenerator::processSignals(const Nodes&
std::string argStr, argTypeStr;
std::tie(argStr, argTypeStr, std::ignore, std::ignore) = argsToNamesAndTypes(args);
registrationSS << tab << tab << "m_proxy"
".uponSignal(\"" << name << "\")"
registrationSS << tab << tab << "proxy_"
"->uponSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)"
".call([this](" << argTypeStr << ")"
"{ this->on" << nameBigFirst << "(" << argStr << "); });" << endl;
@@ -346,13 +343,9 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
propertySS << tab << realRetType << " " << propertyNameSafe << "()" << endl
<< tab << "{" << endl;
propertySS << tab << tab << "return m_proxy.getProperty" << (asyncGet ? "Async" : "") << "(\"" << propertyName << "\")"
propertySS << tab << tab << "return proxy_->getProperty" << (asyncGet ? "Async" : "") << "(\"" << propertyName << "\")"
".onInterface(INTERFACE_NAME)";
if (!asyncGet)
{
propertySS << ".get<" << realRetType << ">()";
}
else
if (asyncGet)
{
auto nameBigFirst = propertyName;
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
@@ -363,11 +356,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
}
else // Async methods implemented through callbacks
{
propertySS << ".uponReplyInvoke([this](std::optional<sdbus::Error> error, const sdbus::Variant& value)"
"{ this->on" << nameBigFirst << "PropertyGetReply(value.get<" << propertyType << ">(), std::move(error)); })";
propertySS << ".uponReplyInvoke([this](const sdbus::Error* error, const sdbus::Variant& value)"
"{ this->on" << nameBigFirst << "PropertyGetReply(value.get<" << propertyType << ">(), error); })";
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "PropertyGetReply("
<< "const " << propertyType << "& value, std::optional<sdbus::Error> error) = 0;" << endl;
<< "const " << propertyType << "& value, const sdbus::Error* error) = 0;" << endl;
}
}
propertySS << ";" << endl << tab << "}" << endl << endl;
@@ -379,7 +372,7 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
propertySS << tab << realRetType << " " << propertyNameSafe << "(" << propertyTypeArg << ")" << endl
<< tab << "{" << endl;
propertySS << tab << tab << (asyncSet ? "return " : "") << "m_proxy.setProperty" << (asyncSet ? "Async" : "")
propertySS << tab << tab << (asyncSet ? "return " : "") << "proxy_->setProperty" << (asyncSet ? "Async" : "")
<< "(\"" << propertyName << "\")"
".onInterface(INTERFACE_NAME)"
".toValue(" << propertyArg << ")";
@@ -395,11 +388,11 @@ std::tuple<std::string, std::string> ProxyGenerator::processProperties(const Nod
}
else // Async methods implemented through callbacks
{
propertySS << ".uponReplyInvoke([this](std::optional<sdbus::Error> error)"
"{ this->on" << nameBigFirst << "PropertySetReply(std::move(error)); })";
propertySS << ".uponReplyInvoke([this](const sdbus::Error* error)"
"{ this->on" << nameBigFirst << "PropertySetReply(error); })";
asyncDeclarationSS << tab << "virtual void on" << nameBigFirst << "PropertySetReply("
<< "std::optional<sdbus::Error> error) = 0;" << endl;
<< "const sdbus::Error* error) = 0;" << endl;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ProxyGenerator.h
*
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2024 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file xml2cpp.cpp
*