Compare commits

..
100 Commits
Author SHA1 Message Date
Oliver FacklamandStanislav Angelovič 0b9b15cbac fix: prevent ambiguous call by calling signal handler with std::nullopt (#541)
Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2026-06-08 20:21:54 +02:00
b5c352700f feat: allow custom direct callbacks in proxy generator (#540)
Co-authored-by: Pavel Pletnev <pletnev_pg@nectech.pro>
Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2026-06-08 13:13:22 +02:00
Stanislav Angelovič ca073434d2 chore: release version v2.3.1 2026-05-20 14:36:18 +02:00
Stanislav Angelovič ca69493b20 fix: move new virtual functions to the end of the class (#539)
This fixes ABI compatibility issue of the recent version.
2026-05-20 14:34:10 +02:00
Stanislav Angelovič fbe77629bc chore: release version v2.3.0 2026-05-17 14:25:54 +02:00
Stanislav Angelovič b042f4e13d ci: fix ctest run (#533)
There was a bug which caused no tests to be run in the CI jobs.
2026-04-20 14:54:54 +02:00
Alex CaniandStanislav Angelovič eb05ed5d18 feat: add coroutine support for client-side async calls (#527)
C++ 20 introduced coroutine support to the language, enabling writing
asynchronous code using the co_await, co_return and co_yield operators.
Similar to other languages, coroutines allow functions to suspend
execution at certain points and later resume from where they left off,
without blocking the calling thread.

Coroutines are a natural fit for any sort of IO-bound asynchronous
operations, such as D-Bus communication.

This PR introduces native coroutine support in sdbus-c++ by the means of
the Awaitable<T> class, a type that implements the C++20 awaitable
protocol, allowing D-Bus method calls to be awaited in coroutines.

Summary of the changes:

Core library:

* New Awaitable<T> type that can be co_awaited, which suspends a running
  coroutine. When the result or error of a method call arrives, the
  coroutine is resumed and the result/error is returned.
  Implementation is done in a thread-safe way, meaning there are no race
  conditions between the awaitable object returned by asynchronous calls
  and the callback invoked by the event loop upon arrival of a message.
  Naturally, it works in the single-threaded scenario as well, where the
  connection event loop may be driven externally and/or integrated in a
  full fledged coroutine runtime.
* Low-level API: new callMethodAsync() overloads accepting
  with_awaitable_t tag and returning Awaitable<MethodReply>.
* High-level API: new getResultAsAwaitable methods in the relevant
  high-level API helpers, covering methods and properties.
  StandardInterfaces.h classes have also been updated to expose
  awaitable-based methods.
* Integration tests for both low-level and high-level APIs.

Codegen:

* Added support for generating awaitable-based async methods in the
  xml2cpp tool through new values for the
  org.freedesktop.DBus.Method.Async.ClientImpl and
  org.freedesktop.DBus.Property.[Get/Set].Async.ClientImpl annotations.

---------

Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2026-04-20 10:41:04 +02:00
DespinaRizkanddespinarizk 7363944751 fix: replace incorrect variable 's' with 'source' in deleteSdEventSource (#528)
Co-authored-by: despinarizk <despina.rizk@inmind.ai>
2026-03-04 23:44:02 +01:00
Stanislav Angelovič c2bb343f8a feat: add support for dumping variant to string (#526)
This allows Message and Variant contents serialization to a string -- based on `sd_bus_message_dump` sd-bus API function.
2026-01-15 23:24:54 +01:00
Stanislav Angelovič 9f3e89eb6a feat: use clang-tidy for static analysis (#495) 2026-01-15 14:38:33 +01:00
Michael NosthoffandStanislav Angelovič 6f694e3fd3 fix: add deduction guides for Struct from std::tuple (#525)
* Types.h: add deduction guides for Struct from std::tuple

C++23 changed the generation of implicit deduction guides. This causes
the compiler to also see deduction guides for std::tuple as candidates
for sdbus::Struct.
Because of the competing guides the compiler doesn't know which one to pick.

This seems to be implemented from gcc 15 on and is thus causing breakage there.

To fix this we need to add explicit deduction guides when std::tuple is passed
to sdbus::Struct.

fixes https://github.com/Kistler-Group/sdbus-cpp/issues/524

* refactor: remove std::decay_t wrapper

We need to be able to create sdbus::Structs
with element types being references.

---------

Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2026-01-15 12:39:22 +01:00
Robert AdamandStanislav Angelovič 44aa4f080f feat(cmake): add export support (#523)
* feat: add export support

This makes sdbus-c++ consumable from the build tree without the need for
an explicit installation. This can be very handy for e.g. testing
purposes. Crucially, downstream projects will depend on upstream to
properly export everything. Otherwise, they can't be built as static
libraries, even if sdbus-c++ is only a private dependency.

See also https://runebook.dev/en/docs/cmake/command/export

* fix(cmake): avoid possibly empty EXPORT_SET

EXPORT_SET is empty when SDBUSCPP_INSTALL is OFF,
which leads to targets file with no targets.

---------

Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2026-01-06 20:11:05 +01:00
Robert Adam 6715ef1fc3 feat: create cmake alias for consistent downstream usage (#522)
At the moment, downstream projects will have to differentiate based off the way the obtained sdbus-c++ in CMake. If they use `find_package`, the target they have to use is `SDBusCpp::sdbus-c++`, whereas the target is merely `sdbus-c++` if they use `FetchContent` (i.e. use without installation).

By creating an alias target, it is now always possible to use the `DBusCpp::sdbus-c++` target regardless of how the library was obtained.
2025-12-31 12:48:26 +01:00
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
Stanislav Angelovič 0261d0ec60 chore: release version v2.1.0 2024-11-20 23:47:56 +01:00
Stanislav Angelovič a1419ee45d fix: add missing c-tor for nesting Variants (#467)
This allows nesting variants, i.e. allows a variant to contain another variant as its value. Until now, there was no such possibility, since the default generated copy constructor would be invoked which would create a copy of source variant instead of embed the source variant as a value in the destination variant. The default generated copy constructor is kept, for it makes sense too, but a new tag-based overload is added for embedding the source variant into the destination variant.
2024-11-20 23:40:30 +01:00
Stanislav Angelovič 02ca7212d1 feat: introduce support for struct-as-dict serialization (#459)
This extends the functionality of SDBUSCPP_REGISTER_STRUCT macro.

It now provides functionality for serializing a user-defined struct as an a{sv} dictionary, and for deserializing an a{sv} dictionary into a user-defined struct. The former one is achieved by decorating the struct with sdbus::as_dictionary(struct), the latter one is an automatic behavior -- when sdbus-c++ is asked to deserialize into a struct but the data in the message is of type a{sv}, then the dict-to-struct deserialization is performed automatically.

There are some aspects of behavior in the serialization/deserialization functionality that can be customized by the client. Newly introduced SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION and SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION macros serve the purpose.
2024-11-20 23:25:13 +01:00
BrianandStanislav Angelovič 8a117f8b42 feat: add version parameter to xml2cpp codegen tool (#463)
* Added -v, --version xml2cpp command line tool options

* Update tools/xml2cpp-codegen/xml2cpp.cpp

Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>

* Updated tools/CMakeLists.txt

---------

Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2024-11-20 19:54:49 +01:00
Karl Ljungkvist 84130b1406 fix(codegen): check fstream status and report in case of failure (#462) 2024-10-31 12:48:19 +01:00
Stanislav Angelovič c55907069b refactor: send larger messages properly (#455)
Until now, the solution to ensure that even large messages are fully sent out has been to flush the connection queues after each sending of a message, which is likely an unnecessary call (with unnecessary cost) in vast majority of cases, and which may block the connection from doing other work until the large message is fully sent out. This was a rather quick, hacky workaround.

Now, after the sending the message we check whether it has been sent out fully or not. If not (outbound queues are non-empty), then we send a wake-up signal to the connection event loop. The event loop thread then fetches new sd-bus timeouts and events and will see that there are pending outbound messages to process, and will process them together with any other prospective pending events, until there is nothing to process (i.e., the outbound message has been fully dispatched).
2024-10-10 20:01:22 +02:00
Stanislav Angelovič 107c6a1a97 refactor: invert dependency between Message and Connection (#457)
This reorganizes the layers of abstraction in the sense how `Message` depends on `Connection` and vice versa. Now, `Message` has a link to the `Connection`. This replaces the shortcut link to the low-level `SdBus` interface that the `Message` kept. The interactions from `Message` now go through `Connection` which forwards them to `SdBus`. `Connection` is now a sole owner of the low-level `SdBus` interface. This allows for future changes around `SdBus` (e.g. a change from virtual functions back to non-virtual functions) without affecting the rest of the library. `Proxy`s and `Object`s can now send messages directly without having to go through `Connection`. The `Connection` no more depends on `Message` business-logic methods; it serves only as a factory for messages.

The flow for creating messages: `Proxy`/`Object` -> `Connection` -> `SdBus`
The flow for sending messages: (`Proxy`/`Object` ->) `Message` -> `Connection` -> `SdBus`

This also better reflects how dependencies are managed in the underlying sd-bus library.

Additionally, `getSdBusInterface()` methods are removed which was anyway planned, and improves the design by "Tell, don't ask" principle.

This refactoring is the necessary enabler for other upcoming improvements (regarding sending long messages, or creds refactoring, for example).
2024-10-02 21:22:02 +02:00
Stanislav Angelovič 1b7acaa735 fix: cleanup for older libsystemd versions (#454)
Fix inconsistencies in documentation and CMake files regarding minimum required libsystemd version (which is v238 for sdbus-c++ v2.0). Also fix compiler error bug in sdbus-c++ code when compiled against older libsystemd versions. Plus some extra related cleanups.
2024-09-15 14:13:49 +02:00
Stanislav Angelovič b7a038f11f refactor: make createPlainMessage public (#448)
Library clients may need to manually create plain sdbus-c++ message to be able to unit test their code.
2024-08-18 10:09:53 +02:00
Stanislav Angelovič c6705faf2f fix: const enum signature_of ambiguity error (#444)
This fixes the template specialization ambiguity error when a signature_of applied upon a const (and/or volatile) enum type would render the const specialization and the enum specialization as two equally-ranked candidates.
2024-05-15 15:46:24 +02:00
Laurence Crestani Tasca e6b87b106c docs: fix examples in using-sdbus-c++.md (#441)
Update in using documentation to version v2.0 syntax making examples work out of the box
2024-05-13 23:51:59 +02:00
Marcel Hellwig 50cc636058 fix: make REGISTER_STRUCT functions inline
This makes that you can use these macros in headers as well as
source files.
Else you will get a warning about multiple definitions of the same
function.
2024-05-13 23:47:53 +02:00
Marcel Hellwig 3d3aa26255 chore: update gtest default version to 1.14.0
this prevents compiler errors on newer compilers
2024-05-13 23:47:53 +02:00
Stanislav Angelovič 14942db075 feat: add macro to teach sdbus-c++ about user-defined structs (#440)
This introduces SDBUSCPP_REGISTER_STRUCT macro that helps clients conveniently, in one line, teach sdbus-c++ to recognize and accept their custom C++ struct types wherever D-Bus structs are expected in a D-Bus API.

The macro saves some boilerplate that would otherwise be needed on client side for every registered struct.
2024-05-06 20:14:32 +02:00
Stanislav Angelovič e62472b210 chore: fix partially renamed BUILD_DOXYGEN_DOC CMake option 2024-04-25 01:23:15 +02:00
Stanislav Angelovič b7d85f936d chore: release version v2.0.0 2024-04-24 21:00:02 +02:00
Stanislav Angelovič 798eaf8626 refactor: remove floating_slot_t tag and use return_slot_t instead (#439)
Make all the API consistent by using return_slot_t-based overloads for returning the slots to clients, and overloads without that tag for "floating" slots. floating_slot_t tag was previously added for API backwards compatibility reasons, but now is a (counter-)duplicate to the return_slot_t.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 2bc9d3ebb3 refactor: object manager API (#438)
This makes the signature of addObjectManager() function overloads consistent with common API design of the library.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 26c6da11be refactor: reorganize public API of main abstract classes (#437) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 0c75ad2b71 docs: fix generated class examples in the tutorial (#436) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 84a6fbbf86 fix: disable move in generated adaptor and proxy classes (#435)
Moving adaptor or proxy instances changes their `this` pointer. But `this` is captured by value in closures used by those instances, and this remains unchanged on move, leading to accessing an invalid instance when a lambda expression executes. Supporting move semantics would require unsubscribing/unregistering vtable, handlers, etc. and re-subscribing and re-registering all that, which is too complicated and may have side effects. Hence it has been decided that these classes are not moveable. One may use an indirection with e.g. `std::unique_ptr` to get move semantics.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 83ece48ab0 feat: add Slot-returning overloads of async method calls (#433) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 310161207a refactor: improve and make more efficient some Message API (#432) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič ef552ec089 chore: update years in header comments 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 7894cda577 perf: provide also const char* overloads for convenience functions 2024-04-24 20:20:29 +02:00
Stanislav Angelovič d41a176c2a refactor: use string_view in Property convenience classes 2024-04-24 20:20:29 +02:00
Stanislav Angelovič f15c260750 feat: add support for string_view as D-Bus type 2024-04-24 20:20:29 +02:00
Stanislav Angelovič d856969051 refactor: enable compile-time D-Bus signature generation (#425)
Since sdbus-c++ knows types of D-Bus call/signal/property input/output parameters at compile time, why not assemble the D-Bus signature strings at compile time, too, instead of assembling them at run time as std::string with likely cost of (unnecessary) heap allocations...

std::array is well supported constexpr type in C++20, so we harness it to build the D-Bus signature string and store it into the binary at compile time.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 32a97f214f fix: make Variant conversion operator explicit (#428)
Having explicit conversion operator is a good practice according to the C++ core guidelines, as it makes the code safer and better follows the principle of least astonishment. Also, it is consistent with the standard library style, where wrappers like std::variant, std::any, std::optional... also do not provide an implicit conversion to the underlying type. Last but not least, it paves the way for the upcoming std::variant <-> sdbus::Variant implicit conversions without surprising behavior in some edge cases.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 42f0bd07c0 refactor: add strong types to public API (#414)
This introduces strong types for `std::string`-based D-Bus types. This facilitates 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.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič fe21ee9656 fix(tests): fix intermittently failing integration tests (#412) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 0dc0c87cc9 chore: use C++20 standard (#410)
As of now sdbus-c++ supports C++20 but does not require it, and the used C++20 features are conditionally compiled depending on whether they are available or not.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 29e94c3b68 fix(cmake): rename forgotten CMake variables 2024-04-24 20:20:29 +02:00
Stanislav Angelovič c433d26176 chore(cmake): implement more flexible searching for sd-bus libs (#409)
A new CMake configuration variable SDBUSCPP_SDBUS_LIB has been introduced that enables clients to specify which sd-bus implementation library shall be used by sdbus-c++.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič abc4d755f7 fix: conditional use and tests of span (#411) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 4c35e98668 fix: pass correctly underlying lib to intg tests 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 87500ad9ad docs: add v2 migration notes to the tutorial (#407) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 9a5616a87a refactor: rename connection creation methods (#406)
This PR makes things around connection factories a little more consistent and more intuitive:

    * createConnection() has been removed. One shall call more expressive createSystemConnection() instead to get a connection to the system bus.
    * createDefaultBusConnection() has been renamed to createBusConnection(), so as not to be confused with libsystemd's default_bus, which is a different thing (a reusable thread-local bus).

Proxies still by default call createBusConnection() to get a connection when the connection is not provided explicitly by the caller, but now createBusConnection() does a different thing, so now the proxies connect to either session bus or system bus depending on the context (as opposed to always to system bus like before).

The integration tests were modified to use createBusConnection().
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 4a90f80933 chore: update ChangeLog for v2.0.0 (#405) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič d177d2e365 chore: reorder some API functions (#404)
In sdbus-c++ v1, new virtual functions (e.g. overloads of existing virtual functions) were always placed at the end of the class to keep backwards ABI compatibility. Now, with v2, these functions are reordered and functions forming a logical group are together.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič c205178fc0 chore: rename COMPONENTs in CMake (#402) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 280fcfb067 refactor: remove deprecated API stuff (#403) 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 0074c79e7f refactor: rename and re-organize CMake options (#401)
This improves usability of sdbus-c++ in downstream CMake projects. CMake options now have `SDBUSCPP_` prefix so potential conflicts with downstream options/variables are minimized. Also, all configurable options and variables are placed in a single place in the root CMake file.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 3de2812a60 refactor: use optional for passing potential call errors (#396)
This switches from a raw pointer to std::optional type to pass prospective call errors to the client (using std::optional was not possible years back when sdbus-c++ was based on C++14). This makes the API a little clearer, safer, idiomatically more expressive, and removes potential confusion associated with raw pointers (like ownership, lifetime questions, etc.).
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 914d133d10 refactor: add nodiscard attribute for more functions 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 4721060d76 refactor: use correct header include order 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 1c72f1ce52 refactor: add Async suffix to async callMethod functions (#394)
This makes naming consistent, so all asynchronously working functions have Async suffix
2024-04-24 20:20:29 +02:00
Stanislav Angelovic b65461c404 refactor: add nodiscard attribute for some functions 2024-04-24 20:20:29 +02:00
Stanislav Angelovic 24776f2047 chore: remove legacy comments 2024-04-24 20:20:29 +02:00
Stanislav Angelovic f1b9226491 refactor: remove deprecated dont_request_slot_t tag 2024-04-24 20:20:29 +02:00
Stanislav Angelovic dc0c2562b8 refactor: rename request_slot tag to return_slot 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 84932a6985 refactor: improve Proxy signal subscription (#389)
This makes D-Bus proxy signal registration more flexible, more dynamic, and less error-prone since no `finishRegistration()` call is needed. A proxy can register to a signal at any time during its lifetime, and can unregister freely by simply destroying the associated slot.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič bdf313bc60 refactor: improve Object vtable registration (#388)
This improves the D-Bus object API registration/unregistration by making it more flexible, more dynamic, closer to sd-bus API design but still on high abstraction level, and -- most importantly -- less error-prone since no `finishRegistration()` call is needed anymore.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič e76d38c317 fix: add missing header to ScopeGuard.h 2024-04-24 20:20:29 +02:00
Stanislav Angelovič c92f5722c7 refactor: use sd_bus_match_signal() for signal registration (#372)
sd_bus_match_signal() is more convenient than more general sd_bus_add_match() for signal registration, and requires less code on our side. Plus, systemd of at least v238 is required for sdbus-c++ v2, and this version already ships with sd_bus_match_signal().
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 20a13eeafb refactor: make Variant constructor explicit (#370)
This makes the library more robust and prone to user's errors when the user writes an extension for their custom type. In case they forget to implement a serialization function for that type and yet insert an object of that type into sdbus::Message, the current behavior is that, surprisingly, the library masks the error as it resolves the call to the Variant overload, because Variant provides an implicit template converting constructor, so the library tries to construct first the Variant object from the object of custom type, and then inserting into the message that Variant object. Variant constructor serializes the underlying object into its internal message object, which resolves to the same message insertion overload, creating an infinite recursion and ultimately the stack overflow. This is undesired and plain wrong. Marking this Variant converting constructor solves these problems, plus in overall it makes the code a little safer and more verbose. With explicit Variant constructor, when the user forgets to implement a serialization function for their type, the call of such function will fail with an expressive compilation error, and will produce no undesired, surprising results.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 4fd09d4e81 fix(tests): enable skipped test for Clang and FreeBSD (#369)
The test now works with Clang, libc++ and -O2 optimization, since the underlying implementation has been completely re-designed and doesn't suffer from that problem anymore.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 35fd3ff5ce refactor: simplify async call state flag (#368)
Since synchronous D-Bus calls are simplified in v2.0 and no more implemented in terms of an async call, the issue reported in #362 is no more relevant, and we can return to the simple boolean flag for indicating a finished call.
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 6f35f00fcf refactor: let callbacks take message objects by value (#367)
Signatures of callbacks async_reply_handler, signal_handler, message_handler and property_set_callback were modified to take input message objects by value, as opposed to non-const ref.

The callee assumes ownership of the message. This API is more idiomatic, more expressive, cleaner and safer. Move semantics is used to pass messages to the callback handlers. In some cases, this also improves performance.
2024-04-24 20:20:29 +02:00
Stanislav Angelovic 9412940d1e refactor: use sd-bus API to get current message 2024-04-24 20:20:29 +02:00
Marcel Hellwig c6afa26541 fix: update cmake version for library to 2.0.0
This will also install the library as `libsdbus-c++.so.2.0.0`, which
represents the actual version of this library
2024-04-24 20:20:29 +02:00
Marcel Hellwig b4d036c503 fix: prevent installing a time event, when there's no need for a timeout
See #324 for discussion
2024-04-24 20:20:29 +02:00
Stanislav Angelovič 0bfda9ab92 chore: require cmake v3.14 and use FetchContent_MakeAvailable 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 64337e545d test: support new googletest git tags 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 788168eded ci: build googletest manually on older ubuntus 2024-04-24 20:20:29 +02:00
Stanislav Angelovič b9aa770f58 feat: introduce sd-event integration 2024-04-24 20:20:29 +02:00
Stanislav Angelovic 2efb6909b5 chore: enable actions on release/v2.0 branch 2024-04-24 20:20:29 +02:00
Stanislav Angelovic 3e20fc639e refactor: simplify async D-Bus connection handling 2024-04-24 20:20:29 +02:00
Stanislav Angelovič 7450515d0b chore: release version v1.6.0 2024-04-24 20:04:34 +02:00
Stanislav Angelovič c769637f3b chore: update years in header comments 2024-04-16 22:48:34 +02:00
Anthony Brandon 334fcb8833 feat: add std::variant constructor and conversion operator to sdbus::Variant (#429)
Signed-off-by: Anthony Brandon <anthony@amarulasolutions.com>
2024-04-04 20:16:27 +02:00
Stanislav Angelovič b9088cc801 test: add integration tests for std::variant (#427)
This also introduces `always_false` technique instead of `sizeof` trick for unsupported D-Bus type representation static assert. This one is more expressive and leads to more specific, more revealing compiler error messages.
2024-04-02 16:44:45 +02:00
Anthony BrandonandStanislav Angelovič a73eb9b8c1 feat: add support for std::variant as D-Bus variant representation (#415)
Signed-off-by: Anthony Brandon <anthony@amarulasolutions.com>
Co-authored-by: Stanislav Angelovič <stanislav.angelovic@protonmail.com>
2024-04-01 13:23:08 +02:00
Yuriy Chernyshov 700a011b80 fix: add missing #include <exception> (#421) 2024-03-25 20:16:23 +01:00
Marcel Hellwig 8a9cfc1f19 feat: support enums in D-Bus serialization and signatures (#416) 2024-03-07 17:49:05 +01:00
104 changed files with 9846 additions and 5732 deletions
+66
View File
@@ -0,0 +1,66 @@
---
# TODO: enable -llvm-include-order in the end, after clang-format
Checks: "*,\
-llvmlibc-*,\
-altera-*,\
-fuchsia-*,
-cert-err58-cpp,\
-modernize-use-trailing-return-type,\
-cppcoreguidelines-avoid-magic-numbers,\
-readability-magic-numbers,\
-readability-braces-around-statements,\
-google-readability-braces-around-statements,\
-hicpp-braces-around-statements,\
-hicpp-signed-bitwise,\
-llvm-include-order,\
-llvm-header-guard,\
-google-runtime-int,\
-google-default-arguments,\
-hicpp-named-parameter,\
-readability-named-parameter,\
-bugprone-macro-parentheses,\
-google-readability-todo,\
-google-build-using-namespace,\
-cppcoreguidelines-pro-type-reinterpret-cast,\
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,\
-hicpp-no-array-decay,\
-cppcoreguidelines-avoid-c-arrays,\
-hicpp-avoid-c-arrays,\
-cppcoreguidelines-non-private-member-variables-in-classes,\
-misc-non-private-member-variables-in-classes,\
-modernize-avoid-c-arrays"
HeaderFilterRegex: '.*'
FormatStyle: file
WarningsAsErrors: "*"
CheckOptions:
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: '1'
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: '1'
- key: readability-implicit-bool-conversion.AllowIntegerConditions
value: '1'
- key: readability-redundant-member-init.IgnoreBaseInCopyConstructors
value: '1'
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
value: '1'
- key: hicpp-special-member-functions.AllowSoleDefaultDtor
value: '1'
- key: readability-identifier-length.IgnoredVariableNames
value: 'r|t|fd|id|ok|it|ts'
- key: readability-identifier-length.IgnoredParameterNames
value: 'fd|id|b'
- key: performance-move-const-arg.CheckTriviallyCopyableMove
value: '0'
- key: hicpp-move-const-arg.CheckTriviallyCopyableMove
value: '0'
- key: misc-include-cleaner.IgnoreHeaders
value: 'systemd/.*|sdbus-c\+\+/.*|gtest/.*|gmock/.*|bits/chrono.h|bits/basic_string.h|time.h|poll.h|stdlib.h|stdio.h'
- key: readability-simplify-boolean-expr.IgnoreMacros
value: '1'
- key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams
value: '1'
# - key: bugprone-easily-swappable-parameters.MinimumLength
# value: '3'
# - key: readability-braces-around-statements.ShortStatementLines
# value: '3'
+52 -39
View File
@@ -4,7 +4,9 @@ on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
branches:
- master
- release/v2.0
jobs:
build:
@@ -12,15 +14,11 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, ubuntu-22.04]
os: [ubuntu-22.04, ubuntu-24.04]
compiler: [g++, clang]
build: [shared-libsystemd]
include:
- os: ubuntu-20.04
compiler: g++
build: embedded-static-libsystemd
build: [shared-libsystemd, embedded-static-libsystemd]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: install-libsystemd-toolchain
if: matrix.build == 'embedded-static-libsystemd'
run: |
@@ -34,61 +32,76 @@ 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++
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
- 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
# We need to use libc++ with Clang because there is a bug in libstdc++ chrono headers that Clang has issues with
echo "SDBUSCPP_EXTRA_CXX_FLAGS=-stdlib=libc++" >> $GITHUB_ENV
# We don't install googletest but we let it be built within sdbus-c++ builds below, since it needs to be built against libc++ for Clang jobs to pass
# - name: install-googletest
# run: |
# sudo apt-get install -y libgmock-dev
- name: configure-debug-gcc11 # For gcc 11, turn off the annoying deprecated-copy warning
if: matrix.build == 'shared-libsystemd' && matrix.compiler == 'g++' && matrix.os == 'ubuntu-22.04'
run: |
sudo apt-get install -y libgmock-dev
cmake -B _build -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-20.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" -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" -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
cmake -B _build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="-O0 -g -W -Wextra -Wall -Wnon-virtual-dtor -Werror $SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0
- name: configure-release-with-embedded-libsystemd
if: matrix.build == 'embedded-static-libsystemd'
run: |
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_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 ..
cmake -B _build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_CXX_FLAGS="$SDBUSCPP_EXTRA_CXX_FLAGS" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_BUILD_LIBSYSTEMD=ON -DSDBUSCPP_LIBSYSTEMD_VERSION=252 -DSDBUSCPP_GOOGLETEST_VERSION=1.14.0
- name: make
run: |
cd build
cmake --build . -j4
cmake --build _build -j4
- name: verify
run: |
cd build
sudo cmake --build . --target install
ctest --output-on-failure
sudo cmake --build _build --target install
ctest --output-on-failure --test-dir _build
- name: pack
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.04'
if: matrix.build == 'shared-libsystemd'
run: |
cd build
cd _build
cpack -G DEB
- name: 'Upload Artifact'
if: matrix.build == 'shared-libsystemd' && matrix.os == 'ubuntu-20.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: |
build/sdbus-c++*.deb
build/sdbus-c++*.ddeb
_build/sdbus-c++*.deb
_build/sdbus-c++*.ddeb
retention-days: 10
static-analysis:
name: static-analysis (ubuntu-24.04, clang-tidy, shared-libsystemd)
runs-on: ubuntu-24.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: install-deps
run: |
sudo apt-get update -y
sudo apt-get install -y libsystemd-dev libgmock-dev clang
sudo update-alternatives --remove-all cc
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 10
sudo update-alternatives --remove-all c++
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 10
- name: configure
run: |
cmake -B _build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-O0 -g" -DCMAKE_VERBOSE_MAKEFILE=ON -DSDBUSCPP_CLANG_TIDY=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON -DSDBUSCPP_BUILD_EXAMPLES=ON
- name: make
run: |
cmake --build _build -j4
freebsd-build:
name: build (freebsd, clang/libc++, basu)
runs-on: ubuntu-22.04 # until https://github.com/actions/runner/issues/385
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Test in FreeBSD VM
uses: vmactions/freebsd-vm@v1
with:
@@ -97,7 +110,7 @@ jobs:
prepare: |
pkg install -y cmake ninja pkgconf basu expat googletest
run: |
cmake -B _build -G Ninja -DBUILD_CODE_GEN=ON -DBUILD_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_PERF_TESTS=ON -DENABLE_STRESS_TESTS=ON
cmake -B _build -G Ninja -DSDBUSCPP_INSTALL=ON -DSDBUSCPP_BUILD_CODEGEN=ON -DSDBUSCPP_BUILD_TESTS=ON -DSDBUSCPP_BUILD_PERF_TESTS=ON -DSDBUSCPP_BUILD_STRESS_TESTS=ON
cmake --build _build
cmake --install _build
pkg install -y dbus
+3
View File
@@ -16,6 +16,9 @@ tests/run-test-on-device.sh
.settings
*.log
# vscode
.vscode/
#autotools
sdbus-cpp.pc
*Makefile
+178 -95
View File
@@ -2,59 +2,136 @@
# PROJECT INFORMATION
#-------------------------------
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.14)
project(sdbus-c++ VERSION 1.5.0 LANGUAGES C CXX)
project(sdbus-c++ VERSION 2.3.1 LANGUAGES CXX C)
include(GNUInstallDirs) # Installation directories for `install` command and pkgconfig file
# -------------------------------
# CONFIGURATION OPTIONS
# -------------------------------
option(SDBUSCPP_BUILD_LIBSYSTEMD "Fetch & build libsystemd static library and make it part of libsdbus-c++, instead of searching for it in the system" OFF)
if(NOT SDBUSCPP_BUILD_LIBSYSTEMD)
set(SDBUSCPP_SDBUS_LIB "default" CACHE STRING "sd-bus implementation library to search for and use (default, systemd, elogind, or basu)")
set_property(CACHE SDBUSCPP_SDBUS_LIB PROPERTY STRINGS default systemd elogind basu)
else()
set(SDBUSCPP_LIBSYSTEMD_VERSION "252" CACHE STRING "libsystemd version (>=239) to build and incorporate into libsdbus-c++")
set(SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS "" CACHE STRING "Additional configuration options to be passed as-is to libsystemd build system")
endif()
option(SDBUSCPP_INSTALL "Enable installation of sdbus-c++ (downstream projects embedding sdbus-c++ may want to turn this OFF)" ON)
option(SDBUSCPP_BUILD_TESTS "Build tests" OFF)
if (SDBUSCPP_BUILD_TESTS)
option(SDBUSCPP_BUILD_PERF_TESTS "Build also sdbus-c++ performance tests" OFF)
option(SDBUSCPP_BUILD_STRESS_TESTS "Build also sdbus-c++ stress tests" OFF)
set(SDBUSCPP_TESTS_INSTALL_PATH "tests/${PROJECT_NAME}" CACHE STRING "Specifies where the test binaries will be installed")
set(SDBUSCPP_GOOGLETEST_VERSION 1.14.0 CACHE STRING "Version of gmock library to use")
set(SDBUSCPP_GOOGLETEST_GIT_REPO "https://github.com/google/googletest.git" CACHE STRING "A git repo to clone and build googletest from if gmock is not found in the system")
endif()
option(SDBUSCPP_BUILD_CODEGEN "Build generator tool for C++ native bindings" OFF)
option(SDBUSCPP_BUILD_EXAMPLES "Build example programs" OFF)
option(SDBUSCPP_BUILD_DOCS "Build documentation for sdbus-c++" ON)
if(SDBUSCPP_BUILD_DOCS)
option(SDBUSCPP_BUILD_DOXYGEN_DOCS "Build doxygen documentation for sdbus-c++ API" OFF)
endif()
option(SDBUSCPP_CLANG_TIDY "Co-compile with clang-tidy static analyzer" OFF)
#option(SDBUSCPP_COVERAGE "Build sdbus-c++ with code coverage instrumentation" OFF)
# We promote the BUILD_SHARED_LIBS flag to a (global) option only if we are the main project
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
option(BUILD_SHARED_LIBS "Build shared libraries (.so) instead of static ones (.a)" ON)
endif()
message(STATUS "")
message(STATUS " *******************************************")
message(STATUS " * SDBUS-C++ CONFIGURATION *")
message(STATUS " *******************************************")
message(STATUS "")
message(STATUS " SDBUSCPP_BUILD_LIBSYSTEMD: ${SDBUSCPP_BUILD_LIBSYSTEMD}")
if(SDBUSCPP_BUILD_LIBSYSTEMD)
message(STATUS " SDBUSCPP_LIBSYSTEMD_VERSION: ${SDBUSCPP_LIBSYSTEMD_VERSION}")
message(STATUS " SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS: ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}")
endif()
message(STATUS " SDBUSCPP_INSTALL: ${SDBUSCPP_INSTALL}")
message(STATUS " SDBUSCPP_BUILD_TESTS: ${SDBUSCPP_BUILD_TESTS}")
if(SDBUSCPP_BUILD_TESTS)
message(STATUS " SDBUSCPP_BUILD_PERF_TESTS: ${SDBUSCPP_BUILD_PERF_TESTS}")
message(STATUS " SDBUSCPP_BUILD_STRESS_TESTS: ${SDBUSCPP_BUILD_STRESS_TESTS}")
message(STATUS " SDBUSCPP_TESTS_INSTALL_PATH: ${SDBUSCPP_TESTS_INSTALL_PATH}")
message(STATUS " SDBUSCPP_GOOGLETEST_VERSION: ${SDBUSCPP_GOOGLETEST_VERSION}")
message(STATUS " SDBUSCPP_GOOGLETEST_GIT_REPO: ${SDBUSCPP_GOOGLETEST_GIT_REPO}")
endif()
message(STATUS " SDBUSCPP_BUILD_CODEGEN: ${SDBUSCPP_BUILD_CODEGEN}")
message(STATUS " SDBUSCPP_BUILD_EXAMPLES: ${SDBUSCPP_BUILD_EXAMPLES}")
message(STATUS " SDBUSCPP_BUILD_DOCS: ${SDBUSCPP_BUILD_DOCS}")
if(SDBUSCPP_BUILD_DOCS)
message(STATUS " SDBUSCPP_BUILD_DOXYGEN_DOCS: ${SDBUSCPP_BUILD_DOXYGEN_DOCS}")
endif()
message(STATUS " BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}")
message(STATUS "")
#-------------------------------
# PERFORMING CHECKS & PREPARING THE DEPENDENCIES
#-------------------------------
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)
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=236)
if(NOT TARGET PkgConfig::Systemd)
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()
if(SDBUSCPP_BUILD_LIBSYSTEMD)
# Build static libsystemd library as an external project
include(cmake/LibsystemdExternalProject.cmake)
set(SDBUS_IMPL "systemd")
set(SDBUS_LIB "libsystemd")
else()
# Search for sd-bus implementations in the system as per user's configuration
set(SDBUS_LIBS ${SDBUSCPP_SDBUS_LIB})
if(SDBUSCPP_SDBUS_LIB STREQUAL "default")
set(SDBUS_LIBS systemd elogind basu) # This is the default search order
endif()
set(MINIMUM_SDBUS_VERSION 238)
find_package(PkgConfig REQUIRED)
foreach(LIB ${SDBUS_LIBS})
if(LIB STREQUAL "systemd")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libsystemd>=${MINIMUM_SDBUS_VERSION})
if(TARGET PkgConfig::Systemd)
set(SDBUS_IMPL "systemd")
set(SDBUS_LIB "libsystemd")
break()
endif()
elseif(LIB STREQUAL "elogind")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL libelogind>=${MINIMUM_SDBUS_VERSION})
if(TARGET PkgConfig::Systemd)
set(SDBUS_IMPL "elogind")
set(SDBUS_LIB "libelogind")
string(REPLACE "." ";" VERSION_LIST ${Systemd_VERSION})
list(GET VERSION_LIST 0 Systemd_VERSION)
break()
endif()
elseif(LIB STREQUAL "basu")
pkg_check_modules(Systemd IMPORTED_TARGET GLOBAL basu)
if(TARGET PkgConfig::Systemd)
set(SDBUS_IMPL "basu")
set(SDBUS_LIB "basu")
set(Systemd_VERSION "240") # https://git.sr.ht/~emersion/basu/commit/d4d185d29a26
break()
endif()
else()
message(FATAL_ERROR "Unrecognized sd-bus implementation library ${LIB}")
endif()
endforeach()
if(NOT TARGET PkgConfig::Systemd)
message(FATAL_ERROR "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++)")
message(FATAL_ERROR "None of ${SDBUS_LIBS} libraries implementing sd-bus was found (Are their dev packages installed?)")
endif()
add_library(Systemd::Libsystemd ALIAS PkgConfig::Systemd)
string(REGEX MATCHALL "([0-9]+)" SYSTEMD_VERSION_LIST "${Systemd_VERSION}")
list(GET SYSTEMD_VERSION_LIST 0 LIBSYSTEMD_VERSION)
message(STATUS "Building with libsystemd v${LIBSYSTEMD_VERSION}")
else()
# Build static libsystemd library as an external project
include(cmake/LibsystemdExternalProject.cmake)
list(GET SYSTEMD_VERSION_LIST 0 SDBUSCPP_LIBSYSTEMD_VERSION)
message(STATUS "Building with libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
endif()
find_package(Threads REQUIRED)
include(cmake/clang-tidy.cmake) # Static analysis with clang-tidy
#-------------------------------
# SOURCE FILES CONFIGURATION
#-------------------------------
@@ -87,8 +164,11 @@ set(SDBUSCPP_HDR_SRCS
${SDBUSCPP_SOURCE_DIR}/ISdBus.h)
set(SDBUSCPP_PUBLIC_HDRS
${SDBUSCPP_INCLUDE_DIR}/Awaitable.h
${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
@@ -109,7 +189,7 @@ set(SDBUSCPP_SRCS ${SDBUSCPP_CPP_SRCS} ${SDBUSCPP_HDR_SRCS} ${SDBUSCPP_PUBLIC_HD
# GENERAL COMPILER CONFIGURATION
#-------------------------------
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
#----------------------------------
# LIBRARY BUILD INFORMATION
@@ -118,24 +198,19 @@ set(CMAKE_CXX_STANDARD 17)
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=${LIBSYSTEMD_VERSION}
SDBUS_${LIBSYSTEMD_IMPL}
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
SDBUS_${SDBUS_IMPL}
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
target_include_directories(sdbus-c++-objlib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>)
if(BUILD_SHARED_LIBS)
set_target_properties(sdbus-c++-objlib PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
if(BUILD_LIBSYSTEMD)
if(SDBUSCPP_BUILD_LIBSYSTEMD)
add_dependencies(sdbus-c++-objlib LibsystemdBuildProject)
endif()
target_link_libraries(sdbus-c++-objlib
@@ -144,6 +219,9 @@ target_link_libraries(sdbus-c++-objlib
Threads::Threads)
add_library(sdbus-c++)
# Create alias to allow consistent target use regardless whether used in-source or found via find_package
add_library(SDBusCpp::sdbus-c++ ALIAS sdbus-c++)
target_include_directories(sdbus-c++ PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
set_target_properties(sdbus-c++
@@ -153,28 +231,11 @@ 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
#----------------------------------
option(BUILD_TESTS "Build tests (default OFF)" OFF)
if(BUILD_TESTS)
if(SDBUSCPP_BUILD_TESTS)
message(STATUS "Building with tests")
enable_testing()
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tests")
@@ -184,9 +245,7 @@ endif()
# UTILS
#----------------------------------
option(BUILD_CODE_GEN "Build and install interface stub code generator (default OFF)" OFF)
if(BUILD_CODE_GEN)
if(SDBUSCPP_BUILD_CODEGEN)
message(STATUS "Building with code generator tool")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/tools")
endif()
@@ -195,9 +254,7 @@ endif()
# EXAMPLES
#----------------------------------
option(BUILD_EXAMPLES "Build example programs (default OFF)" OFF)
if(BUILD_EXAMPLES)
if(SDBUSCPP_BUILD_EXAMPLES)
message(STATUS "Building with examples")
add_subdirectory(examples)
endif()
@@ -206,13 +263,9 @@ endif()
# DOCUMENTATION
#----------------------------------
option(BUILD_DOC "Build documentation for sdbus-c++" ON)
if(BUILD_DOC)
if(SDBUSCPP_BUILD_DOCS)
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()
#----------------------------------
@@ -221,47 +274,77 @@ 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 (BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
if(BUILD_SHARED_LIBS AND (SDBUSCPP_BUILD_LIBSYSTEMD OR Systemd_LINK_LIBRARIES MATCHES "/libsystemd\.a(;|$)"))
set(PKGCONFIG_REQS ".private")
else()
set(PKGCONFIG_REQS "")
endif()
set(PKGCONFIG_DEPS ${LIBSYSTEMD_LIB})
set(PKGCONFIG_DEPS ${SDBUS_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)
#----------------------------------
# CPack
# EXPORTING BUILD-TREE TARGETS
#----------------------------------
set(EXPORT_SET sdbus-c++)
if(NOT BUILD_SHARED_LIBS)
list(APPEND EXPORT_SET "sdbus-c++-objlib")
endif()
export(TARGETS ${EXPORT_SET}
NAMESPACE SDBusCpp::
FILE sdbus-c++-targets.cmake)
#----------------------------------
# INSTALLATION & EXPORTING INSTALL-TREE TARGETS
#----------------------------------
if(SDBUSCPP_INSTALL)
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")
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)
set(CPACK_DEBIAN_ENABLE_COMPONENT_DEPENDS ON)
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libsystemd-dev (>=236)")
set(CPACK_DEBIAN_DEV_PACKAGE_DEPENDS "libsystemd-dev (>=${MINIMUM_SDBUS_VERSION})")
include(CPack)
+77
View File
@@ -248,3 +248,80 @@ v1.5.0
- Add INSTALL_TESTS CMake option
- Minor UnixFd cleanups
- Additional little fixes and updates in code, build system, CI, and documentation
v1.6.0
- Add support for enums in D-Bus serialization and signatures
- Add support for std::variant as an alternative C++ type for D-Bus Variant
- Add support for implicit conversions between std::variant and sdbus::Variant
- Fix missing includes
v2.0.0
- A new major version with revamped API, bringing numerous new features, simplifications, fixes and breaking changes improving not only API consistency, safety and expressiveness, but also performance
- Add section 'Migrating to sdbus-c++ v2' to the 'Using sdbus-c++' document providing the complete list of breaking API/ABI/behavior changes and migration notes
- Switch to C++20 standard (but the API is backwards-compatible with C++17, skipping C++20 features in such a case)
- Add strong types to public API
- Add support for string_view as a D-Bus type representation
- Enable compile-time D-Bus signature generation
- Redesign Object vtable registration API
- Improve Proxy signal subscription API
- Refactor object manager API for consistency
- Introduce native sd-event integration
- Let callbacks take message objects by value
- Simplify async D-Bus connection handling
- Simplify async call state flag
- Make Variant constructor explicit
- Use optional for passing potential call errors
- Rename and re-organize CMake options
- Rename connection creation methods
- Have all async function names finish with *Async for consistency
- Implement more flexible searching for sd-bus libs
- Add new `SDBUSCPP_SDBUS_LIB` CMake configuration variable determining which sd-bus library shall be picked
- Make Variant conversion operator explicit
- Use sd-bus API to get the current message
- Use sd_bus_match_signal() for signal registration
- Provide also const char* overloads for convenience functions
- Disable move in generated adaptor and proxy classes
- Improve and make more efficient some Message API
- Add Slot-returning overloads of async method calls
- Remove floating_slot_t tag and use return_slot_t instead
- Add [[nodiscard]] to many relevant API functions
- Add proper fix for timeout handling
- Fix for external event loops in which the event loop thread ID was not correctly initialized (now fixed and simplified by not needing the thread ID anymore)
- Remove deprecated stuff
- Add `SDBUSCPP_` prefix to CMake configuration variables to avoid conflicts with downstream projects
- Require systemd of at least v238
- Many other fixes and updates in code, tests, build system, CI, and documentation
v2.1.0
- Add SDBUSCPP_REGISTER_STRUCT macro to conveniently teach sdbus-c++ about user-defined structs
- Extend the SDBUSCPP_REGISTER_STRUCT macro with serialization of user-defined structs as dicts, and deserialization of dicts into user-defined structs
- Make createPlainMessage() function public
- Solve the problem of sending large D-Bus messages properly (through the event loop thread)
- Fix partially renamed BUILD_DOXYGEN_DOC CMake option
- Change googletest to default version 1.14.0
- Add version parameter to the xml2cpp codegen tool
- A few other internal refactorings and improvements
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
v2.3.0
- Add coroutine support for client-side async calls
- Add support for dumping variant to string
- Introduce clang-tidy for static analysis and fix issues
- Add deduction guides for Struct from std::tuple
- Other fixes and improvements
v2.3.1
- Fix ABI compat issue by moving new virtual functions to the end of the class
+38 -32
View File
@@ -26,70 +26,76 @@ $ sudo cmake --build . --target install
### CMake configuration flags for sdbus-c++
* `BUILD_CODE_GEN` [boolean]
* `SDBUSCPP_BUILD_CODEGEN` [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 `-DBUILD_CODE_GEN=ON` flag to turn on building the code gen.
Build the codegen tool `sdbus-c++-xml2cpp` for generating the high level C++ bindings out of the D-Bus IDL XML description. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_CODEGEN=ON` flag to turn on building the code gen.
* `BUILD_DOC` [boolean]
* `SDBUSCPP_BUILD_DOCS` [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:
Include sdbus-c++ documentation files and tutorials. Default value: `ON`. With this option turned on, you may also enable/disable the following option:
* `BUILD_DOXYGEN_DOC` [boolean]
* `SDBUSCPP_BUILD_DOXYGEN_DOCS` [boolean]
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.
Build Doxygen documentation of sdbus-c++ API. If enabled, the documentation must still be built explicitly through `cmake --build . --target doc`. Default value: `OFF`. Use `-DSDBUSCPP_BUILD_DOXYGEN_DOCS=OFF` to disable searching for Doxygen and building Doxygen documentation of sdbus-c++ API.
* `BUILD_TESTS` [boolean]
* `SDBUSCPP_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:
Build sdbus-c++ unit and integration tests, invokable by `cmake --build . --target test` (Note: before invoking `cmake --build . --target test`, make sure you copy `tests/integrationtests/files/org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory). That incorporates downloading and building static libraries of Google Test. Default value: `OFF`. Use `-DBUILD_TESTS=ON` to enable building the tests. With this option turned on, you may also enable/disable the following options:
* `ENABLE_PERF_TESTS` [boolean]
* `SDBUSCPP_BUILD_PERF_TESTS` [boolean]
Option for building sdbus-c++ performance tests. Default value: `OFF`.
Build sdbus-c++ performance tests. Default value: `OFF`.
* `ENABLE_STRESS_TESTS` [boolean]
* `SDBUSCPP_BUILD_STRESS_TESTS` [boolean]
Option for building sdbus-c++ stress tests. Default value: `OFF`.
Build sdbus-c++ stress tests. Default value: `OFF`.
* `INSTALL_TESTS` [boolean]
Option for installing tests that were built. Default value: `OFF`.
* `TESTS_INSTALL_PATH` [string]
* `SDBUSCPP_TESTS_INSTALL_PATH` [string]
Path where the test binaries shall get installed. Default value: `${CMAKE_INSTALL_PREFIX}/tests/sdbus-c++` (previously: `/opt/test/bin`).
* `BUILD_LIBSYSTEMD` [boolean]
* `SDBUSCPP_BUILD_EXAMPLES` [boolean]
Option for building libsystemd dependency library automatically when sdbus-c++ is built, and making libsystemd an integral part of sdbus-c++ library. Default value: `OFF`. Might be very helpful in non-systemd environments where libsystemd shared library is unavailable (see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information). With this option turned on, you may also provide the following configuration flag:
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`.
* `LIBSYSTEMD_VERSION` [string]
* `SDBUSCPP_BUILD_LIBSYSTEMD` [boolean]
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `242`.
Build sd-bus (libsystemd library) instead of searching for it in the system, and make it part of sdbus-c++ library. Default value: `OFF`, which means that the sd-bus implementation library (`libsystemd`, `libelogind`, or `basu`) will be searched via `pkg-config` in the system.
* `LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
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:
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.
* `SDBUSCPP_SDBUS_LIB` [string]
Defines which sd-bus implementation library to search for and use. Allowed values: `default`, `systemd`, `elogind`, `basu`. Default value: `default`, which means that sdbus-c++ will try to find any of `systemd`, `elogind`, `basu` in the order as listed here.
With this option turned on, you may provide the following additional configuration flag:
* `SDBUSCPP_LIBSYSTEMD_VERSION` [string]
Defines version of systemd to be downloaded, built and integrated into sdbus-c++. Default value: `252`, minimum value: `239`.
* `SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS` [string]
Additional options to be passed as-is to the libsystemd build system in its configure step. Can be used for passing e.g. toolchain file path in case of cross builds. Default value: empty.
* `CMAKE_BUILD_TYPE` [string]
This is a CMake-builtin option. Set to `Release` to build sdbus-c++ for production use. Set to `Debug` if you want to help further develop (and debug) the library :)
CMake-builtin option. Set to `Release` to build sdbus-c++ for production use. Set to `Debug` if you want to help further develop (and debug) the library :)
* `BUILD_SHARED_LIBS` [boolean]
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`.
* `BUILD_EXAMPLES` [boolean]
Build example programs which are located in the _example_ directory. Examples are not installed. Default value: `OFF`
Global CMake flag, promoted in sdbus-c++ project to a CMake option. Use this to control whether sdbus-c++ is built as either a shared or static library. Default value: `ON`.
Dependencies
------------
* `C++17` - the library uses C++17 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 a non-systemd environment, don't worry, see [Solving libsystemd dependency](docs/using-sdbus-c++.md#solving-libsystemd-dependency) for more information.)
* `C++20/C++17` - the library uses C++20 features, but its public API is backwards compatible with C++17 and provides optional extra features when C++20 features are available
* `libsystemd`/`libelogind`/`basu` - libraries containing sd-bus implementation that sdbus-c++ is written around. In case of `libsystemd` and `libelogind`, version >= 238 is needed. (In case you have you're missing any of those sd-bus implementations, don't worry, see [Solving sd-bus dependency](docs/using-sdbus-c++.md#solving-sd-bus-dependency) for more information.)
* `googletest` - google unit testing framework, only necessary when building tests, will be downloaded and built automatically.
* `pkgconfig` - required for sdbus-c++ to be able to find some dependency packages.
* `expat` - necessary when building xml2cpp code generator (`BUILD_CODE_GEN` option is ON).
* `expat` - necessary when building the xml2cpp binding code generator (`SDBUSCPP_BUILD_CODEGEN` option is `ON`).
Licensing
---------
+6 -9
View File
@@ -18,9 +18,6 @@ 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")
@@ -29,24 +26,24 @@ else()
set(LIBSYSTEMD_BUILD_TYPE "release")
endif()
if(LIBSYSTEMD_VERSION LESS "239")
if(SDBUSCPP_LIBSYSTEMD_VERSION LESS "239")
message(FATAL_ERROR "Only libsystemd version >=239 can be built as static part of sdbus-c++")
endif()
if(LIBSYSTEMD_VERSION GREATER "240")
if(SDBUSCPP_LIBSYSTEMD_VERSION GREATER "240")
set(BUILD_VERSION_H ${NINJA} -C <BINARY_DIR> version.h)
endif()
message(STATUS "Building with embedded libsystemd v${LIBSYSTEMD_VERSION}")
message(STATUS "Building with embedded libsystemd v${SDBUSCPP_LIBSYSTEMD_VERSION}")
include(ExternalProject)
ExternalProject_Add(LibsystemdBuildProject
PREFIX libsystemd-v${LIBSYSTEMD_VERSION}
PREFIX libsystemd-v${SDBUSCPP_LIBSYSTEMD_VERSION}
GIT_REPOSITORY https://github.com/systemd/systemd-stable.git
GIT_TAG v${LIBSYSTEMD_VERSION}-stable
GIT_TAG v${SDBUSCPP_LIBSYSTEMD_VERSION}-stable
GIT_SHALLOW 1
UPDATE_COMMAND ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E remove <BINARY_DIR>/*
COMMAND ${MESON} --prefix=<INSTALL_DIR> --buildtype=${LIBSYSTEMD_BUILD_TYPE} -Drootprefix=<INSTALL_DIR> -Dstatic-libsystemd=pic -Dselinux=false <SOURCE_DIR> <BINARY_DIR> ${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> ${SDBUSCPP_LIBSYSTEMD_EXTRA_CONFIG_OPTS}
BUILD_COMMAND ${BUILD_VERSION_H}
COMMAND ${NINJA} -C <BINARY_DIR> libsystemd.a
BUILD_ALWAYS 0
+16
View File
@@ -0,0 +1,16 @@
#----------------------------------
# STATIC ANALYSIS
#----------------------------------
if(SDBUSCPP_CLANG_TIDY)
message(STATUS "Building with static analysis")
find_program(CLANG_TIDY NAMES clang-tidy)
if(NOT CLANG_TIDY)
message(WARNING "clang-tidy not found")
else()
message(STATUS "clang-tidy found: ${CLANG_TIDY}")
set(DO_CLANG_TIDY "${CLANG_TIDY}")
#set(DO_CLANG_TIDY "${CLANG_TIDY}" "-fix")
set(CMAKE_CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
endif()
endif()
+12 -8
View File
@@ -2,7 +2,7 @@
find_package(Doxygen)
if(BUILD_DOXYGEN_DOC)
if(SDBUSCPP_BUILD_DOXYGEN_DOCS)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
@@ -15,15 +15,19 @@ 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()
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)
if(SDBUSCPP_INSTALL)
install(FILES sdbus-c++-class-diagram.png
sdbus-c++-class-diagram.uml
systemd-dbus-config.md
using-sdbus-c++.md
DESTINATION ${CMAKE_INSTALL_DOCDIR} COMPONENT sdbus-c++-doc)
if (SDBUSCPP_BUILD_DOXYGEN_DOCS AND DOXYGEN_FOUND)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION ${CMAKE_INSTALL_DOCDIR} OPTIONAL COMPONENT sdbus-c++-doc)
endif()
endif()
+438 -192
View File
File diff suppressed because it is too large Load Diff
+21 -3
View File
@@ -1,6 +1,24 @@
# Building examples
add_executable(obj-manager-server org.freedesktop.DBus.ObjectManager/obj-manager-server.cpp)
set(OBJECTMANAGER_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.DBus.ObjectManager)
set(OBJECTMANAGER_GENERATED_DIR ${OBJECTMANAGER_SOURCE_DIR}/dbus-api/gen-cpp)
set(OBJECTMANAGER_SERVER_SRCS
${OBJECTMANAGER_SOURCE_DIR}/obj-manager-server.cpp
${OBJECTMANAGER_GENERATED_DIR}/examplemanager-planet1-server-glue.h)
add_executable(obj-manager-server ${OBJECTMANAGER_SERVER_SRCS})
target_include_directories(obj-manager-server SYSTEM PRIVATE ${OBJECTMANAGER_GENERATED_DIR})
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++)
set(OBJECTMANAGER_CLIENT_SRCS
${OBJECTMANAGER_SOURCE_DIR}/obj-manager-client.cpp
${OBJECTMANAGER_GENERATED_DIR}/examplemanager-planet1-client-glue.h)
add_executable(obj-manager-client ${OBJECTMANAGER_CLIENT_SRCS})
target_include_directories(obj-manager-client SYSTEM PRIVATE ${OBJECTMANAGER_GENERATED_DIR})
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()
@@ -21,28 +21,37 @@ public:
protected:
Planet1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
: m_proxy(proxy)
{
}
Planet1_proxy(const Planet1_proxy&) = delete;
Planet1_proxy& operator=(const Planet1_proxy&) = delete;
Planet1_proxy(Planet1_proxy&&) = delete;
Planet1_proxy& operator=(Planet1_proxy&&) = delete;
~Planet1_proxy() = default;
void registerProxy()
{
}
public:
uint64_t GetPopulation()
{
uint64_t result;
proxy_.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("GetPopulation").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
public:
std::string Name()
{
return proxy_.getProperty("Name").onInterface(INTERFACE_NAME);
return m_proxy.getProperty("Name").onInterface(INTERFACE_NAME).get<std::string>();
}
private:
sdbus::IProxy& proxy_;
sdbus::IProxy& m_proxy;
};
}}} // namespaces
@@ -21,14 +21,24 @@ public:
protected:
Planet1_adaptor(sdbus::IObject& object)
: object_(object)
: m_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;
@@ -36,7 +46,7 @@ private:
virtual std::string Name() = 0;
private:
sdbus::IObject& object_;
sdbus::IObject& m_object;
};
}}} // namespaces
@@ -12,34 +12,48 @@
#include "examplemanager-planet1-client-glue.h"
#include <sdbus-c++/sdbus-c++.h>
#include <iostream>
#include <thread>
#include <utility>
#include <map>
#include <string>
#include <vector>
#include <memory>
class PlanetProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::ExampleManager::Planet1_proxy >
{
public:
PlanetProxy(sdbus::IConnection& connection, std::string destination, std::string path)
: ProxyInterfaces(connection, std::move(destination), std::move(path))
PlanetProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath path)
: ProxyInterfaces(connection, std::move(destination), std::move(path))
{
registerProxy();
}
PlanetProxy(const PlanetProxy&) = delete;
PlanetProxy& operator=(const PlanetProxy&) = delete;
PlanetProxy(PlanetProxy&&) = delete;
PlanetProxy& operator=(PlanetProxy&&) = delete;
~PlanetProxy()
{
unregisterProxy();
}
};
class ManagerProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
class ManagerProxy final : public sdbus::ProxyInterfaces<sdbus::ObjectManager_proxy>
{
public:
ManagerProxy(sdbus::IConnection& connection, const std::string& destination, std::string path)
: ProxyInterfaces(connection, destination, std::move(path))
, m_connection(connection)
, m_destination(destination)
ManagerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath path)
: ProxyInterfaces(connection, destination, std::move(path))
, m_connection(connection)
, m_destination(std::move(destination))
{
registerProxy();
}
ManagerProxy(const ManagerProxy&) = delete;
ManagerProxy& operator=(const ManagerProxy&) = delete;
ManagerProxy(ManagerProxy&&) = delete;
ManagerProxy& operator=(ManagerProxy&&) = delete;
~ManagerProxy()
{
unregisterProxy();
@@ -47,8 +61,7 @@ public:
void handleExistingObjects()
{
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
objectsInterfacesAndProperties = GetManagedObjects();
auto objectsInterfacesAndProperties = GetManagedObjects();
for (const auto& [object, interfacesAndProperties] : objectsInterfacesAndProperties) {
onInterfacesAdded(object, interfacesAndProperties);
}
@@ -56,55 +69,57 @@ public:
private:
void onInterfacesAdded( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties) override
{
std::cout << objectPath << " added:\t";
for (const auto& [interface, _] : interfacesAndProperties) {
std::cout << interface << " ";
}
std::cout << std::endl;
std::cout << '\n';
// Parse and print some more info
auto planetInterface = interfacesAndProperties.find(org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME);
auto planetInterface = interfacesAndProperties.find(sdbus::InterfaceName{org::sdbuscpp::ExampleManager::Planet1_proxy::INTERFACE_NAME});
if (planetInterface == interfacesAndProperties.end()) {
return;
}
const auto& properties = planetInterface->second;
// get a property which was passed as part of the signal.
const auto& name = properties.at("Name").get<std::string>();
const auto& name = properties.at(sdbus::PropertyName{"Name"}).get<std::string>();
// or create a proxy instance to the newly added object.
PlanetProxy planet(m_connection, m_destination, objectPath);
std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << std::endl;
std::cout << name << " has a population of " << planet.GetPopulation() << ".\n" << '\n';
}
void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces) override
, const std::vector<sdbus::InterfaceName>& interfaces) override
{
std::cout << objectPath << " removed:\t";
for (const auto& interface : interfaces) {
std::cout << interface << " ";
}
std::cout << std::endl;
std::cout << '\n';
}
sdbus::IConnection& m_connection;
std::string m_destination;
sdbus::ServiceName m_destination;
};
int main()
{
auto connection = sdbus::createSessionBusConnection();
auto managerProxy = std::make_unique<ManagerProxy>(*connection, "org.sdbuscpp.examplemanager", "/org/sdbuscpp/examplemanager");
sdbus::ServiceName destination{"org.sdbuscpp.examplemanager"};
sdbus::ObjectPath objectPath{"/org/sdbuscpp/examplemanager"};
auto managerProxy = std::make_unique<ManagerProxy>(*connection, std::move(destination), std::move(objectPath));
try {
managerProxy->handleExistingObjects();
}
catch (const sdbus::Error& e) {
if (e.getName() == "org.freedesktop.DBus.Error.ServiceUnknown") {
std::cout << "Waiting for server to start ..." << std::endl;
std::cout << "Waiting for server to start ..." << '\n';
}
}
connection->enterEventLoop();
return 0;
}
}
@@ -18,39 +18,52 @@
#include <memory>
#include <thread>
#include <chrono>
#include <utility>
#include <string>
#include <cstdint>
class ManagerAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
class ManagerAdaptor : public sdbus::AdaptorInterfaces<sdbus::ObjectManager_adaptor>
{
public:
ManagerAdaptor(sdbus::IConnection& connection, std::string path)
: AdaptorInterfaces(connection, std::move(path))
ManagerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path)
: AdaptorInterfaces(connection, std::move(path))
{
registerAdaptor();
}
ManagerAdaptor(const ManagerAdaptor&) = delete;
ManagerAdaptor& operator=(const ManagerAdaptor&) = delete;
ManagerAdaptor(ManagerAdaptor&&) = delete;
ManagerAdaptor& operator=(ManagerAdaptor&&) = delete;
~ManagerAdaptor()
{
unregisterAdaptor();
}
};
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, std::string path, std::string name, uint64_t poulation)
: AdaptorInterfaces(connection, std::move(path))
, m_name(std::move(name))
, m_population(poulation)
PlanetAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path, std::string name, uint64_t population)
: AdaptorInterfaces(connection, std::move(path))
, m_name(std::move(name))
, m_population(population)
{
registerAdaptor();
emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
emitInterfacesAddedSignal({sdbus::InterfaceName{Planet1_adaptor::INTERFACE_NAME}});
}
PlanetAdaptor(const PlanetAdaptor&) = delete;
PlanetAdaptor& operator=(const PlanetAdaptor&) = delete;
PlanetAdaptor(PlanetAdaptor&&) = delete;
PlanetAdaptor& operator=(PlanetAdaptor&&) = delete;
~PlanetAdaptor()
{
emitInterfacesRemovedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
emitInterfacesRemovedSignal({sdbus::InterfaceName{Planet1_adaptor::INTERFACE_NAME}});
unregisterAdaptor();
}
@@ -76,24 +89,26 @@ void printCountDown(const std::string& message, int seconds)
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << i << " " << std::flush;
}
std::cout << std::endl;
std::cout << '\n';
}
int main()
{
auto connection = sdbus::createSessionBusConnection();
connection->requestName("org.sdbuscpp.examplemanager");
const sdbus::ServiceName serviceName{"org.sdbuscpp.examplemanager"};
connection->requestName(serviceName);
connection->enterEventLoopAsync();
auto manager = std::make_unique<ManagerAdaptor>(*connection, "/org/sdbuscpp/examplemanager");
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
auto manager = std::make_unique<ManagerAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager"});
while (true)
{
printCountDown("Creating PlanetAdaptor in ", 5);
auto earth = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Earth", "Earth", 7'874'965'825);
auto earth = std::make_unique<PlanetAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Earth"}, "Earth", 7'874'965'825);
printCountDown("Creating PlanetAdaptor in ", 5);
auto trantor = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Trantor", "Trantor", 40'000'000'000);
auto trantor = std::make_unique<PlanetAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Trantor"}, "Trantor", 40'000'000'000);
printCountDown("Creating PlanetAdaptor in ", 5);
auto laconia = std::make_unique<PlanetAdaptor>(*connection, "/org/sdbuscpp/examplemanager/Planet1/Laconia", "Laconia", 231'721);
auto laconia = std::make_unique<PlanetAdaptor>(*connection, sdbus::ObjectPath{"/org/sdbuscpp/examplemanager/Planet1/Laconia"}, "Laconia", 231'721);
printCountDown("Removing PlanetAdaptor in ", 5);
earth.reset();
printCountDown("Removing PlanetAdaptor in ", 5);
@@ -102,7 +117,7 @@ int main()
laconia.reset();
}
connection->releaseName("org.sdbuscpp.examplemanager");
connection->releaseName(serviceName);
connection->leaveEventLoop();
return 0;
}
}
+25 -26
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorInterfaces.h
*
@@ -35,7 +35,7 @@
// Forward declarations
namespace sdbus {
class IConnection;
}
} // namespace sdbus
namespace sdbus {
@@ -50,18 +50,18 @@ namespace sdbus {
class ObjectHolder
{
protected:
ObjectHolder(std::unique_ptr<IObject>&& object)
explicit ObjectHolder(std::unique_ptr<IObject>&& object)
: object_(std::move(object))
{
}
const IObject& getObject() const
[[nodiscard]] const IObject& getObject() const
{
assert(object_ != nullptr);
return *object_;
}
IObject& getObject()
[[nodiscard]] IObject& getObject()
{
assert(object_ != nullptr);
return *object_;
@@ -82,17 +82,23 @@ 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, it is necessary to finish
* adaptor registration in class constructor (finishRegistration();`), and, conversely,
* unregister the adaptor in class destructor (`unregister();`).
* In the final adaptor class inherited from AdaptorInterfaces, one needs to make sure:
* 1. to call `registerAdaptor();` in the class constructor, and, conversely,
* 2. to call `unregisterAdaptor();` in the class destructor,
* so that the object API vtable is registered and unregistered at the proper time.
*
***********************************************/
template <typename... _Interfaces>
template <typename... Interfaces>
class AdaptorInterfaces
: protected ObjectHolder
, public _Interfaces...
, public Interfaces...
{
public:
AdaptorInterfaces(const AdaptorInterfaces&) = delete;
AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete;
AdaptorInterfaces(AdaptorInterfaces&&) = delete;
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = delete;
/*!
* @brief Creates object instance
*
@@ -101,26 +107,26 @@ namespace sdbus {
*
* For more information, consult @ref createObject(sdbus::IConnection&,std::string)
*/
AdaptorInterfaces(IConnection& connection, std::string objectPath)
AdaptorInterfaces(IConnection& connection, ObjectPath objectPath)
: ObjectHolder(createObject(connection, std::move(objectPath)))
, _Interfaces(getObject())...
, Interfaces(getObject())...
{
}
/*!
* @brief Finishes adaptor API registration and publishes the adaptor on the bus
* @brief Adds object vtable (i.e. D-Bus API) definitions for all interfaces it implements
*
* This function must be called in the constructor of the final adaptor class that implements AdaptorInterfaces.
*
* For more information, see underlying @ref IObject::finishRegistration()
* See also @ref IObject::addVTable()
*/
void registerAdaptor()
{
getObject().finishRegistration();
(Interfaces::registerAdaptor(), ...);
}
/*!
* @brief Unregisters adaptors's API and removes it from the bus
* @brief Unregisters adaptor's API and removes it from the bus
*
* This function must be called in the destructor of the final adaptor class that implements AdaptorInterfaces.
*
@@ -132,23 +138,16 @@ namespace sdbus {
}
/*!
* @brief Returns object path of the underlying DBus object
* @brief Returns reference to the underlying IObject instance
*/
const std::string& getObjectPath() const
{
return getObject().getObjectPath();
}
using ObjectHolder::getObject;
protected:
using base_type = AdaptorInterfaces;
AdaptorInterfaces(const AdaptorInterfaces&) = delete;
AdaptorInterfaces& operator=(const AdaptorInterfaces&) = delete;
AdaptorInterfaces(AdaptorInterfaces&&) = default;
AdaptorInterfaces& operator=(AdaptorInterfaces&&) = default;
~AdaptorInterfaces() = default;
};
}
} // namespace sdbus
#endif /* SDBUS_CXX_ADAPTORINTERFACES_H_ */
+158
View File
@@ -0,0 +1,158 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2026 - Alex Cani <alexcani109@gmail.com>
*
* @file Awaitable.h
*
* Created on: Feb 28, 2026
* 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_AWAITABLE_H_
#define SDBUS_CXX_AWAITABLE_H_
#include <atomic>
#include <cassert>
#if __has_include(<coroutine>)
#include <coroutine>
#endif
#include <cstdint>
#include <exception>
#include <memory>
#include <type_traits>
#include <variant>
namespace sdbus {
// Forward declarations
class AsyncMethodInvoker;
namespace internal {
class Proxy;
} // namespace internal
/********************************************//**
* @enum AwaitableState
*
* Represents the lifecycle state of an asynchronous
* operation in the coroutine awaitable protocol.
* Used for atomic coordination between the coroutine
* and the D-Bus callback thread.
*
***********************************************/
enum class AwaitableState : uint8_t
{
NotReady, // Initial state: callback hasn't fired yet
Waiting, // Coroutine is suspended and waiting for callback
Completed // Callback completed, result is ready
};
// Shared data
template <typename T>
struct AwaitableData
{
using result_type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
std::variant<result_type, std::exception_ptr> result;
std::atomic<AwaitableState> status{AwaitableState::NotReady};
#ifdef __cpp_lib_coroutine
// Keep the handle as the last member to mainting ABI compatibility
// with clients without coroutine support.
std::coroutine_handle<> handle;
#endif // __cpp_lib_coroutine
void resumeCoroutine()
{
#ifdef __cpp_lib_coroutine
handle.resume();
#endif // __cpp_lib_coroutine
}
};
/********************************************//**
* @class Awaitable
*
* A C++20 coroutine awaitable that represents an asynchronous
* operation. Allows suspending a coroutine until a D-Bus method
* call completes, then resuming with the result or exception.
*
* This is not a full-fledged coroutine type, but a simple awaitable
* that can be used with `co_await` to retrieve results of async D-Bus calls.
* This is independent of any specific coroutine framework or scheduler,
* as it relies on the D-Bus callback mechanism to resume the coroutine.
*
* You most likely don't need to use this class directly. Instead, use the
* respective low-level or high-level API functions that return an Awaitable
* instance, such as IProxy::callMethodAsync with with_awaitable_t tag,
* or the .getResultAsAwaitable() methods of the high-level API.
*
* The class represents nothing, i.e. is a simple placeholder class, if the API
* is used as C++17 or with a standard library not supporting coroutines.
*
***********************************************/
template <typename T>
class Awaitable
{
#ifdef __cpp_lib_coroutine
public:
// Called when the coroutine is co_await'ed. Returns true if the coroutine should be suspended.
[[nodiscard]] bool await_ready() const noexcept
{
return data_->status.load(std::memory_order_acquire) == AwaitableState::Completed;
}
// Called when the coroutine is suspended, returning false here will immediately
// resume the coroutine.
bool await_suspend(std::coroutine_handle<> handle) noexcept
{
data_->handle = handle;
// Attempt transition from NotReady to Waiting.
AwaitableState expected = AwaitableState::NotReady;
return data_->status.compare_exchange_strong(expected, AwaitableState::Waiting, std::memory_order_acq_rel);
}
// Called when the coroutine is resumed. Returns the result or throws the exception.
[[nodiscard]] T await_resume() const
{
if (auto* exception = std::get_if<std::exception_ptr>(&data_->result); exception != nullptr)
std::rethrow_exception(*exception);
if constexpr (std::is_void_v<T>)
return;
else
return std::get<T>(std::move(data_->result));
}
#endif // __cpp_lib_coroutine
private:
friend internal::Proxy;
friend AsyncMethodInvoker;
explicit Awaitable(std::shared_ptr<AwaitableData<T>> data)
: data_(std::move(data))
{
assert(data_ != nullptr);
}
std::shared_ptr<AwaitableData<T>> data_;
};
} // namespace sdbus
#endif // SDBUS_CXX_AWAITABLE_H_
+144 -172
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ConvenienceApiClasses.h
*
@@ -27,16 +27,19 @@
#ifndef SDBUS_CXX_CONVENIENCEAPICLASSES_H_
#define SDBUS_CXX_CONVENIENCEAPICLASSES_H_
#include <sdbus-c++/Awaitable.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Types.h>
#include <sdbus-c++/Flags.h>
#include <string>
#include <vector>
#include <type_traits>
#include <sdbus-c++/VTableItems.h>
#include <chrono>
#include <future>
#include <cstdint>
#include <future>
#include <map>
#include <string>
#include <string_view>
#include <vector>
// Forward declarations
namespace sdbus {
@@ -44,119 +47,47 @@ namespace sdbus {
class IProxy;
class Error;
class PendingAsyncCall;
}
} // namespace sdbus
namespace sdbus {
class MethodRegistrator
class VTableAdder
{
public:
MethodRegistrator(IObject& object, std::string methodName);
MethodRegistrator(MethodRegistrator&& other) = default;
~MethodRegistrator() noexcept(false);
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();
void forInterface(InterfaceName interfaceName);
void forInterface(std::string interfaceName);
[[nodiscard]] Slot forInterface(InterfaceName interfaceName, return_slot_t);
[[nodiscard]] Slot forInterface(std::string interfaceName, return_slot_t);
private:
IObject& object_;
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
};
friend IObject;
VTableAdder(IObject& object, std::vector<VTableItem> vtable);
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
IObject& object_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
std::vector<VTableItem> vtable_;
};
class SignalEmitter
{
public:
SignalEmitter(IObject& object, const std::string& signalName);
SignalEmitter(SignalEmitter&& other) = default;
~SignalEmitter() noexcept(false);
SignalEmitter& onInterface(const InterfaceName& interfaceName);
SignalEmitter& onInterface(const std::string& interfaceName);
template <typename... _Args> void withArguments(_Args&&... args);
SignalEmitter& onInterface(const char* interfaceName);
template <typename... Args> void withArguments(Args&&... args);
SignalEmitter(const SignalEmitter&) = delete;
SignalEmitter& operator=(const SignalEmitter&) = delete;
SignalEmitter(SignalEmitter&& other) = default;
SignalEmitter& operator=(SignalEmitter&&) = delete;
~SignalEmitter() noexcept(false);
private:
IObject& object_;
const std::string& signalName_;
friend IObject;
SignalEmitter(IObject& object, const SignalName& signalName);
SignalEmitter(IObject& object, const char* signalName);
IObject& object_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
const char* signalName_;
Signal signal_;
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
};
@@ -164,22 +95,29 @@ namespace sdbus {
class MethodInvoker
{
public:
MethodInvoker(IProxy& proxy, const std::string& methodName);
MethodInvoker(MethodInvoker&& other) = default;
~MethodInvoker() noexcept(false);
MethodInvoker& onInterface(const InterfaceName& interfaceName);
MethodInvoker& onInterface(const std::string& interfaceName);
MethodInvoker& onInterface(const char* interfaceName);
MethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
MethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
template <typename... _Args> void storeResultsTo(_Args&... args);
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(const MethodInvoker&) = delete;
MethodInvoker& operator=(const MethodInvoker&) = delete;
MethodInvoker(MethodInvoker&& other) = default;
MethodInvoker& operator=(MethodInvoker&&) = delete;
~MethodInvoker() noexcept(false);
private:
IProxy& proxy_;
const std::string& methodName_;
friend IProxy;
MethodInvoker(IProxy& proxy, const MethodName& methodName);
MethodInvoker(IProxy& proxy, const char* methodName);
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
const char* methodName_;
uint64_t timeout_{};
MethodCall method_;
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
@@ -189,21 +127,29 @@ namespace sdbus {
class AsyncMethodInvoker
{
public:
AsyncMethodInvoker(IProxy& proxy, const std::string& methodName);
AsyncMethodInvoker& onInterface(const InterfaceName& interfaceName);
AsyncMethodInvoker& onInterface(const std::string& interfaceName);
AsyncMethodInvoker& onInterface(const char* interfaceName);
AsyncMethodInvoker& withTimeout(uint64_t usec);
template <typename _Rep, typename _Period>
AsyncMethodInvoker& withTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename... _Args> AsyncMethodInvoker& withArguments(_Args&&... args);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
template <typename 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();
template <typename... Args> std::future<future_return_t<Args...>> getResultAsFuture();
template <typename... Args> Awaitable<awaitable_return_t<Args...>> getResultAsAwaitable();
private:
IProxy& proxy_;
const std::string& methodName_;
friend IProxy;
AsyncMethodInvoker(IProxy& proxy, const MethodName& methodName);
AsyncMethodInvoker(IProxy& proxy, const char* methodName);
template <typename Function> async_reply_handler makeAsyncReplyHandler(Function&& callback);
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
const char* methodName_;
uint64_t timeout_{};
MethodCall method_;
};
@@ -211,108 +157,134 @@ namespace sdbus {
class SignalSubscriber
{
public:
SignalSubscriber(IProxy& proxy, const std::string& signalName);
SignalSubscriber& onInterface(std::string interfaceName);
template <typename _Function> void call(_Function&& callback);
SignalSubscriber& onInterface(const InterfaceName& interfaceName);
SignalSubscriber& onInterface(const std::string& interfaceName);
SignalSubscriber& onInterface(const char* interfaceName);
template <typename Function> void call(Function&& callback);
template <typename Function> [[nodiscard]] Slot call(Function&& callback, return_slot_t);
private:
IProxy& proxy_;
const std::string& signalName_;
std::string interfaceName_;
};
friend IProxy;
SignalSubscriber(IProxy& proxy, const SignalName& signalName);
SignalSubscriber(IProxy& proxy, const char* signalName);
template <typename Function> signal_handler makeSignalHandler(Function&& callback);
class SignalUnsubscriber
{
public:
SignalUnsubscriber(IProxy& proxy, const std::string& signalName);
void onInterface(const std::string& interfaceName);
private:
IProxy& proxy_;
const std::string& signalName_;
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
const char* signalName_;
const char* interfaceName_{};
};
class PropertyGetter
{
public:
PropertyGetter(IProxy& proxy, const std::string& propertyName);
Variant onInterface(const std::string& interfaceName);
Variant onInterface(std::string_view interfaceName);
private:
IProxy& proxy_;
const std::string& propertyName_;
friend IProxy;
PropertyGetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
std::string_view propertyName_;
};
class AsyncPropertyGetter
{
public:
AsyncPropertyGetter(IProxy& proxy, const std::string& propertyName);
AsyncPropertyGetter& onInterface(const std::string& interfaceName);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
AsyncPropertyGetter& onInterface(std::string_view interfaceName);
template <typename Function> PendingAsyncCall uponReplyInvoke(Function&& callback);
template <typename Function> [[nodiscard]] Slot uponReplyInvoke(Function&& callback, return_slot_t);
std::future<Variant> getResultAsFuture();
Awaitable<Variant> getResultAsAwaitable();
private:
IProxy& proxy_;
const std::string& propertyName_;
const std::string* interfaceName_{};
friend IProxy;
AsyncPropertyGetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
std::string_view propertyName_;
std::string_view interfaceName_;
};
class PropertySetter
{
public:
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);
PropertySetter& onInterface(std::string_view interfaceName);
template <typename Value> void toValue(const Value& value);
template <typename Value> void toValue(const Value& value, dont_expect_reply_t);
void toValue(const Variant& value);
void toValue(const Variant& value, dont_expect_reply_t);
private:
IProxy& proxy_;
const std::string& propertyName_;
const std::string* interfaceName_{};
friend IProxy;
PropertySetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
std::string_view propertyName_;
std::string_view interfaceName_;
};
class AsyncPropertySetter
{
public:
AsyncPropertySetter(IProxy& proxy, const std::string& propertyName);
AsyncPropertySetter& onInterface(const std::string& interfaceName);
template <typename _Value> AsyncPropertySetter& toValue(_Value&& value);
AsyncPropertySetter& onInterface(std::string_view interfaceName);
template <typename Value> AsyncPropertySetter& toValue(Value&& value);
AsyncPropertySetter& toValue(Variant value);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
template <typename Function> PendingAsyncCall uponReplyInvoke(Function&& callback);
template <typename Function> [[nodiscard]] Slot uponReplyInvoke(Function&& callback, return_slot_t);
std::future<void> getResultAsFuture();
Awaitable<void> getResultAsAwaitable();
private:
IProxy& proxy_;
const std::string& propertyName_;
const std::string* interfaceName_{};
friend IProxy;
AsyncPropertySetter(IProxy& proxy, std::string_view propertyName);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
std::string_view propertyName_;
std::string_view interfaceName_;
Variant value_;
};
class AllPropertiesGetter
{
public:
AllPropertiesGetter(IProxy& proxy);
std::map<std::string, Variant> onInterface(const std::string& interfaceName);
std::map<PropertyName, Variant> onInterface(std::string_view interfaceName);
private:
IProxy& proxy_;
friend IProxy;
explicit AllPropertiesGetter(IProxy& proxy);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
};
class AsyncAllPropertiesGetter
{
public:
AsyncAllPropertiesGetter(IProxy& proxy);
AsyncAllPropertiesGetter& onInterface(const std::string& interfaceName);
template <typename _Function> PendingAsyncCall uponReplyInvoke(_Function&& callback);
std::future<std::map<std::string, Variant>> getResultAsFuture();
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();
Awaitable<std::map<PropertyName, Variant>> getResultAsAwaitable();
private:
IProxy& proxy_;
const std::string* interfaceName_{};
friend IProxy;
explicit AsyncAllPropertiesGetter(IProxy& proxy);
static constexpr const char* DBUS_PROPERTIES_INTERFACE_NAME = "org.freedesktop.DBus.Properties";
IProxy& proxy_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
std::string_view interfaceName_;
};
}
} // namespace sdbus
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */
File diff suppressed because it is too large Load Diff
+32 -16
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Error.h
*
@@ -27,7 +27,7 @@
#ifndef SDBUS_CXX_ERROR_H_
#define SDBUS_CXX_ERROR_H_
#include <errno.h>
#include <cerrno>
#include <stdexcept>
#include <string>
@@ -43,47 +43,63 @@ namespace sdbus {
: public std::runtime_error
{
public:
explicit Error(const std::string& name, const char* message = nullptr)
: Error(name, std::string(message ? message : ""))
// Strong type representing the D-Bus error name
class Name : public std::string
{
public:
Name() = default;
explicit Name(std::string value)
: std::string(std::move(value))
{}
explicit Name(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
explicit Error(Name name, const char* message = nullptr)
: Error(std::move(name), std::string(message ? message : ""))
{
}
Error(const std::string& name, const std::string& message)
: std::runtime_error("[" + name + "] " + message)
, name_(name)
, message_(message)
Error(Name name, std::string message)
: std::runtime_error(!message.empty() ? "[" + name + "] " + message : "[" + name + "]")
, name_(std::move(name))
, message_(std::move(message))
{
}
const std::string& getName() const
[[nodiscard]] const Name& getName() const
{
return name_;
}
const std::string& getMessage() const
[[nodiscard]] const std::string& getMessage() const
{
return message_;
}
bool isValid() const
[[nodiscard]] bool isValid() const
{
return !getName().empty();
}
private:
std::string name_;
Name name_;
std::string message_;
};
sdbus::Error createError(int errNo, const std::string& customMsg);
Error createError(int errNo, std::string customMsg = {});
inline const char* SDBUSCPP_ERROR_NAME = "org.sdbuscpp.Error";
}
inline const Error::Name SDBUSCPP_ERROR_NAME{"org.sdbuscpp.Error"};
} // namespace sdbus
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
throw sdbus::createError((_ERRNO), (_MSG)) \
/**/
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \
if (!(_COND)) ; else SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \
/**/
+9 -9
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Flags.h
*
@@ -74,26 +74,26 @@ namespace sdbus {
flags_.set(flag, value);
}
bool test(GeneralFlags flag) const
[[nodiscard]] bool test(GeneralFlags flag) const
{
return flags_.test(flag);
}
bool test(PropertyUpdateBehaviorFlags flag) const
[[nodiscard]] bool test(PropertyUpdateBehaviorFlags flag) const
{
return flags_.test(flag);
}
uint64_t toSdBusInterfaceFlags() const;
uint64_t toSdBusMethodFlags() const;
uint64_t toSdBusSignalFlags() const;
uint64_t toSdBusPropertyFlags() const;
uint64_t toSdBusWritablePropertyFlags() const;
[[nodiscard]] uint64_t toSdBusInterfaceFlags() const;
[[nodiscard]] uint64_t toSdBusMethodFlags() const;
[[nodiscard]] uint64_t toSdBusSignalFlags() const;
[[nodiscard]] uint64_t toSdBusPropertyFlags() const;
[[nodiscard]] uint64_t toSdBusWritablePropertyFlags() const;
private:
std::bitset<FLAG_COUNT> flags_;
};
}
} // namespace sdbus
#endif /* SDBUS_CXX_FLAGS_H_ */
+234 -222
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file IConnection.h
*
@@ -28,13 +28,22 @@
#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
namespace sdbus {
@@ -51,90 +60,10 @@ namespace sdbus {
class IConnection
{
public:
/*!
* 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;
};
struct PollData;
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
*
@@ -163,61 +92,103 @@ namespace sdbus {
virtual void leaveEventLoop() = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
* @brief Attaches the bus connection to a sd-event event loop
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* This call creates a floating registration. The ObjectManager will
* be there for the object path until the connection is destroyed.
*
* Another, recommended way to add object managers is directly through
* IObject API.
* @param[in] event sd-event event loop object
* @param[in] priority Specified priority
*
* @throws sdbus::Error in case of failure
*
* See `man sd_bus_attach_event'.
*/
[[deprecated("Use one of other addObjectManager overloads")]] virtual void addObjectManager(const std::string& objectPath) = 0;
virtual void attachSdEventLoop(sd_event *event, int priority = 0) = 0;
/*!
* @brief Returns fd, I/O events and timeout data you can pass to poll
*
* To integrate sdbus with your app's own custom event handling system
* (without the requirement of an extra thread), you can use this
* method to query which file descriptors, poll events and timeouts you
* should add to your app's poll call in your main event loop. If these
* file descriptors signal, then you should call processPendingRequest
* to process the event. This means that all of sdbus's callbacks will
* arrive on your app's main event thread (opposed to on a thread created
* by sdbus-c++). If you are unsure what this all means then use
* enterEventLoop() or enterEventLoopAsync() instead.
*
* To integrate sdbus-c++ into a gtk app, pass the file descriptor returned
* by this method to g_main_context_add_poll.
* @brief Detaches the bus connection from a sd-event event loop
*
* @throws sdbus::Error in case of failure
*/
virtual PollData getEventLoopPollData() const = 0;
virtual void detachSdEventLoop() = 0;
/*!
* @brief Process a pending request
* @brief Gets current sd-event event loop for the bus connection
*
* @returns true if an event was processed, false if poll should be called
* @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
*
* 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.
* 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 a sd-event event loop, use
* attachSdEventLoop() function.
*
* @throws sdbus::Error in case of failure
*/
virtual bool processPendingRequest() = 0;
[[nodiscard]] virtual PollData getEventLoopPollData() const = 0;
/*!
* @brief Processes a pending event
*
* @returns True if an event was processed, false if no operations were pending
*
* This function drives the D-Bus connection. It processes pending I/O events.
* Queued outgoing messages (or parts thereof) are sent out. Queued incoming
* messages are dispatched to registered callbacks. Timeouts are recalculated.
*
* It returns false when no operations were pending and true if a message was
* processed. When false is returned the caller should synchronously poll for
* I/O events before calling into processPendingEvent() again.
* Don't forget to call getEventLoopPollData() each time before the next poll.
*
* You don't need to directly call this method or getEventLoopPollData() method
* when using convenient, internal bus connection event loops through
* enterEventLoop() or enterEventLoopAsync() calls, or when the bus is
* connected to a sd-event event loop through attachSdEventLoop().
* It is invoked automatically when necessary.
*
* @throws sdbus::Error in case of failure
*/
virtual bool processPendingEvent() = 0;
/*!
* @brief Provides access to the currently processed D-Bus message
*
* This method provides access to the currently processed incoming D-Bus message.
* "Currently processed" means that the registered callback handler(s) for that message
* are being invoked. This method is meant to be called from within a callback handler
* (e.g. from a D-Bus signal handler, or async method reply handler, etc.). In such a case it is
* guaranteed to return a valid D-Bus message instance for which the handler is called.
* If called from other contexts/threads, it may return a valid or invalid message, depending
* on whether a message was processed or not at the time of the call.
*
* @return Currently processed D-Bus message
*/
[[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0;
/*!
* @brief Sets general method call timeout
@@ -236,8 +207,8 @@ namespace sdbus {
/*!
* @copydoc IConnection::setMethodCallTimeout(uint64_t)
*/
template <typename _Rep, typename _Period>
void setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout);
template <typename Rep, typename Period>
void setMethodCallTimeout(const std::chrono::duration<Rep, Period>& timeout);
/*!
* @brief Gets general method call timeout
@@ -248,10 +219,11 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
virtual uint64_t getMethodCallTimeout() const = 0;
[[nodiscard]] virtual uint64_t getMethodCallTimeout() const = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
@@ -260,12 +232,52 @@ 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 std::string& objectPath, floating_slot_t) = 0;
virtual void addObjectManager(const ObjectPath& objectPath) = 0;
/*!
* @brief Adds an ObjectManager at the specified D-Bus object path
* @param[in] objectPath Object path at which the ObjectManager interface shall be installed
* @return Slot handle owning the registration
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* This call returns an owning slot. The lifetime of the ObjectManager
* interface is bound to the lifetime of the returned slot instance.
*
* Another, recommended way to add object managers is directly through IObject API.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) = 0;
/*!
* @brief Installs a floating match rule for messages received on this bus connection
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
*
* The method installs a match rule for messages received on the specified bus connection.
* The syntax of the match rule expression passed in match is described in the D-Bus specification.
* The specified handler function callback is called for each incoming message matching the specified
* expression. The match is installed synchronously when connected to a bus broker, i.e. the call
* sends a control message requesting the match to be added to the broker and waits until the broker
* confirms the match has been installed successfully.
*
* The method installs a floating match rule for messages received on the specified bus connection.
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
* is bound to the lifetime of the bus connection.
*
* For more information, consult `man sd_bus_add_match`.
*
* @throws sdbus::Error in case of failure
*/
virtual void addMatch(const std::string& match, message_handler callback) = 0;
/*!
* @brief Installs a match rule for messages received on this bus connection
@@ -278,34 +290,40 @@ 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 requested the match to be added to the broker and waits until the broker
* 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.
*
* 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.
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
* the slot instance implies uninstalling of the match rule from the bus connection.
*
* For more information, consult `man sd_bus_add_match`.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback) = 0;
[[nodiscard]] virtual Slot addMatch(const std::string& match, message_handler callback, return_slot_t) = 0;
/*!
* @brief Installs a floating match rule for messages received on this bus connection
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
*
* This method operates the same as `addMatch()` above, just that it installs the match rule asynchronously,
* in a non-blocking fashion. A request is sent to the broker, but the call does not wait for a response.
* The `installCallback' callable is called when the response is later received, with the response message
* from the broker as parameter. If it's an empty function object, a default implementation is used that
* terminates the bus connection should installing the match fail.
*
* The method installs a floating match rule for messages received on the specified bus connection.
* Floating means that the bus connection object owns the match rule, i.e. lifetime of the match rule
* is bound to the lifetime of the bus connection.
*
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation for more
* information.
* For more information, consult `man sd_bus_add_match_async`.
*
* @throws sdbus::Error in case of failure
*/
virtual void addMatch(const std::string& match, message_handler callback, floating_slot_t) = 0;
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
/*!
* @brief Asynchronously installs a match rule for messages received on this bus connection
@@ -321,106 +339,100 @@ 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.
*
* Refer to the @c addMatch(const std::string& match, message_handler callback) documentation, and consult
* `man sd_bus_add_match`, for more information.
* The lifetime of the match rule is bound to the lifetime of the returned slot instance. Destroying
* the slot instance implies the uninstalling of the match rule from the bus connection.
*
* For more information, consult `man sd_bus_add_match_async`.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback) = 0;
[[nodiscard]] virtual Slot addMatchAsync( const std::string& match
, message_handler callback
, message_handler installCallback
, return_slot_t ) = 0;
/*!
* @brief Asynchronously installs a floating match rule for messages received on this bus connection
*
* @param[in] match Match expression to filter incoming D-Bus message
* @param[in] callback Callback handler to be called upon processing an inbound D-Bus message matching the rule
* @param[in] installCallback Callback handler to be called upon processing an inbound D-Bus message matching the rule
*
* 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.
* @brief Retrieves the unique name of a connection. E.g. ":1.xx"
*
* @throws sdbus::Error in case of failure
*/
virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) = 0;
[[nodiscard]] virtual BusName getUniqueName() const = 0;
/*!
* @copydoc IConnection::enterEventLoop()
* @brief Requests a well-known D-Bus service name on a bus
*
* @deprecated This function has been replaced by enterEventLoop()
* @param[in] name Name to request
*
* @throws sdbus::Error in case of failure
*/
[[deprecated("This function has been replaced by enterEventLoop()")]] void enterProcessingLoop();
virtual void requestName(const ServiceName& name) = 0;
/*!
* @copydoc IConnection::enterEventLoopAsync()
* @brief Releases an acquired well-known D-Bus service name on a bus
*
* @deprecated This function has been replaced by enterEventLoopAsync()
* @param[in] name Name to release
*
* @throws sdbus::Error in case of failure
*/
[[deprecated("This function has been replaced by enterEventLoopAsync()")]] void enterProcessingLoopAsync();
virtual void releaseName(const ServiceName& name) = 0;
/*!
* @copydoc IConnection::leaveEventLoop()
* @struct PollData
*
* @deprecated This function has been replaced by 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()")]] void leaveProcessingLoop();
struct PollData
{
/*!
* The read fd to be monitored by the event loop.
*/
int fd;
/*!
* @copydoc IConnection::getEventLoopPollData()
*
* @deprecated This function has been replaced by getEventLoopPollData()
*/
[[deprecated("This function has been replaced by getEventLoopPollData()")]] PollData getProcessLoopPollData() const;
/*!
* 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;
};
};
template <typename _Rep, typename _Period>
inline void IConnection::setMethodCallTimeout(const std::chrono::duration<_Rep, _Period>& timeout)
template <typename Rep, typename Period>
inline void IConnection::setMethodCallTimeout(const std::chrono::duration<Rep, Period>& timeout)
{
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
return setMethodCallTimeout(microsecs.count());
}
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.
*
@@ -428,7 +440,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection();
[[nodiscard]] std::unique_ptr<IConnection> createBusConnection();
/*!
* @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.
@@ -438,7 +450,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDefaultBusConnection(const std::string& name);
[[nodiscard]] std::unique_ptr<IConnection> createBusConnection(const ServiceName& name);
/*!
* @brief Creates/opens D-Bus system bus connection
@@ -447,7 +459,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
[[nodiscard]] std::unique_ptr<IConnection> createSystemBusConnection();
/*!
* @brief Creates/opens D-Bus system bus connection with a name
@@ -457,7 +469,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
[[nodiscard]] std::unique_ptr<IConnection> createSystemBusConnection(const ServiceName& name);
/*!
* @brief Creates/opens D-Bus session bus connection
@@ -466,7 +478,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
[[nodiscard]] std::unique_ptr<IConnection> createSessionBusConnection();
/*!
* @brief Creates/opens D-Bus session bus connection with a name
@@ -476,7 +488,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
[[nodiscard]] std::unique_ptr<IConnection> createSessionBusConnection(const ServiceName& name);
/*!
* @brief Creates/opens D-Bus session bus connection at a custom address
@@ -488,7 +500,7 @@ namespace sdbus {
*
* Consult manual pages for `sd_bus_set_address` of the underlying sd-bus library for more information.
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createSessionBusConnectionWithAddress(const std::string& address);
[[nodiscard]] std::unique_ptr<IConnection> createSessionBusConnectionWithAddress(const std::string& address);
/*!
* @brief Creates/opens D-Bus system connection on a remote host using ssh
@@ -498,7 +510,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createRemoteSystemBusConnection(const std::string& host);
[[nodiscard]] std::unique_ptr<IConnection> createRemoteSystemBusConnection(const std::string& host);
/*!
* @brief Opens direct D-Bus connection at a custom address
@@ -508,7 +520,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDirectBusConnection(const std::string& address);
[[nodiscard]] std::unique_ptr<IConnection> createDirectBusConnection(const std::string& address);
/*!
* @brief Opens direct D-Bus connection at the given file descriptor
@@ -521,7 +533,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createDirectBusConnection(int fd);
[[nodiscard]] std::unique_ptr<IConnection> createDirectBusConnection(int fd);
/*!
* @brief Opens direct D-Bus connection at fd as a server
@@ -537,7 +549,7 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createServerBus(int fd);
[[nodiscard]] std::unique_ptr<IConnection> createServerBus(int fd);
/*!
* @brief Creates sdbus-c++ bus connection representation out of underlying sd_bus instance
@@ -566,7 +578,7 @@ namespace sdbus {
* auto con = sdbus::createBusConnection(bus); // IConnection consumes sd_bus object
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IConnection> createBusConnection(sd_bus *bus);
}
[[nodiscard]] std::unique_ptr<IConnection> createBusConnection(sd_bus *bus);
} // namespace sdbus
#endif /* SDBUS_CXX_ICONNECTION_H_ */
+277 -305
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file IObject.h
*
@@ -28,18 +28,21 @@
#define SDBUS_CXX_IOBJECT_H_
#include <sdbus-c++/ConvenienceApiClasses.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Flags.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/VTableItems.h>
#include <functional>
#include <string>
#include <memory>
#include <string>
#include <vector>
// Forward declarations
namespace sdbus {
class Signal;
class IConnection;
}
class ObjectPath;
} // namespace sdbus
namespace sdbus {
@@ -58,186 +61,108 @@ namespace sdbus {
***********************************************/
class IObject
{
public:
public: // High-level, convenience API
virtual ~IObject() = default;
/*!
* @brief Registers method that the object will provide on D-Bus
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @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)
* @param[in] vtable Individual instances of VTable item structures stored in a vector
* @return VTableAdder high-level helper class
*
* This method is used to declare attributes for the object under the given interface.
* Parameter `vtable' represents a vtable definition that may contain method declarations
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
* struct), signal declarations (using SignalVTableItem struct), or global interface
* flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime.
*
* When called like `addVTable(vtable).forInterface(interface)`, then an internal registration
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
* can be added or removed by the user at runtime.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
virtual void registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags = {} ) = 0;
[[nodiscard]] VTableAdder addVTable(std::vector<VTableItem> vtable);
/*!
* @brief Registers method that the object will provide on D-Bus
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @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)
* @param[in] items Individual instances of VTable item structures
* @return VTableAdder high-level helper class
*
* 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.
* This method is used to declare attributes for the object under the given interface.
* Parameter pack contains vtable definition that may contain method declarations
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
* struct), signal declarations (using SignalVTableItem struct), or global interface
* flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime.
*
* When called like `addVTable(items...).forInterface(interface)`, then an internal registration
* slot is created for that vtable and its lifetime is tied to the lifetime of the Object instance.
* When called like `addVTable(items...).forInterface(interface, sdbus::return_slot)`, then an internal
* registration slot is created for the vtable and is returned to the caller. Keeping the slot means
* keep the registration "alive". Destroying the slot means that the vtable is not needed anymore,
* and the vtable gets removed from the object. This allows for "dynamic" object API where vtables
* can be added or removed by the user at runtime.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
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;
template < typename... VTableItems
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> > // NOLINT(modernize-use-constraints): We are C++17 compatible at the moment
[[nodiscard]] VTableAdder addVTable(VTableItems&&... items);
/*!
* @brief Registers signal that the object will emit on D-Bus
* @brief Emits signal on D-Bus
*
* @param[in] interfaceName Name of an interface that the signal will fall under
* @param[in] signalName Name of the signal
* @param[in] signature D-Bus signature of signal parameters
* @param[in] flags D-Bus signal flags (deprecated)
* @return A helper object for convenient emission of signals
*
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
* from the D-Bus message concept. Signal arguments are automatically serialized
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
*
* Example of use:
* @code
* int arg1 = ...;
* double arg2 = ...;
* SignalName fooSignal{"fooSignal"};
* object_.emitSignal(fooSignal).onInterface("com.kistler.foo").withArguments(arg1, arg2);
* @endcode
*
* @throws sdbus::Error in case of failure
*/
virtual void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, Flags flags = {} ) = 0;
[[nodiscard]] SignalEmitter emitSignal(const SignalName& 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
* @copydoc IObject::emitSignal(const SignalName&)
*/
virtual void registerSignal( const std::string& interfaceName
, std::string signalName
, std::string signature
, const std::vector<std::string>& paramNames
, Flags flags = {} ) = 0;
[[nodiscard]] SignalEmitter emitSignal(const std::string& 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
* @copydoc IObject::emitSignal(const 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;
[[nodiscard]] SignalEmitter emitSignal(const char* signalName);
/*!
* @brief Emits PropertyChanged signal for specified properties under a given interface of this object path
@@ -247,7 +172,12 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) = 0;
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;
/*!
* @brief Emits PropertyChanged signal for all properties on a given interface of this object path
@@ -256,7 +186,12 @@ namespace sdbus {
*
* @throws sdbus::Error in case of failure
*/
virtual void emitPropertiesChangedSignal(const std::string& interfaceName) = 0;
virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName) = 0;
/*!
* @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&)
*/
virtual void emitPropertiesChangedSignal(const char* interfaceName) = 0;
/*!
* @brief Emits InterfacesAdded signal on this object path
@@ -276,14 +211,12 @@ 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. 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.
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
* object interfaces and their vtables, so this method now makes more sense.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) = 0;
virtual void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces) = 0;
/*!
* @brief Emits InterfacesRemoved signal on this object path
@@ -300,14 +233,12 @@ 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. 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.
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
* object interfaces and their vtables, so this method now makes more sense.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) = 0;
virtual void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces) = 0;
/*!
* @brief Adds an ObjectManager interface at the path of this D-Bus object
@@ -316,173 +247,213 @@ 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 Removes an ObjectManager interface from the path of this D-Bus object
* @brief Adds an ObjectManager interface at the path of this D-Bus object
*
* @return Slot handle owning the registration
*
* Creates an ObjectManager interface at the specified object path on
* the connection. This is a convenient way to interrogate a connection
* to see what objects it has.
*
* The lifetime of the ObjectManager interface is bound to the lifetime
* of the returned slot instance.
*
* @throws sdbus::Error in case of failure
*/
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;
[[nodiscard]] virtual Slot addObjectManager(return_slot_t) = 0;
/*!
* @brief Provides D-Bus connection used by the object
*
* @return Reference to the D-Bus connection
*/
virtual sdbus::IConnection& getConnection() const = 0;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @param[in] methodName Name of the method
* @return A helper object for convenient registration of the method
*
* This is a high-level, convenience way of registering D-Bus methods that abstracts
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
* in a message and D-Bus signatures automatically deduced from the parameters and return type
* of the provided native method implementation callback.
*
* Example of use:
* @code
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
* @endcode
*
* @throws sdbus::Error in case of failure
*/
[[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);
[[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0;
/*!
* @brief Returns object path of the underlying DBus object
*/
virtual const std::string& getObjectPath() const = 0;
[[nodiscard]] virtual const ObjectPath& getObjectPath() const = 0;
/*!
* @brief Provides currently processed D-Bus message
* @brief Provides access to the currently processed D-Bus message
*
* This method provides immutable access to the currently processed incoming 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. 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.
* (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 A pointer to the currently processed D-Bus message
* @return Currently processed D-Bus message
*/
virtual const Message* getCurrentlyProcessedMessage() const = 0;
[[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;
// Lower-level, message-based API
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @param[in] interfaceName Name of an interface the the vtable is registered for
* @param[in] items Individual instances of VTable item structures
*
* This method is used to declare attributes for the object under the given interface.
* Parameter `items' represents a vtable definition that may contain method declarations
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
* struct), signal declarations (using SignalVTableItem struct), or global interface
* flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime. For each vtable an internal
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
template < typename... VTableItems
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
void addVTable(InterfaceName interfaceName, VTableItems&&... items);
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @param[in] interfaceName Name of an interface the the vtable is registered for
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
*
* This method is used to declare attributes for the object under the given interface.
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
* property declarations (using PropertyVTableItem struct), signal declarations (using
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime. For each vtable an internal
* registration slot is created and its lifetime is tied to the lifetime of the Object instance.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
virtual void addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) = 0;
/*!
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
*
* @param[in] interfaceName Name of an interface the the vtable is registered for
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
*
* This method is used to declare attributes for the object under the given interface.
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
* property declarations (using PropertyVTableItem struct), signal declarations (using
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
*
* An interface can have any number of vtables attached to it.
*
* Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information.
*
* The method can be called at any time during object's lifetime. For each vtable an internal
* registration slot is created and is returned to the caller. The returned slot should be destroyed
* when the vtable is not needed anymore. This allows for "dynamic" object API where vtables
* can be added or removed by the user at runtime.
*
* The function provides strong exception guarantee. The state of the object remains
* unmodified in face of an exception.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) = 0;
/*!
* @brief Creates a signal message
*
* @param[in] interfaceName Name of an interface that the signal belongs under
* @param[in] signalName Name of the signal
* @return A signal message
*
* Serialize signal arguments into the returned message and emit the signal by passing
* the message with serialized arguments to the @c emitSignal function.
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
*
* @throws sdbus::Error in case of failure
*/
[[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const = 0;
/*!
* @brief Emits signal for this object path
*
* @param[in] message Signal message to be sent out
*
* Note: To avoid messing with messages, use higher-level API defined below.
*
* @throws sdbus::Error in case of failure
*/
virtual void emitSignal(const Signal& message) = 0;
protected: // Internal API for efficiency reasons used by high-level API helper classes
friend SignalEmitter;
[[nodiscard]] virtual Signal createSignal(const char* interfaceName, const char* signalName) const = 0;
};
// Out-of-line member definitions
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
inline SignalEmitter IObject::emitSignal(const SignalName& 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);
return {*this, signalName};
}
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
{
return SignalEmitter(*this, signalName);
return {*this, signalName.c_str()};
}
inline SignalEmitter IObject::emitSignal(const char* signalName)
{
return {*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 {*this, std::move(vtable)};
}
/*!
@@ -503,10 +474,11 @@ namespace sdbus {
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
* @endcode
*/
[[nodiscard]] std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
[[nodiscard]] std::unique_ptr<IObject> createObject(IConnection& connection, ObjectPath objectPath);
}
} // namespace sdbus
#include <sdbus-c++/ConvenienceApiClasses.inl>
#include <sdbus-c++/ConvenienceApiClasses.inl> // NOLINT(misc-header-include-cycle)
#include <sdbus-c++/VTableItems.inl>
#endif /* SDBUS_CXX_IOBJECT_H_ */
+564 -207
View File
File diff suppressed because it is too large Load Diff
+382 -222
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Message.h
*
@@ -27,36 +27,40 @@
#ifndef SDBUS_CXX_MESSAGE_H_
#define SDBUS_CXX_MESSAGE_H_
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Error.h>
#include <string>
#include <vector>
#include <sdbus-c++/TypeTraits.h>
#include <algorithm>
#include <array>
#if __cplusplus >= 202002L
#include <span>
#endif
#include <cassert>
#include <cstdint>
#include <cstring>
#include <functional>
#include <map>
#ifdef __has_include
# if __has_include(<span>)
# include <span>
# endif
#endif
#include <string>
#include <sys/types.h>
#include <unordered_map>
#include <utility>
#include <cstdint>
#include <cassert>
#include <functional>
#include <sys/types.h>
#include <algorithm>
#include <variant>
#include <vector>
// Forward declarations
namespace sdbus {
class Variant;
class ObjectPath;
class Signature;
template <typename... _ValueTypes> class Struct;
template <typename... ValueTypes> class Struct;
class UnixFd;
class MethodReply;
namespace internal {
class ISdBus;
class IConnection;
}
}
} // namespace internal
} // namespace sdbus
namespace sdbus {
@@ -69,13 +73,19 @@ namespace sdbus {
* Serialization and deserialization functions are provided for types supported
* by D-Bus.
*
* You don't need to work with this class directly if you use high-level APIs
* of @c IObject and @c IProxy.
* You mostly 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);
@@ -87,27 +97,34 @@ 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);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator<<(const std::span<_Element, _Extent>& items);
template <typename Element, typename Allocator>
Message& operator<<(const std::vector<Element, Allocator>& items);
template <typename Element, std::size_t Size>
Message& operator<<(const std::array<Element, Size>& items);
#ifdef __cpp_lib_span
template <typename Element, std::size_t Extent>
Message& operator<<(const std::span<Element, Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator<<(const Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
Message& operator<<(const std::tuple<_ValueTypes...>& item);
// NOLINTNEXTLINE(modernize-use-constraints): Public API is C++17-compatible
template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
Message& operator<<(const Enum& item);
template <typename Key, typename Value>
Message& operator<<(const DictEntry<Key, Value>& value);
template <typename Key, typename Value, typename Compare, typename Allocator>
Message& operator<<(const std::map<Key, Value, Compare, Allocator>& items);
template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
Message& operator<<(const std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items);
template <typename... ValueTypes>
Message& operator<<(const Struct<ValueTypes...>& item);
template <typename... ValueTypes>
Message& operator<<(const std::tuple<ValueTypes...>& item);
Message& operator>>(bool& item);
Message& operator>>(int16_t& item);
@@ -121,56 +138,88 @@ namespace sdbus {
Message& operator>>(char*& item);
Message& operator>>(std::string &item);
Message& operator>>(Variant &item);
template <typename ...Elements>
Message& operator>>(std::variant<Elements...>& value);
Message& operator>>(ObjectPath &item);
Message& operator>>(Signature &item);
Message& operator>>(UnixFd &item);
template <typename _Element, typename _Allocator>
Message& operator>>(std::vector<_Element, _Allocator>& items);
template <typename _Element, std::size_t _Size>
Message& operator>>(std::array<_Element, _Size>& items);
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
Message& operator>>(std::span<_Element, _Extent>& items);
template <typename Element, typename Allocator>
Message& operator>>(std::vector<Element, Allocator>& items);
template <typename Element, std::size_t Size>
Message& operator>>(std::array<Element, Size>& items);
#ifdef __cpp_lib_span
template <typename Element, std::size_t Extent>
Message& operator>>(std::span<Element, Extent>& items);
#endif
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
Message& operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items);
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
Message& operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items);
template <typename... _ValueTypes>
Message& operator>>(Struct<_ValueTypes...>& item);
template <typename... _ValueTypes>
Message& operator>>(std::tuple<_ValueTypes...>& item);
// NOLINTNEXTLINE(modernize-use-constraints): Public API is C++17-compatible
template <typename Enum, typename = std::enable_if_t<std::is_enum_v<Enum>>>
Message& operator>>(Enum& item);
template <typename Key, typename Value>
Message& operator>>(DictEntry<Key, Value>& value);
template <typename Key, typename Value, typename Compare, typename Allocator>
Message& operator>>(std::map<Key, Value, Compare, Allocator>& items);
template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
Message& operator>>(std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items);
template <typename... ValueTypes>
Message& operator>>(Struct<ValueTypes...>& item);
template <typename... ValueTypes>
Message& operator>>(std::tuple<ValueTypes...>& item);
Message& openContainer(const std::string& signature);
template <typename ElementType>
Message& openContainer();
Message& openContainer(const char* signature);
Message& closeContainer();
Message& openDictEntry(const std::string& signature);
template <typename KeyType, typename ValueType>
Message& openDictEntry();
Message& openDictEntry(const char* signature);
Message& closeDictEntry();
Message& openVariant(const std::string& signature);
template <typename ValueType>
Message& openVariant();
Message& openVariant(const char* signature);
Message& closeVariant();
Message& openStruct(const std::string& signature);
template <typename... ValueTypes>
Message& openStruct();
Message& openStruct(const char* signature);
Message& closeStruct();
Message& enterContainer(const std::string& signature);
template <typename ElementType>
Message& enterContainer();
Message& enterContainer(const char* signature);
Message& exitContainer();
Message& enterDictEntry(const std::string& signature);
template <typename KeyType, typename ValueType>
Message& enterDictEntry();
Message& enterDictEntry(const char* signature);
Message& exitDictEntry();
Message& enterVariant(const std::string& signature);
template <typename ValueType>
Message& enterVariant();
Message& enterVariant(const char* signature);
Message& exitVariant();
Message& enterStruct(const std::string& signature);
template <typename... ValueTypes>
Message& enterStruct();
Message& enterStruct(const char* signature);
Message& exitStruct();
Message& appendArray(char type, const void *ptr, size_t size);
Message& readArray(char type, const void **ptr, size_t *size);
template <typename Key, typename Value, typename Callback>
Message& serializeDictionary(const Callback& callback);
template <typename Key, typename Value>
Message& serializeDictionary(const std::initializer_list<DictEntry<Key, Value>>& dictEntries);
template <typename Key, typename Value, typename Callback>
Message& deserializeDictionary(const Callback& callback);
explicit operator bool() const;
void clearFlags();
std::string getInterfaceName() const;
std::string getMemberName() const;
std::string getSender() const;
std::string getPath() const;
std::string getDestination() const;
void peekType(std::string& type, std::string& contents) const;
const char* getInterfaceName() const;
const char* getMemberName() const;
const char* getSender() const;
const char* getPath() const;
const char* getDestination() const;
uint64_t getCookie() const;
// TODO: short docs in whole Message API
std::pair<char, const char*> peekType() const;
bool isValid() const;
bool isEmpty() const;
bool isAtEnd(bool complete) const;
@@ -179,6 +228,15 @@ namespace sdbus {
void seal();
void rewind(bool complete);
enum class DumpFlags : uint64_t // NOLINT(performance-enum-size): using size from sd-bus
{
Default = 0ULL,
WithHeader = 1ULL << 0,
SubtreeOnly = 1ULL << 1,
SubtreeOnlyWithHeader = WithHeader | SubtreeOnly
};
[[nodiscard]] std::string dumpToString(DumpFlags flags) const;
pid_t getCredsPid() const;
uid_t getCredsUid() const;
uid_t getCredsEuid() const;
@@ -190,42 +248,29 @@ namespace sdbus {
class Factory;
private:
template <typename _Array>
void serializeArray(const _Array& items);
template <typename _Array>
void deserializeArray(_Array& items);
template <typename _Array>
void deserializeArrayFast(_Array& items);
template <typename _Element, typename _Allocator>
void deserializeArrayFast(std::vector<_Element, _Allocator>& items);
template <typename _Array>
void deserializeArraySlow(_Array& items);
template <typename _Element, typename _Allocator>
void deserializeArraySlow(std::vector<_Element, _Allocator>& items);
template <typename _Dictionary>
void serializeDictionary(const _Dictionary& items);
template <typename _Dictionary>
void deserializeDictionary(_Dictionary& items);
template <typename Array>
void serializeArray(const Array& items);
template <typename Array>
void deserializeArray(Array& items);
template <typename Array>
void deserializeArrayFast(Array& items);
template <typename Element, typename Allocator>
void deserializeArrayFast(std::vector<Element, Allocator>& items);
template <typename Array>
void deserializeArraySlow(Array& items);
template <typename Element, typename Allocator>
void deserializeArraySlow(std::vector<Element, Allocator>& items);
protected:
Message() = default;
explicit Message(internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus) noexcept;
Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept;
Message(const Message&) noexcept;
Message& operator=(const Message&) noexcept;
Message(Message&& other) noexcept;
Message& operator=(Message&& other) noexcept;
~Message();
explicit Message(internal::IConnection* connection) noexcept;
Message(void *msg, internal::IConnection* connection) noexcept;
Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept;
friend Factory;
protected:
void* msg_{};
internal::ISdBus* sdbus_{};
internal::IConnection* connection_{};
mutable bool ok_{true};
};
@@ -238,23 +283,20 @@ namespace sdbus {
MethodCall() = default;
MethodReply send(uint64_t timeout) 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;
[[nodiscard]] Slot send(void* callback, void* userData, uint64_t timeout, return_slot_t) const;
MethodReply createReply() const;
MethodReply createErrorReply(const sdbus::Error& error) const;
MethodReply createErrorReply(const Error& error) const;
void dontExpectReply();
bool doesntExpectReply() const;
protected:
MethodCall(void *msg, internal::ISdBus* sdbus, const internal::IConnection* connection, adopt_message_t) noexcept;
MethodCall(void *msg, 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
@@ -265,6 +307,7 @@ namespace sdbus {
public:
MethodReply() = default;
void send() const;
uint64_t getReplyCookie() const;
};
class Signal : public Message
@@ -275,6 +318,7 @@ namespace sdbus {
public:
Signal() = default;
void setDestination(const std::string& destination);
void setDestination(const char* destination);
void send() const;
};
@@ -306,25 +350,40 @@ namespace sdbus {
PlainMessage() = default;
};
template <typename _Element, typename _Allocator>
inline Message& Message::operator<<(const std::vector<_Element, _Allocator>& items)
PlainMessage createPlainMessage();
template <typename ...Elements>
inline Message& Message::operator<<(const std::variant<Elements...>& value)
{
std::visit([this](const auto& inner)
{
openVariant<decltype(inner)>();
*this << inner;
closeVariant();
}, value);
return *this;
}
template <typename Element, typename Allocator>
inline Message& Message::operator<<(const std::vector<Element, Allocator>& items)
{
serializeArray(items);
return *this;
}
template <typename _Element, std::size_t _Size>
inline Message& Message::operator<<(const std::array<_Element, _Size>& items)
template <typename Element, std::size_t Size>
inline Message& Message::operator<<(const std::array<Element, Size>& items)
{
serializeArray(items);
return *this;
}
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator<<(const std::span<_Element, _Extent>& items)
#ifdef __cpp_lib_span
template <typename Element, std::size_t Extent>
inline Message& Message::operator<<(const std::span<Element, Extent>& items)
{
serializeArray(items);
@@ -332,20 +391,27 @@ namespace sdbus {
}
#endif
template <typename _Array>
inline void Message::serializeArray(const _Array& items)
template <typename Enum, typename>
inline Message& Message::operator<<(const Enum &item)
{
using ElementType = typename _Array::value_type;
return operator<<(static_cast<std::underlying_type_t<Enum>>(item));
}
template <typename Array>
inline void Message::serializeArray(const Array& items)
{
using ElementType = typename Array::value_type;
// Use faster, one-step serialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step serialization of individual elements.
if constexpr (signature_of<ElementType>::is_trivial_dbus_type && !std::is_same_v<ElementType, bool>)
{
appendArray(*signature_of<ElementType>::str().c_str(), items.data(), items.size() * sizeof(ElementType));
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
appendArray(*signature.data(), items.data(), items.size() * sizeof(ElementType));
}
else
{
openContainer(signature_of<ElementType>::str());
openContainer<ElementType>();
for (const auto& item : items)
*this << item;
@@ -354,102 +420,131 @@ namespace sdbus {
}
}
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& Message::operator<<(const std::map<_Key, _Value, _Compare, _Allocator>& items)
template <typename Key, typename Value>
inline Message& Message::operator<<(const DictEntry<Key, Value>& value)
{
serializeDictionary(items);
openDictEntry<Key, Value>();
*this << value.first;
*this << value.second;
closeDictEntry();
return *this;
}
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator<<(const std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
template <typename Key, typename Value, typename Compare, typename Allocator>
inline Message& Message::operator<<(const std::map<Key, Value, Compare, Allocator>& items)
{
serializeDictionary(items);
serializeDictionary<Key, Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
return *this;
}
template <typename _Dictionary>
inline void Message::serializeDictionary(const _Dictionary& items)
template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
inline Message& Message::operator<<(const std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
serializeDictionary<Key, Value>([&items](Message& msg){ for (const auto& item : items) msg << item; });
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
return *this;
}
openContainer(arraySignature);
template <typename Key, typename Value>
inline Message& Message::serializeDictionary(const std::initializer_list<DictEntry<Key, Value>>& dictEntries)
{
serializeDictionary<Key, Value>([&](Message& msg){ for (const auto& entry : dictEntries) msg << entry; });
for (const auto& item : items)
{
openDictEntry(dictEntrySignature);
*this << item.first;
*this << item.second;
closeDictEntry();
}
return *this;
}
template <typename Key, typename Value, typename Callback>
inline Message& Message::serializeDictionary(const Callback& callback)
{
openContainer<DictEntry<Key, Value>>();
callback(*this);
closeContainer();
return *this;
}
namespace detail
{
template <typename... _Args>
void serialize_pack(Message& msg, _Args&&... args)
template <typename... Args>
void serialize_pack(Message& msg, Args&&... args)
{
(void)(msg << ... << args);
(void)(msg << ... << std::forward<Args>(args));
}
template <class _Tuple, std::size_t... _Is>
template <class Tuple, std::size_t... Is>
void serialize_tuple( Message& msg
, const _Tuple& t
, std::index_sequence<_Is...>)
, const Tuple& tuple
, std::index_sequence<Is...>)
{
serialize_pack(msg, std::get<_Is>(t)...);
serialize_pack(msg, std::get<Is>(tuple)...);
}
}
} // namespace detail
template <typename... _ValueTypes>
inline Message& Message::operator<<(const Struct<_ValueTypes...>& item)
template <typename... ValueTypes>
inline Message& Message::operator<<(const Struct<ValueTypes...>& item)
{
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
assert(structSignature.size() > 2);
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size() - 2);
openStruct(structContentSignature);
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
openStruct<ValueTypes...>();
detail::serialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
closeStruct();
return *this;
}
template <typename... _ValueTypes>
inline Message& Message::operator<<(const std::tuple<_ValueTypes...>& item)
template <typename... ValueTypes>
inline Message& Message::operator<<(const std::tuple<ValueTypes...>& item)
{
detail::serialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
detail::serialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
return *this;
}
template <typename _Element, typename _Allocator>
inline Message& Message::operator>>(std::vector<_Element, _Allocator>& items)
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(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;
}
} // namespace detail
template <typename... Elements>
inline Message& Message::operator>>(std::variant<Elements...>& value)
{
auto [type, contents] = peekType();
const bool result = (detail::deserialize_variant<Elements>(*this, value, contents) || ...);
SDBUS_THROW_ERROR_IF(!result, "Failed to deserialize variant: signature did not match any of the variant types", EINVAL);
return *this;
}
template <typename Element, typename Allocator>
inline Message& Message::operator>>(std::vector<Element, Allocator>& items)
{
deserializeArray(items);
return *this;
}
template <typename _Element, std::size_t _Size>
inline Message& Message::operator>>(std::array<_Element, _Size>& items)
template <typename Element, std::size_t Size>
inline Message& Message::operator>>(std::array<Element, Size>& items)
{
deserializeArray(items);
return *this;
}
#if __cplusplus >= 202002L
template <typename _Element, std::size_t _Extent>
inline Message& Message::operator>>(std::span<_Element, _Extent>& items)
#ifdef __cpp_lib_span
template <typename Element, std::size_t Extent>
inline Message& Message::operator>>(std::span<Element, Extent>& items)
{
deserializeArray(items);
@@ -457,10 +552,19 @@ namespace sdbus {
}
#endif
template <typename _Array>
inline void Message::deserializeArray(_Array& items)
template <typename Enum, typename>
inline Message& Message::operator>>(Enum& item)
{
using ElementType = typename _Array::value_type;
std::underlying_type_t<Enum> val;
*this >> val;
item = static_cast<Enum>(val);
return *this;
}
template <typename Array>
inline void Message::deserializeArray(Array& items)
{
using ElementType = typename Array::value_type;
// Use faster, one-step deserialization of contiguous array of elements of trivial D-Bus types except bool,
// otherwise use step-by-step deserialization of individual elements.
@@ -474,40 +578,43 @@ namespace sdbus {
}
}
template <typename _Array>
inline void Message::deserializeArrayFast(_Array& items)
template <typename Array>
inline void Message::deserializeArrayFast(Array& items)
{
using ElementType = typename _Array::value_type;
using ElementType = typename Array::value_type;
size_t arraySize{};
const ElementType* arrayPtr{};
readArray(*signature_of<ElementType>::str().c_str(), (const void**)&arrayPtr, &arraySize);
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
readArray(*signature.data(), reinterpret_cast<const void**>(&arrayPtr), &arraySize);
size_t elementsInMsg = arraySize / sizeof(ElementType);
bool notEnoughSpace = items.size() < elementsInMsg;
const size_t elementsInMsg = arraySize / sizeof(ElementType);
const bool notEnoughSpace = items.size() < elementsInMsg;
SDBUS_THROW_ERROR_IF(notEnoughSpace, "Failed to deserialize array: not enough space in destination sequence", EINVAL);
std::copy_n(arrayPtr, elementsInMsg, items.begin());
}
template <typename _Element, typename _Allocator>
void Message::deserializeArrayFast(std::vector<_Element, _Allocator>& items)
template <typename Element, typename Allocator>
void Message::deserializeArrayFast(std::vector<Element, Allocator>& items)
{
size_t arraySize{};
const _Element* arrayPtr{};
const Element* arrayPtr{};
readArray(*signature_of<_Element>::str().c_str(), (const void**)&arrayPtr, &arraySize);
constexpr auto signature = as_null_terminated(signature_of_v<Element>);
readArray(*signature.data(), reinterpret_cast<const void**>(&arrayPtr), &arraySize);
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(_Element)));
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
items.insert(items.end(), arrayPtr, arrayPtr + (arraySize / sizeof(Element)));
}
template <typename _Array>
inline void Message::deserializeArraySlow(_Array& items)
template <typename Array>
inline void Message::deserializeArraySlow(Array& items)
{
using ElementType = typename _Array::value_type;
using ElementType = typename Array::value_type;
if(!enterContainer(signature_of<ElementType>::str()))
if(!enterContainer<ElementType>())
return;
for (auto& elem : items)
@@ -521,15 +628,15 @@ namespace sdbus {
exitContainer();
}
template <typename _Element, typename _Allocator>
void Message::deserializeArraySlow(std::vector<_Element, _Allocator>& items)
template <typename Element, typename Allocator>
void Message::deserializeArraySlow(std::vector<Element, Allocator>& items)
{
if(!enterContainer(signature_of<_Element>::str()))
if(!enterContainer<Element>())
return;
while (true)
{
_Element elem;
Element elem;
if (*this >> elem)
items.emplace_back(std::move(elem));
else
@@ -541,94 +648,147 @@ namespace sdbus {
exitContainer();
}
template <typename _Key, typename _Value, typename _Compare, typename _Allocator>
inline Message& Message::operator>>(std::map<_Key, _Value, _Compare, _Allocator>& items)
template <typename Key, typename Value>
inline Message& Message::operator>>(DictEntry<Key, Value>& value)
{
deserializeDictionary(items);
if (!enterDictEntry<Key, Value>())
return *this;
*this >> value.first >> value.second;
exitDictEntry();
return *this;
}
template <typename _Key, typename _Value, typename _Hash, typename _KeyEqual, typename _Allocator>
inline Message& Message::operator>>(std::unordered_map<_Key, _Value, _Hash, _KeyEqual, _Allocator>& items)
template <typename Key, typename Value, typename Compare, typename Allocator>
inline Message& Message::operator>>(std::map<Key, Value, Compare, Allocator>& items)
{
deserializeDictionary(items);
deserializeDictionary<Key, Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
return *this;
}
template <typename _Dictionary>
inline void Message::deserializeDictionary(_Dictionary& items)
template <typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator>
inline Message& Message::operator>>(std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& items)
{
using KeyType = typename _Dictionary::key_type;
using ValueType = typename _Dictionary::mapped_type;
deserializeDictionary<Key, Value>([&items](auto dictEntry){ items.insert(std::move(dictEntry)); });
const std::string dictEntrySignature = signature_of<KeyType>::str() + signature_of<ValueType>::str();
const std::string arraySignature = "{" + dictEntrySignature + "}";
return *this;
}
if (!enterContainer(arraySignature))
return;
template <typename Key, typename Value, typename Callback>
inline Message& Message::deserializeDictionary(const Callback& callback)
{
if (!enterContainer<DictEntry<Key, Value>>())
return *this;
while (true)
{
if (!enterDictEntry(dictEntrySignature))
DictEntry<Key, Value> dictEntry;
*this >> dictEntry;
if (!*this)
break;
KeyType key;
ValueType value;
*this >> key >> value;
items.emplace(std::move(key), std::move(value));
exitDictEntry();
callback(std::move(dictEntry));
}
clearFlags();
exitContainer();
return *this;
}
namespace detail
{
template <typename... _Args>
void deserialize_pack(Message& msg, _Args&... args)
template <typename... Args>
void deserialize_pack(Message& msg, Args&... args)
{
(void)(msg >> ... >> args);
}
template <class _Tuple, std::size_t... _Is>
template <class Tuple, std::size_t... Is>
void deserialize_tuple( Message& msg
, _Tuple& t
, std::index_sequence<_Is...> )
, Tuple& tuple
, std::index_sequence<Is...> )
{
deserialize_pack(msg, std::get<_Is>(t)...);
deserialize_pack(msg, std::get<Is>(tuple)...);
}
}
} // namespace detail
template <typename... _ValueTypes>
inline Message& Message::operator>>(Struct<_ValueTypes...>& item)
template <typename... ValueTypes>
inline Message& Message::operator>>(Struct<ValueTypes...>& item)
{
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
// Remove opening and closing parenthesis from the struct signature to get contents signature
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
if (!enterStruct(structContentSignature))
if (!enterStruct<ValueTypes...>())
return *this;
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
detail::deserialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
exitStruct();
return *this;
}
template <typename... _ValueTypes>
inline Message& Message::operator>>(std::tuple<_ValueTypes...>& item)
template <typename... ValueTypes>
inline Message& Message::operator>>(std::tuple<ValueTypes...>& item)
{
detail::deserialize_tuple(*this, item, std::index_sequence_for<_ValueTypes...>{});
detail::deserialize_tuple(*this, item, std::index_sequence_for<ValueTypes...>{});
return *this;
}
}
template <typename ElementType>
inline Message& Message::openContainer()
{
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
return openContainer(signature.data());
}
template <typename KeyType, typename ValueType>
inline Message& Message::openDictEntry()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<KeyType, ValueType>>);
return openDictEntry(signature.data());
}
template <typename ValueType>
inline Message& Message::openVariant()
{
constexpr auto signature = as_null_terminated(signature_of_v<ValueType>);
return openVariant(signature.data());
}
template <typename... ValueTypes>
inline Message& Message::openStruct()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<ValueTypes...>>);
return openStruct(signature.data());
}
template <typename ElementType>
inline Message& Message::enterContainer()
{
constexpr auto signature = as_null_terminated(signature_of_v<ElementType>);
return enterContainer(signature.data());
}
template <typename KeyType, typename ValueType>
inline Message& Message::enterDictEntry()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<KeyType, ValueType>>);
return enterDictEntry(signature.data());
}
template <typename ValueType>
inline Message& Message::enterVariant()
{
constexpr auto signature = as_null_terminated(signature_of_v<ValueType>);
return enterVariant(signature.data());
}
template <typename... ValueTypes>
inline Message& Message::enterStruct()
{
constexpr auto signature = as_null_terminated(signature_of_v<std::tuple<ValueTypes...>>);
return enterStruct(signature.data());
}
} // namespace sdbus
#endif /* SDBUS_CXX_MESSAGE_H_ */
+16 -13
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file MethodResult.h
*
@@ -28,12 +28,13 @@
#define SDBUS_CXX_METHODRESULT_H_
#include <sdbus-c++/Message.h>
#include <cassert>
// Forward declaration
// Forward declarations
namespace sdbus {
class Error;
}
} // namespace sdbus
namespace sdbus {
@@ -45,12 +46,12 @@ namespace sdbus {
* by the method to either method return value or an error.
*
***********************************************/
template <typename... _Results>
template <typename... Results>
class Result
{
public:
Result() = default;
Result(MethodCall call);
explicit Result(MethodCall call);
Result(const Result&) = delete;
Result& operator=(const Result&) = delete;
@@ -58,21 +59,23 @@ namespace sdbus {
Result(Result&& other) = default;
Result& operator=(Result&& other) = default;
void returnResults(const _Results&... results) const;
~Result() = default;
void returnResults(const Results&... results) const;
void returnError(const Error& error) const;
private:
MethodCall call_;
};
template <typename... _Results>
inline Result<_Results...>::Result(MethodCall call)
template <typename... Results>
inline Result<Results...>::Result(MethodCall call)
: call_(std::move(call))
{
}
template <typename... _Results>
inline void Result<_Results...>::returnResults(const _Results&... results) const
template <typename... Results>
inline void Result<Results...>::returnResults(const Results&... results) const
{
assert(call_.isValid());
auto reply = call_.createReply();
@@ -80,13 +83,13 @@ namespace sdbus {
reply.send();
}
template <typename... _Results>
inline void Result<_Results...>::returnError(const Error& error) const
template <typename... Results>
inline void Result<Results...>::returnError(const Error& error) const
{
auto reply = call_.createErrorReply(error);
reply.send();
}
}
} // namespace sdbus
#endif /* SDBUS_CXX_METHODRESULT_H_ */
+34 -36
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ProxyInterfaces.h
*
@@ -29,13 +29,12 @@
#include <sdbus-c++/IProxy.h>
#include <cassert>
#include <string>
#include <memory>
// Forward declarations
namespace sdbus {
class IConnection;
}
} // namespace sdbus
namespace sdbus {
@@ -51,19 +50,19 @@ namespace sdbus {
class ProxyObjectHolder
{
protected:
ProxyObjectHolder(std::unique_ptr<IProxy>&& proxy)
explicit ProxyObjectHolder(std::unique_ptr<IProxy>&& proxy)
: proxy_(std::move(proxy))
{
assert(proxy_ != nullptr);
}
const IProxy& getProxy() const
[[nodiscard]] const IProxy& getProxy() const
{
assert(proxy_ != nullptr);
return *proxy_;
}
IProxy& getProxy()
[[nodiscard]] IProxy& getProxy()
{
assert(proxy_ != nullptr);
return *proxy_;
@@ -83,17 +82,23 @@ 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 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();`).
* In the final adaptor class inherited from ProxyInterfaces, one needs to make sure:
* 1. to call `registerProxy();` in the class constructor, and, conversely,
* 2. to call `unregisterProxy();` in the class destructor,
* so that the signals are subscribed to and unsubscribed from at a proper time.
*
***********************************************/
template <typename... _Interfaces>
template <typename... Interfaces>
class ProxyInterfaces
: protected ProxyObjectHolder
, public _Interfaces...
, public Interfaces...
{
public:
ProxyInterfaces(const ProxyInterfaces&) = delete;
ProxyInterfaces& operator=(const ProxyInterfaces&) = delete;
ProxyInterfaces(ProxyInterfaces&&) = delete;
ProxyInterfaces& operator=(ProxyInterfaces&&) = delete;
/*!
* @brief Creates native-like proxy object instance
*
@@ -103,9 +108,9 @@ 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(std::string destination, std::string objectPath)
ProxyInterfaces(ServiceName destination, ObjectPath objectPath)
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
, Interfaces(getProxy())...
{
}
@@ -118,9 +123,9 @@ 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(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
ProxyInterfaces(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
: ProxyObjectHolder(createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
, _Interfaces(getProxy())...
, Interfaces(getProxy())...
{
}
@@ -134,9 +139,9 @@ 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, std::string destination, std::string objectPath)
ProxyInterfaces(IConnection& connection, ServiceName destination, ObjectPath objectPath)
: ProxyObjectHolder(createProxy(connection, std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
, Interfaces(getProxy())...
{
}
@@ -150,9 +155,9 @@ 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, std::string destination, std::string objectPath)
ProxyInterfaces(std::unique_ptr<IConnection>&& connection, ServiceName destination, ObjectPath objectPath)
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath)))
, _Interfaces(getProxy())...
, Interfaces(getProxy())...
{
}
@@ -166,22 +171,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, 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())...
ProxyInterfaces(std::unique_ptr<IConnection>&& connection, ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t)
: ProxyObjectHolder(createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread))
, Interfaces(getProxy())...
{
}
/*!
* @brief Finishes proxy registration and makes the proxy ready for use
* @brief Registers handlers for D-Bus signals of the remote object
*
* This function must be called in the constructor of the final proxy class that implements ProxyInterfaces.
*
* For more information, see underlying @ref IProxy::finishRegistration()
* See also @ref IProxy::registerSignalHandler()
*/
void registerProxy()
{
getProxy().finishRegistration();
(Interfaces::registerProxy(), ...);
}
/*!
@@ -197,23 +202,16 @@ namespace sdbus {
}
/*!
* @brief Returns object path of the underlying DBus object
* @brief Returns reference to the underlying IProxy instance
*/
const std::string& getObjectPath() const
{
return getProxy().getObjectPath();
}
using ProxyObjectHolder::getProxy;
protected:
using base_type = ProxyInterfaces;
ProxyInterfaces(const ProxyInterfaces&) = delete;
ProxyInterfaces& operator=(const ProxyInterfaces&) = delete;
ProxyInterfaces(ProxyInterfaces&&) = default;
ProxyInterfaces& operator=(ProxyInterfaces&&) = default;
~ProxyInterfaces() = default;
};
}
} // namespace sdbus
#endif /* SDBUS_CXX_INTERFACES_H_ */
#endif /* SDBUS_CXX_PROXYINTERFACES_H_ */
+312 -132
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file StandardInterfaces.h
*
@@ -31,6 +31,7 @@
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#include <string>
#include <string_view>
#include <map>
#include <vector>
@@ -39,206 +40,364 @@ namespace sdbus {
// Proxy for peer
class Peer_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Peer";
protected:
Peer_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
explicit Peer_proxy(IProxy& proxy)
: m_proxy(proxy)
{
}
Peer_proxy(const Peer_proxy&) = delete;
Peer_proxy& operator=(const Peer_proxy&) = delete;
Peer_proxy(Peer_proxy&&) = default;
Peer_proxy& operator=(Peer_proxy&&) = default;
~Peer_proxy() = default;
void registerProxy()
{
}
public:
Peer_proxy(const Peer_proxy&) = delete;
Peer_proxy& operator=(const Peer_proxy&) = delete;
Peer_proxy(Peer_proxy&&) = delete;
Peer_proxy& operator=(Peer_proxy&&) = delete;
void Ping()
{
proxy_->callMethod("Ping").onInterface(INTERFACE_NAME);
m_proxy.callMethod("Ping").onInterface(INTERFACE_NAME);
}
std::string GetMachineId()
{
std::string machineUUID;
proxy_->callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
m_proxy.callMethod("GetMachineId").onInterface(INTERFACE_NAME).storeResultsTo(machineUUID);
return machineUUID;
}
private:
sdbus::IProxy* proxy_;
IProxy& m_proxy;
};
// Proxy for introspection
class Introspectable_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable";
protected:
Introspectable_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
explicit Introspectable_proxy(IProxy& proxy)
: m_proxy(proxy)
{
}
Introspectable_proxy(const Introspectable_proxy&) = delete;
Introspectable_proxy& operator=(const Introspectable_proxy&) = delete;
Introspectable_proxy(Introspectable_proxy&&) = default;
Introspectable_proxy& operator=(Introspectable_proxy&&) = default;
~Introspectable_proxy() = default;
void registerProxy()
{
}
public:
Introspectable_proxy(const Introspectable_proxy&) = delete;
Introspectable_proxy& operator=(const Introspectable_proxy&) = delete;
Introspectable_proxy(Introspectable_proxy&&) = delete;
Introspectable_proxy& operator=(Introspectable_proxy&&) = delete;
std::string Introspect()
{
std::string xml;
proxy_->callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
m_proxy.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(xml);
return xml;
}
private:
sdbus::IProxy* proxy_;
IProxy& m_proxy;
};
// Proxy for properties
class Properties_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
protected:
Properties_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
explicit Properties_proxy(IProxy& proxy)
: m_proxy(proxy)
{
proxy_
->uponSignal("PropertiesChanged")
}
~Properties_proxy() = default;
void registerProxy()
{
m_proxy
.uponSignal("PropertiesChanged")
.onInterface(INTERFACE_NAME)
.call([this]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
.call([this]( const InterfaceName& interfaceName
, const std::map<PropertyName, Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties )
{
this->onPropertiesChanged(interfaceName, changedProperties, invalidatedProperties);
});
}
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;
virtual void onPropertiesChanged( const InterfaceName& interfaceName
, const std::map<PropertyName, Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties ) = 0;
public:
sdbus::Variant Get(const std::string& interfaceName, const std::string& propertyName)
Properties_proxy(const Properties_proxy&) = delete;
Properties_proxy& operator=(const Properties_proxy&) = delete;
Properties_proxy(Properties_proxy&&) = delete;
Properties_proxy& operator=(Properties_proxy&&) = delete;
Variant Get(const InterfaceName& interfaceName, const PropertyName& propertyName)
{
return proxy_->getProperty(propertyName).onInterface(interfaceName);
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
}
template <typename _Function>
PendingAsyncCall GetAsync(const std::string& interfaceName, const std::string& propertyName, _Function&& callback)
Variant Get(std::string_view interfaceName, std::string_view propertyName)
{
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
return m_proxy.getProperty(propertyName).onInterface(interfaceName);
}
std::future<sdbus::Variant> GetAsync(const std::string& interfaceName, const std::string& propertyName, with_future_t)
template <typename Function>
PendingAsyncCall GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, Function&& callback)
{
return proxy_->getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
}
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value)
template <typename Function>
[[nodiscard]] Slot GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, Function&& callback, return_slot_t)
{
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value);
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback), return_slot);
}
void Set(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, dont_expect_reply_t)
std::future<Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_future_t)
{
proxy_->setProperty(propertyName).onInterface(interfaceName).toValue(value, dont_expect_reply);
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
}
template <typename _Function>
PendingAsyncCall SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, _Function&& callback)
Awaitable<Variant> GetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, with_awaitable_t)
{
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<_Function>(callback));
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsAwaitable();
}
std::future<void> SetAsync(const std::string& interfaceName, const std::string& propertyName, const sdbus::Variant& value, with_future_t)
template <typename Function>
PendingAsyncCall GetAsync(std::string_view interfaceName, std::string_view propertyName, Function&& callback)
{
return proxy_->setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
}
std::map<std::string, sdbus::Variant> GetAll(const std::string& interfaceName)
template <typename Function>
[[nodiscard]] Slot GetAsync(std::string_view interfaceName, std::string_view propertyName, Function&& callback, return_slot_t)
{
return proxy_->getAllProperties().onInterface(interfaceName);
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback), return_slot);
}
template <typename _Function>
PendingAsyncCall GetAllAsync(const std::string& interfaceName, _Function&& callback)
std::future<Variant> GetAsync(std::string_view interfaceName, std::string_view propertyName, with_future_t)
{
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<_Function>(callback));
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsFuture();
}
std::future<std::map<std::string, sdbus::Variant>> GetAllAsync(const std::string& interfaceName, with_future_t)
Awaitable<Variant> GetAsync(std::string_view interfaceName, std::string_view propertyName, with_awaitable_t)
{
return proxy_->getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
return m_proxy.getPropertyAsync(propertyName).onInterface(interfaceName).getResultAsAwaitable();
}
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const Variant& value)
{
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
}
void Set(std::string_view interfaceName, const std::string_view propertyName, const Variant& value)
{
m_proxy.setProperty(propertyName).onInterface(interfaceName).toValue(value);
}
void Set(const InterfaceName& interfaceName, const PropertyName& propertyName, const 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 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 Variant& value, Function&& callback)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<Function>(callback));
}
template <typename Function>
[[nodiscard]] Slot SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const 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 Variant& value, with_future_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
}
Awaitable<void> SetAsync(const InterfaceName& interfaceName, const PropertyName& propertyName, const Variant& value, with_awaitable_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsAwaitable();
}
template <typename Function>
PendingAsyncCall SetAsync(std::string_view interfaceName, std::string_view propertyName, const Variant& value, Function&& callback)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<Function>(callback));
}
template <typename Function>
[[nodiscard]] Slot SetAsync(std::string_view interfaceName, std::string_view propertyName, const Variant& value, Function&& callback, return_slot_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).uponReplyInvoke(std::forward<Function>(callback), return_slot);
}
std::future<void> SetAsync(std::string_view interfaceName, std::string_view propertyName, const Variant& value, with_future_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsFuture();
}
Awaitable<void> SetAsync(std::string_view interfaceName, std::string_view propertyName, const Variant& value, with_awaitable_t)
{
return m_proxy.setPropertyAsync(propertyName).onInterface(interfaceName).toValue(value).getResultAsAwaitable();
}
std::map<PropertyName, Variant> GetAll(const InterfaceName& interfaceName)
{
return m_proxy.getAllProperties().onInterface(interfaceName);
}
std::map<PropertyName, Variant> GetAll(std::string_view interfaceName)
{
return m_proxy.getAllProperties().onInterface(interfaceName);
}
template <typename Function>
PendingAsyncCall GetAllAsync(const InterfaceName& interfaceName, Function&& callback)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
}
template <typename Function>
[[nodiscard]] Slot GetAllAsync(const InterfaceName& interfaceName, Function&& callback, return_slot_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback), return_slot);
}
std::future<std::map<PropertyName, Variant>> GetAllAsync(const InterfaceName& interfaceName, with_future_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
}
Awaitable<std::map<PropertyName, Variant>> GetAllAsync(const InterfaceName& interfaceName, with_awaitable_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsAwaitable();
}
template <typename Function>
PendingAsyncCall GetAllAsync(std::string_view interfaceName, Function&& callback)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback));
}
template <typename Function>
[[nodiscard]] Slot GetAllAsync(std::string_view interfaceName, Function&& callback, return_slot_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).uponReplyInvoke(std::forward<Function>(callback), return_slot);
}
std::future<std::map<PropertyName, Variant>> GetAllAsync(std::string_view interfaceName, with_future_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsFuture();
}
Awaitable<std::map<PropertyName, Variant>> GetAllAsync(std::string_view interfaceName, with_awaitable_t)
{
return m_proxy.getAllPropertiesAsync().onInterface(interfaceName).getResultAsAwaitable();
}
private:
sdbus::IProxy* proxy_;
IProxy& m_proxy;
};
// Proxy for object manager
class ObjectManager_proxy
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected:
ObjectManager_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
explicit ObjectManager_proxy(IProxy& proxy)
: m_proxy(proxy)
{
proxy_
->uponSignal("InterfacesAdded")
}
~ObjectManager_proxy() = default;
void registerProxy()
{
m_proxy
.uponSignal("InterfacesAdded")
.onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
.call([this]( const ObjectPath& objectPath
, const std::map<InterfaceName, std::map<PropertyName, Variant>>& interfacesAndProperties )
{
this->onInterfacesAdded(objectPath, interfacesAndProperties);
});
proxy_->uponSignal("InterfacesRemoved")
m_proxy
.uponSignal("InterfacesRemoved")
.onInterface(INTERFACE_NAME)
.call([this]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
.call([this]( const ObjectPath& objectPath
, const std::vector<InterfaceName>& 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<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) = 0;
virtual void onInterfacesRemoved( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces) = 0;
virtual void onInterfacesAdded( const ObjectPath& objectPath
, const std::map<InterfaceName, std::map<PropertyName, Variant>>& interfacesAndProperties) = 0;
virtual void onInterfacesRemoved( const ObjectPath& objectPath
, const std::vector<InterfaceName>& interfaces) = 0;
public:
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> GetManagedObjects()
ObjectManager_proxy(const ObjectManager_proxy&) = delete;
ObjectManager_proxy& operator=(const ObjectManager_proxy&) = delete;
ObjectManager_proxy(ObjectManager_proxy&&) = delete;
ObjectManager_proxy& operator=(ObjectManager_proxy&&) = delete;
std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>> GetManagedObjects()
{
std::map<sdbus::ObjectPath, std::map<std::string, std::map<std::string, sdbus::Variant>>> objectsInterfacesAndProperties;
proxy_->callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>> objectsInterfacesAndProperties;
m_proxy.callMethod("GetManagedObjects").onInterface(INTERFACE_NAME).storeResultsTo(objectsInterfacesAndProperties);
return objectsInterfacesAndProperties;
}
template <typename Function>
PendingAsyncCall GetManagedObjectsAsync(Function&& callback)
{
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<Function>(callback));
}
template <typename Function>
[[nodiscard]] Slot GetManagedObjectsAsync(Function&& callback, return_slot_t)
{
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).uponReplyInvoke(std::forward<Function>(callback), return_slot);
}
std::future<std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>> GetManagedObjectsAsync(with_future_t)
{
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsFuture<std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>>();
}
Awaitable<std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>> GetManagedObjectsAsync(with_awaitable_t)
{
return m_proxy.callMethodAsync("GetManagedObjects").onInterface(INTERFACE_NAME).getResultAsAwaitable<std::map<ObjectPath, std::map<InterfaceName, std::map<PropertyName, Variant>>>>();
}
private:
sdbus::IProxy* proxy_;
IProxy& m_proxy;
};
// Adaptors for the above-listed standard D-Bus interfaces are not necessary because the functionality
@@ -248,40 +407,53 @@ namespace sdbus {
// Adaptor for properties
class Properties_adaptor
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
protected:
Properties_adaptor(sdbus::IObject& object)
: object_(&object)
explicit Properties_adaptor(IObject& object) : m_object(object)
{
}
Properties_adaptor(const Properties_adaptor&) = delete;
Properties_adaptor& operator=(const Properties_adaptor&) = delete;
Properties_adaptor(Properties_adaptor&&) = default;
Properties_adaptor& operator=(Properties_adaptor&&) = default;
~Properties_adaptor() = default;
public:
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
void registerAdaptor()
{
object_->emitPropertiesChangedSignal(interfaceName, properties);
}
void emitPropertiesChangedSignal(const std::string& interfaceName)
public:
Properties_adaptor(const Properties_adaptor&) = delete;
Properties_adaptor& operator=(const Properties_adaptor&) = delete;
Properties_adaptor(Properties_adaptor&&) = delete;
Properties_adaptor& operator=(Properties_adaptor&&) = delete;
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& properties)
{
object_->emitPropertiesChangedSignal(interfaceName);
m_object.emitPropertiesChangedSignal(interfaceName, properties);
}
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& properties)
{
m_object.emitPropertiesChangedSignal(interfaceName, properties);
}
void emitPropertiesChangedSignal(const InterfaceName& interfaceName)
{
m_object.emitPropertiesChangedSignal(interfaceName);
}
void emitPropertiesChangedSignal(const char* interfaceName)
{
m_object.emitPropertiesChangedSignal(interfaceName);
}
private:
sdbus::IObject* object_;
IObject& m_object;
};
/*!
* @brief Object Manager Convenience Adaptor
*
* Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces
* Adding this class as Interfaces.. template parameter of class AdaptorInterfaces
* implements the *GetManagedObjects()* method of the [org.freedesktop.DBus.ObjectManager.GetManagedObjects](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager)
* interface.
*
@@ -290,53 +462,61 @@ namespace sdbus {
*/
class ObjectManager_adaptor
{
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
static inline const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected:
explicit ObjectManager_adaptor(sdbus::IObject& object)
: object_(&object)
explicit ObjectManager_adaptor(IObject& object) : m_object(object)
{
object_->addObjectManager();
}
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor(ObjectManager_adaptor&&) = default;
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = default;
~ObjectManager_adaptor() = default;
void registerAdaptor()
{
m_object.addObjectManager();
}
public:
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor& operator=(const ObjectManager_adaptor&) = delete;
ObjectManager_adaptor(ObjectManager_adaptor&&) = delete;
ObjectManager_adaptor& operator=(ObjectManager_adaptor&&) = delete;
private:
sdbus::IObject* object_;
IObject& m_object;
};
/*!
* @brief Managed Object Convenience Adaptor
*
* Adding this class as _Interfaces.. template parameter of class AdaptorInterfaces
* Adding this class as Interfaces.. template parameter of class AdaptorInterfaces
* will extend the resulting object adaptor with emitInterfacesAddedSignal()/emitInterfacesRemovedSignal()
* according to org.freedesktop.DBus.ObjectManager.InterfacesAdded/.InterfacesRemoved.
*
* Note that objects which implement this adaptor require an object manager (e.g via ObjectManager_adaptor) to be
* instantiated on one of it's parent object paths or the same path. InterfacesAdded/InterfacesRemoved
* Note that objects which implement this adaptor require an object manager (e.g., via ObjectManager_adaptor) to be
* instantiated on one of its parent object paths or the same path. InterfacesAdded/InterfacesRemoved
* signals are sent from the closest object manager at either the same path or the closest parent path of an object.
*/
class ManagedObject_adaptor
{
protected:
explicit ManagedObject_adaptor(sdbus::IObject& object)
: object_(&object)
explicit ManagedObject_adaptor(IObject& object)
: m_object(object)
{
}
ManagedObject_adaptor(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor(ManagedObject_adaptor&&) = default;
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = default;
~ManagedObject_adaptor() = default;
void registerAdaptor()
{
}
public:
ManagedObject_adaptor(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor& operator=(const ManagedObject_adaptor&) = delete;
ManagedObject_adaptor(ManagedObject_adaptor&&) = delete;
ManagedObject_adaptor& operator=(ManagedObject_adaptor&&) = delete;
/*!
* @brief Emits InterfacesAdded signal for this object path
*
@@ -344,7 +524,7 @@ namespace sdbus {
*/
void emitInterfacesAddedSignal()
{
object_->emitInterfacesAddedSignal();
m_object.emitInterfacesAddedSignal();
}
/*!
@@ -352,9 +532,9 @@ namespace sdbus {
*
* See IObject::emitInterfacesAddedSignal().
*/
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces)
{
object_->emitInterfacesAddedSignal(interfaces);
m_object.emitInterfacesAddedSignal(interfaces);
}
/*!
@@ -364,7 +544,7 @@ namespace sdbus {
*/
void emitInterfacesRemovedSignal()
{
object_->emitInterfacesRemovedSignal();
m_object.emitInterfacesRemovedSignal();
}
/*!
@@ -372,15 +552,15 @@ namespace sdbus {
*
* See IObject::emitInterfacesRemovedSignal().
*/
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
{
object_->emitInterfacesRemovedSignal(interfaces);
m_object.emitInterfacesRemovedSignal(interfaces);
}
private:
sdbus::IObject* object_;
IObject& m_object;
};
}
} // namespace sdbus
#endif /* SDBUS_CXX_STANDARDINTERFACES_H_ */
File diff suppressed because it is too large Load Diff
+380 -72
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Types.h
*
@@ -29,11 +29,13 @@
#include <sdbus-c++/Message.h>
#include <sdbus-c++/TypeTraits.h>
#include <string>
#include <type_traits>
#include <typeinfo>
#include <cstring>
#include <cstddef>
#include <memory>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
namespace sdbus {
@@ -47,7 +49,7 @@ namespace sdbus {
* Some const methods are conceptually const, but not physically const,
* thus are not thread-safe. This is by design: normally, clients
* should process a single Variant object in a single thread at a time.
* Otherwise they need to take care of synchronization by themselves.
* Otherwise, they need to take care of synchronization by themselves.
*
***********************************************/
class Variant
@@ -55,48 +57,91 @@ namespace sdbus {
public:
Variant();
template <typename _ValueType>
Variant(const _ValueType& value)
: Variant()
template <typename ValueType>
explicit Variant(const ValueType& value) : Variant()
{
msg_.openVariant(signature_of<_ValueType>::str());
msg_.openVariant<ValueType>();
msg_ << value;
msg_.closeVariant();
msg_.seal();
}
template <typename _ValueType>
_ValueType get() const
Variant(const Variant& value, embed_variant_t) : Variant()
{
msg_.openVariant<Variant>();
msg_ << value;
msg_.closeVariant();
msg_.seal();
}
template <typename Struct>
explicit Variant(const as_dictionary<Struct>& value) : Variant()
{
msg_.openVariant<std::map<std::string, Variant>>();
msg_ << as_dictionary(value.m_struct);
msg_.closeVariant();
msg_.seal();
}
template <typename... Elements>
Variant(const std::variant<Elements...>& value) // NOLINT(google-explicit-constructor,hicpp-explicit-conversions): implicit conversion intentional
: Variant()
{
msg_ << value;
msg_.seal();
}
template <typename ValueType>
ValueType get() const
{
_ValueType val;
msg_.rewind(false);
msg_.enterVariant(signature_of<_ValueType>::str());
msg_.enterVariant<ValueType>();
ValueType val;
msg_ >> val;
msg_.exitVariant();
return val;
}
// Only allow conversion operator for true D-Bus type representations in C++
template <typename _ValueType, typename = std::enable_if_t<signature_of<_ValueType>::is_valid>>
operator _ValueType() const
[[nodiscard]] std::string dumpToString() const
{
return get<_ValueType>();
msg_.rewind(false);
return msg_.dumpToString(Message::DumpFlags::SubtreeOnly);
}
template <typename _Type>
// Only allow conversion operator for true D-Bus type representations in C++
// NOLINTNEXTLINE(modernize-use-constraints): TODO for future: Use `requires signature_of<_ValueType>::is_valid` (when we stop supporting C++17 in public API)
template <typename ValueType, typename = std::enable_if_t<signature_of<ValueType>::is_valid>>
explicit operator ValueType() const
{
return get<ValueType>();
}
template <typename... Elements>
operator std::variant<Elements...>() const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions): implicit conversion intentional
{
std::variant<Elements...> result;
msg_.rewind(false);
msg_ >> result;
return result;
}
template <typename Type>
bool containsValueOfType() const
{
return signature_of<_Type>::str() == peekValueType();
constexpr auto signature = as_null_terminated(signature_of_v<Type>);
return std::strcmp(signature.data(), peekValueType()) == 0;
}
bool isEmpty() const;
void serializeTo(Message& msg) const;
void deserializeFrom(Message& msg);
std::string peekValueType() const;
const char* peekValueType() const;
private:
mutable PlainMessage msg_{};
mutable PlainMessage msg_;
};
/********************************************//**
@@ -109,86 +154,154 @@ namespace sdbus {
* std::tuple_size and in structured bindings.
*
***********************************************/
template <typename... _ValueTypes>
template <typename... ValueTypes>
class Struct
: public std::tuple<_ValueTypes...>
: public std::tuple<ValueTypes...>
{
public:
using std::tuple<_ValueTypes...>::tuple;
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)
explicit Struct(const std::tuple<ValueTypes...>& tuple)
: std::tuple<ValueTypes...>(tuple)
{
}
#endif
template <std::size_t _I>
auto& get()
template <std::size_t I>
[[nodiscard]] auto& get()
{
return std::get<_I>(*this);
return std::get<I>(*this);
}
template <std::size_t _I>
const auto& get() const
template <std::size_t I>
[[nodiscard]] const auto& get() const
{
return std::get<_I>(*this);
return std::get<I>(*this);
}
};
template <typename... _Elements>
Struct(_Elements...) -> Struct<_Elements...>;
template <typename... Elements>
Struct(Elements...) -> Struct<Elements...>;
template<typename... _Elements>
constexpr Struct<std::decay_t<_Elements>...>
make_struct(_Elements&&... args)
template <typename... Elements>
Struct(const std::tuple<Elements...>&) -> Struct<Elements...>;
template <typename... Elements>
Struct(std::tuple<Elements...>&&) -> Struct<Elements...>;
template<typename... Elements>
constexpr Struct<std::decay_t<Elements>...>
make_struct(Elements&&... args)
{
typedef Struct<std::decay_t<_Elements>...> result_type;
return result_type(std::forward<_Elements>(args)...);
using result_type = Struct<std::decay_t<Elements>...>;
return result_type(std::forward<Elements>(args)...);
}
/********************************************//**
* @class ObjectPath
*
* Representation of object path D-Bus type
* Strong type representing the D-Bus object path
*
***********************************************/
class ObjectPath : public std::string
{
public:
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))
ObjectPath() = default;
explicit ObjectPath(std::string value)
: std::string(std::move(value))
{}
explicit ObjectPath(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
/********************************************//**
* @class BusName
*
* Strong type representing the D-Bus bus/service/connection name
*
***********************************************/
class BusName : public std::string
{
public:
BusName() = default;
explicit BusName(std::string value)
: std::string(std::move(value))
{}
explicit BusName(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
using ServiceName = BusName;
using ConnectionName = BusName;
/********************************************//**
* @class InterfaceName
*
* Strong type representing the D-Bus interface name
*
***********************************************/
class InterfaceName : public std::string
{
public:
InterfaceName() = default;
explicit InterfaceName(std::string value)
: std::string(std::move(value))
{}
explicit InterfaceName(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
/********************************************//**
* @class MemberName
*
* Strong type representing the D-Bus member name
*
***********************************************/
class MemberName : public std::string
{
public:
MemberName() = default;
explicit MemberName(std::string value)
: std::string(std::move(value))
{}
explicit MemberName(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
using MethodName = MemberName;
using SignalName = MemberName;
using PropertyName = MemberName;
/********************************************//**
* @class Signature
*
* Representation of Signature D-Bus type
* Strong type representing the D-Bus object path
*
***********************************************/
class Signature : public std::string
{
public:
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))
Signature() = default;
explicit Signature(std::string value)
: std::string(std::move(value))
{}
explicit Signature(const char* value)
: std::string(value)
{}
using std::string::operator=;
};
@@ -234,12 +347,12 @@ namespace sdbus {
return *this;
}
UnixFd(UnixFd&& other)
UnixFd(UnixFd&& other) noexcept
{
*this = std::move(other);
}
UnixFd& operator=(UnixFd&& other)
UnixFd& operator=(UnixFd&& other) noexcept
{
if (this == &other)
{
@@ -255,7 +368,7 @@ namespace sdbus {
close();
}
int get() const
[[nodiscard]] int get() const
{
return fd_;
}
@@ -275,7 +388,7 @@ namespace sdbus {
return std::exchange(fd_, -1);
}
bool isValid() const
[[nodiscard]] bool isValid() const
{
return fd_ >= 0;
}
@@ -291,16 +404,211 @@ 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>
struct std::tuple_element<_I, sdbus::Struct<_ValueTypes...>>
: std::tuple_element<_I, std::tuple<_ValueTypes...>>
} // namespace sdbus
// Making sdbus::Struct implement the tuple-protocol, i.e. be a tuple-like type
template <size_t I, typename... ValueTypes>
struct std::tuple_element<I, sdbus::Struct<ValueTypes...>> // NOLINT(cert-dcl58-cpp): specialization in std namespace allowed in this case
: std::tuple_element<I, std::tuple<ValueTypes...>>
{};
template <typename... ValueTypes>
struct std::tuple_size<sdbus::Struct<ValueTypes...>> // NOLINT(cert-dcl58-cpp): specialization in std namespace allowed in this case
: std::tuple_size<std::tuple<ValueTypes...>>
{};
template <typename... _ValueTypes>
struct std::tuple_size<sdbus::Struct<_ValueTypes...>>
: std::tuple_size<std::tuple<_ValueTypes...>>
{};
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
/********************************************//**
* @name SDBUSCPP_REGISTER_STRUCT
*
* A convenient way to extend sdbus-c++ type system with user-defined structs.
*
* The macro teaches sdbus-c++ to recognize the user-defined struct
* as a valid C++ representation of a D-Bus Struct type, and enables
* clients to use their struct conveniently instead of the (too
* generic and less expressive) `sdbus::Struct<...>` in sdbus-c++ API.
*
* It also enables to serialize a user-defined struct as an a{sv} dictionary,
* and to deserialize an a{sv} dictionary into the user-defined struct.
*
* The first argument is the struct type name and the remaining arguments
* are names of struct members. Members must be of types supported by
* sdbus-c++ (or of user-defined types that sdbus-c++ was taught to support).
* Members can be other structs (nesting is supported).
* The macro must be placed in the global namespace.
*
* For example, given the user-defined struct `ABC`:
*
* namespace foo {
* struct ABC
* {
* int number;
* std::string name;
* std::vector<double> data;
* };
* }
*
* one can teach sdbus-c++ about the contents of this struct simply with:
*
* SDBUSCPP_REGISTER_STRUCT(foo::ABC, number, name, data);
*
* Up to 16 struct members are supported by the macro.
*
***********************************************/
#define SDBUSCPP_REGISTER_STRUCT(STRUCT, ...) \
namespace sdbus { \
static_assert(SDBUSCPP_PP_NARG(__VA_ARGS__) <= 16, \
"Not more than 16 struct members are supported, please open an issue if you need more"); \
\
template <> \
struct signature_of<STRUCT> \
: signature_of<Struct<SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, __VA_ARGS__)>> \
{}; \
\
inline auto as_dictionary_if_struct(const STRUCT& object) \
{ \
return as_dictionary<STRUCT>(object); \
} \
\
inline Message& operator<<(Message& msg, const STRUCT& items) \
{ \
return msg << Struct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(items, __VA_ARGS__))}; \
} \
\
inline Message& operator<<(Message& msg, const as_dictionary<STRUCT>& s) \
{ \
if constexpr (!nested_struct_as_dict_serialization_v<STRUCT>) \
return msg.serializeDictionary<std::string, Variant>({SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \
else \
return msg.serializeDictionary<std::string, Variant>({SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(s.m_struct, __VA_ARGS__)}); \
} \
\
inline Message& operator>>(Message& msg, STRUCT& s) \
{ \
/* First, try to deserialize as a struct */ \
if (msg.peekType().first == signature_of<STRUCT>::type_value) \
{ \
Struct sdbusStruct{std::forward_as_tuple(SDBUSCPP_STRUCT_MEMBERS(s, __VA_ARGS__))}; \
return msg >> sdbusStruct; \
} \
\
/* Otherwise try to deserialize as a dictionary of strings to variants */ \
\
return msg.deserializeDictionary<std::string, Variant>([&s](const auto& dictEntry) \
{ \
const std::string& key = dictEntry.first; /* Intentionally not using structured bindings */ \
const Variant& value = dictEntry.second; \
\
using namespace std::string_literals; \
/* This also handles members which are structs serialized as dict of strings to variants, recursively */ \
SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(s, __VA_ARGS__) \
SDBUS_THROW_ERROR_IF( strict_dict_as_struct_deserialization_v<STRUCT> \
, ("Failed to deserialize struct from a dictionary: could not find field '"s += key) += "' in struct 'my::Struct'" \
, EINVAL ); \
}); \
} \
} \
/**/
/********************************************//**
* @name SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION
*
* Enables relaxed deserialization of an a{sv} dictionary into a user-defined struct STRUCT.
*
* The default (strict) deserialization mode is that if there are entries in the dictionary
* which do not have a corresponding field in the struct, an exception is thrown.
* In the relaxed mode, such entries are silently skipped.
*
* The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro,
* and must be placed before SDBUSCPP_REGISTER_STRUCT macro.
***********************************************/
#define SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(STRUCT) \
template <> \
constexpr auto sdbus::strict_dict_as_struct_deserialization_v<STRUCT> = false; \
/**/
/********************************************//**
* @name SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION
*
* Enables nested serialization of user-defined struct STRUCT as an a{sv} dictionary.
*
* By default, STRUCT fields which are structs themselves are serialized as D-Bus structs.
* This macro tells sdbus-c++ to also serialize nested structs, in a recursive fashion,
* as a{sv} dictionaries.
*
* The macro can only be used in combination with SDBUSCPP_REGISTER_STRUCT macro,
* and must be placed before SDBUSCPP_REGISTER_STRUCT macro.
***********************************************/
#define SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(STRUCT) \
template <> \
constexpr auto sdbus::nested_struct_as_dict_serialization_v<STRUCT> = true \
/**/
/*!
* @cond SDBUSCPP_INTERNAL
*
* Internal helper preprocessor facilities
*/
#define SDBUSCPP_STRUCT_MEMBERS(STRUCT, ...) \
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
/**/
#define SDBUSCPP_STRUCT_MEMBER(STRUCT, MEMBER) STRUCT.MEMBER
#define SDBUSCPP_STRUCT_MEMBER_TYPES(STRUCT, ...) \
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_TYPE, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
/**/
#define SDBUSCPP_STRUCT_MEMBER_TYPE(STRUCT, MEMBER) decltype(STRUCT::MEMBER)
#define SDBUSCPP_STRUCT_MEMBERS_AS_DICT_ENTRIES(STRUCT, ...) \
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
/**/
#define SDBUSCPP_STRUCT_MEMBER_AS_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{STRUCT.MEMBER}}
#define SDBUSCPP_STRUCT_MEMBERS_AS_NESTED_DICT_ENTRIES(STRUCT, ...) \
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY, SDBUSCPP_PP_COMMA, STRUCT, __VA_ARGS__) \
/**/
#define SDBUSCPP_STRUCT_MEMBER_AS_NESTED_DICT_ENTRY(STRUCT, MEMBER) {#MEMBER, Variant{as_dictionary_if_struct(STRUCT.MEMBER)}}
#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBERS(STRUCT, ...) \
SDBUSCPP_PP_CAT(SDBUSCPP_FOR_EACH_, SDBUSCPP_PP_NARG(__VA_ARGS__))(SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER, SDBUSCPP_PP_SPACE, STRUCT, __VA_ARGS__) \
/**/
#define SDBUSCPP_FIND_AND_DESERIALIZE_STRUCT_MEMBER(STRUCT, MEMBER) if (key == #MEMBER) STRUCT.MEMBER = value.get<decltype(STRUCT.MEMBER)>(); else
#define SDBUSCPP_FOR_EACH_1(M, D, S, M1) M(S, M1)
#define SDBUSCPP_FOR_EACH_2(M, D, S, M1, M2) M(S, M1) D M(S, M2)
#define SDBUSCPP_FOR_EACH_3(M, D, S, M1, M2, M3) M(S, M1) D M(S, M2) D M(S, M3)
#define SDBUSCPP_FOR_EACH_4(M, D, S, M1, M2, M3, M4) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4)
#define SDBUSCPP_FOR_EACH_5(M, D, S, M1, M2, M3, M4, M5) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5)
#define SDBUSCPP_FOR_EACH_6(M, D, S, M1, M2, M3, M4, M5, M6) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6)
#define SDBUSCPP_FOR_EACH_7(M, D, S, M1, M2, M3, M4, M5, M6, M7) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7)
#define SDBUSCPP_FOR_EACH_8(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8)
#define SDBUSCPP_FOR_EACH_9(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9)
#define SDBUSCPP_FOR_EACH_10(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10)
#define SDBUSCPP_FOR_EACH_11(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11)
#define SDBUSCPP_FOR_EACH_12(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12)
#define SDBUSCPP_FOR_EACH_13(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13)
#define SDBUSCPP_FOR_EACH_14(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14)
#define SDBUSCPP_FOR_EACH_15(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15)
#define SDBUSCPP_FOR_EACH_16(M, D, S, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15, M16) M(S, M1) D M(S, M2) D M(S, M3) D M(S, M4) D M(S, M5) D M(S, M6) D M(S, M7) D M(S, M8) D M(S, M9) D M(S, M10) D M(S, M11) D M(S, M12) D M(S, M13) D M(S, M14) D M(S, M15) D M(S, M16)
#define SDBUSCPP_PP_CAT(X, Y) SDBUSCPP_PP_CAT_IMPL(X, Y)
#define SDBUSCPP_PP_CAT_IMPL(X, Y) X##Y
#define SDBUSCPP_PP_NARG(...) SDBUSCPP_PP_NARG_IMPL(__VA_ARGS__, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define SDBUSCPP_PP_NARG_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _N, ...) _N
#define SDBUSCPP_PP_COMMA ,
#define SDBUSCPP_PP_SPACE
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif /* SDBUS_CXX_TYPES_H_ */
+112
View File
@@ -0,0 +1,112 @@
/**
* (C) 2016 - 2026 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
@@ -0,0 +1,301 @@
/**
* (C) 2016 - 2026 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_v<function_result_t<Function>>, "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_v<function_result_t<Function>>, "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_ */
+3 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file sdbus-c++.h
*
@@ -24,6 +24,7 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
// IWYU pragma: begin_exports
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/IProxy.h>
@@ -36,3 +37,4 @@
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/Flags.h>
// IWYU pragma: end_exports
+613 -302
View File
File diff suppressed because it is too large Load Diff
+150 -80
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Connection.h
*
@@ -27,44 +27,57 @@
#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 "ScopeGuard.h"
#include "ISdBus.h"
#include SDBUS_HEADER
#include <memory>
#include <thread>
#include <string>
#include SDBUS_HEADER
#include <thread>
#include <vector>
#include <atomic>
#include <mutex>
// Forward declarations
struct sd_event_source;
namespace sdbus {
class ObjectPath;
class InterfaceName;
class BusName;
using ServiceName = BusName;
class MemberName;
using MethodName = MemberName;
using SignalName = MemberName;
using PropertyName = MemberName;
} // namespace sdbus
namespace sdbus::internal {
class Connection final
: public sdbus::internal::IConnection
: public IConnection
{
public:
// Bus type tags
struct default_bus_t{};
inline static constexpr default_bus_t default_bus{};
static constexpr default_bus_t default_bus{};
struct system_bus_t{};
inline static constexpr system_bus_t system_bus{};
static constexpr system_bus_t system_bus{};
struct session_bus_t{};
inline static constexpr session_bus_t session_bus{};
static constexpr session_bus_t session_bus{};
struct custom_session_bus_t{};
inline static constexpr custom_session_bus_t custom_session_bus{};
static constexpr custom_session_bus_t custom_session_bus{};
struct remote_system_bus_t{};
inline static constexpr remote_system_bus_t remote_system_bus{};
static constexpr remote_system_bus_t remote_system_bus{};
struct private_bus_t{};
inline static constexpr private_bus_t private_bus{};
static constexpr private_bus_t private_bus{};
struct server_bus_t{};
inline static constexpr server_bus_t server_bus{};
static constexpr server_bus_t server_bus{};
struct sdbus_bus_t{}; // A bus connection created directly from existing sd_bus instance
inline static constexpr sdbus_bus_t sdbus_bus{};
static constexpr sdbus_bus_t sdbus_bus{};
struct pseudo_bus_t{}; // A bus connection that is not really established with D-Bus daemon
inline static constexpr pseudo_bus_t pseudo_bus{};
static constexpr pseudo_bus_t pseudo_bus{};
Connection(std::unique_ptr<ISdBus>&& interface, default_bus_t);
Connection(std::unique_ptr<ISdBus>&& interface, system_bus_t);
@@ -76,64 +89,96 @@ namespace sdbus::internal {
Connection(std::unique_ptr<ISdBus>&& interface, server_bus_t, int fd);
Connection(std::unique_ptr<ISdBus>&& interface, sdbus_bus_t, sd_bus *bus);
Connection(std::unique_ptr<ISdBus>&& interface, pseudo_bus_t);
Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;
Connection(Connection&&) = delete;
Connection& operator=(Connection&&) = delete;
~Connection() override;
void requestName(const std::string& name) override;
void releaseName(const std::string& name) override;
std::string getUniqueName() const override;
void requestName(const ServiceName & name) override;
void releaseName(const ServiceName& name) override;
[[nodiscard]] BusName getUniqueName() const override;
void enterEventLoop() override;
void enterEventLoopAsync() override;
void leaveEventLoop() override;
PollData getEventLoopPollData() const override;
bool processPendingRequest() override;
[[nodiscard]] PollData getEventLoopPollData() const override;
bool processPendingEvent() override;
Message getCurrentlyProcessedMessage() const 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 addObjectManager(const ObjectPath& objectPath) override;
Slot addObjectManager(const ObjectPath& objectPath, return_slot_t) override;
void setMethodCallTimeout(uint64_t timeout) override;
uint64_t getMethodCallTimeout() const override;
[[nodiscard]] uint64_t getMethodCallTimeout() const 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 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;
const ISdBus& getSdBusInterface() const override;
ISdBus& getSdBusInterface() override;
void attachSdEventLoop(sd_event *event, int priority) override;
void detachSdEventLoop() override;
sd_event *getSdEventLoop() override;
Slot addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
Slot addObjectVTable( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) override;
, void* userData
, return_slot_t ) 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;
[[nodiscard]] PlainMessage createPlainMessage() const override;
[[nodiscard]] MethodCall createMethodCall( const ServiceName& destination
, const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const MethodName& methodName ) const override;
[[nodiscard]] MethodCall createMethodCall( const char* destination
, const char* objectPath
, const char* interfaceName
, const char* methodName ) const override;
[[nodiscard]] Signal createSignal( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const SignalName& signalName ) const override;
[[nodiscard]] Signal createSignal( const char* objectPath
, const char* interfaceName
, const char* signalName ) const override;
void emitPropertiesChangedSignal( const 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 std::string& sender
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
Slot registerSignalHandler( const char* sender
, const char* objectPath
, const char* interfaceName
, const char* signalName
, sd_bus_message_handler_t callback
, void* userData ) override;
, void* userData
, return_slot_t ) override;
MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) override;
sd_bus_message* incrementMessageRefCount(sd_bus_message* sdbusMsg) override;
sd_bus_message* decrementMessageRefCount(sd_bus_message* sdbusMsg) override;
int querySenderCredentials(sd_bus_message* sdbusMsg, uint64_t mask, sd_bus_creds **creds) override;
sd_bus_creds* incrementCredsRefCount(sd_bus_creds* creds) override;
sd_bus_creds* decrementCredsRefCount(sd_bus_creds* creds) override;
sd_bus_message* callMethod(sd_bus_message* sdbusMsg, uint64_t timeout) override;
Slot callMethodAsync(sd_bus_message* sdbusMsg, sd_bus_message_handler_t callback, void* userData, uint64_t timeout, return_slot_t) override;
void sendMessage(sd_bus_message* sdbusMsg) override;
sd_bus_message* createMethodReply(sd_bus_message* sdbusMsg) override;
sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) override;
private:
using BusFactory = std::function<int(sd_bus**)>;
@@ -143,27 +188,46 @@ namespace sdbus::internal {
BusPtr openBus(const std::function<int(sd_bus**)>& busFactory);
BusPtr openPseudoBus();
void finishHandshake(sd_bus* bus);
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;
bool waitForNextEvent();
[[nodiscard]] bool arePendingMessagesInQueues() const;
void notifyEventLoopToExit();
void notifyEventLoopToWakeUpFromPoll();
void wakeUpEventLoopIfMessagesInQueue();
void joinWithEventLoop();
static std::vector</*const */char*> to_strv(const std::vector<std::string>& strings);
template <typename StringBasedType>
static std::vector</*const */char*> to_strv(const std::vector<StringBasedType>& strings);
static int sdbus_match_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_match_install_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
private:
#ifndef SDBUS_basu // sd_event integration is not supported if instead of libsystemd we are based on basu
static 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 *source);
static int onSdTimerEvent(sd_event_source *source, uint64_t usec, void *userdata);
static int onSdIoEvent(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int onSdInternalEvent(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int onSdEventPrepare(sd_event_source *source, void *userdata);
#endif
struct EventFd
{
EventFd();
EventFd(const EventFd&) = delete;
EventFd& operator=(const EventFd&) = delete;
EventFd(EventFd&&) = delete;
EventFd& operator=(EventFd&&) = delete;
~EventFd();
void notify();
bool clear();
int fd{-1};
};
@@ -171,22 +235,28 @@ namespace sdbus::internal {
{
message_handler callback;
message_handler installCallback;
Connection& connection;
sd_bus_slot *slot;
Connection& connection; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
Slot slot;
};
private:
std::unique_ptr<ISdBus> iface_;
// sd-event integration
struct SdEvent
{
Slot sdEvent;
Slot sdTimeEventSource;
Slot sdIoEventSource;
Slot sdInternalEventSource;
};
std::unique_ptr<ISdBus> sdbus_;
BusPtr bus_;
std::thread asyncLoopThread_;
std::atomic<std::thread::id> loopThreadId_;
std::mutex loopMutex_;
EventFd loopExitFd_;
EventFd eventFd_;
std::atomic<uint64_t> activeTimeout_{};
EventFd loopExitFd_; // To wake up event loop I/O polling to exit
EventFd eventFd_; // To wake up event loop I/O polling to re-enter poll with fresh PollData values
std::vector<Slot> floatingMatchRules_;
std::unique_ptr<SdEvent> sdEvent_; // Integration of systemd sd-event event loop implementation
};
}
} // namespace sdbus::internal
#endif /* SDBUS_CXX_INTERNAL_CONNECTION_H_ */
+23 -8
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Error.cpp
*
@@ -24,20 +24,35 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sdbus-c++/Error.h>
#include SDBUS_HEADER
#include "sdbus-c++/Error.h"
#include "ScopeGuard.h"
#include SDBUS_HEADER
#include <string>
#include <utility>
namespace sdbus
{
sdbus::Error createError(int errNo, const 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); };
std::string name(sdbusError.name);
std::string message(customMsg + " (" + sdbusError.message + ")");
return sdbus::Error(name, message);
Error::Name name(sd_bus_error_is_set(&sdbusError) != 0 ? sdbusError.name : "");
std::string message(std::move(customMsg));
if (!message.empty() && sdbusError.message != nullptr)
{
message.append(" (");
message.append(sdbusError.message);
message.append(")");
}
else if (sdbusError.message != nullptr)
{
message = sdbusError.message;
}
return {std::move(name), std::move(message)};
}
}
} // namespace sdbus
+3 -7
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Flags.cpp
*
@@ -26,6 +26,7 @@
#include <sdbus-c++/Flags.h>
#include SDBUS_HEADER
#include <cstdint>
namespace sdbus
{
@@ -33,7 +34,6 @@ namespace sdbus
{
uint64_t sdbusFlags{};
using namespace sdbus;
if (flags_.test(Flags::DEPRECATED))
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
if (!flags_.test(Flags::PRIVILEGED))
@@ -55,7 +55,6 @@ namespace sdbus
{
uint64_t sdbusFlags{};
using namespace sdbus;
if (flags_.test(Flags::DEPRECATED))
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
if (!flags_.test(Flags::PRIVILEGED))
@@ -70,7 +69,6 @@ namespace sdbus
{
uint64_t sdbusFlags{};
using namespace sdbus;
if (flags_.test(Flags::DEPRECATED))
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
@@ -81,7 +79,6 @@ namespace sdbus
{
uint64_t sdbusFlags{};
using namespace sdbus;
if (flags_.test(Flags::DEPRECATED))
sdbusFlags |= SD_BUS_VTABLE_DEPRECATED;
//if (!flags_.test(Flags::PRIVILEGED))
@@ -103,10 +100,9 @@ namespace sdbus
{
auto sdbusFlags = toSdBusPropertyFlags();
using namespace sdbus;
if (!flags_.test(Flags::PRIVILEGED))
sdbusFlags |= SD_BUS_VTABLE_UNPRIVILEGED;
return sdbusFlags;
}
}
} // namespace sdbus
+75 -44
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file IConnection.h
*
@@ -27,23 +27,33 @@
#ifndef SDBUS_CXX_INTERNAL_ICONNECTION_H_
#define SDBUS_CXX_INTERNAL_ICONNECTION_H_
#include <sdbus-c++/IConnection.h>
#include SDBUS_HEADER
#include <string>
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/TypeTraits.h"
#include <memory>
#include <functional>
#include SDBUS_HEADER
#include <vector>
// Forward declaration
// Forward declarations
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;
class Error;
namespace internal {
class ISdBus;
}
}
} // namespace internal
} // namespace sdbus
namespace sdbus::internal {
@@ -53,50 +63,71 @@ namespace sdbus::internal {
public:
~IConnection() override = default;
virtual const ISdBus& getSdBusInterface() const = 0;
virtual ISdBus& getSdBusInterface() = 0;
[[nodiscard]] virtual Slot addObjectVTable( const std::string& objectPath
, const std::string& interfaceName
[[nodiscard]] virtual Slot addObjectVTable( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const sd_bus_vtable* vtable
, void* userData ) = 0;
, void* userData
, return_slot_t ) = 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;
[[nodiscard]] virtual PlainMessage createPlainMessage() const = 0;
[[nodiscard]] virtual MethodCall createMethodCall( const ServiceName& destination
, const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const MethodName& methodName ) const = 0;
[[nodiscard]] virtual MethodCall createMethodCall( const char* destination
, const char* objectPath
, const char* interfaceName
, const char* methodName ) const = 0;
[[nodiscard]] virtual Signal createSignal( const ObjectPath& objectPath
, const InterfaceName& interfaceName
, const SignalName& signalName ) const = 0;
[[nodiscard]] virtual Signal createSignal( const char* objectPath
, const char* interfaceName
, const char* signalName ) const = 0;
virtual void 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 std::string& sender
, const std::string& objectPath
, const std::string& interfaceName
, const std::string& signalName
[[nodiscard]] virtual Slot registerSignalHandler( const char* sender
, const char* objectPath
, const char* interfaceName
, const char* signalName
, sd_bus_message_handler_t callback
, void* userData ) = 0;
, void* userData
, return_slot_t ) = 0;
virtual void notifyEventLoopNewTimeout() const = 0;
virtual MethodReply tryCallMethodSynchronously(const MethodCall& message, uint64_t timeout) = 0;
virtual sd_bus_message* incrementMessageRefCount(sd_bus_message* sdbusMsg) = 0;
virtual sd_bus_message* decrementMessageRefCount(sd_bus_message* sdbusMsg) = 0;
// TODO: Refactor to higher level (Creds class will ownership handling and getters)
virtual int querySenderCredentials(sd_bus_message* sdbusMsg, uint64_t mask, sd_bus_creds **creds) = 0;
virtual sd_bus_creds* incrementCredsRefCount(sd_bus_creds* creds) = 0;
virtual sd_bus_creds* decrementCredsRefCount(sd_bus_creds* creds) = 0;
virtual sd_bus_message* callMethod(sd_bus_message* sdbusMsg, uint64_t timeout) = 0;
[[nodiscard]] virtual Slot callMethodAsync( sd_bus_message* sdbusMsg
, sd_bus_message_handler_t callback
, void* userData
, uint64_t timeout
, return_slot_t ) = 0;
virtual void sendMessage(sd_bus_message* sdbusMsg) = 0;
virtual sd_bus_message* createMethodReply(sd_bus_message* sdbusMsg) = 0;
virtual sd_bus_message* createErrorReplyMessage(sd_bus_message* sdbusMsg, const Error& error) = 0;
};
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createConnection();
[[nodiscard]] std::unique_ptr<sdbus::internal::IConnection> createPseudoConnection();
[[nodiscard]] std::unique_ptr<IConnection> createPseudoConnection();
}
} // namespace sdbus::internal
#endif /* SDBUS_CXX_INTERNAL_ICONNECTION_H_ */
+27 -24
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ISdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -44,18 +44,18 @@ namespace sdbus::internal {
virtual ~ISdBus() = default;
virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) = 0;
virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) = 0;
virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *msg) = 0;
virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *msg) = 0;
virtual int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) = 0;
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) = 0;
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) = 0;
virtual int sd_bus_send(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie) = 0;
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) = 0;
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) = 0;
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type) = 0;
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) = 0;
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) = 0;
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) = 0;
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) = 0;
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **msg, uint8_t type) = 0;
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member) = 0;
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member) = 0;
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **msg) = 0;
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err) = 0;
virtual int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) = 0;
virtual int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) = 0;
@@ -81,32 +81,35 @@ 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 int sd_bus_process(sd_bus *bus, sd_bus_message **ret) = 0;
virtual sd_bus_message* sd_bus_get_current_message(sd_bus *bus) = 0;
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) = 0;
virtual int sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) = 0;
virtual int sd_bus_flush(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_flush_close_unref(sd_bus *bus) = 0;
virtual sd_bus *sd_bus_close_unref(sd_bus *bus) = 0;
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) = 0;
virtual int sd_bus_message_set_destination(sd_bus_message *msg, const char *destination) = 0;
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) = 0;
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) = 0;
virtual int sd_bus_query_sender_creds(sd_bus_message *msg, uint64_t mask, sd_bus_creds **creds) = 0;
virtual sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *creds) = 0;
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *creds) = 0;
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) = 0;
virtual int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) = 0;
virtual int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *uid) = 0;
virtual int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) = 0;
virtual int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) = 0;
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) = 0;
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) = 0;
virtual int sd_bus_creds_get_pid(sd_bus_creds *creds, pid_t *pid) = 0;
virtual int sd_bus_creds_get_uid(sd_bus_creds *creds, uid_t *uid) = 0;
virtual int sd_bus_creds_get_euid(sd_bus_creds *creds, uid_t *uid) = 0;
virtual int sd_bus_creds_get_gid(sd_bus_creds *creds, gid_t *gid) = 0;
virtual int sd_bus_creds_get_egid(sd_bus_creds *creds, gid_t *egid) = 0;
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *creds, const gid_t **gids) = 0;
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *creds, const char **label) = 0;
};
}
} // namespace sdbus::internal
#endif //SDBUS_CXX_ISDBUS_H
+243 -214
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Message.cpp
*
@@ -24,39 +24,54 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Types.h>
#include <sdbus-c++/Error.h>
#include "MessageUtils.h"
#include "ISdBus.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/Error.h"
#include "sdbus-c++/Types.h"
#include "sdbus-c++/TypeTraits.h"
#include "IConnection.h"
#include "MessageUtils.h"
#include "ScopeGuard.h"
#include SDBUS_HEADER
#include <cassert>
#include <cerrno>
#include <cstdint> // int16_t, uint64_t, ...
#include <cstdio>
#include <cstdlib> // atexit
#include <cstring>
#include <string>
#include <string_view>
#include <sys/types.h> // pid_t, gid_t, ...
#include <tuple> // std::ignore
#include <memory> // std::unique_ptr
#include <utility> // std::move
#include <vector>
#include SDBUS_HEADER
namespace sdbus {
Message::Message(internal::ISdBus* sdbus) noexcept
: sdbus_(sdbus)
Message::Message(internal::IConnection* connection) noexcept
: connection_(connection)
{
assert(sdbus_ != nullptr);
assert(connection_ != nullptr);
}
Message::Message(void *msg, internal::ISdBus* sdbus) noexcept
Message::Message(void *msg, internal::IConnection* connection) noexcept
: msg_(msg)
, sdbus_(sdbus)
, connection_(connection)
{
assert(msg_ != nullptr);
assert(sdbus_ != nullptr);
sdbus_->sd_bus_message_ref((sd_bus_message*)msg_);
assert(connection_ != nullptr);
connection_->incrementMessageRefCount(static_cast<sd_bus_message*>(msg_));
}
Message::Message(void *msg, internal::ISdBus* sdbus, adopt_message_t) noexcept
Message::Message(void *msg, internal::IConnection* connection, adopt_message_t) noexcept
: msg_(msg)
, sdbus_(sdbus)
, connection_(connection)
{
assert(msg_ != nullptr);
assert(sdbus_ != nullptr);
assert(connection_ != nullptr);
}
Message::Message(const Message& other) noexcept
@@ -66,14 +81,17 @@ Message::Message(const Message& other) noexcept
Message& Message::operator=(const Message& other) noexcept
{
if (this == &other)
return *this;
if (msg_)
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
connection_->decrementMessageRefCount(static_cast<sd_bus_message*>(msg_));
msg_ = other.msg_;
sdbus_ = other.sdbus_;
connection_ = other.connection_;
ok_ = other.ok_;
sdbus_->sd_bus_message_ref((sd_bus_message*)msg_);
connection_->incrementMessageRefCount(static_cast<sd_bus_message*>(msg_));
return *this;
}
@@ -86,12 +104,12 @@ Message::Message(Message&& other) noexcept
Message& Message::operator=(Message&& other) noexcept
{
if (msg_)
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
connection_->decrementMessageRefCount(static_cast<sd_bus_message*>(msg_));
msg_ = other.msg_;
other.msg_ = nullptr;
sdbus_ = other.sdbus_;
other.sdbus_ = nullptr;
connection_ = other.connection_;
other.connection_ = nullptr;
ok_ = other.ok_;
other.ok_ = true;
@@ -101,14 +119,18 @@ Message& Message::operator=(Message&& other) noexcept
Message::~Message()
{
if (msg_)
sdbus_->sd_bus_message_unref((sd_bus_message*)msg_);
connection_->decrementMessageRefCount(static_cast<sd_bus_message*>(msg_));
}
Message& Message::operator<<(bool item)
{
int intItem = item;
int itemAsInt = static_cast<int>(item);
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
// Direct sd-bus method, bypassing SdBus mutex, are called here, since Message serialization/deserialization,
// as well as getter/setter methods are not thread safe by design. Additionally, they are called frequently,
// so some overhead is spared. What is thread-safe in Message class is Message constructors, copy/move operations
// and the destructor, so the Message instance can be passed from one thread to another safely.
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BOOLEAN, &itemAsInt);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a bool value", -r);
return *this;
@@ -116,7 +138,7 @@ Message& Message::operator<<(bool item)
Message& Message::operator<<(int16_t item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT16, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT16, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int16_t value", -r);
return *this;
@@ -124,7 +146,7 @@ Message& Message::operator<<(int16_t item)
Message& Message::operator<<(int32_t item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT32, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT32, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int32_t value", -r);
return *this;
@@ -132,7 +154,7 @@ Message& Message::operator<<(int32_t item)
Message& Message::operator<<(int64_t item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT64, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT64, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int64_t value", -r);
return *this;
@@ -140,7 +162,7 @@ Message& Message::operator<<(int64_t item)
Message& Message::operator<<(uint8_t item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BYTE, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BYTE, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a byte value", -r);
return *this;
@@ -148,7 +170,7 @@ Message& Message::operator<<(uint8_t item)
Message& Message::operator<<(uint16_t item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT16, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT16, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint16_t value", -r);
return *this;
@@ -156,7 +178,7 @@ Message& Message::operator<<(uint16_t item)
Message& Message::operator<<(uint32_t item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT32, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT32, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint32_t value", -r);
return *this;
@@ -164,7 +186,7 @@ Message& Message::operator<<(uint32_t item)
Message& Message::operator<<(uint64_t item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT64, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT64, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint64_t value", -r);
return *this;
@@ -172,7 +194,7 @@ Message& Message::operator<<(uint64_t item)
Message& Message::operator<<(double item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DOUBLE, &item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a double value", -r);
return *this;
@@ -180,7 +202,7 @@ Message& Message::operator<<(double item)
Message& Message::operator<<(const char* item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, item);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRING, item);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a C-string value", -r);
return *this;
@@ -188,12 +210,23 @@ Message& Message::operator<<(const char* item)
Message& Message::operator<<(const std::string& item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, item.c_str());
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRING, item.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a string value", -r);
return *this;
}
Message& Message::operator<<(std::string_view item)
{
char* destPtr{};
auto r = sd_bus_message_append_string_space(static_cast<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);
@@ -203,7 +236,7 @@ Message& Message::operator<<(const Variant &item)
Message& Message::operator<<(const ObjectPath &item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_OBJECT_PATH, item.c_str());
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_OBJECT_PATH, item.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an ObjectPath value", -r);
return *this;
@@ -211,7 +244,7 @@ Message& Message::operator<<(const ObjectPath &item)
Message& Message::operator<<(const Signature &item)
{
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, item.c_str());
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_SIGNATURE, item.c_str());
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a Signature value", -r);
return *this;
@@ -220,7 +253,7 @@ Message& Message::operator<<(const Signature &item)
Message& Message::operator<<(const UnixFd &item)
{
auto fd = item.get();
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
auto r = sd_bus_message_append_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UNIX_FD, &fd);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a UnixFd value", -r);
return *this;
@@ -228,7 +261,7 @@ Message& Message::operator<<(const UnixFd &item)
Message& Message::appendArray(char type, const void *ptr, size_t size)
{
auto r = sd_bus_message_append_array((sd_bus_message*)msg_, type, ptr, size);
auto r = sd_bus_message_append_array(static_cast<sd_bus_message*>(msg_), type, ptr, size);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an array", -r);
return *this;
@@ -236,8 +269,8 @@ Message& Message::appendArray(char type, const void *ptr, size_t size)
Message& Message::operator>>(bool& item)
{
int intItem;
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
int intItem{};
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BOOLEAN, &intItem);
if (r == 0)
ok_ = false;
@@ -250,7 +283,7 @@ Message& Message::operator>>(bool& item)
Message& Message::operator>>(int16_t& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT16, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT16, &item);
if (r == 0)
ok_ = false;
@@ -261,7 +294,7 @@ Message& Message::operator>>(int16_t& item)
Message& Message::operator>>(int32_t& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT32, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT32, &item);
if (r == 0)
ok_ = false;
@@ -272,7 +305,7 @@ Message& Message::operator>>(int32_t& item)
Message& Message::operator>>(int64_t& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT64, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_INT64, &item);
if (r == 0)
ok_ = false;
@@ -283,7 +316,7 @@ Message& Message::operator>>(int64_t& item)
Message& Message::operator>>(uint8_t& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BYTE, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_BYTE, &item);
if (r == 0)
ok_ = false;
@@ -294,7 +327,7 @@ Message& Message::operator>>(uint8_t& item)
Message& Message::operator>>(uint16_t& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT16, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT16, &item);
if (r == 0)
ok_ = false;
@@ -305,7 +338,7 @@ Message& Message::operator>>(uint16_t& item)
Message& Message::operator>>(uint32_t& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT32, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT32, &item);
if (r == 0)
ok_ = false;
@@ -316,7 +349,7 @@ Message& Message::operator>>(uint32_t& item)
Message& Message::operator>>(uint64_t& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT64, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UINT64, &item);
if (r == 0)
ok_ = false;
@@ -327,7 +360,7 @@ Message& Message::operator>>(uint64_t& item)
Message& Message::readArray(char type, const void **ptr, size_t *size)
{
auto r = sd_bus_message_read_array((sd_bus_message*)msg_, type, ptr, size);
auto r = sd_bus_message_read_array(static_cast<sd_bus_message*>(msg_), type, ptr, size);
if (r == 0)
ok_ = false;
@@ -338,7 +371,7 @@ Message& Message::readArray(char type, const void **ptr, size_t *size)
Message& Message::operator>>(double& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DOUBLE, &item);
if (r == 0)
ok_ = false;
@@ -349,7 +382,7 @@ Message& Message::operator>>(double& item)
Message& Message::operator>>(char*& item)
{
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, &item);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRING, reinterpret_cast<void*>(&item));
if (r == 0)
ok_ = false;
@@ -385,7 +418,7 @@ Message& Message::operator>>(Variant &item)
Message& Message::operator>>(ObjectPath &item)
{
char* str{};
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_OBJECT_PATH, &str);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_OBJECT_PATH, reinterpret_cast<void*>(&str));
if (r == 0)
ok_ = false;
@@ -400,7 +433,7 @@ Message& Message::operator>>(ObjectPath &item)
Message& Message::operator>>(Signature &item)
{
char* str{};
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, &str);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_SIGNATURE, reinterpret_cast<void*>(&str));
if (r == 0)
ok_ = false;
@@ -415,7 +448,7 @@ Message& Message::operator>>(Signature &item)
Message& Message::operator>>(UnixFd &item)
{
int fd = -1;
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UNIX_FD, &fd);
auto r = sd_bus_message_read_basic(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_UNIX_FD, &fd);
if (r == 0)
ok_ = false;
@@ -426,10 +459,9 @@ Message& Message::operator>>(UnixFd &item)
return *this;
}
Message& Message::openContainer(const std::string& signature)
Message& Message::openContainer(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_ARRAY, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a container", -r);
return *this;
@@ -437,15 +469,15 @@ Message& Message::openContainer(const std::string& signature)
Message& Message::closeContainer()
{
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a container", -r);
return *this;
}
Message& Message::openDictEntry(const std::string& signature)
Message& Message::openDictEntry(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DICT_ENTRY, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a dictionary entry", -r);
return *this;
@@ -453,15 +485,15 @@ Message& Message::openDictEntry(const std::string& signature)
Message& Message::closeDictEntry()
{
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a dictionary entry", -r);
return *this;
}
Message& Message::openVariant(const std::string& signature)
Message& Message::openVariant(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_VARIANT, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a variant", -r);
return *this;
@@ -469,15 +501,15 @@ Message& Message::openVariant(const std::string& signature)
Message& Message::closeVariant()
{
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a variant", -r);
return *this;
}
Message& Message::openStruct(const std::string& signature)
Message& Message::openStruct(const char* signature)
{
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
auto r = sd_bus_message_open_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRUCT, signature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a struct", -r);
return *this;
@@ -485,16 +517,15 @@ Message& Message::openStruct(const std::string& signature)
Message& Message::closeStruct()
{
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
auto r = sd_bus_message_close_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a struct", -r);
return *this;
}
Message& Message::enterContainer(const std::string& signature)
Message& Message::enterContainer(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_ARRAY, signature);
if (r == 0)
ok_ = false;
@@ -505,15 +536,15 @@ Message& Message::enterContainer(const std::string& signature)
Message& Message::exitContainer()
{
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a container", -r);
return *this;
}
Message& Message::enterDictEntry(const std::string& signature)
Message& Message::enterDictEntry(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_DICT_ENTRY, signature);
if (r == 0)
ok_ = false;
@@ -524,15 +555,15 @@ Message& Message::enterDictEntry(const std::string& signature)
Message& Message::exitDictEntry()
{
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a dictionary entry", -r);
return *this;
}
Message& Message::enterVariant(const std::string& signature)
Message& Message::enterVariant(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_VARIANT, signature);
if (r == 0)
ok_ = false;
@@ -543,15 +574,15 @@ Message& Message::enterVariant(const std::string& signature)
Message& Message::exitVariant()
{
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a variant", -r);
return *this;
}
Message& Message::enterStruct(const std::string& signature)
Message& Message::enterStruct(const char* signature)
{
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
auto r = sd_bus_message_enter_container(static_cast<sd_bus_message*>(msg_), SD_BUS_TYPE_STRUCT, signature);
if (r == 0)
ok_ = false;
@@ -562,7 +593,7 @@ Message& Message::enterStruct(const std::string& signature)
Message& Message::exitStruct()
{
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
auto r = sd_bus_message_exit_container(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a struct", -r);
return *this;
@@ -581,7 +612,7 @@ void Message::clearFlags()
void Message::copyTo(Message& destination, bool complete) const
{
auto r = sd_bus_message_copy((sd_bus_message*)destination.msg_, (sd_bus_message*)msg_, complete);
auto r = sd_bus_message_copy(static_cast<sd_bus_message*>(destination.msg_), static_cast<sd_bus_message*>(msg_), complete); // NOLINT(readability-implicit-bool-conversion)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to copy the message", -r);
}
@@ -589,158 +620,186 @@ void Message::seal()
{
const auto messageCookie = 1;
const auto sealTimeout = 0;
auto r = sd_bus_message_seal((sd_bus_message*)msg_, messageCookie, sealTimeout);
auto r = sd_bus_message_seal(static_cast<sd_bus_message*>(msg_), messageCookie, sealTimeout);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to seal the message", -r);
}
void Message::rewind(bool complete)
{
auto r = sd_bus_message_rewind((sd_bus_message*)msg_, complete);
auto r = sd_bus_message_rewind(static_cast<sd_bus_message*>(msg_), complete); // NOLINT(readability-implicit-bool-conversion)
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
}
std::string Message::getInterfaceName() const
std::string Message::dumpToString(DumpFlags flags) const
{
auto interface = sd_bus_message_get_interface((sd_bus_message*)msg_);
return interface != nullptr ? interface : "";
#if LIBSYSTEMD_VERSION>=245 && !defined(SDBUS_basu)
char* buffer{};
SCOPE_EXIT{ free(buffer); }; // NOLINT(cppcoreguidelines-no-malloc,hicpp-no-malloc,cppcoreguidelines-owning-memory)
size_t size{};
const std::unique_ptr<FILE, int(*)(FILE*)> stream{open_memstream(&buffer, &size), fclose};
SDBUS_THROW_ERROR_IF(!stream, "Failed to open memory stream", errno);
auto r = sd_bus_message_dump(static_cast<sd_bus_message*>(msg_), stream.get(), static_cast<uint64_t>(flags));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to dump the message", -r);
(void)fflush(stream.get());
return {buffer, size};
#else
(void)flags;
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Dumping sd-bus message not supported by underlying version of libsystemd");
#endif
}
std::string Message::getMemberName() const
const char* Message::getInterfaceName() const
{
auto member = sd_bus_message_get_member((sd_bus_message*)msg_);
return member != nullptr ? member : "";
return sd_bus_message_get_interface(static_cast<sd_bus_message*>(msg_));
}
std::string Message::getSender() const
const char* Message::getMemberName() const
{
return sd_bus_message_get_sender((sd_bus_message*)msg_);
return sd_bus_message_get_member(static_cast<sd_bus_message*>(msg_));
}
std::string Message::getPath() const
const char* Message::getSender() const
{
auto path = sd_bus_message_get_path((sd_bus_message*)msg_);
return path != nullptr ? path : "";
return sd_bus_message_get_sender(static_cast<sd_bus_message*>(msg_));
}
std::string Message::getDestination() const
const char* Message::getPath() const
{
auto destination = sd_bus_message_get_destination((sd_bus_message*)msg_);
return destination != nullptr ? destination : "";
return sd_bus_message_get_path(static_cast<sd_bus_message*>(msg_));
}
void Message::peekType(std::string& type, std::string& contents) const
const char* Message::getDestination() const
{
char typeSig;
const char* contentsSig;
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig);
return sd_bus_message_get_destination(static_cast<sd_bus_message*>(msg_));
}
uint64_t Message::getCookie() const
{
uint64_t cookie{};
auto r = sd_bus_message_get_cookie(static_cast<sd_bus_message*>(msg_), &cookie);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
return cookie;
}
std::pair<char, const char*> Message::peekType() const
{
char typeSignature{};
const char* contentsSignature{};
auto r = sd_bus_message_peek_type(static_cast<sd_bus_message*>(msg_), &typeSignature, &contentsSignature);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r);
type = typeSig;
contents = contentsSig ? contentsSig : "";
return {typeSignature, contentsSignature};
}
bool Message::isValid() const
{
return msg_ != nullptr && sdbus_ != nullptr;
return msg_ != nullptr && connection_ != nullptr;
}
bool Message::isEmpty() const
{
return sd_bus_message_is_empty((sd_bus_message*)msg_) != 0;
return sd_bus_message_is_empty(static_cast<sd_bus_message*>(msg_)) != 0;
}
bool Message::isAtEnd(bool complete) const
{
return sd_bus_message_at_end((sd_bus_message*)msg_, complete) > 0;
return sd_bus_message_at_end(static_cast<sd_bus_message*>(msg_), complete) > 0; // NOLINT(readability-implicit-bool-conversion)
}
// TODO: Create a RAII ownership class for creds with copy&move semantics, doing ref()/unref() under the hood.
// Create a method Message::querySenderCreds() that will return an object of this class by value, through IConnection and SdBus mutex.
// The class will expose methods like getPid(), getUid(), etc. that will directly call sd_bus_creds_* functions, no need for mutex here.
pid_t Message::getCredsPid() const
{
uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT;
const uint64_t mask = SD_BUS_CREDS_PID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
int r = connection_->querySenderCredentials(static_cast<sd_bus_message*>(msg_), mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
pid_t pid = 0;
r = sdbus_->sd_bus_creds_get_pid(creds, &pid);
r = sd_bus_creds_get_pid(creds, &pid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred pid", -r);
return pid;
}
uid_t Message::getCredsUid() const
{
uint64_t mask = SD_BUS_CREDS_UID | SD_BUS_CREDS_AUGMENT;
const uint64_t mask = SD_BUS_CREDS_UID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
int r = connection_->querySenderCredentials(static_cast<sd_bus_message*>(msg_), mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
uid_t uid = (uid_t)-1;
r = sdbus_->sd_bus_creds_get_uid(creds, &uid);
auto uid = static_cast<uid_t>(-1);
r = sd_bus_creds_get_uid(creds, &uid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred uid", -r);
return uid;
}
uid_t Message::getCredsEuid() const
{
uint64_t mask = SD_BUS_CREDS_EUID | SD_BUS_CREDS_AUGMENT;
const uint64_t mask = SD_BUS_CREDS_EUID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
int r = connection_->querySenderCredentials(static_cast<sd_bus_message*>(msg_), mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
uid_t euid = (uid_t)-1;
r = sdbus_->sd_bus_creds_get_euid(creds, &euid);
auto euid = static_cast<uid_t>(-1);
r = sd_bus_creds_get_euid(creds, &euid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred euid", -r);
return euid;
}
gid_t Message::getCredsGid() const
{
uint64_t mask = SD_BUS_CREDS_GID | SD_BUS_CREDS_AUGMENT;
const uint64_t mask = SD_BUS_CREDS_GID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
int r = connection_->querySenderCredentials(static_cast<sd_bus_message*>(msg_), mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
gid_t gid = (gid_t)-1;
r = sdbus_->sd_bus_creds_get_gid(creds, &gid);
auto gid = static_cast<gid_t>(-1);
r = sd_bus_creds_get_gid(creds, &gid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred gid", -r);
return gid;
}
gid_t Message::getCredsEgid() const
{
uint64_t mask = SD_BUS_CREDS_EGID | SD_BUS_CREDS_AUGMENT;
const uint64_t mask = SD_BUS_CREDS_EGID | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
int r = connection_->querySenderCredentials(static_cast<sd_bus_message*>(msg_), mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
gid_t egid = (gid_t)-1;
r = sdbus_->sd_bus_creds_get_egid(creds, &egid);
auto egid = static_cast<gid_t>(-1);
r = sd_bus_creds_get_egid(creds, &egid);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred egid", -r);
return egid;
}
std::vector<gid_t> Message::getCredsSupplementaryGids() const
{
uint64_t mask = SD_BUS_CREDS_SUPPLEMENTARY_GIDS | SD_BUS_CREDS_AUGMENT;
const uint64_t mask = SD_BUS_CREDS_SUPPLEMENTARY_GIDS | SD_BUS_CREDS_AUGMENT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
int r = connection_->querySenderCredentials(static_cast<sd_bus_message*>(msg_), mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
const gid_t *cGids = nullptr;
r = sdbus_->sd_bus_creds_get_supplementary_gids(creds, &cGids);
r = sd_bus_creds_get_supplementary_gids(creds, &cGids);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred supplementary gids", -r);
std::vector<gid_t> gids{};
if (cGids != nullptr)
{
for (int i = 0; i < r; i++)
gids.push_back(cGids[i]);
gids.push_back(cGids[i]); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
return gids;
@@ -748,38 +807,35 @@ std::vector<gid_t> Message::getCredsSupplementaryGids() const
std::string Message::getSELinuxContext() const
{
uint64_t mask = SD_BUS_CREDS_AUGMENT | SD_BUS_CREDS_SELINUX_CONTEXT;
const uint64_t mask = SD_BUS_CREDS_AUGMENT | SD_BUS_CREDS_SELINUX_CONTEXT;
sd_bus_creds *creds = nullptr;
SCOPE_EXIT{ sdbus_->sd_bus_creds_unref(creds); };
int r = sdbus_->sd_bus_query_sender_creds((sd_bus_message*)msg_, mask, &creds);
SCOPE_EXIT{ connection_->decrementCredsRefCount(creds); };
int r = connection_->querySenderCredentials(static_cast<sd_bus_message*>(msg_), mask, &creds);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus creds", -r);
const char *cLabel = nullptr;
r = sdbus_->sd_bus_creds_get_selinux_context(creds, &cLabel);
r = sd_bus_creds_get_selinux_context(creds, &cLabel);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus cred selinux context", -r);
return cLabel;
}
MethodCall::MethodCall( void *msg
, internal::ISdBus *sdbus
, const internal::IConnection *connection
, internal::IConnection *connection
, adopt_message_t) noexcept
: Message(msg, sdbus, adopt_message)
, connection_(connection)
: Message(msg, connection, adopt_message)
{
assert(connection_ != nullptr);
}
void MethodCall::dontExpectReply()
{
auto r = sd_bus_message_set_expect_reply((sd_bus_message*)msg_, 0);
auto r = sd_bus_message_set_expect_reply(static_cast<sd_bus_message*>(msg_), 0);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set the dont-expect-reply flag", -r);
}
bool MethodCall::doesntExpectReply() const
{
auto r = sd_bus_message_get_expect_reply((sd_bus_message*)msg_);
auto r = sd_bus_message_get_expect_reply(static_cast<sd_bus_message*>(msg_));
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get the dont-expect-reply flag", -r);
return r == 0;
}
@@ -788,100 +844,69 @@ MethodReply MethodCall::send(uint64_t timeout) const
{
if (!doesntExpectReply())
return sendWithReply(timeout);
else
return sendWithNoReply();
return sendWithNoReply();
}
MethodReply MethodCall::sendWithReply(uint64_t timeout) const
{
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
auto* sdbusReply = connection_->callMethod(static_cast<sd_bus_message*>(msg_), timeout);
sd_bus_message* sdbusReply{};
auto r = sdbus_->sd_bus_call(nullptr, (sd_bus_message*)msg_, timeout, &sdbusError, &sdbusReply);
if (sd_bus_error_is_set(&sdbusError))
throw sdbus::Error(sdbusError.name, sdbusError.message);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
return Factory::create<MethodReply>(sdbusReply, connection_, adopt_message);
}
MethodReply MethodCall::sendWithNoReply() const
{
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method with no reply", -r);
connection_->sendMessage(static_cast<sd_bus_message*>(msg_));
return Factory::create<MethodReply>(); // No reply
}
void MethodCall::send(void* callback, void* userData, uint64_t timeout, dont_request_slot_t) const
Slot MethodCall::send(void* callback, void* userData, uint64_t timeout, return_slot_t) const // NOLINT(bugprone-easily-swappable-parameters)
{
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); }};
return connection_->callMethodAsync(static_cast<sd_bus_message*>(msg_), reinterpret_cast<sd_bus_message_handler_t>(callback), userData, timeout, return_slot);
}
MethodReply MethodCall::createReply() const
{
sd_bus_message* sdbusReply{};
auto r = sdbus_->sd_bus_message_new_method_return((sd_bus_message*)msg_, &sdbusReply);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
auto* sdbusReply = connection_->createMethodReply(static_cast<sd_bus_message*>(msg_));
return Factory::create<MethodReply>(sdbusReply, sdbus_, adopt_message);
return Factory::create<MethodReply>(sdbusReply, connection_, adopt_message);
}
MethodReply MethodCall::createErrorReply(const Error& error) const
{
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str());
sd_bus_message* sdbusErrorReply = connection_->createErrorReplyMessage(static_cast<sd_bus_message*>(msg_), error);
sd_bus_message* sdbusErrorReply{};
auto r = sdbus_->sd_bus_message_new_method_error((sd_bus_message*)msg_, &sdbusErrorReply, &sdbusError);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
return Factory::create<MethodReply>(sdbusErrorReply, sdbus_, adopt_message);
return Factory::create<MethodReply>(sdbusErrorReply, connection_, adopt_message);
}
void MethodReply::send() const
{
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
connection_->sendMessage(static_cast<sd_bus_message*>(msg_));
}
uint64_t MethodReply::getReplyCookie() const
{
uint64_t cookie{};
auto r = sd_bus_message_get_reply_cookie(static_cast<sd_bus_message*>(msg_), &cookie);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get cookie", -r);
return cookie;
}
void Signal::send() const
{
auto r = sdbus_->sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
connection_->sendMessage(static_cast<sd_bus_message*>(msg_));
}
void Signal::setDestination(const std::string& destination)
{
auto r = sdbus_->sd_bus_message_set_destination((sd_bus_message*)msg_, destination.c_str());
setDestination(destination.c_str());
}
void Signal::setDestination(const char* destination)
{
auto r = sd_bus_message_set_destination(static_cast<sd_bus_message*>(msg_), destination);
SDBUS_THROW_ERROR_IF(r < 0, "Failed to set signal destination", -r);
}
@@ -897,27 +922,31 @@ 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.
/*constinit (C++20 keyword) */ static bool pseudoConnectionDestroyed{};
#ifdef __cpp_constinit
constinit bool pseudoConnectionDestroyed{}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
#else
bool pseudoConnectionDestroyed{}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
#endif
std::unique_ptr<sdbus::internal::IConnection, void(*)(sdbus::internal::IConnection*)> createPseudoConnection()
std::unique_ptr<internal::IConnection, void(*)(internal::IConnection*)> createPseudoConnection()
{
auto deleter = [](sdbus::internal::IConnection* con)
auto deleter = [](internal::IConnection* con)
{
delete con;
delete con; // NOLINT(cppcoreguidelines-owning-memory)
pseudoConnectionDestroyed = true;
};
return {internal::createPseudoConnection().release(), std::move(deleter)};
}
sdbus::internal::IConnection& getPseudoConnectionInstance()
internal::IConnection& getPseudoConnectionInstance()
{
static auto connection = createPseudoConnection();
if (pseudoConnectionDestroyed)
{
connection = createPseudoConnection(); // Phoenix rising from the ashes
atexit([](){ connection.~unique_ptr(); }); // We have to manually take care of deleting the phoenix
std::ignore = atexit([](){ connection.~unique_ptr(); }); // We have to manually take care of deleting the phoenix
pseudoConnectionDestroyed = false;
}
@@ -926,7 +955,7 @@ sdbus::internal::IConnection& getPseudoConnectionInstance()
return *connection;
}
}
} // namespace
PlainMessage createPlainMessage()
{
@@ -934,8 +963,8 @@ PlainMessage createPlainMessage()
// This is a bit of a hack, but it enables use to work with D-Bus message locally without
// the need of D-Bus daemon. This is especially useful in unit tests of both sdbus-c++ and client code.
// Additionally, it's light-weight and fast solution.
auto& connection = getPseudoConnectionInstance();
const auto& connection = getPseudoConnectionInstance();
return connection.createPlainMessage();
}
}
} // namespace sdbus
+14 -22
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file MessageUtils.h
*
@@ -34,38 +34,30 @@ namespace sdbus
class Message::Factory
{
public:
template<typename _Msg>
static _Msg create()
template<typename Msg>
static Msg create()
{
return _Msg{};
return Msg{};
}
template<typename _Msg>
static _Msg create(void *msg)
template<typename Msg>
static Msg create(void *msg)
{
return _Msg{msg};
return Msg{msg};
}
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus)
template<typename Msg>
static Msg create(void *msg, internal::IConnection* connection)
{
return _Msg{msg, sdbus};
return Msg{msg, connection};
}
template<typename _Msg>
static _Msg create(void *msg, internal::ISdBus* sdbus, adopt_message_t)
template<typename Msg>
static Msg create(void *msg, internal::IConnection* connection, adopt_message_t)
{
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};
return Msg{msg, connection, adopt_message};
}
};
PlainMessage createPlainMessage();
}
} // namespace sdbus
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */
+230 -240
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Object.cpp
*
@@ -25,172 +25,105 @@
*/
#include "Object.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 "sdbus-c++/Error.h"
#include "sdbus-c++/Flags.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/IObject.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/TypeTraits.h"
#include "sdbus-c++/VTableItems.h"
#include "IConnection.h"
#include "MessageUtils.h"
#include "Utils.h"
#include "VTableUtils.h"
#include SDBUS_HEADER
#include <utility>
#include <algorithm>
#include <cassert>
#include <cerrno>
#include <memory>
#include SDBUS_HEADER
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
namespace sdbus::internal {
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
Object::Object(IConnection& connection, ObjectPath objectPath)
: connection_(connection), objectPath_(std::move(objectPath))
{
SDBUS_CHECK_OBJECT_PATH(objectPath_);
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
}
void Object::registerMethod( const std::string& interfaceName
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags )
void Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
{
registerMethod( interfaceName
, std::move(methodName)
, std::move(inputSignature)
, {}
, std::move(outputSignature)
, {}
, std::move(methodCallback)
, std::move(flags) );
auto slot = Object::addVTable(std::move(interfaceName), std::move(vtable), return_slot);
vtables_.push_back(std::move(slot));
}
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 )
Slot Object::addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t)
{
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(methodName);
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
SDBUS_CHECK_INTERFACE_NAME(interfaceName.c_str());
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;
// 1st pass -- create vtable structure for internal sdbus-c++ purposes
auto internalVTable = std::make_unique<VTable>(createInternalVTable(std::move(interfaceName), std::move(vtable)));
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
}
// 2nd pass -- from internal sdbus-c++ vtable, create vtable structure in format expected by underlying sd-bus library
internalVTable->sdbusVTable = createInternalSdBusVTable(*internalVTable);
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));
}
// 3rd step -- register the vtable with sd-bus
internalVTable->slot = connection_.addObjectVTable( objectPath_
, internalVTable->interfaceName
, internalVTable->sdbusVTable.data()
, internalVTable.get()
, return_slot );
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);
}
// Return vtable wrapped in a Slot object
return {internalVTable.release(), [](void *ptr){ delete static_cast<VTable*>(ptr); }}; // NOLINT(cppcoreguidelines-owning-memory)
}
void Object::unregister()
{
interfaces_.clear();
removeObjectManager();
vtables_.clear();
objectManagerSlot_.reset();
}
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
Signal Object::createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const
{
return connection_.createSignal(objectPath_, interfaceName, signalName);
}
void Object::emitSignal(const sdbus::Signal& message)
Signal Object::createSignal(const char* interfaceName, const char* signalName) const
{
return connection_.createSignal(objectPath_.c_str(), interfaceName, signalName);
}
void Object::emitSignal(const Signal& message)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid signal message provided", EINVAL);
message.send();
}
void Object::emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames)
void Object::emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames)
{
connection_.emitPropertiesChangedSignal(objectPath_, interfaceName, propNames);
}
void Object::emitPropertiesChangedSignal(const std::string& interfaceName)
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)
{
Object::emitPropertiesChangedSignal(interfaceName, {});
}
@@ -200,7 +133,7 @@ void Object::emitInterfacesAddedSignal()
connection_.emitInterfacesAddedSignal(objectPath_);
}
void Object::emitInterfacesAddedSignal(const std::vector<std::string>& interfaces)
void Object::emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces)
{
connection_.emitInterfacesAddedSignal(objectPath_, interfaces);
}
@@ -210,24 +143,19 @@ void Object::emitInterfacesRemovedSignal()
connection_.emitInterfacesRemovedSignal(objectPath_);
}
void Object::emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces)
void Object::emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces)
{
connection_.emitInterfacesRemovedSignal(objectPath_, interfaces);
}
void Object::addObjectManager()
{
objectManagerSlot_ = connection_.addObjectManager(objectPath_, request_slot);
objectManagerSlot_ = connection_.addObjectManager(objectPath_, return_slot);
}
void Object::removeObjectManager()
Slot Object::addObjectManager(return_slot_t)
{
objectManagerSlot_.reset();
}
bool Object::hasObjectManager() const
{
return objectManagerSlot_ != nullptr;
return connection_.addObjectManager(objectPath_, return_slot);
}
sdbus::IConnection& Object::getConnection() const
@@ -235,91 +163,161 @@ sdbus::IConnection& Object::getConnection() const
return connection_;
}
const std::string& Object::getObjectPath() const
const ObjectPath& Object::getObjectPath() const
{
return objectPath_;
}
const Message* Object::getCurrentlyProcessedMessage() const
Message Object::getCurrentlyProcessedMessage() const
{
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
return connection_.getCurrentlyProcessedMessage();
}
Object::InterfaceData& Object::getInterface(const std::string& interfaceName)
Object::VTable Object::createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable)
{
return interfaces_.emplace(interfaceName, *this).first->second;
}
VTable internalVTable;
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
{
auto& vtable = interfaceData.vtable;
assert(vtable.empty());
internalVTable.interfaceName = std::move(interfaceName);
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)
for (auto& vtableItem : vtable)
{
const auto& methodName = item.first;
const auto& methodData = item.second;
vtable.push_back(createVTableMethodItem( methodName.c_str()
, methodData.inputArgs.c_str()
, methodData.outputArgs.c_str()
, methodData.paramNames.c_str()
, &Object::sdbus_method_callback
, methodData.flags.toSdBusMethodFlags() ));
std::visit( overload{ [&](InterfaceFlagsVTableItem&& interfaceFlags){ writeInterfaceFlagsToVTable(std::move(interfaceFlags), internalVTable); }
, [&](MethodVTableItem&& method){ writeMethodRecordToVTable(std::move(method), internalVTable); }
, [&](SignalVTableItem&& signal){ writeSignalRecordToVTable(std::move(signal), internalVTable); }
, [&](PropertyVTableItem&& property){ writePropertyRecordToVTable(std::move(property), internalVTable); } }
, std::move(vtableItem) );
}
// Sort arrays so we can do fast searching for an item in sd-bus callback handlers
std::sort(internalVTable.methods.begin(), internalVTable.methods.end(), [](const auto& lhs, const auto& rhs){ return lhs.name < rhs.name; });
std::sort(internalVTable.signals.begin(), internalVTable.signals.end(), [](const auto& lhs, const auto& rhs){ return lhs.name < rhs.name; });
std::sort(internalVTable.properties.begin(), internalVTable.properties.end(), [](const auto& lhs, const auto& rhs){ return lhs.name < rhs.name; });
internalVTable.object = this;
return internalVTable;
}
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
void Object::writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable)
{
for (const auto& item : interfaceData.signals)
vtable.interfaceFlags = std::move(flags.flags);
}
void Object::writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable)
{
SDBUS_CHECK_MEMBER_NAME(method.name.c_str());
SDBUS_THROW_ERROR_IF(!method.callbackHandler, "Invalid method callback provided", EINVAL);
vtable.methods.push_back({ std::move(method.name)
, std::move(method.inputSignature)
, std::move(method.outputSignature)
, paramNamesToString(method.inputParamNames) + paramNamesToString(method.outputParamNames)
, std::move(method.callbackHandler)
, std::move(method.flags) });
}
void Object::writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable)
{
SDBUS_CHECK_MEMBER_NAME(signal.name.c_str());
vtable.signals.push_back({ std::move(signal.name)
, std::move(signal.signature)
, paramNamesToString(signal.paramNames)
, std::move(signal.flags) });
}
void Object::writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable)
{
SDBUS_CHECK_MEMBER_NAME(property.name.c_str());
SDBUS_THROW_ERROR_IF(!property.getter && !property.setter, "Invalid property callbacks provided", EINVAL);
vtable.properties.push_back({ std::move(property.name)
, std::move(property.signature)
, std::move(property.getter)
, std::move(property.setter)
, std::move(property.flags) });
}
std::vector<sd_bus_vtable> Object::createInternalSdBusVTable(const VTable& vtable)
{
std::vector<sd_bus_vtable> sdbusVTable;
startSdBusVTable(vtable.interfaceFlags, sdbusVTable);
for (const auto& methodItem : vtable.methods)
writeMethodRecordToSdBusVTable(methodItem, sdbusVTable);
for (const auto& signalItem : vtable.signals)
writeSignalRecordToSdBusVTable(signalItem, sdbusVTable);
for (const auto& propertyItem : vtable.properties)
writePropertyRecordToSdBusVTable(propertyItem, sdbusVTable);
finalizeSdBusVTable(sdbusVTable);
return sdbusVTable;
}
void Object::startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = createSdBusVTableStartItem(interfaceFlags.toSdBusInterfaceFlags());
vtable.push_back(std::move(vtableItem));
}
void Object::writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = createSdBusVTableMethodItem( method.name.c_str()
, method.inputSignature.c_str()
, method.outputSignature.c_str()
, method.paramNames.c_str()
, &Object::sdbus_method_callback
, method.flags.toSdBusMethodFlags() );
vtable.push_back(std::move(vtableItem));
}
void Object::writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = createSdBusVTableSignalItem( signal.name.c_str()
, signal.signature.c_str()
, signal.paramNames.c_str()
, signal.flags.toSdBusSignalFlags() );
vtable.push_back(std::move(vtableItem));
}
void Object::writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable)
{
auto vtableItem = !property.setCallback
? createSdBusVTableReadOnlyPropertyItem( property.name.c_str()
, property.signature.c_str()
, &Object::sdbus_property_get_callback
, property.flags.toSdBusPropertyFlags() )
: createSdBusVTableWritablePropertyItem( property.name.c_str()
, property.signature.c_str()
, &Object::sdbus_property_get_callback
, &Object::sdbus_property_set_callback
, property.flags.toSdBusWritablePropertyFlags() );
vtable.push_back(std::move(vtableItem));
}
void Object::finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable)
{
vtable.push_back(createSdBusVTableEndItem());
}
const Object::VTable::MethodItem* Object::findMethod(const VTable& vtable, std::string_view methodName)
{
auto it = std::lower_bound(vtable.methods.begin(), vtable.methods.end(), methodName, [](const auto& methodItem, const auto& methodName)
{
const auto& signalName = item.first;
const auto& signalData = item.second;
return methodItem.name < methodName;
});
vtable.push_back(createVTableSignalItem( signalName.c_str()
, signalData.signature.c_str()
, signalData.paramNames.c_str()
, signalData.flags.toSdBusSignalFlags() ));
}
return it != vtable.methods.end() && it->name == methodName ? &*it : nullptr;
}
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
const Object::VTable::PropertyItem* Object::findProperty(const VTable& vtable, std::string_view propertyName)
{
for (const auto& item : interfaceData.properties)
auto it = std::lower_bound(vtable.properties.begin(), vtable.properties.end(), propertyName, [](const auto& propertyItem, const auto& propertyName)
{
const auto& propertyName = item.first;
const auto& propertyData = item.second;
return propertyItem.name < propertyName;
});
if (!propertyData.setCallback)
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
, propertyData.signature.c_str()
, &Object::sdbus_property_get_callback
, 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);
return it != vtable.properties.end() && it->name == propertyName ? &*it : nullptr;
}
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
@@ -332,22 +330,17 @@ 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* interfaceData = static_cast<InterfaceData*>(userData);
assert(interfaceData != nullptr);
auto& object = interfaceData->object;
auto* vtable = static_cast<VTable*>(userData);
assert(vtable != nullptr);
assert(vtable->object != nullptr);
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object.connection_.getSdBusInterface());
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &vtable->object->connection_);
object.m_CurrentlyProcessedMessage.store(&message, std::memory_order_relaxed);
SCOPE_EXIT
{
object.m_CurrentlyProcessedMessage.store(nullptr, std::memory_order_relaxed);
};
const auto* methodItem = findMethod(*vtable, message.getMemberName());
assert(methodItem != nullptr);
assert(methodItem->callback);
auto& callback = interfaceData->methods[message.getMemberName()].callback;
assert(callback);
auto ok = invokeHandlerAndCatchErrors([&](){ callback(message); }, retError);
auto ok = invokeHandlerAndCatchErrors([&](){ methodItem->callback(std::move(message)); }, retError);
return ok ? 1 : -1;
}
@@ -360,21 +353,23 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
, void *userData
, sd_bus_error *retError )
{
auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(interfaceData != nullptr);
auto& object = interfaceData->object;
auto* vtable = static_cast<VTable*>(userData);
assert(vtable != nullptr);
assert(vtable->object != nullptr);
auto& callback = interfaceData->properties[property].getCallback;
// Getter can be empty - the case of "write-only" property
if (!callback)
const auto* propertyItem = findProperty(*vtable, property);
assert(propertyItem != nullptr);
// Getter may be empty - the case of "write-only" property
if (!propertyItem->getCallback)
{
sd_bus_error_set(retError, "org.freedesktop.DBus.Error.Failed", "Cannot read property as it is write-only");
return 1;
}
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object.connection_.getSdBusInterface());
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &vtable->object->connection_);
auto ok = invokeHandlerAndCatchErrors([&](){ callback(reply); }, retError);
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->getCallback(reply); }, retError);
return ok ? 1 : -1;
}
@@ -387,36 +382,31 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
, void *userData
, sd_bus_error *retError )
{
auto* interfaceData = static_cast<InterfaceData*>(userData);
assert(interfaceData != nullptr);
auto& object = interfaceData->object;
auto* vtable = static_cast<VTable*>(userData);
assert(vtable != nullptr);
assert(vtable->object != nullptr);
auto& callback = interfaceData->properties[property].setCallback;
assert(callback);
const auto* propertyItem = findProperty(*vtable, property);
assert(propertyItem != nullptr);
assert(propertyItem->setCallback);
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &object.connection_.getSdBusInterface());
auto value = Message::Factory::create<PropertySetCall>(sdbusValue, &vtable->object->connection_);
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);
auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->setCallback(std::move(value)); }, retError);
return ok ? 1 : -1;
}
}
} // namespace sdbus::internal
namespace sdbus {
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath)
std::unique_ptr<IObject> createObject(IConnection& connection, ObjectPath objectPath)
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(&connection);
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
return std::make_unique<sdbus::internal::Object>(*sdbusConnection, std::move(objectPath));
return std::make_unique<internal::Object>(*sdbusConnection, std::move(objectPath));
}
}
} // namespace sdbus
+88 -97
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Object.h
*
@@ -27,16 +27,26 @@
#ifndef SDBUS_CXX_INTERNAL_OBJECT_H_
#define SDBUS_CXX_INTERNAL_OBJECT_H_
#include <sdbus-c++/IObject.h>
#include "IConnection.h"
#include SDBUS_HEADER
#include <string>
#include <map>
#include <vector>
#include <functional>
#include <memory>
#include <atomic>
#include "sdbus-c++/IObject.h"
#include "sdbus-c++/Types.h"
#include <cassert>
#include <functional>
#include <list>
#include <memory>
#include <string>
#include <string_view>
#include SDBUS_HEADER
#include <vector>
// Forward declarations
namespace sdbus {
class IConnection;
namespace internal {
class IConnection;
} // namespace internal
} // namespace sdbus
namespace sdbus::internal {
@@ -44,117 +54,100 @@ namespace sdbus::internal {
: public IObject
{
public:
Object(sdbus::internal::IConnection& connection, std::string objectPath);
Object(IConnection& connection, ObjectPath objectPath);
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 addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable) override;
Slot addVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable, return_slot_t) override;
void unregister() override;
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
void emitSignal(const sdbus::Signal& message) override;
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& propNames) override;
void emitPropertiesChangedSignal(const std::string& interfaceName) override;
[[nodiscard]] Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) const override;
Signal createSignal(const char* interfaceName, const char* signalName) const override;
void emitSignal(const Signal& message) override;
void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector<PropertyName>& propNames) override;
void emitPropertiesChangedSignal(const char* interfaceName, const std::vector<PropertyName>& propNames) override;
void emitPropertiesChangedSignal(const InterfaceName& interfaceName) override;
void emitPropertiesChangedSignal(const char* interfaceName) override;
void emitInterfacesAddedSignal() override;
void emitInterfacesAddedSignal(const std::vector<std::string>& interfaces) override;
void emitInterfacesAddedSignal(const std::vector<InterfaceName>& interfaces) override;
void emitInterfacesRemovedSignal() override;
void emitInterfacesRemovedSignal(const std::vector<std::string>& interfaces) override;
void emitInterfacesRemovedSignal(const std::vector<InterfaceName>& interfaces) override;
void addObjectManager() override;
void removeObjectManager() override;
bool hasObjectManager() const override;
[[nodiscard]] Slot addObjectManager(return_slot_t) override;
sdbus::IConnection& getConnection() const override;
const std::string& getObjectPath() const override;
const Message* getCurrentlyProcessedMessage() const override;
[[nodiscard]] sdbus::IConnection& getConnection() const override;
[[nodiscard]] const ObjectPath& getObjectPath() const override;
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
private:
using InterfaceName = std::string;
struct InterfaceData
// A vtable record comprising methods, signals, properties, flags.
// Once created, it cannot be modified. Only new vtables records can be added.
// An interface can have any number of vtables attached to it, not only one.
struct VTable
{
InterfaceData(Object& object) : object(object) {}
InterfaceName interfaceName;
Flags interfaceFlags;
using MethodName = std::string;
struct MethodData
struct MethodItem
{
const std::string inputArgs;
const std::string outputArgs;
const std::string paramNames;
MethodName name;
Signature inputSignature;
Signature outputSignature;
std::string paramNames;
method_callback callback;
Flags flags;
};
std::map<MethodName, MethodData> methods;
using SignalName = std::string;
struct SignalData
// Array of method records sorted by method name
std::vector<MethodItem> methods;
struct SignalItem
{
const std::string signature;
const std::string paramNames;
SignalName name;
Signature signature;
std::string paramNames;
Flags flags;
};
std::map<SignalName, SignalData> signals;
using PropertyName = std::string;
struct PropertyData
// Array of signal records sorted by signal name
std::vector<SignalItem> signals;
struct PropertyItem
{
const std::string signature;
PropertyName name;
Signature signature;
property_get_callback getCallback;
property_set_callback setCallback;
Flags flags;
};
std::map<PropertyName, PropertyData> properties;
std::vector<sd_bus_vtable> vtable;
Flags flags;
Object& object;
// Array of signal records sorted by signal name
std::vector<PropertyItem> properties;
// VTable structure in format required by sd-bus API
std::vector<sd_bus_vtable> sdbusVTable;
// Back-reference to the owning object from sd-bus callback handlers
Object* object{};
// This is intentionally the last member, because it must be destructed first,
// releasing callbacks above before the callbacks themselves are destructed.
Slot slot;
};
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 );
VTable createInternalVTable(InterfaceName interfaceName, std::vector<VTableItem> vtable);
static void writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable);
static void writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable);
static void writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable);
static void writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable);
static std::vector<sd_bus_vtable> createInternalSdBusVTable(const VTable& vtable);
static void startSdBusVTable(const Flags& interfaceFlags, std::vector<sd_bus_vtable>& vtable);
static void writeMethodRecordToSdBusVTable(const VTable::MethodItem& method, std::vector<sd_bus_vtable>& vtable);
static void writeSignalRecordToSdBusVTable(const VTable::SignalItem& signal, std::vector<sd_bus_vtable>& vtable);
static void writePropertyRecordToSdBusVTable(const VTable::PropertyItem& property, std::vector<sd_bus_vtable>& vtable);
static void finalizeSdBusVTable(std::vector<sd_bus_vtable>& vtable);
static const VTable::MethodItem* findMethod(const VTable& vtable, std::string_view methodName);
static const VTable::PropertyItem* findProperty(const VTable& vtable, std::string_view propertyName);
static std::string paramNamesToString(const std::vector<std::string>& paramNames);
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
@@ -173,14 +166,12 @@ namespace sdbus::internal {
, void *userData
, sd_bus_error *retError );
private:
sdbus::internal::IConnection& connection_;
std::string objectPath_;
std::map<InterfaceName, InterfaceData> interfaces_;
IConnection& connection_; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
ObjectPath objectPath_;
std::vector<Slot> vtables_;
Slot objectManagerSlot_;
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
};
}
} // namespace sdbus::internal
#endif /* SDBUS_CXX_INTERNAL_OBJECT_H_ */
+264 -221
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Proxy.cpp
*
@@ -25,233 +25,238 @@
*/
#include "Proxy.h"
#include "sdbus-c++/Error.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/IProxy.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/TypeTraits.h"
#include "IConnection.h"
#include "MessageUtils.h"
#include "Utils.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Error.h"
#include "ScopeGuard.h"
#include SDBUS_HEADER
#include "Utils.h"
#include <algorithm>
#include <atomic>
#include <cassert>
#include <chrono>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <exception>
#include <future>
#include <memory>
#include <mutex>
#include <optional>
#include SDBUS_HEADER
#include <utility>
namespace sdbus::internal {
Proxy::Proxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
Proxy::Proxy(IConnection& connection, ServiceName destination, ObjectPath objectPath)
: connection_(&connection, [](IConnection *){ /* Intentionally left empty */ })
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
// The connection is not ours only, it is owned and managed by the user and we just reference
// it here, so we expect the client to manage the event loop upon this connection themselves.
}
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath )
Proxy::Proxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath )
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
// The connection is ours only, i.e. it's us who has to manage the event loop upon this connection,
// in order that we get and process signals, async call replies, and other messages from D-Bus.
connection_->enterEventLoopAsync();
}
Proxy::Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath
Proxy::Proxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath
, dont_run_event_loop_thread_t )
: connection_(std::move(connection))
, destination_(std::move(destination))
, objectPath_(std::move(objectPath))
{
SDBUS_CHECK_SERVICE_NAME(destination_);
SDBUS_CHECK_OBJECT_PATH(objectPath_);
SDBUS_CHECK_SERVICE_NAME(destination_.c_str());
SDBUS_CHECK_OBJECT_PATH(objectPath_.c_str());
// Even though the connection is ours only, we don't start an event loop thread.
// This proxy is meant to be created, used for simple synchronous D-Bus call(s) and then dismissed.
}
MethodCall Proxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
MethodCall Proxy::createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const
{
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
}
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
MethodCall Proxy::createMethodCall(const char* interfaceName, const char* methodName) const
{
// 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);
// 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);
return connection_->createMethodCall(destination_.c_str(), objectPath_.c_str(), interfaceName, methodName);
}
PendingAsyncCall Proxy::callMethod(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
MethodReply Proxy::callMethod(const MethodCall& message)
{
return Proxy::callMethod(message, /*timeout*/ 0);
}
MethodReply Proxy::callMethod(const MethodCall& message, uint64_t timeout)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid method call message provided", EINVAL);
return message.send(timeout);
}
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback)
{
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0);
}
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, return_slot_t)
{
return Proxy::callMethodAsync(message, std::move(asyncReplyCallback), /*timeout*/ 0, return_slot);
}
PendingAsyncCall Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout)
{
SDBUS_THROW_ERROR_IF(!message.isValid(), "Invalid async method call message provided", EINVAL);
auto 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};
auto asyncCallInfo = std::make_shared<AsyncCallInfo>(AsyncCallInfo{ .callback = std::move(asyncReplyCallback)
, .proxy = *this
, .slot = {}
, .floating = false });
callData->slot = message.send(callback, callData.get(), timeout);
asyncCallInfo->slot = message.send(reinterpret_cast<void*>(&Proxy::sdbus_async_reply_handler), asyncCallInfo.get(), timeout, return_slot);
pendingAsyncCalls_.addCall(std::move(callData));
auto asyncCallInfoWeakPtr = std::weak_ptr{asyncCallInfo};
return {weakData};
floatingAsyncCallSlots_.push_back(std::move(asyncCallInfo));
return PendingAsyncCall{asyncCallInfoWeakPtr};
}
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, with_future_t)
Slot Proxy::callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback, uint64_t timeout, return_slot_t)
{
return Proxy::callMethod(message, {}, with_future);
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
, .slot = {}
, .floating = true });
asyncCallInfo->slot = message.send(reinterpret_cast<void*>(&Proxy::sdbus_async_reply_handler), asyncCallInfo.get(), timeout, return_slot);
return {asyncCallInfo.release(), [](void *ptr){ delete static_cast<AsyncCallInfo*>(ptr); }}; // NOLINT(cppcoreguidelines-owning-memory)
}
std::future<MethodReply> Proxy::callMethod(const MethodCall& message, uint64_t timeout, with_future_t)
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, with_future_t)
{
return Proxy::callMethodAsync(message, {}, with_future);
}
std::future<MethodReply> Proxy::callMethodAsync(const MethodCall& message, uint64_t timeout, with_future_t)
{
auto promise = std::make_shared<std::promise<MethodReply>>();
auto future = promise->get_future();
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply& reply, const Error* error) noexcept
async_reply_handler asyncReplyCallback = [promise = std::move(promise)](MethodReply reply, std::optional<Error> error) noexcept
{
if (error == nullptr)
promise->set_value(reply);
if (!error)
promise->set_value(std::move(reply));
else
promise->set_exception(std::make_exception_ptr(*error));
promise->set_exception(std::make_exception_ptr(*std::move(error)));
};
(void)Proxy::callMethod(message, std::move(asyncReplyCallback), timeout);
(void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout);
return future;
}
MethodReply Proxy::sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout)
Awaitable<MethodReply> Proxy::callMethodAsync(const MethodCall& message, with_awaitable_t)
{
/*thread_local*/ SyncCallReplyData syncCallReplyData;
return Proxy::callMethodAsync(message, /*timeout*/ 0, with_awaitable);
}
async_reply_handler asyncReplyCallback = [&syncCallReplyData](MethodReply& reply, const Error* error)
Awaitable<MethodReply> Proxy::callMethodAsync(const MethodCall& message, uint64_t timeout, with_awaitable_t)
{
auto data = std::make_shared<AwaitableData<MethodReply>>();
async_reply_handler asyncReplyCallback = [data](MethodReply reply, std::optional<Error> error) noexcept
{
syncCallReplyData.sendMethodReplyToWaitingThread(reply, error);
if (!error)
data->result = std::move(reply);
else
data->result = std::make_exception_ptr(*std::move(error));
auto previous = data->status.exchange(AwaitableState::Completed, std::memory_order_acq_rel);
if (previous == AwaitableState::Waiting)
data->resumeCoroutine();
};
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);
(void)Proxy::callMethodAsync(message, std::move(asyncReplyCallback), timeout);
return syncCallReplyData.waitForMethodReply();
return Awaitable{data};
}
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
void Proxy::registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler )
{
Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler));
}
void Proxy::registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler )
{
auto slot = Proxy::registerSignalHandler(interfaceName, signalName, std::move(signalHandler), return_slot);
floatingSignalSlots_.push_back(std::move(slot));
}
Slot Proxy::registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler
, return_slot_t )
{
return Proxy::registerSignalHandler(interfaceName.c_str(), signalName.c_str(), std::move(signalHandler), return_slot);
}
Slot Proxy::registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler
, return_slot_t )
{
SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(signalName);
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
auto& interface = interfaces_[interfaceName];
auto signalInfo = std::make_unique<SignalInfo>(SignalInfo{std::move(signalHandler), *this, {}});
auto signalData = std::make_unique<InterfaceData::SignalData>(*this, std::move(signalHandler), nullptr);
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
signalInfo->slot = connection_->registerSignalHandler( destination_.c_str()
, objectPath_.c_str()
, interfaceName
, signalName
, &Proxy::sdbus_signal_handler
, signalInfo.get()
, return_slot );
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);
}
}
return {signalInfo.release(), [](void *ptr){ delete static_cast<SignalInfo*>(ptr); }}; // NOLINT(cppcoreguidelines-owning-memory)
}
void Proxy::unregister()
{
pendingAsyncCalls_.clear();
interfaces_.clear();
floatingAsyncCallSlots_.clear();
floatingSignalSlots_.clear();
}
sdbus::IConnection& Proxy::getConnection() const
@@ -259,53 +264,44 @@ sdbus::IConnection& Proxy::getConnection() const
return *connection_;
}
const std::string& Proxy::getObjectPath() const
const ObjectPath& Proxy::getObjectPath() const
{
return objectPath_;
}
const Message* Proxy::getCurrentlyProcessedMessage() const
Message Proxy::getCurrentlyProcessedMessage() const
{
return m_CurrentlyProcessedMessage.load(std::memory_order_relaxed);
return connection_->getCurrentlyProcessedMessage();
}
int Proxy::sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{
auto* asyncCallData = static_cast<AsyncCalls::CallData*>(userData);
assert(asyncCallData != nullptr);
assert(asyncCallData->callback);
auto& proxy = asyncCallData->proxy;
auto state = asyncCallData->state;
auto* asyncCallInfo = static_cast<AsyncCallInfo*>(userData);
assert(asyncCallInfo != nullptr);
assert(asyncCallInfo->callback);
auto& proxy = asyncCallInfo->proxy;
// We are removing the CallData item at the complete scope exit, after the callback has been invoked.
// We can't do it earlier (before callback invocation for example), because CallBack data (slot release)
// is the synchronization point between callback invocation and Proxy::unregister.
SCOPE_EXIT
{
// 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);
proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
};
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 message = Message::Factory::create<MethodReply>(sdbusMessage, proxy.connection_.get());
auto ok = invokeHandlerAndCatchErrors([&]
{
const auto* error = sd_bus_message_get_error(sdbusMessage);
if (error == nullptr)
{
asyncCallData->callback(message, nullptr);
asyncCallInfo->callback(std::move(message), {});
}
else
{
Error exception(error->name, error->message);
asyncCallData->callback(message, &exception);
Error exception(Error::Name{error->name}, error->message);
asyncCallInfo->callback(std::move(message), std::move(exception));
}
}, retError);
@@ -314,38 +310,75 @@ 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* signalData = static_cast<InterfaceData::SignalData*>(userData);
assert(signalData != nullptr);
assert(signalData->callback);
auto* signalInfo = static_cast<SignalInfo*>(userData);
assert(signalInfo != nullptr);
assert(signalInfo->callback);
auto message = Message::Factory::create<Signal>(sdbusMessage, &signalData->proxy.connection_->getSdBusInterface());
auto message = Message::Factory::create<Signal>(sdbusMessage, signalInfo->proxy.connection_.get());
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);
auto ok = invokeHandlerAndCatchErrors([&](){ signalInfo->callback(std::move(message)); }, retError);
return ok ? 0 : -1;
}
Proxy::FloatingAsyncCallSlots::~FloatingAsyncCallSlots()
{
clear();
}
void Proxy::FloatingAsyncCallSlots::push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo)
{
const 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::internal
namespace sdbus {
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callData)
: callData_(std::move(callData))
PendingAsyncCall::PendingAsyncCall(std::weak_ptr<void> callInfo)
: callInfo_(std::move(callInfo))
{
}
void PendingAsyncCall::cancel()
{
if (auto ptr = callData_.lock(); ptr != nullptr)
if (auto ptr = callInfo_.lock(); ptr != nullptr)
{
auto* callData = static_cast<internal::Proxy::AsyncCalls::CallData*>(ptr.get());
callData->proxy.pendingAsyncCalls_.removeCall(callData);
auto* asyncCallInfo = static_cast<internal::Proxy::AsyncCallInfo*>(ptr.get());
asyncCallInfo->proxy.floatingAsyncCallSlots_.erase(asyncCallInfo);
// At this point, the callData item is being deleted, leading to the release of the
// sd-bus slot pointer. This release locks the global sd-bus mutex. If the async
@@ -356,81 +389,91 @@ void PendingAsyncCall::cancel()
bool PendingAsyncCall::isPending() const
{
return !callData_.expired();
return !callInfo_.expired();
}
}
} // namespace sdbus
namespace sdbus {
std::unique_ptr<sdbus::IProxy> createProxy( IConnection& connection
, std::string destination
, std::string objectPath )
std::unique_ptr<IProxy> createProxy( IConnection& connection
, ServiceName destination
, ObjectPath objectPath )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(&connection);
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
return std::make_unique<sdbus::internal::Proxy>( *sdbusConnection
return std::make_unique<internal::Proxy>( *sdbusConnection
, std::move(destination)
, std::move(objectPath) );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, std::string destination
, std::string objectPath )
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved): connection is moved but cast to an internal type
std::unique_ptr<IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(connection.release());
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
connection.release();
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
return std::make_unique<internal::Proxy>( std::unique_ptr<internal::IConnection>(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, std::string destination
, std::string objectPath
, dont_run_event_loop_thread_t )
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved): connection is moved cast to an internal type
std::unique_ptr<IProxy> createProxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath
, dont_run_event_loop_thread_t )
{
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
auto* sdbusConnection = dynamic_cast<internal::IConnection*>(connection.release());
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
connection.release();
return std::make_unique<sdbus::internal::Proxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
, std::move(destination)
, std::move(objectPath)
, dont_run_event_loop_thread );
return std::make_unique<internal::Proxy>( std::unique_ptr<internal::IConnection>(sdbusConnection)
, std::move(destination)
, std::move(objectPath)
, dont_run_event_loop_thread );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath )
std::unique_ptr<IProxy> createLightWeightProxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath )
{
auto connection = sdbus::createConnection();
return createProxy(std::move(connection), std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
}
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
std::unique_ptr<IProxy> createProxy( ServiceName destination
, ObjectPath objectPath )
{
auto connection = createBusConnection();
auto sdbusConnection = std::unique_ptr<internal::IConnection>(dynamic_cast<internal::IConnection*>(connection.release()));
assert(sdbusConnection != nullptr);
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
return std::make_unique<internal::Proxy>( std::move(sdbusConnection)
, std::move(destination)
, std::move(objectPath) );
}
std::unique_ptr<sdbus::IProxy> createProxy( std::string destination
, std::string objectPath
std::unique_ptr<IProxy> createProxy( ServiceName destination
, ObjectPath objectPath
, dont_run_event_loop_thread_t )
{
auto connection = sdbus::createConnection();
auto connection = createBusConnection();
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
auto sdbusConnection = std::unique_ptr<internal::IConnection>(dynamic_cast<internal::IConnection*>(connection.release()));
assert(sdbusConnection != nullptr);
return std::make_unique<sdbus::internal::Proxy>( std::move(sdbusConnection)
, std::move(destination)
, std::move(objectPath)
, dont_run_event_loop_thread );
return std::make_unique<internal::Proxy>( std::move(sdbusConnection)
, std::move(destination)
, std::move(objectPath)
, dont_run_event_loop_thread );
}
std::unique_ptr<IProxy> createLightWeightProxy(ServiceName destination, ObjectPath objectPath)
{
return createProxy(std::move(destination), std::move(objectPath), dont_run_event_loop_thread);
}
} // namespace sdbus
+88 -136
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Proxy.h
*
@@ -27,16 +27,17 @@
#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_HEADER
#include <string>
#include <memory>
#include <map>
#include "sdbus-c++/Types.h"
#include <deque>
#include <memory>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <string>
#include SDBUS_HEADER
#include <vector>
namespace sdbus::internal {
@@ -44,159 +45,110 @@ namespace sdbus::internal {
: public IProxy
{
public:
Proxy( sdbus::internal::IConnection& connection
, std::string destination
, std::string objectPath );
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath );
Proxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
, std::string destination
, std::string objectPath
Proxy( IConnection& connection
, ServiceName destination
, ObjectPath objectPath );
Proxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath );
Proxy( std::unique_ptr<IConnection>&& connection
, ServiceName destination
, ObjectPath objectPath
, dont_run_event_loop_thread_t );
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
[[nodiscard]] MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) const override;
[[nodiscard]] MethodCall createMethodCall(const char* interfaceName, const char* methodName) const override;
MethodReply callMethod(const MethodCall& message) override;
MethodReply callMethod(const MethodCall& message, uint64_t timeout) override;
PendingAsyncCall 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;
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;
Awaitable<MethodReply> callMethodAsync(const MethodCall& message, with_awaitable_t) override;
Awaitable<MethodReply> callMethodAsync( const MethodCall& message
, uint64_t timeout
, with_awaitable_t ) override;
void registerSignalHandler( const std::string& interfaceName
, const std::string& signalName
void registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler ) override;
void unregisterSignalHandler( const std::string& interfaceName
, const std::string& signalName ) override;
void finishRegistration() override;
void registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler ) override;
Slot registerSignalHandler( const InterfaceName& interfaceName
, const SignalName& signalName
, signal_handler signalHandler
, return_slot_t ) override;
Slot registerSignalHandler( const char* interfaceName
, const char* signalName
, signal_handler signalHandler
, return_slot_t ) override;
void unregister() override;
sdbus::IConnection& getConnection() const override;
const std::string& getObjectPath() const override;
const Message* getCurrentlyProcessedMessage() const override;
[[nodiscard]] sdbus::IConnection& getConnection() const override;
[[nodiscard]] const ObjectPath& getObjectPath() const override;
[[nodiscard]] Message getCurrentlyProcessedMessage() const override;
private:
class SyncCallReplyData
{
public:
void sendMethodReplyToWaitingThread(MethodReply& reply, const Error* error);
MethodReply waitForMethodReply();
private:
std::mutex mutex_;
std::condition_variable cond_;
bool arrived_{};
MethodReply reply_;
std::unique_ptr<Error> error_;
};
MethodReply sendMethodCallMessageAndWaitForReply(const MethodCall& message, uint64_t timeout);
void registerSignalHandlers(sdbus::internal::IConnection& connection);
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_signal_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
static int sdbus_async_reply_handler(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
private:
friend PendingAsyncCall;
std::unique_ptr< sdbus::internal::IConnection
, std::function<void(sdbus::internal::IConnection*)>
> connection_;
std::string destination_;
std::string objectPath_;
std::unique_ptr<IConnection, std::function<void(IConnection*)>> connection_;
ServiceName destination_;
ObjectPath objectPath_;
using InterfaceName = std::string;
struct InterfaceData
std::vector<Slot> floatingSignalSlots_;
struct SignalInfo
{
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_;
signal_handler callback;
Proxy& proxy; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
Slot slot;
};
std::map<InterfaceName, InterfaceData> interfaces_;
// We need to keep track of pending async calls. When the proxy is being destructed, we must
// remove all slots of these pending calls, otherwise in case when the connection outlives
// the proxy, we might get async reply handlers invoked for pending async calls after the proxy
// has been destroyed, which is a free ticket into the realm of undefined behavior.
class AsyncCalls
struct AsyncCallInfo
{
async_reply_handler callback;
Proxy& proxy; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
Slot slot;
bool finished{false};
bool floating;
};
// Container keeping track of pending async calls
class FloatingAsyncCallSlots
{
public:
struct CallData
{
enum class State
{ NOT_ASYNC
, RUNNING
, FINISHED
};
FloatingAsyncCallSlots() = default;
FloatingAsyncCallSlots(const FloatingAsyncCallSlots&) = delete;
FloatingAsyncCallSlots& operator=(const FloatingAsyncCallSlots&) = delete;
FloatingAsyncCallSlots(FloatingAsyncCallSlots&& other) = delete;
FloatingAsyncCallSlots& operator=(FloatingAsyncCallSlots&&) = delete;
~FloatingAsyncCallSlots();
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.
}
void push_back(std::shared_ptr<AsyncCallInfo> asyncCallInfo);
void erase(AsyncCallInfo* info);
void clear();
private:
std::mutex mutex_;
std::deque<std::shared_ptr<CallData>> calls_;
} pendingAsyncCalls_;
std::deque<std::shared_ptr<AsyncCallInfo>> slots_;
};
std::atomic<const Message*> m_CurrentlyProcessedMessage{nullptr};
FloatingAsyncCallSlots floatingAsyncCallSlots_;
};
}
} // namespace sdbus::internal
#endif /* SDBUS_CXX_INTERNAL_PROXY_H_ */
+22 -16
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file ScopeGuard.h
*
@@ -27,8 +27,11 @@
#ifndef SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
#define SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
#include <exception>
#include <utility>
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
// Straightforward, modern, easy-to-use RAII utility to perform work on scope exit in an exception-safe manner.
//
// The utility helps providing basic exception safety guarantee by ensuring that the resources are always
@@ -104,21 +107,22 @@ namespace sdbus::internal {
}
};
template <class _Fun, typename _Tag>
template <class Fun, typename Tag>
class ScopeGuard
{
public:
ScopeGuard(_Fun f) : fnc_(std::move(f))
explicit ScopeGuard(Fun fun) : fnc_(std::move(fun))
{
}
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& rhs) : fnc_(std::move(rhs.fnc_)), active_(rhs.active_), exceptions_(rhs.exceptions_)
ScopeGuard(ScopeGuard&& rhs) noexcept : fnc_(std::move(rhs.fnc_)), exceptions_(rhs.exceptions_), active_(rhs.active_)
{
rhs.dismiss();
}
ScopeGuard& operator=(ScopeGuard&&) = delete;
void dismiss()
{
@@ -127,35 +131,35 @@ namespace sdbus::internal {
~ScopeGuard()
{
if (active_ && _Tag::holds(exceptions_))
if (active_ && Tag::holds(exceptions_))
fnc_();
}
private:
_Fun fnc_;
Fun fnc_;
int exceptions_{std::uncaught_exceptions()};
bool active_{true};
};
template <typename _Fun>
ScopeGuard<_Fun, ScopeGuardOnExitTag> operator+(ScopeGuardOnExitTag, _Fun&& fnc)
template <typename Fun>
ScopeGuard<Fun, ScopeGuardOnExitTag> operator+(ScopeGuardOnExitTag, Fun&& fnc)
{
return ScopeGuard<_Fun, ScopeGuardOnExitTag>(std::forward<_Fun>(fnc));
return ScopeGuard<Fun, ScopeGuardOnExitTag>(std::forward<Fun>(fnc));
}
template <typename _Fun>
ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag> operator+(ScopeGuardOnExitSuccessTag, _Fun&& fnc)
template <typename Fun>
ScopeGuard<Fun, ScopeGuardOnExitSuccessTag> operator+(ScopeGuardOnExitSuccessTag, Fun&& fnc)
{
return ScopeGuard<_Fun, ScopeGuardOnExitSuccessTag>(std::forward<_Fun>(fnc));
return ScopeGuard<Fun, ScopeGuardOnExitSuccessTag>(std::forward<Fun>(fnc));
}
template <typename _Fun>
ScopeGuard<_Fun, ScopeGuardOnExitFailureTag> operator+(ScopeGuardOnExitFailureTag, _Fun&& fnc)
template <typename Fun>
ScopeGuard<Fun, ScopeGuardOnExitFailureTag> operator+(ScopeGuardOnExitFailureTag, Fun&& fnc)
{
return ScopeGuard<_Fun, ScopeGuardOnExitFailureTag>(std::forward<_Fun>(fnc));
return ScopeGuard<Fun, ScopeGuardOnExitFailureTag>(std::forward<Fun>(fnc));
}
}
} // namespace sdbus::internal
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
@@ -166,4 +170,6 @@ namespace sdbus::internal {
#define ANONYMOUS_VARIABLE(str) CONCATENATE(str, __LINE__)
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif /* SDBUS_CPP_INTERNAL_SCOPEGUARD_H_ */
+126 -95
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file SdBus.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -26,151 +26,150 @@
*/
#include "SdBus.h"
#include <sdbus-c++/Error.h>
#include "sdbus-c++/Error.h" // NOLINT(misc-include-cleaner)
#include SDBUS_HEADER
#include <algorithm>
#include <cstdint>
#include <mutex>
#include <sys/types.h>
namespace sdbus::internal {
sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *m)
sd_bus_message* SdBus::sd_bus_message_ref(sd_bus_message *msg)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_ref(m);
return ::sd_bus_message_ref(msg);
}
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *m)
sd_bus_message* SdBus::sd_bus_message_unref(sd_bus_message *msg)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_unref(m);
return ::sd_bus_message_unref(msg);
}
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie)
int SdBus::sd_bus_send(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
auto r = ::sd_bus_send(bus, m, cookie);
auto r = ::sd_bus_send(bus, msg, cookie);
if (r < 0)
return r;
// Make sure long messages are not only stored in outgoing queues but also really sent out
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
return r;
}
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
int SdBus::sd_bus_call(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_call(bus, m, usec, ret_error, reply);
return ::sd_bus_call(bus, msg, usec, ret_error, reply);
}
int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)
int SdBus::sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
auto r = ::sd_bus_call_async(bus, slot, m, callback, userdata, usec);
auto r = ::sd_bus_call_async(bus, slot, msg, callback, userdata, usec);
if (r < 0)
return r;
// Make sure long messages are not only stored in outgoing queues but also really sent out
::sd_bus_flush(bus != nullptr ? bus : ::sd_bus_message_get_bus(m));
return r;
}
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type)
int SdBus::sd_bus_message_new(sd_bus *bus, sd_bus_message **msg, uint8_t type)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new(bus, m, type);
return ::sd_bus_message_new(bus, msg, type);
}
int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member)
int SdBus::sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_method_call(bus, m, destination, path, interface, member);
return ::sd_bus_message_new_method_call(bus, msg, destination, path, interface, member);
}
int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member)
int SdBus::sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_signal(bus, m, path, interface, member);
return ::sd_bus_message_new_signal(bus, msg, path, interface, member);
}
int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m)
int SdBus::sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **msg)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_method_return(call, m);
return ::sd_bus_message_new_method_return(call, msg);
}
int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e)
int SdBus::sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_new_method_error(call, m, e);
return ::sd_bus_message_new_method_error(call, msg, err);
}
int SdBus::sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec)
{
#if LIBSYSTEMD_VERSION>=240
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_set_method_call_timeout(bus, usec);
#else
(void)bus;
(void)usec;
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Setting general method call timeout not supported by underlying version of libsystemd");
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Setting general method call timeout not supported by underlying version of libsystemd");
#endif
}
int SdBus::sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret)
{
#if LIBSYSTEMD_VERSION>=240
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_get_method_call_timeout(bus, ret);
#else
(void)bus;
(void)ret;
throw sdbus::Error(SD_BUS_ERROR_NOT_SUPPORTED, "Getting general method call timeout not supported by underlying version of libsystemd");
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Getting general method call timeout not supported by underlying version of libsystemd");
#endif
}
int SdBus::sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_properties_changed_strv(bus, path, interface, names);
}
int SdBus::sd_bus_emit_object_added(sd_bus *bus, const char *path)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_object_added(bus, path);
}
int SdBus::sd_bus_emit_object_removed(sd_bus *bus, const char *path)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_object_removed(bus, path);
}
int SdBus::sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
}
int SdBus::sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
}
@@ -202,14 +201,14 @@ int SdBus::sd_bus_open_user_with_address(sd_bus **ret, const char* address)
if (r < 0)
return r;
r = ::sd_bus_set_bus_client(bus, true);
r = ::sd_bus_set_bus_client(bus, true); // NOLINT(readability-implicit-bool-conversion)
if (r < 0)
return r;
// Copying behavior from
// https://github.com/systemd/systemd/blob/fee6441601c979165ebcbb35472036439f8dad5f/src/libsystemd/sd-bus/sd-bus.c#L1381
// Here, we make the bus as trusted
r = ::sd_bus_set_trusted(bus, true);
r = ::sd_bus_set_trusted(bus, true); // NOLINT(readability-implicit-bool-conversion)
if (r < 0)
return r;
@@ -281,7 +280,7 @@ int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
if (r < 0)
return r;
r = ::sd_bus_set_server(bus, true, id);
r = ::sd_bus_set_server(bus, true, id); // NOLINT(readability-implicit-bool-conversion)
if (r < 0)
return r;
@@ -296,65 +295,75 @@ int SdBus::sd_bus_open_server(sd_bus **ret, int fd)
int SdBus::sd_bus_open_system_remote(sd_bus **ret, const char *host)
{
#ifdef SDBUS_basu
#ifndef SDBUS_basu
return ::sd_bus_open_system_remote(ret, host);
#else
(void)ret;
(void)host;
// https://git.sr.ht/~emersion/basu/commit/01d33b244eb6
return -EOPNOTSUPP;
#else
return ::sd_bus_open_system_remote(ret, host);
#endif
}
int SdBus::sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_request_name(bus, name, flags);
}
int SdBus::sd_bus_release_name(sd_bus *bus, const char *name)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_release_name(bus, name);
}
int SdBus::sd_bus_get_unique_name(sd_bus *bus, const char **name)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_get_unique_name(bus, name);
}
int SdBus::sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_add_object_vtable(bus, slot, path, interface, vtable, userdata);
}
int SdBus::sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_add_object_manager(bus, slot, path);
}
int SdBus::sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_add_match(bus, slot, match, callback, userdata);
}
int SdBus::sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_add_match_async(bus, slot, match, callback, install_callback, userdata);
}
int SdBus::sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata)
{
const 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_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_slot_unref(slot);
}
@@ -369,16 +378,21 @@ int SdBus::sd_bus_start(sd_bus *bus)
return ::sd_bus_start(bus);
}
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **r)
int SdBus::sd_bus_process(sd_bus *bus, sd_bus_message **msg)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_process(bus, r);
return ::sd_bus_process(bus, msg);
}
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_);
const std::lock_guard lock(sdbusMutex_);
auto r = ::sd_bus_get_fd(bus);
if (r < 0)
@@ -395,6 +409,16 @@ int SdBus::sd_bus_get_poll_data(sd_bus *bus, PollData* data)
return r;
}
int SdBus::sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) // NOLINT(bugprone-easily-swappable-parameters)
{
const std::lock_guard lock(sdbusMutex_);
auto r1 = ::sd_bus_get_n_queued_read(bus, read);
auto r2 = ::sd_bus_get_n_queued_write(bus, write);
return std::min(r1, r2);
}
int SdBus::sd_bus_flush(sd_bus *bus)
{
return ::sd_bus_flush(bus);
@@ -415,74 +439,81 @@ sd_bus* SdBus::sd_bus_close_unref(sd_bus *bus)
#endif
}
int SdBus::sd_bus_message_set_destination(sd_bus_message *m, const char *destination)
int SdBus::sd_bus_message_set_destination(sd_bus_message *msg, const char *destination)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_message_set_destination(m, destination);
return ::sd_bus_message_set_destination(msg, destination);
}
int SdBus::sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c)
int SdBus::sd_bus_query_sender_creds(sd_bus_message *msg, uint64_t mask, sd_bus_creds **creds)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_query_sender_creds(m, mask, c);
return ::sd_bus_query_sender_creds(msg, mask, creds);
}
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *c)
sd_bus_creds* SdBus::sd_bus_creds_ref(sd_bus_creds *creds)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_unref(c);
return ::sd_bus_creds_ref(creds);
}
int SdBus::sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid)
sd_bus_creds* SdBus::sd_bus_creds_unref(sd_bus_creds *creds)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_pid(c, pid);
return ::sd_bus_creds_unref(creds);
}
int SdBus::sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid)
int SdBus::sd_bus_creds_get_pid(sd_bus_creds *creds, pid_t *pid)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_uid(c, uid);
return ::sd_bus_creds_get_pid(creds, pid);
}
int SdBus::sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid)
int SdBus::sd_bus_creds_get_uid(sd_bus_creds *creds, uid_t *uid)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_euid(c, euid);
return ::sd_bus_creds_get_uid(creds, uid);
}
int SdBus::sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid)
int SdBus::sd_bus_creds_get_euid(sd_bus_creds *creds, uid_t *euid)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_gid(c, gid);
return ::sd_bus_creds_get_euid(creds, euid);
}
int SdBus::sd_bus_creds_get_egid(sd_bus_creds *c, uid_t *egid)
int SdBus::sd_bus_creds_get_gid(sd_bus_creds *creds, gid_t *gid)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_egid(c, egid);
return ::sd_bus_creds_get_gid(creds, gid);
}
int SdBus::sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids)
int SdBus::sd_bus_creds_get_egid(sd_bus_creds *creds, uid_t *egid)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_supplementary_gids(c, gids);
return ::sd_bus_creds_get_egid(creds, egid);
}
int SdBus::sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label)
int SdBus::sd_bus_creds_get_supplementary_gids(sd_bus_creds *creds, const gid_t **gids)
{
std::lock_guard lock(sdbusMutex_);
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_selinux_context(c, label);
return ::sd_bus_creds_get_supplementary_gids(creds, gids);
}
int SdBus::sd_bus_creds_get_selinux_context(sd_bus_creds *creds, const char **label)
{
const std::lock_guard lock(sdbusMutex_);
return ::sd_bus_creds_get_selinux_context(creds, label);
}
} // namespace sdbus::internal
+57 -54
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file SdBus.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -36,72 +36,75 @@ namespace sdbus::internal {
class SdBus final : public ISdBus
{
public:
virtual sd_bus_message* sd_bus_message_ref(sd_bus_message *m) override;
virtual sd_bus_message* sd_bus_message_unref(sd_bus_message *m) override;
sd_bus_message* sd_bus_message_ref(sd_bus_message *msg) override;
sd_bus_message* sd_bus_message_unref(sd_bus_message *msg) override;
virtual int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) override;
virtual int sd_bus_call(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) override;
virtual int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) override;
int sd_bus_send(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie) override;
int sd_bus_call(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply) override;
int sd_bus_call_async(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec) override;
virtual int sd_bus_message_new(sd_bus *bus, sd_bus_message **m, uint8_t type) override;
virtual int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member) override;
virtual int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member) override;
virtual int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **m) override;
virtual int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e) override;
int sd_bus_message_new(sd_bus *bus, sd_bus_message **msg, uint8_t type) override;
int sd_bus_message_new_method_call(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member) override;
int sd_bus_message_new_signal(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member) override;
int sd_bus_message_new_method_return(sd_bus_message *call, sd_bus_message **msg) override;
int sd_bus_message_new_method_error(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err) override;
virtual int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) override;
virtual int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) override;
int sd_bus_set_method_call_timeout(sd_bus *bus, uint64_t usec) override;
int sd_bus_get_method_call_timeout(sd_bus *bus, uint64_t *ret) override;
virtual int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) override;
virtual int sd_bus_emit_object_added(sd_bus *bus, const char *path) override;
virtual int sd_bus_emit_object_removed(sd_bus *bus, const char *path) override;
virtual int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) override;
virtual int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) override;
int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) override;
int sd_bus_emit_object_added(sd_bus *bus, const char *path) override;
int sd_bus_emit_object_removed(sd_bus *bus, const char *path) override;
int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) override;
int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) override;
virtual int sd_bus_open(sd_bus **ret) override;
virtual int sd_bus_open_system(sd_bus **ret) override;
virtual int sd_bus_open_user(sd_bus **ret) override;
virtual int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
virtual int sd_bus_open_system_remote(sd_bus **ret, const char* hsot) override;
virtual int sd_bus_open_direct(sd_bus **ret, const char* address) override;
virtual int sd_bus_open_direct(sd_bus **ret, int fd) override;
virtual int sd_bus_open_server(sd_bus **ret, int fd) override;
virtual int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
virtual int sd_bus_release_name(sd_bus *bus, const char *name) override;
virtual int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
virtual int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
virtual int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
virtual int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
virtual int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata) override;
virtual sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) override;
int sd_bus_open(sd_bus **ret) override;
int sd_bus_open_system(sd_bus **ret) override;
int sd_bus_open_user(sd_bus **ret) override;
int sd_bus_open_user_with_address(sd_bus **ret, const char* address) override;
int sd_bus_open_system_remote(sd_bus **ret, const char* host) override;
int sd_bus_open_direct(sd_bus **ret, const char* address) override;
int sd_bus_open_direct(sd_bus **ret, int fd) override;
int sd_bus_open_server(sd_bus **ret, int fd) override;
int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) override;
int sd_bus_release_name(sd_bus *bus, const char *name) override;
int sd_bus_get_unique_name(sd_bus *bus, const char **name) override;
int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata) override;
int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) override;
int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) override;
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;
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;
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;
int sd_bus_new(sd_bus **ret) override;
int sd_bus_start(sd_bus *bus) override;
virtual int sd_bus_process(sd_bus *bus, sd_bus_message **r) override;
virtual int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
int sd_bus_process(sd_bus *bus, sd_bus_message **msg) override;
sd_bus_message* sd_bus_get_current_message(sd_bus *bus) override;
int sd_bus_get_poll_data(sd_bus *bus, PollData* data) override;
int sd_bus_get_n_queued(sd_bus *bus, uint64_t *read, uint64_t* write) override;
int sd_bus_flush(sd_bus *bus) override;
sd_bus *sd_bus_flush_close_unref(sd_bus *bus) override;
sd_bus *sd_bus_close_unref(sd_bus *bus) 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;
int sd_bus_message_set_destination(sd_bus_message *msg, const char *destination) override;
virtual int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) override;
int sd_bus_query_sender_creds(sd_bus_message *msg, uint64_t mask, sd_bus_creds **creds) override;
sd_bus_creds* sd_bus_creds_ref(sd_bus_creds *creds) override;
sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *creds) override;
virtual int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **c) override;
virtual sd_bus_creds* sd_bus_creds_unref(sd_bus_creds *c) override;
virtual int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) override;
virtual int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) override;
virtual int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid) override;
virtual int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) override;
virtual int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) override;
virtual int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) override;
virtual int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **label) override;
int sd_bus_creds_get_pid(sd_bus_creds *creds, pid_t *pid) override;
int sd_bus_creds_get_uid(sd_bus_creds *creds, uid_t *uid) override;
int sd_bus_creds_get_euid(sd_bus_creds *creds, uid_t *euid) override;
int sd_bus_creds_get_gid(sd_bus_creds *creds, gid_t *gid) override;
int sd_bus_creds_get_egid(sd_bus_creds *creds, gid_t *egid) override;
int sd_bus_creds_get_supplementary_gids(sd_bus_creds *creds, const gid_t **gids) override;
int sd_bus_creds_get_selinux_context(sd_bus_creds *creds, const char **label) override;
private:
std::recursive_mutex sdbusMutex_;
};
}
} // namespace sdbus::internal
#endif //SDBUS_C_SDBUS_H
#endif // SDBUS_CXX_SDBUS_H
+11 -13
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Types.cpp
*
@@ -24,13 +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 "MessageUtils.h"
#include SDBUS_HEADER
#include <cassert>
#include "sdbus-c++/Error.h"
#include "sdbus-c++/Message.h"
#include "sdbus-c++/Types.h"
#include <cerrno>
#include <system_error>
#include SDBUS_HEADER
#include <unistd.h>
namespace sdbus {
@@ -53,12 +53,10 @@ void Variant::deserializeFrom(Message& msg)
msg_.seal();
}
std::string Variant::peekValueType() const
const char* Variant::peekValueType() const
{
msg_.rewind(false);
std::string type;
std::string contents;
msg_.peekType(type, contents);
auto [type, contents] = msg_.peekType();
return contents;
}
@@ -67,7 +65,7 @@ bool Variant::isEmpty() const
return msg_.isEmpty();
}
void UnixFd::close()
void UnixFd::close() // NOLINT(readability-make-member-function-const)
{
if (fd_ >= 0)
{
@@ -82,7 +80,7 @@ int UnixFd::checkedDup(int fd)
return fd;
}
int ret = ::dup(fd);
const int ret = ::dup(fd); // NOLINT(android-cloexec-dup) // TODO: verify
if (ret < 0)
{
throw std::system_error(errno, std::generic_category(), "dup failed");
@@ -90,4 +88,4 @@ int UnixFd::checkedDup(int fd)
return ret;
}
}
} // namespace sdbus
+29 -14
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Utils.h
*
@@ -30,18 +30,20 @@
#include <sdbus-c++/Error.h>
#include SDBUS_HEADER
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if LIBSYSTEMD_VERSION>=246
#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_OBJECT_PATH(_PATH) \
SDBUS_THROW_ERROR_IF(!sd_bus_object_path_is_valid(_PATH), std::string("Invalid object path '") + _PATH + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME.c_str()), "Invalid interface name '" + _NAME + "' provided", EINVAL) \
#define SDBUS_CHECK_INTERFACE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_interface_name_is_valid(_NAME), std::string("Invalid interface name '") + _NAME + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!_NAME.empty() && !sd_bus_service_name_is_valid(_NAME.c_str()), "Invalid service name '" + _NAME + "' provided", EINVAL) \
#define SDBUS_CHECK_SERVICE_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(*_NAME && !sd_bus_service_name_is_valid(_NAME), std::string("Invalid service name '") + _NAME + "' provided", EINVAL) \
/**/
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME.c_str()), "Invalid member name '" + _NAME + "' provided", EINVAL) \
#define SDBUS_CHECK_MEMBER_NAME(_NAME) \
SDBUS_THROW_ERROR_IF(!sd_bus_member_name_is_valid(_NAME), std::string("Invalid member name '") + _NAME + "' provided", EINVAL) \
/**/
#else
#define SDBUS_CHECK_OBJECT_PATH(_PATH)
@@ -50,10 +52,12 @@
#define SDBUS_CHECK_MEMBER_NAME(_NAME)
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)
namespace sdbus::internal {
template <typename _Callable>
bool invokeHandlerAndCatchErrors(_Callable callable, sd_bus_error *retError)
template <typename Callable>
bool invokeHandlerAndCatchErrors(Callable callable, sd_bus_error *retError)
{
try
{
@@ -66,22 +70,33 @@ namespace sdbus::internal {
}
catch (const std::exception& e)
{
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, e.what());
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), e.what());
return false;
}
catch (...)
{
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME, "Unknown error occurred");
sd_bus_error_set(retError, SDBUSCPP_ERROR_NAME.c_str(), "Unknown error occurred");
return false;
}
return true;
}
// Returns time since epoch based of POSIX CLOCK_MONOTONIC,
// so we use the very same clock as underlying sd-bus library.
[[nodiscard]] inline auto now()
{
struct timespec ts{};
auto r = clock_gettime(CLOCK_MONOTONIC, &ts);
SDBUS_THROW_ERROR_IF(r < 0, "clock_gettime failed: ", -errno);
return std::chrono::nanoseconds(ts.tv_nsec) + std::chrono::seconds(ts.tv_sec);
}
// Implementation of the overload pattern for variant visitation
template <class... Ts> struct overload : Ts... { using Ts::operator()...; };
template <class... Ts> overload(Ts...) -> overload<Ts...>;
}
} // namespace sdbus::internal
#endif /* SDBUS_CXX_INTERNAL_UTILS_H_ */
+22 -22
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file VTableUtils.c
*
@@ -27,18 +27,18 @@
#include "VTableUtils.h"
#include SDBUS_HEADER
sd_bus_vtable createVTableStartItem(uint64_t flags)
sd_bus_vtable createSdBusVTableStartItem(uint64_t flags)
{
struct sd_bus_vtable vtableStart = SD_BUS_VTABLE_START(flags);
return vtableStart;
}
sd_bus_vtable createVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, 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 )
{
#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 createVTableMethodItem( const char *member
return vtableItem;
}
sd_bus_vtable createVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags )
sd_bus_vtable createSdBusVTableSignalItem( 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 createVTableSignalItem( const char *member
return vtableItem;
}
sd_bus_vtable createVTablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, uint64_t flags )
sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, uint64_t flags )
{
struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, flags);
return vtableItem;
}
sd_bus_vtable createVTableWritablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, sd_bus_property_set_t setter
, 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 )
{
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, flags);
return vtableItem;
}
sd_bus_vtable createVTableEndItem()
sd_bus_vtable createSdBusVTableEndItem()
{
struct sd_bus_vtable vtableEnd = SD_BUS_VTABLE_END;
return vtableEnd;
+23 -23
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 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 SDBUS_HEADER
#include <stdbool.h>
#include SDBUS_HEADER
#ifdef __cplusplus
extern "C" {
#endif
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();
sd_bus_vtable createSdBusVTableStartItem(uint64_t flags);
sd_bus_vtable createSdBusVTableMethodItem( const char *member
, const char *signature
, const char *result
, const char *paramNames
, sd_bus_message_handler_t handler
, uint64_t flags );
sd_bus_vtable createSdBusVTableSignalItem( const char *member
, const char *signature
, const char *outnames
, uint64_t flags );
sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, uint64_t flags );
sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
, const char *signature
, sd_bus_property_get_t getter
, sd_bus_property_set_t setter
, uint64_t flags );
sd_bus_vtable createSdBusVTableEndItem();
#ifdef __cplusplus
}
+75 -70
View File
@@ -2,15 +2,12 @@
# DOWNLOAD AND BUILD OF GOOGLETEST
#-------------------------------
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)
find_package(GTest ${SDBUSCPP_GOOGLETEST_VERSION} CONFIG)
if (NOT TARGET GTest::gmock)
# Try pkg-config if GTest was not found through CMake config
find_package(PkgConfig)
if (PkgConfig_FOUND)
pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${GOOGLETEST_VERSION})
pkg_check_modules(GMock IMPORTED_TARGET GLOBAL gmock>=${SDBUSCPP_GOOGLETEST_VERSION})
if(TARGET PkgConfig::GMock)
add_library(GTest::gmock ALIAS PkgConfig::GMock)
endif()
@@ -19,26 +16,26 @@ if (NOT TARGET GTest::gmock)
if (NOT TARGET GTest::gmock)
include(FetchContent)
message("Fetching googletest v${GOOGLETEST_VERSION}...")
if (SDBUSCPP_GOOGLETEST_VERSION VERSION_GREATER_EQUAL 1.13.0)
set(GOOGLETEST_TAG "v${SDBUSCPP_GOOGLETEST_VERSION}")
else()
set(GOOGLETEST_TAG "release-${SDBUSCPP_GOOGLETEST_VERSION}")
endif()
message("Manually fetching & building googletest ${GOOGLETEST_TAG}...")
FetchContent_Declare(googletest
GIT_REPOSITORY ${GOOGLETEST_GIT_REPO}
GIT_TAG release-${GOOGLETEST_VERSION}
GIT_REPOSITORY ${SDBUSCPP_GOOGLETEST_GIT_REPO}
GIT_TAG ${GOOGLETEST_TAG}
GIT_SHALLOW 1
UPDATE_COMMAND "")
#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()
set(gtest_force_shared_crt ON CACHE INTERNAL "" FORCE)
set(INSTALL_GTEST OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS_BAK ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS OFF)
FetchContent_MakeAvailable(googletest)
set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_BAK})
add_library(GTest::gmock ALIAS gmock)
endif()
endif()
@@ -50,54 +47,53 @@ set(UNITTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/unittests)
set(UNITTESTS_SRCS
${UNITTESTS_SOURCE_DIR}/sdbus-c++-unit-tests.cpp
${UNITTESTS_SOURCE_DIR}/Message_test.cpp
${UNITTESTS_SOURCE_DIR}/PollData_test.cpp
${UNITTESTS_SOURCE_DIR}/Types_test.cpp
${UNITTESTS_SOURCE_DIR}/TypeTraits_test.cpp
${UNITTESTS_SOURCE_DIR}/Connection_test.cpp
${UNITTESTS_SOURCE_DIR}/mocks/SdBusMock.h)
set(INTEGRATIONTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/integrationtests)
set(INTEGRATIONTESTS_GENERATED_DIR ${INTEGRATIONTESTS_SOURCE_DIR}/dbus-api/gen-cpp)
set(INTEGRATIONTESTS_SRCS
${INTEGRATIONTESTS_SOURCE_DIR}/DBusConnectionTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/DBusGeneralTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/DBusMethodsTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/DBusAsyncMethodsTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/DBusAwaitableMethodsTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/DBusSignalsTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/DBusPropertiesTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/DBusStandardInterfacesTests.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/Defs.h
${INTEGRATIONTESTS_SOURCE_DIR}/integrationtests-adaptor.h
${INTEGRATIONTESTS_SOURCE_DIR}/integrationtests-proxy.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestFixture.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestFixture.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/TestAdaptor.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestAdaptor.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/TestProxy.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestProxy.cpp
${INTEGRATIONTESTS_SOURCE_DIR}/sdbus-c++-integration-tests.cpp)
${INTEGRATIONTESTS_SOURCE_DIR}/sdbus-c++-integration-tests.cpp
${INTEGRATIONTESTS_GENERATED_DIR}/integrationtests-adaptor.h
${INTEGRATIONTESTS_GENERATED_DIR}/integrationtests-proxy.h)
set(PERFTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/perftests)
set(STRESSTESTS_CLIENT_SRCS
set(PERFTESTS_GENERATED_DIR ${PERFTESTS_SOURCE_DIR}/dbus-api/gen-cpp)
set(PERFTESTS_CLIENT_SRCS
${PERFTESTS_SOURCE_DIR}/client.cpp
${PERFTESTS_SOURCE_DIR}/perftests-proxy.h)
set(STRESSTESTS_SERVER_SRCS
${PERFTESTS_GENERATED_DIR}/perftests-proxy.h)
set(PERFTESTS_SERVER_SRCS
${PERFTESTS_SOURCE_DIR}/server.cpp
${PERFTESTS_SOURCE_DIR}/perftests-adaptor.h)
${PERFTESTS_GENERATED_DIR}/perftests-adaptor.h)
set(STRESSTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stresstests)
set(STRESSTESTS_GENERATED_DIR ${STRESSTESTS_SOURCE_DIR}/dbus-api/gen-cpp)
set(STRESSTESTS_SRCS
${STRESSTESTS_SOURCE_DIR}/sdbus-c++-stress-tests.cpp
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-adaptor.h
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-proxy.h
${STRESSTESTS_SOURCE_DIR}/celsius-thermometer-adaptor.h
${STRESSTESTS_SOURCE_DIR}/celsius-thermometer-proxy.h
${STRESSTESTS_SOURCE_DIR}/concatenator-adaptor.h
${STRESSTESTS_SOURCE_DIR}/concatenator-proxy.h)
#-------------------------------
# GENERAL COMPILER CONFIGURATION
#-------------------------------
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
${STRESSTESTS_GENERATED_DIR}/fahrenheit-thermometer-adaptor.h
${STRESSTESTS_GENERATED_DIR}/fahrenheit-thermometer-proxy.h
${STRESSTESTS_GENERATED_DIR}/celsius-thermometer-adaptor.h
${STRESSTESTS_GENERATED_DIR}/celsius-thermometer-proxy.h
${STRESSTESTS_GENERATED_DIR}/concatenator-adaptor.h
${STRESSTESTS_GENERATED_DIR}/concatenator-proxy.h)
#----------------------------------
# BUILD INFORMATION
@@ -105,33 +101,42 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(sdbus-c++-unit-tests ${UNITTESTS_SRCS})
target_compile_definitions(sdbus-c++-unit-tests PRIVATE
LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION}
SDBUS_HEADER=<${LIBSYSTEMD_IMPL}/sd-bus.h>)
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
SDBUS_${SDBUS_IMPL}
SDBUS_HEADER=<${SDBUS_IMPL}/sd-bus.h>)
target_link_libraries(sdbus-c++-unit-tests sdbus-c++-objlib GTest::gmock)
add_executable(sdbus-c++-integration-tests ${INTEGRATIONTESTS_SRCS})
target_compile_definitions(sdbus-c++-integration-tests PRIVATE LIBSYSTEMD_VERSION=${LIBSYSTEMD_VERSION})
target_link_libraries(sdbus-c++-integration-tests sdbus-c++ GTest::gmock)
target_compile_definitions(sdbus-c++-integration-tests PRIVATE
LIBSYSTEMD_VERSION=${SDBUSCPP_LIBSYSTEMD_VERSION}
SDBUS_${SDBUS_IMPL})
target_include_directories(sdbus-c++-integration-tests SYSTEM PRIVATE ${INTEGRATIONTESTS_GENERATED_DIR})
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()
# 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)
if(SDBUSCPP_BUILD_PERF_TESTS OR SDBUSCPP_BUILD_STRESS_TESTS)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
if(ENABLE_PERF_TESTS)
if(SDBUSCPP_BUILD_PERF_TESTS)
message(STATUS "Building with performance tests")
add_executable(sdbus-c++-perf-tests-client ${STRESSTESTS_CLIENT_SRCS})
add_executable(sdbus-c++-perf-tests-client ${PERFTESTS_CLIENT_SRCS})
target_include_directories(sdbus-c++-perf-tests-client SYSTEM PRIVATE ${PERFTESTS_GENERATED_DIR})
target_link_libraries(sdbus-c++-perf-tests-client sdbus-c++ Threads::Threads)
add_executable(sdbus-c++-perf-tests-server ${STRESSTESTS_SERVER_SRCS})
add_executable(sdbus-c++-perf-tests-server ${PERFTESTS_SERVER_SRCS})
target_include_directories(sdbus-c++-perf-tests-server SYSTEM PRIVATE ${PERFTESTS_GENERATED_DIR})
target_link_libraries(sdbus-c++-perf-tests-server sdbus-c++ Threads::Threads)
endif()
if(ENABLE_STRESS_TESTS)
if(SDBUSCPP_BUILD_STRESS_TESTS)
message(STATUS "Building with stress tests")
add_executable(sdbus-c++-stress-tests ${STRESSTESTS_SRCS})
target_include_directories(sdbus-c++-stress-tests SYSTEM PRIVATE ${STRESSTESTS_GENERATED_DIR})
target_link_libraries(sdbus-c++-stress-tests sdbus-c++ Threads::Threads)
endif()
endif()
@@ -140,25 +145,25 @@ endif()
# INSTALLATION
#----------------------------------
option(INSTALL_TESTS "Install tests (default OFF)" OFF)
if(INSTALL_TESTS)
if(SDBUSCPP_INSTALL)
include(GNUInstallDirs)
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)
install(TARGETS sdbus-c++-unit-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(TARGETS sdbus-c++-integration-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(FILES ${INTEGRATIONTESTS_SOURCE_DIR}/files/org.sdbuscpp.integrationtests.conf
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
COMPONENT sdbus-c++-test)
if(SDBUSCPP_BUILD_PERF_TESTS)
install(TARGETS sdbus-c++-perf-tests-client DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(TARGETS sdbus-c++-perf-tests-server DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(FILES ${PERFTESTS_SOURCE_DIR}/files/org.sdbuscpp.perftests.conf
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
COMPONENT sdbus-c++-test)
endif()
if(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)
if(SDBUSCPP_BUILD_STRESS_TESTS)
install(TARGETS sdbus-c++-stress-tests DESTINATION ${SDBUSCPP_TESTS_INSTALL_PATH} COMPONENT sdbus-c++-test)
install(FILES ${STRESSTESTS_SOURCE_DIR}/files/org.sdbuscpp.stresstests.conf
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}/dbus-1/system.d
COMPONENT sdbus-c++-test)
endif()
endif()
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusAsyncMethodsTests.cpp
*
@@ -25,53 +25,54 @@
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include "Defs.h"
#include <sdbus-c++/sdbus-c++.h>
#include <cstdint>
#include <exception>
#include <atomic>
#include <cstddef>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <optional>
#include <mutex>
#include <map>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
#include <utility>
#include <vector>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::Le;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
TYPED_TEST(AsyncSdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
{
std::chrono::time_point<std::chrono::steady_clock> start;
try
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
{
if (err == nullptr)
if (!err)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
promise.set_exception(std::make_exception_ptr(*std::move(err)));
});
start = std::chrono::steady_clock::now();
m_proxy->doOperationClientSideAsyncWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 500ms, so we should time out
this->m_proxy->doOperationClientSideAsyncWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
future.get();
FAIL() << "Expected sdbus::Error exception";
@@ -89,39 +90,39 @@ TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenClientSideAsyncMethodTimesOut)
}
}
TEST_F(SdbusTestObject, RunsServerSideAsynchoronousMethodAsynchronously)
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchronousMethodAsynchronously)
{
// Yeah, this is kinda timing-dependent test, but times should be safe...
std::mutex mtx;
std::vector<uint32_t> results;
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
std::atomic invoke{false};
std::atomic startedCount{0};
auto call = [&](uint32_t param)
{
TestProxy proxy{BUS_NAME, OBJECT_PATH};
TestProxy proxy{SERVICE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
auto result = proxy.doOperationAsync(param);
std::lock_guard<std::mutex> guard(mtx);
std::lock_guard const guard(mtx);
results.push_back(result);
};
std::thread invocations[]{std::thread{call, 1500}, std::thread{call, 1000}, std::thread{call, 500}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
std::for_each(std::begin(invocations), std::end(invocations), [](auto& thread){ thread.join(); });
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
}
TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
TYPED_TEST(AsyncSdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
{
std::atomic<size_t> resultCount{};
std::atomic<bool> invoke{};
std::atomic<int> startedCount{};
std::atomic invoke{false};
std::atomic startedCount{0};
auto call = [&]()
{
TestProxy proxy{BUS_NAME, OBJECT_PATH};
TestProxy proxy{SERVICE_NAME, OBJECT_PATH};
++startedCount;
while (!invoke) ;
@@ -139,38 +140,51 @@ TEST_F(SdbusTestObject, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
std::thread invocations[]{std::thread{call}, std::thread{call}, std::thread{call}};
while (startedCount != 3) ;
invoke = true;
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
std::for_each(std::begin(invocations), std::end(invocations), [](auto& thread){ thread.join(); });
ASSERT_THAT(resultCount, Eq(1500));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
TYPED_TEST(AsyncSdbusTestObject, RunsServerSideAsynchronousMethodWithLargeMessage)
{
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 40'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
auto result1 = this->m_proxy->doOperationAsyncWithLargeData(0, largeMap); // Sends large map back in the context of the callback (event loop thread)
auto result2 = this->m_proxy->doOperationAsyncWithLargeData(500, largeMap); // Sends large map back outside the context of the event loop thread
ASSERT_THAT(result1, Eq(largeMap));
ASSERT_THAT(result2, Eq(largeMap));
}
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
{
if (err == nullptr)
if (!err)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
promise.set_exception(std::make_exception_ptr(std::move(err)));
});
m_proxy->doOperationClientSideAsync(100);
this->m_proxy->doOperationClientSideAsync(100);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFuture)
{
auto future = m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
auto future = this->m_proxy->doOperationClientSideAsync(100, sdbus::with_future);
ASSERT_THAT(future.get(), Eq(100));
}
TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasicAPILevel)
{
auto future = m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
auto future = this->m_proxy->doOperationClientSideAsyncOnBasicAPILevel(100);
auto methodReply = future.get();
uint32_t returnValue{};
@@ -179,87 +193,111 @@ TEST_F(SdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithFutureOnBasic
ASSERT_THAT(returnValue, Eq(100));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodWithLargeDataAsynchronouslyOnClientSideWithFuture)
{
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){});
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 40'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
auto call = m_proxy->doOperationClientSideAsync(100);
auto future = this->m_proxy->doOperationWithLargeDataClientSideAsync(largeMap, sdbus::with_future);
ASSERT_THAT(future.get(), Eq(largeMap));
}
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsPendingIfItIsInProgress)
{
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){});
auto call = this->m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TEST_F(SdbusTestObject, CancelsPendingAsyncCallOnClientSide)
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSide)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){ promise.set_value(1); });
auto call = this->m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_THAT(future.wait_for(300ms), Eq(std::future_status::timeout));
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCancelled)
TYPED_TEST(AsyncSdbusTestObject, CancelsPendingAsyncCallOnClientSideByDestroyingOwningSlot)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(100);
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const 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;
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){ promise.set_value(1); });
auto call = this->m_proxy->doOperationClientSideAsync(100);
call.cancel();
ASSERT_FALSE(call.isPending());
}
TEST_F(SdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
TYPED_TEST(AsyncSdbusTestObject, AnswersThatAsyncCallIsNotPendingAfterItHasBeenCompleted)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const sdbus::Error* /*err*/){ promise.set_value(1); });
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t /*res*/, const std::optional<sdbus::Error>& /*err*/){ promise.set_value(1); });
auto call = m_proxy->doOperationClientSideAsync(0);
auto call = this->m_proxy->doOperationClientSideAsync(0);
(void) future.get(); // Wait for the call to finish
ASSERT_TRUE(waitUntil([&call](){ return !call.isPending(); }));
}
TEST_F(SdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
TYPED_TEST(AsyncSdbusTestObject, AnswersThatDefaultConstructedAsyncCallIsNotPending)
{
sdbus::PendingAsyncCall call;
sdbus::PendingAsyncCall const call;
ASSERT_FALSE(call.isPending());
}
TEST_F(SdbusTestObject, SupportsAsyncCallCopyAssignment)
TYPED_TEST(AsyncSdbusTestObject, SupportsAsyncCallCopyAssignment)
{
sdbus::PendingAsyncCall call;
call = m_proxy->doOperationClientSideAsync(100);
call = this->m_proxy->doOperationClientSideAsync(100);
ASSERT_TRUE(call.isPending());
}
TEST_F(SdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails)
TYPED_TEST(AsyncSdbusTestObject, ReturnsNonnullErrorWhenAsynchronousMethodCallFails) // NOLINT(readability-function-cognitive-complexity)
{
std::promise<uint32_t> promise;
auto future = promise.get_future();
m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, const sdbus::Error* err)
this->m_proxy->installDoOperationClientSideAsyncReplyHandler([&](uint32_t res, std::optional<sdbus::Error> err)
{
if (err == nullptr)
if (!err)
promise.set_value(res);
else
promise.set_exception(std::make_exception_ptr(*err));
promise.set_exception(std::make_exception_ptr(*std::move(err)));
});
m_proxy->doErroneousOperationClientSideAsync();
this->m_proxy->doErroneousOperationClientSideAsync();
ASSERT_THROW(future.get(), sdbus::Error);
}
TEST_F(SdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
TYPED_TEST(AsyncSdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithFutureFails)
{
auto future = m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
auto future = this->m_proxy->doErroneousOperationClientSideAsync(sdbus::with_future);
ASSERT_THROW(future.get(), sdbus::Error);
}
@@ -0,0 +1,247 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2026 - Alex Cani <alexcani109@gmail.com>
*
* @file DBusAwaitableMethodsTests.cpp
*
* Created on: Mar 1, 2026
* 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 <coroutine>
#include <cstdint>
#include <exception>
#include <future>
#include <map>
#include <string>
#include <utility>
#include <sdbus-c++/sdbus-c++.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "TestFixture.h"
#include "TestProxy.h"
using ::testing::Eq;
using namespace std::chrono_literals;
using namespace sdbus::test;
// Simple coroutine task type for testing purposes
// Uses a promise/future pair to communicate results and exceptions
// between the coroutine and the test code. Do not mistake this for
// the std::future-based async API of sdbus-c++.
template<typename T>
struct Task {
struct promise_type {
T value;
std::exception_ptr exception;
std::promise<T> completion;
std::future<T> future;
promise_type() : future(completion.get_future()) {}
Task get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
// Lazy coroutine
std::suspend_always initial_suspend() noexcept { return {}; }
// final_suspend suspends so that the test code can retrieve the result
// or exception from the promise before the coroutine is destroyed
std::suspend_always final_suspend() noexcept
{
if (exception)
{
completion.set_exception(exception);
}
else
{
completion.set_value(std::move(value));
}
return {};
}
void return_value(T val) { value = std::move(val); }
void unhandled_exception() { exception = std::current_exception(); }
};
std::coroutine_handle<promise_type> handle;
// Ctor and rule of 5 for proper handle management
explicit Task(std::coroutine_handle<promise_type> hnd) : handle(hnd) {}
Task(Task&& other) noexcept : handle(std::exchange(other.handle, {})) {}
Task& operator=(Task&& other) noexcept {
if (this != &other) {
if (handle) handle.destroy();
handle = std::exchange(other.handle, {});
}
return *this;
}
Task(const Task&) = delete;
Task& operator=(const Task&) = delete;
~Task() { if (handle) handle.destroy(); }
// "User API" for the test code, allows starting the task and retrieving the result or exception
void resume() { if (handle && !handle.done()) handle.resume(); }
T get() { return handle.promise().future.get(); }
};
// Specialization for void
template<>
struct Task<void> {
struct promise_type {
std::exception_ptr exception;
std::promise<void> completion;
std::future<void> future;
promise_type() : future(completion.get_future()) {}
Task get_return_object() {
return Task{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() noexcept { return {}; } // NOLINT(readability-convert-member-functions-to-static)
std::suspend_always final_suspend() noexcept
{
if (exception)
{
completion.set_exception(exception);
}
else
{
completion.set_value();
}
return {};
}
void return_void() {}
void unhandled_exception() { exception = std::current_exception(); }
};
std::coroutine_handle<promise_type> handle;
explicit Task(std::coroutine_handle<promise_type> hnd) : handle(hnd) {}
Task(Task&& other) noexcept : handle(std::exchange(other.handle, {})) {}
Task& operator=(Task&& other) noexcept {
if (this != &other) {
if (handle) handle.destroy();
handle = std::exchange(other.handle, {});
}
return *this;
}
Task(const Task&) = delete;
Task& operator=(const Task&) = delete;
~Task() { if (handle) handle.destroy(); }
void resume() { if (handle && !handle.done()) handle.resume(); } // NOLINT(readability-make-member-function-const)
void get() { handle.promise().future.get(); } // NOLINT(readability-make-member-function-const)
};
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithAwaitable)
{
auto task = [](TestProxy* proxy) -> Task<uint32_t> {
co_return co_await proxy->doOperationClientSideAsync(100, sdbus::with_awaitable);
}(this->m_proxy.get());
task.resume();
ASSERT_THAT(task.get(), Eq(100));
}
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodAsynchronouslyOnClientSideWithAwaitableOnBasicAPILevel)
{
auto task = [](TestProxy* proxy) -> Task<uint32_t> {
auto methodReply = co_await proxy->doOperationClientSideAsyncOnBasicAPILevel(100, sdbus::with_awaitable);
uint32_t returnValue{};
methodReply >> returnValue;
co_return returnValue;
}(this->m_proxy.get());
task.resume();
ASSERT_THAT(task.get(), Eq(100));
}
TYPED_TEST(AsyncSdbusTestObject, InvokesMethodWithLargeDataAsynchronouslyOnClientSideWithAwaitable)
{
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 40'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
// NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) -- lambda closure has guaranteed lifetime
auto lambda = [&largeMap, this]() -> Task<std::map<int32_t, std::string>> {
co_return co_await this->m_proxy->doOperationWithLargeDataClientSideAsync(largeMap, sdbus::with_awaitable);
};
auto task = lambda();
task.resume();
ASSERT_THAT(task.get(), Eq(largeMap));
}
TYPED_TEST(AsyncSdbusTestObject, ThrowsErrorWhenClientSideAsynchronousMethodCallWithAwaitableFails)
{
auto task = [](TestProxy* proxy) -> Task<void> {
co_await proxy->doErroneousOperationClientSideAsync(sdbus::with_awaitable);
}(this->m_proxy.get());
task.resume();
ASSERT_THROW(task.get(), sdbus::Error);
}
TYPED_TEST(AsyncSdbusTestObject, AwaitableSupportsMultipleSequentialCalls)
{
auto task = [](TestProxy* proxy) -> Task<uint32_t> {
auto result1 = co_await proxy->doOperationClientSideAsync(10, sdbus::with_awaitable);
auto result2 = co_await proxy->doOperationClientSideAsync(20, sdbus::with_awaitable);
auto result3 = co_await proxy->doOperationClientSideAsync(30, sdbus::with_awaitable);
co_return result1 + result2 + result3;
}(this->m_proxy.get());
task.resume();
ASSERT_THAT(task.get(), Eq(60));
}
TYPED_TEST(AsyncSdbusTestObject, AwaitablePropagatesExceptionsCorrectly)
{
auto task = [](TestProxy* proxy) -> Task<std::string> {
try {
co_await proxy->doErroneousOperationClientSideAsync(sdbus::with_awaitable);
co_return "FAILED";
} catch (const sdbus::Error& e) {
// Verify we can inspect the exception
co_return std::string(e.getName());
}
}(this->m_proxy.get());
task.resume();
ASSERT_THAT(task.get(), ::testing::HasSubstr("Error"));
}
+22 -62
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusConnectionTests.cpp
*
@@ -33,15 +33,11 @@
// gmock
#include <gtest/gtest.h>
#include <gmock/gmock.h>
// STL
#include <thread>
#include <chrono>
using ::testing::Eq;
using namespace sdbus::test;
using namespace std::chrono_literals;
/*-------------------------------------*/
/* -- TEST CASES -- */
@@ -49,85 +45,49 @@ using namespace std::chrono_literals;
TEST(Connection, CanBeDefaultConstructed)
{
ASSERT_NO_THROW(auto con = sdbus::createConnection());
ASSERT_NO_THROW(auto con = sdbus::createBusConnection());
}
TEST(Connection, CanRequestRegisteredDbusName)
TEST(Connection, CanRequestName)
{
auto connection = sdbus::createConnection();
auto connection = sdbus::createBusConnection();
ASSERT_NO_THROW(connection->requestName(BUS_NAME))
// In case of system bus connection, requesting may throw as we need to allow that first through a config file in /etc/dbus-1/system.d
ASSERT_NO_THROW(connection->requestName(SERVICE_NAME))
<< "Perhaps you've forgotten to copy `org.sdbuscpp.integrationtests.conf` file to `/etc/dbus-1/system.d` directory before running the tests?";
}
TEST(Connection, CannotRequestNonregisteredDbusName)
TEST(SystemBusConnection, CannotRequestNonregisteredDbusName)
{
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->requestName("some.random.not.supported.dbus.name"), sdbus::Error);
auto connection = sdbus::createSystemBusConnection();
sdbus::ServiceName const notSupportedBusName{"some.random.not.supported.dbus.name"};
ASSERT_THROW(connection->requestName(notSupportedBusName), sdbus::Error);
}
TEST(Connection, CanReleasedRequestedName)
TEST(Connection, CanReleaseRequestedName)
{
auto connection = sdbus::createConnection();
auto connection = sdbus::createBusConnection();
connection->requestName(SERVICE_NAME);
connection->requestName(BUS_NAME);
ASSERT_NO_THROW(connection->releaseName(BUS_NAME));
ASSERT_NO_THROW(connection->releaseName(SERVICE_NAME));
}
TEST(Connection, CannotReleaseNonrequestedName)
{
auto connection = sdbus::createConnection();
ASSERT_THROW(connection->releaseName("some.random.nonrequested.name"), sdbus::Error);
auto connection = sdbus::createBusConnection();
sdbus::ServiceName const notAcquiredBusName{"some.random.unacquired.name"};
ASSERT_THROW(connection->releaseName(notAcquiredBusName), sdbus::Error);
}
TEST(Connection, CanEnterAndLeaveEventLoop)
TEST(Connection, CanEnterAndLeaveInternalEventLoop)
{
auto connection = sdbus::createConnection();
connection->requestName(BUS_NAME);
auto connection = sdbus::createBusConnection();
connection->requestName(SERVICE_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);
}
+67 -66
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusGeneralTests.cpp
*
@@ -27,143 +27,144 @@
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "TestFixture.h"
#include "sdbus-c++/sdbus-c++.h"
#include "Defs.h"
#include <sdbus-c++/sdbus-c++.h>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <thread>
#include <tuple>
#include <string_view>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
#include <variant>
#include <type_traits>
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;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
TEST(AdaptorAndProxy, CanBeConstructedSuccessfully)
{
auto connection = sdbus::createConnection();
connection->requestName(BUS_NAME);
auto connection = sdbus::createBusConnection();
connection->requestName(SERVICE_NAME);
ASSERT_NO_THROW(TestAdaptor adaptor(*connection, OBJECT_PATH));
ASSERT_NO_THROW(TestProxy proxy(BUS_NAME, OBJECT_PATH));
ASSERT_NO_THROW(const TestAdaptor adaptor(*connection, OBJECT_PATH));
ASSERT_NO_THROW(const TestProxy proxy(SERVICE_NAME, OBJECT_PATH));
connection->releaseName(SERVICE_NAME);
}
TEST(AProxy, SupportsMoveSemantics)
TEST(AProxy, DoesNotSupportMoveSemantics)
{
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, SupportsMoveSemantics)
TEST(AnAdaptor, DoesNotSupportMoveSemantics)
{
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>);
}
TEST_F(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
TYPED_TEST(AConnection, WillCallCallbackHandlerForIncomingMessageMatchingMatchRule)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic matchingMessageReceived{false};
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](const sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
});
}, sdbus::return_slot);
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(matchingMessageReceived));
}
TEST_F(AConnection, CanInstallMatchRuleAsynchronously)
TYPED_TEST(AConnection, CanInstallMatchRuleAsynchronously)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
std::atomic<bool> matchRuleInstalled{false};
auto slot = s_proxyConnection->addMatchAsync( matchRule
, [&](sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}
, [&](sdbus::Message& /*msg*/)
{
matchRuleInstalled = true;
} );
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic matchingMessageReceived{false};
std::atomic matchRuleInstalled{false};
auto slot = this->s_proxyConnection->addMatchAsync( matchRule
, [&](const sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
}
, [&](const sdbus::Message& /*msg*/)
{
matchRuleInstalled = true;
}
, sdbus::return_slot );
EXPECT_TRUE(waitUntil(matchRuleInstalled));
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(matchingMessageReceived));
}
TEST_F(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
TYPED_TEST(AConnection, WillUnsubscribeMatchRuleWhenClientDestroysTheAssociatedSlot)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic matchingMessageReceived{false};
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](const sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
});
}, sdbus::return_slot);
slot.reset();
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
}
TEST_F(AConnection, CanAddFloatingMatchRule)
TYPED_TEST(AConnection, CanAddFloatingMatchRule)
{
auto matchRule = "sender='" + BUS_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic<bool> matchingMessageReceived{false};
auto con = sdbus::createSystemBusConnection();
auto matchRule = "sender='" + SERVICE_NAME + "',path='" + OBJECT_PATH + "'";
std::atomic matchingMessageReceived{false};
auto con = sdbus::createBusConnection();
con->enterEventLoopAsync();
auto callback = [&](sdbus::Message& msg)
auto callback = [&](const sdbus::Message& msg)
{
if(msg.getPath() == OBJECT_PATH)
matchingMessageReceived = true;
};
con->addMatch(matchRule, std::move(callback), sdbus::floating_slot);
m_adaptor->emitSimpleSignal();
con->addMatch(matchRule, std::move(callback));
this->m_adaptor->emitSimpleSignal();
[[maybe_unused]] auto gotMessage = waitUntil(matchingMessageReceived, 2s);
assert(gotMessage);
matchingMessageReceived = false;
con.reset();
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(matchingMessageReceived, 2s));
ASSERT_FALSE(waitUntil(matchingMessageReceived, 1s));
}
TEST_F(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
TYPED_TEST(AConnection, WillNotPassToMatchCallbackMessagesThatDoNotMatchTheRule)
{
auto matchRule = "type='signal',interface='" + INTERFACE_NAME + "',member='simpleSignal'";
std::atomic<size_t> numberOfMatchingMessages{};
auto slot = s_proxyConnection->addMatch(matchRule, [&](sdbus::Message& msg)
auto slot = this->s_proxyConnection->addMatch(matchRule, [&](const sdbus::Message& msg)
{
if(msg.getMemberName() == "simpleSignal")
if(msg.getMemberName() == "simpleSignal"sv)
numberOfMatchingMessages++;
});
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
}, sdbus::return_slot);
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
m_adaptor->emitSignalWithMap({});
this->m_adaptor->emitSignalWithMap({});
adaptor2->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil([&](){ return numberOfMatchingMessages == 2; }));
ASSERT_FALSE(waitUntil([&](){ return numberOfMatchingMessages > 2; }, 1s));
+186 -95
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusMethodsTests.cpp
*
@@ -25,156 +25,171 @@
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include "Defs.h"
#include <sdbus-c++/sdbus-c++.h>
#include <cstdint>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <map>
#include <memory>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
#include <vector>
#include <variant>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::Le;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace std::string_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
namespace my {
struct Struct
{
int i{};
std::string s;
std::vector<double> l;
friend bool operator==(const Struct &lhs, const Struct &rhs) = default;
};
} // namespace my
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l); // NOLINT(readability-identifier-length)
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
TYPED_TEST(SdbusTestObject, CallsEmptyMethodSuccessfully)
{
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
ASSERT_NO_THROW(this->m_proxy->noArgNoReturn());
}
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodsWithBaseTypesSuccessfully)
{
auto resInt = m_proxy->getInt();
auto resInt = this->m_proxy->getInt();
ASSERT_THAT(resInt, Eq(INT32_VALUE));
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
auto multiplyRes = this->m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodsWithTuplesSuccessfully)
{
auto resTuple = m_proxy->getTuple();
auto resTuple = this->m_proxy->getTuple();
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodsWithStructSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccessfully)
{
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
auto vectorRes = m_proxy->getInts16FromStruct(a);
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> const strctA{};
auto vectorRes = this->m_proxy->getInts16FromStruct(strctA);
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{
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> const strctB{
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
};
vectorRes = m_proxy->getInts16FromStruct(b);
vectorRes = this->m_proxy->getInts16FromStruct(strctB);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
}
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithVariantSuccessfully)
{
sdbus::Variant v{DOUBLE_VALUE};
auto variantRes = m_proxy->processVariant(v);
sdbus::Variant const var{DOUBLE_VALUE};
sdbus::Variant const variantRes = this->m_proxy->processVariant(var);
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
}
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithStdVariantSuccessfully)
{
std::vector<int32_t> x{-2, 0, 2};
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
decltype(mapOfVariants) res{ {sdbus::Variant{-2}, sdbus::Variant{false}}
, {sdbus::Variant{0}, sdbus::Variant{false}}
, {sdbus::Variant{2}, sdbus::Variant{true}}};
std::variant<int32_t, double, std::string> const var{DOUBLE_VALUE};
auto variantRes = this->m_proxy->processVariant(var);
ASSERT_THAT(std::get<int32_t>(variantRes), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccessfully)
{
std::vector const vec{-2, 0, 2};
sdbus::Struct<sdbus::Variant, sdbus::Variant> const strct{false, true};
std::map<int32_t, sdbus::Variant> mapOfVariants = this->m_proxy->getMapOfVariants(vec, strct);
decltype(mapOfVariants) res{ {-2, sdbus::Variant{false}}
, {0, sdbus::Variant{false}}
, {2, sdbus::Variant{true}}};
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
}
TEST_F(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithStructInStructSuccessfully)
{
auto val = m_proxy->getStructInStruct();
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
auto val = this->m_proxy->getStructInStruct();
ASSERT_THAT(val.template get<0>(), Eq(STRING_VALUE));
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoStructsSuccessfully)
{
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
auto val = this->m_proxy->sumStructItems({1, 2}, {3, 4});
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
}
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithTwoVectorsSuccessfully)
{
auto val = m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
auto val = this->m_proxy->sumArrayItems({1, 7}, {2, 3, 4});
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3 + 4));
}
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithSignatureSuccessfully)
{
auto resSignature = m_proxy->getSignature();
auto resSignature = this->m_proxy->getSignature();
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithObjectPathSuccessfully)
{
auto resObjectPath = m_proxy->getObjPath();
auto resObjectPath = this->m_proxy->getObjPath();
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithUnixFdSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithUnixFdSuccessfully)
{
auto resUnixFd = m_proxy->getUnixFd();
auto resUnixFd = this->m_proxy->getUnixFd();
ASSERT_THAT(resUnixFd.get(), Gt(UNIX_FD_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithComplexTypeSuccessfully)
{
auto resComplex = m_proxy->getComplex();
auto resComplex = this->m_proxy->getComplex();
ASSERT_THAT(resComplex.count(0), Eq(1));
}
TEST_F(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
TYPED_TEST(SdbusTestObject, CallsMultiplyMethodWithNoReplyFlag)
{
m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
this->m_proxy->multiplyWithNoReply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_TRUE(waitUntil(m_adaptor->m_wasMultiplyCalled));
ASSERT_THAT(m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasMultiplyCalled));
ASSERT_THAT(this->m_adaptor->m_multiplyResult, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TEST_F(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
TYPED_TEST(SdbusTestObject, CallsMethodWithCustomTimeoutSuccessfully)
{
auto res = m_proxy->doOperationWithTimeout(500ms, 20); // The operation will take 20ms, but the timeout is 500ms, so we are fine
auto res = this->m_proxy->doOperationWithTimeout(500ms, (20ms).count()); // The operation will take 20ms, but the timeout is 500ms, so we are fine
ASSERT_THAT(res, Eq(20));
}
TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
TYPED_TEST(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
{
auto start = std::chrono::steady_clock::now();
try
{
m_proxy->doOperationWithTimeout(1us, 1000); // The operation will take 1s, but the timeout is 1us, so we should time out
this->m_proxy->doOperationWithTimeout(1us, (1s).count()); // The operation will take 1s, but the timeout is 1us, so we should time out
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
@@ -190,11 +205,11 @@ TEST_F(SdbusTestObject, ThrowsTimeoutErrorWhenMethodTimesOut)
}
}
TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
TYPED_TEST(SdbusTestObject, CallsMethodThatThrowsError)
{
try
{
m_proxy->throwError();
this->m_proxy->throwError();
FAIL() << "Expected sdbus::Error exception";
}
catch (const sdbus::Error& e)
@@ -208,82 +223,158 @@ TEST_F(SdbusTestObject, CallsMethodThatThrowsError)
}
}
TEST_F(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
TYPED_TEST(SdbusTestObject, CallsErrorThrowingMethodWithDontExpectReplySet)
{
ASSERT_NO_THROW(m_proxy->throwErrorWithNoReply());
ASSERT_NO_THROW(this->m_proxy->throwErrorWithNoReply());
ASSERT_TRUE(waitUntil(m_adaptor->m_wasThrowErrorCalled));
ASSERT_TRUE(waitUntil(this->m_adaptor->m_wasThrowErrorCalled));
}
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
TYPED_TEST(SdbusTestObject, FailsCallingNonexistentMethod)
{
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
ASSERT_THROW(this->m_proxy->callNonexistentMethod(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
{
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
ASSERT_THROW(this->m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
{
TestProxy proxy("sdbuscpp.destination.that.does.not.exist", OBJECT_PATH);
TestProxy proxy(sdbus::ServiceName{"sdbuscpp.destination.that.does.not.exist"}, OBJECT_PATH);
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
TYPED_TEST(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
{
TestProxy proxy(BUS_NAME, "/sdbuscpp/path/that/does/not/exist");
TestProxy proxy(SERVICE_NAME, sdbus::ObjectPath{"/sdbuscpp/path/that/does/not/exist"});
ASSERT_THROW(proxy.getInt(), sdbus::Error);
}
TEST_F(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
TYPED_TEST(SdbusTestObject, CanReceiveSignalWhileMakingMethodCall)
{
m_proxy->emitTwoSimpleSignals();
this->m_proxy->emitTwoSimpleSignals();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
EXPECT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithMap));
}
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
TYPED_TEST(SdbusTestObject, CanSendAndReceiveDictionariesAsCustomStructsImplicitly)
{
m_proxy->doOperation(10); // This will save pointer to method call message on server side
// This test demonstrates that sdbus-c++ can send a SDBUSCPP_REGISTER_STRUCT-described struct as a dictionary of strings to variants,
// and that sdbus-c++ can automatically deserialize a dictionary of strings to variants into such a struct (instead of a map).
const my::Struct structSent{3545342, "hello"s, {3.14, 2.4568546}};
my::Struct structReceived;
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperation"));
this->m_proxy->getProxy().callMethod("returnDictionary")
.onInterface("org.sdbuscpp.integrationtests")
.withArguments(sdbus::as_dictionary(structSent))
.storeResultsTo(structReceived);
ASSERT_THAT(structReceived, Eq(structSent));
}
TEST_F(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInMethodCallHandler)
{
m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
this->m_proxy->doOperation(10); // This will save pointer to method call message on server side
ASSERT_THAT(m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(m_adaptor->m_methodCallMemberName, Eq("doOperationAsync"));
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperation"));
}
TYPED_TEST(SdbusTestObject, ProvidesSerialInMethodCallAndMethodReplyMessage)
{
auto reply = this->m_proxy->doOperationOnBasicAPILevel(ANY_UNSIGNED_NUMBER);
ASSERT_THAT(this->m_proxy->m_methodCallMsg->getCookie(), Gt(0));
ASSERT_THAT(reply.getReplyCookie(), Eq(this->m_proxy->m_methodCallMsg->getCookie())); // Pairing method reply with method call message
}
TYPED_TEST(SdbusTestObject, CanAccessAssociatedMethodCallMessageInAsyncMethodCallHandler)
{
this->m_proxy->doOperationAsync(10); // This will save pointer to method call message on server side
ASSERT_THAT(this->m_adaptor->m_methodCallMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_methodName, Eq("doOperationAsync"));
}
TYPED_TEST(SdbusTestObject, CallsMethodWithLargeArgument)
{
std::map<int, std::string> collection;
//std::size_t totalSize{};
for (int i = 0; i < 400'000; i++)
{
collection[i] = ("This is a string of fifty characters. This is a string of fifty " + std::to_string(i));
//totalSize += sizeof(int) + collection[i].size();
}
//printf("Sending large message with collection of size %zu bytes\n", totalSize);
this->m_proxy->sendLargeMessage(collection);
}
TYPED_TEST(SdbusTestObject, CanSendCallsAndReceiveRepliesWithLargeData)
{
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 40'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
auto returnedMap = this->m_proxy->doOperationWithLargeData(largeMap);
ASSERT_THAT(returnedMap, Eq(largeMap));
}
#if LIBSYSTEMD_VERSION>=240
TEST_F(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
TYPED_TEST(SdbusTestObject, CanSetGeneralMethodTimeoutWithLibsystemdVersionGreaterThan239)
{
s_adaptorConnection->setMethodCallTimeout(5000000);
ASSERT_THAT(s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
this->s_adaptorConnection->setMethodCallTimeout(5000000);
ASSERT_THAT(this->s_adaptorConnection->getMethodCallTimeout(), Eq(5000000));
}
#else
TEST_F(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
TYPED_TEST(SdbusTestObject, CannotSetGeneralMethodTimeoutWithLibsystemdVersionLessThan240)
{
ASSERT_THROW(s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
ASSERT_THROW(this->s_adaptorConnection->setMethodCallTimeout(5000000), sdbus::Error);
ASSERT_THROW(this->s_adaptorConnection->getMethodCallTimeout(), sdbus::Error);
}
#endif
TEST_F(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
TYPED_TEST(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
{
#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 proxy = std::make_unique<TestProxy>(SERVICE_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
auto multiplyRes = proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
}
TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime)
{
auto& object = this->m_adaptor->getObject();
sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"};
auto vtableSlot = object.addVTable( interfaceName
, { sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })
, sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) }
, sdbus::return_slot );
// The new remote vtable is registered as long as we keep vtableSlot, so remote method calls now should pass
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
int result{};
proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2).storeResultsTo(result);
ASSERT_THAT(result, Eq(8));
}
TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime)
{
auto& object = this->m_adaptor->getObject();
sdbus::InterfaceName const interfaceName{"org.sdbuscpp.integrationtests2"};
auto vtableSlot = object.addVTable( interfaceName
, { sdbus::registerMethod("add").implementedAs([](const double& lhs, const double& rhs){ return lhs + rhs; })
, sdbus::registerMethod("subtract").implementedAs([](const int& lhs, const int& rhs){ return lhs - rhs; }) }
, sdbus::return_slot );
vtableSlot.reset(); // Letting the slot go means letting go the associated vtable registration
// No such remote D-Bus method under given interface exists anymore...
auto proxy = sdbus::createLightWeightProxy(SERVICE_NAME, OBJECT_PATH);
ASSERT_THROW(proxy->callMethod("subtract").onInterface(interfaceName).withArguments(10, 2), sdbus::Error);
}
+25 -29
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusPropertiesTests.cpp
*
@@ -25,61 +25,57 @@
*/
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include "Defs.h"
#include <sdbus-c++/sdbus-c++.h>
#include <cstdint>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::NotNull;
using ::testing::Not;
using ::testing::IsEmpty;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, ReadsReadOnlyPropertySuccesfully)
TYPED_TEST(SdbusTestObject, ReadsReadOnlyPropertySuccessfully)
{
ASSERT_THAT(m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(this->m_proxy->state(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, FailsWritingToReadOnlyProperty)
TYPED_TEST(SdbusTestObject, FailsWritingToReadOnlyProperty)
{
ASSERT_THROW(m_proxy->setStateProperty("new_value"), sdbus::Error);
ASSERT_THROW(this->m_proxy->setStateProperty("new_value"), sdbus::Error);
}
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWritePropertySuccessfully)
{
uint32_t newActionValue = 5678;
uint32_t const newActionValue = 5678;
m_proxy->action(newActionValue);
this->m_proxy->action(newActionValue);
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
TYPED_TEST(SdbusTestObject, CanAccessAssociatedPropertySetMessageInPropertySetHandler)
{
m_proxy->blocking(true); // This will save pointer to property get message on server side
this->m_proxy->blocking(true); // This will save pointer to property get message on server side
ASSERT_THAT(m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(m_adaptor->m_propertySetSender, Not(IsEmpty()));
ASSERT_THAT(this->m_adaptor->m_propertySetMsg, NotNull());
ASSERT_THAT(this->m_adaptor->m_propertySetSender, Not(IsEmpty()));
}
TYPED_TEST(SdbusTestObject, WritesAndReadsReadWriteVariantPropertySuccessfully)
{
sdbus::Variant const newActionValue{5678};
this->m_proxy->actionVariant(newActionValue);
ASSERT_THAT(this->m_proxy->actionVariant().template get<int>(), Eq(5678));
}
+70 -66
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorAndProxy_test.cpp
*
@@ -27,143 +27,147 @@
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include "Defs.h"
#include <sdbus-c++/sdbus-c++.h>
#include <cstdint>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <map>
#include <string>
#include <chrono>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::NotNull;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalSuccessfully)
{
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSimpleSignalToMultipleProxiesSuccessfully)
{
auto proxy1 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*s_adaptorConnection, BUS_NAME, OBJECT_PATH);
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);
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
}
TEST_F(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
TYPED_TEST(SdbusTestObject, ProxyDoesNotReceiveSignalFromOtherBusName)
{
auto otherBusName = BUS_NAME + "2";
auto connection2 = sdbus::createConnection(otherBusName);
sdbus::ServiceName const otherBusName{SERVICE_NAME + "2"};
auto connection2 = sdbus::createBusConnection(otherBusName);
auto adaptor2 = std::make_unique<TestAdaptor>(*connection2, OBJECT_PATH);
adaptor2->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
}
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithMapSuccessfully)
{
m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
this->m_adaptor->emitSignalWithMap({{0, "zero"}, {1, "one"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithMap));
ASSERT_THAT(m_proxy->m_mapFromSignal[0], Eq("zero"));
ASSERT_THAT(m_proxy->m_mapFromSignal[1], Eq("one"));
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"));
}
TEST_F(SdbusTestObject, EmitsSignalWithLargeMapSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithLargeMapSuccessfully)
{
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 20'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
m_adaptor->emitSignalWithMap(largeMap);
std::map<int32_t, std::string> largeMap;
for (int32_t i = 0; i < 20'000; ++i)
largeMap.emplace(i, "This is string nr. " + std::to_string(i+1));
this->m_adaptor->emitSignalWithMap(largeMap);
ASSERT_TRUE(waitUntil(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"));
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"));
}
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithVariantSuccessfully)
{
double d = 3.14;
m_adaptor->emitSignalWithVariant(sdbus::Variant{d});
double const val = 3.14;
this->m_adaptor->emitSignalWithVariant(sdbus::Variant{val});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(m_proxy->m_variantFromSignal, DoubleEq(d));
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithVariant));
ASSERT_THAT(this->m_proxy->m_variantFromSignal, DoubleEq(val));
}
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
TYPED_TEST(SdbusTestObject, EmitsSignalWithoutRegistrationSuccessfully)
{
m_adaptor->emitSignalWithoutRegistration({"platform", {"av"}});
this->m_adaptor->emitSignalWithoutRegistration({"platform", sdbus::Signature{"av"}});
ASSERT_TRUE(waitUntil(m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(m_proxy->m_signatureFromSignal["platform"], Eq("av"));
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithSignature));
ASSERT_THAT(this->m_proxy->m_signatureFromSignal["platform"], Eq(sdbus::Signature{"av"}));
}
TEST_F(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
TYPED_TEST(SdbusTestObject, EmitsSignalWithErrorAndTypeMismatchSuccessfully)
{
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSignalWithErrorAndTypeMismatch();
waitUntil(m_proxy->m_gotSimpleSignal);
ASSERT_THAT(m_proxy->m_signalMsg, NotNull());
ASSERT_THAT(m_proxy->m_signalMemberName, Eq("simpleSignal"));
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSignalWithTypeMismatch));
ASSERT_TRUE(this->m_proxy->m_errorFromSignal.has_value());
}
TEST_F(SdbusTestObject, UnregistersSignalHandler)
TYPED_TEST(SdbusTestObject, CanAccessAssociatedSignalMessageInSignalHandler)
{
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
this->m_adaptor->emitSimpleSignal();
m_adaptor->emitSimpleSignal();
waitUntil(this->m_proxy->m_gotSimpleSignal);
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_THAT(this->m_proxy->m_signalMsg, NotNull());
ASSERT_THAT(this->m_proxy->m_signalName, Eq(std::string("simpleSignal")));
}
TEST_F(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
TYPED_TEST(SdbusTestObject, UnregistersSignalHandler)
{
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_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
}
TYPED_TEST(SdbusTestObject, UnregistersSignalHandlerForSomeProxies)
{
auto proxy1 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
auto proxy2 = std::make_unique<TestProxy>(*this->s_adaptorConnection, SERVICE_NAME, OBJECT_PATH);
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(proxy1->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(proxy2->m_gotSimpleSignal));
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
}
TEST_F(SdbusTestObject, ReRegistersSignalHandler)
TYPED_TEST(SdbusTestObject, ReRegistersSignalHandler)
{
// unregister simple-signal handler
ASSERT_NO_THROW(m_proxy->unregisterSimpleSignalHandler());
ASSERT_NO_THROW(this->m_proxy->unregisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_FALSE(waitUntil(m_proxy->m_gotSimpleSignal, 2s));
ASSERT_FALSE(waitUntil(this->m_proxy->m_gotSimpleSignal, 1s));
// re-register simple-signal handler
ASSERT_NO_THROW(m_proxy->reRegisterSimpleSignalHandler());
ASSERT_NO_THROW(this->m_proxy->reRegisterSimpleSignalHandler());
m_adaptor->emitSimpleSignal();
this->m_adaptor->emitSimpleSignal();
ASSERT_TRUE(waitUntil(m_proxy->m_gotSimpleSignal));
ASSERT_TRUE(waitUntil(this->m_proxy->m_gotSimpleSignal));
}
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file DBusStandardInterfacesTests.cpp
*
@@ -26,259 +26,321 @@
#include "TestFixture.h"
#include "TestAdaptor.h"
#include "TestProxy.h"
#include "sdbus-c++/sdbus-c++.h"
#include "Defs.h"
#include "integrationtests-adaptor.h"
#include <sdbus-c++/sdbus-c++.h>
#include <exception>
#include <cstdint>
#include <atomic>
#include <cstddef>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <optional>
#include <map>
#include <string>
#include <thread>
#include <tuple>
#include <chrono>
#include <fstream>
#include <future>
#include <unistd.h>
#include <utility>
#include <vector>
using ::testing::Eq;
using ::testing::DoubleEq;
using ::testing::Gt;
using ::testing::AnyOf;
using ::testing::ElementsAre;
using ::testing::SizeIs;
using namespace std::chrono_literals;
using namespace sdbus::test;
using SdbusTestObject = TestFixture;
/*-------------------------------------*/
/* -- TEST CASES -- */
/*-------------------------------------*/
TEST_F(SdbusTestObject, PingsViaPeerInterface)
TYPED_TEST(SdbusTestObject, PingsViaPeerInterface)
{
ASSERT_NO_THROW(m_proxy->Ping());
ASSERT_NO_THROW(this->m_proxy->Ping());
}
TEST_F(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
TYPED_TEST(SdbusTestObject, AnswersMachineUuidViaPeerInterface)
{
if (::access("/etc/machine-id", F_OK) == -1 &&
::access("/var/lib/dbus/machine-id", F_OK) == -1)
GTEST_SKIP() << "/etc/machine-id and /var/lib/dbus/machine-id files do not exist, GetMachineId() will not work";
ASSERT_NO_THROW(m_proxy->GetMachineId());
ASSERT_NO_THROW(this->m_proxy->GetMachineId());
}
// TODO: Adjust expected xml and uncomment this test
//TEST_F(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
//TYPED_TEST(SdbusTestObject, AnswersXmlApiDescriptionViaIntrospectableInterface)
//{
// ASSERT_THAT(m_proxy->Introspect(), Eq(m_adaptor->getExpectedXmlApiDescription()));
// ASSERT_THAT(this->m_proxy->Introspect(), Eq(this->m_adaptor->getExpectedXmlApiDescription()));
//}
TEST_F(SdbusTestObject, GetsPropertyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, GetsPropertyViaPropertiesInterface)
{
ASSERT_THAT(m_proxy->Get(INTERFACE_NAME, "state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(this->m_proxy->Get(INTERFACE_NAME, "state").template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterface)
{
std::promise<std::string> promise;
auto future = promise.get_future();
m_proxy->GetAsync(INTERFACE_NAME, "state", [&](const sdbus::Error* err, sdbus::Variant value)
this->m_proxy->GetAsync(INTERFACE_NAME, "state", [&](std::optional<sdbus::Error> err, const sdbus::Variant& value)
{
if (err == nullptr)
if (!err)
promise.set_value(value.get<std::string>());
else
promise.set_exception(std::make_exception_ptr(*err));
promise.set_exception(std::make_exception_ptr(*std::move(err)));
});
ASSERT_THAT(future.get(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
TYPED_TEST(SdbusTestObject, GetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
{
auto future = m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
auto future = this->m_proxy->GetAsync(INTERFACE_NAME, "state", sdbus::with_future);
ASSERT_THAT(future.get().get<std::string>(), Eq(DEFAULT_STATE_VALUE));
ASSERT_THAT(future.get().template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
}
TEST_F(SdbusTestObject, SetsPropertyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, SetsPropertyViaPropertiesInterface)
{
uint32_t newActionValue = 2345;
uint32_t const newActionValue = 2345;
m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
this->m_proxy->Set(INTERFACE_NAME, "action", sdbus::Variant{newActionValue});
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
TYPED_TEST(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterface)
{
uint32_t newActionValue = 2346;
uint32_t const newActionValue = 2346;
std::promise<void> promise;
auto future = promise.get_future();
m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](const sdbus::Error* err)
this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, [&](std::optional<sdbus::Error> err)
{
if (err == nullptr)
if (!err)
promise.set_value();
else
promise.set_exception(std::make_exception_ptr(*err));
promise.set_exception(std::make_exception_ptr(*std::move(err)));
});
ASSERT_NO_THROW(future.get());
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, SetsPropertyAsynchronouslyViaPropertiesInterfaceWithFuture)
TYPED_TEST(SdbusTestObject, CancelsAsynchronousPropertySettingViaPropertiesInterface)
{
uint32_t newActionValue = 2347;
auto future = m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
ASSERT_NO_THROW(future.get());
ASSERT_THAT(m_proxy->action(), Eq(newActionValue));
}
TEST_F(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
{
std::promise<std::map<std::string, sdbus::Variant>> promise;
uint32_t const newActionValue = 2346;
std::promise<void> promise;
auto future = promise.get_future();
m_proxy->GetAllAsync(INTERFACE_NAME, [&](const sdbus::Error* err, std::map<std::string, sdbus::Variant> value)
{
if (err == nullptr)
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 const newActionValue = 2347;
auto future = this->m_proxy->SetAsync(INTERFACE_NAME, "action", sdbus::Variant{newActionValue}, sdbus::with_future);
ASSERT_NO_THROW(future.get());
ASSERT_THAT(this->m_proxy->action(), Eq(newActionValue));
}
TYPED_TEST(SdbusTestObject, GetsAllPropertiesViaPropertiesInterface)
{
const auto properties = this->m_proxy->GetAll(INTERFACE_NAME);
ASSERT_THAT(properties, SizeIs(4));
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterface)
{
std::promise<std::map<sdbus::PropertyName, sdbus::Variant>> promise;
auto future = promise.get_future();
this->m_proxy->GetAllAsync(INTERFACE_NAME, [&](std::optional<sdbus::Error> err, std::map<sdbus::PropertyName, sdbus::Variant> value)
{
if (!err)
promise.set_value(std::move(value));
else
promise.set_exception(std::make_exception_ptr(*err));
promise.set_exception(std::make_exception_ptr(*std::move(err)));
});
const auto properties = future.get();
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
ASSERT_THAT(properties, SizeIs(4));
EXPECT_THAT(properties.at(STATE_PROPERTY).get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
TEST_F(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
TYPED_TEST(SdbusTestObject, GetsAllPropertiesAsynchronouslyViaPropertiesInterfaceWithFuture)
{
auto future = m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
auto future = this->m_proxy->GetAllAsync(INTERFACE_NAME, sdbus::with_future);
auto properties = future.get();
ASSERT_THAT(properties, SizeIs(3));
EXPECT_THAT(properties.at("state").get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
ASSERT_THAT(properties, SizeIs(4));
EXPECT_THAT(properties.at(STATE_PROPERTY).template get<std::string>(), Eq(DEFAULT_STATE_VALUE));
EXPECT_THAT(properties.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(properties.at(ACTION_VARIANT_PROPERTY).template get<sdbus::Variant>().template get<std::string>(), Eq(DEFAULT_ACTION_VARIANT_VALUE));
EXPECT_THAT(properties.at(BLOCKING_PROPERTY).template get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForSelectedProperties)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& /*invalidatedProperties*/ )
std::atomic signalReceived{false};
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
, const std::vector<sdbus::PropertyName>& /*invalidatedProperties*/ )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
EXPECT_THAT(changedProperties.at(BLOCKING_PROPERTY).get<bool>(), Eq(!DEFAULT_BLOCKING_VALUE));
signalReceived = true;
};
m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
m_proxy->action(DEFAULT_ACTION_VALUE*2);
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {"blocking"});
this->m_proxy->blocking(!DEFAULT_BLOCKING_VALUE);
this->m_proxy->action(DEFAULT_ACTION_VALUE*2);
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME, {BLOCKING_PROPERTY});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties)
TYPED_TEST(SdbusTestObject, EmitsPropertyChangedSignalForAllProperties) // NOLINT(readability-function-cognitive-complexity)
{
std::atomic<bool> signalReceived{false};
m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
std::atomic signalReceived{false};
this->m_proxy->m_onPropertiesChangedHandler = [&signalReceived]( const sdbus::InterfaceName& interfaceName
, const std::map<sdbus::PropertyName, sdbus::Variant>& changedProperties
, const std::vector<sdbus::PropertyName>& invalidatedProperties )
{
EXPECT_THAT(interfaceName, Eq(INTERFACE_NAME));
EXPECT_THAT(changedProperties, SizeIs(1));
EXPECT_THAT(changedProperties.at("blocking").get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
EXPECT_THAT(changedProperties.at(BLOCKING_PROPERTY).get<bool>(), Eq(DEFAULT_BLOCKING_VALUE));
ASSERT_THAT(invalidatedProperties, SizeIs(1));
EXPECT_THAT(invalidatedProperties[0], Eq("action"));
signalReceived = true;
};
m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
this->m_adaptor->emitPropertiesChangedSignal(INTERFACE_NAME);
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
TYPED_TEST(SdbusTestObject, GetsZeroManagedObjectsIfHasNoSubPathObjects)
{
m_adaptor.reset();
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
this->m_adaptor.reset();
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(0));
}
TEST_F(SdbusTestObject, GetsManagedObjectsSuccessfully)
TYPED_TEST(SdbusTestObject, GetsManagedObjectsSuccessfully)
{
auto adaptor2 = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH_2);
const auto objectsInterfacesAndProperties = m_objectManagerProxy->GetManagedObjects();
auto adaptor2 = std::make_unique<TestAdaptor>(*this->s_adaptorConnection, OBJECT_PATH_2);
const auto objectsInterfacesAndProperties = this->m_objectManagerProxy->GetManagedObjects();
ASSERT_THAT(objectsInterfacesAndProperties, SizeIs(2));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH)
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
EXPECT_THAT(objectsInterfacesAndProperties.at(OBJECT_PATH_2)
.at(org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME)
.at("action").get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
.at(sdbus::InterfaceName{org::sdbuscpp::integrationtests_adaptor::INTERFACE_NAME})
.at(ACTION_PROPERTY).template get<uint32_t>(), Eq(DEFAULT_ACTION_VALUE));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForSelectedObjectInterfaces)
TYPED_TEST(SdbusTestObject, GetsManagedObjectsAsynchronously)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
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([&](const 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([&](const 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) // NOLINT(readability-function-cognitive-complexity)
{
std::atomic signalReceived{false};
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
EXPECT_THAT(interfacesAndProperties, SizeIs(1));
EXPECT_THAT(interfacesAndProperties.count(INTERFACE_NAME), Eq(1));
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(4));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
#endif
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
this->m_adaptor->emitInterfacesAddedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
TYPED_TEST(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces) // NOLINT(readability-function-cognitive-complexity)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties )
std::atomic signalReceived{false};
this->m_objectManagerProxy->m_onInterfacesAddedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::map<sdbus::InterfaceName, std::map<sdbus::PropertyName, sdbus::Variant>>& interfacesAndProperties )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
@@ -290,31 +352,33 @@ TEST_F(SdbusTestObject, EmitsInterfacesAddedSignalForAllObjectInterfaces)
#endif
#if LIBSYSTEMD_VERSION<=244
// Up to sd-bus v244, all properties are added to the list, i.e. `state', `action', and `blocking' in this case.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("action"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(4));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
#else
// Since v245 sd-bus does not add to the InterfacesAdded signal message the values of properties marked only
// for invalidation on change, which makes the behavior consistent with the PropertiesChangedSignal.
// So in this specific instance, `action' property is no more added to the list.
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(2));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("state"));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count("blocking"));
EXPECT_THAT(interfacesAndProperties.at(INTERFACE_NAME), SizeIs(3));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(STATE_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(BLOCKING_PROPERTY));
EXPECT_TRUE(interfacesAndProperties.at(INTERFACE_NAME).count(ACTION_VARIANT_PROPERTY));
#endif
signalReceived = true;
};
m_adaptor->emitInterfacesAddedSignal();
this->m_adaptor->emitInterfacesAddedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
std::atomic signalReceived{false};
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<sdbus::InterfaceName>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
ASSERT_THAT(interfaces, SizeIs(1));
@@ -322,16 +386,16 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForSelectedObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
this->m_adaptor->emitInterfacesRemovedSignal({INTERFACE_NAME});
ASSERT_TRUE(waitUntil(signalReceived));
}
TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
TYPED_TEST(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
{
std::atomic<bool> signalReceived{false};
m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<std::string>& interfaces )
std::atomic signalReceived{false};
this->m_objectManagerProxy->m_onInterfacesRemovedHandler = [&signalReceived]( const sdbus::ObjectPath& objectPath
, const std::vector<sdbus::InterfaceName>& interfaces )
{
EXPECT_THAT(objectPath, Eq(OBJECT_PATH));
#if LIBSYSTEMD_VERSION<=250
@@ -344,7 +408,7 @@ TEST_F(SdbusTestObject, EmitsInterfacesRemovedSignalForAllObjectInterfaces)
signalReceived = true;
};
m_adaptor->emitInterfacesRemovedSignal();
this->m_adaptor->emitInterfacesRemovedSignal();
ASSERT_TRUE(waitUntil(signalReceived));
}
+22 -16
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Defs.h
*
@@ -32,14 +32,18 @@
#include <ostream>
#include <filesystem>
namespace sdbus { namespace test {
namespace sdbus::test {
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 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 ACTION_VARIANT_PROPERTY{"actionVariant"};
const PropertyName BLOCKING_PROPERTY{"blocking"};
const std::string DIRECT_CONNECTION_SOCKET_PATH{std::filesystem::temp_directory_path() / "sdbus-cpp-direct-connection-test"};
constexpr const uint8_t UINT8_VALUE{1};
@@ -49,28 +53,30 @@ constexpr const int32_t INT32_VALUE{-42};
constexpr const int32_t INT64_VALUE{-1024};
const std::string STRING_VALUE{"sdbus-c++-testing"};
const sdbus::Signature SIGNATURE_VALUE{"a{is}"};
const sdbus::ObjectPath OBJECT_PATH_VALUE{"/"};
const Signature SIGNATURE_VALUE{"a{is}"};
const ObjectPath OBJECT_PATH_VALUE{"/"};
const int UNIX_FD_VALUE = 0;
const std::string DEFAULT_STATE_VALUE{"default-state-value"};
const uint32_t DEFAULT_ACTION_VALUE{999};
const std::string DEFAULT_ACTION_VARIANT_VALUE{"ahoj"};
const bool DEFAULT_BLOCKING_VALUE{true};
constexpr const double DOUBLE_VALUE{3.24L};
}}
} // namespace sdbus::test
namespace testing::internal {
// Printer for std::chrono::duration types.
// This is a workaround, since it's not a good thing to add this to std namespace.
template< class Rep, class Period >
void PrintTo(const ::std::chrono::duration<Rep, Period>& d, ::std::ostream* os) {
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(d);
*os << seconds.count() << "s";
template<class Rep, class Period>
void PrintTo(const ::std::chrono::duration<Rep, Period>& duration, ::std::ostream* stream)
{
auto seconds = std::chrono::duration_cast<std::chrono::duration<double>>(duration);
*stream << seconds.count() << "s";
}
}
} // namespace testing::internal
#endif /* SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ */
+107 -36
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
*
@@ -25,14 +25,31 @@
*/
#include "TestAdaptor.h"
#include "sdbus-c++/IConnection.h"
#include "sdbus-c++/Types.h"
#include "sdbus-c++/AdaptorInterfaces.h"
#include <cstdint>
#include "Defs.h"
#include <string>
#include <map>
#include <array>
#include <memory>
#include "sdbus-c++/Message.h"
#include "sdbus-c++/MethodResult.h"
#include "sdbus-c++/Error.h"
#include <thread>
#include <chrono>
#include <atomic>
#include <utility>
#include <tuple>
#include <vector>
#include <variant>
#include <unordered_map>
namespace sdbus { namespace test {
namespace sdbus::test {
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, const std::string& path) :
AdaptorInterfaces(connection, path)
TestAdaptor::TestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path) :
AdaptorInterfaces(connection, std::move(path))
{
registerAdaptor();
}
@@ -56,37 +73,37 @@ std::tuple<uint32_t, std::string> TestAdaptor::getTuple()
return std::make_tuple(UINT32_VALUE, STRING_VALUE);
}
double TestAdaptor::multiply(const int64_t& a, const double& b)
double TestAdaptor::multiply(const int64_t& lhs, const double& rhs)
{
return a * b;
return lhs * rhs; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
}
void TestAdaptor::multiplyWithNoReply(const int64_t& a, const double& b)
void TestAdaptor::multiplyWithNoReply(const int64_t& lhs, const double& rhs)
{
m_multiplyResult = a * b;
m_multiplyResult = lhs * rhs; // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
m_wasMultiplyCalled = true;
}
std::vector<int16_t> TestAdaptor::getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x)
std::vector<int16_t> TestAdaptor::getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& strct)
{
std::vector<int16_t> res{x.get<1>()};
auto y = std::get<std::vector<int16_t>>(x);
res.insert(res.end(), y.begin(), y.end());
std::vector res{strct.get<1>()};
auto vec = std::get<std::vector<int16_t>>(strct);
res.insert(res.end(), vec.begin(), vec.end());
return res;
}
sdbus::Variant TestAdaptor::processVariant(const sdbus::Variant& v)
sdbus::Variant TestAdaptor::processVariant(const std::variant<int32_t, double, std::string>& var)
{
sdbus::Variant res{static_cast<int32_t>(v.get<double>())};
sdbus::Variant res{static_cast<int32_t>(std::get<double>(var))};
return res;
}
std::map<int32_t, sdbus::Variant> TestAdaptor::getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
std::map<int32_t, sdbus::Variant> TestAdaptor::getMapOfVariants(const std::vector<int32_t>& vec, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& strct)
{
std::map<int32_t, sdbus::Variant> res;
for (auto item : x)
for (auto item : vec)
{
res[item] = (item <= 0) ? std::get<0>(y) : std::get<1>(y);
res[item] = (item <= 0) ? std::get<0>(strct) : std::get<1>(strct);
}
return res;
}
@@ -96,24 +113,24 @@ sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> TestAdapto
return sdbus::Struct{STRING_VALUE, sdbus::Struct{std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}}};
}
int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
int32_t TestAdaptor::sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& strctA, const sdbus::Struct<int32_t, int64_t>& strctB)
{
int32_t res{0};
res += std::get<0>(a) + std::get<1>(a);
res += std::get<0>(b) + std::get<1>(b);
res += std::get<0>(strctA) + std::get<1>(strctA);
res += std::get<0>(strctB) + std::get<1>(strctB); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
return res;
}
uint32_t TestAdaptor::sumArrayItems(const std::vector<uint16_t>& a, const std::array<uint64_t, 3>& b)
uint32_t TestAdaptor::sumArrayItems(const std::vector<uint16_t>& vec, const std::array<uint64_t, 3>& arr)
{
uint32_t res{0};
for (auto x : a)
for (auto elem : vec)
{
res += x;
res += elem;
}
for (auto x : b)
for (auto elem : arr)
{
res += x;
res += elem;
}
return res;
}
@@ -122,16 +139,24 @@ uint32_t TestAdaptor::doOperation(const uint32_t& param)
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
m_methodCallMemberName = m_methodCallMsg->getMemberName();
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_methodName = m_methodCallMsg->getMemberName();
return param;
}
std::map<int32_t, std::string> TestAdaptor::doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam)
{
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_methodName = m_methodCallMsg->getMemberName();
return largeParam;
}
void TestAdaptor::doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t param)
{
m_methodCallMsg = getObject().getCurrentlyProcessedMessage();
m_methodCallMemberName = m_methodCallMsg->getMemberName();
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_methodName = m_methodCallMsg->getMemberName();
if (param == 0)
{
@@ -149,6 +174,27 @@ void TestAdaptor::doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t pa
}
}
void TestAdaptor::doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t param, const std::map<int32_t, std::string>& largeMap)
{
m_methodCallMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_methodName = m_methodCallMsg->getMemberName();
if (param == 0)
{
// Don't sleep and return the result from this thread
result.returnResults(largeMap);
}
else
{
// Process asynchronously in another thread and return the result from there
std::thread([param, largeMap, result = std::move(result)]()
{
std::this_thread::sleep_for(std::chrono::milliseconds(param));
result.returnResults(largeMap);
}).detach();
}
}
sdbus::Signature TestAdaptor::getSignature()
{
return SIGNATURE_VALUE;
@@ -173,7 +219,7 @@ std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::
23, // uint8_t
{ // vector
{ // struct
"/object/path", // object path
sdbus::ObjectPath{"/object/path"}, // object path
false,
Variant{3.14},
{ // map
@@ -183,7 +229,7 @@ std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::
}
}
},
"a{t(a{ya(obva{is})}gs)}", // signature
sdbus::Signature{"a{t(a{ya(obva{is})}gs)}"}, // signature
std::string{}
}
}
@@ -212,6 +258,16 @@ void TestAdaptor::emitTwoSimpleSignals()
emitSignalWithMap({});
}
void TestAdaptor::sendLargeMessage(const std::map<int, std::string>& /*collection*/)
{
//printf("Adaptor: got collection with %zu items", collection.size());
}
std::map<std::string, sdbus::Variant> TestAdaptor::returnDictionary(const std::map<std::string, sdbus::Variant>& dict)
{
return dict;
}
std::string TestAdaptor::state()
{
return m_state;
@@ -227,6 +283,16 @@ void TestAdaptor::action(const uint32_t& value)
m_action = value;
}
sdbus::Variant TestAdaptor::actionVariant()
{
return m_actionVariant;
}
void TestAdaptor::actionVariant(const sdbus::Variant& value)
{
m_actionVariant = value;
}
bool TestAdaptor::blocking()
{
return m_blocking;
@@ -234,18 +300,23 @@ bool TestAdaptor::blocking()
void TestAdaptor::blocking(const bool& value)
{
m_propertySetMsg = getObject().getCurrentlyProcessedMessage();
m_propertySetMsg = std::make_unique<const Message>(getObject().getCurrentlyProcessedMessage());
m_propertySetSender = m_propertySetMsg->getSender();
m_blocking = value;
}
void TestAdaptor::emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
void TestAdaptor::emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct)
{
getObject().emitSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).withArguments(s);
getObject().emitSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).withArguments(strct);
}
std::string TestAdaptor::getExpectedXmlApiDescription() const
void TestAdaptor::emitSignalWithErrorAndTypeMismatch()
{
getObject().emitSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME);
}
std::string TestAdaptor::getExpectedXmlApiDescription()
{
return
R"delimiter(<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
@@ -423,4 +494,4 @@ R"delimiter(
)delimiter";
}
}}
} // namespace sdbus::test
+47 -21
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
*
@@ -33,18 +33,24 @@
#include <chrono>
#include <atomic>
#include <utility>
#include <memory>
namespace sdbus { namespace test {
namespace sdbus::test {
class ObjectManagerTestAdaptor final : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor >
{
public:
ObjectManagerTestAdaptor(sdbus::IConnection& connection, std::string path) :
ObjectManagerTestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path) :
AdaptorInterfaces(connection, std::move(path))
{
registerAdaptor();
}
ObjectManagerTestAdaptor(const ObjectManagerTestAdaptor&) = delete;
ObjectManagerTestAdaptor& operator=(const ObjectManagerTestAdaptor&) = delete;
ObjectManagerTestAdaptor(ObjectManagerTestAdaptor&&) = delete;
ObjectManagerTestAdaptor& operator=(ObjectManagerTestAdaptor&&) = delete;
~ObjectManagerTestAdaptor()
{
unregisterAdaptor();
@@ -56,23 +62,29 @@ class TestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::integr
, sdbus::ManagedObject_adaptor >
{
public:
TestAdaptor(sdbus::IConnection& connection, const std::string& path);
TestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path);
TestAdaptor(const TestAdaptor&) = delete;
TestAdaptor& operator=(const TestAdaptor&) = delete;
TestAdaptor(TestAdaptor&&) = delete;
TestAdaptor& operator=(TestAdaptor&&) = delete;
~TestAdaptor();
protected:
void noArgNoReturn() override;
int32_t getInt() override;
std::tuple<uint32_t, std::string> getTuple() override;
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 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;
double multiply(const int64_t& lhs, const double& rhs) override;
void multiplyWithNoReply(const int64_t& lhs, const double& rhs) override;
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& strct) override;
sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>& variant) override;
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& vec, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& strct) 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;
uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1) override;
uint32_t doOperation(const uint32_t& arg0) override;
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) override;
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& strctA, const sdbus::Struct<int32_t, int64_t>& strctB) override;
uint32_t sumArrayItems(const std::vector<uint16_t>& vec, const std::array<uint64_t, 3>& arr) override;
uint32_t doOperation(const uint32_t& param) override;
std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam) override;
void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t param) override;
void doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t param, const std::map<int32_t, std::string>& largeMap) override;
sdbus::Signature getSignature() override;
sdbus::ObjectPath getObjPath() override;
sdbus::UnixFd getUnixFd() override;
@@ -81,21 +93,27 @@ protected:
void throwErrorWithNoReply() override;
void doPrivilegedStuff() override;
void emitTwoSimpleSignals() override;
void sendLargeMessage(const std::map<int, std::string>& collection) override;
std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>& dict) override;
uint32_t action() override;
void action(const uint32_t& value) override;
sdbus::Variant actionVariant() override;
void actionVariant(const sdbus::Variant& value) override;
bool blocking() override;
void blocking(const bool& value) override;
std::string state() override;
public:
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
std::string getExpectedXmlApiDescription() const;
void emitSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct);
void emitSignalWithErrorAndTypeMismatch();
static std::string getExpectedXmlApiDescription() ;
private:
const std::string m_state{DEFAULT_STATE_VALUE};
uint32_t m_action{DEFAULT_ACTION_VALUE};
bool m_blocking{DEFAULT_BLOCKING_VALUE};
sdbus::Variant m_actionVariant{"ahoj"};
public: // for tests
// For dont-expect-reply method call verifications
@@ -103,9 +121,9 @@ public: // for tests
mutable double m_multiplyResult{};
mutable std::atomic<bool> m_wasThrowErrorCalled{false};
const Message* m_methodCallMsg{};
std::string m_methodCallMemberName;
const Message* m_propertySetMsg{};
std::unique_ptr<const Message> m_methodCallMsg;
MethodName m_methodName;
std::unique_ptr<const Message> m_propertySetMsg;
std::string m_propertySetSender;
};
@@ -114,7 +132,9 @@ class DummyTestAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::i
, sdbus::ManagedObject_adaptor >
{
public:
DummyTestAdaptor(sdbus::IConnection& connection, const std::string& path) : AdaptorInterfaces(connection, path) {}
DummyTestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath path)
: AdaptorInterfaces(connection, std::move(path))
{}
protected:
void noArgNoReturn() override {}
@@ -123,13 +143,15 @@ 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 sdbus::Variant&) override { return {}; }
sdbus::Variant processVariant(const std::variant<int32_t, double, std::string>&) 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 {}; }
uint32_t sumArrayItems(const std::vector<uint16_t>&, const std::array<uint64_t, 3>&) override { return {}; }
uint32_t doOperation(const uint32_t&) override { return {}; }
std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>&) override { return {}; }
void doOperationAsync(sdbus::Result<uint32_t>&&, uint32_t) override {}
void doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&&, uint32_t, const std::map<int32_t, std::string>&) override {}
sdbus::Signature getSignature() override { return {}; }
sdbus::ObjectPath getObjPath() override { return {}; }
sdbus::UnixFd getUnixFd() override { return {}; }
@@ -138,14 +160,18 @@ protected:
void throwErrorWithNoReply() override {}
void doPrivilegedStuff() override {}
void emitTwoSimpleSignals() override {}
void sendLargeMessage(const std::map<int, std::string>&) override {}
std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>&) override { return {}; }
uint32_t action() override { return {}; }
void action(const uint32_t&) override {}
sdbus::Variant actionVariant() override { return {}; }
void actionVariant(const sdbus::Variant&) override {}
bool blocking() override { return {}; }
void blocking(const bool&) override {}
std::string state() override { return {}; }
};
}}
} // namespace sdbus::test
#endif /* INTEGRATIONTESTS_TESTADAPTOR_H_ */
+19 -6
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
* @file TestFixture.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
@@ -25,10 +25,23 @@
*/
#include "TestFixture.h"
#include <memory>
#include "sdbus-c++/IConnection.h"
#include <thread>
namespace sdbus { namespace test {
namespace sdbus::test {
std::unique_ptr<sdbus::IConnection> TestFixture::s_adaptorConnection = sdbus::createSystemBusConnection();
std::unique_ptr<sdbus::IConnection> TestFixture::s_proxyConnection = sdbus::createSystemBusConnection();
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
} // namespace sdbus::test
+132 -29
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
* @file TestFixture.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
@@ -33,42 +33,42 @@
#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>
#include <atomic>
#include <chrono>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
namespace sdbus { namespace test {
namespace sdbus::test {
class TestFixture : public ::testing::Test
inline const uint32_t ANY_UNSIGNED_NUMBER{123};
class BaseTestFixture : public ::testing::Test
{
public:
static void SetUpTestCase()
static void SetUpTestSuite()
{
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
s_adaptorConnection->requestName(SERVICE_NAME);
}
static void TearDownTestCase()
static void TearDownTestSuite()
{
s_adaptorConnection->releaseName(BUS_NAME);
s_adaptorConnection->leaveEventLoop();
s_proxyConnection->leaveEventLoop();
s_adaptorConnection->releaseName(SERVICE_NAME);
}
private:
void SetUp() override
{
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_objectManagerProxy = std::make_unique<ObjectManagerTestProxy>(*s_proxyConnection, SERVICE_NAME, MANAGER_PATH);
m_proxy = std::make_unique<TestProxy>(*s_proxyConnection, SERVICE_NAME, OBJECT_PATH);
m_objectManagerAdaptor = std::make_unique<ObjectManagerTestAdaptor>(*s_adaptorConnection, MANAGER_PATH);
m_adaptor = std::make_unique<TestAdaptor>(*s_adaptorConnection, OBJECT_PATH);
@@ -89,12 +89,114 @@ 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 SetUpTestSuite()
{
BaseTestFixture::SetUpTestSuite();
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 TearDownTestSuite()
{
BaseTestFixture::TearDownTestSuite();
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 SetUpTestSuite()
{
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 *src, auto...){ return sd_event_exit(sd_event_source_get_event(src), 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::SetUpTestSuite();
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Give time for the proxy connection to start listening to signals
}
static void TearDownTestSuite()
{
(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::TearDownTestSuite();
}
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;
};
using EventLoopTags = ::testing::Types<SdBusCppLoop, SdEventLoop>;
#else // SDBUS_basu
using EventLoopTags = ::testing::Types<SdBusCppLoop>;
#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:
void SetUp() override
{
int sock = openUnixSocket();
const int sock = openUnixSocket();
createClientAndServerConnections(sock);
createAdaptorAndProxyObjects();
}
@@ -109,18 +211,19 @@ private:
static int openUnixSocket()
{
int sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
const int sock = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
assert(sock >= 0);
sockaddr_un sa;
memset(&sa, 0, sizeof(sa));
sa.sun_family = AF_UNIX;
snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", DIRECT_CONNECTION_SOCKET_PATH.c_str());
sockaddr_un saddr{};
memset(&saddr, 0, sizeof(saddr));
saddr.sun_family = AF_UNIX;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
(void)snprintf(saddr.sun_path, sizeof(saddr.sun_path), "%s", DIRECT_CONNECTION_SOCKET_PATH.c_str());
unlink(DIRECT_CONNECTION_SOCKET_PATH.c_str());
umask(0000);
[[maybe_unused]] int r = bind(sock, (const sockaddr*) &sa, sizeof(sa.sun_path));
[[maybe_unused]] int r = bind(sock, reinterpret_cast<const sockaddr*>(&saddr), sizeof(saddr.sun_path));
assert(r >= 0);
r = listen(sock, 5);
@@ -133,7 +236,7 @@ private:
{
std::thread t([&]()
{
auto fd = accept4(sock, NULL, NULL, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC);
auto fd = accept4(sock, nullptr, nullptr, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC);
m_adaptorConnection = sdbus::createServerBus(fd);
// This is necessary so that createDirectBusConnection() below does not block
m_adaptorConnection->enterEventLoopAsync();
@@ -162,14 +265,14 @@ public:
std::unique_ptr<TestProxy> m_proxy;
};
template <typename _Fnc>
inline bool waitUntil(_Fnc&& fnc, std::chrono::milliseconds timeout = std::chrono::seconds(5))
template <typename Fnc>
inline bool waitUntil(const Fnc& fnc, std::chrono::milliseconds timeout = std::chrono::seconds(5))
{
using namespace std::chrono_literals;
std::chrono::milliseconds elapsed{};
std::chrono::milliseconds step{5ms};
do {
const std::chrono::milliseconds step{5ms};
do { // NOLINT(cppcoreguidelines-avoid-do-while)
std::this_thread::sleep_for(step);
elapsed += step;
if (elapsed > timeout)
@@ -184,6 +287,6 @@ inline bool waitUntil(std::atomic<bool>& flag, std::chrono::milliseconds timeout
return waitUntil([&flag]() -> bool { return flag; }, timeout);
}
}}
} // namespace sdbus::test
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTFIXTURE_H_ */
+114 -33
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.cpp
* @file TestProxy.cpp
*
* Created on: May 23, 2020
* Project: sdbus-c++
@@ -25,31 +25,43 @@
*/
#include "TestProxy.h"
#include <thread>
#include <sdbus-c++/sdbus-c++.h>
#include "Defs.h"
#include <string>
#include <memory>
#include <map>
#include <cstdint>
#include <optional>
#include <functional>
#include <future>
#include <chrono>
#include <atomic>
#include <utility>
#include <vector>
namespace sdbus { namespace test {
namespace sdbus::test {
TestProxy::TestProxy(std::string destination, std::string objectPath)
TestProxy::TestProxy(ServiceName destination, ObjectPath 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); });
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct){ this->onSignalWithoutRegistration(strct); });
getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional<sdbus::Error> err, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(std::move(err), wrongParameter); });
registerProxy();
}
TestProxy::TestProxy(std::string destination, std::string objectPath, dont_run_event_loop_thread_t)
TestProxy::TestProxy(ServiceName destination, ObjectPath 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, std::string destination, std::string objectPath)
TestProxy::TestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath 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); });
getProxy().uponSignal("signalWithoutRegistration").onInterface(sdbus::test::INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct){ this->onSignalWithoutRegistration(strct); });
getProxy().uponSignal("signalWithErrorAndTypeMismatch").onInterface(sdbus::test::INTERFACE_NAME).call([this](std::optional<sdbus::Error> err, int wrongParameter){ this->onSignalWithErrorAndTypeMismatch(std::move(err), wrongParameter); });
registerProxy();
}
@@ -61,8 +73,8 @@ TestProxy::~TestProxy()
void TestProxy::onSimpleSignal()
{
m_signalMsg = getProxy().getCurrentlyProcessedMessage();
m_signalMemberName = m_signalMsg->getMemberName();
m_signalMsg = std::make_unique<sdbus::Message>(getProxy().getCurrentlyProcessedMessage());
m_signalName = m_signalMsg->getMemberName();
m_gotSimpleSignal = true;
}
@@ -79,27 +91,34 @@ void TestProxy::onSignalWithVariant(const sdbus::Variant& aVariant)
m_gotSignalWithVariant = true;
}
void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
void TestProxy::onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct)
{
m_signatureFromSignal[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(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>(strct)] = static_cast<std::string>(std::get<0>(std::get<1>(strct)));
m_gotSignalWithSignature = true;
}
void TestProxy::onDoOperationReply(uint32_t returnValue, const sdbus::Error* error)
void TestProxy::onSignalWithErrorAndTypeMismatch(std::optional<sdbus::Error> err, [[maybe_unused]] int wrongParameter)
{
if (m_DoOperationClientSideAsyncReplyHandler)
m_DoOperationClientSideAsyncReplyHandler(returnValue, error);
m_errorFromSignal = std::move(err);
m_gotSignalWithTypeMismatch = true;
}
void TestProxy::onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties )
void TestProxy::onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error) const
{
if (m_DoOperationClientSideAsyncReplyHandler)
m_DoOperationClientSideAsyncReplyHandler(returnValue, std::move(error));
}
void TestProxy::onPropertiesChanged( const sdbus::InterfaceName& interfaceName
, const std::map<PropertyName, sdbus::Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties )
{
if (m_onPropertiesChangedHandler)
m_onPropertiesChangedHandler(interfaceName, changedProperties, invalidatedProperties);
}
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler)
void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, std::optional<sdbus::Error> err)> handler)
{
m_DoOperationClientSideAsyncReplyHandler = std::move(handler);
}
@@ -107,22 +126,43 @@ void TestProxy::installDoOperationClientSideAsyncReplyHandler(std::function<void
uint32_t TestProxy::doOperationWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
{
using namespace std::chrono_literals;
uint32_t result;
uint32_t result = 0;
getProxy().callMethod("doOperation").onInterface(sdbus::test::INTERFACE_NAME).withTimeout(timeout).withArguments(param).storeResultsTo(result);
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")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
this->onDoOperationReply(returnValue, std::move(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")
@@ -133,19 +173,27 @@ 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, "doOperation");
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, sdbus::MethodName{"doOperation"});
methodCall << param;
return getProxy().callMethod(methodCall, sdbus::with_future);
return getProxy().callMethodAsync(methodCall, sdbus::with_future);
}
std::future<std::map<int32_t, std::string>> TestProxy::doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, with_future_t)
{
return getProxy().callMethodAsync("doOperationWithLargeData")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(largeParam)
.getResultAsFuture<std::map<int32_t, std::string>>();
}
void TestProxy::doErroneousOperationClientSideAsync()
{
getProxy().callMethodAsync("throwError")
.onInterface(sdbus::test::INTERFACE_NAME)
.uponReplyInvoke([this](const sdbus::Error* error)
.uponReplyInvoke([this](std::optional<sdbus::Error> error)
{
this->onDoOperationReply(0, error);
this->onDoOperationReply(0, std::move(error));
});
}
@@ -156,6 +204,38 @@ std::future<void> TestProxy::doErroneousOperationClientSideAsync(with_future_t)
.getResultAsFuture<>();
}
sdbus::Awaitable<uint32_t> TestProxy::doOperationClientSideAsync(uint32_t param, sdbus::with_awaitable_t)
{
return getProxy().callMethodAsync("doOperation")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(param)
.getResultAsAwaitable<uint32_t>();
}
sdbus::Awaitable<std::map<int32_t, std::string>> TestProxy::doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, sdbus::with_awaitable_t)
{
return getProxy().callMethodAsync("doOperationWithLargeData")
.onInterface(sdbus::test::INTERFACE_NAME)
.withArguments(largeParam)
.getResultAsAwaitable<std::map<int32_t, std::string>>();
}
sdbus::Awaitable<MethodReply> TestProxy::doOperationClientSideAsyncOnBasicAPILevel(uint32_t param, sdbus::with_awaitable_t)
{
auto methodCall = getProxy().createMethodCall(sdbus::test::INTERFACE_NAME, sdbus::MethodName{"doOperation"});
methodCall << param;
return getProxy().callMethodAsync(methodCall, sdbus::with_awaitable);
}
sdbus::Awaitable<void> TestProxy::doErroneousOperationClientSideAsync(sdbus::with_awaitable_t)
{
return getProxy().callMethodAsync("throwError")
.onInterface(sdbus::test::INTERFACE_NAME)
.getResultAsAwaitable<>();
}
void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param)
{
using namespace std::chrono_literals;
@@ -163,23 +243,24 @@ void TestProxy::doOperationClientSideAsyncWithTimeout(const std::chrono::microse
.onInterface(sdbus::test::INTERFACE_NAME)
.withTimeout(timeout)
.withArguments(param)
.uponReplyInvoke([this](const sdbus::Error* error, uint32_t returnValue)
.uponReplyInvoke([this](std::optional<sdbus::Error> error, uint32_t returnValue)
{
this->onDoOperationReply(returnValue, error);
this->onDoOperationReply(returnValue, std::move(error));
});
}
int32_t TestProxy::callNonexistentMethod()
{
int32_t result;
int32_t result = 0;
getProxy().callMethod("callNonexistentMethod").onInterface(sdbus::test::INTERFACE_NAME).storeResultsTo(result);
return result;
}
int32_t TestProxy::callMethodOnNonexistentInterface()
{
int32_t result;
getProxy().callMethod("someMethod").onInterface("sdbuscpp.interface.that.does.not.exist").storeResultsTo(result);
sdbus::InterfaceName const nonexistentInterfaceName{"sdbuscpp.interface.that.does.not.exist"};
int32_t result = 0;
getProxy().callMethod("someMethod").onInterface(nonexistentInterfaceName).storeResultsTo(result);
return result;
}
@@ -188,4 +269,4 @@ void TestProxy::setStateProperty(const std::string& value)
getProxy().setProperty("state").onInterface(sdbus::test::INTERFACE_NAME).toValue(value);
}
}}
} // namespace sdbus::test
+50 -30
View File
@@ -1,8 +1,8 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TestAdaptor.h
* @file TestProxy.h
*
* Created on: Jan 2, 2017
* Project: sdbus-c++
@@ -33,38 +33,44 @@
#include <chrono>
#include <atomic>
#include <future>
#include <memory>
namespace sdbus { namespace test {
namespace sdbus::test {
class ObjectManagerTestProxy final : public sdbus::ProxyInterfaces< sdbus::ObjectManager_proxy >
{
public:
ObjectManagerTestProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
ObjectManagerTestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
ObjectManagerTestProxy(const ObjectManagerTestProxy&) = delete;
ObjectManagerTestProxy& operator=(const ObjectManagerTestProxy&) = delete;
ObjectManagerTestProxy(ObjectManagerTestProxy&&) = delete;
ObjectManagerTestProxy& operator=(ObjectManagerTestProxy&&) = delete;
~ObjectManagerTestProxy()
{
unregisterProxy();
}
protected:
void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map<std::string, std::map<std::string, sdbus::Variant>>& interfacesAndProperties) override
void onInterfacesAdded(const sdbus::ObjectPath& objectPath, const std::map<sdbus::InterfaceName, std::map<PropertyName, sdbus::Variant>>& interfacesAndProperties) override
{
if (m_onInterfacesAddedHandler)
m_onInterfacesAddedHandler(objectPath, interfacesAndProperties);
}
void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector<std::string>& interfaces) override
void onInterfacesRemoved(const sdbus::ObjectPath& objectPath, const std::vector<sdbus::InterfaceName>& interfaces) override
{
if (m_onInterfacesRemovedHandler)
m_onInterfacesRemovedHandler(objectPath, interfaces);
}
public: // for tests
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;
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;
};
class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
@@ -73,9 +79,13 @@ class TestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integratio
, sdbus::Properties_proxy >
{
public:
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(ServiceName destination, ObjectPath objectPath);
TestProxy(ServiceName destination, ObjectPath objectPath, dont_run_event_loop_thread_t);
TestProxy(sdbus::IConnection& connection, ServiceName destination, ObjectPath objectPath);
TestProxy(const TestProxy&) = delete;
TestProxy& operator=(const TestProxy&) = delete;
TestProxy(TestProxy&&) = delete;
TestProxy& operator=(TestProxy&&) = delete;
~TestProxy();
protected:
@@ -83,43 +93,53 @@ protected:
void onSignalWithMap(const std::map<int32_t, std::string>& aMap) override;
void onSignalWithVariant(const sdbus::Variant& aVariant) override;
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s);
void onDoOperationReply(uint32_t returnValue, const sdbus::Error* error);
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& strct);
void onSignalWithErrorAndTypeMismatch(std::optional<sdbus::Error> err, int wrongParameter);
void onDoOperationReply(uint32_t returnValue, std::optional<sdbus::Error> error) const;
// Signals of standard D-Bus interfaces
void onPropertiesChanged( const std::string& interfaceName
, const std::map<std::string, sdbus::Variant>& changedProperties
, const std::vector<std::string>& invalidatedProperties ) override;
void onPropertiesChanged( const sdbus::InterfaceName& interfaceName
, const std::map<PropertyName, sdbus::Variant>& changedProperties
, const std::vector<PropertyName>& invalidatedProperties ) override;
public:
void installDoOperationClientSideAsyncReplyHandler(std::function<void(uint32_t res, const sdbus::Error* err)> handler);
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);
std::future<std::map<int32_t, std::string>> doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, with_future_t);
std::future<MethodReply> doOperationClientSideAsyncOnBasicAPILevel(uint32_t param);
std::future<void> doErroneousOperationClientSideAsync(with_future_t);
sdbus::Awaitable<uint32_t> doOperationClientSideAsync(uint32_t param, sdbus::with_awaitable_t);
sdbus::Awaitable<std::map<int32_t, std::string>> doOperationWithLargeDataClientSideAsync(const std::map<int32_t, std::string>& largeParam, sdbus::with_awaitable_t);
sdbus::Awaitable<MethodReply> doOperationClientSideAsyncOnBasicAPILevel(uint32_t param, sdbus::with_awaitable_t);
sdbus::Awaitable<void> doErroneousOperationClientSideAsync(sdbus::with_awaitable_t);
void doErroneousOperationClientSideAsync();
void doOperationClientSideAsyncWithTimeout(const std::chrono::microseconds &timeout, uint32_t param);
int32_t callNonexistentMethod();
int32_t callMethodOnNonexistentInterface();
void setStateProperty(const std::string& value);
//private:
public: // for tests
//private: (Kept public for tests)
int m_SimpleSignals = 0;
std::atomic<bool> m_gotSimpleSignal{false};
std::atomic<bool> m_gotSignalWithMap{false};
std::map<int32_t, std::string> m_mapFromSignal;
std::atomic<bool> m_gotSignalWithVariant{false};
double m_variantFromSignal;
double m_variantFromSignal{};
std::atomic<bool> m_gotSignalWithSignature{false};
std::map<std::string, std::string> m_signatureFromSignal;
std::map<std::string, Signature> m_signatureFromSignal;
std::atomic<bool> m_gotSignalWithTypeMismatch{false};
std::optional<sdbus::Error> m_errorFromSignal;
std::function<void(uint32_t res, const sdbus::Error* err)> m_DoOperationClientSideAsyncReplyHandler;
std::function<void(const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>&)> m_onPropertiesChangedHandler;
std::function<void(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;
const Message* m_signalMsg{};
std::string m_signalMemberName;
std::unique_ptr<const MethodCall> m_methodCallMsg;
std::unique_ptr<const Message> m_signalMsg;
SignalName m_signalName;
};
class DummyTestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integrationtests_proxy
@@ -128,8 +148,8 @@ class DummyTestProxy final : public sdbus::ProxyInterfaces< org::sdbuscpp::integ
, sdbus::Properties_proxy >
{
public:
DummyTestProxy(std::string destination, std::string objectPath)
: ProxyInterfaces(destination, objectPath)
DummyTestProxy(ServiceName destination, ObjectPath objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
}
@@ -139,12 +159,12 @@ protected:
void onSignalWithVariant(const sdbus::Variant&) override {}
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>&) {}
void onDoOperationReply(uint32_t, const sdbus::Error*) {}
void onDoOperationReply(uint32_t, const std::optional<sdbus::Error>&) {}
// Signals of standard D-Bus interfaces
void onPropertiesChanged( const std::string&, const std::map<std::string, sdbus::Variant>&, const std::vector<std::string>& ) override {}
void onPropertiesChanged(const InterfaceName&, const std::map<PropertyName, sdbus::Variant>&, const std::vector<PropertyName>&) override {}
};
}}
} // namespace sdbus::test
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTPROXY_H_ */
@@ -0,0 +1,130 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
#define __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class integrationtests_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.integrationtests";
protected:
integrationtests_adaptor(sdbus::IObject& object)
: m_object(object)
{
}
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() = 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("doOperationWithLargeData").withInputParamNames("largeMap").withOutputParamNames("largeMap").implementedAs([this](const std::map<int32_t, std::string>& largeMap){ return this->doOperationWithLargeData(largeMap); })
, 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("doOperationAsyncWithLargeData").withInputParamNames("arg0", "largeMap").withOutputParamNames("largeMap").implementedAs([this](sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t arg0, const std::map<int32_t, std::string>& largeMap){ this->doOperationAsyncWithLargeData(std::move(result), std::move(arg0), largeMap); })
, 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::registerMethod("sendLargeMessage").implementedAs([this](const std::map<int, std::string>& collection){ this->sendLargeMessage(collection); })
, sdbus::registerMethod("returnDictionary").implementedAs([this](const std::map<std::string, sdbus::Variant>& dictionary){ return this->returnDictionary(dictionary); })
, 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("actionVariant").withGetter([this](){ return this->actionVariant(); }).withSetter([this](const sdbus::Variant& value){ this->actionVariant(value); }).withUpdateBehavior(sdbus::Flags::EMITS_NO_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);
}
void emitSignalWithMap(const std::map<int32_t, std::string>& aMap)
{
m_object.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
}
void emitSignalWithVariant(const sdbus::Variant& aVariant)
{
m_object.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
}
private:
virtual void noArgNoReturn() = 0;
virtual int32_t getInt() = 0;
virtual std::tuple<uint32_t, std::string> getTuple() = 0;
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 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;
virtual uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1) = 0;
virtual uint32_t doOperation(const uint32_t& arg0) = 0;
virtual std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam) = 0;
virtual void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0;
virtual void doOperationAsyncWithLargeData(sdbus::Result<std::map<int32_t, std::string>>&& result, uint32_t arg0, const std::map<int32_t, std::string>& largeParam) = 0;
virtual sdbus::Signature getSignature() = 0;
virtual sdbus::ObjectPath getObjPath() = 0;
virtual sdbus::UnixFd getUnixFd() = 0;
virtual std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
virtual void throwError() = 0;
virtual void throwErrorWithNoReply() = 0;
virtual void doPrivilegedStuff() = 0;
virtual void emitTwoSimpleSignals() = 0;
virtual void sendLargeMessage(const std::map<int, std::string>& collection) = 0;
virtual std::map<std::string, sdbus::Variant> returnDictionary(const std::map<std::string, sdbus::Variant>& dict) = 0;
private:
virtual uint32_t action() = 0;
virtual sdbus::Variant actionVariant() = 0;
virtual void action(const uint32_t& value) = 0;
virtual void actionVariant(const sdbus::Variant& value) = 0;
virtual bool blocking() = 0;
virtual void blocking(const bool& value) = 0;
virtual std::string state() = 0;
private:
sdbus::IObject& m_object;
};
}} // namespaces
#endif
@@ -20,20 +20,24 @@ public:
protected:
integrationtests_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
: m_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&&) = default;
integrationtests_proxy& operator=(integrationtests_proxy&&) = default;
integrationtests_proxy(integrationtests_proxy&&) = delete;
integrationtests_proxy& operator=(integrationtests_proxy&&) = delete;
~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;
@@ -41,178 +45,214 @@ protected:
public:
void noArgNoReturn()
{
proxy_->callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
m_proxy.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
}
int32_t getInt()
{
int32_t result;
proxy_->callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::tuple<uint32_t, std::string> getTuple()
{
std::tuple<uint32_t, std::string> result;
proxy_->callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
double multiply(const int64_t& a, const double& b)
{
double result;
proxy_->callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
m_proxy.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
return result;
}
void multiplyWithNoReply(const int64_t& a, const double& b)
{
proxy_->callMethod("multiplyWithNoReply").onInterface(INTERFACE_NAME).withArguments(a, b).dontExpectReply();
m_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;
proxy_->callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
m_proxy.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
sdbus::Variant processVariant(const sdbus::Variant& variant)
{
sdbus::Variant result;
proxy_->callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(variant).storeResultsTo(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);
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;
proxy_->callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
m_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;
proxy_->callMethod("getStructInStruct").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_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;
proxy_->callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
m_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;
proxy_->callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
m_proxy.callMethod("sumArrayItems").onInterface(INTERFACE_NAME).withArguments(arg0, arg1).storeResultsTo(result);
return result;
}
uint32_t doOperation(const uint32_t& arg0)
{
uint32_t result;
proxy_->callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
m_proxy.callMethod("doOperation").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
std::map<int32_t, std::string> doOperationWithLargeData(const std::map<int32_t, std::string>& largeParam)
{
std::map<int32_t, std::string> result;
m_proxy.callMethod("doOperationWithLargeData").onInterface(INTERFACE_NAME).withArguments(largeParam).storeResultsTo(result);
return result;
}
uint32_t doOperationAsync(const uint32_t& arg0)
{
uint32_t result;
proxy_->callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
m_proxy.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(arg0).storeResultsTo(result);
return result;
}
std::map<int32_t, std::string> doOperationAsyncWithLargeData(const uint32_t& arg0, const std::map<int32_t, std::string>& largeParam)
{
std::map<int32_t, std::string> result;
m_proxy.callMethod("doOperationAsyncWithLargeData").onInterface(INTERFACE_NAME).withArguments(arg0, largeParam).storeResultsTo(result);
return result;
}
sdbus::Signature getSignature()
{
sdbus::Signature result;
proxy_->callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::ObjectPath getObjPath()
{
sdbus::ObjectPath result;
proxy_->callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("getObjPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::UnixFd getUnixFd()
{
sdbus::UnixFd result;
proxy_->callMethod("getUnixFd").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_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;
proxy_->callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void throwError()
{
proxy_->callMethod("throwError").onInterface(INTERFACE_NAME);
m_proxy.callMethod("throwError").onInterface(INTERFACE_NAME);
}
void throwErrorWithNoReply()
{
proxy_->callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
m_proxy.callMethod("throwErrorWithNoReply").onInterface(INTERFACE_NAME).dontExpectReply();
}
void doPrivilegedStuff()
{
proxy_->callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
m_proxy.callMethod("doPrivilegedStuff").onInterface(INTERFACE_NAME);
}
void emitTwoSimpleSignals()
{
proxy_->callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
m_proxy.callMethod("emitTwoSimpleSignals").onInterface(INTERFACE_NAME);
}
void sendLargeMessage(const std::map<int, std::string>& collection)
{
m_proxy.callMethod("sendLargeMessage").onInterface(INTERFACE_NAME).withArguments(collection);
}
void unregisterSimpleSignalHandler()
{
proxy_->muteSignal("simpleSignal").onInterface(INTERFACE_NAME);
simpleSignalSlot_.reset();
}
void reRegisterSimpleSignalHandler()
{
proxy_->uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
proxy_->finishRegistration();
simpleSignalSlot_ = m_proxy.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); }, sdbus::return_slot);
}
public:
uint32_t action()
{
return proxy_->getProperty("action").onInterface(INTERFACE_NAME);
return m_proxy.getProperty("action").onInterface(INTERFACE_NAME).get<uint32_t>();
}
void action(const uint32_t& value)
{
proxy_->setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
m_proxy.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
}
sdbus::Variant actionVariant()
{
return m_proxy.getProperty("actionVariant").onInterface(INTERFACE_NAME).get<sdbus::Variant>();
}
void actionVariant(const sdbus::Variant& value)
{
m_proxy.setProperty("actionVariant").onInterface(INTERFACE_NAME).toValue({value, sdbus::embed_variant});
}
bool blocking()
{
return proxy_->getProperty("blocking").onInterface(INTERFACE_NAME);
return m_proxy.getProperty("blocking").onInterface(INTERFACE_NAME).get<bool>();
}
void blocking(const bool& value)
{
proxy_->setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
m_proxy.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
}
std::string state()
{
return proxy_->getProperty("state").onInterface(INTERFACE_NAME);
return m_proxy.getProperty("state").onInterface(INTERFACE_NAME).get<std::string>();
}
private:
sdbus::IProxy* proxy_;
sdbus::IProxy& m_proxy;
sdbus::Slot simpleSignalSlot_;
};
}} // namespaces
@@ -1,114 +0,0 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
#define __sdbuscpp__integrationtests_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
class integrationtests_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.sdbuscpp.integrationtests";
protected:
integrationtests_adaptor(sdbus::IObject& 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&&) = default;
integrationtests_adaptor& operator=(integrationtests_adaptor&&) = default;
~integrationtests_adaptor() = default;
public:
void emitSimpleSignal()
{
object_->emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
}
void emitSignalWithMap(const std::map<int32_t, std::string>& aMap)
{
object_->emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(aMap);
}
void emitSignalWithVariant(const sdbus::Variant& aVariant)
{
object_->emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(aVariant);
}
private:
virtual void noArgNoReturn() = 0;
virtual int32_t getInt() = 0;
virtual std::tuple<uint32_t, std::string> getTuple() = 0;
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 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;
virtual uint32_t sumArrayItems(const std::vector<uint16_t>& arg0, const std::array<uint64_t, 3>& arg1) = 0;
virtual uint32_t doOperation(const uint32_t& arg0) = 0;
virtual void doOperationAsync(sdbus::Result<uint32_t>&& result, uint32_t arg0) = 0;
virtual sdbus::Signature getSignature() = 0;
virtual sdbus::ObjectPath getObjPath() = 0;
virtual sdbus::UnixFd getUnixFd() = 0;
virtual std::unordered_map<uint64_t, sdbus::Struct<std::map<uint8_t, std::vector<sdbus::Struct<sdbus::ObjectPath, bool, sdbus::Variant, std::map<int32_t, std::string>>>>, sdbus::Signature, std::string>> getComplex() = 0;
virtual void throwError() = 0;
virtual void throwErrorWithNoReply() = 0;
virtual void doPrivilegedStuff() = 0;
virtual void emitTwoSimpleSignals() = 0;
private:
virtual uint32_t action() = 0;
virtual void action(const uint32_t& value) = 0;
virtual bool blocking() = 0;
virtual void blocking(const bool& value) = 0;
virtual std::string state() = 0;
private:
sdbus::IObject* object_;
};
}} // namespaces
#endif
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file sdbus-c++-integration-tests.cpp
*
+42 -29
View File
@@ -1,5 +1,6 @@
/**
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file client.cpp
*
@@ -24,37 +25,44 @@
*/
#include "perftests-proxy.h"
#include <cstdint>
#include <cstddef>
#include <cstdlib>
#include <ctime>
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include <utility>
#include <string>
#include <iostream>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <cassert>
#include <algorithm>
#include <iostream>
using namespace std::chrono_literals;
uint64_t totalDuration = 0;
uint64_t totalDuration = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
class PerftestProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::perftests_proxy>
{
public:
PerftestProxy(std::string destination, std::string objectPath)
PerftestProxy(sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
: ProxyInterfaces(std::move(destination), std::move(objectPath))
{
registerProxy();
}
PerftestProxy(const PerftestProxy&) = delete;
PerftestProxy& operator=(const PerftestProxy&) = delete;
PerftestProxy(PerftestProxy&&) = delete;
PerftestProxy& operator=(PerftestProxy&&) = delete;
~PerftestProxy()
{
unregisterProxy();
}
protected:
virtual void onDataSignal([[maybe_unused]] const std::string& data) override
void onDataSignal([[maybe_unused]] const std::string& data) override
{
static unsigned int counter = 0;
static std::chrono::time_point<std::chrono::steady_clock> startTime;
@@ -70,7 +78,7 @@ protected:
auto stopTime = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
totalDuration += duration;
std::cout << "Received " << m_msgCount << " signals in: " << duration << " ms" << std::endl;
std::cout << "Received " << m_msgCount << " signals in: " << duration << " ms" << '\n';
counter = 0;
}
}
@@ -89,8 +97,13 @@ std::string createRandomString(size_t length)
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const size_t max_index = (sizeof(charset) - 1);
return charset[ rand() % max_index ];
return charset[ random() % max_index ];
};
struct timespec ts{};
(void)timespec_get(&ts, TIME_UTC);
srandom(ts.tv_nsec ^ ts.tv_sec); /* Seed the PRNG */
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
@@ -100,49 +113,49 @@ std::string createRandomString(size_t length)
//-----------------------------------------
int main(int /*argc*/, char */*argv*/[])
{
const char* destinationName = "org.sdbuscpp.perftests";
const char* objectPath = "/org/sdbuscpp/perftests";
PerftestProxy client(destinationName, objectPath);
sdbus::ServiceName destination{"org.sdbuscpp.perftests"};
sdbus::ObjectPath objectPath{"/org/sdbuscpp/perftests"};
PerftestProxy client(std::move(destination), std::move(objectPath));
const unsigned int repetitions{20};
unsigned int msgCount = 1000;
unsigned int const msgCount = 1000;
unsigned int msgSize{};
msgSize = 20;
std::cout << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
std::cout << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
client.m_msgCount = msgCount; client.m_msgSize = msgSize;
for (unsigned int r = 0; r < repetitions; ++r)
for (unsigned int i = 0; i < repetitions; ++i)
{
client.sendDataSignals(msgCount, msgSize);
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
totalDuration = 0;
msgSize = 1000;
std::cout << std::endl << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
std::cout << '\n' << "** Measuring signals of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
client.m_msgCount = msgCount; client.m_msgSize = msgSize;
for (unsigned int r = 0; r < repetitions; ++r)
for (unsigned int i = 0; i < repetitions; ++i)
{
client.sendDataSignals(msgCount, msgSize);
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
totalDuration = 0;
msgSize = 20;
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
for (unsigned int r = 0; r < repetitions; ++r)
std::cout << '\n' << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
for (unsigned int i = 0; i < repetitions; ++i)
{
auto str1 = createRandomString(msgSize/2);
auto str2 = createRandomString(msgSize/2);
auto startTime = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < msgCount; i++)
for (unsigned int j = 0; j < msgCount; j++)
{
auto result = client.concatenateTwoStrings(str1, str2);
@@ -152,23 +165,23 @@ int main(int /*argc*/, char */*argv*/[])
auto stopTime = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
totalDuration += duration;
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << std::endl;
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << '\n';
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
totalDuration = 0;
msgSize = 1000;
std::cout << std::endl << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << std::endl << std::endl;
for (unsigned int r = 0; r < repetitions; ++r)
std::cout << '\n' << "** Measuring method calls of size " << msgSize << " bytes (" << repetitions << " repetitions)..." << '\n' << '\n';
for (unsigned int i = 0; i < repetitions; ++i)
{
auto str1 = createRandomString(msgSize/2);
auto str2 = createRandomString(msgSize/2);
auto startTime = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < msgCount; i++)
for (unsigned int j = 0; j < msgCount; j++)
{
auto result = client.concatenateTwoStrings(str1, str2);
@@ -178,12 +191,12 @@ int main(int /*argc*/, char */*argv*/[])
auto stopTime = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime).count();
totalDuration += duration;
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << std::endl;
std::cout << "Called " << msgCount << " methods in: " << duration << " ms" << '\n';
std::this_thread::sleep_for(1000ms);
}
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << std::endl;
std::cout << "AVERAGE: " << (totalDuration/repetitions) << " ms" << '\n';
totalDuration = 0;
return 0;
@@ -20,24 +20,29 @@ public:
protected:
perftests_adaptor(sdbus::IObject& object)
: object_(&object)
: m_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&&) = default;
perftests_adaptor& operator=(perftests_adaptor&&) = default;
perftests_adaptor(perftests_adaptor&&) = delete;
perftests_adaptor& operator=(perftests_adaptor&&) = delete;
~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)
{
object_->emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
m_object.emitSignal("dataSignal").onInterface(INTERFACE_NAME).withArguments(data);
}
private:
@@ -45,7 +50,7 @@ private:
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) = 0;
private:
sdbus::IObject* object_;
sdbus::IObject& m_object;
};
}} // namespaces
@@ -20,35 +20,39 @@ public:
protected:
perftests_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
: m_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&&) = default;
perftests_proxy& operator=(perftests_proxy&&) = default;
perftests_proxy(perftests_proxy&&) = delete;
perftests_proxy& operator=(perftests_proxy&&) = delete;
~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)
{
proxy_->callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
m_proxy.callMethod("sendDataSignals").onInterface(INTERFACE_NAME).withArguments(numberOfSignals, signalMsgSize);
}
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2)
{
std::string result;
proxy_->callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
m_proxy.callMethod("concatenateTwoStrings").onInterface(INTERFACE_NAME).withArguments(string1, string2).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy* proxy_;
sdbus::IProxy& m_proxy;
};
}} // namespaces
+25 -11
View File
@@ -1,5 +1,6 @@
/**
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file server.cpp
*
@@ -24,10 +25,13 @@
*/
#include "perftests-adaptor.h"
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <ctime>
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include <utility>
#include <string>
#include <thread>
#include <chrono>
#include <algorithm>
#include <iostream>
@@ -39,19 +43,24 @@ std::string createRandomString(size_t length);
class PerftestAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::perftests_adaptor>
{
public:
PerftestAdaptor(sdbus::IConnection& connection, std::string objectPath)
PerftestAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
}
PerftestAdaptor(const PerftestAdaptor&) = delete;
PerftestAdaptor& operator=(const PerftestAdaptor&) = delete;
PerftestAdaptor(PerftestAdaptor&&) = delete;
PerftestAdaptor& operator=(PerftestAdaptor&&) = delete;
~PerftestAdaptor()
{
unregisterAdaptor();
}
protected:
virtual void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) override
void sendDataSignals(const uint32_t& numberOfSignals, const uint32_t& signalMsgSize) override // NOLINT(bugprone-easily-swappable-parameters)
{
auto data = createRandomString(signalMsgSize);
@@ -62,10 +71,10 @@ protected:
emitDataSignal(data);
}
auto stop_time = std::chrono::steady_clock::now();
std::cout << "Server sent " << numberOfSignals << " signals in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count() << " ms" << std::endl;
std::cout << "Server sent " << numberOfSignals << " signals in: " << std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count() << " ms" << '\n';
}
virtual std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) override
std::string concatenateTwoStrings(const std::string& string1, const std::string& string2) override
{
return string1 + string2;
}
@@ -80,8 +89,13 @@ std::string createRandomString(size_t length)
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const size_t max_index = (sizeof(charset) - 1);
return charset[ rand() % max_index ];
return charset[ random() % max_index ];
};
struct timespec ts{};
(void)timespec_get(&ts, TIME_UTC);
srandom(ts.tv_nsec ^ ts.tv_sec); /* Seed the PRNG */
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
@@ -91,11 +105,11 @@ std::string createRandomString(size_t length)
//-----------------------------------------
int main(int /*argc*/, char */*argv*/[])
{
const char* serviceName = "org.sdbuscpp.perftests";
sdbus::ServiceName const serviceName{"org.sdbuscpp.perftests"};
auto connection = sdbus::createSystemBusConnection(serviceName);
const char* objectPath = "/org/sdbuscpp/perftests";
PerftestAdaptor server(*connection, objectPath);
sdbus::ObjectPath objectPath{"/org/sdbuscpp/perftests"};
PerftestAdaptor server(*connection, std::move(objectPath)); // NOLINT(misc-const-correctness)
connection->enterEventLoop();
}
@@ -22,23 +22,27 @@ public:
protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(&object)
: m_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&&) = default;
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
thermometer_adaptor(thermometer_adaptor&&) = delete;
thermometer_adaptor& operator=(thermometer_adaptor&&) = delete;
~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* object_;
sdbus::IObject& m_object;
};
}}}} // namespaces
@@ -22,27 +22,31 @@ public:
protected:
thermometer_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
: m_proxy(proxy)
{
}
thermometer_proxy(const thermometer_proxy&) = delete;
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
thermometer_proxy(thermometer_proxy&&) = default;
thermometer_proxy& operator=(thermometer_proxy&&) = default;
thermometer_proxy(thermometer_proxy&&) = delete;
thermometer_proxy& operator=(thermometer_proxy&&) = delete;
~thermometer_proxy() = default;
void registerProxy()
{
}
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy* proxy_;
sdbus::IProxy& m_proxy;
};
}}}} // namespaces
@@ -21,30 +21,35 @@ public:
protected:
concatenator_adaptor(sdbus::IObject& object)
: object_(&object)
: m_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&&) = default;
concatenator_adaptor& operator=(concatenator_adaptor&&) = default;
concatenator_adaptor(concatenator_adaptor&&) = delete;
concatenator_adaptor& operator=(concatenator_adaptor&&) = delete;
~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)
{
object_->emitSignal("concatenatedSignal").onInterface(INTERFACE_NAME).withArguments(concatenatedString);
m_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* object_;
sdbus::IObject& m_object;
};
}}} // namespaces
@@ -21,30 +21,34 @@ public:
protected:
concatenator_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
: m_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&&) = default;
concatenator_proxy& operator=(concatenator_proxy&&) = default;
concatenator_proxy(concatenator_proxy&&) = delete;
concatenator_proxy& operator=(concatenator_proxy&&) = delete;
~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, const sdbus::Error* error) = 0;
virtual void onConcatenateReply(const std::string& result, std::optional<sdbus::Error> error) = 0;
public:
sdbus::PendingAsyncCall concatenate(const std::map<std::string, sdbus::Variant>& params)
{
return proxy_->callMethodAsync("concatenate").onInterface(INTERFACE_NAME).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
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)); });
}
private:
sdbus::IProxy* proxy_;
sdbus::IProxy& m_proxy;
};
}}} // namespaces
@@ -22,23 +22,27 @@ public:
protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(&object)
: m_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&&) = default;
thermometer_adaptor& operator=(thermometer_adaptor&&) = default;
thermometer_adaptor(thermometer_adaptor&&) = delete;
thermometer_adaptor& operator=(thermometer_adaptor&&) = delete;
~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* object_;
sdbus::IObject& m_object;
};
}}}} // namespaces
@@ -56,25 +60,30 @@ public:
protected:
factory_adaptor(sdbus::IObject& object)
: object_(&object)
: m_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&&) = default;
factory_adaptor& operator=(factory_adaptor&&) = default;
factory_adaptor(factory_adaptor&&) = delete;
factory_adaptor& operator=(factory_adaptor&&) = delete;
~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* object_;
sdbus::IObject& m_object;
};
}}}}} // namespaces
@@ -22,27 +22,31 @@ public:
protected:
thermometer_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
: m_proxy(proxy)
{
}
thermometer_proxy(const thermometer_proxy&) = delete;
thermometer_proxy& operator=(const thermometer_proxy&) = delete;
thermometer_proxy(thermometer_proxy&&) = default;
thermometer_proxy& operator=(thermometer_proxy&&) = default;
thermometer_proxy(thermometer_proxy&&) = delete;
thermometer_proxy& operator=(thermometer_proxy&&) = delete;
~thermometer_proxy() = default;
void registerProxy()
{
}
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
proxy_->callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy* proxy_;
sdbus::IProxy& m_proxy;
};
}}}} // namespaces
@@ -60,32 +64,36 @@ public:
protected:
factory_proxy(sdbus::IProxy& proxy)
: proxy_(&proxy)
: m_proxy(proxy)
{
}
factory_proxy(const factory_proxy&) = delete;
factory_proxy& operator=(const factory_proxy&) = delete;
factory_proxy(factory_proxy&&) = default;
factory_proxy& operator=(factory_proxy&&) = default;
factory_proxy(factory_proxy&&) = delete;
factory_proxy& operator=(factory_proxy&&) = delete;
~factory_proxy() = default;
void registerProxy()
{
}
public:
sdbus::ObjectPath createDelegateObject()
{
sdbus::ObjectPath result;
proxy_->callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
m_proxy.callMethod("createDelegateObject").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void destroyDelegateObject(const sdbus::ObjectPath& delegate)
{
proxy_->callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
m_proxy.callMethod("destroyDelegateObject").onInterface(INTERFACE_NAME).withArguments(delegate).dontExpectReply();
}
private:
sdbus::IProxy* proxy_;
sdbus::IProxy& m_proxy;
};
}}}}} // namespaces
+86 -47
View File
@@ -1,5 +1,6 @@
/**
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file sdbus-c++-stress-tests.cpp
*
@@ -33,42 +34,52 @@
#include <vector>
#include <string>
#include <iostream>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <cassert>
#include <cstdlib>
#include <cstdint>
#include <cstddef>
#include <atomic>
#include <sstream>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <memory>
#include <utility>
#include <map>
#include <optional>
#include <stdexcept>
using namespace std::chrono_literals;
using namespace std::string_literals;
#define SERVICE_1_BUS_NAME "org.sdbuscpp.stresstests.service1"s
#define SERVICE_2_BUS_NAME "org.sdbuscpp.stresstests.service2"s
#define CELSIUS_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/celsius/thermometer"s
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstests/fahrenheit/thermometer"s
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstests/concatenator"s
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"};
class CelsiusThermometerAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_adaptor>
{
public:
CelsiusThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
CelsiusThermometerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
registerAdaptor();
}
CelsiusThermometerAdaptor(const CelsiusThermometerAdaptor&) = delete;
CelsiusThermometerAdaptor& operator=(const CelsiusThermometerAdaptor&) = delete;
CelsiusThermometerAdaptor(CelsiusThermometerAdaptor&&) = delete;
CelsiusThermometerAdaptor& operator=(CelsiusThermometerAdaptor&&) = delete;
~CelsiusThermometerAdaptor()
{
unregisterAdaptor();
}
protected:
virtual uint32_t getCurrentTemperature() override
uint32_t getCurrentTemperature() override
{
return m_currentTemperature++;
}
@@ -80,12 +91,17 @@ private:
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::celsius::thermometer_proxy>
{
public:
CelsiusThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
CelsiusThermometerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
CelsiusThermometerProxy(const CelsiusThermometerProxy&) = delete;
CelsiusThermometerProxy& operator=(const CelsiusThermometerProxy&) = delete;
CelsiusThermometerProxy(CelsiusThermometerProxy&&) = delete;
CelsiusThermometerProxy& operator=(CelsiusThermometerProxy&&) = delete;
~CelsiusThermometerProxy()
{
unregisterProxy();
@@ -96,7 +112,7 @@ class FahrenheitThermometerAdaptor final : public sdbus::AdaptorInterfaces< org:
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_adaptor >
{
public:
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath, bool isDelegate)
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath, bool isDelegate)
: AdaptorInterfaces(connection, std::move(objectPath))
, celsiusProxy_(connection, SERVICE_2_BUS_NAME, CELSIUS_THERMOMETER_OBJECT_PATH)
{
@@ -127,7 +143,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);
@@ -142,7 +158,7 @@ public:
{
// Destroy existing delegate object
// Here we are testing dynamic removal of a D-Bus object in an async way
std::lock_guard<std::mutex> lock{childrenMutex_};
const std::lock_guard lock{childrenMutex_};
children_.erase(request.delegateObjectPath);
}
}
@@ -152,6 +168,11 @@ public:
registerAdaptor();
}
FahrenheitThermometerAdaptor(const FahrenheitThermometerAdaptor&) = delete;
FahrenheitThermometerAdaptor& operator=(const FahrenheitThermometerAdaptor&) = delete;
FahrenheitThermometerAdaptor(FahrenheitThermometerAdaptor&&) = delete;
FahrenheitThermometerAdaptor& operator=(FahrenheitThermometerAdaptor&&) = delete;
~FahrenheitThermometerAdaptor()
{
exit_ = true;
@@ -163,24 +184,24 @@ public:
}
protected:
virtual uint32_t getCurrentTemperature() override
uint32_t getCurrentTemperature() override
{
// In this D-Bus call, make yet another D-Bus call to another service over the same connection
return static_cast<uint32_t>(celsiusProxy_.getCurrentTemperature() * 1.8 + 32.);
}
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) override
void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) override
{
static size_t objectCounter{};
objectCounter++;
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{objectCounter, std::string{}, std::move(result)});
requests_.push(WorkItem{objectCounter, {}, std::move(result)});
lock.unlock();
cond_.notify_one();
}
virtual void destroyDelegateObject(sdbus::Result<>&& /*result*/, sdbus::ObjectPath delegate) override
void destroyDelegateObject(sdbus::Result<>&& /*result*/, sdbus::ObjectPath delegate) override
{
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{0, std::move(delegate), {}});
@@ -190,7 +211,7 @@ protected:
private:
CelsiusThermometerProxy celsiusProxy_;
std::map<std::string, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
std::map<sdbus::ObjectPath, std::unique_ptr<FahrenheitThermometerAdaptor>> children_;
std::mutex childrenMutex_;
struct WorkItem
@@ -203,19 +224,24 @@ private:
std::condition_variable cond_;
std::queue<WorkItem> requests_;
std::vector<std::thread> workers_;
std::atomic<bool> exit_{};
std::atomic<bool> exit_;
};
class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces< org::sdbuscpp::stresstests::fahrenheit::thermometer_proxy
, org::sdbuscpp::stresstests::fahrenheit::thermometer::factory_proxy >
{
public:
FahrenheitThermometerProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
FahrenheitThermometerProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
FahrenheitThermometerProxy(const FahrenheitThermometerProxy&) = delete;
FahrenheitThermometerProxy& operator=(const FahrenheitThermometerProxy&) = delete;
FahrenheitThermometerProxy(FahrenheitThermometerProxy&&) = delete;
FahrenheitThermometerProxy& operator=(FahrenheitThermometerProxy&&) = delete;
~FahrenheitThermometerProxy()
{
unregisterProxy();
@@ -225,7 +251,7 @@ public:
class ConcatenatorAdaptor final : public sdbus::AdaptorInterfaces<org::sdbuscpp::stresstests::concatenator_adaptor>
{
public:
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
ConcatenatorAdaptor(sdbus::IConnection& connection, sdbus::ObjectPath objectPath)
: AdaptorInterfaces(connection, std::move(objectPath))
{
unsigned int workers = std::thread::hardware_concurrency();
@@ -262,6 +288,11 @@ public:
registerAdaptor();
}
ConcatenatorAdaptor(const ConcatenatorAdaptor&) = delete;
ConcatenatorAdaptor& operator=(const ConcatenatorAdaptor&) = delete;
ConcatenatorAdaptor(ConcatenatorAdaptor&&) = delete;
ConcatenatorAdaptor& operator=(ConcatenatorAdaptor&&) = delete;
~ConcatenatorAdaptor()
{
exit_ = true;
@@ -273,7 +304,7 @@ public:
}
protected:
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) override
void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) override
{
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{std::move(params), std::move(result)});
@@ -291,48 +322,53 @@ private:
std::condition_variable cond_;
std::queue<WorkItem> requests_;
std::vector<std::thread> workers_;
std::atomic<bool> exit_{};
std::atomic<bool> exit_;
};
class ConcatenatorProxy final : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstests::concatenator_proxy>
{
public:
ConcatenatorProxy(sdbus::IConnection& connection, std::string destination, std::string objectPath)
ConcatenatorProxy(sdbus::IConnection& connection, sdbus::ServiceName destination, sdbus::ObjectPath objectPath)
: ProxyInterfaces(connection, std::move(destination), std::move(objectPath))
{
registerProxy();
}
ConcatenatorProxy(const ConcatenatorProxy&) = delete;
ConcatenatorProxy& operator=(const ConcatenatorProxy&) = delete;
ConcatenatorProxy(ConcatenatorProxy&&) = delete;
ConcatenatorProxy& operator=(ConcatenatorProxy&&) = delete;
~ConcatenatorProxy()
{
unregisterProxy();
}
private:
virtual void onConcatenateReply(const std::string& result, [[maybe_unused]] const sdbus::Error* error) override
void onConcatenateReply(const std::string& result, [[maybe_unused]] std::optional<sdbus::Error> error) override
{
assert(error == nullptr);
assert(error == std::nullopt);
std::stringstream str(result);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
uint32_t aNumber{};
str >> aNumber;
assert(aNumber > 0);
++repliesReceived_;
}
virtual void onConcatenatedSignal(const std::string& concatenatedString) override
void onConcatenatedSignal(const std::string& concatenatedString) override
{
std::stringstream str(concatenatedString);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
uint32_t aNumber{};
str >> aNumber;
assert(aNumber > 0);
@@ -340,15 +376,15 @@ private:
}
public:
std::atomic<uint32_t> repliesReceived_{};
std::atomic<uint32_t> signalsReceived_{};
std::atomic<uint32_t> repliesReceived_;
std::atomic<uint32_t> signalsReceived_;
};
//-----------------------------------------
int main(int argc, char *argv[])
int main(int argc, char *argv[]) // NOLINT(bugprone-exception-escape, readability-function-cognitive-complexity)
{
long loops;
long loopDuration;
long loops{};
long loopDuration{};
if (argc == 1)
{
@@ -357,49 +393,52 @@ int main(int argc, char *argv[])
}
else if (argc == 3)
{
loops = std::atol(argv[1]);
loopDuration = std::atol(argv[2]);
loops = std::atol(argv[1]); // NOLINT(cert-err34-c, cppcoreguidelines-pro-bounds-pointer-arithmetic)
loopDuration = std::atol(argv[2]); // NOLINT(cert-err34-c, cppcoreguidelines-pro-bounds-pointer-arithmetic)
}
else
throw std::runtime_error("Wrong program options");
std::cout << "Going on with " << loops << " loops and " << loopDuration << "ms loop duration" << std::endl;
std::cout << "Going on with " << loops << " loops and " << loopDuration << "ms loop duration\n";
std::atomic<uint32_t> concatenationCallsMade{0};
std::atomic<uint32_t> concatenationRepliesReceived{0};
std::atomic<uint32_t> concatenationSignalsReceived{0};
std::atomic<uint32_t> thermometerCallsMade{0};
std::atomic<bool> exitLogger{};
std::atomic exitLogger{false};
std::thread loggerThread([&]()
{
while (!exitLogger)
{
std::this_thread::sleep_for(1s);
std::cout << "Made " << concatenationCallsMade << " concatenation calls, received " << concatenationRepliesReceived << " replies and " << concatenationSignalsReceived << " signals so far." << std::endl;
std::cout << "Made " << thermometerCallsMade << " thermometer calls so far." << std::endl << std::endl;
std::cout << "Made " << concatenationCallsMade << " concatenation calls, received " << concatenationRepliesReceived << " replies and " << concatenationSignalsReceived << " signals so far.\n";
std::cout << "Made " << thermometerCallsMade << " thermometer calls so far.\n\n";
}
});
for (long loop = 0; loop < loops; ++loop)
{
std::cout << "Entering loop " << loop+1 << std::endl;
std::cout << "Entering loop " << loop+1 << '\n';
auto service2Connection = sdbus::createSystemBusConnection(SERVICE_2_BUS_NAME);
std::atomic<bool> service2ThreadReady{};
std::atomic service2ThreadReady{false};
std::thread service2Thread([&con = *service2Connection, &service2ThreadReady]()
{
// NOLINTNEXTLINE(misc-const-correctness)
CelsiusThermometerAdaptor thermometer(con, CELSIUS_THERMOMETER_OBJECT_PATH);
service2ThreadReady = true;
con.enterEventLoop();
});
auto service1Connection = sdbus::createSystemBusConnection(SERVICE_1_BUS_NAME);
std::atomic<bool> service1ThreadReady{};
std::atomic service1ThreadReady{false};
std::thread service1Thread([&con = *service1Connection, &service1ThreadReady]()
{
// NOLINTNEXTLINE(misc-const-correctness)
ConcatenatorAdaptor concatenator(con, CONCATENATOR_OBJECT_PATH);
// NOLINTNEXTLINE(misc-const-correctness)
FahrenheitThermometerAdaptor thermometer(con, FAHRENHEIT_THERMOMETER_OBJECT_PATH, false);
service1ThreadReady = true;
con.enterEventLoop();
@@ -415,7 +454,7 @@ int main(int argc, char *argv[])
bool clientThreadExit{};
std::thread clientThread([&, &con = *clientConnection]()
{
std::atomic<bool> stopClients{false};
std::atomic stopClients{false};
std::thread concatenatorThread([&]()
{
@@ -442,8 +481,8 @@ int main(int argc, char *argv[])
// Update statistics
concatenationCallsMade = localCounter;
concatenationRepliesReceived = (uint32_t)concatenator.repliesReceived_;
concatenationSignalsReceived = (uint32_t)concatenator.signalsReceived_;
concatenationRepliesReceived = static_cast<uint32_t>(concatenator.repliesReceived_);
concatenationSignalsReceived = static_cast<uint32_t>(concatenator.signalsReceived_);
}
}
});
+29 -16
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Connection_test.cpp
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -26,9 +26,16 @@
*/
#include "Connection.h"
#include "unittests/mocks/SdBusMock.h"
#include "sdbus-c++/Error.h"
#include "sdbus-c++/Types.h"
#include "mocks/SdBusMock.h"
#include <gtest/gtest.h>
#include <gtest/gtest.h> // IWYU pragma: export
#include <cerrno>
#include <memory>
#include <utility>
// NOLINTBEGIN(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
using ::testing::_;
using ::testing::DoAll;
@@ -179,10 +186,10 @@ template<> void AConnectionNameRequest<Connection::pseudo_bus_t>::setUpBusOpenEx
// `sd_bus_start` for pseudo connection shall return an error value, remember this is a fake connection...
EXPECT_CALL(*sdBusIntfMock_, sd_bus_start(fakeBusPtr_)).WillOnce(Return(-EINVAL));
}
template <typename _BusTypeTag>
std::unique_ptr<Connection> AConnectionNameRequest<_BusTypeTag>::makeConnection()
template <typename BusTypeTag>
std::unique_ptr<Connection> AConnectionNameRequest<BusTypeTag>::makeConnection()
{
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), _BusTypeTag{});
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), BusTypeTag{});
}
template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::custom_session_bus_t>::makeConnection()
{
@@ -193,26 +200,32 @@ template<> std::unique_ptr<Connection> AConnectionNameRequest<Connection::remote
return std::make_unique<Connection>(std::unique_ptr<NiceMock<SdBusMock>>(sdBusIntfMock_), Connection::remote_system_bus, "some host");
}
typedef ::testing::Types< Connection::default_bus_t
, Connection::system_bus_t
, Connection::session_bus_t
, Connection::custom_session_bus_t
, Connection::remote_system_bus_t
, Connection::pseudo_bus_t
> BusTypeTags;
using BusTypeTags = ::testing::Types< Connection::default_bus_t
, Connection::system_bus_t
, Connection::session_bus_t
, Connection::custom_session_bus_t
, Connection::remote_system_bus_t
, Connection::pseudo_bus_t
>;
TYPED_TEST_SUITE(AConnectionNameRequest, BusTypeTags);
}
} // namespace
TYPED_TEST(AConnectionNameRequest, DoesNotThrowOnSuccess)
{
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(1));
this->con_->requestName("org.sdbuscpp.somename");
const sdbus::ConnectionName name{"org.sdbuscpp.somename"};
this->con_->requestName(name);
}
TYPED_TEST(AConnectionNameRequest, ThrowsOnFail)
{
const sdbus::ConnectionName name{"org.sdbuscpp.somename"};
EXPECT_CALL(*this->sdBusIntfMock_, sd_bus_request_name(_, _, _)).WillOnce(Return(-1));
ASSERT_THROW(this->con_->requestName("org.sdbuscpp.somename"), sdbus::Error);
ASSERT_THROW(this->con_->requestName(name), sdbus::Error);
}
// NOLINTEND(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
+266 -61
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Message_test.cpp
*
@@ -24,16 +24,28 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sdbus-c++/Error.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Types.h>
#include "MessageUtils.h"
#include <sdbus-c++/TypeTraits.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <array>
#include <cstdint>
#include <list>
#include <map>
#include <span>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
using ::testing::Eq;
using ::testing::StrEq;
using ::testing::Gt;
using ::testing::DoubleEq;
using ::testing::IsNull;
using ::testing::SizeIs;
using ::testing::ElementsAre;
using namespace std::string_literals;
namespace
@@ -44,14 +56,15 @@ namespace
msg >> str;
return str;
}
}
} // namespace
namespace sdbus {
template <typename _ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<_ElementType>& items)
template <typename ElementType>
sdbus::Message& operator<<(sdbus::Message& msg, const std::list<ElementType>& items)
{
msg.openContainer(sdbus::signature_of<_ElementType>::str());
// TODO: This can also be simplified on the basis of a callback (see dictionary...)
msg.openContainer<ElementType>();
for (const auto& item : items)
msg << item;
@@ -61,15 +74,15 @@ namespace sdbus {
return msg;
}
template <typename _ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<_ElementType>& items)
template <typename ElementType>
sdbus::Message& operator>>(sdbus::Message& msg, std::list<ElementType>& items)
{
if(!msg.enterContainer(sdbus::signature_of<_ElementType>::str()))
if(!msg.enterContainer<ElementType>())
return msg;
while (true)
{
_ElementType elem;
ElementType elem;
if (msg >> elem)
items.emplace_back(std::move(elem));
else
@@ -83,44 +96,59 @@ namespace sdbus {
return msg;
}
}
} // namespace sdbus
template <typename _Element, typename _Allocator>
struct sdbus::signature_of<std::list<_Element, _Allocator>>
: sdbus::signature_of<std::vector<_Element, _Allocator>>
template <typename Element, typename Allocator>
struct sdbus::signature_of<std::list<Element, Allocator>>
: sdbus::signature_of<std::vector<Element, Allocator>>
{};
namespace my {
enum class Enum : std::uint8_t
{
Value1,
Value2,
Value3
};
struct Struct
{
int i;
int i{};
std::string s;
std::list<double> l;
Enum e{};
friend bool operator==(const Struct& lhs, const Struct& rhs) = default;
};
bool operator==(const Struct& lhs, const Struct& rhs)
struct RelaxedStruct
{
return lhs.i == rhs.i && lhs.s == rhs.s && lhs.l == rhs.l;
}
int i{};
std::string s;
std::list<double> l;
Enum e{};
sdbus::Message& operator<<(sdbus::Message& msg, const Struct& items)
friend bool operator==(const RelaxedStruct& lhs, const RelaxedStruct& rhs) = default;
};
struct NestedStruct
{
return msg << sdbus::Struct{std::forward_as_tuple(items.i, items.s, items.l)};
}
int i{};
std::string s;
Enum e{};
Struct x;
sdbus::Message& operator>>(sdbus::Message& msg, Struct& items)
{
sdbus::Struct s{std::forward_as_tuple(items.i, items.s, items.l)};
return msg >> s;
}
friend bool operator==(const NestedStruct& lhs, const NestedStruct& rhs) = default;
};
} // namespace my
}
SDBUSCPP_REGISTER_STRUCT(my::Struct, i, s, l, e); // NOLINT(readability-identifier-length)
template <>
struct sdbus::signature_of<my::Struct>
: sdbus::signature_of<sdbus::Struct<int, std::string, std::list<double>>>
{};
SDBUSCPP_ENABLE_RELAXED_DICT2STRUCT_DESERIALIZATION(my::RelaxedStruct);
SDBUSCPP_REGISTER_STRUCT(my::RelaxedStruct, i, s, l, e); // NOLINT(readability-identifier-length)
SDBUSCPP_ENABLE_NESTED_STRUCT2DICT_SERIALIZATION(my::NestedStruct);
SDBUSCPP_REGISTER_STRUCT(my::NestedStruct, i, s, e, x); // NOLINT(readability-identifier-length)
/*-------------------------------------*/
/* -- TEST CASES -- */
@@ -133,7 +161,7 @@ TEST(AMessage, CanBeDefaultConstructed)
TEST(AMessage, IsInvalidAfterDefaultConstructed)
{
sdbus::PlainMessage msg;
const sdbus::PlainMessage msg;
ASSERT_FALSE(msg.isValid());
}
@@ -198,7 +226,22 @@ TEST(AMessage, CanCarryASimpleInteger)
msg << dataWritten;
msg.seal();
int dataRead;
int dataRead{};
msg >> dataRead;
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));
@@ -223,7 +266,7 @@ TEST(AMessage, CanCarryAVariant)
{
auto msg = sdbus::createPlainMessage();
const auto dataWritten = sdbus::Variant((double)3.14);
const auto dataWritten = sdbus::Variant(3.14);
msg << dataWritten;
msg.seal();
@@ -238,7 +281,7 @@ TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
{
auto msg = sdbus::createPlainMessage();
std::vector<sdbus::Variant> value{sdbus::Variant{"hello"s}, sdbus::Variant{(double)3.14}};
std::vector<sdbus::Variant> value{sdbus::Variant{"hello"s}, sdbus::Variant{3.14}};
const auto dataWritten = sdbus::Variant{value};
msg << dataWritten;
@@ -270,7 +313,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdVector)
{
auto msg = sdbus::createPlainMessage();
const std::vector<sdbus::Signature> dataWritten{"s", "u", "b"};
const std::vector dataWritten{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
msg << dataWritten;
msg.seal();
@@ -285,12 +328,12 @@ TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdArray)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> dataWritten{3545342, 43643532, 324325};
const std::array dataWritten{3545342, 43643532, 324325};
msg << dataWritten;
msg.seal();
std::array<int, 3> dataRead;
std::array<int, 3> dataRead{};
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
@@ -300,7 +343,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
{
auto msg = sdbus::createPlainMessage();
const std::array<sdbus::Signature, 3> dataWritten{"s", "u", "b"};
const std::array dataWritten{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
msg << dataWritten;
msg.seal();
@@ -311,18 +354,18 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdArray)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
#if __cplusplus >= 202002L
#ifdef __cpp_lib_span
TEST(AMessage, CanCarryDBusArrayOfTrivialTypesGivenAsStdSpan)
{
auto msg = sdbus::createPlainMessage();
const std::array<int, 3> sourceArray{3545342, 43643532, 324325};
const std::array sourceArray{3545342, 43643532, 324325};
const std::span dataWritten{sourceArray};
msg << dataWritten;
msg.seal();
std::array<int, 3> destinationArray;
std::array<int, 3> destinationArray{};
std::span dataRead{destinationArray};
msg >> dataRead;
@@ -333,7 +376,7 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan)
{
auto msg = sdbus::createPlainMessage();
const std::array<sdbus::Signature, 3> sourceArray{"s", "u", "b"};
const std::array sourceArray{sdbus::Signature{"s"}, sdbus::Signature{"u"}, sdbus::Signature{"b"}};
const std::span dataWritten{sourceArray};
msg << dataWritten;
@@ -347,6 +390,24 @@ TEST(AMessage, CanCarryDBusArrayOfNontrivialTypesGivenAsStdSpan)
}
#endif
TEST(AMessage, CanCarryAnEnumValue)
{
auto msg = sdbus::createPlainMessage();
const enum class EnumA : int16_t {X = 5} aWritten{EnumA::X}; // NOLINT(performance-enum-size)
const enum EnumB {Y = 11} bWritten{EnumB::Y}; // NOLINT(performance-enum-size)
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();
@@ -356,11 +417,11 @@ TEST(AMessage, ThrowsWhenDestinationStdArrayIsTooSmallDuringDeserialization)
msg << dataWritten;
msg.seal();
std::array<int, 3> dataRead;
std::array<int, 3> dataRead{};
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
#if __cplusplus >= 202002L
#ifdef __cpp_lib_span
TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization)
{
auto msg = sdbus::createPlainMessage();
@@ -370,7 +431,7 @@ TEST(AMessage, ThrowsWhenDestinationStdSpanIsTooSmallDuringDeserialization)
msg << dataWritten;
msg.seal();
std::array<int, 2> destinationArray;
std::array<int, 2> destinationArray{};
std::span dataRead{destinationArray};
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
@@ -380,7 +441,7 @@ TEST(AMessage, CanCarryADictionary)
{
auto msg = sdbus::createPlainMessage();
std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
const std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
msg << dataWritten;
msg.seal();
@@ -415,7 +476,7 @@ TEST(AMessage, CanCarryAComplexType)
>
>;
ComplexType dataWritten = { {1, {{{5, {{"/some/object", true, 45, {{6, "hello"}, {7, "world"}}}}}}, "av", 3.14}}};
const ComplexType dataWritten = { {1, {{{5, {{sdbus::ObjectPath{"/some/object"}, true, 45, {{6, "hello"}, {7, "world"}}}}}}, sdbus::Signature{"av"}, 3.14}}};
msg << dataWritten;
msg.seal();
@@ -432,11 +493,10 @@ TEST(AMessage, CanPeekASimpleType)
msg << 123;
msg.seal();
std::string type;
std::string contents;
msg.peekType(type, contents);
ASSERT_THAT(type, "i");
ASSERT_THAT(contents, "");
auto [type, contents] = msg.peekType();
ASSERT_THAT(type, Eq('i'));
ASSERT_THAT(contents, IsNull());
}
TEST(AMessage, CanPeekContainerContents)
@@ -445,11 +505,10 @@ TEST(AMessage, CanPeekContainerContents)
msg << std::map<int, std::string>{{1, "one"}, {2, "two"}};
msg.seal();
std::string type;
std::string contents;
msg.peekType(type, contents);
ASSERT_THAT(type, "a");
ASSERT_THAT(contents, "{is}");
auto [type, contents] = msg.peekType();
ASSERT_THAT(type, Eq('a'));
ASSERT_THAT(contents, StrEq("{is}"));
}
TEST(AMessage, CanCarryDBusArrayGivenAsCustomType)
@@ -469,11 +528,11 @@ TEST(AMessage, CanCarryDBusArrayGivenAsCustomType)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryDBusStructGivenAsCustomType)
TEST(AMessage, CanCarryUserDefinedStruct)
{
auto msg = sdbus::createPlainMessage();
const my::Struct dataWritten{3545342, "hello"s, {3.14, 2.4568546}};
const my::Struct dataWritten{3545342, "hello"s, {3.14, 2.4568546}, my::Enum::Value2};
msg << dataWritten;
msg.seal();
@@ -483,3 +542,149 @@ TEST(AMessage, CanCarryDBusStructGivenAsCustomType)
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanCarryNestedUserDefinedStruct)
{
auto msg = sdbus::createPlainMessage();
const my::NestedStruct dataWritten{3545342, "hello"s, my::Enum::Value2, {12, "world"s, {3.14, 2.4568546}, my::Enum::Value3}};
msg << dataWritten;
msg.seal();
my::NestedStruct dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(dataWritten));
}
TEST(AMessage, CanSerializeUserDefinedStructAsDictionaryOfStringsToVariants)
{
auto msg = sdbus::createPlainMessage();
const my::Struct dataWritten{3545342, "hello"s, {3.14, 2.4568546}, my::Enum::Value2};
msg << sdbus::as_dictionary{dataWritten};
msg.seal();
std::map<std::string, sdbus::Variant> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, SizeIs(4));
ASSERT_THAT(dataRead.at("i").get<int>(), Eq(3545342));
ASSERT_THAT(dataRead.at("s").get<std::string>(), Eq("hello"));
ASSERT_THAT(dataRead.at("l").get<std::list<double>>(), ElementsAre(3.14, 2.4568546));
ASSERT_THAT(dataRead.at("e").get<my::Enum>(), Eq(my::Enum::Value2));
}
TEST(AMessage, CanRecursivelySerializeUserDefinedStructAsDictionaryOfStringsToVariants)
{
auto msg = sdbus::createPlainMessage();
const my::NestedStruct dataWritten{3545342, "hello"s, my::Enum::Value2, {12, "world"s, {3.14, 2.4568546}, my::Enum::Value3}};
msg << sdbus::as_dictionary{dataWritten};
msg.seal();
std::map<std::string, sdbus::Variant> dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, SizeIs(4));
ASSERT_THAT(dataRead.at("i").get<int>(), Eq(3545342));
ASSERT_THAT(dataRead.at("s").get<std::string>(), Eq("hello"));
ASSERT_THAT(dataRead.at("e").get<my::Enum>(), Eq(my::Enum::Value2));
auto nestedStructRead = dataRead.at("x").get<std::map<std::string, sdbus::Variant>>(); // Nested struct serialized as dict
ASSERT_THAT(nestedStructRead.at("i").get<int>(), Eq(12));
ASSERT_THAT(nestedStructRead.at("s").get<std::string>(), Eq("world"));
ASSERT_THAT(nestedStructRead.at("l").get<std::list<double>>(), ElementsAre(3.14, 2.4568546));
ASSERT_THAT(nestedStructRead.at("e").get<my::Enum>(), Eq(my::Enum::Value3));
}
TEST(AMessage, CanDeserializeDictionaryOfStringsToVariantsIntoUserDefinedStruct)
{
auto msg = sdbus::createPlainMessage();
const std::map<std::string, sdbus::Variant> dataWritten{ {"i", sdbus::Variant{3545342}}
, {"s", sdbus::Variant{"hello"s}}
, {"l", sdbus::Variant{std::list<double>{3.14, 2.4568546}}}
, {"e", sdbus::Variant{my::Enum::Value2}} };
msg << dataWritten;
msg.seal();
my::Struct dataRead;
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(my::Struct{3545342, "hello"s, {3.14, 2.4568546}, my::Enum::Value2}));
}
TEST(AMessage, FailsDeserializingDictionaryIntoUserDefinedStructIfStructMemberIsNotFound)
{
auto msg = sdbus::createPlainMessage();
const std::map<std::string, sdbus::Variant> dataWritten{ {"i", sdbus::Variant{3545342}}
, {"nonexistent", sdbus::Variant{"hello"s}}
, {"l", sdbus::Variant{std::list<double>{3.14, 2.4568546}}}
, {"e", sdbus::Variant{my::Enum::Value2}} };
msg << dataWritten;
msg.seal();
my::Struct dataRead;
ASSERT_THROW(msg >> dataRead, sdbus::Error);
}
TEST(AMessage, DeserializesDictionaryIntoStructWithMissingMembersSuccessfullyIfRelaxedOptionIsSet)
{
auto msg = sdbus::createPlainMessage();
const std::map<std::string, sdbus::Variant> dataWritten{ {"some_nonexistent_struct_member", sdbus::Variant{3545342}}
, {"another_nonexistent_struct_member", sdbus::Variant{"hello"s}}
, {"l", sdbus::Variant{std::list<double>{3.14, 2.4568546}}}
, {"e", sdbus::Variant{my::Enum::Value2}} };
msg << dataWritten;
msg.seal();
my::RelaxedStruct dataRead{};
msg >> dataRead;
ASSERT_THAT(dataRead, Eq(my::RelaxedStruct{{}, {}, {3.14, 2.4568546}, my::Enum::Value2}));
}
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
@@ -0,0 +1,125 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2026 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 pollData{};
pollData.timeout = std::chrono::microseconds::zero();
auto relativeTimeout = pollData.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, Eq(std::chrono::microseconds::zero()));
}
TEST(PollData, ReturnsZeroPollTimeoutForZeroAbsoluteTimeout)
{
sdbus::IConnection::PollData pollData{};
pollData.timeout = std::chrono::microseconds::zero();
auto pollTimeout = pollData.getPollTimeout();
EXPECT_THAT(pollTimeout, Eq(0));
}
TEST(PollData, ReturnsInfiniteRelativeTimeoutForInfiniteAbsoluteTimeout)
{
sdbus::IConnection::PollData pollData{};
pollData.timeout = std::chrono::microseconds::max();
auto relativeTimeout = pollData.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, Eq(std::chrono::microseconds::max()));
}
TEST(PollData, ReturnsNegativePollTimeoutForInfiniteAbsoluteTimeout)
{
sdbus::IConnection::PollData pollData{};
pollData.timeout = std::chrono::microseconds::max();
auto pollTimeout = pollData.getPollTimeout();
EXPECT_THAT(pollTimeout, Eq(-1));
}
TEST(PollData, ReturnsZeroRelativeTimeoutForPastAbsoluteTimeout)
{
sdbus::IConnection::PollData pollData{};
auto past = std::chrono::steady_clock::now() - 10s;
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch());
auto relativeTimeout = pollData.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, Eq(0us));
}
TEST(PollData, ReturnsZeroPollTimeoutForPastAbsoluteTimeout)
{
sdbus::IConnection::PollData pollData{};
auto past = std::chrono::steady_clock::now() - 10s;
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(past.time_since_epoch());
auto pollTimeout = pollData.getPollTimeout();
EXPECT_THAT(pollTimeout, Eq(0));
}
TEST(PollData, ReturnsCorrectRelativeTimeoutForFutureAbsoluteTimeout)
{
sdbus::IConnection::PollData pollData{};
auto future = std::chrono::steady_clock::now() + 1s;
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch());
auto relativeTimeout = pollData.getRelativeTimeout();
EXPECT_THAT(relativeTimeout, AllOf(Ge(900ms), Le(1100ms)));
}
TEST(PollData, ReturnsCorrectPollTimeoutForFutureAbsoluteTimeout)
{
sdbus::IConnection::PollData pollData{};
auto future = std::chrono::steady_clock::now() + 1s;
pollData.timeout = std::chrono::duration_cast<std::chrono::microseconds>(future.time_since_epoch());
auto pollTimeout = pollData.getPollTimeout();
EXPECT_THAT(pollTimeout, AllOf(Ge(900), Le(1100)));
}
+95 -46
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file TypeTraits_test.cpp
*
@@ -30,6 +30,15 @@
#include <gmock/gmock.h>
#include <cstdint>
#include <type_traits>
#include <string>
#include <string_view>
#include <vector>
#include <array>
#include <map>
#include <unordered_map>
#include <variant>
#include <span>
#include <tuple>
using ::testing::Eq;
@@ -39,20 +48,37 @@ namespace
// FIXTURE DEFINITION FOR TYPED TESTS
// ----
template <typename _T>
template <typename>
class Type2DBusTypeSignatureConversion
: public ::testing::Test
{
protected:
const std::string dbusTypeSignature_{getDBusTypeSignature()};
std::string dbusTypeSignature_{getDBusTypeSignature()};
private:
static std::string getDBusTypeSignature();
};
enum class SomeEnumClass : uint8_t
{
A, B, C
};
enum struct SomeEnumStruct : int64_t // NOLINT(performance-enum-size)
{
A, B, C
};
enum SomeClassicEnum // NOLINT(performance-enum-size)
{
A, B, C
};
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define TYPE(...) \
template <> \
std::string Type2DBusTypeSignatureConversion<__VA_ARGS__>::getDBusTypeSignature() \
/**/
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
#define HAS_DBUS_TYPE_SIGNATURE(_SIG) \
{ \
return (_SIG); \
@@ -70,17 +96,28 @@ 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")
#if __cplusplus >= 202002L
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("ao")
#ifdef __cpp_lib_span
TYPE(std::span<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
#endif
TYPE(SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
TYPE(const SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
TYPE(volatile SomeEnumClass)HAS_DBUS_TYPE_SIGNATURE("y")
TYPE(const volatile 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<
@@ -100,40 +137,51 @@ namespace
>,
sdbus::Signature,
sdbus::UnixFd,
const char*
const char*,
std::string_view
>
>;
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghs)}")
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(oanbva{is})}ghss)}")
typedef ::testing::Types< bool
, uint8_t
, int16_t
, uint16_t
, int32_t
, uint32_t
, int64_t
, uint64_t
, double
, const char*
, std::string
, sdbus::ObjectPath
, sdbus::Signature
, sdbus::Variant
, sdbus::UnixFd
, sdbus::Struct<bool>
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
, std::vector<int16_t>
, std::array<int16_t, 3>
#if __cplusplus >= 202002L
, std::span<int16_t>
using DBusSupportedTypes = ::testing::Types< bool
, uint8_t
, int16_t
, uint16_t
, int32_t
, uint32_t
, int64_t
, uint64_t
, 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
, std::span<int16_t>
#endif
, std::map<int32_t, int64_t>
, std::unordered_map<int32_t, int64_t>
, ComplexType
> DBusSupportedTypes;
, SomeEnumClass
, const SomeEnumClass
, volatile SomeEnumClass
, const volatile SomeEnumClass
, SomeEnumStruct
, SomeClassicEnum
, std::map<int32_t, int64_t>
, std::unordered_map<int32_t, int64_t>
, ComplexType >;
TYPED_TEST_SUITE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
}
} // namespace
/*-------------------------------------*/
/* -- TEST CASES -- */
@@ -141,7 +189,8 @@ namespace
TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
{
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
constexpr auto signature = sdbus::as_null_terminated(sdbus::signature_of_v<TypeParam>);
ASSERT_THAT(signature.data(), Eq(this->dbusTypeSignature_));
}
TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)
@@ -150,11 +199,11 @@ TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)
using Fnc = decltype(f);
static_assert(!sdbus::is_async_method_v<Fnc>, "Free function incorrectly detected as async method");
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<>>::value, "Incorrectly detected free function parameters");
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<>>::value, "Incorrectly detected tuple of free function parameters");
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, void>::value, "Incorrectly detected tuple of free function return types");
static_assert(std::is_same_v<sdbus::function_arguments_t<Fnc>, std::tuple<>>, "Incorrectly detected free function parameters");
static_assert(std::is_same_v<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<>>, "Incorrectly detected tuple of free function parameters");
static_assert(std::is_same_v<sdbus::tuple_of_function_output_arg_types_t<Fnc>, void>, "Incorrectly detected tuple of free function return types");
static_assert(sdbus::function_argument_count_v<Fnc> == 0, "Incorrectly detected free function parameter count");
static_assert(std::is_void<sdbus::function_result_t<Fnc>>::value, "Incorrectly detected free function return type");
static_assert(std::is_void_v<sdbus::function_result_t<Fnc>>, "Incorrectly detected free function return type");
}
TEST(FreeFunctionTypeTraits, DetectsTraitsOfNontrivialSignatureFunction)
@@ -163,11 +212,11 @@ TEST(FreeFunctionTypeTraits, DetectsTraitsOfNontrivialSignatureFunction)
using Fnc = decltype(f);
static_assert(!sdbus::is_async_method_v<Fnc>, "Free function incorrectly detected as async method");
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>::value, "Incorrectly detected free function parameters");
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>::value, "Incorrectly detected tuple of free function parameters");
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected tuple of free function return types");
static_assert(std::is_same_v<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>, "Incorrectly detected free function parameters");
static_assert(std::is_same_v<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>, "Incorrectly detected tuple of free function parameters");
static_assert(std::is_same_v<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected tuple of free function return types");
static_assert(sdbus::function_argument_count_v<Fnc> == 3, "Incorrectly detected free function parameter count");
static_assert(std::is_same<sdbus::function_result_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected free function return type");
static_assert(std::is_same_v<sdbus::function_result_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected free function return type");
}
TEST(FreeFunctionTypeTraits, DetectsTraitsOfAsyncFunction)
@@ -176,9 +225,9 @@ TEST(FreeFunctionTypeTraits, DetectsTraitsOfAsyncFunction)
using Fnc = decltype(f);
static_assert(sdbus::is_async_method_v<Fnc>, "Free async function incorrectly detected as sync method");
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>::value, "Incorrectly detected free function parameters");
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>::value, "Incorrectly detected tuple of free function parameters");
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected tuple of free function return types");
static_assert(std::is_same_v<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>, "Incorrectly detected free function parameters");
static_assert(std::is_same_v<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>, "Incorrectly detected tuple of free function parameters");
static_assert(std::is_same_v<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected tuple of free function return types");
static_assert(sdbus::function_argument_count_v<Fnc> == 3, "Incorrectly detected free function parameter count");
static_assert(std::is_same<sdbus::function_result_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected free function return type");
static_assert(std::is_same_v<sdbus::function_result_t<Fnc>, std::tuple<char, int>>, "Incorrectly detected free function return type");
}
+223 -59
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file Types_test.cpp
*
@@ -24,22 +24,35 @@
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include <sdbus-c++/Error.h>
#include <sdbus-c++/Message.h>
#include <sdbus-c++/Types.h>
#include "MessageUtils.h"
#include <sdbus-c++/TypeTraits.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <cstdint>
#include <cerrno>
#include <map>
#include <string>
#include <variant>
#include <vector>
#include <sys/eventfd.h>
#include <tuple>
#include <type_traits>
#include <unistd.h>
#include <utility>
using ::testing::Eq;
using ::testing::Gt;
using ::testing::HasSubstr;
using namespace std::string_literals;
namespace
{
constexpr const uint64_t ANY_UINT64 = 84578348354;
constexpr const double ANY_DOUBLE = 3.14;
}
} // namespace
/*-------------------------------------*/
/* -- TEST CASES -- */
@@ -52,14 +65,14 @@ TEST(AVariant, CanBeDefaultConstructed)
TEST(AVariant, ContainsNoValueAfterDefaultConstructed)
{
sdbus::Variant v;
const sdbus::Variant var;
ASSERT_TRUE(v.isEmpty());
ASSERT_TRUE(var.isEmpty());
}
TEST(AVariant, CanBeConstructedFromASimpleValue)
{
int value = 5;
const int value = 5;
ASSERT_NO_THROW(sdbus::Variant{value});
}
@@ -67,53 +80,140 @@ TEST(AVariant, CanBeConstructedFromASimpleValue)
TEST(AVariant, CanBeConstructedFromAComplexValue)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
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}};
const StdVariantType stdVariant{value};
const sdbus::Variant sdbusVariant{stdVariant};
ASSERT_TRUE(sdbusVariant.containsValueOfType<ComplexType>());
ASSERT_THAT(sdbusVariant.get<ComplexType>(), Eq(value));
}
TEST(AVariant, CanBeCopied)
{
auto value = "hello"s;
sdbus::Variant variant(value);
const sdbus::Variant variant(value);
auto variantCopy1{variant};
auto variantCopy2 = variantCopy1;
const auto variantCopy1{variant}; // NOLINT(performance-unnecessary-copy-initialization)
const auto variantCopy2 = variantCopy1; // NOLINT(performance-unnecessary-copy-initialization)
ASSERT_THAT(variantCopy1.get<std::string>(), Eq(value));
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()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
}
TEST(AVariant, CanBeMovedIntoAMap)
{
const auto value = "hello"s;
sdbus::Variant variant(value); // NOLINT(misc-const-correctness)
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()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
}
TEST(AVariant, IsNotEmptyWhenContainsAValue)
{
sdbus::Variant v("hello");
const sdbus::Variant var("hello");
ASSERT_FALSE(v.isEmpty());
ASSERT_FALSE(var.isEmpty());
}
TEST(ASimpleVariant, ReturnsTheSimpleValueWhenAsked)
{
int value = 5;
const int value = 5;
sdbus::Variant variant(value);
const sdbus::Variant variant(value);
ASSERT_THAT(variant.get<int>(), Eq(value));
}
#ifndef SDBUS_basu // Dumping message or variant to a string is not supported on basu backend
TEST(ASimpleVariant, CanBeDumpedToAString)
{
const int value = 5;
const sdbus::Variant variant(value);
// This should produce something like:
// VARIANT "i" {
// INT32 5;
// };
const auto str = variant.dumpToString();
EXPECT_THAT(str, ::HasSubstr("VARIANT \"i\""));
EXPECT_THAT(str, ::HasSubstr("INT32"));
EXPECT_THAT(str, ::HasSubstr("5"));
}
#endif // SDBUS_basu
TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value);
const sdbus::Variant variant(value);
ASSERT_THAT(variant.get<decltype(value)>(), Eq(value));
ASSERT_THAT(variant.get<ComplexType>(), Eq(value));
}
#ifndef SDBUS_basu // Dumping message or variant to a string is not supported on basu backend
TEST(AComplexVariant, CanBeDumpedToAString)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
const sdbus::Variant variant(value);
// This should produce something like:
// VARIANT "a{ta(sd)}" {
// ARRAY "{ta(sd)}" {
// DICT_ENTRY "ta(sd)" {
// UINT64 84578348354;
// ARRAY "(sd)" {
// STRUCT "sd" {
// STRING "hello";
// DOUBLE 3.14;
// };
// STRUCT "sd" {
// STRING "world";
// DOUBLE 3.14;
// };
// };
// };
// };
// };
const auto str = variant.dumpToString();
EXPECT_THAT(str, ::HasSubstr("VARIANT \"a{ta(sd)}\""));
EXPECT_THAT(str, ::HasSubstr("hello"));
EXPECT_THAT(str, ::HasSubstr("world"));
}
#endif // SDBUS_basu
TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
{
std::string value{"I am a string"};
sdbus::Variant variant(value);
const std::string value{"I am a string"};
const sdbus::Variant variant(value);
ASSERT_THAT(variant.get<std::string>(), Eq(value));
ASSERT_THAT(variant.get<std::string>(), Eq(value));
@@ -123,18 +223,42 @@ TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value);
const sdbus::Variant variant(value);
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>;
const ComplexType value{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}};
const 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}};
const StdVariantType stdVariant{value};
auto stdVariantCopy = [](const sdbus::Variant &var) -> StdVariantType { return var; }(stdVariant);
ASSERT_THAT(stdVariantCopy, Eq(stdVariant));
}
TEST(ASimpleVariant, ReturnsFalseWhenAskedIfItContainsTypeItDoesntReallyContain)
{
int value = 5;
const int value = 5;
sdbus::Variant variant(value);
const sdbus::Variant variant(value);
ASSERT_FALSE(variant.containsValueOfType<double>());
}
@@ -143,17 +267,17 @@ TEST(AVariant, CanContainOtherEmbeddedVariants)
{
using TypeWithVariants = std::vector<sdbus::Struct<sdbus::Variant, double>>;
TypeWithVariants value;
value.push_back({sdbus::Variant("a string"), ANY_DOUBLE});
value.push_back({sdbus::Variant(ANY_UINT64), ANY_DOUBLE});
value.emplace_back(sdbus::Variant("a string"), ANY_DOUBLE);
value.emplace_back(sdbus::Variant(ANY_UINT64), ANY_DOUBLE);
sdbus::Variant variant(value);
const sdbus::Variant variant(value);
ASSERT_TRUE(variant.containsValueOfType<TypeWithVariants>());
}
TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
{
sdbus::Variant variant("a string");
const sdbus::Variant variant("a string");
auto msg = sdbus::createPlainMessage();
@@ -162,7 +286,7 @@ TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
{
sdbus::Variant variant;
const sdbus::Variant variant;
auto msg = sdbus::createPlainMessage();
@@ -172,8 +296,8 @@ TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value);
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
const sdbus::Variant variant(value);
auto msg = sdbus::createPlainMessage();
variant.serializeTo(msg);
@@ -181,30 +305,30 @@ TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
sdbus::Variant variant2;
variant2.deserializeFrom(msg);
ASSERT_THAT(variant2.get<decltype(value)>(), Eq(value));
ASSERT_THAT(variant2.get<ComplexType>(), Eq(value));
}
TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
{
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
sdbus::Variant variant(value);
auto variantCopy1{variant};
auto variantCopy2 = variant;
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
const sdbus::Variant variant(value);
auto variantCopy1{variant}; // NOLINT(performance-unnecessary-copy-initialization)
auto variantCopy2 = variant; // NOLINT(performance-unnecessary-copy-initialization)
auto msg = sdbus::createPlainMessage();
variant.serializeTo(msg);
variantCopy1.serializeTo(msg);
variantCopy2.serializeTo(msg);
msg.seal();
sdbus::Variant receivedVariant1, receivedVariant2, receivedVariant3;
sdbus::Variant receivedVariant1, receivedVariant2, receivedVariant3; // NOLINT(readability-isolate-declaration)
receivedVariant1.deserializeFrom(msg);
receivedVariant2.deserializeFrom(msg);
receivedVariant3.deserializeFrom(msg);
ASSERT_THAT(receivedVariant1.get<decltype(value)>(), Eq(value));
ASSERT_THAT(receivedVariant2.get<decltype(value)>(), Eq(value));
ASSERT_THAT(receivedVariant3.get<decltype(value)>(), Eq(value));
ASSERT_THAT(receivedVariant1.get<ComplexType>(), Eq(value));
ASSERT_THAT(receivedVariant2.get<ComplexType>(), Eq(value));
ASSERT_THAT(receivedVariant3.get<ComplexType>(), Eq(value));
}
TEST(AStruct, CanBeCreatedFromStdTuple)
@@ -235,7 +359,7 @@ TEST(AStruct, CanBeUsedLikeStdTupleType)
TEST(AStruct, CanBeUsedInStructuredBinding)
{
sdbus::Struct valueStruct(1234, "abcd", true);
const sdbus::Struct valueStruct(1234, "abcd", true);
auto [first, second, third] = valueStruct;
@@ -251,7 +375,7 @@ TEST(AnObjectPath, CanBeConstructedFromCString)
TEST(AnObjectPath, CanBeConstructedFromStdString)
{
std::string aPath{"/some/path"};
const std::string aPath{"/some/path"};
ASSERT_THAT(sdbus::ObjectPath{aPath}, Eq(aPath));
}
@@ -262,7 +386,6 @@ TEST(AnObjectPath, CanBeMovedLikeAStdString)
sdbus::ObjectPath oPath{aPath};
ASSERT_THAT(sdbus::ObjectPath{std::move(oPath)}, Eq(sdbus::ObjectPath(std::move(aPath))));
ASSERT_THAT(std::string(oPath), Eq(aPath));
}
TEST(ASignature, CanBeConstructedFromCString)
@@ -274,7 +397,7 @@ TEST(ASignature, CanBeConstructedFromCString)
TEST(ASignature, CanBeConstructedFromStdString)
{
std::string aSignature{"us"};
const std::string aSignature{"us"};
ASSERT_THAT(sdbus::Signature{aSignature}, Eq(aSignature));
}
@@ -285,7 +408,6 @@ 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)
@@ -306,9 +428,9 @@ TEST(AUnixFd, AdoptsAndOwnsFdAsIsUponAdoptionConstruction)
TEST(AUnixFd, DuplicatesFdUponCopyConstruction)
{
sdbus::UnixFd unixFd(::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK));
const sdbus::UnixFd unixFd(::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK));
sdbus::UnixFd unixFdCopy{unixFd};
const sdbus::UnixFd unixFdCopy{unixFd}; // NOLINT(performance-unnecessary-copy-initialization)
EXPECT_THAT(unixFdCopy.get(), Gt(unixFd.get()));
}
@@ -318,20 +440,20 @@ TEST(AUnixFd, TakesOverFdUponMoveConstruction)
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
sdbus::UnixFd unixFdNew{std::move(unixFd)};
const sdbus::UnixFd unixFdNew{std::move(unixFd)};
EXPECT_FALSE(unixFd.isValid());
EXPECT_FALSE(unixFd.isValid()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move)
EXPECT_THAT(unixFdNew.get(), Eq(fd));
}
TEST(AUnixFd, ClosesFdProperlyUponDestruction)
{
int fd, fdCopy;
int fd{}, fdCopy{}; // NOLINT(readability-isolate-declaration)
{
fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
auto unixFdNew = std::move(unixFd);
auto unixFdCopy = unixFdNew;
auto unixFdCopy = unixFdNew; // NOLINT(performance-unnecessary-copy-initialization)
fdCopy = unixFdCopy.get();
}
@@ -342,7 +464,7 @@ TEST(AUnixFd, ClosesFdProperlyUponDestruction)
TEST(AUnixFd, DoesNotCloseReleasedFd)
{
auto fd = ::eventfd(0, EFD_SEMAPHORE | EFD_NONBLOCK);
int fdReleased;
int fdReleased{};
{
sdbus::UnixFd unixFd(fd, sdbus::adopt_fd);
fdReleased = unixFd.release();
@@ -391,18 +513,60 @@ TEST(AUnixFd, TakesOverNewFdAndClosesOriginalFdOnAdoptingReset)
TEST(AnError, CanBeConstructedFromANameAndAMessage)
{
auto error = sdbus::Error("name", "message");
EXPECT_THAT(error.getName(), Eq<std::string>("name"));
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)
{
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"));
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"));
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());
}
+17 -14
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file SdBusMock.h
* @author Ardazishvili Roman (ardazishvili.roman@yandex.ru)
@@ -35,18 +35,18 @@
class SdBusMock : public sdbus::internal::ISdBus
{
public:
MOCK_METHOD1(sd_bus_message_ref, sd_bus_message*(sd_bus_message *m));
MOCK_METHOD1(sd_bus_message_unref, sd_bus_message*(sd_bus_message *m));
MOCK_METHOD1(sd_bus_message_ref, sd_bus_message*(sd_bus_message *msg));
MOCK_METHOD1(sd_bus_message_unref, sd_bus_message*(sd_bus_message *msg));
MOCK_METHOD3(sd_bus_send, int(sd_bus *bus, sd_bus_message *m, uint64_t *cookie));
MOCK_METHOD5(sd_bus_call, int(sd_bus *bus, sd_bus_message *m, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply));
MOCK_METHOD6(sd_bus_call_async, int(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *m, sd_bus_message_handler_t callback, void *userdata, uint64_t usec));
MOCK_METHOD3(sd_bus_send, int(sd_bus *bus, sd_bus_message *msg, uint64_t *cookie));
MOCK_METHOD5(sd_bus_call, int(sd_bus *bus, sd_bus_message *msg, uint64_t usec, sd_bus_error *ret_error, sd_bus_message **reply));
MOCK_METHOD6(sd_bus_call_async, int(sd_bus *bus, sd_bus_slot **slot, sd_bus_message *msg, sd_bus_message_handler_t callback, void *userdata, uint64_t usec));
MOCK_METHOD3(sd_bus_message_new, int(sd_bus *bus, sd_bus_message **m, uint8_t type));
MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **m, const char *destination, const char *path, const char *interface, const char *member));
MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **m, const char *path, const char *interface, const char *member));
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **m));
MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **m, const sd_bus_error *e));
MOCK_METHOD3(sd_bus_message_new, int(sd_bus *bus, sd_bus_message **msg, uint8_t type));
MOCK_METHOD6(sd_bus_message_new_method_call, int(sd_bus *bus, sd_bus_message **msg, const char *destination, const char *path, const char *interface, const char *member));
MOCK_METHOD5(sd_bus_message_new_signal, int(sd_bus *bus, sd_bus_message **msg, const char *path, const char *interface, const char *member));
MOCK_METHOD2(sd_bus_message_new_method_return, int(sd_bus_message *call, sd_bus_message **msg));
MOCK_METHOD3(sd_bus_message_new_method_error, int(sd_bus_message *call, sd_bus_message **msg, const sd_bus_error *err));
MOCK_METHOD2(sd_bus_set_method_call_timeout, int(sd_bus *bus, uint64_t usec));
MOCK_METHOD2(sd_bus_get_method_call_timeout, int(sd_bus *bus, uint64_t *ret));
@@ -72,21 +72,24 @@ 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_METHOD2(sd_bus_process, int(sd_bus *bus, sd_bus_message **ret));
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_METHOD3(sd_bus_get_n_queued, int(sd_bus *bus, uint64_t *read, uint64_t* write));
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));
MOCK_METHOD2(sd_bus_message_set_destination, int(sd_bus_message *m, const char *destination));
MOCK_METHOD2(sd_bus_message_set_destination, int(sd_bus_message *msg, const char *destination));
MOCK_METHOD3(sd_bus_query_sender_creds, int(sd_bus_message *, uint64_t, sd_bus_creds **));
MOCK_METHOD1(sd_bus_creds_ref, sd_bus_creds*(sd_bus_creds *));
MOCK_METHOD1(sd_bus_creds_unref, sd_bus_creds*(sd_bus_creds *));
MOCK_METHOD2(sd_bus_creds_get_pid, int(sd_bus_creds *, pid_t *));
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file sdbus-c++-unit-tests.cpp
*
+28 -19
View File
@@ -4,7 +4,7 @@
cmake_minimum_required(VERSION 3.5)
project(sdbus-c++-tools VERSION 1.5.0)
project(sdbus-c++-tools VERSION 2.3.1)
include(GNUInstallDirs)
@@ -35,7 +35,8 @@ set(SDBUSCPP_XML2CPP_SRCS
# GENERAL COMPILER CONFIGURATION
#-------------------------------
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD 17)
unset(CMAKE_CXX_CLANG_TIDY) # Do not propagate clang-tidy to tools
#----------------------------------
# EXECUTABLE BUILD INFORMATION
@@ -44,12 +45,7 @@ set(CMAKE_CXX_STANDARD 14)
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)
target_compile_definitions(sdbus-c++-xml2cpp PRIVATE SDBUS_XML2CPP_VERSION="${CMAKE_PROJECT_VERSION}")
#----------------------------------
# CMAKE CONFIG & PACKAGE CONFIG
@@ -57,19 +53,32 @@ install(TARGETS sdbus-c++-xml2cpp EXPORT sdbus-c++-tools-targets DESTINATION ${C
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)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/sdbus-c++-tools.pc
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT dev)
#----------------------------------
# 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()
+92 -44
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorGenerator.cpp
*
@@ -85,7 +85,17 @@ 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 << ": object_(&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;
Nodes methods = interface["method"];
Nodes signals = interface["signal"];
@@ -111,34 +121,29 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
if(!annotationRegistration.empty())
{
std::stringstream str;
str << tab << tab << "object_->setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl;
str << "sdbus::setInterfaceFlags()" << annotationRegistration;
annotationRegistration = str.str();
}
std::string methodRegistration, methodDeclaration;
std::tie(methodRegistration, methodDeclaration) = processMethods(methods);
std::vector<std::string> methodRegistrations;
std::string methodDeclaration;
std::tie(methodRegistrations, methodDeclaration) = processMethods(methods);
std::string signalRegistration, signalMethods;
std::tie(signalRegistration, signalMethods) = processSignals(signals);
std::vector<std::string> signalRegistrations;
std::string signalMethods;
std::tie(signalRegistrations, signalMethods) = processSignals(signals);
std::string propertyRegistration, propertyAccessorDeclaration;
std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties);
std::vector<std::string> propertyRegistrations;
std::string propertyAccessorDeclaration;
std::tie(propertyRegistrations, propertyAccessorDeclaration) = processProperties(properties);
body << tab << "{" << endl
<< annotationRegistration
<< methodRegistration
<< signalRegistration
<< propertyRegistration
auto vtableRegistration = createVTableRegistration(annotationRegistration, methodRegistrations, signalRegistrations, propertyRegistrations);
body << tab << "void registerAdaptor()" << endl
<< tab << "{" << endl
<< vtableRegistration << 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 << "&&) = default;" << endl;
body << tab << className << "& operator=(" << className << "&&) = default;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
if (!signalMethods.empty())
{
body << "public:" << endl << signalMethods;
@@ -155,7 +160,7 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
}
body << "private:" << endl
<< tab << "sdbus::IObject* object_;" << endl
<< tab << "sdbus::IObject& m_object;" << endl
<< "};" << endl << endl
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
@@ -163,12 +168,16 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
}
std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
{
std::ostringstream registrationSS, declarationSS;
std::ostringstream declarationSS;
std::vector<std::string> methodRegistrations;
for (const auto& method : methods)
{
std::ostringstream registrationSS;
auto methodName = method->get("name");
auto methodNameSafe = mangle_name(methodName);
@@ -217,9 +226,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
using namespace std::string_literals;
registrationSS << tab << tab << "object_->registerMethod(\""
registrationSS << "sdbus::registerMethod(\""
<< methodName << "\")"
<< ".onInterface(INTERFACE_NAME)"
<< (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
<< (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "")
<< ".implementedAs("
@@ -229,7 +237,9 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
<< "){ " << (async ? "" : "return ") << "this->" << methodNameSafe << "("
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
<< argStr << "); })"
<< annotationRegistration << ";" << endl;
<< annotationRegistration;
methodRegistrations.push_back(registrationSS.str());
declarationSS << tab
<< "virtual "
@@ -241,16 +251,20 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
<< ") = 0;" << endl;
}
return std::make_tuple(registrationSS.str(), declarationSS.str());
return std::make_tuple(methodRegistrations, declarationSS.str());
}
std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
{
std::ostringstream signalRegistrationSS, signalMethodSS;
std::ostringstream signalMethodSS;
std::vector<std::string> signalRegistrations;
for (const auto& signal : signals)
{
std::stringstream signalRegistrationSS;
auto name = signal->get("name");
auto annotations = getAnnotations(*signal);
@@ -272,9 +286,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
std::string argStr, argTypeStr, typeStr, argStringsStr;
std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
signalRegistrationSS << tab << tab
<< "object_->registerSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)";
signalRegistrationSS << "sdbus::registerSignal(\"" << name << "\")";
if (args.size() > 0)
{
@@ -282,7 +294,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
}
signalRegistrationSS << annotationRegistration;
signalRegistrationSS << ";" << endl;
signalRegistrations.push_back(signalRegistrationSS.str());
auto nameWithCapFirstLetter = name;
nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]);
@@ -290,7 +303,7 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl
<< tab << "{" << endl
<< tab << tab << "object_->emitSignal(\"" << name << "\")"
<< tab << tab << "m_object.emitSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)";
if (!argStr.empty())
@@ -302,16 +315,20 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
<< tab << "}" << endl << endl;
}
return std::make_tuple(signalRegistrationSS.str(), signalMethodSS.str());
return std::make_tuple(signalRegistrations, signalMethodSS.str());
}
std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
std::tuple<std::vector<std::string>, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
{
std::ostringstream registrationSS, declarationSS;
std::ostringstream declarationSS;
std::vector<std::string> propertyRegistrations;
for (const auto& property : properties)
{
std::ostringstream registrationSS;
auto propertyName = property->get("name");
auto propertyNameSafe = mangle_name(propertyName);
auto propertyAccess = property->get("access");
@@ -339,9 +356,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
<< "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
}
registrationSS << tab << tab << "object_->registerProperty(\""
<< propertyName << "\")"
<< ".onInterface(INTERFACE_NAME)";
registrationSS << "sdbus::registerProperty(\""
<< propertyName << "\")";
if (propertyAccess == "read" || propertyAccess == "readwrite")
{
@@ -356,7 +372,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
}
registrationSS << annotationRegistration;
registrationSS << ";" << endl;
propertyRegistrations.push_back(registrationSS.str());
if (propertyAccess == "read" || propertyAccess == "readwrite")
declarationSS << tab << "virtual " << propertyType << " " << propertyNameSafe << "() = 0;" << endl;
@@ -364,7 +381,38 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
declarationSS << tab << "virtual void " << propertyNameSafe << "(" << propertyTypeArg << ") = 0;" << endl;
}
return std::make_tuple(registrationSS.str(), declarationSS.str());
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();
}
std::map<std::string, std::string> AdaptorGenerator::getAnnotations( sdbuscpp::xml::Node& node) const
+9 -4
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file AdaptorGenerator.h
*
@@ -59,21 +59,26 @@ private:
* @param methods
* @return tuple: registration of methods, declaration of abstract methods
*/
std::tuple<std::string, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
std::tuple<std::vector<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::string, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
std::tuple<std::vector<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::string, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
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;
/**
* Get annotations listed for a given node
+2 -2
View File
@@ -1,6 +1,6 @@
/**
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
* (C) 2016 - 2026 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
*
* @file BaseGenerator.cpp
*
@@ -53,7 +53,7 @@ int BaseGenerator::transformXmlToFile(const Document& doc, const char* filename)
int BaseGenerator::writeToFile(const char* filename, const std::string& data) const
{
std::ofstream file(filename);
if (file.bad())
if (file.fail())
{
std::cerr << "Unable to write file " << filename << endl;
return 1;

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