/** * (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland * (C) 2016 - 2024 Stanislav Angelovic * * @file IObject.h * * Created on: Nov 8, 2016 * 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 . */ #ifndef SDBUS_CXX_IOBJECT_H_ #define SDBUS_CXX_IOBJECT_H_ #include #include #include #include #include #include #include #include // Forward declarations namespace sdbus { class Signal; class IConnection; class ObjectPath; } namespace sdbus { /********************************************//** * @class IObject * * IObject class represents a D-Bus object instance identified by a specific object path. * D-Bus object provides its interfaces, methods, signals and properties on a bus * identified by a specific bus name. * * All IObject member methods throw @c sdbus::Error in case of D-Bus or sdbus-c++ error. * The IObject class has been designed as thread-aware. However, the operation of * creating and sending asynchronous method replies, as well as creating and emitting * signals, is thread-safe by design. * ***********************************************/ class IObject { 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] 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 */ [[nodiscard]] VTableAdder addVTable(std::vector vtable); /*! * @brief Adds a declaration of methods, properties and signals of the object at a given interface * * @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. * 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 */ template < typename... VTableItems , typename = std::enable_if_t<(is_one_of_variants_types> && ...)> > [[nodiscard]] VTableAdder addVTable(VTableItems&&... items); /*! * @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 = ...; * SignalName fooSignal{"fooSignal"}; * object_.emitSignal(fooSignal).onInterface("com.kistler.foo").withArguments(arg1, arg2); * @endcode * * @throws sdbus::Error in case of failure */ [[nodiscard]] SignalEmitter emitSignal(const SignalName& signalName); /*! * @copydoc IObject::emitSignal(const SignalName&) */ [[nodiscard]] SignalEmitter emitSignal(const std::string& signalName); /*! * @copydoc IObject::emitSignal(const SignalName&) */ [[nodiscard]] SignalEmitter emitSignal(const char* signalName); /*! * @brief Emits PropertyChanged signal for specified properties under a given interface of this object path * * @param[in] interfaceName Name of an interface that properties belong to * @param[in] propNames Names of properties that will be included in the PropertiesChanged signal * * @throws sdbus::Error in case of failure */ virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName, const std::vector& propNames) = 0; /*! * @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&,const std::vector&) */ virtual void emitPropertiesChangedSignal(const char* interfaceName, const std::vector& propNames) = 0; /*! * @brief Emits PropertyChanged signal for all properties on a given interface of this object path * * @param[in] interfaceName Name of an interface * * @throws sdbus::Error in case of failure */ virtual void emitPropertiesChangedSignal(const InterfaceName& interfaceName) = 0; /*! * @copydoc IObject::emitPropertiesChangedSignal(const InterfaceName&) */ virtual void emitPropertiesChangedSignal(const char* interfaceName) = 0; /*! * @brief Emits InterfacesAdded signal on this object path * * This emits an InterfacesAdded signal on this object path, by iterating all registered * interfaces on the path. All properties are queried and included in the signal. * This call is equivalent to emitInterfacesAddedSignal() with an explicit list of * registered interfaces. However, unlike emitInterfacesAddedSignal(interfaces), this * call can figure out the list of supported interfaces itself. Furthermore, it properly * adds the builtin org.freedesktop.DBus.* interfaces. * * @throws sdbus::Error in case of failure */ virtual void emitInterfacesAddedSignal() = 0; /*! * @brief Emits InterfacesAdded signal on this object path * * This emits an InterfacesAdded signal on this object path with explicitly provided list * of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable * object interfaces and their vtables, so this method now makes more sense. * * @throws sdbus::Error in case of failure */ virtual void emitInterfacesAddedSignal(const std::vector& interfaces) = 0; /*! * @brief Emits InterfacesRemoved signal on this object path * * This is like sd_bus_emit_object_added(), but emits an InterfacesRemoved signal on this * object path. This only includes any registered interfaces but skips the properties. * This function shall be called (just) before destroying the object. * * @throws sdbus::Error in case of failure */ virtual void emitInterfacesRemovedSignal() = 0; /*! * @brief Emits InterfacesRemoved signal on this object path * * This emits an InterfacesRemoved signal on the given path with explicitly provided list * of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable * object interfaces and their vtables, so this method now makes more sense. * * @throws sdbus::Error in case of failure */ virtual void emitInterfacesRemovedSignal(const std::vector& interfaces) = 0; /*! * @brief Adds an ObjectManager interface at the path of this D-Bus object * * 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 so-called floating registration. This means that * the ObjectManager interface stays there for the lifetime of the object. * * @throws sdbus::Error in case of failure */ virtual void addObjectManager() = 0; /*! * @brief Adds an ObjectManager interface at the path of this D-Bus object * * @return Slot handle owning the registration * * Creates an ObjectManager interface at the specified object path on * the connection. This is a convenient way to interrogate a connection * to see what objects it has. * * The lifetime of the ObjectManager interface is bound to the lifetime * of the returned slot instance. * * @throws sdbus::Error in case of failure */ [[nodiscard]] virtual Slot addObjectManager(return_slot_t) = 0; /*! * @brief Provides D-Bus connection used by the object * * @return Reference to the D-Bus connection */ [[nodiscard]] virtual sdbus::IConnection& getConnection() const = 0; /*! * @brief Returns object path of the underlying DBus object */ [[nodiscard]] virtual const ObjectPath& getObjectPath() const = 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 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) 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 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) const = 0; }; // Out-of-line member definitions inline SignalEmitter IObject::emitSignal(const SignalName& signalName) { return SignalEmitter(*this, signalName); } inline SignalEmitter IObject::emitSignal(const std::string& signalName) { return SignalEmitter(*this, signalName.c_str()); } inline SignalEmitter IObject::emitSignal(const char* signalName) { return SignalEmitter(*this, signalName); } template void IObject::addVTable(InterfaceName interfaceName, VTableItems&&... items) { addVTable(std::move(interfaceName), {std::forward(items)...}); } template VTableAdder IObject::addVTable(VTableItems&&... items) { return addVTable(std::vector{std::forward(items)...}); } inline VTableAdder IObject::addVTable(std::vector vtable) { return VTableAdder(*this, std::move(vtable)); } /*! * @brief Creates instance representing a D-Bus object * * @param[in] connection D-Bus connection to be used by the object * @param[in] objectPath Path of the D-Bus object * @return Pointer to the object representation instance * * The provided connection will be used by the object to export methods, * issue signals and provide properties. * * Creating a D-Bus object instance is (thread-)safe even upon the connection * which is already running its I/O event loop. * * Code example: * @code * auto proxy = sdbus::createObject(connection, "/com/kistler/foo"); * @endcode */ [[nodiscard]] std::unique_ptr createObject(sdbus::IConnection& connection, ObjectPath objectPath); } #include #include #endif /* SDBUS_CXX_IOBJECT_H_ */