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.
This commit is contained in:
Stanislav Angelovič
2023-12-30 18:40:38 +01:00
committed by Stanislav Angelovic
parent 29bae0aaa8
commit e3040c0998
27 changed files with 1047 additions and 1103 deletions

View File

@ -89,6 +89,8 @@ set(SDBUSCPP_HDR_SRCS
set(SDBUSCPP_PUBLIC_HDRS set(SDBUSCPP_PUBLIC_HDRS
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.h ${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.h
${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.inl ${SDBUSCPP_INCLUDE_DIR}/ConvenienceApiClasses.inl
${SDBUSCPP_INCLUDE_DIR}/VTableItems.h
${SDBUSCPP_INCLUDE_DIR}/VTableItems.inl
${SDBUSCPP_INCLUDE_DIR}/Error.h ${SDBUSCPP_INCLUDE_DIR}/Error.h
${SDBUSCPP_INCLUDE_DIR}/IConnection.h ${SDBUSCPP_INCLUDE_DIR}/IConnection.h
${SDBUSCPP_INCLUDE_DIR}/AdaptorInterfaces.h ${SDBUSCPP_INCLUDE_DIR}/AdaptorInterfaces.h

View File

@ -253,6 +253,7 @@ v2.x
- `PollData::getRelativeTimeout()` return type was changed to `std::chrono::microseconds`. - `PollData::getRelativeTimeout()` return type was changed to `std::chrono::microseconds`.
- `IConnection::processPendingRequest()` was renamed to `IConnection::processPendingEvent()`. - `IConnection::processPendingRequest()` was renamed to `IConnection::processPendingEvent()`.
- `Variant` constructor is now explicit. - `Variant` constructor is now explicit.
- Object D-Bus API registration is now done through `IObject::addVTable()` method. The registration is immediate; no `finishRegistration()` call is needed anymore.
- Systemd of at least v238 is required to compile sdbus-c++ - Systemd of at least v238 is required to compile sdbus-c++
- A proper fix for timeout handling - A 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) - 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)

View File

@ -306,16 +306,20 @@ int main(int argc, char *argv[])
// Register D-Bus methods and signals on the concatenator object, and exports the object. // Register D-Bus methods and signals on the concatenator object, and exports the object.
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->registerMethod(interfaceName, "concatenate", "ais", "s", &concatenate); concatenator->addVTable( sdbus::MethodVTableItem{"concatenate", "ais", {}, "s", {}, &concatenate, {}}
concatenator->registerSignal(interfaceName, "concatenated", "s"); , sdbus::SignalVTableItem{"concatenated", "s", {}, {}} )
concatenator->finishRegistration(); .forInterface(interfaceName);
// Run the I/O event loop on the bus connection. // Run the I/O event loop on the bus connection.
connection->enterEventLoop(); connection->enterEventLoop();
} }
``` ```
We establish a D-Bus system connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We register interfaces, its methods, signals that the object provides, and, through `finishRegistration()`, export the object (i.e., make it visible to clients) on the bus. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests. We establish a D-Bus system connection and request `org.sdbuscpp.concatenator` D-Bus name on it. This name will be used by D-Bus clients to find the service. We then create an object with path `/org/sdbuscpp/concatenator` on this connection. We add a so-called object vtable, where we declare and describe its D-Bus API, i.e. its interface, methods, signals, properties (if any) that the object provides. Then we need to make sure to run the event loop upon the connection, which handles all incoming, outgoing and other requests.
> **_Tip_:** There's also an overload of `addVTable()` method with `request_slot_t` tag parameter which returns a `Slot` object. The slot is a simple RAII-based handle of the associated vtable registration. As long as you keep the slot object, the vtable registration is active. When you let go of the slot, the vtable is automatically removed from the D-Bus object. This gives you the ability to implement "dynamic" D-Bus object API that is addable as well as removable at any time during object lifetime.
> **_Note_:** A D-Bus object can have any number of vtables attached to it. Even a D-Bus interface of an object can have multiple vtables attached to it.
The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall call)`. The `call` parameter is the incoming method call message. We need to deserialize our method input arguments from it. Then we can invoke the logic of the method and get the results. Then for the given `call`, we create a `reply` message, pack results into it and send it back to the caller through `send()`. (If we had a void-returning method, we'd just send an empty `reply` back.) We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`. The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall call)`. The `call` parameter is the incoming method call message. We need to deserialize our method input arguments from it. Then we can invoke the logic of the method and get the results. Then for the given `call`, we create a `reply` message, pack results into it and send it back to the caller through `send()`. (If we had a void-returning method, we'd just send an empty `reply` back.) We also fire a signal with the results. To do this, we need to create a signal message via object's `createSignal()`, serialize the results into it, and then send it out to subscribers by invoking object's `emitSignal()`.
@ -365,7 +369,7 @@ int main(int argc, char *argv[])
assert(result == "1:2:3"); assert(result == "1:2:3");
} }
// Invoke concatenate again, this time with no numbers and we shall get an error / // Invoke concatenate again, this time with no numbers and we shall get an error
{ {
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate"); auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
method << std::vector<int>() << separator; method << std::vector<int>() << separator;
@ -526,15 +530,19 @@ int main(int argc, char *argv[])
// Register D-Bus methods and signals on the concatenator object, and exports the object. // Register D-Bus methods and signals on the concatenator object, and exports the object.
const char* interfaceName = "org.sdbuscpp.Concatenator"; const char* interfaceName = "org.sdbuscpp.Concatenator";
concatenator->registerMethod("concatenate").onInterface(interfaceName).implementedAs(std::move(concatenate)); concatenator->addVTable( sdbus::registerMethod("concatenate").implementedAs(std::move(concatenate))
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>(); , sdbus::registerSignal{"concatenated").withParameters<std::string>() )
concatenator->finishRegistration(); .forInterface(interfaceName);
// Run the loop on the connection. // Run the loop on the connection.
connection->enterEventLoop(); connection->enterEventLoop();
} }
``` ```
> **_Tip_:** There's also an overload of `addVTable(...).forInterface()` method with `request_slot_t` tag parameter which returns a `Slot` object. The slot is a simple RAII-based handle of the associated vtable registration. As long as you keep the slot object, the vtable registration is active. When you let go of the slot, the vtable is automatically removed from the D-Bus object. This gives you the ability to implement "dynamic" D-Bus object API that is addable as well as removable at any time during object lifetime.
> **_Note_:** A D-Bus object can have any number of vtables attached to it. Even a D-Bus interface of an object can have multiple vtables attached to it.
### Client side ### Client side
```c++ ```c++

View File

@ -21,14 +21,24 @@ public:
protected: protected:
Planet1_adaptor(sdbus::IObject& object) Planet1_adaptor(sdbus::IObject& object)
: object_(object) : 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&&) = default;
Planet1_adaptor& operator=(Planet1_adaptor&&) = default;
~Planet1_adaptor() = default; ~Planet1_adaptor() = default;
void registerAdaptor()
{
object_->addVTable( sdbus::registerMethod("GetPopulation").withOutputParamNames("population").implementedAs([this](){ return this->GetPopulation(); })
, sdbus::registerProperty("Name").withGetter([this](){ return this->Name(); })
).forInterface(INTERFACE_NAME);
}
private: private:
virtual uint64_t GetPopulation() = 0; virtual uint64_t GetPopulation() = 0;
@ -36,7 +46,7 @@ private:
virtual std::string Name() = 0; virtual std::string Name() = 0;
private: private:
sdbus::IObject& object_; sdbus::IObject* object_;
}; };
}}} // namespaces }}} // namespaces

View File

@ -19,11 +19,11 @@
#include <thread> #include <thread>
#include <chrono> #include <chrono>
class ManagerAdaptor : public sdbus::AdaptorInterfaces< sdbus::ObjectManager_adaptor > class ManagerAdaptor : public sdbus::AdaptorInterfaces<sdbus::ObjectManager_adaptor>
{ {
public: public:
ManagerAdaptor(sdbus::IConnection& connection, std::string path) ManagerAdaptor(sdbus::IConnection& connection, std::string path)
: AdaptorInterfaces(connection, std::move(path)) : AdaptorInterfaces(connection, std::move(path))
{ {
registerAdaptor(); registerAdaptor();
} }
@ -34,15 +34,15 @@ public:
} }
}; };
class PlanetAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor, class PlanetAdaptor final : public sdbus::AdaptorInterfaces< org::sdbuscpp::ExampleManager::Planet1_adaptor
sdbus::ManagedObject_adaptor, , sdbus::ManagedObject_adaptor
sdbus::Properties_adaptor > , sdbus::Properties_adaptor >
{ {
public: public:
PlanetAdaptor(sdbus::IConnection& connection, std::string path, std::string name, uint64_t poulation) PlanetAdaptor(sdbus::IConnection& connection, std::string path, std::string name, uint64_t population)
: AdaptorInterfaces(connection, std::move(path)) : AdaptorInterfaces(connection, std::move(path))
, m_name(std::move(name)) , m_name(std::move(name))
, m_population(poulation) , m_population(population)
{ {
registerAdaptor(); registerAdaptor();
emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME}); emitInterfacesAddedSignal({org::sdbuscpp::ExampleManager::Planet1_adaptor::INTERFACE_NAME});
@ -105,4 +105,4 @@ int main()
connection->releaseName("org.sdbuscpp.examplemanager"); connection->releaseName("org.sdbuscpp.examplemanager");
connection->leaveEventLoop(); connection->leaveEventLoop();
return 0; return 0;
} }

View File

@ -82,9 +82,10 @@ namespace sdbus {
* adaptor-side interface classes representing interfaces (with methods, signals and properties) * adaptor-side interface classes representing interfaces (with methods, signals and properties)
* of the D-Bus object. * of the D-Bus object.
* *
* In the final adaptor class inherited from AdaptorInterfaces, it is necessary to finish * In the final adaptor class inherited from AdaptorInterfaces, one needs to make sure:
* adaptor registration in class constructor (finishRegistration();`), and, conversely, * 1. to call `registerAdaptor();` in the class constructor, and, conversely,
* unregister the adaptor in class destructor (`unregister();`). * 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>
@ -108,15 +109,15 @@ namespace sdbus {
} }
/*! /*!
* @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. * 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() void registerAdaptor()
{ {
getObject().finishRegistration(); (_Interfaces::registerAdaptor(), ...);
} }
/*! /*!
@ -132,12 +133,9 @@ namespace sdbus {
} }
/*! /*!
* @brief Returns object path of the underlying DBus object * @brief Returns reference to the underlying IObject instance
*/ */
const std::string& getObjectPath() const using ObjectHolder::getObject;
{
return getObject().getObjectPath();
}
protected: protected:
using base_type = AdaptorInterfaces; using base_type = AdaptorInterfaces;

