From b7b39eab6e8265c4ebb8cc5faf71d4417e8a6301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stanislav=20Angelovi=C4=8D?= Date: Wed, 24 Apr 2024 08:48:39 +0200 Subject: [PATCH] refactor: reorganize public API of main abstract classes (#437) --- include/sdbus-c++/IConnection.h | 50 +-- include/sdbus-c++/IObject.h | 249 +++++++------ include/sdbus-c++/IProxy.h | 619 ++++++++++++++++---------------- 3 files changed, 484 insertions(+), 434 deletions(-) diff --git a/include/sdbus-c++/IConnection.h b/include/sdbus-c++/IConnection.h index a95ee20a..c8b82c44 100644 --- a/include/sdbus-c++/IConnection.h +++ b/include/sdbus-c++/IConnection.h @@ -64,31 +64,6 @@ namespace sdbus { virtual ~IConnection() = default; - /*! - * @brief Requests a well-known D-Bus service name on a bus - * - * @param[in] name Name to request - * - * @throws sdbus::Error in case of failure - */ - virtual void requestName(const ServiceName& name) = 0; - - /*! - * @brief Releases an acquired well-known D-Bus service name on a bus - * - * @param[in] name Name to release - * - * @throws sdbus::Error in case of failure - */ - virtual void releaseName(const ServiceName& name) = 0; - - /*! - * @brief Retrieves the unique name of a connection. E.g. ":1.xx" - * - * @throws sdbus::Error in case of failure - */ - [[nodiscard]] virtual BusName getUniqueName() const = 0; - /*! * @brief Enters I/O event loop on this bus connection * @@ -342,6 +317,31 @@ namespace sdbus { */ virtual void addMatchAsync(const std::string& match, message_handler callback, message_handler installCallback, floating_slot_t) = 0; + /*! + * @brief Retrieves the unique name of a connection. E.g. ":1.xx" + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual BusName getUniqueName() const = 0; + + /*! + * @brief Requests a well-known D-Bus service name on a bus + * + * @param[in] name Name to request + * + * @throws sdbus::Error in case of failure + */ + virtual void requestName(const ServiceName& name) = 0; + + /*! + * @brief Releases an acquired well-known D-Bus service name on a bus + * + * @param[in] name Name to release + * + * @throws sdbus::Error in case of failure + */ + virtual void releaseName(const ServiceName& name) = 0; + /*! * @struct PollData * diff --git a/include/sdbus-c++/IObject.h b/include/sdbus-c++/IObject.h index 2606c879..3458fa6c 100644 --- a/include/sdbus-c++/IObject.h +++ b/include/sdbus-c++/IObject.h @@ -61,80 +61,66 @@ namespace sdbus { ***********************************************/ class IObject { - public: + public: // High-level, convenience API virtual ~IObject() = default; /*! * @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 + * @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 `items' represents a vtable definition that may contain method declarations + * 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 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 - * match 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> && ...)> > - 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. * - * Consult manual pages for underlying `sd_bus_add_object_vtable` function for more information. + * The method can be called at any time during object's lifetime. * - * The method can be called at any time during object's lifetime. For each vtable an internal - * match slot is created and its lifetime is tied to the lifetime of the Object instance. + * 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 addVTable(InterfaceName interfaceName, std::vector vtable) = 0; + [[nodiscard]] VTableAdder addVTable(std::vector vtable); /*! * @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 + * @param[in] items Individual instances of VTable item structures + * @return VTableAdder high-level helper class * * 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). + * 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 underlying `sd_bus_add_object_vtable` function for more information. + * 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 - * match 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 + * 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 @@ -142,66 +128,10 @@ namespace sdbus { * * @throws sdbus::Error in case of failure */ - [[nodiscard]] virtual Slot addVTable(InterfaceName interfaceName, std::vector vtable, return_slot_t) = 0; - - /*! - * @brief A little more convenient overload of addVTable() above - * - * This version allows method chaining for a little safer and more readable VTable registration. - * - * See addVTable() overloads above for detailed documentation. - */ template < typename... VTableItems , typename = std::enable_if_t<(is_one_of_variants_types> && ...)> > [[nodiscard]] VTableAdder addVTable(VTableItems&&... items); - /*! - * @brief A little more convenient overload of addVTable() above - * - * This version allows method chaining for a little safer and more readable VTable registration. - * - * See addVTable() overloads above for detailed documentation. - */ - [[nodiscard]] VTableAdder addVTable(std::vector vtable); - - /*! - * @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 - */ - [[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) = 0; - - /*! - * @brief Emits signal for this object path - * - * @param[in] message Signal message to be sent out - * - * Note: To avoid messing with messages, use higher-level API defined below. - * - * @throws sdbus::Error in case of failure - */ - virtual void emitSignal(const sdbus::Signal& message) = 0; - /*! * @brief Emits signal on D-Bus * @@ -361,7 +291,126 @@ namespace sdbus { */ [[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0; - protected: + /*! + * @brief Unregisters object's API and removes object from the bus + * + * This method unregisters the object, its interfaces, methods, signals and properties + * from the bus. Unregistration is done automatically also in object's destructor. This + * method makes sense if, in the process of object removal, we need to make sure that + * callbacks are unregistered explicitly before the final destruction of the object instance. + * + * @throws sdbus::Error in case of failure + */ + virtual void unregister() = 0; + + public: // Lower-level, message-based API + /*! + * @brief Adds a declaration of methods, properties and signals of the object at a given interface + * + * @param[in] interfaceName Name of an interface the the vtable is registered for + * @param[in] items Individual instances of VTable item structures + * + * This method is used to declare attributes for the object under the given interface. + * Parameter `items' represents a vtable definition that may contain method declarations + * (using MethodVTableItem struct), property declarations (using PropertyVTableItem + * struct), signal declarations (using SignalVTableItem struct), or global interface + * flags (using InterfaceFlagsVTableItem struct). + * + * An interface can have any number of vtables attached to it. + * + * Consult manual pages for the underlying `sd_bus_add_object_vtable` function for more information. + * + * The method can be called at any time during object's lifetime. For each vtable an internal + * registration slot is created and its lifetime is tied to the lifetime of the Object instance. + * + * The function provides strong exception guarantee. The state of the object remains + * unmodified in face of an exception. + * + * @throws sdbus::Error in case of failure + */ + template < typename... VTableItems + , typename = std::enable_if_t<(is_one_of_variants_types> && ...)> > + 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 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 vtable, return_slot_t) = 0; + + /*! + * @brief Creates a signal message + * + * @param[in] interfaceName Name of an interface that the signal belongs under + * @param[in] signalName Name of the signal + * @return A signal message + * + * Serialize signal arguments into the returned message and emit the signal by passing + * the message with serialized arguments to the @c emitSignal function. + * Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Signal createSignal(const InterfaceName& interfaceName, const SignalName& signalName) = 0; + + /*! + * @brief Emits signal for this object path + * + * @param[in] message Signal message to be sent out + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual void emitSignal(const sdbus::Signal& message) = 0; + + protected: // Internal API for efficiency reasons used by high-level API helper classes friend SignalEmitter; [[nodiscard]] virtual Signal createSignal(const char* interfaceName, const char* signalName) = 0; diff --git a/include/sdbus-c++/IProxy.h b/include/sdbus-c++/IProxy.h index 9ea16a6d..029127f8 100644 --- a/include/sdbus-c++/IProxy.h +++ b/include/sdbus-c++/IProxy.h @@ -67,264 +67,9 @@ namespace sdbus { ***********************************************/ class IProxy { - public: + public: // High-level, convenience API virtual ~IProxy() = default; - /*! - * @brief Creates a method call message - * - * @param[in] interfaceName Name of an interface that provides a given method - * @param[in] methodName Name of the method - * @return A method call message - * - * Serialize method arguments into the returned message and invoke the method by passing - * the message with serialized arguments to the @c callMethod function. - * Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below. - * - * @throws sdbus::Error in case of failure - */ - [[nodiscard]] virtual MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) = 0; - - /*! - * @brief Calls method on the remote D-Bus object - * - * @param[in] message Message representing a method call - * @return A method reply message - * - * The call does not block if the method call has dont-expect-reply flag set. In that case, - * the call returns immediately and the return value is an empty, invalid method reply. - * - * The call blocks otherwise, waiting for the remote peer to send back a reply or an error, - * or until the call times out. - * - * While blocking, other concurrent operations (in other threads) on the underlying bus - * connection are stalled until the call returns. This is not an issue in vast majority of - * (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving - * shared bus connections, this may be an issue. It is advised to instead use an asynchronous - * callMethod() function overload, which does not block the bus connection, or do the synchronous - * call from another Proxy instance created just before the call and then destroyed (which is - * anyway quite a typical approach in D-Bus implementations). Such proxy instance must have - * its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread` - * tag are designed for exactly that purpose. - * - * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. - * - * @throws sdbus::Error in case of failure (also in case the remote function returned an error) - */ - virtual MethodReply callMethod(const MethodCall& message) = 0; - - /*! - * @brief Calls method on the remote D-Bus object - * - * @param[in] message Message representing a method call - * @param[in] timeout Method call timeout (in microseconds) - * @return A method reply message - * - * The call does not block if the method call has dont-expect-reply flag set. In that case, - * the call returns immediately and the return value is an empty, invalid method reply. - * - * The call blocks otherwise, waiting for the remote peer to send back a reply or an error, - * or until the call times out. - * - * While blocking, other concurrent operations (in other threads) on the underlying bus - * connection are stalled until the call returns. This is not an issue in vast majority of - * (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving - * shared bus connections, this may be an issue. It is advised to instead use an asynchronous - * callMethod() function overload, which does not block the bus connection, or do the synchronous - * call from another Proxy instance created just before the call and then destroyed (which is - * anyway quite a typical approach in D-Bus implementations). Such proxy instance must have - * its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread` - * tag are designed for exactly that purpose. - * - * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. - * - * @throws sdbus::Error in case of failure (also in case the remote function returned an error) - */ - virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout) = 0; - - /*! - * @copydoc IProxy::callMethod(const MethodCall&,uint64_t) - */ - template - MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout); - - /*! - * @brief Calls method on the D-Bus object asynchronously - * - * @param[in] message Message representing an async method call - * @param[in] asyncReplyCallback Handler for the async reply - * @return Observing handle for the the pending asynchronous call - * - * This is a callback-based way of asynchronously calling a remote D-Bus method. - * - * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, - * the provided async reply handler will get invoked from the context of the bus - * connection I/O event loop thread. - * - * An non-owning, observing async call handle is returned that can be used to query call status or cancel the call. - * - * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. - * - * @throws sdbus::Error in case of failure - */ - virtual PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) = 0; - - /*! - * @brief Calls method on the D-Bus object asynchronously - * - * @param[in] message Message representing an async method call - * @param[in] asyncReplyCallback Handler for the async reply - * @return RAII-style slot handle representing the ownership of the async call - * - * This is a callback-based way of asynchronously calling a remote D-Bus method. - * - * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, - * the provided async reply handler will get invoked from the context of the bus - * connection I/O event loop thread. - * - * A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot. - * The slot can be used to cancel the method call at a later time by simply destroying it. - * - * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. - * - * @throws sdbus::Error in case of failure - */ - [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback - , return_slot_t ) = 0; - - /*! - * @brief Calls method on the D-Bus object asynchronously, with custom timeout - * - * @param[in] message Message representing an async method call - * @param[in] asyncReplyCallback Handler for the async reply - * @param[in] timeout Method call timeout (in microseconds) - * @return Observing handle for the the pending asynchronous call - * - * This is a callback-based way of asynchronously calling a remote D-Bus method. - * - * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, - * the provided async reply handler will get invoked from the context of the bus - * connection I/O event loop thread. - * - * An non-owning, observing async call handle is returned that can be used to query call status or cancel the call. - * - * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. - * - * @throws sdbus::Error in case of failure - */ - virtual PendingAsyncCall callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback - , uint64_t timeout ) = 0; - - /*! - * @brief Calls method on the D-Bus object asynchronously, with custom timeout - * - * @param[in] message Message representing an async method call - * @param[in] asyncReplyCallback Handler for the async reply - * @param[in] timeout Method call timeout (in microseconds) - * @return RAII-style slot handle representing the ownership of the async call - * - * This is a callback-based way of asynchronously calling a remote D-Bus method. - * - * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, - * the provided async reply handler will get invoked from the context of the bus - * connection I/O event loop thread. - * - * A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot. - * The slot can be used to cancel the method call at a later time by simply destroying it. - * - * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. - * - * @throws sdbus::Error in case of failure - */ - [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback - , uint64_t timeout - , return_slot_t ) = 0; - - /*! - * @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t) - */ - template - PendingAsyncCall callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback - , const std::chrono::duration<_Rep, _Period>& timeout ); - - /*! - * @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t,return_slot_t) - */ - template - [[nodiscard]] Slot callMethodAsync( const MethodCall& message - , async_reply_handler asyncReplyCallback - , const std::chrono::duration<_Rep, _Period>& timeout - , return_slot_t ); - - /*! - * @brief Calls method on the D-Bus object asynchronously - * - * @param[in] message Message representing an async method call - * @param[in] Tag denoting a std::future-based overload - * @return Future object providing access to the future method reply message - * - * This is a std::future-based way of asynchronously calling a remote D-Bus method. - * - * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, - * the provided future object will be set to contain the reply (or sdbus::Error - * in case the remote method threw an exception). - * - * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use higher-level API defined below. - * - * @throws sdbus::Error in case of failure - */ - virtual std::future callMethodAsync(const MethodCall& message, with_future_t) = 0; - - /*! - * @brief Calls method on the D-Bus object asynchronously, with custom timeout - * - * @param[in] message Message representing an async method call - * @param[in] timeout Method call timeout - * @param[in] Tag denoting a std::future-based overload - * @return Future object providing access to the future method reply message - * - * This is a std::future-based way of asynchronously calling a remote D-Bus method. - * - * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, - * the provided future object will be set to contain the reply (or sdbus::Error - * in case the remote method threw an exception, or the call timed out). - * - * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). - * - * Note: To avoid messing with messages, use higher-level API defined below. - * - * @throws sdbus::Error in case of failure - */ - virtual std::future callMethodAsync( const MethodCall& message - , uint64_t timeout - , with_future_t ) = 0; - - /*! - * @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t) - */ - template - std::future callMethodAsync( const MethodCall& message - , const std::chrono::duration<_Rep, _Period>& timeout - , with_future_t ); - /*! * @brief Calls method on the D-Bus object * @@ -392,47 +137,6 @@ namespace sdbus { */ [[nodiscard]] AsyncMethodInvoker callMethodAsync(const char* methodName); - /*! - * @brief Registers a handler for the desired signal emitted by the D-Bus object - * - * @param[in] interfaceName Name of an interface that the signal belongs to - * @param[in] signalName Name of the signal - * @param[in] signalHandler Callback that implements the body of the signal handler - * - * A signal can be subscribed to at any time during proxy lifetime. The subscription - * is active immediately after the call, and stays active for the entire lifetime - * of the Proxy object. - * - * To be able to unsubscribe from the signal at a later time, use the registerSignalHandler() - * overload with request_slot tag. - * - * @throws sdbus::Error in case of failure - */ - virtual void registerSignalHandler( const InterfaceName& interfaceName - , const SignalName& signalName - , signal_handler signalHandler ) = 0; - - /*! - * @brief Registers a handler for the desired signal emitted by the D-Bus object - * - * @param[in] interfaceName Name of an interface that the signal belongs to - * @param[in] signalName Name of the signal - * @param[in] signalHandler Callback that implements the body of the signal handler - * - * @return RAII-style slot handle representing the ownership of the subscription - * - * A signal can be subscribed to and unsubscribed from at any time during proxy - * lifetime. The subscription is active immediately after the call. The lifetime - * of the subscription is bound to the lifetime of the slot object. The subscription - * is unregistered by letting go of the slot object. - * - * @throws sdbus::Error in case of failure - */ - [[nodiscard]] virtual Slot registerSignalHandler( const InterfaceName& interfaceName - , const SignalName& signalName - , signal_handler signalHandler - , return_slot_t ) = 0; - /*! * @brief Registers signal handler for a given signal of the D-Bus object * @@ -469,17 +173,6 @@ namespace sdbus { */ [[nodiscard]] SignalSubscriber uponSignal(const char* signalName); - /*! - * @brief Unregisters proxy's signal handlers and stops receiving replies to pending async calls - * - * Unregistration is done automatically also in proxy's destructor. This method makes - * sense if, in the process of proxy removal, we need to make sure that callbacks - * are unregistered explicitly before the final destruction of the proxy instance. - * - * @throws sdbus::Error in case of failure - */ - virtual void unregister() = 0; - /*! * @brief Gets value of a property of the D-Bus object * @@ -646,7 +339,315 @@ namespace sdbus { */ [[nodiscard]] virtual Message getCurrentlyProcessedMessage() const = 0; - protected: + /*! + * @brief Unregisters proxy's signal handlers and stops receiving replies to pending async calls + * + * Unregistration is done automatically also in proxy's destructor. This method makes + * sense if, in the process of proxy removal, we need to make sure that callbacks + * are unregistered explicitly before the final destruction of the proxy instance. + * + * @throws sdbus::Error in case of failure + */ + virtual void unregister() = 0; + + public: // Lower-level, message-based API + /*! + * @brief Creates a method call message + * + * @param[in] interfaceName Name of an interface that provides a given method + * @param[in] methodName Name of the method + * @return A method call message + * + * Serialize method arguments into the returned message and invoke the method by passing + * the message with serialized arguments to the @c callMethod function. + * Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual MethodCall createMethodCall(const InterfaceName& interfaceName, const MethodName& methodName) = 0; + + /*! + * @brief Calls method on the remote D-Bus object + * + * @param[in] message Message representing a method call + * @return A method reply message + * + * The call does not block if the method call has dont-expect-reply flag set. In that case, + * the call returns immediately and the return value is an empty, invalid method reply. + * + * The call blocks otherwise, waiting for the remote peer to send back a reply or an error, + * or until the call times out. + * + * While blocking, other concurrent operations (in other threads) on the underlying bus + * connection are stalled until the call returns. This is not an issue in vast majority of + * (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving + * shared bus connections, this may be an issue. It is advised to instead use an asynchronous + * callMethod() function overload, which does not block the bus connection, or do the synchronous + * call from another Proxy instance created just before the call and then destroyed (which is + * anyway quite a typical approach in D-Bus implementations). Such proxy instance must have + * its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread` + * tag are designed for exactly that purpose. + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure (also in case the remote function returned an error) + */ + virtual MethodReply callMethod(const MethodCall& message) = 0; + + /*! + * @brief Calls method on the remote D-Bus object + * + * @param[in] message Message representing a method call + * @param[in] timeout Method call timeout (in microseconds) + * @return A method reply message + * + * The call does not block if the method call has dont-expect-reply flag set. In that case, + * the call returns immediately and the return value is an empty, invalid method reply. + * + * The call blocks otherwise, waiting for the remote peer to send back a reply or an error, + * or until the call times out. + * + * While blocking, other concurrent operations (in other threads) on the underlying bus + * connection are stalled until the call returns. This is not an issue in vast majority of + * (simple, single-threaded) applications. In asynchronous, multi-threaded designs involving + * shared bus connections, this may be an issue. It is advised to instead use an asynchronous + * callMethod() function overload, which does not block the bus connection, or do the synchronous + * call from another Proxy instance created just before the call and then destroyed (which is + * anyway quite a typical approach in D-Bus implementations). Such proxy instance must have + * its own bus connection. So-called light-weight proxies (ones created with `dont_run_event_loop_thread` + * tag are designed for exactly that purpose. + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure (also in case the remote function returned an error) + */ + virtual MethodReply callMethod(const MethodCall& message, uint64_t timeout) = 0; + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,uint64_t) + */ + template + MethodReply callMethod(const MethodCall& message, const std::chrono::duration<_Rep, _Period>& timeout); + + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @return Observing handle for the the pending asynchronous call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * An non-owning, observing async call handle is returned that can be used to query call status or cancel the call. + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual PendingAsyncCall callMethodAsync(const MethodCall& message, async_reply_handler asyncReplyCallback) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @return RAII-style slot handle representing the ownership of the async call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot. + * The slot can be used to cancel the method call at a later time by simply destroying it. + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , return_slot_t ) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously, with custom timeout + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @param[in] timeout Method call timeout (in microseconds) + * @return Observing handle for the the pending asynchronous call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * An non-owning, observing async call handle is returned that can be used to query call status or cancel the call. + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual PendingAsyncCall callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , uint64_t timeout ) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously, with custom timeout + * + * @param[in] message Message representing an async method call + * @param[in] asyncReplyCallback Handler for the async reply + * @param[in] timeout Method call timeout (in microseconds) + * @return RAII-style slot handle representing the ownership of the async call + * + * This is a callback-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided async reply handler will get invoked from the context of the bus + * connection I/O event loop thread. + * + * A slot (an owning handle) is returned for the async call. Lifetime of the call is bound to the lifetime of the slot. + * The slot can be used to cancel the method call at a later time by simply destroying it. + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use API on a higher level of abstraction defined below. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , uint64_t timeout + , return_slot_t ) = 0; + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t) + */ + template + PendingAsyncCall callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , const std::chrono::duration<_Rep, _Period>& timeout ); + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,async_reply_handler,uint64_t,return_slot_t) + */ + template + [[nodiscard]] Slot callMethodAsync( const MethodCall& message + , async_reply_handler asyncReplyCallback + , const std::chrono::duration<_Rep, _Period>& timeout + , return_slot_t ); + + /*! + * @brief Calls method on the D-Bus object asynchronously + * + * @param[in] message Message representing an async method call + * @param[in] Tag denoting a std::future-based overload + * @return Future object providing access to the future method reply message + * + * This is a std::future-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided future object will be set to contain the reply (or sdbus::Error + * in case the remote method threw an exception). + * + * The default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual std::future callMethodAsync(const MethodCall& message, with_future_t) = 0; + + /*! + * @brief Calls method on the D-Bus object asynchronously, with custom timeout + * + * @param[in] message Message representing an async method call + * @param[in] timeout Method call timeout + * @param[in] Tag denoting a std::future-based overload + * @return Future object providing access to the future method reply message + * + * This is a std::future-based way of asynchronously calling a remote D-Bus method. + * + * The call itself is non-blocking. It doesn't wait for the reply. Once the reply arrives, + * the provided future object will be set to contain the reply (or sdbus::Error + * in case the remote method threw an exception, or the call timed out). + * + * If timeout is zero, the default D-Bus method call timeout is used. See IConnection::getMethodCallTimeout(). + * + * Note: To avoid messing with messages, use higher-level API defined below. + * + * @throws sdbus::Error in case of failure + */ + virtual std::future callMethodAsync( const MethodCall& message + , uint64_t timeout + , with_future_t ) = 0; + + /*! + * @copydoc IProxy::callMethod(const MethodCall&,uint64_t,with_future_t) + */ + template + std::future callMethodAsync( const MethodCall& message + , const std::chrono::duration<_Rep, _Period>& timeout + , with_future_t ); + + /*! + * @brief Registers a handler for the desired signal emitted by the D-Bus object + * + * @param[in] interfaceName Name of an interface that the signal belongs to + * @param[in] signalName Name of the signal + * @param[in] signalHandler Callback that implements the body of the signal handler + * + * A signal can be subscribed to at any time during proxy lifetime. The subscription + * is active immediately after the call, and stays active for the entire lifetime + * of the Proxy object. + * + * To be able to unsubscribe from the signal at a later time, use the registerSignalHandler() + * overload with request_slot tag. + * + * @throws sdbus::Error in case of failure + */ + virtual void registerSignalHandler( const InterfaceName& interfaceName + , const SignalName& signalName + , signal_handler signalHandler ) = 0; + + /*! + * @brief Registers a handler for the desired signal emitted by the D-Bus object + * + * @param[in] interfaceName Name of an interface that the signal belongs to + * @param[in] signalName Name of the signal + * @param[in] signalHandler Callback that implements the body of the signal handler + * + * @return RAII-style slot handle representing the ownership of the subscription + * + * A signal can be subscribed to and unsubscribed from at any time during proxy + * lifetime. The subscription is active immediately after the call. The lifetime + * of the subscription is bound to the lifetime of the slot object. The subscription + * is unregistered by letting go of the slot object. + * + * @throws sdbus::Error in case of failure + */ + [[nodiscard]] virtual Slot registerSignalHandler( const InterfaceName& interfaceName + , const SignalName& signalName + , signal_handler signalHandler + , return_slot_t ) = 0; + + protected: // Internal API for efficiency reasons used by high-level API helper classes friend MethodInvoker; friend AsyncMethodInvoker; friend SignalSubscriber;