forked from Kistler-Group/sdbus-cpp
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28b78822cf | ||
|
|
70750e4557 | ||
|
|
2f3a10a73c | ||
|
|
071c595717 | ||
|
|
91f0d3fca7 | ||
|
|
126ac9ffbe | ||
|
|
7fbfcec455 | ||
|
|
0430ae0ad9 | ||
|
|
6212b12159 | ||
|
|
4389ea39bf | ||
|
|
48ea775531 | ||
|
|
fafe8487ff |
+14
-29
@@ -14,15 +14,11 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04]
|
||||
os: [ubuntu-22.04, ubuntu-24.04]
|
||||
compiler: [g++, clang]
|
||||
build: [shared-libsystemd, embedded-static-libsystemd]
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
compiler: gcc
|
||||
build: shared-libsystemd
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: install-libsystemd-toolchain
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
@@ -36,7 +32,7 @@ jobs:
|
||||
- name: install-clang
|
||||
if: matrix.compiler == 'clang'
|
||||
run: |
|
||||
sudo apt-get install -y clang
|
||||
sudo apt-get install -y clang libc++-dev
|
||||
sudo update-alternatives --remove-all cc
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
|
||||
sudo update-alternatives --remove-all c++
|
||||
@@ -45,31 +41,20 @@ jobs:
|
||||
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: configure-debug-gcc11 # For gcc 11, turn off the annoying deprecated-copy warning
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.compiler == 'g++' && matrix.os == 'ubuntu-22.04'
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Wno-deprecated-copy -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: configure-debug
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04'
|
||||
if: matrix.build == 'shared-libsystemd' && (matrix.compiler != 'g++' || matrix.os != 'ubuntu-22.04')
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0 ..
|
||||
- name: configure-debug-no-tests
|
||||
if: 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=OFF -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON ..
|
||||
- name: configure-release-with-embedded-libsystemd
|
||||
if: matrix.build == 'embedded-static-libsystemd'
|
||||
run: |
|
||||
@@ -86,13 +71,13 @@ jobs:
|
||||
sudo cmake --build . --target install
|
||||
ctest --output-on-failure
|
||||
- name: pack
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
|
||||
if: matrix.build == 'shared-libsystemd'
|
||||
run: |
|
||||
cd build
|
||||
cpack -G DEB
|
||||
- name: 'Upload Artifact'
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-22.04' && matrix.compiler == 'g++'
|
||||
uses: actions/upload-artifact@v3
|
||||
if: matrix.build == 'shared-libsystemd' && matrix.compiler == 'g++'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "debian-packages-${{ matrix.os }}-${{ matrix.compiler }}"
|
||||
path: |
|
||||
@@ -103,7 +88,7 @@ jobs:
|
||||
name: build (freebsd, clang/libc++, basu)
|
||||
runs-on: ubuntu-22.04 # until https://github.com/actions/runner/issues/385
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Test in FreeBSD VM
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
|
||||
+6
-4
@@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(sdbus-c++ VERSION 2.1.0 LANGUAGES CXX C)
|
||||
project(sdbus-c++ VERSION 2.2.1 LANGUAGES CXX C)
|
||||
|
||||
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
|
||||
|
||||
@@ -318,12 +318,14 @@ set(CPACK_PACKAGE_VENDOR "Kistler")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "High-level C++ D-Bus library")
|
||||
set(CPACK_PACKAGE_CONTACT "info@kistler.com")
|
||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||
set(CPACK_COMPONENTS_ALL runtime dev doc)
|
||||
set(CPACK_COMPONENT_DEV_DEPENDS "runtime")
|
||||
set(CPACK_COMPONENTS_ALL sdbus-c++-runtime sdbus-c++-dev sdbus-c++-doc)
|
||||
set(CPACK_COMPONENT_DEV_DEPENDS "sdbus-c++-runtime")
|
||||
# specific for DEB generator
|
||||
set(CPACK_DEB_COMPONENT_INSTALL ON)
|
||||
set(CPACK_DEBIAN_RUNTIME_DEBUGINFO_PACKAGE ON)
|
||||
set(CPACK_DEBIAN_RUNTIME_PACKAGE_NAME ${PROJECT_NAME})
|
||||
set(CPACK_DEBIAN_SDBUS-C++-RUNTIME_PACKAGE_NAME ${PROJECT_NAME})
|
||||
set(CPACK_DEBIAN_SDBUS-C++-DEV_PACKAGE_NAME "sdbus-c++-dev")
|
||||
set(CPACK_DEBIAN_SDBUS-C++-DOC_PACKAGE_NAME "sdbus-c++-doc")
|
||||
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
|
||||
@@ -301,3 +301,17 @@ v2.1.0
|
||||
- Change googletest to default version 1.14.0
|
||||
- Add version parameter to the xml2cpp codegen tool
|
||||
- A few other internal refactorings and improvements
|
||||
|
||||
v2.2.0
|
||||
- Add createLightWeightProxy() overload for convenience
|
||||
- Add cookie-related API to Message class
|
||||
- Add async overloads of GetManagedObjects() function
|
||||
- Add signature_of specialization for r-value references
|
||||
- Create a valid sdbus::Error even when errno is invalid or zero
|
||||
- Fix the return type of some slot-returning functions
|
||||
- Fix annotation generation in xml2cpp tool
|
||||
- A few other, mainly CI related updates
|
||||
|
||||
v2.2.1
|
||||
- Fix component names in CPack configuration
|
||||
- Generate correct, expected DEB package names
|
||||
|
||||
@@ -466,18 +466,18 @@ On the **client** side we likewise need a connection -- just that unlike on the
|
||||
|
||||
* 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).
|
||||
|
||||
* 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 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:
|
||||
|
||||
* 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,
|
||||
|
||||
* 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 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:
|
||||
* Or we don't care about connections 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.
|
||||
|
||||
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.
|
||||
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 (or with `createLightWeightProxy()`). 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
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
Error(Name name, std::string message)
|
||||
: std::runtime_error("[" + name + "] " + message)
|
||||
: std::runtime_error(!message.empty() ? "[" + name + "] " + message : "[" + name + "]")
|
||||
, name_(std::move(name))
|
||||
, message_(std::move(message))
|
||||
{
|
||||
@@ -90,7 +90,7 @@ namespace sdbus {
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
Error createError(int errNo, std::string customMsg);
|
||||
Error createError(int errNo, std::string customMsg = {});
|
||||
|
||||
inline const Error::Name SDBUSCPP_ERROR_NAME{"org.sdbuscpp.Error"};
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ namespace sdbus {
|
||||
* 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`
|
||||
* its own bus connection. So-called light-weight proxies (ones running without an event loop thread)
|
||||
* tag are designed for exactly that purpose.
|
||||
*
|
||||
* The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
@@ -416,7 +416,7 @@ namespace sdbus {
|
||||
* 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`
|
||||
* its own bus connection. So-called light-weight proxies (ones running without an event loop thread)
|
||||
* tag are designed for exactly that purpose.
|
||||
*
|
||||
* If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout().
|
||||
@@ -889,7 +889,7 @@ namespace sdbus {
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides the remote D-Bus object
|
||||
@@ -916,6 +916,15 @@ namespace sdbus {
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* Does the same thing as createProxy(std::unique_ptr<sdbus::IConnection>&&, ServiceName, ObjectPath, dont_run_event_loop_thread_t);
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createLightWeightProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
*
|
||||
@@ -937,7 +946,7 @@ namespace sdbus {
|
||||
, ObjectPath objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates a proxy object for a specific remote D-Bus object
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* @param[in] destination Bus name that provides the remote D-Bus object
|
||||
* @param[in] objectPath Path of the remote D-Bus object
|
||||
@@ -958,6 +967,13 @@ namespace sdbus {
|
||||
, ObjectPath objectPath
|
||||
, dont_run_event_loop_thread_t );
|
||||
|
||||
/*!
|
||||
* @brief Creates a light-weight proxy object for a specific remote D-Bus object
|
||||
*
|
||||
* Does the same thing as createProxy(ServiceName, ObjectPath, dont_run_event_loop_thread_t);
|
||||
*/
|
||||
[[nodiscard]] std::unique_ptr<sdbus::IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath);
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
|
||||
@@ -215,6 +215,7 @@ namespace sdbus {
|
||||
const char* getSender() const;
|
||||
const char* getPath() const;
|
||||
const char* getDestination() const;
|
||||
uint64_t getCookie() const;
|
||||
// TODO: short docs in whole Message API
|
||||
std::pair<char, const char*> peekType() const;
|
||||
bool isValid() const;
|
||||
@@ -296,6 +297,7 @@ namespace sdbus {
|
||||
public:
|
||||
MethodReply() = default;
|
||||
void send() const;
|
||||
uint64_t getReplyCookie() const;
|
||||
};
|
||||
|
||||
class Signal : public Message
|
||||
|
||||
@@ -168,6 +168,11 @@ namespace sdbus {
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<sdbus::Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t)
|
||||
{
|
||||
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAsync(std::string_view interfaceName, std::string_view propertyName, _Function&& callback)
|
||||
{
|
||||
@@ -180,11 +185,6 @@ namespace sdbus {
|
||||
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();
|
||||
@@ -217,11 +217,16 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const sdbus::Variant& value, with_future_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback)
|
||||
{
|
||||
@@ -229,16 +234,11 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot SetAsync(std::string_view interfaceName, std::string_view propertyName, const sdbus::Variant& value, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<void> SetAsync(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();
|
||||
@@ -261,11 +261,16 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot GetAllAsync(const InterfaceName& interfaceName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(const InterfaceName& interfaceName, with_future_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback)
|
||||
{
|
||||
@@ -273,16 +278,11 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetAllAsync(std::string_view interfaceName, _Function&& callback, return_slot_t)
|
||||
[[nodiscard]] Slot GetAllAsync(std::string_view interfaceName, _Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<PropertyName, sdbus::Variant>> GetAllAsync(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();
|
||||
@@ -344,6 +344,23 @@ namespace sdbus {
|
||||
return objectsInterfacesAndProperties;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
PendingAsyncCall GetManagedObjectsAsync(_Function&& callback)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
[[nodiscard]] Slot GetManagedObjectsAsync(_Function&& callback, return_slot_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<_Function>(callback), return_slot);
|
||||
}
|
||||
|
||||
std::future<std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>>> GetManagedObjectsAsync(with_future_t)
|
||||
{
|
||||
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsFuture<std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>>>();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IProxy& m_proxy;
|
||||
};
|
||||
|
||||
@@ -151,6 +151,10 @@ namespace sdbus {
|
||||
struct signature_of<_T&> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <typename _T>
|
||||
struct signature_of<_T&&> : signature_of<_T>
|
||||
{};
|
||||
|
||||
template <>
|
||||
struct signature_of<void>
|
||||
{
|
||||
|
||||
+12
-5
@@ -32,17 +32,24 @@
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
sdbus::Error createError(int errNo, std::string customMsg)
|
||||
Error createError(int errNo, std::string customMsg)
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
sd_bus_error_set_errno(&sdbusError, errNo);
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
|
||||
Error::Name name(sdbusError.name);
|
||||
Error::Name name(sd_bus_error_is_set(&sdbusError) ? sdbusError.name : "");
|
||||
std::string message(std::move(customMsg));
|
||||
message.append(" (");
|
||||
message.append(sdbusError.message);
|
||||
message.append(")");
|
||||
if (!message.empty() && sdbusError.message != nullptr)
|
||||
{
|
||||
message.append(" (");
|
||||
message.append(sdbusError.message);
|
||||
message.append(")");
|
||||
}
|
||||
else if (sdbusError.message != nullptr)
|
||||
{
|
||||
message = sdbusError.message;
|
||||
}
|
||||
|
||||
return Error(std::move(name), std::move(message));
|
||||
}
|
||||
|
||||
@@ -640,6 +640,14 @@ const char* Message::getDestination() const
|
||||
return sd_bus_message_get_destination((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
uint64_t Message::getCookie() const
|
||||
{
|
||||
uint64_t cookie;
|
||||
auto r = sd_bus_message_get_cookie((sd_bus_message*)msg_, &cookie);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
std::pair<char, const char*> Message::peekType() const
|
||||
{
|
||||
char typeSignature{};
|
||||
@@ -841,6 +849,14 @@ void MethodReply::send() const
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
uint64_t MethodReply::getReplyCookie() const
|
||||
{
|
||||
uint64_t cookie;
|
||||
auto r = sd_bus_message_get_reply_cookie((sd_bus_message*)msg_, &cookie);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
|
||||
return cookie;
|
||||
}
|
||||
|
||||
void Signal::send() const
|
||||
{
|
||||
connection_->sendMessage((sd_bus_message*)msg_);
|
||||
|
||||
@@ -400,6 +400,13 @@ std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& conne
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createLightWeightProxy( std::unique_ptr<IConnection>&& connection
|
||||
, ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
return createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
|
||||
, ObjectPath objectPath )
|
||||
{
|
||||
@@ -428,4 +435,9 @@ std::unique_ptr<sdbus::IProxy> createProxy( ServiceName destination
|
||||
, dont_run_event_loop_thread );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath)
|
||||
{
|
||||
return createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -286,6 +286,14 @@ TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHand
|
||||
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperation"));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, ProvidesSerialInMethodCallAndMethodReplyMessage)
|
||||
{
|
||||
auto reply = this->m_proxy->doOperationOnBasicAPILevel(ANY_UNSIGNED_NUMBER);
|
||||
|
||||
ASSERT_THAT(this->m_proxy->m_methodCallMsg->getCookie(), Gt(0));
|
||||
ASSERT_THAT(reply.getReplyCookie(), Eq(this->m_proxy->m_methodCallMsg->getCookie())); // Pairing method reply with method call message
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
|
||||
{
|
||||
this->m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
|
||||
@@ -351,7 +359,7 @@ TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime)
|
||||
, 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);
|
||||
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
int result{};
|
||||
proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2).storeResultsTo(result);
|
||||
|
||||
@@ -370,6 +378,6 @@ TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime)
|
||||
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);
|
||||
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
|
||||
ASSERT_THROW(proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2), sdbus::Error);
|
||||
}
|
||||
|
||||
@@ -127,6 +127,26 @@ TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
|
||||
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, CancelsAsynchronousPropertySettingViaPropertiesInterface)
|
||||
{
|
||||
uint32_t newActionValue = 2346;
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
|
||||
{
|
||||
auto slot = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
|
||||
{
|
||||
if (!err)
|
||||
promise.set_value();
|
||||
else
|
||||
promise.set_exception(std::make_exception_ptr(*std::move(err)));
|
||||
}, sdbus::return_slot);
|
||||
// Now the slot is destroyed, cancelling the async call
|
||||
}
|
||||
|
||||
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
|
||||
{
|
||||
uint32_t newActionValue = 2347;
|
||||
@@ -244,6 +264,43 @@ TYPED_TEST(SdbusTestObject, GetsManagedObjectsSuccessfully)
|
||||
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronously)
|
||||
{
|
||||
std::promise<size_t> promise;
|
||||
auto future = promise.get_future();
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
this->m_objectManagerProxy->GetManagedObjectsAsync([&](std::optional<sdbus::Error> /*err*/, const std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>>& objectsInterfacesAndProperties)
|
||||
{
|
||||
promise.set_value(objectsInterfacesAndProperties.size());
|
||||
});
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronouslyViaSlotReturningOverload)
|
||||
{
|
||||
std::promise<size_t> promise;
|
||||
auto future = promise.get_future();
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
auto slot = this->m_objectManagerProxy->GetManagedObjectsAsync([&](std::optional<sdbus::Error> /*err*/, const std::map<sdbus::ObjectPath, std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>>& objectsInterfacesAndProperties)
|
||||
{
|
||||
promise.set_value(objectsInterfacesAndProperties.size());
|
||||
}, sdbus::return_slot);
|
||||
|
||||
ASSERT_THAT(future.get(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronouslyViaFutureOverload)
|
||||
{
|
||||
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
|
||||
|
||||
auto future = this->m_objectManagerProxy->GetManagedObjectsAsync(sdbus::with_future);
|
||||
|
||||
ASSERT_THAT(future.get().size(), Eq(2));
|
||||
}
|
||||
|
||||
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
|
||||
{
|
||||
std::atomic<bool> signalReceived{false};
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
|
||||
namespace sdbus { namespace test {
|
||||
|
||||
inline const uint32_t ANY_UNSIGNED_NUMBER{123};
|
||||
|
||||
class BaseTestFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -113,6 +113,16 @@ uint32_t TestProxy::doOperationWithTimeout(const std::chrono::microseconds &time
|
||||
return result;
|
||||
}
|
||||
|
||||
MethodReply TestProxy::doOperationOnBasicAPILevel(uint32_t param)
|
||||
{
|
||||
auto methodCall = getProxy().createMethodCall(test::INTERFACE_NAME, MethodName{"doOperation"});
|
||||
methodCall << param;
|
||||
|
||||
m_methodCallMsg = std::make_unique<MethodCall>(methodCall);
|
||||
|
||||
return getProxy().callMethod(methodCall);
|
||||
}
|
||||
|
||||
sdbus::PendingAsyncCall TestProxy::doOperationClientSideAsync(uint32_t param)
|
||||
{
|
||||
return getProxy().callMethodAsync("doOperation")
|
||||
|
||||
@@ -95,6 +95,7 @@ protected:
|
||||
public:
|
||||
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler);
|
||||
uint32_t doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
|
||||
MethodReply doOperationOnBasicAPILevel(uint32_t param);
|
||||
sdbus::PendingAsyncCall doOperationClientSideAsync(uint32_t param);
|
||||
[[nodiscard]] sdbus::Slot doOperationClientSideAsync(uint32_t param, sdbus::return_slot_t);
|
||||
std::future<uint32_t> doOperationClientSideAsync(uint32_t param, with_future_t);
|
||||
@@ -121,6 +122,7 @@ public: // for tests
|
||||
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::unique_ptr<const MethodCall> m_methodCallMsg;
|
||||
std::unique_ptr<const Message> m_signalMsg;
|
||||
SignalName m_signalName;
|
||||
};
|
||||
|
||||
@@ -97,6 +97,29 @@ TEST(AVariant, CanBeCopied)
|
||||
ASSERT_THAT(variantCopy2.get<std::string>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeMoved)
|
||||
{
|
||||
auto value = "hello"s;
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
auto movedVariant{std::move(variant)};
|
||||
|
||||
ASSERT_THAT(movedVariant.get<std::string>(), Eq(value));
|
||||
ASSERT_TRUE(variant.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeMovedIntoAMap)
|
||||
{
|
||||
auto value = "hello"s;
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
std::map<std::string, sdbus::Variant> mymap;
|
||||
mymap.try_emplace("payload", std::move(variant));
|
||||
|
||||
ASSERT_THAT(mymap["payload"].get<std::string>(), Eq(value));
|
||||
ASSERT_TRUE(variant.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AVariant, IsNotEmptyWhenContainsAValue)
|
||||
{
|
||||
sdbus::Variant v("hello");
|
||||
@@ -430,6 +453,7 @@ TEST(AnError, CanBeConstructedFromANameAndAMessage)
|
||||
auto error = sdbus::Error(sdbus::Error::Name{"org.sdbuscpp.error"}, "message");
|
||||
EXPECT_THAT(error.getName(), Eq<std::string>("org.sdbuscpp.error"));
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("message"));
|
||||
EXPECT_TRUE(error.isValid());
|
||||
}
|
||||
|
||||
TEST(AnError, CanBeConstructedFromANameOnly)
|
||||
@@ -439,6 +463,47 @@ TEST(AnError, CanBeConstructedFromANameOnly)
|
||||
EXPECT_THAT(error1.getName(), Eq<std::string>("org.sdbuscpp.error"));
|
||||
EXPECT_THAT(error2.getName(), Eq<std::string>("org.sdbuscpp.error"));
|
||||
|
||||
EXPECT_THAT(error1.getMessage(), Eq<std::string>(""));
|
||||
EXPECT_THAT(error2.getMessage(), Eq<std::string>(""));
|
||||
EXPECT_TRUE(error1.getMessage().empty());
|
||||
EXPECT_TRUE(error2.getMessage().empty());
|
||||
|
||||
EXPECT_TRUE(error1.isValid());
|
||||
EXPECT_TRUE(error2.isValid());
|
||||
}
|
||||
|
||||
TEST(AnError, IsInvalidWhenConstructedWithAnEmptyName)
|
||||
{
|
||||
auto error = sdbus::Error({});
|
||||
|
||||
EXPECT_TRUE(error.getName().empty());
|
||||
EXPECT_TRUE(error.getMessage().empty());
|
||||
EXPECT_FALSE(error.isValid());
|
||||
}
|
||||
|
||||
TEST(AnErrorFactory, CanCreateAnErrorFromErrno)
|
||||
{
|
||||
auto error = sdbus::createError(ENOENT, "custom message");
|
||||
|
||||
EXPECT_THAT(error.getName(), Eq<std::string>("org.freedesktop.DBus.Error.FileNotFound"));
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("custom message (No such file or directory)"));
|
||||
EXPECT_TRUE(error.isValid());
|
||||
}
|
||||
|
||||
#ifndef SDBUS_basu // Creating error from invalid errno is not supported on basu backend
|
||||
TEST(AnErrorFactory, CreatesGenericErrorWhenErrnoIsUnknown)
|
||||
{
|
||||
auto error = sdbus::createError(123456, "custom message");
|
||||
|
||||
EXPECT_THAT(error.getName(), Eq<std::string>("org.freedesktop.DBus.Error.Failed"));
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("custom message (Unknown error 123456)"));
|
||||
EXPECT_TRUE(error.isValid());
|
||||
}
|
||||
#endif // SDBUS_basu
|
||||
|
||||
TEST(AnErrorFactory, CreatesEmptyInvalidErrorWhenErrnoIsZero)
|
||||
{
|
||||
auto error = sdbus::createError(0, "custom message");
|
||||
|
||||
EXPECT_TRUE(error.getName().empty());
|
||||
EXPECT_THAT(error.getMessage(), Eq<std::string>("custom message"));
|
||||
EXPECT_FALSE(error.isValid());
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(sdbus-c++-tools VERSION 2.1.0)
|
||||
project(sdbus-c++-tools VERSION 2.2.1)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
if(!annotationRegistration.empty())
|
||||
{
|
||||
std::stringstream str;
|
||||
str << "sdbus::setInterfaceFlags()" << annotationRegistration << ";";
|
||||
str << "sdbus::setInterfaceFlags()" << annotationRegistration;
|
||||
annotationRegistration = str.str();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user