View File

@ -27,13 +27,13 @@
#ifndef SDBUS_CXX_CONVENIENCEAPICLASSES_H_ #ifndef SDBUS_CXX_CONVENIENCEAPICLASSES_H_
#define SDBUS_CXX_CONVENIENCEAPICLASSES_H_ #define SDBUS_CXX_CONVENIENCEAPICLASSES_H_
#include <sdbus-c++/VTableItems.h>
#include <sdbus-c++/Message.h> #include <sdbus-c++/Message.h>
#include <sdbus-c++/TypeTraits.h> #include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Types.h> #include <sdbus-c++/Types.h>
#include <sdbus-c++/Flags.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <type_traits> #include <map>
#include <chrono> #include <chrono>
#include <future> #include <future>
#include <cstdint> #include <cstdint>
@ -48,101 +48,16 @@ namespace sdbus {
namespace sdbus { namespace sdbus {
class MethodRegistrator class VTableAdder
{ {
public: public:
MethodRegistrator(IObject& object, std::string methodName); VTableAdder(IObject& object, std::vector<VTableItem> vtable);
MethodRegistrator(MethodRegistrator&& other) = default; void forInterface(std::string interfaceName);
~MethodRegistrator() noexcept(false); [[nodiscard]] Slot forInterface(std::string interfaceName, request_slot_t);
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();
private: private:
IObject& object_; IObject& object_;
std::string methodName_; std::vector<VTableItem> vtable_;
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
};
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
}; };
class SignalEmitter class SignalEmitter
@ -313,6 +228,6 @@ namespace sdbus {
const std::string* interfaceName_{}; const std::string* interfaceName_{};
}; };
} } // namespace sdbus
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */ #endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */

View File

