forked from Kistler-Group/sdbus-cpp
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
4f5dfbc301 | |||
fa878e594c | |||
d3d698f02a | |||
d8fd053714 | |||
b041f76bfc | |||
f1ff05cb6f | |||
44be60555d | |||
dfdc6b153e | |||
fd3799dbc3 | |||
24b2f2bda3 | |||
d40fdf1b1c | |||
a395adbecf | |||
dafd7a791a | |||
83ae4cf5ae | |||
b535198571 | |||
d68be891ee | |||
10d0da9067 | |||
2564bbfb21 | |||
e1cf50d2cd | |||
db5e9dc963 | |||
933e8e204d | |||
47139527f4 | |||
b22cac9a63 | |||
b81c4b494c | |||
7e61a83d09 | |||
dc5ec014eb | |||
f559fc0663 | |||
b5866fe5e9 | |||
96684ce37f | |||
55d8084729 | |||
be754eb991 | |||
7fbc0e360d |
28
ChangeLog
28
ChangeLog
@ -0,0 +1,28 @@
|
||||
v0.2.3
|
||||
- Initially published version
|
||||
|
||||
v0.2.4
|
||||
- Fixed closing of file descriptor of event loop's semaphore on exec
|
||||
- Fixed interrupt handling when polling
|
||||
- Improved tutorial
|
||||
- Fixed issue with googlemock
|
||||
- Added object proxy factory overload that takes unique_ptr to a connection
|
||||
- Workaround: Clang compilation error when compiling sdbus::Struct (seems like an issue of Clang)
|
||||
|
||||
v0.2.5
|
||||
- Real fix for issue with sdbus::Struct inherited constructors
|
||||
- Little code refactorings and improvements
|
||||
|
||||
v0.2.6
|
||||
- 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++.
|
||||
|
||||
v0.3.1
|
||||
- Fixed hogging the CPU by server with async methods (issue #15)
|
||||
|
@ -36,7 +36,7 @@ References/documentation
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Contributions that increase the library quality, functionality, or fix issues are very welcome. To introduce a change, please submit a merge request with a description.
|
||||
Contributions that increase the library quality, functionality, or fix issues are very welcome. To introduce a change, please submit a pull request with a description.
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
@ -4,8 +4,8 @@ AC_PREREQ(2.61)
|
||||
# odd micro numbers indicate in-progress development
|
||||
# even micro numbers indicate released versions
|
||||
m4_define(sdbus_cpp_version_major, 0)
|
||||
m4_define(sdbus_cpp_version_minor, 2)
|
||||
m4_define(sdbus_cpp_version_micro, 3)
|
||||
m4_define(sdbus_cpp_version_minor, 3)
|
||||
m4_define(sdbus_cpp_version_micro, 1)
|
||||
|
||||
m4_define([sdbus_cpp_version],
|
||||
[sdbus_cpp_version_major.sdbus_cpp_version_minor.sdbus_cpp_version_micro])
|
||||
@ -25,8 +25,8 @@ AC_PROG_INSTALL
|
||||
# enable pkg-config
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
PKG_CHECK_MODULES(SYSTEMD, [libsystemd >= 0.10.1],,
|
||||
AC_MSG_ERROR([You need the libsystemd library (version 0.10.1 or better)]
|
||||
PKG_CHECK_MODULES(SYSTEMD, [libsystemd >= 236],,
|
||||
AC_MSG_ERROR([You need the libsystemd library (version 236 or newer)]
|
||||
[https://www.freedesktop.org/wiki/Software/systemd/])
|
||||
)
|
||||
|
||||
|
BIN
doc/sdbus-c++-class-diagram.png
Executable file
BIN
doc/sdbus-c++-class-diagram.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
59
doc/sdbus-c++-class-diagram.uml
Normal file
59
doc/sdbus-c++-class-diagram.uml
Normal file
@ -0,0 +1,59 @@
|
||||
@startuml
|
||||
|
||||
package "Public API" <<frame>> #DDDDDD {
|
||||
interface IConnection {
|
||||
+requestName()
|
||||
+enterProcessLoop()
|
||||
+leaveProcessLoop()
|
||||
}
|
||||
|
||||
interface IObject {
|
||||
+registerMethod()
|
||||
+emitSignal()
|
||||
}
|
||||
|
||||
interface IObjectProxy {
|
||||
+callMethod()
|
||||
+subscribeToSignal()
|
||||
}
|
||||
|
||||
class Message {
|
||||
+serialize(...)
|
||||
+deserialize(...)
|
||||
+send()
|
||||
Type msgType
|
||||
}
|
||||
}
|
||||
|
||||
interface IConnectionInternal {
|
||||
+addObjectVTable()
|
||||
+createMethodCall()
|
||||
+createSignal()
|
||||
}
|
||||
|
||||
class Connection {
|
||||
}
|
||||
|
||||
class Object {
|
||||
IConnectionInternal& connection
|
||||
string objectPath
|
||||
List interfaces
|
||||
List methods
|
||||
}
|
||||
|
||||
class ObjectProxy {
|
||||
IConnectionInternal& connection
|
||||
string destination
|
||||
string objectPath
|
||||
}
|
||||
|
||||
IConnection <|-- Connection
|
||||
IConnectionInternal <|- Connection
|
||||
IObject <|-- Object
|
||||
IObjectProxy <|-- ObjectProxy
|
||||
Connection <-- Object : "use"
|
||||
Connection <-- ObjectProxy : "use"
|
||||
Message <.. Object : "send/receive"
|
||||
Message <.. ObjectProxy : "send/receive"
|
||||
|
||||
@enduml
|
@ -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)
|
||||
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)
|
||||
10. [Using D-Bus properties](#using-d-bus-properties)
|
||||
11. [Conclusion](#conclusion)
|
||||
10. [Asynchronous server-side methods](#asynchronous-server-side-methods)
|
||||
11. [Using D-Bus properties](#using-d-bus-properties)
|
||||
12. [Conclusion](#conclusion)
|
||||
|
||||
Introduction
|
||||
------------
|
||||
@ -75,17 +76,36 @@ Error signalling and propagation
|
||||
|
||||
The exception object carries the error name and error message with it.
|
||||
|
||||
sdbus-c++ design
|
||||
----------------
|
||||
|
||||
The following diagram illustrates the major entities in sdbus-c++.
|
||||
|
||||

|
||||
|
||||
`IConnection` represents the concept of a D-Bus connection. You can connect to either the system bus or a session bus. Services can assign unique service names to those connections. A processing loop can be run on the connection.
|
||||
|
||||
`IObject` represents the concept of an object that exposes its methods, signals and properties. Its responsibilities are:
|
||||
* registering (possibly multiple) interfaces and methods, signals, properties on those interfaces,
|
||||
* emitting signals.
|
||||
|
||||
`IObjectProxy` represents the concept of the proxy, which is a view of the `Object` from the client side. Its responsibilities are:
|
||||
* invoking remote methods of the corresponding object,
|
||||
* registering handlers for signals.
|
||||
|
||||
`Message` class represents a message, which is the fundamental DBus concept. There are three distinctive types of message that derive from the `Message` class:
|
||||
* `MethodCall` (with serialized parameters),
|
||||
* `MethodReply` (with serialized return values),
|
||||
* or a `Signal` (with serialized parameters).
|
||||
|
||||
Multiple layers of sdbus-c++ API
|
||||
-------------------------------
|
||||
|
||||
sdbus-c++ API comes in two layers:
|
||||
* the basic layer, which is almost pure wrapper layer on top of sd-bus, using mechanisms that are native to C++,
|
||||
* the convenience layer, building on top of the basic layer, which aims at providing shorter, safer, and more expressive way of writing the
|
||||
client code.
|
||||
* [the basic layer](#implementing-the-concatenator-example-using-basic-sdbus-c-api-layer), which is a simple wrapper layer on top of sd-bus, using mechanisms that are native to C++ (e.g. serialization/deserialization of data from messages),
|
||||
* [the convenience layer](#implementing-the-concatenator-example-using-convenience-sdbus-c-api-layer), building on top of the basic layer, which aims at alleviating users from unnecessary details and enables them to write shorter, safer, and more expressive code.
|
||||
|
||||
sdbus-c++ also ships with a stub generator tool that converts D-Bus IDL in XML format into stub code for the adaptor as well as proxy part.
|
||||
Hierarchically, these stubs provide yet another layer of convenience (the "stubs layer"), making it possible for D-Bus RPC calls to look like
|
||||
native C++ calls on a local object.
|
||||
sdbus-c++ also ships with a stub generator tool that converts D-Bus IDL in XML format into stub code for the adaptor as well as proxy part. Hierarchically, these stubs provide yet another layer of convenience (the "stubs layer"), making it possible for D-Bus RPC calls to completely look like native C++ calls on a local object.
|
||||
|
||||
An example: Number concatenator
|
||||
-------------------------------
|
||||
@ -119,15 +139,15 @@ Overloaded versions of C++ insertion/extraction operators are used for serializa
|
||||
// to emit signals.
|
||||
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
|
||||
std::vector<int> numbers;
|
||||
msg >> numbers;
|
||||
call >> numbers;
|
||||
|
||||
// Deserialize separator from the message
|
||||
std::string separator;
|
||||
msg >> separator;
|
||||
call >> separator;
|
||||
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
@ -144,16 +164,16 @@ void concatenate(sdbus::Message& msg, sdbus::Message& reply)
|
||||
|
||||
// Emit 'concatenated' signal
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
auto signalMsg = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||
signalMsg << result;
|
||||
g_concatenator->emitSignal(signalMsg);
|
||||
auto signal = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||
signal << result;
|
||||
g_concatenator->emitSignal(signal);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection and requests name on it.
|
||||
// Create D-Bus connection to the system bus and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createConnection(serviceName);
|
||||
auto connection = sdbus::createSystemBusConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
@ -181,17 +201,19 @@ int main(int argc, char *argv[])
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
void onConcatenated(sdbus::Message& signalMsg)
|
||||
void onConcatenated(sdbus::Signal& signal)
|
||||
{
|
||||
std::string concatenatedString;
|
||||
msg >> concatenatedString;
|
||||
signal >> concatenatedString;
|
||||
|
||||
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create proxy object for the concatenator object on the server side
|
||||
// Create proxy object for the concatenator object on the server side. Since we don't pass
|
||||
// the D-Bus connection object to the proxy constructor, the proxy will internally create
|
||||
// its own connection to the system bus.
|
||||
const char* destinationName = "org.sdbuscpp.concatenator";
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenatorProxy = sdbus::createObjectProxy(destinationName, objectPath);
|
||||
@ -237,7 +259,7 @@ int main(int argc, char *argv[])
|
||||
```
|
||||
|
||||
The object proxy is created without explicitly providing a D-Bus connection as an argument in its factory function. In that case, the proxy
|
||||
will create its own connection and listen to signals on it in a separate thread. That means the `onConcatenated` method is invoked always
|
||||
will create its own connection to the *system* bus and listen to signals on it in a separate thread. That means the `onConcatenated` method is invoked always
|
||||
in the context of a thread different from the main thread.
|
||||
|
||||
Implementing the Concatenator example using convenience sdbus-c++ API layer
|
||||
@ -291,9 +313,9 @@ std::string concatenate(const std::vector<int> numbers, const std::string& separ
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection and requests name on it.
|
||||
// Create D-Bus connection to the system bus and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createConnection(serviceName);
|
||||
auto connection = sdbus::createSystemBusConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
@ -565,9 +587,9 @@ publishing the object.
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection and requests name on it.
|
||||
// Create D-Bus connection to the system bus and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createConnection(serviceName);
|
||||
auto connection = sdbus::createSystemBusConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
@ -645,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
|
||||
----------------------
|
||||
|
||||
|
@ -14,6 +14,7 @@ libsdbuscpp_HEADERS = \
|
||||
$(HEADER_DIR)/IObject.h \
|
||||
$(HEADER_DIR)/IObjectProxy.h \
|
||||
$(HEADER_DIR)/Message.h \
|
||||
$(HEADER_DIR)/MethodResult.h \
|
||||
$(HEADER_DIR)/Types.h \
|
||||
$(HEADER_DIR)/TypeTraits.h \
|
||||
$(HEADER_DIR)/sdbus-c++.h
|
||||
|
@ -27,7 +27,9 @@
|
||||
#define SDBUS_CXX_CONVENIENCECLASSES_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
@ -43,7 +45,10 @@ namespace sdbus {
|
||||
public:
|
||||
MethodRegistrator(IObject& object, const std::string& methodName);
|
||||
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:
|
||||
IObject& object_;
|
||||
@ -103,7 +108,7 @@ namespace sdbus {
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& signalName_;
|
||||
Message signal_;
|
||||
Signal signal_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
||||
};
|
||||
|
||||
@ -122,7 +127,7 @@ namespace sdbus {
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
const std::string& methodName_;
|
||||
Message method_;
|
||||
MethodCall method_;
|
||||
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
||||
bool methodCalled_{};
|
||||
};
|
||||
|
34
include/sdbus-c++/ConvenienceClasses.inl
Normal file → Executable file
34
include/sdbus-c++/ConvenienceClasses.inl
Normal file → Executable file
@ -29,6 +29,7 @@
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
@ -52,7 +53,7 @@ namespace sdbus {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -60,7 +61,7 @@ namespace sdbus {
|
||||
, methodName_
|
||||
, signature_of_function_input_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
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
@ -72,7 +73,7 @@ namespace sdbus {
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
// For callbacks returning a non-void value, `apply' also returns that value.
|
||||
// For callbacks returning void, `apply' returns an empty tuple.
|
||||
auto ret = apply(callback, inputArgs); // We don't yet have C++17's std::apply :-(
|
||||
auto ret = sdbus::apply(callback, inputArgs); // We don't yet have C++17's std::apply :-(
|
||||
|
||||
// The return value is stored to the reply message.
|
||||
// In case of void functions, ret is an empty tuple and thus nothing is stored.
|
||||
@ -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.
|
||||
sdbus::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
|
||||
/*
|
||||
@ -339,7 +363,7 @@ namespace sdbus {
|
||||
|
||||
objectProxy_.registerSignalHandler( interfaceName_
|
||||
, 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
|
||||
// as a storage for the argument values deserialized from the signal message.
|
||||
@ -349,7 +373,7 @@ namespace sdbus {
|
||||
signal >> signalArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
apply(callback, signalArgs); // We don't yet have C++17's std::apply :-(
|
||||
sdbus::apply(callback, signalArgs); // We don't yet have C++17's std::apply :-(
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class Signal;
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
@ -70,6 +70,28 @@ namespace sdbus {
|
||||
, const std::string& outputSignature
|
||||
, 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
|
||||
*
|
||||
@ -138,7 +160,7 @@ namespace sdbus {
|
||||
*
|
||||
* @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
|
||||
@ -149,7 +171,7 @@ namespace sdbus {
|
||||
*
|
||||
* @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
|
||||
|
@ -33,7 +33,8 @@
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
@ -65,7 +66,7 @@ namespace sdbus {
|
||||
*
|
||||
* @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
|
||||
@ -76,7 +77,7 @@ namespace sdbus {
|
||||
*
|
||||
* @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
|
||||
@ -225,6 +226,27 @@ namespace sdbus {
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates object proxy instance
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the proxy object
|
||||
* @param[in] destination Bus name that provides a D-Bus object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
* @return Pointer to the object proxy instance
|
||||
*
|
||||
* The provided connection will be used by the proxy to issue calls against the object,
|
||||
* and signals, if any, will be subscribed to on this connection. Object proxy becomes
|
||||
* an exclusive owner of this connection.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObjectProxy(std::move(connection), "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::unique_ptr<sdbus::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates object proxy instance that uses its own D-Bus connection
|
||||
*
|
||||
|
@ -44,6 +44,12 @@ namespace sdbus {
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
|
||||
class Message;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
template <typename... _Results> class Result;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
@ -65,16 +71,8 @@ namespace sdbus {
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
ePlainMessage
|
||||
, eMethodCall
|
||||
, eMethodReply
|
||||
, eSignal
|
||||
};
|
||||
|
||||
Message() = default;
|
||||
Message(void *msg, Type type = Type::ePlainMessage) noexcept;
|
||||
Message(void *msg) noexcept;
|
||||
Message(const Message&) noexcept;
|
||||
Message& operator=(const Message&) noexcept;
|
||||
Message(Message&& other) noexcept;
|
||||
@ -137,21 +135,42 @@ namespace sdbus {
|
||||
void peekType(std::string& type, std::string& contents) const;
|
||||
bool isValid() const;
|
||||
bool isEmpty() const;
|
||||
Type getType() const;
|
||||
|
||||
void copyTo(Message& destination, bool complete) const;
|
||||
void seal();
|
||||
void rewind(bool complete);
|
||||
|
||||
Message createReply() const;
|
||||
Message send() const;
|
||||
protected:
|
||||
void* getMsg() const;
|
||||
|
||||
private:
|
||||
void* msg_{};
|
||||
Type type_{Type::ePlainMessage};
|
||||
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>
|
||||
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_
|
||||
#define SDBUS_CXX_TYPETRAITS_H_
|
||||
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@ -40,19 +41,26 @@ namespace sdbus {
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
class Message;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
class MethodResult;
|
||||
template <typename... _Results> class Result;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
using method_callback = std::function<void(Message& msg, Message& reply)>;
|
||||
using signal_handler = std::function<void(Message& signal)>;
|
||||
using method_callback = std::function<void(MethodCall& msg, MethodReply& reply)>;
|
||||
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_get_callback = std::function<void(Message& reply)>;
|
||||
|
||||
// Primary template
|
||||
template <typename _T>
|
||||
struct signature_of
|
||||
{
|
||||
static constexpr bool is_valid = false;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
// sizeof(_T) < 0 is here to make compiler not being able to figure out
|
||||
@ -65,6 +73,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<void>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "";
|
||||
@ -74,6 +84,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<bool>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "b";
|
||||
@ -83,6 +95,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<uint8_t>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "y";
|
||||
@ -92,6 +106,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<int16_t>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "n";
|
||||
@ -101,6 +117,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<uint16_t>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "q";
|
||||
@ -110,6 +128,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<int32_t>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "i";
|
||||
@ -119,6 +139,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<uint32_t>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "u";
|
||||
@ -128,6 +150,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<int64_t>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "x";
|
||||
@ -137,6 +161,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<uint64_t>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "t";
|
||||
@ -146,6 +172,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<double>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "d";
|
||||
@ -155,6 +183,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<char*>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
@ -164,6 +194,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<const char*>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
@ -173,6 +205,8 @@ namespace sdbus {
|
||||
template <std::size_t _N>
|
||||
struct signature_of<char[_N]>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
@ -182,6 +216,8 @@ namespace sdbus {
|
||||
template <std::size_t _N>
|
||||
struct signature_of<const char[_N]>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
@ -191,6 +227,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<std::string>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
@ -200,6 +238,8 @@ namespace sdbus {
|
||||
template <typename... _ValueTypes>
|
||||
struct signature_of<Struct<_ValueTypes...>>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
std::initializer_list<std::string> signatures{signature_of<_ValueTypes>::str()...};
|
||||
@ -215,6 +255,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<Variant>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "v";
|
||||
@ -224,6 +266,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<ObjectPath>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "o";
|
||||
@ -233,6 +277,8 @@ namespace sdbus {
|
||||
template <>
|
||||
struct signature_of<Signature>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "g";
|
||||
@ -242,6 +288,8 @@ namespace sdbus {
|
||||
template <typename _Element>
|
||||
struct signature_of<std::vector<_Element>>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "a" + signature_of<_Element>::str();
|
||||
@ -251,12 +299,17 @@ namespace sdbus {
|
||||
template <typename _Key, typename _Value>
|
||||
struct signature_of<std::map<_Key, _Value>>
|
||||
{
|
||||
static constexpr bool is_valid = true;
|
||||
|
||||
static const std::string str()
|
||||
{
|
||||
return "a{" + signature_of<_Key>::str() + signature_of<_Value>::str() + "}";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Function traits implementation inspired by (c) kennytm,
|
||||
// https://github.com/kennytm/utils/blob/master/traits.hpp
|
||||
template <typename _Type>
|
||||
struct function_traits
|
||||
: public function_traits<decltype(&_Type::operator())>
|
||||
@ -272,18 +325,32 @@ namespace sdbus {
|
||||
: 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>
|
||||
struct function_traits<_ReturnType(_Args...)>
|
||||
struct function_traits_base
|
||||
{
|
||||
typedef _ReturnType result_type;
|
||||
typedef std::tuple<_Args...> arguments_type;
|
||||
typedef std::tuple<std::decay_t<_Args>...> decayed_arguments_type;
|
||||
|
||||
typedef _ReturnType function_type(_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>
|
||||
struct arg
|
||||
{
|
||||
@ -294,6 +361,20 @@ namespace sdbus {
|
||||
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>
|
||||
struct function_traits<_ReturnType(*)(_Args...)>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
@ -332,12 +413,39 @@ namespace sdbus {
|
||||
: 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>
|
||||
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>
|
||||
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>
|
||||
struct aggregate_signature
|
||||
{
|
||||
@ -352,6 +460,7 @@ namespace sdbus {
|
||||
{
|
||||
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::string signature;
|
||||
for (const auto& item : signatures)
|
||||
@ -360,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>
|
||||
struct signature_of_function_input_arguments
|
||||
{
|
||||
@ -396,12 +483,21 @@ namespace sdbus {
|
||||
{
|
||||
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
|
||||
{
|
||||
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.
|
||||
// In this case just forward function return value.
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
@ -436,6 +532,16 @@ namespace sdbus {
|
||||
, 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_ */
|
||||
|
@ -68,7 +68,8 @@ namespace sdbus {
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename _ValueType>
|
||||
// Only allow conversion operator for true D-Bus type representations in C++
|
||||
template <typename _ValueType, typename = std::enable_if_t<signature_of<_ValueType>::is_valid>>
|
||||
operator _ValueType() const
|
||||
{
|
||||
return get<_ValueType>();
|
||||
@ -97,6 +98,16 @@ namespace sdbus {
|
||||
public:
|
||||
using std::tuple<_ValueTypes...>::tuple;
|
||||
|
||||
// Disable constructor if an older then 7.1.0 version of GCC is used
|
||||
#if !((defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) && !(__GNUC__ > 7 || (__GNUC__ == 7 && (__GNUC_MINOR__ > 1 || (__GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ > 0)))))
|
||||
Struct() = default;
|
||||
|
||||
explicit Struct(const std::tuple<_ValueTypes...>& t)
|
||||
: std::tuple<_ValueTypes...>(t)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template <std::size_t _I>
|
||||
auto& get()
|
||||
{
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <sdbus-c++/Interfaces.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Introspection.h>
|
||||
|
@ -32,91 +32,50 @@
|
||||
#include <poll.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
namespace {
|
||||
std::map<sdbus::internal::Connection::BusType, int(*)(sd_bus **)> busTypeToFactory
|
||||
{
|
||||
{sdbus::internal::Connection::BusType::eSystem, &sd_bus_open_system},
|
||||
{sdbus::internal::Connection::BusType::eSession, &sd_bus_open_user}
|
||||
};
|
||||
}
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
Connection::Connection(Connection::BusType type)
|
||||
: busType_(type)
|
||||
{
|
||||
sd_bus* bus{};
|
||||
auto r = busTypeToFactory[busType_](&bus);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to open system bus", -r);
|
||||
|
||||
auto bus = openBus(busType_);
|
||||
bus_.reset(bus);
|
||||
|
||||
// Process all requests that are part of the initial handshake,
|
||||
// like processing the Hello message response, authentication etc.,
|
||||
// to avoid connection authentication timeout in dbus daemon.
|
||||
r = sd_bus_flush(bus_.get());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to flush system bus on opening", -r);
|
||||
finishHandshake(bus);
|
||||
|
||||
r = eventfd(0, EFD_SEMAPHORE);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
|
||||
runFd_ = r;
|
||||
notificationFd_ = createLoopNotificationDescriptor();
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
leaveProcessingLoop();
|
||||
close(runFd_);
|
||||
closeLoopNotificationDescriptor(notificationFd_);
|
||||
}
|
||||
|
||||
void Connection::requestName(const std::string& name)
|
||||
{
|
||||
auto r = sd_bus_request_name(bus_.get(), name.c_str(), 0);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to request bus name", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to request bus name", -r);
|
||||
}
|
||||
|
||||
void Connection::releaseName(const std::string& name)
|
||||
{
|
||||
auto r = sd_bus_release_name(bus_.get(), name.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to release bus name", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to release bus name", -r);
|
||||
}
|
||||
|
||||
void Connection::enterProcessingLoop()
|
||||
{
|
||||
int semaphoreFd = runFd_;
|
||||
short int semaphoreEvents = POLLIN;
|
||||
|
||||
while (true)
|
||||
{
|
||||
/* Process requests */
|
||||
int r = sd_bus_process(bus_.get(), nullptr);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to process bus requests", -r);
|
||||
if (r > 0) /* we processed a request, try to process another one, right-away */
|
||||
continue;
|
||||
auto processed = processPendingRequest();
|
||||
if (processed)
|
||||
continue; // Process next one
|
||||
|
||||
r = sd_bus_get_fd(bus_.get());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r);
|
||||
auto sdbusFd = r;
|
||||
|
||||
r = sd_bus_get_events(bus_.get());
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r);
|
||||
short int sdbusEvents = r;
|
||||
|
||||
struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {semaphoreFd, semaphoreEvents, 0}};
|
||||
|
||||
/* Wait for the next request to process */
|
||||
uint64_t usec;
|
||||
sd_bus_get_timeout(bus_.get(), &usec);
|
||||
|
||||
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
||||
r = poll(fds, fdsCount, usec == (uint64_t) -1 ? -1 : (usec+999)/1000);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
|
||||
|
||||
if (fds[1].revents & POLLIN)
|
||||
break;
|
||||
auto success = waitForNextRequest();
|
||||
if (!success)
|
||||
break; // Exit processing loop
|
||||
if (success.asyncMsgsToProcess)
|
||||
processAsynchronousMessages();
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,12 +86,8 @@ void Connection::enterProcessingLoopAsync()
|
||||
|
||||
void Connection::leaveProcessingLoop()
|
||||
{
|
||||
assert(runFd_ >= 0);
|
||||
uint64_t value = 1;
|
||||
write(runFd_, &value, sizeof(value));
|
||||
|
||||
if (asyncLoopThread_.joinable())
|
||||
asyncLoopThread_.join();
|
||||
notifyProcessingLoopToExit();
|
||||
joinWithProcessingLoop();
|
||||
}
|
||||
|
||||
void* Connection::addObjectVTable( const std::string& objectPath
|
||||
@ -141,14 +96,15 @@ void* Connection::addObjectVTable( const std::string& objectPath
|
||||
, void* userData )
|
||||
{
|
||||
sd_bus_slot *slot{};
|
||||
|
||||
auto r = sd_bus_add_object_vtable( bus_.get()
|
||||
, &slot
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, static_cast<const sd_bus_vtable*>(vtable)
|
||||
, userData );
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to register object vtable", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to register object vtable", -r);
|
||||
|
||||
return slot;
|
||||
}
|
||||
@ -158,40 +114,46 @@ void Connection::removeObjectVTable(void* vtableHandle)
|
||||
sd_bus_slot_unref((sd_bus_slot *)vtableHandle);
|
||||
}
|
||||
|
||||
sdbus::Message Connection::createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const
|
||||
sdbus::MethodCall Connection::createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const
|
||||
{
|
||||
sd_bus_message *sdbusMsg{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); }; // Returned message will become an owner of sdbusMsg
|
||||
|
||||
// Returned message will become an owner of sdbusMsg
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); };
|
||||
|
||||
auto r = sd_bus_message_new_method_call( bus_.get()
|
||||
, &sdbusMsg
|
||||
, destination.c_str()
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, methodName.c_str() );
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to create method call", -r);
|
||||
|
||||
return Message(sdbusMsg, Message::Type::eMethodCall);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create method call", -r);
|
||||
|
||||
return MethodCall(sdbusMsg);
|
||||
}
|
||||
|
||||
sdbus::Message Connection::createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const
|
||||
sdbus::Signal Connection::createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const
|
||||
{
|
||||
sd_bus_message *sdbusSignal{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusSignal); }; // Returned message will become an owner of sdbusSignal
|
||||
|
||||
// Returned message will become an owner of sdbusSignal
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusSignal); };
|
||||
|
||||
auto r = sd_bus_message_new_signal( bus_.get()
|
||||
, &sdbusSignal
|
||||
, objectPath.c_str()
|
||||
, interfaceName.c_str()
|
||||
, signalName.c_str() );
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to create signal", -r);
|
||||
|
||||
return Message(sdbusSignal, Message::Type::eSignal);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create signal", -r);
|
||||
|
||||
return Signal(sdbusSignal);
|
||||
}
|
||||
|
||||
void* Connection::registerSignalHandler( const std::string& objectPath
|
||||
@ -201,10 +163,11 @@ void* Connection::registerSignalHandler( const std::string& objectPath
|
||||
, void* userData )
|
||||
{
|
||||
sd_bus_slot *slot{};
|
||||
|
||||
auto filter = composeSignalMatchFilter(objectPath, interfaceName, signalName);
|
||||
auto r = sd_bus_add_match(bus_.get(), &slot, filter.c_str(), callback, userData);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to register signal handler", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to register signal handler", -r);
|
||||
|
||||
return slot;
|
||||
}
|
||||
@ -214,20 +177,166 @@ void Connection::unregisterSignalHandler(void* 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
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(busType_);
|
||||
}
|
||||
|
||||
sd_bus* Connection::openBus(Connection::BusType type)
|
||||
{
|
||||
static std::map<sdbus::internal::Connection::BusType, int(*)(sd_bus **)> busTypeToFactory
|
||||
{
|
||||
{sdbus::internal::Connection::BusType::eSystem, &sd_bus_open_system},
|
||||
{sdbus::internal::Connection::BusType::eSession, &sd_bus_open_user}
|
||||
};
|
||||
|
||||
sd_bus* bus{};
|
||||
|
||||
auto r = busTypeToFactory[type](&bus);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open bus", -r);
|
||||
assert(bus != nullptr);
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
void Connection::finishHandshake(sd_bus* bus)
|
||||
{
|
||||
// Process all requests that are part of the initial handshake,
|
||||
// like processing the Hello message response, authentication etc.,
|
||||
// to avoid connection authentication timeout in dbus daemon.
|
||||
|
||||
assert(bus != nullptr);
|
||||
|
||||
auto r = sd_bus_flush(bus);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to flush bus on opening", -r);
|
||||
}
|
||||
|
||||
int Connection::createLoopNotificationDescriptor()
|
||||
{
|
||||
auto r = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void Connection::closeLoopNotificationDescriptor(int fd)
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void Connection::notifyProcessingLoop()
|
||||
{
|
||||
assert(notificationFd_ >= 0);
|
||||
|
||||
uint64_t value = 1;
|
||||
auto r = write(notificationFd_, &value, sizeof(value));
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to notify processing loop", -errno);
|
||||
}
|
||||
|
||||
void Connection::notifyProcessingLoopToExit()
|
||||
{
|
||||
exitLoopThread_ = true;
|
||||
|
||||
notifyProcessingLoop();
|
||||
}
|
||||
|
||||
void Connection::joinWithProcessingLoop()
|
||||
{
|
||||
if (asyncLoopThread_.joinable())
|
||||
asyncLoopThread_.join();
|
||||
}
|
||||
|
||||
bool Connection::processPendingRequest()
|
||||
{
|
||||
auto bus = bus_.get();
|
||||
|
||||
assert(bus != nullptr);
|
||||
|
||||
int r = sd_bus_process(bus, nullptr);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to process bus requests", -r);
|
||||
|
||||
return r > 0;
|
||||
}
|
||||
|
||||
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(notificationFd_ != 0);
|
||||
|
||||
auto r = sd_bus_get_fd(bus);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus descriptor", -r);
|
||||
auto sdbusFd = r;
|
||||
|
||||
r = sd_bus_get_events(bus);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get bus events", -r);
|
||||
short int sdbusEvents = r;
|
||||
|
||||
uint64_t usec;
|
||||
sd_bus_get_timeout(bus, &usec);
|
||||
|
||||
struct pollfd fds[] = {{sdbusFd, sdbusEvents, 0}, {notificationFd_, POLLIN, 0}};
|
||||
auto fdsCount = sizeof(fds)/sizeof(fds[0]);
|
||||
|
||||
r = poll(fds, fdsCount, usec == (uint64_t) -1 ? -1 : (usec+999)/1000);
|
||||
|
||||
if (r < 0 && errno == EINTR)
|
||||
return {true, false}; // Try again
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to wait on the bus", -errno);
|
||||
|
||||
if (fds[1].revents & POLLIN)
|
||||
{
|
||||
if (exitLoopThread_)
|
||||
return {false, false}; // Got exit notification
|
||||
|
||||
// Otherwise we have some async messages to process
|
||||
|
||||
uint64_t value{};
|
||||
auto r = read(notificationFd_, &value, sizeof(value));
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to read from the event descriptor", -errno);
|
||||
|
||||
return {false, true};
|
||||
}
|
||||
|
||||
return {true, false};
|
||||
}
|
||||
|
||||
std::string Connection::composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName )
|
||||
{
|
||||
std::string filter;
|
||||
|
||||
filter += "type='signal',";
|
||||
filter += "interface='" + interfaceName + "',";
|
||||
filter += "member='" + signalName + "',";
|
||||
filter += "path='" + objectPath + "'";
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,14 @@
|
||||
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include "IConnection.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "IConnection.h"
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
@ -62,13 +64,13 @@ namespace sdbus { namespace internal {
|
||||
, void* userData ) override;
|
||||
void removeObjectVTable(void* vtableHandle) override;
|
||||
|
||||
sdbus::Message createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const override;
|
||||
sdbus::Message createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const override;
|
||||
sdbus::MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const override;
|
||||
sdbus::Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const override;
|
||||
|
||||
void* registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
@ -77,17 +79,41 @@ namespace sdbus { namespace internal {
|
||||
, void* userData ) override;
|
||||
void unregisterSignalHandler(void* handlerCookie) override;
|
||||
|
||||
void sendReplyAsynchronously(const sdbus::MethodReply& reply) override;
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> clone() const override;
|
||||
|
||||
private:
|
||||
struct WaitResult
|
||||
{
|
||||
bool msgsToProcess;
|
||||
bool asyncMsgsToProcess;
|
||||
operator bool()
|
||||
{
|
||||
return msgsToProcess || asyncMsgsToProcess;
|
||||
}
|
||||
};
|
||||
static sd_bus* openBus(Connection::BusType type);
|
||||
static void finishHandshake(sd_bus* bus);
|
||||
static int createLoopNotificationDescriptor();
|
||||
static void closeLoopNotificationDescriptor(int fd);
|
||||
bool processPendingRequest();
|
||||
void processAsynchronousMessages();
|
||||
WaitResult waitForNextRequest();
|
||||
static std::string composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName );
|
||||
void notifyProcessingLoop();
|
||||
void notifyProcessingLoopToExit();
|
||||
void joinWithProcessingLoop();
|
||||
|
||||
private:
|
||||
std::unique_ptr<sd_bus, decltype(&sd_bus_flush_close_unref)> bus_{nullptr, &sd_bus_flush_close_unref};
|
||||
std::thread asyncLoopThread_;
|
||||
std::atomic<int> runFd_{-1};
|
||||
std::mutex mutex_;
|
||||
std::queue<MethodReply> asyncReplies_;
|
||||
std::atomic<bool> exitLoopThread_;
|
||||
int notificationFd_{-1};
|
||||
BusType busType_;
|
||||
|
||||
static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000;
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include "ScopeGuard.h"
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
@ -32,9 +33,10 @@ namespace sdbus
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
sd_bus_error_set_errno(&sdbusError, errNo);
|
||||
SCOPE_EXIT{ sd_bus_error_free(&sdbusError); };
|
||||
|
||||
std::string name(sdbusError.name);
|
||||
std::string message(customMsg + " (" + sdbusError.message + ")");
|
||||
sd_bus_error_free(&sdbusError);
|
||||
return sdbus::Error(name, message);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,9 @@
|
||||
|
||||
// Forward declaration
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class MethodCall;
|
||||
class MethodReply;
|
||||
class Signal;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
@ -47,14 +49,14 @@ namespace internal {
|
||||
, void* userData ) = 0;
|
||||
virtual void removeObjectVTable(void* vtableHandle) = 0;
|
||||
|
||||
virtual sdbus::Message createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
virtual sdbus::MethodCall createMethodCall( const std::string& destination
|
||||
, const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& methodName ) const = 0;
|
||||
|
||||
virtual sdbus::Message createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
virtual sdbus::Signal createSignal( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName ) const = 0;
|
||||
|
||||
virtual void* registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
@ -66,6 +68,8 @@ namespace internal {
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
|
||||
virtual void sendReplyAsynchronously(const sdbus::MethodReply& reply) = 0;
|
||||
|
||||
virtual std::unique_ptr<sdbus::internal::IConnection> clone() const = 0;
|
||||
|
||||
virtual ~IConnection() = default;
|
||||
|
@ -5,6 +5,7 @@ libsdbus_c___la_SOURCES = \
|
||||
Connection.cpp \
|
||||
ConvenienceClasses.cpp \
|
||||
Message.cpp \
|
||||
MethodResult.cpp \
|
||||
Object.cpp \
|
||||
ObjectProxy.cpp \
|
||||
Types.cpp \
|
||||
|
296
src/Message.cpp
296
src/Message.cpp
@ -31,11 +31,10 @@
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { /*namespace internal {*/
|
||||
namespace sdbus {
|
||||
|
||||
Message::Message(void *msg, Type type) noexcept
|
||||
Message::Message(void *msg) noexcept
|
||||
: msg_(msg)
|
||||
, type_(type)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
@ -48,8 +47,10 @@ Message::Message(const Message& other) noexcept
|
||||
|
||||
Message& Message::operator=(const Message& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
type_ = other.type_;
|
||||
ok_ = other.ok_;
|
||||
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
@ -64,10 +65,11 @@ Message::Message(Message&& other) noexcept
|
||||
|
||||
Message& Message::operator=(Message&& other) noexcept
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
|
||||
msg_ = other.msg_;
|
||||
other.msg_ = nullptr;
|
||||
type_ = other.type_;
|
||||
other.type_ = {};
|
||||
ok_ = other.ok_;
|
||||
other.ok_ = true;
|
||||
|
||||
@ -85,8 +87,7 @@ Message& Message::operator<<(bool item)
|
||||
int intItem = item;
|
||||
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a bool value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a bool value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -94,8 +95,7 @@ Message& Message::operator<<(bool item)
|
||||
Message& Message::operator<<(int16_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT16, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a int16_t value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int16_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -103,8 +103,7 @@ Message& Message::operator<<(int16_t item)
|
||||
Message& Message::operator<<(int32_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT32, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a int32_t value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int32_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -112,8 +111,7 @@ Message& Message::operator<<(int32_t item)
|
||||
Message& Message::operator<<(int64_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT64, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a int64_t value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a int64_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -121,8 +119,7 @@ Message& Message::operator<<(int64_t item)
|
||||
Message& Message::operator<<(uint8_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BYTE, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a byte value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a byte value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -130,8 +127,7 @@ Message& Message::operator<<(uint8_t item)
|
||||
Message& Message::operator<<(uint16_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT16, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a uint16_t value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint16_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -139,8 +135,7 @@ Message& Message::operator<<(uint16_t item)
|
||||
Message& Message::operator<<(uint32_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT32, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a uint32_t value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint32_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -148,8 +143,7 @@ Message& Message::operator<<(uint32_t item)
|
||||
Message& Message::operator<<(uint64_t item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT64, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a uint64_t value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a uint64_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -157,8 +151,7 @@ Message& Message::operator<<(uint64_t item)
|
||||
Message& Message::operator<<(double item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a double value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a double value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -166,8 +159,7 @@ Message& Message::operator<<(double item)
|
||||
Message& Message::operator<<(const char* item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, item);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a C-string value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a C-string value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -175,8 +167,7 @@ Message& Message::operator<<(const char* item)
|
||||
Message& Message::operator<<(const std::string& item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, item.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize a string value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize a string value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -191,8 +182,7 @@ Message& Message::operator<<(const Variant &item)
|
||||
Message& Message::operator<<(const ObjectPath &item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_OBJECT_PATH, item.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize an ObjectPath value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an ObjectPath value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -200,8 +190,7 @@ Message& Message::operator<<(const ObjectPath &item)
|
||||
Message& Message::operator<<(const Signature &item)
|
||||
{
|
||||
auto r = sd_bus_message_append_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, item.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to serialize an Signature value", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to serialize an Signature value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -213,8 +202,8 @@ Message& Message::operator>>(bool& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BOOLEAN, &intItem);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a bool value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a bool value", -r);
|
||||
|
||||
item = static_cast<bool>(intItem);
|
||||
|
||||
@ -226,8 +215,8 @@ Message& Message::operator>>(int16_t& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT16, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a int16_t value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a int16_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -237,8 +226,8 @@ Message& Message::operator>>(int32_t& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT32, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a int32_t value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a int32_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -248,8 +237,8 @@ Message& Message::operator>>(int64_t& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_INT64, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a bool value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a bool value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -259,8 +248,8 @@ Message& Message::operator>>(uint8_t& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_BYTE, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a byte value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a byte value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -270,8 +259,8 @@ Message& Message::operator>>(uint16_t& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT16, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a uint16_t value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a uint16_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -281,8 +270,8 @@ Message& Message::operator>>(uint32_t& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT32, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a uint32_t value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a uint32_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -292,8 +281,8 @@ Message& Message::operator>>(uint64_t& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_UINT64, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a uint64_t value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a uint64_t value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -303,8 +292,8 @@ Message& Message::operator>>(double& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_DOUBLE, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a double value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a double value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -314,8 +303,8 @@ Message& Message::operator>>(char*& item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_STRING, &item);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a string value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a string value", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -350,8 +339,8 @@ Message& Message::operator>>(ObjectPath &item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_OBJECT_PATH, &str);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize an ObjectPath value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize an ObjectPath value", -r);
|
||||
|
||||
if (str != nullptr)
|
||||
item = str;
|
||||
@ -365,8 +354,8 @@ Message& Message::operator>>(Signature &item)
|
||||
auto r = sd_bus_message_read_basic((sd_bus_message*)msg_, SD_BUS_TYPE_SIGNATURE, &str);
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to deserialize a Signature value", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to deserialize a Signature value", -r);
|
||||
|
||||
if (str != nullptr)
|
||||
item = str;
|
||||
@ -378,8 +367,7 @@ Message& Message::operator>>(Signature &item)
|
||||
Message& Message::openContainer(const std::string& signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to open a container", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a container", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -387,8 +375,7 @@ Message& Message::openContainer(const std::string& signature)
|
||||
Message& Message::closeContainer()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to close a container", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a container", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -396,8 +383,7 @@ Message& Message::closeContainer()
|
||||
Message& Message::openDictEntry(const std::string& signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to open a dictionary entry", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -405,8 +391,7 @@ Message& Message::openDictEntry(const std::string& signature)
|
||||
Message& Message::closeDictEntry()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to close a dictionary entry", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -414,8 +399,7 @@ Message& Message::closeDictEntry()
|
||||
Message& Message::openVariant(const std::string& signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to open a variant", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a variant", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -423,8 +407,7 @@ Message& Message::openVariant(const std::string& signature)
|
||||
Message& Message::closeVariant()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to close a variant", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a variant", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -432,8 +415,7 @@ Message& Message::closeVariant()
|
||||
Message& Message::openStruct(const std::string& signature)
|
||||
{
|
||||
auto r = sd_bus_message_open_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to open a struct", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to open a struct", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -441,8 +423,7 @@ Message& Message::openStruct(const std::string& signature)
|
||||
Message& Message::closeStruct()
|
||||
{
|
||||
auto r = sd_bus_message_close_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to close a struct", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to close a struct", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -453,8 +434,8 @@ Message& Message::enterContainer(const std::string& signature)
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_ARRAY, signature.c_str());
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to enter a container", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to enter a container", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -462,8 +443,7 @@ Message& Message::enterContainer(const std::string& signature)
|
||||
Message& Message::exitContainer()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to exit a container", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a container", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -473,8 +453,8 @@ Message& Message::enterDictEntry(const std::string& signature)
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_DICT_ENTRY, signature.c_str());
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to enter a dictionary entry", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to enter a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -482,8 +462,7 @@ Message& Message::enterDictEntry(const std::string& signature)
|
||||
Message& Message::exitDictEntry()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to exit a dictionary entry", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a dictionary entry", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -493,8 +472,8 @@ Message& Message::enterVariant(const std::string& signature)
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_VARIANT, signature.c_str());
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to enter a variant", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to enter a variant", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -502,8 +481,7 @@ Message& Message::enterVariant(const std::string& signature)
|
||||
Message& Message::exitVariant()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to exit a variant", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a variant", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -513,8 +491,8 @@ Message& Message::enterStruct(const std::string& signature)
|
||||
auto r = sd_bus_message_enter_container((sd_bus_message*)msg_, SD_BUS_TYPE_STRUCT, signature.c_str());
|
||||
if (r == 0)
|
||||
ok_ = false;
|
||||
else if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to enter a struct", -r);
|
||||
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to enter a struct", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -522,8 +500,7 @@ Message& Message::enterStruct(const std::string& signature)
|
||||
Message& Message::exitStruct()
|
||||
{
|
||||
auto r = sd_bus_message_exit_container((sd_bus_message*)msg_);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to exit a struct", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to exit a struct", -r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -542,8 +519,7 @@ void Message::clearFlags()
|
||||
void Message::copyTo(Message& destination, bool complete) const
|
||||
{
|
||||
auto r = sd_bus_message_copy((sd_bus_message*)destination.msg_, (sd_bus_message*)msg_, complete);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to copy the message", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to copy the message", -r);
|
||||
}
|
||||
|
||||
void Message::seal()
|
||||
@ -551,71 +527,13 @@ void Message::seal()
|
||||
const auto messageCookie = 1;
|
||||
const auto sealTimeout = 0;
|
||||
auto r = sd_bus_message_seal((sd_bus_message*)msg_, messageCookie, sealTimeout);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to seal the message", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to seal the message", -r);
|
||||
}
|
||||
|
||||
void Message::rewind(bool complete)
|
||||
{
|
||||
auto r = sd_bus_message_rewind((sd_bus_message*)msg_, complete);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("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);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to send reply", -r);
|
||||
|
||||
return Message();
|
||||
}
|
||||
else if (type_ == Type::eSignal)
|
||||
{
|
||||
auto r = sd_bus_send(nullptr, (sd_bus_message*)msg_, nullptr);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("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);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to create method reply", -r);
|
||||
|
||||
assert(sdbusReply != nullptr);
|
||||
|
||||
return Message(sdbusReply, Type::eMethodReply);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
|
||||
}
|
||||
|
||||
std::string Message::getInterfaceName() const
|
||||
@ -633,8 +551,7 @@ void Message::peekType(std::string& type, std::string& contents) const
|
||||
char typeSig;
|
||||
const char* contentsSig;
|
||||
auto r = sd_bus_message_peek_type((sd_bus_message*)msg_, &typeSig, &contentsSig);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to peek message type", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to peek message type", -r);
|
||||
type = typeSig;
|
||||
contents = contentsSig;
|
||||
}
|
||||
@ -649,9 +566,70 @@ bool Message::isEmpty() const
|
||||
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()
|
||||
@ -661,16 +639,14 @@ Message createPlainMessage()
|
||||
sd_bus* bus{};
|
||||
SCOPE_EXIT{ sd_bus_unref(bus); }; // sdbusMsg will hold reference to the bus
|
||||
r = sd_bus_default_system(&bus);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("Failed to get default system bus", -r);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to get default system bus", -r);
|
||||
|
||||
sd_bus_message* sdbusMsg{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); }; // Returned message will become an owner of sdbusMsg
|
||||
r = sd_bus_message_new(bus, &sdbusMsg, _SD_BUS_MESSAGE_TYPE_INVALID);
|
||||
if (r < 0)
|
||||
SDBUS_THROW_ERROR("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);
|
||||
}
|
||||
|
||||
}
|
173
src/Object.cpp
173
src/Object.cpp
@ -27,6 +27,7 @@
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/MethodResult.h>
|
||||
#include "IConnection.h"
|
||||
#include "VTableUtils.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);
|
||||
|
||||
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;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register method: method already exists", EINVAL);
|
||||
@ -103,86 +131,116 @@ void Object::finishRegistration()
|
||||
const auto& interfaceName = item.first;
|
||||
auto& interfaceData = item.second;
|
||||
|
||||
auto& vtable = interfaceData.vtable_;
|
||||
assert(vtable.empty());
|
||||
|
||||
vtable.push_back(createVTableStartItem());
|
||||
for (const auto& item : interfaceData.methods_)
|
||||
{
|
||||
const auto& methodName = item.first;
|
||||
const auto& methodData = item.second;
|
||||
|
||||
vtable.push_back(createVTableMethodItem( methodName.c_str()
|
||||
, methodData.inputArgs_.c_str()
|
||||
, methodData.outputArgs_.c_str()
|
||||
, &Object::sdbus_method_callback ));
|
||||
}
|
||||
for (const auto& item : interfaceData.signals_)
|
||||
{
|
||||
const auto& signalName = item.first;
|
||||
const auto& signalData = item.second;
|
||||
|
||||
vtable.push_back(createVTableSignalItem( signalName.c_str()
|
||||
, signalData.signature_.c_str() ));
|
||||
}
|
||||
for (const auto& item : interfaceData.properties_)
|
||||
{
|
||||
const auto& propertyName = item.first;
|
||||
const auto& propertyData = item.second;
|
||||
|
||||
if (!propertyData.setCallback_)
|
||||
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, &Object::sdbus_property_get_callback ));
|
||||
else
|
||||
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, &Object::sdbus_property_set_callback ));
|
||||
}
|
||||
vtable.push_back(createVTableEndItem());
|
||||
|
||||
// Tell, don't ask
|
||||
auto slot = (sd_bus_slot*) connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
|
||||
interfaceData.slot_.reset(slot);
|
||||
interfaceData.slot_.get_deleter() = [this](void *slot){ connection_.removeObjectVTable(slot); };
|
||||
const auto& vtable = createInterfaceVTable(interfaceData);
|
||||
activateInterfaceVTable(interfaceName, interfaceData, vtable);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void Object::sendReplyAsynchronously(const MethodReply& reply)
|
||||
{
|
||||
connection_.sendReplyAsynchronously(reply);
|
||||
}
|
||||
|
||||
const std::vector<sd_bus_vtable>& Object::createInterfaceVTable(InterfaceData& interfaceData)
|
||||
{
|
||||
auto& vtable = interfaceData.vtable_;
|
||||
assert(vtable.empty());
|
||||
|
||||
vtable.push_back(createVTableStartItem());
|
||||
registerMethodsToVTable(interfaceData, vtable);
|
||||
registerSignalsToVTable(interfaceData, vtable);
|
||||
registerPropertiesToVTable(interfaceData, vtable);
|
||||
vtable.push_back(createVTableEndItem());
|
||||
|
||||
return vtable;
|
||||
}
|
||||
|
||||
void Object::registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.methods_)
|
||||
{
|
||||
const auto& methodName = item.first;
|
||||
const auto& methodData = item.second;
|
||||
|
||||
vtable.push_back(createVTableMethodItem( methodName.c_str()
|
||||
, methodData.inputArgs_.c_str()
|
||||
, methodData.outputArgs_.c_str()
|
||||
, &Object::sdbus_method_callback ));
|
||||
}
|
||||
}
|
||||
|
||||
void Object::registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.signals_)
|
||||
{
|
||||
const auto& signalName = item.first;
|
||||
const auto& signalData = item.second;
|
||||
|
||||
vtable.push_back(createVTableSignalItem( signalName.c_str()
|
||||
, signalData.signature_.c_str() ));
|
||||
}
|
||||
}
|
||||
|
||||
void Object::registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable)
|
||||
{
|
||||
for (const auto& item : interfaceData.properties_)
|
||||
{
|
||||
const auto& propertyName = item.first;
|
||||
const auto& propertyData = item.second;
|
||||
|
||||
if (!propertyData.setCallback_)
|
||||
vtable.push_back(createVTablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, &Object::sdbus_property_get_callback ));
|
||||
else
|
||||
vtable.push_back(createVTableWritablePropertyItem( propertyName.c_str()
|
||||
, propertyData.signature_.c_str()
|
||||
, &Object::sdbus_property_get_callback
|
||||
, &Object::sdbus_property_set_callback ));
|
||||
}
|
||||
}
|
||||
|
||||
void Object::activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable )
|
||||
{
|
||||
// Tell, don't ask
|
||||
auto slot = (sd_bus_slot*) connection_.addObjectVTable(objectPath_, interfaceName, &vtable[0], this);
|
||||
interfaceData.slot_.reset(slot);
|
||||
interfaceData.slot_.get_deleter() = [this](void *slot){ connection_.removeObjectVTable(slot); };
|
||||
}
|
||||
|
||||
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);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[message.getInterfaceName()].methods_[message.getMemberName()].callback_;
|
||||
assert(callback);
|
||||
|
||||
auto reply = message.createReply();
|
||||
|
||||
try
|
||||
{
|
||||
callback(message, reply);
|
||||
callback(message);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
reply.send();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -194,7 +252,7 @@ int Object::sdbus_property_get_callback( sd_bus */*bus*/
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
Message reply(sdbusReply, Message::Type::ePlainMessage);
|
||||
Message reply(sdbusReply);
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
@ -226,7 +284,7 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
, void *userData
|
||||
, sd_bus_error *retError )
|
||||
{
|
||||
Message value(sdbusValue, Message::Type::ePlainMessage);
|
||||
Message value(sdbusValue);
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
@ -240,7 +298,6 @@ int Object::sdbus_property_set_callback( sd_bus */*bus*/
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
63
src/Object.h
63
src/Object.h
@ -32,6 +32,7 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
@ -50,6 +51,12 @@ namespace internal {
|
||||
, const std::string& outputSignature
|
||||
, 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
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) override;
|
||||
@ -67,30 +74,12 @@ namespace internal {
|
||||
|
||||
void finishRegistration() override;
|
||||
|
||||
sdbus::Message createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Message& message) override;
|
||||
sdbus::Signal createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Signal& message) override;
|
||||
|
||||
void sendReplyAsynchronously(const MethodReply& reply);
|
||||
|
||||
private:
|
||||
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_property_get_callback( sd_bus *bus
|
||||
, const char *objectPath
|
||||
, const char *interface
|
||||
, const char *property
|
||||
, sd_bus_message *sdbusReply
|
||||
, void *userData
|
||||
, sd_bus_error *retError );
|
||||
static int sdbus_property_set_callback( sd_bus *bus
|
||||
, const char *objectPath
|
||||
, const char *interface
|
||||
, const char *property
|
||||
, sd_bus_message *sdbusValue
|
||||
, void *userData
|
||||
, sd_bus_error *retError );
|
||||
|
||||
private:
|
||||
sdbus::internal::IConnection& connection_;
|
||||
std::string objectPath_;
|
||||
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
{
|
||||
@ -99,7 +88,7 @@ namespace internal {
|
||||
{
|
||||
std::string inputArgs_;
|
||||
std::string outputArgs_;
|
||||
method_callback callback_;
|
||||
std::function<void(MethodCall&)> callback_;
|
||||
};
|
||||
std::map<MethodName, MethodData> methods_;
|
||||
using SignalName = std::string;
|
||||
@ -120,6 +109,34 @@ namespace internal {
|
||||
|
||||
std::unique_ptr<void, std::function<void(void*)>> slot_;
|
||||
};
|
||||
|
||||
static const std::vector<sd_bus_vtable>& createInterfaceVTable(InterfaceData& interfaceData);
|
||||
static void registerMethodsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
static void registerSignalsToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
static void registerPropertiesToVTable(const InterfaceData& interfaceData, std::vector<sd_bus_vtable>& vtable);
|
||||
void activateInterfaceVTable( const std::string& interfaceName
|
||||
, InterfaceData& interfaceData
|
||||
, const std::vector<sd_bus_vtable>& vtable );
|
||||
|
||||
static int sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
static int sdbus_property_get_callback( sd_bus *bus
|
||||
, const char *objectPath
|
||||
, const char *interface
|
||||
, const char *property
|
||||
, sd_bus_message *sdbusReply
|
||||
, void *userData
|
||||
, sd_bus_error *retError );
|
||||
static int sdbus_property_set_callback( sd_bus *bus
|
||||
, const char *objectPath
|
||||
, const char *interface
|
||||
, const char *property
|
||||
, sd_bus_message *sdbusValue
|
||||
, void *userData
|
||||
, sd_bus_error *retError );
|
||||
|
||||
private:
|
||||
sdbus::internal::IConnection& connection_;
|
||||
std::string objectPath_;
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
};
|
||||
|
||||
|
@ -60,13 +60,13 @@ ObjectProxy::~ObjectProxy()
|
||||
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
|
||||
return connection_->createMethodCall(destination_, objectPath_, interfaceName, methodName);
|
||||
}
|
||||
|
||||
Message ObjectProxy::callMethod(const Message& message)
|
||||
MethodReply ObjectProxy::callMethod(const MethodCall& message)
|
||||
{
|
||||
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*/)
|
||||
{
|
||||
Message message(sdbusMessage, Message::Type::eSignal);
|
||||
Signal message(sdbusMessage);
|
||||
|
||||
auto* object = static_cast<ObjectProxy*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
@ -166,6 +166,20 @@ std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( IConnection& connection
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::unique_ptr<IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(connection.get());
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
connection.release();
|
||||
|
||||
return std::make_unique<sdbus::internal::ObjectProxy>( std::unique_ptr<sdbus::internal::IConnection>(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
|
@ -52,8 +52,8 @@ namespace internal {
|
||||
, std::string objectPath );
|
||||
~ObjectProxy();
|
||||
|
||||
Message createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
Message callMethod(const Message& message) override;
|
||||
MethodCall createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
MethodReply callMethod(const MethodCall& message) override;
|
||||
|
||||
void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
|
@ -136,25 +136,47 @@ std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Node
|
||||
{
|
||||
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 inArgs = args.select("direction" , "in");
|
||||
Nodes outArgs = args.select("direction" , "out");
|
||||
|
||||
std::string argStr, argTypeStr;
|
||||
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
registrationSS << tab << tab << "object_.registerMethod(\""
|
||||
<< methodName << "\")"
|
||||
<< ".onInterface(interfaceName)"
|
||||
<< ".implementedAs("
|
||||
<< "[this]("
|
||||
<< (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + "> result" + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argTypeStr
|
||||
<< "){ return this->" << methodName << "("
|
||||
<< "){ " << (async ? "" : "return ") << "this->" << methodName << "("
|
||||
<< (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
|
||||
<< argStr << "); });" << endl;
|
||||
|
||||
declarationSS << tab
|
||||
<< "virtual " << outArgsToType(outArgs) << " " << methodName
|
||||
<< "(" << argTypeStr << ") = 0;" << endl;
|
||||
<< "virtual "
|
||||
<< (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());
|
||||
|
@ -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;
|
||||
|
||||
if (args.size() == 0)
|
||||
{
|
||||
if (bareList)
|
||||
return "";
|
||||
|
||||
retTypeSS << "void";
|
||||
}
|
||||
else if (args.size() == 1)
|
||||
@ -141,7 +144,8 @@ std::string BaseGenerator::outArgsToType(const Nodes& args) const
|
||||
}
|
||||
else if (args.size() >= 2)
|
||||
{
|
||||
retTypeSS << "std::tuple<";
|
||||
if (!bareList)
|
||||
retTypeSS << "std::tuple<";
|
||||
|
||||
bool firstArg = true;
|
||||
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 << ">";
|
||||
if (!bareList)
|
||||
retTypeSS << ">";
|
||||
}
|
||||
|
||||
return retTypeSS.str();
|
||||
|
@ -94,7 +94,7 @@ protected:
|
||||
* @param args
|
||||
* @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)
|
||||
|
||||
|
@ -14,7 +14,7 @@ endif
|
||||
|
||||
# Setting per-file flags
|
||||
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@
|
||||
|
||||
CLEANFILES = *~ *.lo *.la
|
||||
@ -29,7 +29,7 @@ unittests/TypeTraits_test.cpp \
|
||||
unittests/Types_test.cpp \
|
||||
unittests/Message_test.cpp
|
||||
|
||||
libsdbus_c___unittests_LDFLAGS = -L$(top_builddir)/src
|
||||
libsdbus_c___unittests_LDFLAGS = -L$(top_builddir)/src -pthread
|
||||
|
||||
libsdbus_c___unittests_LDADD = \
|
||||
-lsdbus-c++ \
|
||||
@ -45,14 +45,13 @@ integrationtests/libsdbus-c++_integrationtests.cpp \
|
||||
integrationtests/Connection_test.cpp \
|
||||
integrationtests/AdaptorAndProxy_test.cpp
|
||||
|
||||
libsdbus_c___integrationtests_LDFLAGS = -L$(top_builddir)/src
|
||||
libsdbus_c___integrationtests_LDFLAGS = -L$(top_builddir)/src -pthread
|
||||
|
||||
libsdbus_c___integrationtests_LDADD = \
|
||||
-lsdbus-c++ \
|
||||
@libsdbus_cpp_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
-lgmock \
|
||||
-lpthread
|
||||
-lgmock
|
||||
|
||||
TESTS += libsdbus-c++_integrationtests
|
||||
|
||||
|
@ -40,9 +40,12 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <chrono>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
using ::testing::ElementsAre;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -67,7 +70,7 @@ private:
|
||||
{
|
||||
m_adaptor = std::make_unique<TestingAdaptor>(m_connection);
|
||||
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
|
||||
@ -197,6 +200,62 @@ TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
||||
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, HandlesCorrectlyABulkOfParallelServerSideAsyncMethods)
|
||||
{
|
||||
std::mutex mtx;
|
||||
std::atomic<size_t> resultCount{};
|
||||
std::atomic<bool> invoke{};
|
||||
std::atomic<int> startedCount{};
|
||||
auto call = [&]()
|
||||
{
|
||||
TestingProxy proxy{INTERFACE_NAME, OBJECT_PATH};
|
||||
++startedCount;
|
||||
while (!invoke) ;
|
||||
|
||||
size_t localResultCount{};
|
||||
for (size_t i = 0; i < 500; ++i)
|
||||
{
|
||||
auto result = proxy.doOperationAsync(i % 2);
|
||||
if (result == (i % 2)) // Correct return value?
|
||||
localResultCount++;
|
||||
}
|
||||
|
||||
resultCount += localResultCount;
|
||||
};
|
||||
|
||||
std::thread invocations[]{std::thread{call}, std::thread{call}, std::thread{call}};
|
||||
while (startedCount != 3) ;
|
||||
invoke = true;
|
||||
std::for_each(std::begin(invocations), std::end(invocations), [](auto& t){ t.join(); });
|
||||
|
||||
ASSERT_THAT(resultCount, Eq(1500));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||
|
@ -27,6 +27,8 @@
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
|
||||
|
||||
#include "adaptor-glue.h"
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
class TestingAdaptor : public sdbus::Interfaces<testing_adaptor>
|
||||
{
|
||||
@ -72,8 +74,7 @@ protected:
|
||||
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const
|
||||
{
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> x{STRING_VALUE, {{{INT32_VALUE, INT32_VALUE}}}};
|
||||
return x;
|
||||
return sdbus::make_struct(STRING_VALUE, sdbus::make_struct(std::map<int32_t, int32_t>{{INT32_VALUE, INT32_VALUE}}));
|
||||
}
|
||||
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
@ -98,6 +99,30 @@ protected:
|
||||
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)
|
||||
{
|
||||
if (param == 0)
|
||||
{
|
||||
// Don't sleep and return the result from this thread
|
||||
result.returnResults(param);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Process asynchronously in another thread and return the result from there
|
||||
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::ObjectPath getObjectPath() const { return OBJECT_PATH_VALUE; }
|
||||
|
||||
|
@ -82,6 +82,16 @@ protected:
|
||||
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("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 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 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::ObjectPath getObjectPath() const = 0;
|
||||
virtual ComplexType getComplex() const = 0;
|
||||
|
@ -120,6 +120,20 @@ public:
|
||||
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 result;
|
||||
|
@ -110,13 +110,6 @@ TEST(AMessage, IsNotEmptyWhenContainsAValue)
|
||||
ASSERT_FALSE(msg.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AMessage, ReturnsItsTypeWhenAsked)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
ASSERT_THAT(msg.getType(), Eq(sdbus::Message::Type::ePlainMessage));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryASimpleInteger)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
using ::testing::Eq;
|
||||
|
||||
@ -127,3 +128,42 @@ TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
@ -203,3 +203,12 @@ TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
|
||||
ASSERT_THAT(receivedVariant2.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant3.get<decltype(value)>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AStruct, CreatesStructFromTuple)
|
||||
{
|
||||
std::tuple<int32_t, std::string> value{1234, "abcd"};
|
||||
sdbus::Struct<int32_t, std::string> valueStruct{value};
|
||||
|
||||
ASSERT_THAT(std::get<0>(valueStruct), Eq(std::get<0>(value)));
|
||||
ASSERT_THAT(std::get<1>(valueStruct), Eq(std::get<1>(value)));
|
||||
}
|
||||
|
Reference in New Issue
Block a user