forked from Kistler-Group/sdbus-cpp
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:
@@ -82,9 +82,10 @@ namespace sdbus {
|
||||
* adaptor-side interface classes representing interfaces (with methods, signals and properties)
|
||||
* of the D-Bus object.
|
||||
*
|
||||
* In the final adaptor class inherited from AdaptorInterfaces, it is necessary to finish
|
||||
* adaptor registration in class constructor (finishRegistration();`), and, conversely,
|
||||
* unregister the adaptor in class destructor (`unregister();`).
|
||||
* In the final adaptor class inherited from AdaptorInterfaces, one needs to make sure:
|
||||
* 1. to call `registerAdaptor();` in the class constructor, and, conversely,
|
||||
* 2. to call `unregisterAdaptor();` in the class destructor,
|
||||
* so that the object API vtable is registered and unregistered at the proper time.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
@@ -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.
|
||||
*
|
||||
* For more information, see underlying @ref IObject::finishRegistration()
|
||||
* See also @ref IObject::addVTable()
|
||||
*/
|
||||
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
|
||||
{
|
||||
return getObject().getObjectPath();
|
||||
}
|
||||
using ObjectHolder::getObject;
|
||||
|
||||
protected:
|
||||
using base_type = AdaptorInterfaces;
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
#ifndef SDBUS_CXX_CONVENIENCEAPICLASSES_H_
|
||||
#define SDBUS_CXX_CONVENIENCEAPICLASSES_H_
|
||||
|
||||
#include <sdbus-c++/VTableItems.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <cstdint>
|
||||
@@ -48,101 +48,16 @@ namespace sdbus {
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
class MethodRegistrator
|
||||
class VTableAdder
|
||||
{
|
||||
public:
|
||||
MethodRegistrator(IObject& object, std::string methodName);
|
||||
MethodRegistrator(MethodRegistrator&& other) = default;
|
||||
~MethodRegistrator() noexcept(false);
|
||||
|
||||
MethodRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename _Function> MethodRegistrator& implementedAs(_Function&& callback);
|
||||
MethodRegistrator& withInputParamNames(std::vector<std::string> paramNames);
|
||||
template <typename... _String> MethodRegistrator& withInputParamNames(_String... paramNames);
|
||||
MethodRegistrator& withOutputParamNames(std::vector<std::string> paramNames);
|
||||
template <typename... _String> MethodRegistrator& withOutputParamNames(_String... paramNames);
|
||||
MethodRegistrator& markAsDeprecated();
|
||||
MethodRegistrator& markAsPrivileged();
|
||||
MethodRegistrator& withNoReply();
|
||||
VTableAdder(IObject& object, std::vector<VTableItem> vtable);
|
||||
void forInterface(std::string interfaceName);
|
||||
[[nodiscard]] Slot forInterface(std::string interfaceName, request_slot_t);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
std::string methodName_;
|
||||
std::string interfaceName_;
|
||||
std::string inputSignature_;
|
||||
std::vector<std::string> inputParamNames_;
|
||||
std::string outputSignature_;
|
||||
std::vector<std::string> outputParamNames_;
|
||||
method_callback methodCallback_;
|
||||
Flags flags_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
|
||||
};
|
||||
|
||||
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
|
||||
std::vector<VTableItem> vtable_;
|
||||
};
|
||||
|
||||
class SignalEmitter
|
||||
@@ -313,6 +228,6 @@ namespace sdbus {
|
||||
const std::string* interfaceName_{};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CXX_CONVENIENCEAPICLASSES_H_ */
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <exception>
|
||||
@@ -41,374 +42,24 @@
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/*** ----------------- ***/
|
||||
/*** MethodRegistrator ***/
|
||||
/*** ----------------- ***/
|
||||
/*** ------------- ***/
|
||||
/*** VTableAdder ***/
|
||||
/*** ------------- ***/
|
||||
|
||||
inline MethodRegistrator::MethodRegistrator(IObject& object, std::string methodName)
|
||||
inline VTableAdder::VTableAdder(IObject& object, std::vector<VTableItem> vtable)
|
||||
: object_(object)
|
||||
, methodName_(std::move(methodName))
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
, vtable_(std::move(vtable))
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // 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)
|
||||
inline void VTableAdder::forInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
object_.addVTable(std::move(interfaceName), std::move(vtable_));
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
MethodRegistrator& MethodRegistrator::implementedAs(_Function&& callback)
|
||||
inline Slot VTableAdder::forInterface(std::string interfaceName, request_slot_t)
|
||||
{
|
||||
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
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;
|
||||
return object_.addVTable(std::move(interfaceName), std::move(vtable_), request_slot);
|
||||
}
|
||||
|
||||
/*** ------------- ***/
|
||||
@@ -930,6 +581,6 @@ namespace sdbus {
|
||||
.getResultAsFuture<std::map<std::string, Variant>>();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace sdbus
|
||||
|
||||
#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */
|
||||
|
||||
+94
-214
@@ -27,6 +27,7 @@
|
||||
#ifndef SDBUS_CXX_IOBJECT_H_
|
||||
#define SDBUS_CXX_IOBJECT_H_
|
||||
|
||||
#include <sdbus-c++/VTableItems.h>
|
||||
#include <sdbus-c++/ConvenienceApiClasses.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Flags.h>
|
||||
@@ -62,144 +63,104 @@ namespace sdbus {
|
||||
virtual ~IObject() = default;
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] methodCallback Callback that implements the body of the method
|
||||
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] items Individual instances of VTable item structures
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* Parameter `items' represents a vtable definition that may contain method declarations
|
||||
* (using MethodVTableItem struct), property declarations (using PropertyVTableItem
|
||||
* struct), signal declarations (using SignalVTableItem struct), or global interface
|
||||
* flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for 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
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, std::string outputSignature
|
||||
, method_callback methodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
template < typename... VTableItems
|
||||
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
|
||||
void addVTable(std::string interfaceName, VTableItems&&... items);
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
* @brief Adds a declaration of methods, properties and signals of the object at a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method will belong to
|
||||
* @param[in] methodName Name of the method
|
||||
* @param[in] inputSignature D-Bus signature of method input parameters
|
||||
* @param[in] inputNames Names of input parameters
|
||||
* @param[in] outputSignature D-Bus signature of method output parameters
|
||||
* @param[in] outputNames Names of output parameters
|
||||
* @param[in] methodCallback Callback that implements the body of the method
|
||||
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
|
||||
*
|
||||
* Provided names of input and output parameters will be included in the introspection
|
||||
* description (given that at least version 242 of underlying libsystemd library is
|
||||
* used; otherwise, names of parameters are ignored). This usually helps better describe
|
||||
* the API to the introspector.
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* 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 its lifetime is tied to the lifetime of the Object instance.
|
||||
*
|
||||
* The function provides strong exception guarantee. The state of the object remains
|
||||
* unmodified in face of an exception.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerMethod( const std::string& interfaceName
|
||||
, std::string methodName
|
||||
, std::string inputSignature
|
||||
, const std::vector<std::string>& inputNames
|
||||
, std::string outputSignature
|
||||
, const std::vector<std::string>& outputNames
|
||||
, method_callback methodCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
virtual void addVTable(std::string interfaceName, std::vector<VTableItem> vtable) = 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] signalName Name of the signal
|
||||
* @param[in] signature D-Bus signature of signal parameters
|
||||
* @param[in] flags D-Bus signal flags (deprecated)
|
||||
* @param[in] interfaceName Name of an interface the the vtable is registered for
|
||||
* @param[in] vtable A list of individual descriptions in the form of VTable item instances
|
||||
*
|
||||
* This method is used to declare attributes for the object under the given interface.
|
||||
* The `vtable' parameter may contain method declarations (using MethodVTableItem struct),
|
||||
* property declarations (using PropertyVTableItem struct), signal declarations (using
|
||||
* SignalVTableItem struct), or global interface flags (using InterfaceFlagsVTableItem struct).
|
||||
*
|
||||
* An interface can have any number of vtables attached to it.
|
||||
*
|
||||
* Consult manual pages for 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
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, Flags flags = {} ) = 0;
|
||||
virtual Slot addVTable(std::string interfaceName, std::vector<VTableItem> vtable, request_slot_t) = 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
|
||||
* @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)
|
||||
* This version allows method chaining for a little safer and more readable VTable registration.
|
||||
*
|
||||
* Provided names of signal output parameters will be included in the introspection
|
||||
* description (given that at least version 242 of underlying libsystemd library is
|
||||
* used; otherwise, names of parameters are ignored). This usually helps better describe
|
||||
* the API to the introspector.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
* See addVTable() overloads above for detailed documentation.
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, std::string signalName
|
||||
, std::string signature
|
||||
, const std::vector<std::string>& paramNames
|
||||
, Flags flags = {} ) = 0;
|
||||
template < typename... VTableItems
|
||||
, typename = std::enable_if_t<(is_one_of_variants_types<VTableItem, std::decay_t<VTableItems>> && ...)> >
|
||||
[[nodiscard]] VTableAdder addVTable(VTableItems&&... items);
|
||||
|
||||
/*!
|
||||
* @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
|
||||
* @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)
|
||||
* This version allows method chaining for a little safer and more readable VTable registration.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
* See addVTable() overloads above for detailed documentation.
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers read/write property that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the property will fall under
|
||||
* @param[in] propertyName Name of the property
|
||||
* @param[in] signature D-Bus signature of property parameters
|
||||
* @param[in] getCallback Callback that implements the body of the property getter
|
||||
* @param[in] setCallback Callback that implements the body of the property setter
|
||||
* @param[in] flags D-Bus property flags (deprecated, property update behavior)
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, std::string propertyName
|
||||
, std::string signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback
|
||||
, Flags flags = {} ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Sets flags for a given interface
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface whose flags will be set
|
||||
* @param[in] flags Flags to be set
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void setInterfaceFlags(const std::string& interfaceName, Flags flags) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Finishes object API registration and publishes the object on the bus
|
||||
*
|
||||
* The method exports all up to now registered methods, signals and properties on D-Bus.
|
||||
* Must be called after all methods, signals and properties have been registered.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void finishRegistration() = 0;
|
||||
[[nodiscard]] VTableAdder addVTable(std::vector<VTableItem> vtable);
|
||||
|
||||
/*!
|
||||
* @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
|
||||
*
|
||||
* This emits an InterfacesAdded signal on this object path with explicitly provided list
|
||||
* of registered interfaces. As sdbus-c++ does currently not supported adding/removing
|
||||
* interfaces of an existing object at run time (an object has a fixed set of interfaces
|
||||
* registered by the time of invoking finishRegistration()), emitInterfacesAddedSignal(void)
|
||||
* is probably what you are looking for.
|
||||
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
|
||||
* object interfaces and their vtables, so this method now makes more sense.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
@@ -300,10 +259,8 @@ namespace sdbus {
|
||||
* @brief Emits InterfacesRemoved signal on this object path
|
||||
*
|
||||
* This emits an InterfacesRemoved signal on the given path with explicitly provided list
|
||||
* of registered interfaces. As sdbus-c++ does currently not supported adding/removing
|
||||
* interfaces of an existing object at run time (an object has a fixed set of interfaces
|
||||
* registered by the time of invoking finishRegistration()), emitInterfacesRemovedSignal(void)
|
||||
* is probably what you are looking for.
|
||||
* of registered interfaces. Since v2.0, sdbus-c++ supports dynamically addable/removable
|
||||
* object interfaces and their vtables, so this method now makes more sense.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
@@ -340,81 +297,6 @@ namespace sdbus {
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -459,31 +341,28 @@ namespace sdbus {
|
||||
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
*
|
||||
@@ -506,6 +385,7 @@ namespace sdbus {
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/VTableItems.inl>
|
||||
#include <sdbus-c++/ConvenienceApiClasses.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IOBJECT_H_ */
|
||||
|
||||
@@ -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
|
||||
{
|
||||
return getProxy().getObjectPath();
|
||||
}
|
||||
using ProxyObjectHolder::getProxy;
|
||||
|
||||
protected:
|
||||
using base_type = ProxyInterfaces;
|
||||
|
||||
@@ -251,8 +251,7 @@ namespace sdbus {
|
||||
static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties";
|
||||
|
||||
protected:
|
||||
Properties_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
Properties_adaptor(sdbus::IObject& object) : object_(&object)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -263,6 +262,10 @@ namespace sdbus {
|
||||
|
||||
~Properties_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
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";
|
||||
|
||||
protected:
|
||||
explicit ObjectManager_adaptor(sdbus::IObject& object)
|
||||
: object_(&object)
|
||||
explicit ObjectManager_adaptor(sdbus::IObject& object) : object_(&object)
|
||||
{
|
||||
object_->addObjectManager();
|
||||
}
|
||||
|
||||
ObjectManager_adaptor(const ObjectManager_adaptor&) = delete;
|
||||
@@ -306,6 +307,11 @@ namespace sdbus {
|
||||
|
||||
~ObjectManager_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
object_->addObjectManager();
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject* object_;
|
||||
};
|
||||
@@ -336,6 +342,10 @@ namespace sdbus {
|
||||
|
||||
~ManagedObject_adaptor() = default;
|
||||
|
||||
void registerAdaptor()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/*!
|
||||
* @brief Emits InterfacesAdded signal for this object path
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@@ -659,6 +660,15 @@ namespace sdbus {
|
||||
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
|
||||
{
|
||||
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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_ */
|
||||
Reference in New Issue
Block a user