Simplify and unify callback design for both sync and async methods

This commit is contained in:
sangelovic
2019-04-03 00:05:20 +02:00
parent 5673a9bcf2
commit 08945acbc4
11 changed files with 116 additions and 237 deletions

View File

@@ -57,7 +57,6 @@ set(SDBUSCPP_PUBLIC_HDRS
${SDBUSCPP_INCLUDE_DIR}/IObjectProxy.h
${SDBUSCPP_INCLUDE_DIR}/Message.h
${SDBUSCPP_INCLUDE_DIR}/MethodResult.h
${SDBUSCPP_INCLUDE_DIR}/MethodResult.inl
${SDBUSCPP_INCLUDE_DIR}/Types.h
${SDBUSCPP_INCLUDE_DIR}/TypeTraits.h
${SDBUSCPP_INCLUDE_DIR}/Flags.h

View File

@@ -150,7 +150,7 @@ This is how a simple Concatenator service implemented upon the basic sdbus-c++ A
// to emit signals.
sdbus::IObject* g_concatenator{};
void concatenate(sdbus::MethodCall& call, sdbus::MethodReply& reply)
void concatenate(sdbus::MethodCall call)
{
// Deserialize the collection of numbers from the message
std::vector<int> numbers;
@@ -170,8 +170,10 @@ void concatenate(sdbus::MethodCall& call, sdbus::MethodReply& reply)
result += (result.empty() ? std::string() : separator) + std::to_string(number);
}
// Serialize resulting string to the reply
// Serialize resulting string to the reply and send the reply to the caller
auto reply = call.createReply();
reply << result;
reply.send();
// Emit 'concatenated' signal
const char* interfaceName = "org.sdbuscpp.Concatenator";
@@ -205,7 +207,7 @@ int main(int argc, char *argv[])
We establish a D-Bus sytem 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.
The callback for any D-Bus object method on this level is any callable of signature `void(sdbus::MethodCall& call, sdbus::MethodReply& reply)`. The first parameter `call` is the incoming method call message. We need to deserialize input arguments from it. When we can invoke the logic and get the results. Then we serialize the results to the second parameter, the pre-constructed `reply` message. The reply is then sent automatically by sdbus-c++. 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()`.
Please note that we can create and destroy D-Bus objects on a connection dynamically, at any time during runtime, even while there is an active event loop upon the connection. So managing D-Bus objects' lifecycle (creating, exporting and destroying D-Bus objects) is completely thread-safe.
@@ -709,12 +711,12 @@ Asynchronous server-side methods
So far in our tutorial, we have only considered simple server methods that are executed in a synchronous way. Sometimes the method call may take longer, however, and we don't want to block (potentially starve) other clients (whose requests may take relative short time). The solution is to execute the D-Bus methods asynchronously, and return the control quickly back to the D-Bus dispatching thread. sdbus-c++ provides API supporting async methods, and gives users the freedom to come up with their own concrete implementation mechanics (one worker thread? thread pool? ...).
### Lower-level API
### Using basic sdbus-c++ API
Considering the Concatenator example based on lower-level API, if we wanted to write `concatenate` method in an asynchronous way, you only have to adapt method signature and its body (registering the method and all the other stuff stays the same):
This is how the concatenate method would look like if wrote it as an asynchronous D-Bus method using the basic, lower-level API of sdbus-c++:
```c++
void concatenate(sdbus::MethodCall call, sdbus::MethodResult&& result)
void concatenate(sdbus::MethodCall call)
{
// Deserialize the collection of numbers from the message
std::vector<int> numbers;
@@ -725,24 +727,27 @@ void concatenate(sdbus::MethodCall call, sdbus::MethodResult&& result)
call >> separator;
// Launch a thread for async execution...
std::thread([numbers = std::move(numbers), separator = std::move(separator), result = std::move(result)]()
std::thread([numbers = std::move(numbers), separator = std::move(separator), call = std::move(call)]()
{
// Return error if there are no numbers in the collection
if (numbers.empty())
{
// This will send the error reply message back to the client
result.returnError("org.sdbuscpp.Concatenator.Error", "No numbers provided");
// Let's send the error reply message back to the client
auto reply = call.createErrorReply({"org.sdbuscpp.Concatenator.Error", "No numbers provided"});
reply.send();
return;
}
std::string concatenatedStr;
std::string result;
for (auto number : numbers)
{
concatenatedStr += (result.empty() ? std::string() : separator) + std::to_string(number);
result += (result.empty() ? std::string() : separator) + std::to_string(number);
}
// This will send the reply message back to the client
result.returnResults(concatenatedStr);
// Let's send the reply message back to the client
auto reply = call.createReply();
reply << result;
reply.send();
// Emit 'concatenated' signal (creating and emitting signals is thread-safe)
const char* interfaceName = "org.sdbuscpp.Concatenator";
@@ -753,34 +758,53 @@ void concatenate(sdbus::MethodCall call, sdbus::MethodResult&& result)
}
```
Notice these differences as compared to the synchronous version:
There are a few slight differences compared to the synchronous version. Notice that we `std::move` the `call` message to the worker thread (btw we might also do input arguments deserialization in the worker thread, we don't have to do it in the current thread and then move input arguments to the worker thread...). We need the `call` message there to create the reply message once we have the (normal or error) result. Creating and sending replies, as well as creating and emitting signals is thread-safe by design. Also notice that, unlike in sync methods, sending back errors cannot be done by throwing `Error`, since we are now in the context of the worker thread, not that of the D-Bus dispatcher thread. Instead, we pass the `Error` object to the `createErrorReply()` method of the call message (this way of sending back errors, in addition to throwing, we can actually use also in classic synchronous D-Bus methods).
* `MethodCall` message is not given by reference, but it's `std::move`d in. Usually, we either deserialize D-Bus method input arguments synchronously (as in the example above) and then `std::move` them to the worker thread, or we `std::move` (or copy, but moving is faster and idiomatic) the `MethodCall` message to that thread directly and do both the deserialization of input arguments and the logic in that thread.
* Instead of `MethodReply` message given by reference, there is `MethodResult&&` as a second parameter of the callback. `MethodResult` class is move-only.
* We shall `std::move` the `MethodResult` to the worker thread, and eventually write D-Bus method results (or method error, respectively) to it via its `returnResults()` method (or `returnError()` method, respectively).
* Unlike in sync methods, reporting errors cannot be done by throwing `Error`, since the execution takes place out of the context of the D-Bus dispatcher thread. Instead, just pass the error name and message to the `returnError` method of the result holder.
That's all.
Method callback signature is the same in sync and async version. That means sdbus-c++ doesn't care how we execute our D-Bus method. We might very well in run-time decide whether we execute it synchronously, or whether (perhaps in case of longer, more complex calculations) we move the execution to a worker thread.
### Convenience API
Callbacks of async methods in convenience sdbus-c++ API also need to take the result object as a parameter. The requirements are:
Callbacks of async methods based on convenience sdbus-c++ API have slightly different signature. They take a result object parameter in addition to other input parameters. The requirements are:
* The result holder is of type `sdbus::Result<Types...>`, where `Types...` is a list of method output argument types.
* The result object must be a first physical parameter of the callback taken by value.
* The result holder is of type `Result<Types...>&&`, where `Types...` is a list of method output argument types.
* The result object must be the first physical parameter of the callback taken by r-value ref. `Result` class template is move-only.
* The callback itself is physically a void-returning function.
* Method input arguments are taken by value rathern than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, copying is a fallback.
* Method input arguments are taken by value rathern than by const ref, because we usually want to `std::move` them to the worker thread. Moving is usually a lot cheaper than copying, and it's idiomatic. For non-movable types, this falls back to copying.
For example, we would have to change the concatenate callback signature from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator)`.
So the concatenate callback signature would change from `std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)` to `void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator)`:
The `Result` class template has effectively the same API as the `MethodResult` class mentioned in the above example (it inherits from `MethodResult`), so you use it in the very same way to send the results or an error back to the client.
```c++
void concatenate(sdbus::Result<std::string>&& result, std::vector<int32_t> numbers, std::string separator) override
{
// Launch a thread for async execution...
std::thread([this, methodResult = std::move(result), numbers = std::move(numbers), separator = std::move(separator)]()
{
// Return error if there are no numbers in the collection
if (numbers.empty())
{
// Let's send the error reply message back to the client
methodResult.returnError({"org.sdbuscpp.Concatenator.Error", "No numbers provided"});
return;
}
std::string result;
for (auto number : numbers)
{
result += (result.empty() ? std::string() : separator) + std::to_string(number);
}
// Let's send the reply message back to the client
methodResult.returnReply(result);
// Emit the 'concatenated' signal with the resulting string
this->concatenated(result);
}).detach();
}
```
Nothing else has to be changed. The registration of the method callback (`implementedAs`) and all the mechanics around remains completely the same.
The `Result` is a convenience class that represents a future method result, and it is where we write the results (`returnReply()`) or an error (`returnError()`) which we want to send back to the client.
Note: Async D-Bus method doesn't necessarily mean we always have to delegate the work to a different thread and immediately return. We can very well execute the work and return the results (via `returnResults()`) or an error (via `return Error()`) synchronously -- i.e. directly in this thread. sdbus-c++ doesn't care, it supports both approaches. This has the benefit that we can decide at run-time, per each method call, whether we execute it synchronously or (in case of complex operation, for example) execute it asynchronously by moving the work to a worker thread.
Registraion (`implementedAs()`) doesn't change. Nothing else needs to change.
### Marking server-side async methods in the IDL

View File

@@ -65,8 +65,7 @@ namespace sdbus {
std::string interfaceName_;
std::string inputSignature_;
std::string outputSignature_;
method_callback syncCallback_;
async_method_callback asyncCallback_;
method_callback methodCallback_;
Flags flags_;
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
};

View File

@@ -55,6 +55,7 @@ namespace sdbus {
return;
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
// 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
@@ -65,12 +66,7 @@ namespace sdbus {
// 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.
if (syncCallback_)
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(syncCallback_));
else if(asyncCallback_)
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(asyncCallback_));
else
SDBUS_THROW_ERROR("Method handler not specified when registering a DBus method", EINVAL);
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
}
*/
@@ -86,14 +82,14 @@ namespace sdbus {
{
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
syncCallback_ = [callback = std::forward<_Function>(callback)](MethodCall& msg, MethodReply& reply)
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
msg >> inputArgs;
call >> inputArgs;
// Invoke callback with input arguments from the tuple.
// For callbacks returning a non-void value, `apply' also returns that value.
@@ -102,7 +98,9 @@ namespace sdbus {
// The return value is stored to the reply message.
// In case of void functions, ret is an empty tuple and thus nothing is stored.
auto reply = call.createReply();
reply << ret;
reply.send();
};
return *this;
@@ -113,17 +111,17 @@ namespace sdbus {
{
inputSignature_ = signature_of_function_input_arguments<_Function>::str();
outputSignature_ = signature_of_function_output_arguments<_Function>::str();
asyncCallback_ = [callback = std::forward<_Function>(callback)](MethodCall msg, MethodResult&& result)
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.
msg >> inputArgs;
call >> inputArgs;
// Invoke callback with input arguments from the tuple.
sdbus::apply(callback, std::move(result), std::move(inputArgs)); // TODO: Use std::apply when switching to full C++17 support
sdbus::apply(callback, typename function_traits<_Function>::async_result_t{std::move(call)}, std::move(inputArgs));
};
return *this;

View File

@@ -76,30 +76,6 @@ namespace sdbus {
, method_callback methodCallback
, Flags flags = {} ) = 0;
/*!
* @brief Registers method that the object will provide on D-Bus
*
* @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] asyncMethodCallback Callback that implements the body of the method
* @param[in] flags D-Bus method flags (privileged, deprecated, or no reply)
*
* This overload register a method callback that will have a freedom to execute
* its body in asynchronous contexts, and send the results from those contexts.
* This can help in e.g. long operations, which then don't block the D-Bus processing
* loop thread.
*
* @throws sdbus::Error in case of failure
*/
virtual void registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, async_method_callback asyncMethodCallback
, Flags flags = {} ) = 0;
/*!
* @brief Registers signal that the object will emit on D-Bus
*

View File

@@ -31,43 +31,11 @@
// Forward declaration
namespace sdbus {
namespace internal {
class Object;
}
class Error;
}
namespace sdbus {
/********************************************//**
* @class MethodResult
*
* Represents result of an asynchronous server-side method.
* An instance is provided to the method and shall be set
* by the method to either method return value or an error.
*
***********************************************/
class MethodResult
{
protected:
friend sdbus::internal::Object;
MethodResult() = default;
MethodResult(MethodCall msg);
MethodResult(const MethodResult&) = delete;
MethodResult& operator=(const MethodResult&) = delete;
MethodResult(MethodResult&& other) = default;
MethodResult& operator=(MethodResult&& other) = default;
template <typename... _Results> void returnResults(const _Results&... results) const;
void returnError(const Error& error) const;
private:
MethodCall call_;
};
/********************************************//**
* @class Result
*
@@ -77,18 +45,52 @@ namespace sdbus {
*
***********************************************/
template <typename... _Results>
class Result : protected MethodResult
class Result
{
public:
Result() = default;
Result(MethodResult&& result);
Result(MethodCall call);
Result(const Result&) = delete;
Result& operator=(const Result&) = delete;
Result(Result&& other) = default;
Result& operator=(Result&& other) = default;
void returnResults(const _Results&... results) const;
void returnError(const Error& error) const;
private:
MethodCall call_;
};
template <typename... _Results>
inline Result<_Results...>::Result(MethodCall call)
: call_(std::move(call))
{
}
template <typename... _Results>
inline void Result<_Results...>::returnResults(const _Results&... results) const
{
assert(call_.isValid());
auto reply = call_.createReply();
#ifdef __cpp_fold_expressions
(reply << ... << results);
#else
using _ = std::initializer_list<int>;
(void)_{(void(reply << results), 0)...};
#endif
reply.send();
}
template <typename... _Results>
inline void Result<_Results...>::returnError(const Error& error) const
{
auto reply = call_.createErrorReply(error);
reply.send();
}
}
#include <sdbus-c++/MethodResult.inl>
#endif /* SDBUS_CXX_METHODRESULT_H_ */

View File

@@ -1,79 +0,0 @@
/**
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file MethodResult.inl
*
* Created on: Mar 21, 2019
* 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_METHODRESULT_INL_
#define SDBUS_CXX_METHODRESULT_INL_
#include <sdbus-c++/MethodResult.h>
#include <cassert>
namespace sdbus {
inline MethodResult::MethodResult(MethodCall msg)
: call_(std::move(msg))
{
}
template <typename... _Results>
inline void MethodResult::returnResults(const _Results&... results) const
{
assert(call_.isValid());
auto reply = call_.createReply();
#ifdef __cpp_fold_expressions
(reply << ... << results);
#else
using _ = std::initializer_list<int>;
(void)_{(void(reply << results), 0)...};
#endif
reply.send();
}
inline void MethodResult::returnError(const Error& error) const
{
auto reply = call_.createErrorReply(error);
reply.send();
}
template <typename... _Results>
inline Result<_Results...>::Result(MethodResult&& result)
: MethodResult(std::move(result))
{
}
template <typename... _Results>
inline void Result<_Results...>::returnResults(const _Results&... results) const
{
MethodResult::returnResults(results...);
}
template <typename... _Results>
inline void Result<_Results...>::returnError(const Error& error) const
{
MethodResult::returnError(error);
}
}
#endif /* SDBUS_CXX_METHODRESULT_INL_ */

View File

@@ -44,15 +44,13 @@ namespace sdbus {
class MethodCall;
class MethodReply;
class Signal;
class MethodResult;
template <typename... _Results> class Result;
class Error;
}
namespace sdbus {
using method_callback = std::function<void(MethodCall& msg, MethodReply& reply)>;
using async_method_callback = std::function<void(MethodCall msg, MethodResult&& result)>;
using method_callback = std::function<void(MethodCall msg)>;
using async_reply_handler = std::function<void(MethodReply& reply, const Error* error)>;
using signal_handler = std::function<void(Signal& signal)>;
using property_set_callback = std::function<void(Message& msg)>;
@@ -381,6 +379,7 @@ namespace sdbus {
: public function_traits_base<std::tuple<_Results...>, _Args...>
{
static constexpr bool is_async = true;
using async_result_t = Result<_Results...>;
};
template <typename... _Args, typename... _Results>
@@ -388,6 +387,7 @@ namespace sdbus {
: public function_traits_base<std::tuple<_Results...>, _Args...>
{
static constexpr bool is_async = true;
using async_result_t = Result<_Results...>;
};
template <typename _ReturnType, typename... _Args>
@@ -504,9 +504,9 @@ namespace sdbus {
namespace detail
{
template <class _Function, class _Tuple, std::size_t... _I>
template <class _Function, class _Tuple, typename... _Args, std::size_t... _I>
constexpr decltype(auto) apply_impl( _Function&& f
, MethodResult&& r
, Result<_Args...>&& r
, _Tuple&& t
, std::index_sequence<_I...> )
{
@@ -558,8 +558,8 @@ namespace sdbus {
// Convert tuple `t' of values into a list of arguments
// and invoke function `f' with those arguments.
template <class _Function, class _Tuple>
constexpr decltype(auto) apply(_Function&& f, MethodResult&& r, _Tuple&& t)
template <class _Function, class _Tuple, typename... _Args>
constexpr decltype(auto) apply(_Function&& f, Result<_Args...>&& r, _Tuple&& t)
{
return detail::apply_impl( std::forward<_Function>(f)
, std::move(r)

View File

@@ -39,12 +39,13 @@ MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodN
}
MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destructors must
{ // explicitly be allowed to throw
{ // 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;
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
SDBUS_THROW_ERROR_IF(!methodCallback_, "Method handler not specified when registering a DBus method", EINVAL);
// 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
@@ -55,12 +56,7 @@ MethodRegistrator::~MethodRegistrator() noexcept(false) // since C++11, destruct
// 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.
if (syncCallback_)
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(syncCallback_), flags_);
else if(asyncCallback_)
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(asyncCallback_), flags_);
else
SDBUS_THROW_ERROR("Method handler not specified when registering a DBus method", EINVAL);
object_.registerMethod(interfaceName_, methodName_, inputSignature_, outputSignature_, std::move(methodCallback_), flags_);
}
SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)

View File

@@ -51,37 +51,8 @@ void Object::registerMethod( const std::string& interfaceName
{
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
auto syncCallback = [callback = std::move(methodCallback)](MethodCall& msg)
{
auto reply = msg.createReply();
callback(msg, reply);
reply.send();
};
auto& interface = interfaces_[interfaceName];
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(syncCallback), flags};
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
}
void Object::registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, async_method_callback asyncMethodCallback
, Flags flags )
{
SDBUS_THROW_ERROR_IF(!asyncMethodCallback, "Invalid method callback provided", EINVAL);
auto asyncCallback = [callback = std::move(asyncMethodCallback)](MethodCall& msg)
{
MethodResult result{msg};
callback(std::move(msg), std::move(result));
};
auto& interface = interfaces_[interfaceName];
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(asyncCallback), flags};
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback), flags};
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
@@ -245,7 +216,7 @@ int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData,
try
{
callback(message);
callback(std::move(message));
}
catch (const sdbus::Error& e)
{

View File

@@ -52,13 +52,6 @@ namespace internal {
, method_callback methodCallback
, Flags flags ) override;
void registerMethod( const std::string& interfaceName
, const std::string& methodName
, const std::string& inputSignature
, const std::string& outputSignature
, async_method_callback asyncMethodCallback
, Flags flags ) override;
void registerSignal( const std::string& interfaceName
, const std::string& signalName
, const std::string& signature
@@ -93,7 +86,7 @@ namespace internal {
{
std::string inputArgs_;
std::string outputArgs_;
std::function<void(MethodCall&)> callback_;
method_callback callback_;
Flags flags_;
};
std::map<MethodName, MethodData> methods_;