forked from Kistler-Group/sdbus-cpp
Introduce support for asynchronous server-side methods (#12)
* Add preliminary changes for async server methods * Refactor the Message concept and break it into distinctive types * Continue working on async server methods (high-level API mainly) * Continue developing support for async server * Finishing async server methods * Finishing async server methods (fixing tests & cleaning up) * A little code cleaning * Add unit tests for type traits of free functions * Support for generating async server methods in stub headers * Update ChangeLog for v0.3.0 * Update the tutorial with how to use async server-side methods * Update the TOC in sdbus-c++ tutorial * Update numbering in TOC * Remove unnecessary code * Final cleanups
This commit is contained in:
committed by
Lukáš Ďurfina
parent
b041f76bfc
commit
d8fd053714
@ -15,3 +15,11 @@ v0.2.5
|
|||||||
|
|
||||||
v0.2.6
|
v0.2.6
|
||||||
- Fixed memory leak in Message copy operations
|
- Fixed memory leak in Message copy operations
|
||||||
|
|
||||||
|
v0.3.0
|
||||||
|
- Introduced support for asynchronous server-side methods
|
||||||
|
- Refactored the concept of Message into distinctive concrete message types
|
||||||
|
- As this release comes with breaking API changes:
|
||||||
|
* if you're using lower-level API, please rename 'Message' to whatever concrete message type (MethodCall, MethodReply, Signal) is needed there,
|
||||||
|
* if you're using higher-level API, please re-compile and re-link your application against sdbus-c++,
|
||||||
|
* if you're using generated stub headers, please re-compile and re-link your application against sdbus-c++.
|
||||||
|
@ -4,8 +4,8 @@ AC_PREREQ(2.61)
|
|||||||
# odd micro numbers indicate in-progress development
|
# odd micro numbers indicate in-progress development
|
||||||
# even micro numbers indicate released versions
|
# even micro numbers indicate released versions
|
||||||
m4_define(sdbus_cpp_version_major, 0)
|
m4_define(sdbus_cpp_version_major, 0)
|
||||||
m4_define(sdbus_cpp_version_minor, 2)
|
m4_define(sdbus_cpp_version_minor, 3)
|
||||||
m4_define(sdbus_cpp_version_micro, 6)
|
m4_define(sdbus_cpp_version_micro, 0)
|
||||||
|
|
||||||
m4_define([sdbus_cpp_version],
|
m4_define([sdbus_cpp_version],
|
||||||
[sdbus_cpp_version_major.sdbus_cpp_version_minor.sdbus_cpp_version_micro])
|
[sdbus_cpp_version_major.sdbus_cpp_version_minor.sdbus_cpp_version_micro])
|
||||||
|
@ -12,8 +12,9 @@ Using sdbus-c++ library
|
|||||||
7. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
|
7. [Implementing the Concatenator example using basic sdbus-c++ API layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer)
|
||||||
8. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
|
8. [Implementing the Concatenator example using convenience sdbus-c++ API layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer)
|
||||||
9. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs)
|
9. [Implementing the Concatenator example using sdbus-c++-generated stubs](#implementing-the-concatenator-example-using-sdbus-c-generated-stubs)
|
||||||
10. [Using D-Bus properties](#using-d-bus-properties)
|
10. [Asynchronous server-side methods](#asynchronous-server-side-methods)
|
||||||
11. [Conclusion](#conclusion)
|
11. [Using D-Bus properties](#using-d-bus-properties)
|
||||||
|
12. [Conclusion](#conclusion)
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
@ -92,10 +93,10 @@ The following diagram illustrates the major entities in sdbus-c++.
|
|||||||
* invoking remote methods of the corresponding object,
|
* invoking remote methods of the corresponding object,
|
||||||
* registering handlers for signals.
|
* registering handlers for signals.
|
||||||
|
|
||||||
`Message` class represents a message, which is the fundamental DBus concept. The message can be
|
`Message` class represents a message, which is the fundamental DBus concept. There are three distinctive types of message that derive from the `Message` class:
|
||||||
* a method call (with serialized parameters),
|
* `MethodCall` (with serialized parameters),
|
||||||
* a method reply (with serialized return values),
|
* `MethodReply` (with serialized return values),
|
||||||
* or a signal (with serialized parameters).
|
* or a `Signal` (with serialized parameters).
|
||||||
|
|
||||||
Multiple layers of sdbus-c++ API
|
Multiple layers of sdbus-c++ API
|
||||||
-------------------------------
|
-------------------------------
|
||||||
@ -138,15 +139,15 @@ Overloaded versions of C++ insertion/extraction operators are used for serializa
|
|||||||
// to emit signals.
|
// to emit signals.
|
||||||
sdbus::IObject* g_concatenator{};
|
sdbus::IObject* g_concatenator{};
|
||||||
|
|
||||||
void concatenate(sdbus::Message& msg, sdbus::Message& reply)
|
void concatenate(sdbus::MethodCall& call, sdbus::MethodReply& reply)
|
||||||
{
|
{
|
||||||
// Deserialize the collection of numbers from the message
|
// Deserialize the collection of numbers from the message
|
||||||
std::vector<int> numbers;
|
std::vector<int> numbers;
|
||||||
msg >> numbers;
|
call >> numbers;
|
||||||
|
|
||||||
// Deserialize separator from the message
|
// Deserialize separator from the message
|
||||||
std::string separator;
|
std::string separator;
|
||||||
msg >> separator;
|
call >> separator;
|
||||||
|
|
||||||
// Return error if there are no numbers in the collection
|
// Return error if there are no numbers in the collection
|
||||||
if (numbers.empty())
|
if (numbers.empty())
|
||||||
@ -163,9 +164,9 @@ void concatenate(sdbus::Message& msg, sdbus::Message& reply)
|
|||||||
|
|
||||||
// Emit 'concatenated' signal
|
// Emit 'concatenated' signal
|
||||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||||
auto signalMsg = g_concatenator->createSignal(interfaceName, "concatenated");
|
auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||||
signalMsg << result;
|
signal << result;
|
||||||
g_concatenator->emitSignal(signalMsg);
|
g_concatenator->emitSignal(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@ -200,10 +201,10 @@ int main(int argc, char *argv[])
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
void onConcatenated(sdbus::Message& signalMsg)
|
void onConcatenated(sdbus::Signal& signal)
|
||||||
{
|
{
|
||||||
std::string concatenatedString;
|
std::string concatenatedString;
|
||||||
msg >> concatenatedString;
|
signal >> concatenatedString;
|
||||||
|
|
||||||
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
|
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
|
||||||
}
|
}
|
||||||
@ -666,6 +667,104 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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. How physically is that done is up to the server design (e.g. thread pool), but sdbus-c++ provides API supporting async methods.
|
||||||
|
|
||||||
|
### Lower-level 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):
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void concatenate(sdbus::MethodCall& call, sdbus::MethodResult result)
|
||||||
|
{
|
||||||
|
// Deserialize the collection of numbers from the message
|
||||||
|
std::vector<int> numbers;
|
||||||
|
call >> numbers;
|
||||||
|
|
||||||
|
// Deserialize separator from the message
|
||||||
|
std::string separator;
|
||||||
|
call >> separator;
|
||||||
|
|
||||||
|
// Launch a thread for async execution...
|
||||||
|
std::thread([numbers, separator, result = std::move(result)]()
|
||||||
|
{
|
||||||
|
// 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");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string concatenatedStr;
|
||||||
|
for (auto number : numbers)
|
||||||
|
{
|
||||||
|
concatenatedStr += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will send the reply message back to the client
|
||||||
|
result.returnResults(concatenatedStr);
|
||||||
|
|
||||||
|
// Note: emitting signals from other than D-Bus dispatcher thread is not supported yet...
|
||||||
|
/*
|
||||||
|
// Emit 'concatenated' signal
|
||||||
|
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||||
|
auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||||
|
signal << result;
|
||||||
|
g_concatenator->emitSignal(signal);
|
||||||
|
*/
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice these differences as compared to the synchronous version:
|
||||||
|
|
||||||
|
* Instead of `MethodReply` message given by reference, there is `MethodResult` as a second parameter of the callback, which will hold method results and can be written to from any thread.
|
||||||
|
* You shall pass the result holder (`MethodResult` instance) by moving it to the thread of execution, and eventually write method results (or method error) to it via its `returnResults()` or `returnError()` method, respectively.
|
||||||
|
* Unlike in sync methods, reporting errors cannot be done by throwing sdbus::Error, since the execution takes place out of 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.
|
||||||
|
|
||||||
|
Note: We can use the concept of asynchronous D-Bus methods in both the synchronous and asynchronous way. Whether we return the results directly in the callback in the synchronous way, or we pass the arguments and the result holder to a different thread, and compute and set the results in there, is irrelevant to sdbus-c++. This has the benefit that we can decide at run-time, per each method call, whether we execute it synchronously or (perhaps in case of complex operation) execute it asynchronously to e.g. a thread pool.
|
||||||
|
|
||||||
|
### Convenience API
|
||||||
|
|
||||||
|
Method callbacks in convenience sdbus-c++ API also need to take the result object as a parameter. 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 callback itself is physically a void-returning function.
|
||||||
|
|
||||||
|
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, const std::vector<int32_t>& numbers, const std::string& separator)`.
|
||||||
|
|
||||||
|
`sdbus::Result` class template has effectively the same API as `sdbus::MethodResult` class from 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.
|
||||||
|
|
||||||
|
Nothing else has to be changed. The registration of the method callback (`implementedAs`) and all the mechanics around remains completely the same.
|
||||||
|
|
||||||
|
### Marking async methods in the IDL
|
||||||
|
|
||||||
|
sdbus-c++ stub generator can generate stub code for server-side async methods. We just need to annotate the method with the `annotate` element having the "org.freedesktop.DBus.Method.Async" name, like so:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<node name="/org/sdbuscpp/concatenator">
|
||||||
|
<interface name="org.sdbuscpp.Concatenator">
|
||||||
|
<method name="concatenate">
|
||||||
|
<annotation name="org.freedesktop.DBus.Method.Async" value="server" />
|
||||||
|
<arg type="ai" name="numbers" direction="in" />
|
||||||
|
<arg type="s" name="separator" direction="in" />
|
||||||
|
<arg type="s" name="concatenatedString" direction="out" />
|
||||||
|
</method>
|
||||||
|
<signal name="concatenated">
|
||||||
|
<arg type="s" name="concatenatedString" />
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
|
```
|
||||||
|
|
||||||
Using D-Bus properties
|
Using D-Bus properties
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ libsdbuscpp_HEADERS = \
|
|||||||
$(HEADER_DIR)/IObject.h \
|
$(HEADER_DIR)/IObject.h \
|
||||||
$(HEADER_DIR)/IObjectProxy.h \
|
$(HEADER_DIR)/IObjectProxy.h \
|
||||||
$(HEADER_DIR)/Message.h \
|
$(HEADER_DIR)/Message.h \
|
||||||
|
$(HEADER_DIR)/MethodResult.h \
|
||||||
$(HEADER_DIR)/Types.h \
|
$(HEADER_DIR)/Types.h \
|
||||||
$(HEADER_DIR)/TypeTraits.h \
|
$(HEADER_DIR)/TypeTraits.h \
|
||||||
$(HEADER_DIR)/sdbus-c++.h
|
$(HEADER_DIR)/sdbus-c++.h
|
||||||
|
@ -27,7 +27,9 @@
|
|||||||
#define SDBUS_CXX_CONVENIENCECLASSES_H_
|
#define SDBUS_CXX_CONVENIENCECLASSES_H_
|
||||||
|
|
||||||
#include <sdbus-c++/Message.h>
|
#include <sdbus-c++/Message.h>
|
||||||
|
#include <sdbus-c++/TypeTraits.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
@ -43,7 +45,10 @@ namespace sdbus {
|
|||||||
public:
|
public:
|
||||||
MethodRegistrator(IObject& object, const std::string& methodName);
|
MethodRegistrator(IObject& object, const std::string& methodName);
|
||||||
MethodRegistrator& onInterface(const std::string& interfaceName);
|
MethodRegistrator& onInterface(const std::string& interfaceName);
|
||||||
template <typename _Function> void implementedAs(_Function&& callback);
|
template <typename _Function>
|
||||||
|
std::enable_if_t<!is_async_method_v<_Function>> implementedAs(_Function&& callback);
|
||||||
|
template <typename _Function>
|
||||||
|
std::enable_if_t<is_async_method_v<_Function>> implementedAs(_Function&& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IObject& object_;
|
IObject& object_;
|
||||||
@ -103,7 +108,7 @@ namespace sdbus {
|
|||||||
private:
|
private:
|
||||||
IObject& object_;
|
IObject& object_;
|
||||||
const std::string& signalName_;
|
const std::string& signalName_;
|
||||||
Message signal_;
|
Signal signal_;
|
||||||
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,7 +127,7 @@ namespace sdbus {
|
|||||||
private:
|
private:
|
||||||
IObjectProxy& objectProxy_;
|
IObjectProxy& objectProxy_;
|
||||||
const std::string& methodName_;
|
const std::string& methodName_;
|
||||||
Message method_;
|
MethodCall method_;
|
||||||
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
||||||
bool methodCalled_{};
|
bool methodCalled_{};
|
||||||
};
|
};
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <sdbus-c++/IObject.h>
|
#include <sdbus-c++/IObject.h>
|
||||||
#include <sdbus-c++/IObjectProxy.h>
|
#include <sdbus-c++/IObjectProxy.h>
|
||||||
#include <sdbus-c++/Message.h>
|
#include <sdbus-c++/Message.h>
|
||||||
|
#include <sdbus-c++/MethodResult.h>
|
||||||
#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>
|
||||||
@ -52,7 +53,7 @@ namespace sdbus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename _Function>
|
template <typename _Function>
|
||||||
inline void MethodRegistrator::implementedAs(_Function&& callback)
|
inline std::enable_if_t<!is_async_method_v<_Function>> MethodRegistrator::implementedAs(_Function&& callback)
|
||||||
{
|
{
|
||||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ namespace sdbus {
|
|||||||
, methodName_
|
, methodName_
|
||||||
, signature_of_function_input_arguments<_Function>::str()
|
, signature_of_function_input_arguments<_Function>::str()
|
||||||
, signature_of_function_output_arguments<_Function>::str()
|
, signature_of_function_output_arguments<_Function>::str()
|
||||||
, [callback = std::forward<_Function>(callback)](Message& msg, Message& reply)
|
, [callback = std::forward<_Function>(callback)](MethodCall& msg, MethodReply& reply)
|
||||||
{
|
{
|
||||||
// Create a tuple of callback input arguments' types, which will be used
|
// Create a tuple of callback input arguments' types, which will be used
|
||||||
// as a storage for the argument values deserialized from the message.
|
// as a storage for the argument values deserialized from the message.
|
||||||
@ -80,6 +81,29 @@ namespace sdbus {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
inline std::enable_if_t<is_async_method_v<_Function>> MethodRegistrator::implementedAs(_Function&& callback)
|
||||||
|
{
|
||||||
|
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus method", EINVAL);
|
||||||
|
|
||||||
|
object_.registerMethod( interfaceName_
|
||||||
|
, methodName_
|
||||||
|
, signature_of_function_input_arguments<_Function>::str()
|
||||||
|
, signature_of_function_output_arguments<_Function>::str() //signature_of<last_function_argument_t<_Function>>::str() // because last argument contains output types
|
||||||
|
, [callback = std::forward<_Function>(callback)](MethodCall& msg, MethodResult result)
|
||||||
|
{
|
||||||
|
// 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,
|
||||||
|
// plus store the result object as a last item of the tuple.
|
||||||
|
msg >> inputArgs;
|
||||||
|
|
||||||
|
// Invoke callback with input arguments from the tuple.
|
||||||
|
apply(callback, std::move(result), inputArgs); // TODO: Use std::apply when switching to full C++17 support
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Moved into the library to isolate from C++17 dependency
|
// Moved into the library to isolate from C++17 dependency
|
||||||
/*
|
/*
|
||||||
@ -339,7 +363,7 @@ namespace sdbus {
|
|||||||
|
|
||||||
objectProxy_.registerSignalHandler( interfaceName_
|
objectProxy_.registerSignalHandler( interfaceName_
|
||||||
, signalName_
|
, signalName_
|
||||||
, [callback = std::forward<_Function>(callback)](Message& signal)
|
, [callback = std::forward<_Function>(callback)](Signal& signal)
|
||||||
{
|
{
|
||||||
// Create a tuple of callback input arguments' types, which will be used
|
// Create a tuple of callback input arguments' types, which will be used
|
||||||
// as a storage for the argument values deserialized from the signal message.
|
// as a storage for the argument values deserialized from the signal message.
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
class Message;
|
class Signal;
|
||||||
class IConnection;
|
class IConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,6 +70,28 @@ namespace sdbus {
|
|||||||
, const std::string& outputSignature
|
, const std::string& outputSignature
|
||||||
, method_callback methodCallback ) = 0;
|
, method_callback methodCallback ) = 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
|
||||||
|
*
|
||||||
|
* 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 ) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Registers signal that the object will emit on D-Bus
|
* @brief Registers signal that the object will emit on D-Bus
|
||||||
*
|
*
|
||||||
@ -138,7 +160,7 @@ namespace sdbus {
|
|||||||
*
|
*
|
||||||
* @throws sdbus::Error in case of failure
|
* @throws sdbus::Error in case of failure
|
||||||
*/
|
*/
|
||||||
virtual Message createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
|
virtual Signal createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Emits signal on D-Bus
|
* @brief Emits signal on D-Bus
|
||||||
@ -149,7 +171,7 @@ namespace sdbus {
|
|||||||
*
|
*
|
||||||
* @throws sdbus::Error in case of failure
|
* @throws sdbus::Error in case of failure
|
||||||
*/
|
*/
|
||||||
virtual void emitSignal(const sdbus::Message& message) = 0;
|
virtual void emitSignal(const sdbus::Signal& message) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Registers method that the object will provide on D-Bus
|
* @brief Registers method that the object will provide on D-Bus
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
class Message;
|
class MethodCall;
|
||||||
|
class MethodReply;
|
||||||
class IConnection;
|
class IConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ namespace sdbus {
|
|||||||
*
|
*
|
||||||
* @throws sdbus::Error in case of failure
|
* @throws sdbus::Error in case of failure
|
||||||
*/
|
*/
|
||||||
virtual Message createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
|
virtual MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Calls method on the proxied D-Bus object
|
* @brief Calls method on the proxied D-Bus object
|
||||||
@ -76,7 +77,7 @@ namespace sdbus {
|
|||||||
*
|
*
|
||||||
* @throws sdbus::Error in case of failure
|
* @throws sdbus::Error in case of failure
|
||||||
*/
|
*/
|
||||||
virtual Message callMethod(const sdbus::Message& message) = 0;
|
virtual MethodReply callMethod(const sdbus::MethodCall& message) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
|
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
|
||||||
|
@ -44,6 +44,12 @@ namespace sdbus {
|
|||||||
class ObjectPath;
|
class ObjectPath;
|
||||||
class Signature;
|
class Signature;
|
||||||
template <typename... _ValueTypes> class Struct;
|
template <typename... _ValueTypes> class Struct;
|
||||||
|
|
||||||
|
class Message;
|
||||||
|
class MethodCall;
|
||||||
|
class MethodReply;
|
||||||
|
class Signal;
|
||||||
|
template <typename... _Results> class Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
@ -65,16 +71,8 @@ namespace sdbus {
|
|||||||
class Message
|
class Message
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
ePlainMessage
|
|
||||||
, eMethodCall
|
|
||||||
, eMethodReply
|
|
||||||
, eSignal
|
|
||||||
};
|
|
||||||
|
|
||||||
Message() = default;
|
Message() = default;
|
||||||
Message(void *msg, Type type = Type::ePlainMessage) noexcept;
|
Message(void *msg) noexcept;
|
||||||
Message(const Message&) noexcept;
|
Message(const Message&) noexcept;
|
||||||
Message& operator=(const Message&) noexcept;
|
Message& operator=(const Message&) noexcept;
|
||||||
Message(Message&& other) noexcept;
|
Message(Message&& other) noexcept;
|
||||||
@ -137,21 +135,42 @@ namespace sdbus {
|
|||||||
void peekType(std::string& type, std::string& contents) const;
|
void peekType(std::string& type, std::string& contents) const;
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
Type getType() const;
|
|
||||||
|
|
||||||
void copyTo(Message& destination, bool complete) const;
|
void copyTo(Message& destination, bool complete) const;
|
||||||
void seal();
|
void seal();
|
||||||
void rewind(bool complete);
|
void rewind(bool complete);
|
||||||
|
|
||||||
Message createReply() const;
|
protected:
|
||||||
Message send() const;
|
void* getMsg() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void* msg_{};
|
void* msg_{};
|
||||||
Type type_{Type::ePlainMessage};
|
|
||||||
mutable bool ok_{true};
|
mutable bool ok_{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MethodCall : public Message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Message::Message;
|
||||||
|
MethodReply send() const;
|
||||||
|
MethodReply createReply() const;
|
||||||
|
MethodReply createErrorReply(const sdbus::Error& error) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MethodReply : public Message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Message::Message;
|
||||||
|
void send() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Signal : public Message
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Message::Message;
|
||||||
|
void send() const;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename _Element>
|
template <typename _Element>
|
||||||
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
|
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
|
||||||
{
|
{
|
||||||
|
126
include/sdbus-c++/MethodResult.h
Executable file
126
include/sdbus-c++/MethodResult.h
Executable file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||||
|
*
|
||||||
|
* @file ConvenienceClasses.h
|
||||||
|
*
|
||||||
|
* Created on: Nov 8, 2016
|
||||||
|
* Project: sdbus-c++
|
||||||
|
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||||
|
*
|
||||||
|
* This file is part of sdbus-c++.
|
||||||
|
*
|
||||||
|
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SDBUS_CXX_METHODRESULT_H_
|
||||||
|
#define SDBUS_CXX_METHODRESULT_H_
|
||||||
|
|
||||||
|
#include <sdbus-c++/Message.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
// 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(const MethodCall& msg, sdbus::internal::Object& object);
|
||||||
|
|
||||||
|
template <typename... _Results> void returnResults(const _Results&... results) const;
|
||||||
|
void returnError(const Error& error) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void send(const MethodReply& reply) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MethodCall call_;
|
||||||
|
sdbus::internal::Object* object_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
send(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void MethodResult::returnError(const Error& error) const
|
||||||
|
{
|
||||||
|
auto reply = call_.createErrorReply(error);
|
||||||
|
send(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************//**
|
||||||
|
* @class Result
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
***********************************************/
|
||||||
|
template <typename... _Results>
|
||||||
|
class Result : protected MethodResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Result() = default;
|
||||||
|
Result(MethodResult result);
|
||||||
|
void returnResults(const _Results&... results) const;
|
||||||
|
void returnError(const Error& error) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
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_H_ */
|
@ -26,6 +26,7 @@
|
|||||||
#ifndef SDBUS_CXX_TYPETRAITS_H_
|
#ifndef SDBUS_CXX_TYPETRAITS_H_
|
||||||
#define SDBUS_CXX_TYPETRAITS_H_
|
#define SDBUS_CXX_TYPETRAITS_H_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -40,16 +41,21 @@ namespace sdbus {
|
|||||||
class ObjectPath;
|
class ObjectPath;
|
||||||
class Signature;
|
class Signature;
|
||||||
class Message;
|
class Message;
|
||||||
|
class MethodCall;
|
||||||
|
class MethodReply;
|
||||||
|
class Signal;
|
||||||
|
class MethodResult;
|
||||||
|
template <typename... _Results> class Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
|
|
||||||
using method_callback = std::function<void(Message& msg, Message& reply)>;
|
using method_callback = std::function<void(MethodCall& msg, MethodReply& reply)>;
|
||||||
using signal_handler = std::function<void(Message& signal)>;
|
using async_method_callback = std::function<void(MethodCall& msg, MethodResult result)>;
|
||||||
|
using signal_handler = std::function<void(Signal& signal)>;
|
||||||
using property_set_callback = std::function<void(Message& msg)>;
|
using property_set_callback = std::function<void(Message& msg)>;
|
||||||
using property_get_callback = std::function<void(Message& reply)>;
|
using property_get_callback = std::function<void(Message& reply)>;
|
||||||
|
|
||||||
// Primary template
|
|
||||||
template <typename _T>
|
template <typename _T>
|
||||||
struct signature_of
|
struct signature_of
|
||||||
{
|
{
|
||||||
@ -301,6 +307,9 @@ namespace sdbus {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Function traits implementation inspired by (c) kennytm,
|
||||||
|
// https://github.com/kennytm/utils/blob/master/traits.hpp
|
||||||
template <typename _Type>
|
template <typename _Type>
|
||||||
struct function_traits
|
struct function_traits
|
||||||
: public function_traits<decltype(&_Type::operator())>
|
: public function_traits<decltype(&_Type::operator())>
|
||||||
@ -316,18 +325,32 @@ namespace sdbus {
|
|||||||
: public function_traits<_Type>
|
: public function_traits<_Type>
|
||||||
{};
|
{};
|
||||||
|
|
||||||
// Function traits implementation inspired by (c) kennytm,
|
|
||||||
// https://github.com/kennytm/utils/blob/master/traits.hpp
|
|
||||||
template <typename _ReturnType, typename... _Args>
|
template <typename _ReturnType, typename... _Args>
|
||||||
struct function_traits<_ReturnType(_Args...)>
|
struct function_traits_base
|
||||||
{
|
{
|
||||||
typedef _ReturnType result_type;
|
typedef _ReturnType result_type;
|
||||||
typedef std::tuple<_Args...> arguments_type;
|
typedef std::tuple<_Args...> arguments_type;
|
||||||
|
typedef std::tuple<std::decay_t<_Args>...> decayed_arguments_type;
|
||||||
|
|
||||||
typedef _ReturnType function_type(_Args...);
|
typedef _ReturnType function_type(_Args...);
|
||||||
|
|
||||||
static constexpr std::size_t arity = sizeof...(_Args);
|
static constexpr std::size_t arity = sizeof...(_Args);
|
||||||
|
|
||||||
|
// template <size_t _Idx, typename _Enabled = void>
|
||||||
|
// struct arg;
|
||||||
|
//
|
||||||
|
// template <size_t _Idx>
|
||||||
|
// struct arg<_Idx, std::enable_if_t<(_Idx < arity)>>
|
||||||
|
// {
|
||||||
|
// typedef std::tuple_element_t<_Idx, arguments_type> type;
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// template <size_t _Idx>
|
||||||
|
// struct arg<_Idx, std::enable_if_t<!(_Idx < arity)>>
|
||||||
|
// {
|
||||||
|
// typedef void type;
|
||||||
|
// };
|
||||||
|
|
||||||
template <size_t _Idx>
|
template <size_t _Idx>
|
||||||
struct arg
|
struct arg
|
||||||
{
|
{
|
||||||
@ -338,6 +361,20 @@ namespace sdbus {
|
|||||||
using arg_t = typename arg<_Idx>::type;
|
using arg_t = typename arg<_Idx>::type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename _ReturnType, typename... _Args>
|
||||||
|
struct function_traits<_ReturnType(_Args...)>
|
||||||
|
: public function_traits_base<_ReturnType, _Args...>
|
||||||
|
{
|
||||||
|
static constexpr bool is_async = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... _Args, typename... _Results>
|
||||||
|
struct function_traits<void(Result<_Results...>, _Args...)>
|
||||||
|
: public function_traits_base<std::tuple<_Results...>, _Args...>
|
||||||
|
{
|
||||||
|
static constexpr bool is_async = true;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename _ReturnType, typename... _Args>
|
template <typename _ReturnType, typename... _Args>
|
||||||
struct function_traits<_ReturnType(*)(_Args...)>
|
struct function_traits<_ReturnType(*)(_Args...)>
|
||||||
: public function_traits<_ReturnType(_Args...)>
|
: public function_traits<_ReturnType(_Args...)>
|
||||||
@ -376,12 +413,39 @@ namespace sdbus {
|
|||||||
: public function_traits<FunctionType>
|
: public function_traits<FunctionType>
|
||||||
{};
|
{};
|
||||||
|
|
||||||
|
template <class _Function>
|
||||||
|
constexpr auto is_async_method_v = function_traits<_Function>::is_async;
|
||||||
|
|
||||||
|
template <typename _FunctionType>
|
||||||
|
using function_arguments_t = typename function_traits<_FunctionType>::arguments_type;
|
||||||
|
|
||||||
template <typename _FunctionType, size_t _Idx>
|
template <typename _FunctionType, size_t _Idx>
|
||||||
using function_argument_t = typename function_traits<_FunctionType>::template arg_t<_Idx>;
|
using function_argument_t = typename function_traits<_FunctionType>::template arg_t<_Idx>;
|
||||||
|
|
||||||
|
template <typename _FunctionType>
|
||||||
|
constexpr auto function_argument_count_v = function_traits<_FunctionType>::arity;
|
||||||
|
|
||||||
template <typename _FunctionType>
|
template <typename _FunctionType>
|
||||||
using function_result_t = typename function_traits<_FunctionType>::result_type;
|
using function_result_t = typename function_traits<_FunctionType>::result_type;
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
struct tuple_of_function_input_arg_types
|
||||||
|
{
|
||||||
|
typedef typename function_traits<_Function>::decayed_arguments_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
using tuple_of_function_input_arg_types_t = typename tuple_of_function_input_arg_types<_Function>::type;
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
struct tuple_of_function_output_arg_types
|
||||||
|
{
|
||||||
|
typedef typename function_traits<_Function>::result_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename _Function>
|
||||||
|
using tuple_of_function_output_arg_types_t = typename tuple_of_function_output_arg_types<_Function>::type;
|
||||||
|
|
||||||
template <typename _Type>
|
template <typename _Type>
|
||||||
struct aggregate_signature
|
struct aggregate_signature
|
||||||
{
|
{
|
||||||
@ -396,6 +460,7 @@ namespace sdbus {
|
|||||||
{
|
{
|
||||||
static const std::string str()
|
static const std::string str()
|
||||||
{
|
{
|
||||||
|
// TODO: This could be a fold expression in C++17...
|
||||||
std::initializer_list<std::string> signatures{signature_of<std::decay_t<_Types>>::str()...};
|
std::initializer_list<std::string> signatures{signature_of<std::decay_t<_Types>>::str()...};
|
||||||
std::string signature;
|
std::string signature;
|
||||||
for (const auto& item : signatures)
|
for (const auto& item : signatures)
|
||||||
@ -404,28 +469,6 @@ namespace sdbus {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get a tuple of function input argument types from function signature.
|
|
||||||
// But first, convert provided function signature to the standardized form `out(in...)'.
|
|
||||||
template <typename _Function>
|
|
||||||
struct tuple_of_function_input_arg_types
|
|
||||||
: public tuple_of_function_input_arg_types<typename function_traits<_Function>::function_type>
|
|
||||||
{};
|
|
||||||
|
|
||||||
// Get a tuple of function input argument types from function signature.
|
|
||||||
// Function signature is expected in the standardized form `out(in...)'.
|
|
||||||
template <typename _ReturnType, typename... _Args>
|
|
||||||
struct tuple_of_function_input_arg_types<_ReturnType(_Args...)>
|
|
||||||
{
|
|
||||||
// Arguments may be cv-qualified and may be references, so we have to strip cv and references
|
|
||||||
// with decay_t in order to get real 'naked' types.
|
|
||||||
// Example: for a function with signature void(const int i, const std::vector<float> v, double d)
|
|
||||||
// the `type' will be `std::tuple<int, std::vector<float>, double>'.
|
|
||||||
typedef std::tuple<std::decay_t<_Args>...> type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename _Function>
|
|
||||||
using tuple_of_function_input_arg_types_t = typename tuple_of_function_input_arg_types<_Function>::type;
|
|
||||||
|
|
||||||
template <typename _Function>
|
template <typename _Function>
|
||||||
struct signature_of_function_input_arguments
|
struct signature_of_function_input_arguments
|
||||||
{
|
{
|
||||||
@ -440,12 +483,21 @@ namespace sdbus {
|
|||||||
{
|
{
|
||||||
static const std::string str()
|
static const std::string str()
|
||||||
{
|
{
|
||||||
return aggregate_signature<function_result_t<_Function>>::str();
|
return aggregate_signature<tuple_of_function_output_arg_types_t<_Function>>::str();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
template <class _Function, class _Tuple, std::size_t... _I>
|
||||||
|
constexpr decltype(auto) apply_impl( _Function&& f
|
||||||
|
, MethodResult&& r
|
||||||
|
, _Tuple&& t
|
||||||
|
, std::index_sequence<_I...> )
|
||||||
|
{
|
||||||
|
return std::forward<_Function>(f)(std::move(r), std::get<_I>(std::forward<_Tuple>(t))...);
|
||||||
|
}
|
||||||
|
|
||||||
// Version of apply_impl for functions returning non-void values.
|
// Version of apply_impl for functions returning non-void values.
|
||||||
// In this case just forward function return value.
|
// In this case just forward function return value.
|
||||||
template <class _Function, class _Tuple, std::size_t... _I>
|
template <class _Function, class _Tuple, std::size_t... _I>
|
||||||
@ -480,6 +532,16 @@ namespace sdbus {
|
|||||||
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
return detail::apply_impl( std::forward<_Function>(f)
|
||||||
|
, std::move(r)
|
||||||
|
, std::forward<_Tuple>(t)
|
||||||
|
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* SDBUS_CXX_TYPETRAITS_H_ */
|
#endif /* SDBUS_CXX_TYPETRAITS_H_ */
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <sdbus-c++/IObjectProxy.h>
|
#include <sdbus-c++/IObjectProxy.h>
|
||||||
#include <sdbus-c++/Interfaces.h>
|
#include <sdbus-c++/Interfaces.h>
|
||||||
#include <sdbus-c++/Message.h>
|
#include <sdbus-c++/Message.h>
|
||||||
|
#include <sdbus-c++/MethodResult.h>
|
||||||
#include <sdbus-c++/Types.h>
|
#include <sdbus-c++/Types.h>
|
||||||
#include <sdbus-c++/TypeTraits.h>
|
#include <sdbus-c++/TypeTraits.h>
|
||||||
#include <sdbus-c++/Introspection.h>
|
#include <sdbus-c++/Introspection.h>
|
||||||
|
@ -42,13 +42,13 @@ Connection::Connection(Connection::BusType type)
|
|||||||
|
|
||||||
finishHandshake(bus);
|
finishHandshake(bus);
|
||||||
|
|
||||||
exitLoopFd_ = createProcessingExitDescriptor();
|
notificationFd_ = createLoopNotificationDescriptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection::~Connection()
|
Connection::~Connection()
|
||||||
{
|
{
|
||||||
leaveProcessingLoop();
|
leaveProcessingLoop();
|
||||||
closeProcessingExitDescriptor(exitLoopFd_);
|
closeLoopNotificationDescriptor(notificationFd_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::requestName(const std::string& name)
|
void Connection::requestName(const std::string& name)
|
||||||
@ -67,13 +67,15 @@ void Connection::enterProcessingLoop()
|
|||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
auto processed = processPendingRequest(bus_.get());
|
auto processed = processPendingRequest();
|
||||||
if (processed)
|
if (processed)
|
||||||
continue; // Process next one
|
continue; // Process next one
|
||||||
|
|
||||||
auto success = waitForNextRequest(bus_.get(), exitLoopFd_);
|
auto success = waitForNextRequest();
|
||||||
if (!success)
|
if (!success)
|
||||||
break; // Exit processing loop
|
break; // Exit processing loop
|
||||||
|
if (success.asyncMsgsToProcess)
|
||||||
|
processAsynchronousMessages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,10 +114,10 @@ void Connection::removeObjectVTable(void* vtableHandle)
|
|||||||
sd_bus_slot_unref((sd_bus_slot *)vtableHandle);
|
sd_bus_slot_unref((sd_bus_slot *)vtableHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
sdbus::Message Connection::createMethodCall( const std::string& destination
|
sdbus::MethodCall Connection::createMethodCall( const std::string& destination
|
||||||
, const std::string& objectPath
|
, const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const std::string& methodName ) const
|
, const std::string& methodName ) const
|
||||||
{
|
{
|
||||||
sd_bus_message *sdbusMsg{};
|
sd_bus_message *sdbusMsg{};
|
||||||
|
|
||||||
@ -131,12 +133,12 @@ sdbus::Message Connection::createMethodCall( const std::string& destination
|
|||||||
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
|
||||||
|
|
||||||
return Message(sdbusMsg, Message::Type::eMethodCall);
|
return MethodCall(sdbusMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
sdbus::Message Connection::createSignal( const std::string& objectPath
|
sdbus::Signal Connection::createSignal( const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const std::string& signalName ) const
|
, const std::string& signalName ) const
|
||||||
{
|
{
|
||||||
sd_bus_message *sdbusSignal{};
|
sd_bus_message *sdbusSignal{};
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ sdbus::Message Connection::createSignal( const std::string& objectPath
|
|||||||
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
|
||||||
|
|
||||||
return Message(sdbusSignal, Message::Type::eSignal);
|
return Signal(sdbusSignal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* Connection::registerSignalHandler( const std::string& objectPath
|
void* Connection::registerSignalHandler( const std::string& objectPath
|
||||||
@ -175,6 +177,13 @@ void Connection::unregisterSignalHandler(void* handlerCookie)
|
|||||||
sd_bus_slot_unref((sd_bus_slot *)handlerCookie);
|
sd_bus_slot_unref((sd_bus_slot *)handlerCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Connection::sendReplyAsynchronously(const sdbus::MethodReply& reply)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
asyncReplies_.push(reply);
|
||||||
|
notifyProcessingLoop();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<sdbus::internal::IConnection> Connection::clone() const
|
std::unique_ptr<sdbus::internal::IConnection> Connection::clone() const
|
||||||
{
|
{
|
||||||
return std::make_unique<sdbus::internal::Connection>(busType_);
|
return std::make_unique<sdbus::internal::Connection>(busType_);
|
||||||
@ -211,10 +220,8 @@ void Connection::finishHandshake(sd_bus* bus)
|
|||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Connection::createProcessingExitDescriptor()
|
int Connection::createLoopNotificationDescriptor()
|
||||||
{
|
{
|
||||||
// Mechanism for graceful termination of processing loop
|
|
||||||
|
|
||||||
auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC);
|
auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC);
|
||||||
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
|
||||||
@ -222,16 +229,23 @@ int Connection::createProcessingExitDescriptor()
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::closeProcessingExitDescriptor(int fd)
|
void Connection::closeLoopNotificationDescriptor(int fd)
|
||||||
{
|
{
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Connection::notifyProcessingLoop()
|
||||||
|
{
|
||||||
|
assert(notificationFd_ >= 0);
|
||||||
|
uint64_t value = 1;
|
||||||
|
write(notificationFd_, &value, sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
void Connection::notifyProcessingLoopToExit()
|
void Connection::notifyProcessingLoopToExit()
|
||||||
{
|
{
|
||||||
assert(exitLoopFd_ >= 0);
|
exitLoopThread_ = true;
|
||||||
uint64_t value = 1;
|
|
||||||
write(exitLoopFd_, &value, sizeof(value));
|
notifyProcessingLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Connection::joinWithProcessingLoop()
|
void Connection::joinWithProcessingLoop()
|
||||||
@ -240,8 +254,10 @@ void Connection::joinWithProcessingLoop()
|
|||||||
asyncLoopThread_.join();
|
asyncLoopThread_.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connection::processPendingRequest(sd_bus* bus)
|
bool Connection::processPendingRequest()
|
||||||
{
|
{
|
||||||
|
auto bus = bus_.get();
|
||||||
|
|
||||||
assert(bus != nullptr);
|
assert(bus != nullptr);
|
||||||
|
|
||||||
int r = sd_bus_process(bus, nullptr);
|
int r = sd_bus_process(bus, nullptr);
|
||||||
@ -251,10 +267,23 @@ bool Connection::processPendingRequest(sd_bus* bus)
|
|||||||
return r > 0;
|
return r > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connection::waitForNextRequest(sd_bus* bus, int exitFd)
|
void Connection::processAsynchronousMessages()
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
while (!asyncReplies_.empty())
|
||||||
|
{
|
||||||
|
auto reply = asyncReplies_.front();
|
||||||
|
asyncReplies_.pop();
|
||||||
|
reply.send();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection::WaitResult Connection::waitForNextRequest()
|
||||||
|
{
|
||||||
|
auto bus = bus_.get();
|
||||||
|
|
||||||
assert(bus != nullptr);
|
assert(bus != nullptr);
|
||||||
assert(exitFd != 0);
|
assert(notificationFd_ != 0);
|
||||||
|
|
||||||
auto r = sd_bus_get_fd(bus);
|
auto r = sd_bus_get_fd(bus);
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r);
|
||||||
@ -267,20 +296,26 @@ bool Connection::waitForNextRequest(sd_bus* bus, int exitFd)
|
|||||||
uint64_t usec;
|
uint64_t usec;
|
||||||
sd_bus_get_timeout(bus, &usec);
|
sd_bus_get_timeout(bus, &usec);
|
||||||
|
|
||||||
struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {exitFd, POLLIN, 0}};
|
struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {notificationFd_, POLLIN, 0}};
|
||||||
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
||||||
|
|
||||||
r = poll(fds, fdsCount, usec == (uint64_t) -1 ? -1 : (usec+999)/1000);
|
r = poll(fds, fdsCount, usec == (uint64_t) -1 ? -1 : (usec+999)/1000);
|
||||||
|
|
||||||
if (r < 0 && errno == EINTR)
|
if (r < 0 && errno == EINTR)
|
||||||
return true; // Try again
|
return {true, false}; // Try again
|
||||||
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
|
||||||
|
|
||||||
if (fds[1].revents & POLLIN)
|
if (fds[1].revents & POLLIN)
|
||||||
return false; // Got exit notification
|
{
|
||||||
|
if (exitLoopThread_)
|
||||||
|
return {false, false}; // Got exit notification
|
||||||
|
|
||||||
return true;
|
// Otherwise we have some async messages to process
|
||||||
|
return {false, true};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {true, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Connection::composeSignalMatchFilter( const std::string& objectPath
|
std::string Connection::composeSignalMatchFilter( const std::string& objectPath
|
||||||
|
@ -27,12 +27,14 @@
|
|||||||
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
|
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||||
|
|
||||||
#include <sdbus-c++/IConnection.h>
|
#include <sdbus-c++/IConnection.h>
|
||||||
|
#include <sdbus-c++/Message.h>
|
||||||
|
#include "IConnection.h"
|
||||||
#include <systemd/sd-bus.h>
|
#include <systemd/sd-bus.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <atomic>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
#include "IConnection.h"
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
namespace sdbus { namespace internal {
|
namespace sdbus { namespace internal {
|
||||||
|
|
||||||
@ -62,13 +64,13 @@ namespace sdbus { namespace internal {
|
|||||||
, void* userData ) override;
|
, void* userData ) override;
|
||||||
void removeObjectVTable(void* vtableHandle) override;
|
void removeObjectVTable(void* vtableHandle) override;
|
||||||
|
|
||||||
sdbus::Message createMethodCall( const std::string& destination
|
sdbus::MethodCall createMethodCall( const std::string& destination
|
||||||
, const std::string& objectPath
|
, const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const std::string& methodName ) const override;
|
, const std::string& methodName ) const override;
|
||||||
sdbus::Message createSignal( const std::string& objectPath
|
sdbus::Signal createSignal( const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const std::string& signalName ) const override;
|
, const std::string& signalName ) const override;
|
||||||
|
|
||||||
void* registerSignalHandler( const std::string& objectPath
|
void* registerSignalHandler( const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
@ -77,25 +79,41 @@ namespace sdbus { namespace internal {
|
|||||||
, void* userData ) override;
|
, void* userData ) override;
|
||||||
void unregisterSignalHandler(void* handlerCookie) override;
|
void unregisterSignalHandler(void* handlerCookie) override;
|
||||||
|
|
||||||
|
void sendReplyAsynchronously(const sdbus::MethodReply& reply) override;
|
||||||
|
|
||||||
std::unique_ptr<sdbus::internal::IConnection> clone() const override;
|
std::unique_ptr<sdbus::internal::IConnection> clone() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct WaitResult
|
||||||
|
{
|
||||||
|
bool msgsToProcess;
|
||||||
|
bool asyncMsgsToProcess;
|
||||||
|
operator bool()
|
||||||
|
{
|
||||||
|
return msgsToProcess || asyncMsgsToProcess;
|
||||||
|
}
|
||||||
|
};
|
||||||
static sd_bus* openBus(Connection::BusType type);
|
static sd_bus* openBus(Connection::BusType type);
|
||||||
static void finishHandshake(sd_bus* bus);
|
static void finishHandshake(sd_bus* bus);
|
||||||
static int createProcessingExitDescriptor();
|
static int createLoopNotificationDescriptor();
|
||||||
static void closeProcessingExitDescriptor(int fd);
|
static void closeLoopNotificationDescriptor(int fd);
|
||||||
static bool processPendingRequest(sd_bus* bus);
|
bool processPendingRequest();
|
||||||
static bool waitForNextRequest(sd_bus* bus, int exitFd);
|
void processAsynchronousMessages();
|
||||||
|
WaitResult waitForNextRequest();
|
||||||
static std::string composeSignalMatchFilter( const std::string& objectPath
|
static std::string composeSignalMatchFilter( const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const std::string& signalName );
|
, const std::string& signalName );
|
||||||
|
void notifyProcessingLoop();
|
||||||
void notifyProcessingLoopToExit();
|
void notifyProcessingLoopToExit();
|
||||||
void joinWithProcessingLoop();
|
void joinWithProcessingLoop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<sd_bus, decltype(&sd_bus_flush_close_unref)> bus_{nullptr, &sd_bus_flush_close_unref};
|
std::unique_ptr<sd_bus, decltype(&sd_bus_flush_close_unref)> bus_{nullptr, &sd_bus_flush_close_unref};
|
||||||
std::thread asyncLoopThread_;
|
std::thread asyncLoopThread_;
|
||||||
std::atomic<int> exitLoopFd_{-1};
|
std::mutex mutex_;
|
||||||
|
std::queue<MethodReply> asyncReplies_;
|
||||||
|
std::atomic<bool> exitLoopThread_;
|
||||||
|
int notificationFd_{-1};
|
||||||
BusType busType_;
|
BusType busType_;
|
||||||
|
|
||||||
static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000;
|
static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000;
|
||||||
|
@ -32,7 +32,9 @@
|
|||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
class Message;
|
class MethodCall;
|
||||||
|
class MethodReply;
|
||||||
|
class Signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace sdbus {
|
namespace sdbus {
|
||||||
@ -47,14 +49,14 @@ namespace internal {
|
|||||||
, void* userData ) = 0;
|
, void* userData ) = 0;
|
||||||
virtual void removeObjectVTable(void* vtableHandle) = 0;
|
virtual void removeObjectVTable(void* vtableHandle) = 0;
|
||||||
|
|
||||||
virtual sdbus::Message createMethodCall( const std::string& destination
|
virtual sdbus::MethodCall createMethodCall( const std::string& destination
|
||||||
, const std::string& objectPath
|
, const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const std::string& methodName ) const = 0;
|
, const std::string& methodName ) const = 0;
|
||||||
|
|
||||||
virtual sdbus::Message createSignal( const std::string& objectPath
|
virtual sdbus::Signal createSignal( const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
, const std::string& signalName ) const = 0;
|
, const std::string& signalName ) const = 0;
|
||||||
|
|
||||||
virtual void* registerSignalHandler( const std::string& objectPath
|
virtual void* registerSignalHandler( const std::string& objectPath
|
||||||
, const std::string& interfaceName
|
, const std::string& interfaceName
|
||||||
@ -66,6 +68,8 @@ namespace internal {
|
|||||||
virtual void enterProcessingLoopAsync() = 0;
|
virtual void enterProcessingLoopAsync() = 0;
|
||||||
virtual void leaveProcessingLoop() = 0;
|
virtual void leaveProcessingLoop() = 0;
|
||||||
|
|
||||||
|
virtual void sendReplyAsynchronously(const sdbus::MethodReply& reply) = 0;
|
||||||
|
|
||||||
virtual std::unique_ptr<sdbus::internal::IConnection> clone() const = 0;
|
virtual std::unique_ptr<sdbus::internal::IConnection> clone() const = 0;
|
||||||
|
|
||||||
virtual ~IConnection() = default;
|
virtual ~IConnection() = default;
|
||||||
|
@ -5,6 +5,7 @@ libsdbus_c___la_SOURCES = \
|
|||||||
Connection.cpp \
|
Connection.cpp \
|
||||||
ConvenienceClasses.cpp \
|
ConvenienceClasses.cpp \
|
||||||
Message.cpp \
|
Message.cpp \
|
||||||
|
MethodResult.cpp \
|
||||||
Object.cpp \
|
Object.cpp \
|
||||||
ObjectProxy.cpp \
|
ObjectProxy.cpp \
|
||||||
Types.cpp \
|
Types.cpp \
|
||||||
|
130
src/Message.cpp
130
src/Message.cpp
@ -31,11 +31,10 @@
|
|||||||
#include <systemd/sd-bus.h>
|
#include <systemd/sd-bus.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace sdbus { /*namespace internal {*/
|
namespace sdbus {
|
||||||
|
|
||||||
Message::Message(void *msg, Type type) noexcept
|
Message::Message(void *msg) noexcept
|
||||||
: msg_(msg)
|
: msg_(msg)
|
||||||
, type_(type)
|
|
||||||
{
|
{
|
||||||
assert(msg_ != nullptr);
|
assert(msg_ != nullptr);
|
||||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||||
@ -52,7 +51,6 @@ Message& Message::operator=(const Message& other) noexcept
|
|||||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||||
|
|
||||||
msg_ = other.msg_;
|
msg_ = other.msg_;
|
||||||
type_ = other.type_;
|
|
||||||
ok_ = other.ok_;
|
ok_ = other.ok_;
|
||||||
|
|
||||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||||
@ -72,8 +70,6 @@ Message& Message::operator=(Message&& other) noexcept
|
|||||||
|
|
||||||
msg_ = other.msg_;
|
msg_ = other.msg_;
|
||||||
other.msg_ = nullptr;
|
other.msg_ = nullptr;
|
||||||
type_ = other.type_;
|
|
||||||
other.type_ = {};
|
|
||||||
ok_ = other.ok_;
|
ok_ = other.ok_;
|
||||||
other.ok_ = true;
|
other.ok_ = true;
|
||||||
|
|
||||||
@ -540,59 +536,6 @@ void Message::rewind(bool complete)
|
|||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message Message::send() const
|
|
||||||
{
|
|
||||||
if (type_ == Type::eMethodCall)
|
|
||||||
{
|
|
||||||
sd_bus_message* sdbusReply{};
|
|
||||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply
|
|
||||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
|
||||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
|
||||||
|
|
||||||
auto r = sd_bus_call(nullptr, (sd_bus_message*)msg_, 0, &sdbusError, &sdbusReply);
|
|
||||||
|
|
||||||
if (sd_bus_error_is_set(&sdbusError))
|
|
||||||
{
|
|
||||||
throw sdbus::Error(sdbusError.name, sdbusError.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
|
|
||||||
|
|
||||||
return Message(sdbusReply);
|
|
||||||
}
|
|
||||||
else if (type_ == Type::eMethodReply)
|
|
||||||
{
|
|
||||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
|
|
||||||
|
|
||||||
return Message();
|
|
||||||
}
|
|
||||||
else if (type_ == Type::eSignal)
|
|
||||||
{
|
|
||||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
|
|
||||||
|
|
||||||
return Message();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(false);
|
|
||||||
return Message();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Message Message::createReply() const
|
|
||||||
{
|
|
||||||
sd_bus_message *sdbusReply{};
|
|
||||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply
|
|
||||||
auto r = sd_bus_message_new_method_return((sd_bus_message*)msg_, &sdbusReply);
|
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
|
|
||||||
|
|
||||||
assert(sdbusReply != nullptr);
|
|
||||||
|
|
||||||
return Message(sdbusReply, Type::eMethodReply);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Message::getInterfaceName() const
|
std::string Message::getInterfaceName() const
|
||||||
{
|
{
|
||||||
return sd_bus_message_get_interface((sd_bus_message*)msg_);
|
return sd_bus_message_get_interface((sd_bus_message*)msg_);
|
||||||
@ -623,9 +566,70 @@ bool Message::isEmpty() const
|
|||||||
return sd_bus_message_is_empty((sd_bus_message*)msg_);
|
return sd_bus_message_is_empty((sd_bus_message*)msg_);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::Type Message::getType() const
|
void* Message::getMsg() const
|
||||||
{
|
{
|
||||||
return type_;
|
return msg_;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodReply MethodCall::send() const
|
||||||
|
{
|
||||||
|
sd_bus_message* sdbusReply{};
|
||||||
|
SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply
|
||||||
|
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||||
|
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||||
|
|
||||||
|
auto r = sd_bus_call(nullptr, (sd_bus_message*)getMsg(), 0, &sdbusError, &sdbusReply);
|
||||||
|
|
||||||
|
if (sd_bus_error_is_set(&sdbusError))
|
||||||
|
{
|
||||||
|
throw sdbus::Error(sdbusError.name, sdbusError.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to call method", -r);
|
||||||
|
|
||||||
|
return MethodReply(sdbusReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodReply MethodCall::createReply() const
|
||||||
|
{
|
||||||
|
sd_bus_message *sdbusReply{};
|
||||||
|
SCOPE_EXIT{ sd_bus_message_unref(sdbusReply); }; // Returned message will become an owner of sdbusReply
|
||||||
|
|
||||||
|
auto r = sd_bus_message_new_method_return((sd_bus_message*)getMsg(), &sdbusReply);
|
||||||
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method reply", -r);
|
||||||
|
|
||||||
|
assert(sdbusReply != nullptr);
|
||||||
|
|
||||||
|
return MethodReply(sdbusReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodReply MethodCall::createErrorReply(const Error& error) const
|
||||||
|
{
|
||||||
|
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||||
|
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||||
|
sd_bus_error_set(&sdbusError, error.getName().c_str(), error.getMessage().c_str());
|
||||||
|
|
||||||
|
sd_bus_message *sdbusErrorReply{};
|
||||||
|
SCOPE_EXIT{ sd_bus_message_unref(sdbusErrorReply); }; // Returned message will become an owner of sdbusErrorReply
|
||||||
|
|
||||||
|
auto r = sd_bus_message_new_method_error((sd_bus_message*)getMsg(), &sdbusErrorReply, &sdbusError);
|
||||||
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method error reply", -r);
|
||||||
|
|
||||||
|
assert(sdbusErrorReply != nullptr);
|
||||||
|
|
||||||
|
return MethodReply(sdbusErrorReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MethodReply::send() const
|
||||||
|
{
|
||||||
|
auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), nullptr);
|
||||||
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to send reply", -r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Signal::send() const
|
||||||
|
{
|
||||||
|
auto r = sd_bus_send(nullptr, (sd_bus_message*)getMsg(), nullptr);
|
||||||
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to emit signal", -r);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message createPlainMessage()
|
Message createPlainMessage()
|
||||||
@ -642,7 +646,7 @@ Message createPlainMessage()
|
|||||||
r = sd_bus_message_new(bus, &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
|
r = sd_bus_message_new(bus, &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
|
||||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a new message", -r);
|
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create a new message", -r);
|
||||||
|
|
||||||
return Message(sdbusMsg, Message::Type::ePlainMessage);
|
return Message(sdbusMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*}*/}
|
}
|
||||||
|
43
src/MethodResult.cpp
Normal file
43
src/MethodResult.cpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||||
|
*
|
||||||
|
* @file Object.cpp
|
||||||
|
*
|
||||||
|
* Created on: Nov 8, 2016
|
||||||
|
* Project: sdbus-c++
|
||||||
|
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||||
|
*
|
||||||
|
* This file is part of sdbus-c++.
|
||||||
|
*
|
||||||
|
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sdbus-c++/MethodResult.h>
|
||||||
|
#include "Object.h"
|
||||||
|
|
||||||
|
namespace sdbus {
|
||||||
|
|
||||||
|
MethodResult::MethodResult(const MethodCall& msg, sdbus::internal::Object& object)
|
||||||
|
: call_(msg)
|
||||||
|
, object_(&object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MethodResult::send(const MethodReply& reply) const
|
||||||
|
{
|
||||||
|
assert(object_ != nullptr);
|
||||||
|
object_->sendReplyAsynchronously(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -27,6 +27,7 @@
|
|||||||
#include <sdbus-c++/IConnection.h>
|
#include <sdbus-c++/IConnection.h>
|
||||||
#include <sdbus-c++/Message.h>
|
#include <sdbus-c++/Message.h>
|
||||||
#include <sdbus-c++/Error.h>
|
#include <sdbus-c++/Error.h>
|
||||||
|
#include <sdbus-c++/MethodResult.h>
|
||||||
#include "IConnection.h"
|
#include "IConnection.h"
|
||||||
#include "VTableUtils.h"
|
#include "VTableUtils.h"
|
||||||
#include <systemd/sd-bus.h>
|
#include <systemd/sd-bus.h>
|
||||||
@ -48,9 +49,36 @@ void Object::registerMethod( const std::string& interfaceName
|
|||||||
{
|
{
|
||||||
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
||||||
|
|
||||||
auto& interface = interfaces_[interfaceName];
|
auto syncCallback = [callback = std::move(methodCallback)](MethodCall& msg)
|
||||||
|
{
|
||||||
|
auto reply = msg.createReply();
|
||||||
|
callback(msg, reply);
|
||||||
|
reply.send();
|
||||||
|
};
|
||||||
|
|
||||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback)};
|
auto& interface = interfaces_[interfaceName];
|
||||||
|
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(syncCallback)};
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
SDBUS_THROW_ERROR_IF(!asyncMethodCallback, "Invalid method callback provided", EINVAL);
|
||||||
|
|
||||||
|
auto asyncCallback = [this, callback = std::move(asyncMethodCallback)](MethodCall& msg)
|
||||||
|
{
|
||||||
|
MethodResult result{msg, *this};
|
||||||
|
callback(msg, result);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& interface = interfaces_[interfaceName];
|
||||||
|
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(asyncCallback)};
|
||||||
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
auto inserted = interface.methods_.emplace(methodName, std::move(methodData)).second;
|
||||||
|
|
||||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||||
@ -108,17 +136,24 @@ void Object::finishRegistration()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sdbus::Message Object::createSignal(const std::string& interfaceName, const std::string& signalName)
|
sdbus::Signal Object::createSignal(const std::string& interfaceName, const std::string& signalName)
|
||||||
{
|
{
|
||||||
// Tell, don't ask
|
|
||||||
return connection_.createSignal(objectPath_, interfaceName, signalName);
|
return connection_.createSignal(objectPath_, interfaceName, signalName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::emitSignal(const sdbus::Message& message)
|
void Object::emitSignal(const sdbus::Signal& message)
|
||||||
{
|
{
|
||||||
|
// TODO: Make signal emitting asynchronous. Now signal can probably be emitted only from user code
|
||||||
|
// handled within the D-Bus processing loop thread, but not from any thread. In principle it will
|
||||||
|
// be the same as async replies.
|
||||||
message.send();
|
message.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Object::sendReplyAsynchronously(const MethodReply& reply)
|
||||||
|
{
|
||||||
|
connection_.sendReplyAsynchronously(reply);
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||||
{
|
{
|
||||||
auto& vtable = interfaceData.vtable_;
|
auto& vtable = interfaceData.vtable_;
|
||||||
@ -190,27 +225,22 @@ void Object::activateInterfaceVTable( const std::string& interfaceName
|
|||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
Message message(sdbusMessage, Message::Type::eMethodCall);
|
MethodCall message(sdbusMessage);
|
||||||
|
|
||||||
auto* object = static_cast<Object*>(userData);
|
auto* object = static_cast<Object*>(userData);
|
||||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||||
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
|
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
|
||||||
assert(callback);
|
assert(callback);
|
||||||
|
|
||||||
auto reply = message.createReply();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
callback(message, reply);
|
callback(message);
|
||||||
}
|
}
|
||||||
catch (const sdbus::Error& e)
|
catch (const sdbus::Error& e)
|
||||||
{
|
{
|
||||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reply.send();
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +252,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
|||||||
, void *userData
|
, void *userData
|
||||||
, sd_bus_error *retError )
|
, sd_bus_error *retError )
|
||||||
{
|
{
|
||||||
Message reply(sdbusReply, Message::Type::ePlainMessage);
|
Message reply(sdbusReply);
|
||||||
|
|
||||||
auto* object = static_cast<Object*>(userData);
|
auto* object = static_cast<Object*>(userData);
|
||||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||||
@ -254,7 +284,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
|||||||
, void *userData
|
, void *userData
|
||||||
, sd_bus_error *retError )
|
, sd_bus_error *retError )
|
||||||
{
|
{
|
||||||
Message value(sdbusValue, Message::Type::ePlainMessage);
|
Message value(sdbusValue);
|
||||||
|
|
||||||
auto* object = static_cast<Object*>(userData);
|
auto* object = static_cast<Object*>(userData);
|
||||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||||
@ -268,7 +298,6 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
|||||||
catch (const sdbus::Error& e)
|
catch (const sdbus::Error& e)
|
||||||
{
|
{
|
||||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
15
src/Object.h
15
src/Object.h
@ -32,6 +32,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
@ -50,6 +51,12 @@ namespace internal {
|
|||||||
, const std::string& outputSignature
|
, const std::string& outputSignature
|
||||||
, method_callback methodCallback ) override;
|
, method_callback methodCallback ) override;
|
||||||
|
|
||||||
|
void registerMethod( const std::string& interfaceName
|
||||||
|
, const std::string& methodName
|
||||||
|
, const std::string& inputSignature
|
||||||
|
, const std::string& outputSignature
|
||||||
|
, async_method_callback asyncMethodCallback ) override;
|
||||||
|
|
||||||
void registerSignal( const std::string& interfaceName
|
void registerSignal( const std::string& interfaceName
|
||||||
, const std::string& signalName
|
, const std::string& signalName
|
||||||
, const std::string& signature ) override;
|
, const std::string& signature ) override;
|
||||||
@ -67,8 +74,10 @@ namespace internal {
|
|||||||
|
|
||||||
void finishRegistration() override;
|
void finishRegistration() override;
|
||||||
|
|
||||||
sdbus::Message createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||||
void emitSignal(const sdbus::Message& message) override;
|
void emitSignal(const sdbus::Signal& message) override;
|
||||||
|
|
||||||
|
void sendReplyAsynchronously(const MethodReply& reply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using InterfaceName = std::string;
|
using InterfaceName = std::string;
|
||||||
@ -79,7 +88,7 @@ namespace internal {
|
|||||||
{
|
{
|
||||||
std::string inputArgs_;
|
std::string inputArgs_;
|
||||||
std::string outputArgs_;
|
std::string outputArgs_;
|
||||||
method_callback callback_;
|
std::function<void(MethodCall&)> callback_;
|
||||||
};
|
};
|
||||||
std::map<MethodName, MethodData> methods_;
|
std::map<MethodName, MethodData> methods_;
|
||||||
using SignalName = std::string;
|
using SignalName = std::string;
|
||||||
|
@ -60,13 +60,13 @@ ObjectProxy::~ObjectProxy()
|
|||||||
signalConnection_->leaveProcessingLoop();
|
signalConnection_->leaveProcessingLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Message ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
|
MethodCall ObjectProxy::createMethodCall(const std::string& interfaceName, const std::string& methodName)
|
||||||
{
|
{
|
||||||
// Tell, don't ask
|
// Tell, don't ask
|
||||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message ObjectProxy::callMethod(const Message& message)
|
MethodReply ObjectProxy::callMethod(const MethodCall& message)
|
||||||
{
|
{
|
||||||
return message.send();
|
return message.send();
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ void ObjectProxy::registerSignalHandlers(sdbus::internal::IConnection& connectio
|
|||||||
|
|
||||||
int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
||||||
{
|
{
|
||||||
Message message(sdbusMessage, Message::Type::eSignal);
|
Signal message(sdbusMessage);
|
||||||
|
|
||||||
auto* object = static_cast<ObjectProxy*>(userData);
|
auto* object = static_cast<ObjectProxy*>(userData);
|
||||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||||
|
@ -52,8 +52,8 @@ namespace internal {
|
|||||||
, std::string objectPath );
|
, std::string objectPath );
|
||||||
~ObjectProxy();
|
~ObjectProxy();
|
||||||
|
|
||||||
Message createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||||
Message callMethod(const Message& message) override;
|
MethodReply callMethod(const MethodCall& message) override;
|
||||||
|
|
||||||
void registerSignalHandler( const std::string& interfaceName
|
void registerSignalHandler( const std::string& interfaceName
|
||||||
, const std::string& signalName
|
, const std::string& signalName
|
||||||
|
@ -136,25 +136,47 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
|||||||
{
|
{
|
||||||
auto methodName = method->get("name");
|
auto methodName = method->get("name");
|
||||||
|
|
||||||
|
bool async{false};
|
||||||
|
Nodes annotations = (*method)["annotation"];
|
||||||
|
|
||||||
|
for (const auto& annotation : annotations)
|
||||||
|
{
|
||||||
|
if (annotation->get("name") == "org.freedesktop.DBus.Method.Async"
|
||||||
|
&& (annotation->get("value") == "server" || annotation->get("value") == "clientserver"))
|
||||||
|
{
|
||||||
|
async = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Nodes args = (*method)["arg"];
|
Nodes args = (*method)["arg"];
|
||||||
Nodes inArgs = args.select("direction" , "in");
|
Nodes inArgs = args.select("direction" , "in");
|
||||||
Nodes outArgs = args.select("direction" , "out");
|
Nodes outArgs = args.select("direction" , "out");
|
||||||
|
|
||||||
std::string argStr, argTypeStr;
|
std::string argStr, argTypeStr;
|
||||||
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
|
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
|
||||||
|
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
registrationSS << tab << tab << "object_.registerMethod(\""
|
registrationSS << tab << tab << "object_.registerMethod(\""
|
||||||
<< methodName << "\")"
|
<< methodName << "\")"
|
||||||
<< ".onInterface(interfaceName)"
|
<< ".onInterface(interfaceName)"
|
||||||
<< ".implementedAs("
|
<< ".implementedAs("
|
||||||
<< "[this]("
|
<< "[this]("
|
||||||
|
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "")
|
||||||
<< argTypeStr
|
<< argTypeStr
|
||||||
<< "){ return this->" << methodName << "("
|
<< "){ " << (async ? "" : "return ") << "this->" << methodName << "("
|
||||||
|
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
|
||||||
<< argStr << "); });" << endl;
|
<< argStr << "); });" << endl;
|
||||||
|
|
||||||
declarationSS << tab
|
declarationSS << tab
|
||||||
<< "virtual " << outArgsToType(outArgs) << " " << methodName
|
<< "virtual "
|
||||||
<< "(" << argTypeStr << ") = 0;" << endl;
|
<< (async ? "void" : outArgsToType(outArgs))
|
||||||
|
<< " " << methodName
|
||||||
|
<< "("
|
||||||
|
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "")
|
||||||
|
<< argTypeStr
|
||||||
|
<< ") = 0;" << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||||
|
@ -126,12 +126,15 @@ std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndT
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
std::string BaseGenerator::outArgsToType(const Nodes& args) const
|
std::string BaseGenerator::outArgsToType(const Nodes& args, bool bareList) const
|
||||||
{
|
{
|
||||||
std::ostringstream retTypeSS;
|
std::ostringstream retTypeSS;
|
||||||
|
|
||||||
if (args.size() == 0)
|
if (args.size() == 0)
|
||||||
{
|
{
|
||||||
|
if (bareList)
|
||||||
|
return "";
|
||||||
|
|
||||||
retTypeSS << "void";
|
retTypeSS << "void";
|
||||||
}
|
}
|
||||||
else if (args.size() == 1)
|
else if (args.size() == 1)
|
||||||
@ -141,7 +144,8 @@ std::string BaseGenerator::outArgsToType(const Nodes& args) const
|
|||||||
}
|
}
|
||||||
else if (args.size() >= 2)
|
else if (args.size() >= 2)
|
||||||
{
|
{
|
||||||
retTypeSS << "std::tuple<";
|
if (!bareList)
|
||||||
|
retTypeSS << "std::tuple<";
|
||||||
|
|
||||||
bool firstArg = true;
|
bool firstArg = true;
|
||||||
for (const auto& arg : args)
|
for (const auto& arg : args)
|
||||||
@ -150,7 +154,8 @@ std::string BaseGenerator::outArgsToType(const Nodes& args) const
|
|||||||
retTypeSS << signature_to_type(arg->get("type"));
|
retTypeSS << signature_to_type(arg->get("type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
retTypeSS << ">";
|
if (!bareList)
|
||||||
|
retTypeSS << ">";
|
||||||
}
|
}
|
||||||
|
|
||||||
return retTypeSS.str();
|
return retTypeSS.str();
|
||||||
|
@ -94,7 +94,7 @@ protected:
|
|||||||
* @param args
|
* @param args
|
||||||
* @return return type
|
* @return return type
|
||||||
*/
|
*/
|
||||||
std::string outArgsToType(const sdbuscpp::xml::Nodes& args) const;
|
std::string outArgsToType(const sdbuscpp::xml::Nodes& args, bool bareList = false) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required (VERSION 3.3)
|
cmake_minimum_required (VERSION 3.4)
|
||||||
|
|
||||||
add_definitions(-std=c++14)
|
add_definitions(-std=c++14)
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ endif
|
|||||||
|
|
||||||
# Setting per-file flags
|
# Setting per-file flags
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
|
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src
|
||||||
AM_CXXFLAGS = @libsdbus_cpp_CFLAGS@ @SYSTEMD_CFLAGS@ -W -Wall -Werror -pedantic -pipe -std=c++14
|
AM_CXXFLAGS = @libsdbus_cpp_CFLAGS@ @SYSTEMD_CFLAGS@ -W -Wall -Werror -pedantic -pipe -std=c++17
|
||||||
AM_LDFLAGS = @libsdbus_cpp_LIBS@ @SYSTEMD_LIBS@
|
AM_LDFLAGS = @libsdbus_cpp_LIBS@ @SYSTEMD_LIBS@
|
||||||
|
|
||||||
CLEANFILES = *~ *.lo *.la
|
CLEANFILES = *~ *.lo *.la
|
||||||
|
@ -40,9 +40,12 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
using ::testing::Eq;
|
using ::testing::Eq;
|
||||||
using ::testing::Gt;
|
using ::testing::Gt;
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -67,7 +70,7 @@ private:
|
|||||||
{
|
{
|
||||||
m_adaptor = std::make_unique<TestingAdaptor>(m_connection);
|
m_adaptor = std::make_unique<TestingAdaptor>(m_connection);
|
||||||
m_proxy = std::make_unique<TestingProxy>(INTERFACE_NAME, OBJECT_PATH);
|
m_proxy = std::make_unique<TestingProxy>(INTERFACE_NAME, OBJECT_PATH);
|
||||||
usleep(50000); // Give time for the proxy to start listening to signals
|
std::this_thread::sleep_for(50ms); // Give time for the proxy to start listening to signals
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override
|
void TearDown() override
|
||||||
@ -197,6 +200,31 @@ TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
|||||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(SdbusTestObject, DoesServerSideAsynchoronousMethodInParallel)
|
||||||
|
{
|
||||||
|
// Yeah, this is kinda timing-dependent test, but times should be safe...
|
||||||
|
std::mutex mtx;
|
||||||
|
std::vector<uint32_t> results;
|
||||||
|
std::atomic<bool> invoke{};
|
||||||
|
std::atomic<int> startedCount{};
|
||||||
|
auto call = [&](uint32_t param)
|
||||||
|
{
|
||||||
|
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
|
||||||
|
++startedCount;
|
||||||
|
while (!invoke) ;
|
||||||
|
auto result = proxy.doOperationAsync(param);
|
||||||
|
std::lock_guard<std::mutex> guard(mtx);
|
||||||
|
results.push_back(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::thread invocations[]{std::thread{call, 1500}, std::thread{call, 1000}, std::thread{call, 500}};
|
||||||
|
while (startedCount != 3) ;
|
||||||
|
invoke = true;
|
||||||
|
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
|
||||||
|
|
||||||
|
ASSERT_THAT(results, ElementsAre(500, 1000, 1500));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
|
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||||
{
|
{
|
||||||
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
|
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||||
|
@ -27,6 +27,8 @@
|
|||||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
|
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
|
||||||
|
|
||||||
#include "adaptor-glue.h"
|
#include "adaptor-glue.h"
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
class TestingAdaptor : public sdbus::Interfaces<testing_adaptor>
|
class TestingAdaptor : public sdbus::Interfaces<testing_adaptor>
|
||||||
{
|
{
|
||||||
@ -97,6 +99,22 @@ protected:
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t doOperationSync(uint32_t param)
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result)
|
||||||
|
{
|
||||||
|
// The same as doOperationSync, just written as an asynchronous method callback
|
||||||
|
std::thread([param, result = std::move(result)]()
|
||||||
|
{
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(param));
|
||||||
|
result.returnResults(param);
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
sdbus::Signature getSignature() const { return SIGNATURE_VALUE; }
|
sdbus::Signature getSignature() const { return SIGNATURE_VALUE; }
|
||||||
sdbus::ObjectPath getObjectPath() const { return OBJECT_PATH_VALUE; }
|
sdbus::ObjectPath getObjectPath() const { return OBJECT_PATH_VALUE; }
|
||||||
|
|
||||||
|
@ -82,6 +82,16 @@ protected:
|
|||||||
return this->sumVectorItems(a, b);
|
return this->sumVectorItems(a, b);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
object_.registerMethod("doOperationSync").onInterface(INTERFACE_NAME).implementedAs([this](uint32_t param)
|
||||||
|
{
|
||||||
|
return this->doOperationSync(param);
|
||||||
|
});
|
||||||
|
|
||||||
|
object_.registerMethod("doOperationAsync").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Result<uint32_t> result, uint32_t param)
|
||||||
|
{
|
||||||
|
this->doOperationAsync(param, std::move(result));
|
||||||
|
});
|
||||||
|
|
||||||
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
|
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
|
||||||
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
|
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
|
||||||
|
|
||||||
@ -139,6 +149,8 @@ protected:
|
|||||||
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const = 0;
|
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const = 0;
|
||||||
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) = 0;
|
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) = 0;
|
||||||
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) = 0;
|
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) = 0;
|
||||||
|
virtual uint32_t doOperationSync(uint32_t param) = 0;
|
||||||
|
virtual void doOperationAsync(uint32_t param, sdbus::Result<uint32_t> result) = 0;
|
||||||
virtual sdbus::Signature getSignature() const = 0;
|
virtual sdbus::Signature getSignature() const = 0;
|
||||||
virtual sdbus::ObjectPath getObjectPath() const = 0;
|
virtual sdbus::ObjectPath getObjectPath() const = 0;
|
||||||
virtual ComplexType getComplex() const = 0;
|
virtual ComplexType getComplex() const = 0;
|
||||||
|
@ -120,6 +120,20 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t doOperationSync(uint32_t param)
|
||||||
|
{
|
||||||
|
uint32_t result;
|
||||||
|
object_.callMethod("doOperationSync").onInterface(INTERFACE_NAME).withArguments(param).storeResultsTo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t doOperationAsync(uint32_t param)
|
||||||
|
{
|
||||||
|
uint32_t result;
|
||||||
|
object_.callMethod("doOperationAsync").onInterface(INTERFACE_NAME).withArguments(param).storeResultsTo(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
sdbus::Signature getSignature()
|
sdbus::Signature getSignature()
|
||||||
{
|
{
|
||||||
sdbus::Signature result;
|
sdbus::Signature result;
|
||||||
|
@ -110,13 +110,6 @@ TEST(AMessage, IsNotEmptyWhenContainsAValue)
|
|||||||
ASSERT_FALSE(msg.isEmpty());
|
ASSERT_FALSE(msg.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AMessage, ReturnsItsTypeWhenAsked)
|
|
||||||
{
|
|
||||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
|
||||||
|
|
||||||
ASSERT_THAT(msg.getType(), Eq(sdbus::Message::Type::ePlainMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(AMessage, CanCarryASimpleInteger)
|
TEST(AMessage, CanCarryASimpleInteger)
|
||||||
{
|
{
|
||||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
using ::testing::Eq;
|
using ::testing::Eq;
|
||||||
|
|
||||||
@ -127,3 +128,42 @@ TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
|
|||||||
{
|
{
|
||||||
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
|
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FreeFunctionTypeTraits, DetectsTraitsOfTrivialSignatureFunction)
|
||||||
|
{
|
||||||
|
void f();
|
||||||
|
using Fnc = decltype(f);
|
||||||
|
|
||||||
|
static_assert(!sdbus::is_async_method_v<Fnc>, "Free function incorrectly detected as async method");
|
||||||
|
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<>>::value, "Incorrectly detected free function parameters");
|
||||||
|
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<>>::value, "Incorrectly detected tuple of free function parameters");
|
||||||
|
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, void>::value, "Incorrectly detected tuple of free function return types");
|
||||||
|
static_assert(sdbus::function_argument_count_v<Fnc> == 0, "Incorrectly detected free function parameter count");
|
||||||
|
static_assert(std::is_void<sdbus::function_result_t<Fnc>>::value, "Incorrectly detected free function return type");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FreeFunctionTypeTraits, DetectsTraitsOfNontrivialSignatureFunction)
|
||||||
|
{
|
||||||
|
std::tuple<char, int> f(double&, const char*, int);
|
||||||
|
using Fnc = decltype(f);
|
||||||
|
|
||||||
|
static_assert(!sdbus::is_async_method_v<Fnc>, "Free function incorrectly detected as async method");
|
||||||
|
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>::value, "Incorrectly detected free function parameters");
|
||||||
|
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>::value, "Incorrectly detected tuple of free function parameters");
|
||||||
|
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected tuple of free function return types");
|
||||||
|
static_assert(sdbus::function_argument_count_v<Fnc> == 3, "Incorrectly detected free function parameter count");
|
||||||
|
static_assert(std::is_same<sdbus::function_result_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected free function return type");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FreeFunctionTypeTraits, DetectsTraitsOfAsyncFunction)
|
||||||
|
{
|
||||||
|
void f(sdbus::Result<char, int>, double&, const char*, int);
|
||||||
|
using Fnc = decltype(f);
|
||||||
|
|
||||||
|
static_assert(sdbus::is_async_method_v<Fnc>, "Free async function incorrectly detected as sync method");
|
||||||
|
static_assert(std::is_same<sdbus::function_arguments_t<Fnc>, std::tuple<double&, const char*, int>>::value, "Incorrectly detected free function parameters");
|
||||||
|
static_assert(std::is_same<sdbus::tuple_of_function_input_arg_types_t<Fnc>, std::tuple<double, const char*, int>>::value, "Incorrectly detected tuple of free function parameters");
|
||||||
|
static_assert(std::is_same<sdbus::tuple_of_function_output_arg_types_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected tuple of free function return types");
|
||||||
|
static_assert(sdbus::function_argument_count_v<Fnc> == 3, "Incorrectly detected free function parameter count");
|
||||||
|
static_assert(std::is_same<sdbus::function_result_t<Fnc>, std::tuple<char, int>>::value, "Incorrectly detected free function return type");
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user