@ -34,6 +34,7 @@
#include <sdbus-c++/Types.h> #include <sdbus-c++/Types.h>
#include <sdbus-c++/TypeTraits.h> #include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Error.h> #include <sdbus-c++/Error.h>
#include <type_traits>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <exception> #include <exception>
@ -41,374 +42,24 @@
namespace sdbus { namespace sdbus {
/*** ----------------- ***/ /*** ------------- ***/
/*** MethodRegistrator ***/ /*** VTableAdder ***/
/*** ----------------- ***/ /*** ------------- ***/
inline MethodRegistrator::MethodRegistrator(IObject& object, std::string methodName) inline VTableAdder::VTableAdder(IObject& object, std::vector<VTableItem> vtable)
: object_(object) : object_(object)
, methodName_(std::move(methodName)) , vtable_(std::move(vtable))
, exceptions_(std::uncaught_exceptions())
{ {
} }
inline MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must inline void VTableAdder::forInterface(std::string interfaceName)
{ // explicitly be allowed to throw
// Don't register the method if MethodRegistrator threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
assert(methodCallback_); // implementedAs() must be placed/called prior to this function
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerMethod( interfaceName_
, std::move(methodName_)
, std::move(inputSignature_)
, inputParamNames_
, std::move(outputSignature_)
, outputParamNames_
, std::move(methodCallback_)
, std::move(flags_));
}
inline MethodRegistrator& MethodRegistrator::onInterface(std::string interfaceName)
{ {
interfaceName_ = std::move(interfaceName); object_.addVTable(std::move(interfaceName), std::move(vtable_));
return *this;
} }
template <typename _Function> inline Slot VTableAdder::forInterface(std::string interfaceName, request_slot_t)
MethodRegistrator& MethodRegistrator::implementedAs(_Function&& callback)
{ {
inputSignature_ = signature_of_function_input_arguments<_Function>::str(); return object_.addVTable(std::move(interfaceName), std::move(vtable_), request_slot);
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
methodCallback_ = [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 MethodRegistrator& MethodRegistrator::withInputParamNames(std::vector<std::string> paramNames)
{
inputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline MethodRegistrator& MethodRegistrator::withInputParamNames(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withInputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(std::vector<std::string> paramNames)
{
outputParamNames_ = std::move(paramNames);
return *this;
}
template <typename... _String>
inline MethodRegistrator& MethodRegistrator::withOutputParamNames(_String... paramNames)
{
static_assert(std::conjunction_v<std::is_convertible<_String, std::string>...>, "Parameter names must be (convertible to) strings");
return withOutputParamNames({paramNames...});
}
inline MethodRegistrator& MethodRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
inline MethodRegistrator& MethodRegistrator::markAsPrivileged()
{
flags_.set(Flags::PRIVILEGED);
return *this;
}
inline MethodRegistrator& MethodRegistrator::withNoReply()
{
flags_.set(Flags::METHOD_NO_REPLY);
return *this;
}
/*** ----------------- ***/
/*** SignalRegistrator ***/
/*** ----------------- ***/
inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
: object_(object)
, signalName_(std::move(signalName))
, exceptions_(std::uncaught_exceptions())
{
}
inline SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerSignal( interfaceName_
, std::move(signalName_)
, std::move(signalSignature_)
, paramNames_
, std::move(flags_) );
}
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
{
interfaceName_ = std::move(interfaceName);
return *this;
}
template <typename... _Args>
inline SignalRegistrator& SignalRegistrator::withParameters()
{
signalSignature_ = signature_of_function_input_arguments<void(_Args...)>::str();
return *this;
}
template <typename... _Args>
inline SignalRegistrator& SignalRegistrator::withParameters(std::vector<std::string> paramNames)
{
paramNames_ = std::move(paramNames);
return withParameters<_Args...>();
}
template <typename... _Args, typename... _String>
inline SignalRegistrator& SignalRegistrator::withParameters(_String... paramNames)
{
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...>({paramNames...});
}
inline SignalRegistrator& SignalRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
/*** ------------------- ***/
/*** PropertyRegistrator ***/
/*** ------------------- ***/
inline PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
: object_(object)
, propertyName_(propertyName)
, exceptions_(std::uncaught_exceptions())
{
}
inline PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
assert(!interfaceName_.empty()); // onInterface() must be placed/called prior to this function
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerProperty() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.registerProperty( interfaceName_
, propertyName_
, propertySignature_
, std::move(getter_)
, std::move(setter_)
, flags_ );
}
inline PropertyRegistrator& PropertyRegistrator::onInterface(std::string interfaceName)
{
interfaceName_ = std::move(interfaceName);
return *this;
}
template <typename _Function>
inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
{
static_assert(function_argument_count_v<_Function> == 0, "Property getter function must not take any arguments");
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
if (propertySignature_.empty())
propertySignature_ = signature_of_function_output_arguments<_Function>::str();
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 PropertyRegistrator& PropertyRegistrator::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<function_result_t<_Function>>::value, "Property setter function must not return any value");
if (propertySignature_.empty())
propertySignature_ = signature_of_function_input_arguments<_Function>::str();
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 PropertyRegistrator& PropertyRegistrator::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
inline PropertyRegistrator& PropertyRegistrator::markAsPrivileged()
{
flags_.set(Flags::PRIVILEGED);
return *this;
}
inline PropertyRegistrator& PropertyRegistrator::withUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
{
flags_.set(behavior);
return *this;
}
/*** -------------------- ***/
/*** InterfaceFlagsSetter ***/
/*** -------------------- ***/
inline InterfaceFlagsSetter::InterfaceFlagsSetter(IObject& object, const std::string& interfaceName)
: object_(object)
, interfaceName_(interfaceName)
, exceptions_(std::uncaught_exceptions())
{
}
inline InterfaceFlagsSetter::~InterfaceFlagsSetter() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't set any flags if InterfaceFlagsSetter threw an exception in one of its methods
if (std::uncaught_exceptions() != exceptions_)
return;
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow setInterfaceFlags() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
object_.setInterfaceFlags(interfaceName_, std::move(flags_));
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsDeprecated()
{
flags_.set(Flags::DEPRECATED);
return *this;
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::markAsPrivileged()
{
flags_.set(Flags::PRIVILEGED);
return *this;
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::withNoReplyMethods()
{
flags_.set(Flags::METHOD_NO_REPLY);
return *this;
}
inline InterfaceFlagsSetter& InterfaceFlagsSetter::withPropertyUpdateBehavior(Flags::PropertyUpdateBehaviorFlags behavior)
{
flags_.set(behavior);
return *this;
} }
/*** ------------- ***/ /*** ------------- ***/
@ -930,6 +581,6 @@ namespace sdbus {
.getResultAsFuture<std::map<std::string, Variant>>(); .getResultAsFuture<std::map<std::string, Variant>>();
} }
} } // namespace sdbus
#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */ #endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */

View File

@ -27,6 +27,7 @@
#ifndef SDBUS_CXX_IOBJECT_H_ #ifndef SDBUS_CXX_IOBJECT_H_
#define SDBUS_CXX_IOBJECT_H_ #define SDBUS_CXX_IOBJECT_H_
#include <sdbus-c++/VTableItems.h>
#include <sdbus-c++/ConvenienceApiClasses.h> #include <sdbus-c++/ConvenienceApiClasses.h>
#include <sdbus-c++/TypeTraits.h> #include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Flags.h> #include <sdbus-c++/Flags.h>
@ -62,144 +63,104 @@ namespace sdbus {
virtual ~IObject() = default; 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] interfaceName Name of an interface the the vtable is registered for
* @param[in] methodName Name of the method * @param[in] items Individual instances of VTable item structures
* @param[in] inputSignature D-Bus signature of method input parameters *
* @param[in] outputSignature D-Bus signature of method output parameters * This method is used to declare attributes for the object under the given interface.
* @param[in] methodCallback Callback that implements the body of the method * Parameter `items' represents a vtable definition that may contain method declarations
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply) * (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 * @throws sdbus::Error in case of failure
*/ */
virtual void registerMethod( const std::string& interfaceName template < typename... VTableItems
, std::string methodName , typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
, std::string inputSignature void addVTable(std::string interfaceName, VTableItems&&... items);
, std::string outputSignature
, method_callback methodCallback
, Flags flags = {} ) = 0;
/*! /*!
* @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] interfaceName Name of an interface the the vtable is registered for
* @param[in] methodName Name of the method * @param[in] vtable A list of individual descriptions in the form of VTable item instances
* @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)
* *
* Provided names of input and output parameters will be included in the introspection * This method is used to declare attributes for the object under the given interface.
* description (given that at least version 242 of underlying libsystemd library is * The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
* used; otherwise, names of parameters are ignored). This usually helps better describe * property declarations (using PropertyVTableItem struct), signal declarations (using
* the API to the introspector. * 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 * @throws sdbus::Error in case of failure
*/ */
virtual void registerMethod( const std::string& interfaceName virtual void addVTable(std::string interfaceName, std::vector<VTableItem> vtable) = 0;
, 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;
/*! /*!
* @brief Registers signal that the object will emit 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 signal will fall under * @param[in] interfaceName Name of an interface the the vtable is registered for
* @param[in] signalName Name of the signal * @param[in] vtable A list of individual descriptions in the form of VTable item instances
* @param[in] signature D-Bus signature of signal parameters *
* @param[in] flags D-Bus signal flags (deprecated) * 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 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
* 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 * @throws sdbus::Error in case of failure
*/ */
virtual void registerSignal( const std::string& interfaceName virtual Slot addVTable(std::string interfaceName, std::vector<VTableItem> vtable, request_slot_t) = 0;
, std::string signalName
, std::string signature
, Flags flags = {} ) = 0;
/*! /*!
* @brief Registers signal that the object will emit on D-Bus * @brief A little more convenient overload of addVTable() above
* *
* @param[in] interfaceName Name of an interface that the signal will fall under * This version allows method chaining for a little safer and more readable VTable registration.
* @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 * See addVTable() overloads above for detailed documentation.
* 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
*/ */
virtual void registerSignal( const std::string& interfaceName template < typename... VTableItems
, std::string signalName , typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
, std::string signature [[nodiscard]] VTableAdder addVTable(VTableItems&&... items);
, const std::vector<std::string>& paramNames
, Flags flags = {} ) = 0;
/*! /*!
* @brief Registers read-only property that the object will provide on D-Bus * @brief A little more convenient overload of addVTable() above
* *
* @param[in] interfaceName Name of an interface that the property will fall under * This version allows method chaining for a little safer and more readable VTable registration.
* @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 * See addVTable() overloads above for detailed documentation.
*/ */
virtual void registerProperty( const std::string& interfaceName [[nodiscard]] VTableAdder addVTable(std::vector<VTableItem> vtable);
, 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 * @brief Unregisters object's API and removes object from the bus
@ -276,10 +237,8 @@ namespace sdbus {
* @brief Emits InterfacesAdded signal on this object path * @brief Emits InterfacesAdded signal on this object path
* *
* This emits an InterfacesAdded signal on this object path with explicitly provided list * 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 * of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
* interfaces of an existing object at run time (an object has a fixed set of interfaces * object interfaces and their vtables, so this method now makes more sense.
* registered by the time of invoking finishRegistration()), emitInterfacesAddedSignal(void)
* is probably what you are looking for.
* *
* @throws sdbus::Error in case of failure * @throws sdbus::Error in case of failure
*/ */
@ -300,10 +259,8 @@ namespace sdbus {
* @brief Emits InterfacesRemoved signal on this object path * @brief Emits InterfacesRemoved signal on this object path
* *
* This emits an InterfacesRemoved signal on the given path with explicitly provided list * 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 * of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
* interfaces of an existing object at run time (an object has a fixed set of interfaces * object interfaces and their vtables, so this method now makes more sense.
* registered by the time of invoking finishRegistration()), emitInterfacesRemovedSignal(void)
* is probably what you are looking for.
* *
* @throws sdbus::Error in case of failure * @throws sdbus::Error in case of failure
*/ */
@ -340,81 +297,6 @@ namespace sdbus {
*/ */
virtual sdbus::IConnection& getConnection() const = 0; 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 * @brief Emits signal on D-Bus
* *
@ -459,31 +341,28 @@ namespace sdbus {
// Out-of-line member definitions // Out-of-line member definitions
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
{
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);
}
inline SignalEmitter IObject::emitSignal(const std::string& signalName) inline SignalEmitter IObject::emitSignal(const std::string& signalName)
{ {
return SignalEmitter(*this, signalName); return SignalEmitter(*this, signalName);
} }
template <typename... VTableItems, typename>
void IObject::addVTable(std::string 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 VTableAdder(*this, std::move(vtable));
}
/*! /*!
* @brief Creates instance representing a D-Bus object * @brief Creates instance representing a D-Bus object
* *
@ -506,6 +385,7 @@ namespace sdbus {
} }
#include <sdbus-c++/VTableItems.inl>
#include <sdbus-c++/ConvenienceApiClasses.inl> #include <sdbus-c++/ConvenienceApiClasses.inl>
#endif /* SDBUS_CXX_IOBJECT_H_ */ #endif /* SDBUS_CXX_IOBJECT_H_ */

View File

@ -197,12 +197,9 @@ namespace sdbus {
} }
/*! /*!
* @brief Returns object path of the underlying DBus object * @brief Returns reference to the underlying IProxy instance
*/ */
const std::string& getObjectPath() const using ProxyObjectHolder::getProxy;
{
return getProxy().getObjectPath();
}
protected: protected:
using base_type = ProxyInterfaces; using base_type = ProxyInterfaces;

View File

@ -251,8 +251,7 @@ namespace sdbus {
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties"; static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
protected: protected:
Properties_adaptor(sdbus::IObject& object) Properties_adaptor(sdbus::IObject& object) : object_(&object)
: object_(&object)
{ {
} }
@ -263,6 +262,10 @@ namespace sdbus {
~Properties_adaptor() = default; ~Properties_adaptor() = default;
void registerAdaptor()
{
}
public: public:
void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties) void emitPropertiesChangedSignal(const std::string& interfaceName, const std::vector<std::string>& properties)
{ {
@ -293,10 +296,8 @@ namespace sdbus {
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager"; static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.ObjectManager";
protected: protected:
explicit ObjectManager_adaptor(sdbus::IObject& object) explicit ObjectManager_adaptor(sdbus::IObject& object) : object_(&object)
: object_(&object)
{ {
object_->addObjectManager();
} }
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete; ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
@ -306,6 +307,11 @@ namespace sdbus {
~ObjectManager_adaptor() = default; ~ObjectManager_adaptor() = default;
void registerAdaptor()
{
object_->addObjectManager();
}
private: private:
sdbus::IObject* object_; sdbus::IObject* object_;
}; };
@ -336,6 +342,10 @@ namespace sdbus {
~ManagedObject_adaptor() = default; ~ManagedObject_adaptor() = default;
void registerAdaptor()
{
}
public: public:
/*! /*!
* @brief Emits InterfacesAdded signal for this object path * @brief Emits InterfacesAdded signal for this object path

View File

@ -40,6 +40,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <tuple> #include <tuple>
#include <variant>
// Forward declarations // Forward declarations
namespace sdbus { namespace sdbus {
@ -646,6 +647,15 @@ namespace sdbus {
using future_return_t = typename future_return<_Args...>::type; using future_return_t = typename future_return<_Args...>::type;
// Credit: Piotr Skotnicki (https://stackoverflow.com/a/57639506)
template <typename, typename>
constexpr bool is_one_of_variants_types = false;
template <typename... _VariantTypes, typename _QueriedType>
constexpr bool is_one_of_variants_types<std::variant<_VariantTypes...>, _QueriedType>
= (std::is_same_v<_QueriedType, _VariantTypes> || ...);
namespace detail namespace detail
{ {
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I> template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>

View File

@ -0,0 +1,107 @@
/**
* (C) 2023 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++/TypeTraits.h>
#include <sdbus-c++/Flags.h>
#include <string>
#include <vector>
#include <variant>
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();
std::string name;
std::string inputSignature;
std::vector<std::string> inputParamNames;
std::string outputSignature;
std::vector<std::string> outputParamNames;
method_callback callbackHandler;
Flags flags;
};
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();
std::string name;
std::string signature;
std::vector<std::string> paramNames;
Flags flags;
};
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);
std::string name;
std::string signature;
property_get_callback getter;
property_set_callback setter;
Flags flags;
};
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_ */

View File

@ -0,0 +1,285 @@
/**
* (C) 2023 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++/TypeTraits.h>
#include <sdbus-c++/Error.h>
#include <type_traits>
#include <string>
#include <vector>
namespace sdbus {
/*** -------------------- ***/
/*** Method VTable Item ***/
/*** -------------------- ***/
template <typename _Function>
MethodVTableItem& MethodVTableItem::implementedAs(_Function&& callback)
{
inputSignature = signature_of_function_input_arguments<_Function>::str();
outputSignature = signature_of_function_output_arguments<_Function>::str();
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(std::string methodName)
{
return {std::move(methodName), {}, {}, {}, {}, {}, {}};
}
/*** -------------------- ***/
/*** Signal VTable Item ***/
/*** -------------------- ***/
template <typename... _Args>
inline SignalVTableItem& SignalVTableItem::withParameters()
{
signature = signature_of_function_input_arguments<void(_Args...)>::str();
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(std::string signalName)
{
return {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<function_result_t<_Function>>::value, "Property getter function must return property value");
if (signature.empty())
signature = signature_of_function_output_arguments<_Function>::str();
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<function_result_t<_Function>>::value, "Property setter function must not return any value");
if (signature.empty())
signature = signature_of_function_input_arguments<_Function>::str();
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(std::string propertyName)
{
return {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_ */

View File

@ -470,7 +470,7 @@ Slot Connection::addObjectVTable( const std::string& objectPath
{ {
sd_bus_slot *slot{}; sd_bus_slot *slot{};
auto r = sdbus_->sd_bus_add_object_vtable(bus_.get() auto r = sdbus_->sd_bus_add_object_vtable( bus_.get()
, &slot , &slot
, objectPath.c_str() , objectPath.c_str()
, interfaceName.c_str() , interfaceName.c_str()
@ -500,7 +500,7 @@ MethodCall Connection::createMethodCall( const std::string& destination
{ {
sd_bus_message *sdbusMsg{}; sd_bus_message *sdbusMsg{};
auto r = sdbus_->sd_bus_message_new_method_call(bus_.get() auto r = sdbus_->sd_bus_message_new_method_call( bus_.get()
, &sdbusMsg , &sdbusMsg
, destination.empty() ? nullptr : destination.c_str() , destination.empty() ? nullptr : destination.c_str()
, objectPath.c_str() , objectPath.c_str()
@ -518,7 +518,7 @@ Signal Connection::createSignal( const std::string& objectPath
{ {
sd_bus_message *sdbusMsg{}; sd_bus_message *sdbusMsg{};
auto r = sdbus_->sd_bus_message_new_signal(bus_.get() auto r = sdbus_->sd_bus_message_new_signal( bus_.get()
, &sdbusMsg , &sdbusMsg
, objectPath.c_str() , objectPath.c_str()
, interfaceName.c_str() , interfaceName.c_str()
@ -575,7 +575,7 @@ void Connection::emitPropertiesChangedSignal( const std::string& objectPath
{ {
auto names = to_strv(propNames); auto names = to_strv(propNames);
auto r = sdbus_->sd_bus_emit_properties_changed_strv(bus_.get() auto r = sdbus_->sd_bus_emit_properties_changed_strv( bus_.get()
, objectPath.c_str() , objectPath.c_str()
, interfaceName.c_str() , interfaceName.c_str()
, propNames.empty() ? nullptr : &names[0] ); , propNames.empty() ? nullptr : &names[0] );
@ -595,7 +595,7 @@ void Connection::emitInterfacesAddedSignal( const std::string& objectPath
{ {
auto names = to_strv(interfaces); auto names = to_strv(interfaces);
auto r = sdbus_->sd_bus_emit_interfaces_added_strv(bus_.get() auto r = sdbus_->sd_bus_emit_interfaces_added_strv( bus_.get()
, objectPath.c_str() , objectPath.c_str()
, interfaces.empty() ? nullptr : &names[0] ); , interfaces.empty() ? nullptr : &names[0] );
@ -614,7 +614,7 @@ void Connection::emitInterfacesRemovedSignal( const std::string& objectPath
{ {
auto names = to_strv(interfaces); auto names = to_strv(interfaces);
auto r = sdbus_->sd_bus_emit_interfaces_removed_strv(bus_.get() auto r = sdbus_->sd_bus_emit_interfaces_removed_strv( bus_.get()
, objectPath.c_str() , objectPath.c_str()
, interfaces.empty() ? nullptr : &names[0] ); , interfaces.empty() ? nullptr : &names[0] );

View File

@ -47,129 +47,33 @@ Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
SDBUS_CHECK_OBJECT_PATH(objectPath_); SDBUS_CHECK_OBJECT_PATH(objectPath_);
} }
void Object::registerMethod( const std::string& interfaceName void Object::addVTable(std::string interfaceName, std::vector<VTableItem> vtable)
, std::string methodName
, std::string inputSignature
, std::string outputSignature
, method_callback methodCallback
, Flags flags )
{ {
registerMethod( interfaceName auto slot = Object::addVTable(std::move(interfaceName), std::move(vtable), request_slot);
, std::move(methodName)
, std::move(inputSignature) vtables_.push_back(std::move(slot));
, {}
, std::move(outputSignature)
, {}
, std::move(methodCallback)
, std::move(flags) );
} }
void Object::registerMethod( const std::string& interfaceName Slot Object::addVTable(std::string interfaceName, std::vector<VTableItem> vtable, request_slot_t)
, 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 )
{ {
SDBUS_CHECK_INTERFACE_NAME(interfaceName); SDBUS_CHECK_INTERFACE_NAME(interfaceName);
SDBUS_CHECK_MEMBER_NAME(methodName);
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
auto& interface = getInterface(interfaceName); // 1st pass -- create vtable structure for internal sdbus-c++ purposes
InterfaceData::MethodData methodData{ std::move(inputSignature) auto internalVTable = std::make_unique<VTable>(createInternalVTable(std::move(interfaceName), std::move(vtable)));
, 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;
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 // 3rd step -- register the vtable with sd-bus
, std::string signalName internalVTable->slot = connection_.addObjectVTable(objectPath_, internalVTable->interfaceName, &internalVTable->sdbusVTable[0], internalVTable.get());
, std::string signature
, Flags flags )
{
registerSignal(interfaceName, std::move(signalName), std::move(signature), {}, std::move(flags));
}
void Object::registerSignal( const std::string& interfaceName // Return vtable wrapped in a Slot object
, std::string signalName return {internalVTable.release(), [](void *ptr){ delete static_cast<VTable*>(ptr); }};
, 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);
}
} }
void Object::unregister() void Object::unregister()
{ {
interfaces_.clear(); vtables_.clear();
removeObjectManager(); removeObjectManager();
} }
@ -245,81 +149,151 @@ Message Object::getCurrentlyProcessedMessage() const
return connection_.getCurrentlyProcessedMessage(); return connection_.getCurrentlyProcessedMessage();
} }
Object::InterfaceData& Object::getInterface(const std::string& interfaceName) Object::VTable Object::createInternalVTable(std::string interfaceName, std::vector<VTableItem> vtable)
{ {
return interfaces_.emplace(interfaceName, *this).first->second; VTable internalVTable;
}
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData) internalVTable.interfaceName = std::move(interfaceName);
{
auto& vtable = interfaceData.vtable;
assert(vtable.empty());
vtable.push_back(createVTableStartItem(interfaceData.flags.toSdBusInterfaceFlags())); for (auto& vtableItem : vtable)
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)
{ {
const auto& methodName = item.first; std::visit( overload{ [&](InterfaceFlagsVTableItem&& interfaceFlags){ writeInterfaceFlagsToVTable(std::move(interfaceFlags), internalVTable); }
const auto& methodData = item.second; , [&](MethodVTableItem&& method){ writeMethodRecordToVTable(std::move(method), internalVTable); }
, [&](SignalVTableItem&& signal){ writeSignalRecordToVTable(std::move(signal), internalVTable); }
vtable.push_back(createVTableMethodItem( methodName.c_str() , [&](PropertyVTableItem&& property){ writePropertyRecordToVTable(std::move(property), internalVTable); } }
, methodData.inputArgs.c_str() , std::move(vtableItem) );
, methodData.outputArgs.c_str()
, methodData.paramNames.c_str()
, &Object::sdbus_method_callback
, methodData.flags.toSdBusMethodFlags() ));
} }
// 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& a, const auto& b){ return a.name < b.name; });
std::sort(internalVTable.signals.begin(), internalVTable.signals.end(), [](const auto& a, const auto& b){ return a.name < b.name; });
std::sort(internalVTable.properties.begin(), internalVTable.properties.end(), [](const auto& a, const auto& b){ return a.name < b.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);
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);
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);
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.inputArgs.c_str()
, method.outputArgs.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, const std::string& methodName)
{
auto it = std::lower_bound(vtable.methods.begin(), vtable.methods.end(), methodName, [](const auto& methodItem, const auto& methodName)
{ {
const auto& signalName = item.first; return methodItem.name < methodName;
const auto& signalData = item.second; });
vtable.push_back(createVTableSignalItem( signalName.c_str() return it != vtable.methods.end() && it->name == methodName ? &*it : nullptr;
, signalData.signature.c_str()
, signalData.paramNames.c_str()
, signalData.flags.toSdBusSignalFlags() ));
}
} }
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable) const Object::VTable::PropertyItem* Object::findProperty(const VTable& vtable, const std::string& 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; return propertyItem.name < propertyName;
const auto& propertyData = item.second; });
if (!propertyData.setCallback) return it != vtable.properties.end() && it->name == propertyName ? &*it : nullptr;
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);
} }
std::string Object::paramNamesToString(const std::vector<std::string>& paramNames) std::string Object::paramNamesToString(const std::vector<std::string>& paramNames)
@ -332,16 +306,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) int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
{ {
auto* interfaceData = static_cast<InterfaceData*>(userData); auto* vtable = static_cast<VTable*>(userData);
assert(interfaceData != nullptr); assert(vtable != nullptr);
auto& object = interfaceData->object; assert(vtable->object != nullptr);
auto message = Message::Factory::create<MethodCall>(sdbusMessage, &object.connection_.getSdBusInterface()); auto message = Message::Factory::create<MethodCall>(sdbusMessage, &vtable->object->connection_.getSdBusInterface());
auto& callback = interfaceData->methods[message.getMemberName()].callback; const auto* methodItem = findMethod(*vtable, message.getMemberName());
assert(callback); assert(methodItem != nullptr);
assert(methodItem->callback);
auto ok = invokeHandlerAndCatchErrors([&](){ callback(std::move(message)); }, retError); auto ok = invokeHandlerAndCatchErrors([&](){ methodItem->callback(std::move(message)); }, retError);
return ok ? 1 : -1; return ok ? 1 : -1;
} }
@ -354,21 +329,23 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
, void *userData , void *userData
, sd_bus_error *retError ) , sd_bus_error *retError )
{ {
auto* interfaceData = static_cast<InterfaceData*>(userData); auto* vtable = static_cast<VTable*>(userData);
assert(interfaceData != nullptr); assert(vtable != nullptr);
auto& object = interfaceData->object; assert(vtable->object != nullptr);
auto& callback = interfaceData->properties[property].getCallback; const auto* propertyItem = findProperty(*vtable, property);
// Getter can be empty - the case of "write-only" property assert(propertyItem != nullptr);
if (!callback)
// 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"); sd_bus_error_set(retError, "org.freedesktop.DBus.Error.Failed", "Cannot read property as it is write-only");
return 1; return 1;
} }
auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &object.connection_.getSdBusInterface()); auto reply = Message::Factory::create<PropertyGetReply>(sdbusReply, &vtable->object->connection_.getSdBusInterface());
auto ok = invokeHandlerAndCatchErrors([&](){ callback(reply); }, retError); auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->getCallback(reply); }, retError);
return ok ? 1 : -1; return ok ? 1 : -1;
} }
@ -381,16 +358,17 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
, void *userData , void *userData
, sd_bus_error *retError ) , sd_bus_error *retError )
{ {
auto* interfaceData = static_cast<InterfaceData*>(userData); auto* vtable = static_cast<VTable*>(userData);
assert(interfaceData != nullptr); assert(vtable != nullptr);
auto& object = interfaceData->object; assert(vtable->object != nullptr);
auto& callback = interfaceData->properties[property].setCallback; const auto* propertyItem = findProperty(*vtable, property);
assert(callback); 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_.getSdBusInterface());
auto ok = invokeHandlerAndCatchErrors([&](){ callback(std::move(value)); }, retError); auto ok = invokeHandlerAndCatchErrors([&](){ propertyItem->setCallback(std::move(value)); }, retError);
return ok ? 1 : -1; return ok ? 1 : -1;
} }

View File

@ -31,7 +31,7 @@
#include "IConnection.h" #include "IConnection.h"
#include SDBUS_HEADER #include SDBUS_HEADER
#include <string> #include <string>
#include <map> #include <list>
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <memory> #include <memory>
@ -45,46 +45,8 @@ namespace sdbus::internal {
public: public:
Object(sdbus::internal::IConnection& connection, std::string objectPath); Object(sdbus::internal::IConnection& connection, std::string objectPath);
void registerMethod( const std::string& interfaceName void addVTable(std::string interfaceName, std::vector<VTableItem> vtable) override;
, std::string methodName Slot addVTable(std::string interfaceName, std::vector<VTableItem> vtable, request_slot_t) override;
, 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 unregister() override; void unregister() override;
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override; sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
@ -105,55 +67,74 @@ namespace sdbus::internal {
Message getCurrentlyProcessedMessage() const override; Message getCurrentlyProcessedMessage() const override;
private: private:
using InterfaceName = std::string; // A vtable record comprising methods, signals, properties, flags.
struct InterfaceData // 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) {} std::string interfaceName;
Flags interfaceFlags;
using MethodName = std::string; struct MethodItem
struct MethodData
{ {
const std::string inputArgs; std::string name;
const std::string outputArgs; std::string inputArgs;
const std::string paramNames; std::string outputArgs;
std::string paramNames;
method_callback callback; method_callback callback;
Flags flags; Flags flags;
}; };
std::map<MethodName, MethodData> methods; // Array of method records sorted by method name
using SignalName = std::string; std::vector<MethodItem> methods;
struct SignalData
struct SignalItem
{ {
const std::string signature; std::string name;
const std::string paramNames; std::string signature;
std::string paramNames;
Flags flags; Flags flags;
}; };
std::map<SignalName, SignalData> signals; // Array of signal records sorted by signal name
using PropertyName = std::string; std::vector<SignalItem> signals;
struct PropertyData
struct PropertyItem
{ {
const std::string signature; std::string name;
std::string signature;
property_get_callback getCallback; property_get_callback getCallback;
property_set_callback setCallback; property_set_callback setCallback;
Flags flags; Flags flags;
}; };
std::map<PropertyName, PropertyData> properties; // Array of signal records sorted by signal name
std::vector<sd_bus_vtable> vtable; std::vector<PropertyItem> properties;
Flags flags;
Object& object; // 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, // This is intentionally the last member, because it must be destructed first,
// releasing callbacks above before the callbacks themselves are destructed. // releasing callbacks above before the callbacks themselves are destructed.
Slot slot; Slot slot;
}; };
InterfaceData& getInterface(const std::string& interfaceName); VTable createInternalVTable(std::string interfaceName, std::vector<VTableItem> vtable);
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData); void writeInterfaceFlagsToVTable(InterfaceFlagsVTableItem flags, VTable& vtable);
static void registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable); void writeMethodRecordToVTable(MethodVTableItem method, VTable& vtable);
static void registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable); void writeSignalRecordToVTable(SignalVTableItem signal, VTable& vtable);
static void registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable); void writePropertyRecordToVTable(PropertyVTableItem property, VTable& vtable);
void activateInterfaceVTable( const std::string& interfaceName
, InterfaceData& interfaceData std::vector<sd_bus_vtable> createInternalSdBusVTable(const VTable& vtable);
, const std::vector<sd_bus_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, const std::string& methodName);
static const VTable::PropertyItem* findProperty(const VTable& vtable, const std::string& propertyName);
static std::string paramNamesToString(const std::vector<std::string>& paramNames); 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); static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
@ -175,7 +156,7 @@ namespace sdbus::internal {
private: private:
sdbus::internal::IConnection& connection_; sdbus::internal::IConnection& connection_;
std::string objectPath_; std::string objectPath_;
std::map<InterfaceName, InterfaceData> interfaces_; std::vector<Slot> vtables_;
Slot objectManagerSlot_; Slot objectManagerSlot_;
}; };

View File

@ -27,18 +27,18 @@
#include "VTableUtils.h" #include "VTableUtils.h"
#include SDBUS_HEADER #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); struct sd_bus_vtable vtableStart = SD_BUS_VTABLE_START(flags);
return vtableStart; return vtableStart;
} }
sd_bus_vtable createVTableMethodItem( const char *member sd_bus_vtable createSdBusVTableMethodItem( const char *member
, const char *signature , const char *signature
, const char *result , const char *result
, const char *paramNames , const char *paramNames
, sd_bus_message_handler_t handler , sd_bus_message_handler_t handler
, uint64_t flags ) , uint64_t flags )
{ {
#if LIBSYSTEMD_VERSION>=242 #if LIBSYSTEMD_VERSION>=242
// We have to expand macro SD_BUS_METHOD_WITH_NAMES manually here, because the macro expects literal char strings // 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; return vtableItem;
} }
sd_bus_vtable createVTableSignalItem( const char *member sd_bus_vtable createSdBusVTableSignalItem( const char *member
, const char *signature , const char *signature
, const char *outnames , const char *outnames
, uint64_t flags ) , uint64_t flags )
{ {
#if LIBSYSTEMD_VERSION>=242 #if LIBSYSTEMD_VERSION>=242
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL_WITH_NAMES(member, signature, outnames, flags); 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; return vtableItem;
} }
sd_bus_vtable createVTablePropertyItem( const char *member sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
, const char *signature , const char *signature
, sd_bus_property_get_t getter , sd_bus_property_get_t getter
, uint64_t flags ) , uint64_t flags )
{ {
struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, flags); struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, flags);
return vtableItem; return vtableItem;
} }
sd_bus_vtable createVTableWritablePropertyItem( const char *member sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
, const char *signature , const char *signature
, sd_bus_property_get_t getter , sd_bus_property_get_t getter
, sd_bus_property_set_t setter , sd_bus_property_set_t setter
, uint64_t flags ) , uint64_t flags )
{ {
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, flags); struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, flags);
return vtableItem; return vtableItem;
} }
sd_bus_vtable createVTableEndItem() sd_bus_vtable createSdBusVTableEndItem()
{ {
struct sd_bus_vtable vtableEnd = SD_BUS_VTABLE_END; struct sd_bus_vtable vtableEnd = SD_BUS_VTABLE_END;
return vtableEnd; return vtableEnd;

View File

@ -34,27 +34,27 @@
extern "C" { extern "C" {
#endif #endif
sd_bus_vtable createVTableStartItem(uint64_t flags); sd_bus_vtable createSdBusVTableStartItem(uint64_t flags);
sd_bus_vtable createVTableMethodItem( const char *member sd_bus_vtable createSdBusVTableMethodItem( const char *member
, const char *signature , const char *signature
, const char *result , const char *result
, const char *paramNames , const char *paramNames
, sd_bus_message_handler_t handler , sd_bus_message_handler_t handler
, uint64_t flags ); , uint64_t flags );
sd_bus_vtable createVTableSignalItem( const char *member sd_bus_vtable createSdBusVTableSignalItem( const char *member
, const char *signature , const char *signature
, const char *outnames , const char *outnames
, uint64_t flags ); , uint64_t flags );
sd_bus_vtable createVTablePropertyItem( const char *member sd_bus_vtable createSdBusVTableReadOnlyPropertyItem( const char *member
, const char *signature , const char *signature
, sd_bus_property_get_t getter , sd_bus_property_get_t getter
, uint64_t flags ); , uint64_t flags );
sd_bus_vtable createVTableWritablePropertyItem( const char *member sd_bus_vtable createSdBusVTableWritablePropertyItem( const char *member
, const char *signature , const char *signature
, sd_bus_property_get_t getter , sd_bus_property_get_t getter
, sd_bus_property_set_t setter , sd_bus_property_set_t setter
, uint64_t flags ); , uint64_t flags );
sd_bus_vtable createVTableEndItem(); sd_bus_vtable createSdBusVTableEndItem();
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -81,7 +81,6 @@ TYPED_TEST(SdbusTestObject, CallsMethodsWithStructSuccesfully)
auto vectorRes = this->m_proxy->getInts16FromStruct(a); auto vectorRes = this->m_proxy->getInts16FromStruct(a);
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{0})); // because second item is by default initialized to 0 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>> b{
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE} UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
}; };
@ -281,3 +280,34 @@ TYPED_TEST(SdbusTestObject, CanCallMethodSynchronouslyWithoutAnEventLoopThread)
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE)); ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
} }
TYPED_TEST(SdbusTestObject, CanRegisterAdditionalVTableDynamicallyAtAnyTime)
{
auto& object = this->m_adaptor->getObject();
auto vtableSlot = object.addVTable( "org.sdbuscpp.integrationtests2"
, { sdbus::registerMethod("add").implementedAs([](const int64_t& a, const double& b){ return a + b; })
, sdbus::registerMethod("subtract").implementedAs([](const int& a, const int& b){ return a - b; })}
, sdbus::request_slot );
// The new remote vtable is registered as long as we keep vtableSlot, so remote method calls now should pass
auto proxy = sdbus::createProxy(BUS_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
int result{};
proxy->callMethod("subtract").onInterface("org.sdbuscpp.integrationtests2").withArguments(10, 2).storeResultsTo(result);
ASSERT_THAT(result, Eq(8));
}
TYPED_TEST(SdbusTestObject, CanUnregisterAdditionallyRegisteredVTableAtAnyTime)
{
auto& object = this->m_adaptor->getObject();
auto vtableSlot = object.addVTable( "org.sdbuscpp.integrationtests2"
, { sdbus::registerMethod("add").implementedAs([](const int64_t& a, const double& b){ return a + b; })
, sdbus::registerMethod("subtract").implementedAs([](const int& a, const int& b){ return a - b; })}
, sdbus::request_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::createProxy(BUS_NAME, OBJECT_PATH, sdbus::dont_run_event_loop_thread);
ASSERT_THROW(proxy->callMethod("subtract").onInterface("org.sdbuscpp.integrationtests2").withArguments(10, 2), sdbus::Error);
}

View File

@ -22,34 +22,6 @@ protected:
integrationtests_adaptor(sdbus::IObject& object) integrationtests_adaptor(sdbus::IObject& object)
: object_(&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(const integrationtests_adaptor&) = delete;
@ -59,6 +31,39 @@ protected:
~integrationtests_adaptor() = default; ~integrationtests_adaptor() = default;
void registerAdaptor()
{
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 sdbus::Variant& 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("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("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::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("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: public:
void emitSimpleSignal() void emitSimpleSignal()
{ {

View File

@ -22,9 +22,6 @@ protected:
perftests_adaptor(sdbus::IObject& object) perftests_adaptor(sdbus::IObject& object)
: object_(&object) : 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(const perftests_adaptor&) = delete;
@ -34,6 +31,14 @@ protected:
~perftests_adaptor() = default; ~perftests_adaptor() = default;
void registerAdaptor()
{
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: public:
void emitDataSignal(const std::string& data) void emitDataSignal(const std::string& data)
{ {

View File

@ -24,7 +24,6 @@ protected:
thermometer_adaptor(sdbus::IObject& object) thermometer_adaptor(sdbus::IObject& object)
: object_(&object) : object_(&object)
{ {
object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
} }
thermometer_adaptor(const thermometer_adaptor&) = delete; thermometer_adaptor(const thermometer_adaptor&) = delete;
@ -34,6 +33,11 @@ protected:
~thermometer_adaptor() = default; ~thermometer_adaptor() = default;
void registerAdaptor()
{
object_->addVTable(sdbus::registerMethod("getCurrentTemperature").withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); })).forInterface(INTERFACE_NAME);
}
private: private:
virtual uint32_t getCurrentTemperature() = 0; virtual uint32_t getCurrentTemperature() = 0;

View File

@ -23,8 +23,6 @@ protected:
concatenator_adaptor(sdbus::IObject& object) concatenator_adaptor(sdbus::IObject& object)
: object_(&object) : 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(const concatenator_adaptor&) = delete;
@ -34,6 +32,13 @@ protected:
~concatenator_adaptor() = default; ~concatenator_adaptor() = default;
void registerAdaptor()
{
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: public:
void emitConcatenatedSignal(const std::string& concatenatedString) void emitConcatenatedSignal(const std::string& concatenatedString)
{ {

View File

@ -24,7 +24,6 @@ protected:
thermometer_adaptor(sdbus::IObject& object) thermometer_adaptor(sdbus::IObject& object)
: object_(&object) : object_(&object)
{ {
object_->registerMethod("getCurrentTemperature").onInterface(INTERFACE_NAME).withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); });
} }
thermometer_adaptor(const thermometer_adaptor&) = delete; thermometer_adaptor(const thermometer_adaptor&) = delete;
@ -34,6 +33,11 @@ protected:
~thermometer_adaptor() = default; ~thermometer_adaptor() = default;
void registerAdaptor()
{
object_->addVTable(sdbus::registerMethod("getCurrentTemperature").withOutputParamNames("result").implementedAs([this](){ return this->getCurrentTemperature(); })).forInterface(INTERFACE_NAME);
}
private: private:
virtual uint32_t getCurrentTemperature() = 0; virtual uint32_t getCurrentTemperature() = 0;
@ -58,8 +62,6 @@ protected:
factory_adaptor(sdbus::IObject& object) factory_adaptor(sdbus::IObject& object)
: object_(&object) : 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(const factory_adaptor&) = delete;
@ -69,6 +71,13 @@ protected:
~factory_adaptor() = default; ~factory_adaptor() = default;
void registerAdaptor()
{
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: private:
virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) = 0; virtual void createDelegateObject(sdbus::Result<sdbus::ObjectPath>&& result) = 0;
virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0; virtual void destroyDelegateObject(sdbus::Result<>&& result, sdbus::ObjectPath delegate) = 0;

View File

@ -85,7 +85,17 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
<< tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl << tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
<< "protected:" << endl << "protected:" << endl
<< tab << className << "(sdbus::IObject& object)" << endl << tab << className << "(sdbus::IObject& object)" << endl
<< tab << tab << ": object_(&object)" << endl; << tab << tab << ": 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 << "&&) = default;" << endl;
body << tab << className << "& operator=(" << className << "&&) = default;" << endl << endl;
body << tab << "~" << className << "() = default;" << endl << endl;
Nodes methods = interface["method"]; Nodes methods = interface["method"];
Nodes signals = interface["signal"]; Nodes signals = interface["signal"];
@ -111,34 +121,29 @@ std::string AdaptorGenerator::processInterface(Node& interface) const
if(!annotationRegistration.empty()) if(!annotationRegistration.empty())
{ {
std::stringstream str; std::stringstream str;
str << tab << tab << "object_->setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl; str << "sdbus::setInterfaceFlags()" << annotationRegistration << ";";
annotationRegistration = str.str(); annotationRegistration = str.str();
} }
std::string methodRegistration, methodDeclaration; std::vector<std::string> methodRegistrations;
std::tie(methodRegistration, methodDeclaration) = processMethods(methods); std::string methodDeclaration;
std::tie(methodRegistrations, methodDeclaration) = processMethods(methods);
std::string signalRegistration, signalMethods; std::vector<std::string> signalRegistrations;
std::tie(signalRegistration, signalMethods) = processSignals(signals); std::string signalMethods;
std::tie(signalRegistrations, signalMethods) = processSignals(signals);
std::string propertyRegistration, propertyAccessorDeclaration; std::vector<std::string> propertyRegistrations;
std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties); std::string propertyAccessorDeclaration;
std::tie(propertyRegistrations, propertyAccessorDeclaration) = processProperties(properties);
body << tab << "{" << endl auto vtableRegistration = createVTableRegistration(annotationRegistration, methodRegistrations, signalRegistrations, propertyRegistrations);
<< annotationRegistration
<< methodRegistration body << tab << "void registerAdaptor()" << endl
<< signalRegistration << tab << "{" << endl
<< propertyRegistration << vtableRegistration << endl
<< tab << "}" << endl << 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()) if (!signalMethods.empty())
{ {
body << "public:" << endl << signalMethods; body << "public:" << endl << signalMethods;
@ -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) for (const auto& method : methods)
{ {
std::ostringstream registrationSS;
auto methodName = method->get("name"); auto methodName = method->get("name");
auto methodNameSafe = mangle_name(methodName); auto methodNameSafe = mangle_name(methodName);
@ -217,9 +226,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
using namespace std::string_literals; using namespace std::string_literals;
registrationSS << tab << tab << "object_->registerMethod(\"" registrationSS << "sdbus::registerMethod(\""
<< methodName << "\")" << methodName << "\")"
<< ".onInterface(INTERFACE_NAME)"
<< (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "") << (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
<< (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "") << (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "")
<< ".implementedAs(" << ".implementedAs("
@ -229,7 +237,9 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
<< "){ " << (async ? "" : "return ") << "this->" << methodNameSafe << "(" << "){ " << (async ? "" : "return ") << "this->" << methodNameSafe << "("
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "") << (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
<< argStr << "); })" << argStr << "); })"
<< annotationRegistration << ";" << endl; << annotationRegistration;
methodRegistrations.push_back(registrationSS.str());
declarationSS << tab declarationSS << tab
<< "virtual " << "virtual "
@ -241,16 +251,20 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
<< ") = 0;" << endl; << ") = 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) for (const auto& signal : signals)
{ {
std::stringstream signalRegistrationSS;
auto name = signal->get("name"); auto name = signal->get("name");
auto annotations = getAnnotations(*signal); 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::string argStr, argTypeStr, typeStr, argStringsStr;
std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args); std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
signalRegistrationSS << tab << tab signalRegistrationSS << "sdbus::registerSignal(\"" << name << "\")";
<< "object_->registerSignal(\"" << name << "\")"
".onInterface(INTERFACE_NAME)";
if (args.size() > 0) if (args.size() > 0)
{ {
@ -282,7 +294,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
} }
signalRegistrationSS << annotationRegistration; signalRegistrationSS << annotationRegistration;
signalRegistrationSS << ";" << endl;
signalRegistrations.push_back(signalRegistrationSS.str());
auto nameWithCapFirstLetter = name; auto nameWithCapFirstLetter = name;
nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]); nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]);
@ -302,16 +315,20 @@ std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Node
<< tab << "}" << endl << endl; << 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) for (const auto& property : properties)
{ {
std::ostringstream registrationSS;
auto propertyName = property->get("name"); auto propertyName = property->get("name");
auto propertyNameSafe = mangle_name(propertyName); auto propertyNameSafe = mangle_name(propertyName);
auto propertyAccess = property->get("access"); 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; << "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
} }
registrationSS << tab << tab << "object_->registerProperty(\"" registrationSS << "sdbus::registerProperty(\""
<< propertyName << "\")" << propertyName << "\")";
<< ".onInterface(INTERFACE_NAME)";
if (propertyAccess == "read" || propertyAccess == "readwrite") if (propertyAccess == "read" || propertyAccess == "readwrite")
{ {
@ -356,7 +372,8 @@ std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const N
} }
registrationSS << annotationRegistration; registrationSS << annotationRegistration;
registrationSS << ";" << endl;
propertyRegistrations.push_back(registrationSS.str());
if (propertyAccess == "read" || propertyAccess == "readwrite") if (propertyAccess == "read" || propertyAccess == "readwrite")
declarationSS << tab << "virtual " << propertyType << " " << propertyNameSafe << "() = 0;" << endl; 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; 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 << "object_->addVTable(" << allRegistrations[0] << ").forInterface(INTERFACE_NAME);";
}
else
{
registrationSS << tab << tab << "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 std::map<std::string, std::string> AdaptorGenerator::getAnnotations( sdbuscpp::xml::Node& node) const

View File

@ -59,21 +59,26 @@ private:
* @param methods * @param methods
* @return tuple: registration of methods, declaration of abstract 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 * Generate source code for signals
* @param signals * @param signals
* @return tuple: registration of signals, definition of signal methods * @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 * Generate source code for properties
* @param properties * @param properties
* @return tuple: registration of properties, declaration of property accessor virtual methods * @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 * Get annotations listed for a given node