Compare commits

...
12 Commits
Author SHA1 Message Date
Stanislav Angelovič 28b78822cf chore: release version v2.2.1 2025-11-21 18:14:15 +01:00
AlexeyandStanislav Angelovič 70750e4557 fix: invalid cpack configuration inside CMakeLists.txt (#521)
* Change CPACK_COMPONENTS_ALL variable in CMakeLists.txt according to components names referenced in install function call
* Remove redundant sdbus-c++ from Debian packages

---------

Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2025-11-21 18:08:32 +01:00
Stanislav Angelovič 2f3a10a73c chore: release version v2.2.0 2025-11-19 22:00:11 +01:00
Stanislav Angelovič 071c595717 fix: disable invalid errno test for basu backend (#519) 2025-11-19 21:47:48 +01:00
Stanislav Angelovič 91f0d3fca7 fix: create sdbus::Error correctly when errno is 0 (#517) 2025-11-12 22:43:44 +01:00
SimonBraunschmidtIBAandStanislav Angelovic 126ac9ffbe fix: add signature_of specialization for r-value references (#515)
* test: add Variant move test

* fix: provide signature_of<_T&&>

Provide signature_of specialization for rvalue references resp.
allow when forwarding references resolve to rvalue references

signature_of<_T&&> is needed e.g. for std::map::try_emplace() when the
emplaced sdbus:: type is passed as rvalue reference, otherwise the
static_assert "Unsupported D-Bus type ..." would trigger

See https://github.com/Kistler-Group/sdbus-cpp/issues/513#issuecomment-3429733769
for an elaborate explanation

Signed-off-by: Simon Braunschmidt <simon.braunschmidt@iba-group.com>

---------

Signed-off-by: Simon Braunschmidt <simon.braunschmidt@iba-group.com>
Co-authored-by: Stanislav Angelovic <stanislav.angelovic.ext@siemens.com>
2025-10-23 21:08:25 +02:00
Joel WinarskeandStanislav Angelovič 7fbfcec455 feat: add async overloads of GetManagedObjects() (#480)
This implements three variants of `GetManagedObjectsAsync()` API into the standard `ObjectManager` client interface.

---------

Signed-off-by: Joel Winarske <joel.winarske@gmail.com>
Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2025-05-05 13:31:44 +02:00
Stanislav Angelovič 0430ae0ad9 fix: fix the return type of the slot-returning overloads (#494) 2025-05-05 11:18:00 +02:00
6212b12159 feat: add cookie-related API to Message (#491)
Add `Message::getCookie()` and `MethodReply::getReplyCookie()` functions, based on underlying sd-bus cookie functions. They can be useful when pairing method call messages and method call reply messages.

---------

Co-authored-by: Dylan Howey <dylan.howey@evgo.com>
Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2025-05-05 08:04:20 +02:00
Stanislav Angelovič 4389ea39bf chore(ci): do necessary updates and adjustments (#493)
* Bump to latest versions of upload-artifact and checkout GitHub scripts
* Remove obsolete Ubuntu-20.04 image
* Introduce Ubuntu-24.04 image
* Other related updates and fixes in the CI
2025-05-05 07:51:21 +02:00
Stanislav Angelovič 48ea775531 feat: add createLightWeightProxy overload for convenience (#474) 2025-01-08 13:55:38 +01:00
sgiurgiu fafe8487ff fix(codegen): generate correct annotation code (#473)
Before the patch, generating an adaptor from https://gitlab.freedesktop.org/hadess/mpris-spec/-/blob/master/spec/org.mpris.MediaPlayer2.xml
would produce incorrect code like this:
```
        m_object.addVTable( sdbus::setInterfaceFlags().withPropertyUpdateBehavior(sdbus::Flags::EMITS_CHANGE_SIGNAL);
                          , sdbus::registerMethod("Raise").implementedAs([this](){ return this->Raise(); })
                          , sdbus::registerMethod("Quit").implementedAs([this](){ return this->Quit(); })
...

```

With this patch, the code produced is:
```
        m_object.addVTable( sdbus::setInterfaceFlags().withPropertyUpdateBehavior(sdbus::Flags::EMITS_CHANGE_SIGNAL)
                          , sdbus::registerMethod("Raise").implementedAs([this](){ return this->Raise(); })
                          , sdbus::registerMethod("Quit").implementedAs([this](){ return this->Quit(); })
...

```
2025-01-02 17:48:48 +01:00
20 changed files with 291 additions and 72 deletions
+14 -29
View File
@@ -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
View File
@@ -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)
+14
View File
@@ -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
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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"};
}
+20 -4
View File
@@ -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>
+2
View File
@@ -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
+36 -19
View File
@@ -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;
};
+4
View File
@@ -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
View File
@@ -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));
}
+16
View File
@@ -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_);
+12
View File
@@ -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);
}
}
+10 -2
View File
@@ -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};
+2
View File
@@ -50,6 +50,8 @@
namespace sdbus { namespace test {
inline const uint32_t ANY_UNSIGNED_NUMBER{123};
class BaseTestFixture : public ::testing::Test
{
public:
+10
View File
@@ -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")
+2
View File
@@ -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;
};
+67 -2
View File
@@ -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());
}
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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();
}