forked from Kistler-Group/sdbus-cpp
Introduce sdbus-c++ v0.2.3
This commit is contained in:
42
.gitignore
vendored
Normal file
42
.gitignore
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# c++
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
m4/
|
||||
build/
|
||||
.deps/
|
||||
.dirstamp
|
||||
.libs/
|
||||
test/libsdbus-c++_unittests
|
||||
test/libsdbus-c++_integrationtests
|
||||
test/run-test-on-device.sh
|
||||
|
||||
#eclipse
|
||||
.cproject
|
||||
.settings
|
||||
*.log
|
||||
|
||||
#autotools
|
||||
sdbus-cpp.pc
|
||||
*Makefile
|
||||
*Makefile.in
|
||||
aclocal.m4
|
||||
arm-poky-linux-gnueabi-libtool
|
||||
autom4te.cache
|
||||
configure
|
||||
config.h
|
||||
config.status
|
||||
config.guess
|
||||
config.h.in
|
||||
config.sub
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
ltmain.sh
|
||||
missing
|
||||
stamp-h1
|
||||
test-driver
|
||||
.autotools
|
||||
.gdbinit
|
||||
sdbus-cpp_gdb_arm-poky-linux-gnueabi.launch
|
||||
sdbus-c++.pc
|
35
.project
Normal file
35
.project
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>sdbus-cpp</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.autotools.core.genmakebuilderV2</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
<nature>org.yocto.sdk.ide.YoctoSDKNature</nature>
|
||||
<nature>org.eclipse.cdt.autotools.core.autotoolsNatureV2</nature>
|
||||
<nature>org.yocto.sdk.ide.YoctoSDKAutotoolsNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
5
AUTHORS
Normal file
5
AUTHORS
Normal file
@ -0,0 +1,5 @@
|
||||
The library:
|
||||
Stanislav Angelovic (stanislav.angelovic[at]kistler.com)
|
||||
|
||||
Stub generator:
|
||||
Lukas Durfina (lukas.durfina[at]kistler.com)
|
6
INSTALL
Normal file
6
INSTALL
Normal file
@ -0,0 +1,6 @@
|
||||
Building:
|
||||
$ ./autogen.sh ${CONFIGURE_FLAGS}
|
||||
$ make
|
||||
|
||||
Installing:
|
||||
$ sudo make install
|
22
Makefile.am
Normal file
22
Makefile.am
Normal file
@ -0,0 +1,22 @@
|
||||
EXTRA_DIST = autogen.sh
|
||||
SUBDIRS = include src test
|
||||
|
||||
DISTCHECK_CONFIGURE_FLAGS=
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = sdbus-c++.pc
|
||||
|
||||
CLEANFILES = *~ *.lo *.la
|
||||
|
||||
MOSTLYCLEANFILES = *.o
|
||||
|
||||
DISTCLEANFILES = \
|
||||
*libtool* \
|
||||
aclocal.m4 \
|
||||
compile config.* configure \
|
||||
depcomp install-sh \
|
||||
ltmain.sh \
|
||||
Makefile Makefile.in \
|
||||
missing \
|
||||
stamp-h1 \
|
||||
sdbus-c++.pc
|
4
NEWS
Normal file
4
NEWS
Normal file
@ -0,0 +1,4 @@
|
||||
sdbus-c++: D-Bus IPC C++ binding library based on sd-bus
|
||||
|
||||
v0.2.3:
|
||||
* sdbus-c++ v0.2.3 released to the public!
|
46
README.md
46
README.md
@ -1,2 +1,44 @@
|
||||
# sdbus-cpp
|
||||
D-Bus IPC C++ binding library build on top of sd-bus
|
||||
sdbus-c++
|
||||
=========
|
||||
|
||||
sdbus-c++ is a C++ API library for D-Bus IPC, based on sd-bus implementation.
|
||||
|
||||
Building and installing the library
|
||||
-----------------------------------
|
||||
|
||||
```bash
|
||||
$ ./autogen.sh ${CONFIGURE_FLAGS}
|
||||
$ make
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
Use `--disable-tests` flag when configuring to disable building unit and integration tests for the library.
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
* `C++17` - the library uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature.
|
||||
* `libsystemd` - systemd library containing sd-bus implementation. Systemd v236 at least is needed for sdbus-c++ to compile.
|
||||
* `googletest` - google unit testing framework, only necessary when building tests
|
||||
|
||||
Licensing
|
||||
---------
|
||||
|
||||
The library is distributed under LGPLv2.1+ license.
|
||||
|
||||
References/documentation
|
||||
------------------------
|
||||
|
||||
* [D-Bus Specification](https://dbus.freedesktop.org/doc/dbus-specification.html)
|
||||
* [sd-bus Overview](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html)
|
||||
* [Tutorial: Using sdbus-c++](doc/using-sdbus-c++.md)
|
||||
|
||||
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.
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
stanislav.angelovic[at]kistler.com
|
||||
|
10
autogen.sh
Executable file
10
autogen.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#! /bin/sh
|
||||
[ -e config.cache ] && rm -f config.cache
|
||||
|
||||
libtoolize --automake
|
||||
aclocal ${OECORE_ACLOCAL_OPTS}
|
||||
autoconf
|
||||
autoheader
|
||||
automake -a
|
||||
./configure $@
|
||||
exit
|
62
configure.ac
Normal file
62
configure.ac
Normal file
@ -0,0 +1,62 @@
|
||||
AC_PREREQ(2.61)
|
||||
|
||||
# package version number (not shared library version)
|
||||
# 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],
|
||||
[sdbus_cpp_version_major.sdbus_cpp_version_minor.sdbus_cpp_version_micro])
|
||||
m4_define([sdbus_cpp_api_version],
|
||||
[sdbus_cpp_version_major.sdbus_cpp_version_minor])
|
||||
|
||||
AC_INIT(libsdbus-c++, sdbus_cpp_version)
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_LIBTOOL
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
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)]
|
||||
[https://www.freedesktop.org/wiki/Software/systemd/])
|
||||
)
|
||||
|
||||
# Checks for library functions.
|
||||
#AC_CHECK_FUNCS([memset])
|
||||
|
||||
AC_SUBST(libsdbus_cpp_CFLAGS)
|
||||
AC_SUBST(libsdbus_cpp_LIBS)
|
||||
|
||||
AC_ARG_ENABLE([tests],
|
||||
[AS_HELP_STRING([--enable-tests],
|
||||
[build and install tests @<:@default=yes@:>@])],
|
||||
[],
|
||||
[enable_tests=yes])
|
||||
AM_CONDITIONAL([ENABLE_TESTS], [test x$enable_tests = xyes])
|
||||
|
||||
#icondir=${datadir}/icons/hicolor/32x32/apps
|
||||
#AC_SUBST(icondir)
|
||||
|
||||
AC_OUTPUT([
|
||||
Makefile
|
||||
include/Makefile
|
||||
src/Makefile
|
||||
test/Makefile
|
||||
sdbus-c++.pc
|
||||
])
|
||||
|
||||
echo ""
|
||||
echo " sdbus-cpp $VERSION"
|
||||
echo " ====================="
|
||||
echo ""
|
||||
echo " To build the project, run \"make\""
|
||||
echo ""
|
727
doc/using-sdbus-c++.md
Normal file
727
doc/using-sdbus-c++.md
Normal file
@ -0,0 +1,727 @@
|
||||
Using sdbus-c++ library
|
||||
=======================
|
||||
|
||||
**Table of contents**
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
2. [Integrating sdbus-c++ into your project](#integrating-sdbus-c-into-your-project)
|
||||
3. [Header files and namespaces](#header-files-and-namespaces)
|
||||
4. [Error signalling and propagation](#error-signalling-and-propagation)
|
||||
5. [Multiple layers of sdbus-c++ API](#multiple-layers-of-sdbus-c-api)
|
||||
6. [An example: Number concatenator](#an-example-number-concatenator)
|
||||
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)
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
sdbus-c++ is a C++ wrapper library built on top of [sd-bus](http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html), a lightweight D-Bus client
|
||||
library implemented within systemd project. It provides D-Bus functionality on a higher level of abstraction, trying to employ C++ type system
|
||||
to shift as much work as possible from the developer to the compiler.
|
||||
|
||||
sdbus-c++ does not cover the entire sd-bus API, but provides tools for implementing the most common functionality - RPC
|
||||
method calls, signals and properties. There is room for additions and improvements, as needed and when needed.
|
||||
|
||||
Integrating sdbus-c++ into your project
|
||||
---------------------------------------
|
||||
|
||||
The library build system is based on Autotools. The library supports `pkg-config`, so integrating it into your autotools project
|
||||
is a two step process.
|
||||
|
||||
1. Add `PKG_CHECK_MODULES` macro into your `configure.ac`:
|
||||
```bash
|
||||
PKG_CHECK_MODULES(SDBUSCPP, [sdbus-c++ >= 0.1],,
|
||||
AC_MSG_ERROR([You need the sdbus-c++ library (version 0.1 or better)]
|
||||
[http://www.kistler.com/])
|
||||
)
|
||||
```
|
||||
|
||||
2. Update `*_CFLAGS` and `*_LDFLAGS` in Makefiles of modules that use *sdbus-c++*, for example like this:
|
||||
```bash
|
||||
AM_CXXFLAGS = -std=c++17 -pedantic -W -Wall @SDBUSCPP_CFLAGS@ ...
|
||||
AM_LDFLAGS = @SDBUSCPP_LIBS@ ...
|
||||
```
|
||||
|
||||
Note: sdbus-c++ library depends on C++17, since it uses C++17 `std::uncaught_exceptions()` feature. When building sdbus-c++ manually, make sure you use a compiler that supports that feature. To use the library, make sure you have a C++ standard library that supports the feature. The feature is supported by e.g. gcc >= 6, and clang >= 3.7.
|
||||
|
||||
Header files and namespaces
|
||||
---------------------------
|
||||
|
||||
All sdbus-c++ header files reside in the `sdbus-c++` subdirectory within the standard include directory. Users can either include
|
||||
individual header files, like so:
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
```
|
||||
|
||||
or just include the global header file that pulls in everything:
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
```
|
||||
|
||||
All public types and functions of sdbus-c++ reside in the `sdbus` namespace.
|
||||
|
||||
Error signalling and propagation
|
||||
--------------------------------
|
||||
|
||||
`sdbus::Error` exception is used to signal errors in sdbus-c++. There are two types of errors:
|
||||
* D-Bus related errors, like call timeouts, failed socket allocation, etc.
|
||||
* user errors, i.e. errors signalled and propagated from remote methods back to the caller.
|
||||
|
||||
The exception object carries the error name and error message with it.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
An example: Number concatenator
|
||||
-------------------------------
|
||||
|
||||
Let's have an object `/org/sdbuscpp/concatenator` that implements the `org.sdbuscpp.concatenator` interface. The interface exposes the following:
|
||||
* a `concatenate` method that takes a collection of integers and a separator string and returns a string that is the concatenation of all
|
||||
integers from the collection using given separator,
|
||||
* a `concatenated` signal that is emitted at the end of each successful concatenation.
|
||||
|
||||
In the following sections, we will elaborate on the ways of implementing such an object on both the server and the client side.
|
||||
|
||||
Implementing the Concatenator example using basic sdbus-c++ API layer
|
||||
---------------------------------------------------------------------
|
||||
|
||||
In the basic API layer, we already have abstractions for D-Bus connections, objects and object proxies, with which we can interact via
|
||||
interfaces. However, we still work with the concept of messages. To issue a method call for example, we have to go through several steps:
|
||||
we have to create a method call message first, serialize method arguments into the message, and send the message at last. We get the reply
|
||||
message (if applicable) in return, so we have to deserialize the return values from it manually.
|
||||
|
||||
Overloaded versions of C++ insertion/extraction operators are used for serialization/deserialization. That makes the client code much simpler.
|
||||
|
||||
### Server side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Yeah, global variable is ugly, but this is just an example and we want to access
|
||||
// the concatenator instance from within the concatenate method handler to be able
|
||||
// to emit signals.
|
||||
sdbus::IObject* g_concatenator{};
|
||||
|
||||
void concatenate(sdbus::Message& msg, sdbus::Message& reply)
|
||||
{
|
||||
// Deserialize the collection of numbers from the message
|
||||
std::vector<int> numbers;
|
||||
msg >> numbers;
|
||||
|
||||
// Deserialize separator from the message
|
||||
std::string separator;
|
||||
msg >> separator;
|
||||
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Serialize resulting string to the reply
|
||||
reply << result;
|
||||
|
||||
// Emit 'concatenated' signal
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
auto signalMsg = g_concatenator->createSignal(interfaceName, "concatenated");
|
||||
signalMsg << result;
|
||||
g_concatenator->emitSignal(signalMsg);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenator = sdbus::createObject(*connection, objectPath);
|
||||
|
||||
g_concatenator = concatenator.get();
|
||||
|
||||
// Register D-Bus methods and signals on the concatenator object, and exports the object.
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenator->registerMethod(interfaceName, "concatenate", "ais", "s", &concatenate);
|
||||
concatenator->registerSignal(interfaceName, "concatenated", "s");
|
||||
concatenator->finishRegistration();
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
}
|
||||
```
|
||||
|
||||
### Client side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
void onConcatenated(sdbus::Message& signalMsg)
|
||||
{
|
||||
std::string concatenatedString;
|
||||
msg >> 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
|
||||
const char* destinationName = "org.sdbuscpp.concatenator";
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenatorProxy = sdbus::createObjectProxy(destinationName, objectPath);
|
||||
|
||||
// Let's subscribe for the 'concatenated' signals
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenatorProxy->registerSignalHandler(interfaceName, "concatenated", &onConcatenated);
|
||||
concatenatorProxy->finishRegistration();
|
||||
|
||||
std::vector<int> numbers = {1, 2, 3};
|
||||
std::string separator = ":";
|
||||
|
||||
// Invoke concatenate on given interface of the object
|
||||
{
|
||||
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
|
||||
method << numbers << separator;
|
||||
auto reply = concatenatorProxy->callMethod(method);
|
||||
std::string result;
|
||||
reply >> result;
|
||||
assert(result == "1:2:3");
|
||||
}
|
||||
|
||||
// Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
{
|
||||
auto method = concatenatorProxy->createMethodCall(interfaceName, "concatenate");
|
||||
method << std::vector<int>() << separator;
|
||||
try
|
||||
{
|
||||
auto reply = concatenatorProxy->callMethod(method);
|
||||
assert(false);
|
||||
}
|
||||
catch(const sdbus::Error& e)
|
||||
{
|
||||
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
in the context of a thread different from the main thread.
|
||||
|
||||
Implementing the Concatenator example using convenience sdbus-c++ API layer
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
One of the major sdbus-c++ design goals is to make the sdbus-c++ API easy to use correctly, and hard to use incorrectly.
|
||||
|
||||
The convenience API layer abstracts the concept of underlying D-Bus messages away completely. It abstracts away D-Bus signatures. And it tries
|
||||
to provide an interface that uses small, focused functions, with one or zero parameters, to form a chained function statement that reads like
|
||||
a sentence to a human reading the code. To achieve that, sdbus-c++ utilizes the power of the C++ type system, so many issues are resolved at
|
||||
compile time, and the run-time performance cost compared to the basic layer is close to zero.
|
||||
|
||||
Thus, in the end of the day, the code written using the convenience API is:
|
||||
- more expressive,
|
||||
- closer to the abstraction level of the problem being solved,
|
||||
- shorter,
|
||||
- almost as fast (if not equally fast) as one written using the basic API layer.
|
||||
|
||||
Rather than *how*, the code written using this layer expresses *what* it does. Let's look at code samples to see if you agree :)
|
||||
|
||||
### Server side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
// Yeah, global variable is ugly, but this is just an example and we want to access
|
||||
// the concatenator instance from within the concatenate method handler to be able
|
||||
// to emit signals.
|
||||
sdbus::IObject* g_concatenator{};
|
||||
|
||||
std::string concatenate(const std::vector<int> numbers, const std::string& separator)
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Emit 'concatenated' signal
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
g_concatenator->emitSignal("concatenated").onInterface(interfaceName).withArguments(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenator = sdbus::createObject(*connection, objectPath);
|
||||
|
||||
g_concatenator = concatenator.get();
|
||||
|
||||
// Register D-Bus methods and signals on the concatenator object, and exports the object.
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenator->registerMethod("concatenate").onInterface(interfaceName).implementedAs(&concatenate);
|
||||
concatenator->registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>();
|
||||
concatenator->finishRegistration();
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
}
|
||||
```
|
||||
|
||||
### Client side
|
||||
|
||||
```c++
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
void onConcatenated(const std::string& 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
|
||||
const char* destinationName = "org.sdbuscpp.concatenator";
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
auto concatenatorProxy = sdbus::createObjectProxy(destinationName, objectPath);
|
||||
|
||||
// Let's subscribe for the 'concatenated' signals
|
||||
const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
concatenatorProxy->uponSignal("concatenated").onInterface(interfaceName).call([this](const std::string& str){ onConcatenated(str); });
|
||||
concatenatorProxy->finishRegistration();
|
||||
|
||||
std::vector<int> numbers = {1, 2, 3};
|
||||
std::string separator = ":";
|
||||
|
||||
// Invoke concatenate on given interface of the object
|
||||
{
|
||||
std::string concatenatedString;
|
||||
concatenatorProxy->callMethod("concatenate").onInterface(interfaceName).withArguments(numbers, separator).storeResultsTo(concatenatedString);
|
||||
assert(concatenatedString == "1:2:3");
|
||||
}
|
||||
|
||||
// Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
{
|
||||
try
|
||||
{
|
||||
concatenatorProxy->callMethod("concatenate").onInterface(interfaceName).withArguments(std::vector<int>(), separator);
|
||||
assert(false);
|
||||
}
|
||||
catch(const sdbus::Error& e)
|
||||
{
|
||||
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Several lines of code have shrunk into one-liners when registering/calling methods or signals. D-Bus signatures and the serialization/deserialization
|
||||
of arguments from the messages is generated at compile time, by introspecting signatures of provided callbacks or deducing types of provided arguments.
|
||||
|
||||
Implementing the Concatenator example using sdbus-c++-generated stubs
|
||||
---------------------------------------------------------------------
|
||||
|
||||
sdbus-c++ ships with the native stub generator tool called sdbuscpp-xml2cpp. The tool is very similar to dbusxx-xml2cpp tool that comes from
|
||||
dbus-c++ project.
|
||||
|
||||
The generator tool takes D-Bus XML IDL description of D-Bus interfaces on its input, and can be instructed to generate one or both of these:
|
||||
an adaptor header file for use at server side, and a proxy header file for use at client side. Like this:
|
||||
|
||||
```bash
|
||||
sdbuscpp-xml2cpp database-bindings.xml --adaptor=database-server-glue.h --proxy=database-client-glue.h
|
||||
```
|
||||
|
||||
The adaptor header file contains classes that can be used to implement described interfaces. The proxy header file contains classes that can be used
|
||||
to make calls to remote objects.
|
||||
|
||||
### XML description of the Concatenator interface
|
||||
|
||||
As an example, let's look at an XML description of our Concatenator's interfaces.
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/concatenator">
|
||||
<interface name="org.sdbuscpp.Concatenator">
|
||||
<method name="concatenate">
|
||||
<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>
|
||||
```
|
||||
|
||||
After running this through the stubs generator, we get the stub code that is described in the following two subsections.
|
||||
|
||||
### concatenator-server-glue.h
|
||||
|
||||
There is specific class for each interface in the XML IDL file. The class is de facto an interface which shall be implemented by inheriting from it.
|
||||
The class' constructor takes care of registering all methods, signals and properties. For each D-Bus method there is a pure virtual member function.
|
||||
These pure virtual functions must be implemented in the child class. For each signal, there is a public function member that emits this signal.
|
||||
|
||||
```cpp
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__concatenator_server_glue_h__adaptor__H__
|
||||
#define __sdbuscpp__concatenator_server_glue_h__adaptor__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
|
||||
class Concatenator_adaptor
|
||||
{
|
||||
public:
|
||||
static constexpr const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
|
||||
protected:
|
||||
Concatenator_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.registerMethod("concatenate").onInterface(interfaceName).implementedAs([this](const std::vector<int32_t>& numbers, const std::string& separator){ return this->concatenate(numbers, separator); });
|
||||
object_.registerSignal("concatenated").onInterface(interfaceName).withParameters<std::string>();
|
||||
}
|
||||
|
||||
public:
|
||||
void concatenated(const std::string& concatenatedString)
|
||||
{
|
||||
object_.emitSignal("concatenated").onInterface(interfaceName).withArguments(concatenatedString);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) = 0;
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
### concatenator-client-glue.h
|
||||
|
||||
Analogously to the adaptor classes described above, there is specific class for each interface in the XML IDL file. The class is de facto a proxy
|
||||
to the concrete interface of a remote object. For each D-Bus signal there is a pure virtual member function whose body must be provided in a child
|
||||
class. For each method, there is a public function member that calls the method remotely.
|
||||
|
||||
```cpp
|
||||
/*
|
||||
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp__concatenator_client_glue_h__proxy__H__
|
||||
#define __sdbuscpp__concatenator_client_glue_h__proxy__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
namespace org {
|
||||
namespace sdbuscpp {
|
||||
|
||||
class Concatenator_proxy
|
||||
{
|
||||
public:
|
||||
static constexpr const char* interfaceName = "org.sdbuscpp.Concatenator";
|
||||
|
||||
protected:
|
||||
Concatenator_proxy(sdbus::IObjectProxy& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.uponSignal("concatenated").onInterface(interfaceName).call([this](const std::string& concatenatedString){ this->onConcatenated(concatenatedString); });
|
||||
}
|
||||
|
||||
virtual void onConcatenated(const std::string& concatenatedString) = 0;
|
||||
|
||||
public:
|
||||
std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator)
|
||||
{
|
||||
std::string result;
|
||||
object_.callMethod("concatenate").onInterface(interfaceName).withArguments(numbers, separator).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObjectProxy& object_;
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
### Providing server implementation based on generated adaptors
|
||||
|
||||
To implement a D-Bus object that implements all its D-Bus interfaces, we shall create a class representing the object that inherits from all
|
||||
corresponding `*_adaptor` classes and implements all pure virtual member functions. Specifically, the object class shall inherit from the
|
||||
`Interfaces` template class, the template arguments of which are individual adaptor classes. The `Interfaces` is just a convenience class that
|
||||
hides a few boiler-plate details. For example, in its constructor, it creates an `Object` instance, it takes care of proper initialization of
|
||||
all adaptor superclasses, and exports the object finally.
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include "concatenator-server-glue.h"
|
||||
|
||||
class Concatenator : public sdbus::Interfaces<org::sdbuscpp::Concatenator_adaptor /*, more adaptor classes if there are more interfaces*/>
|
||||
{
|
||||
public:
|
||||
Concatenator(sdbus::IConnection& connection, std::string objectPath)
|
||||
: sdbus::Interfaces<org::sdbuscpp::Concatenator_adaptor>(connection, std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string concatenate(const std::vector<int32_t>& numbers, const std::string& separator) override
|
||||
{
|
||||
// Return error if there are no numbers in the collection
|
||||
if (numbers.empty())
|
||||
throw sdbus::Error("org.sdbuscpp.Concatenator.Error", "No numbers provided");
|
||||
|
||||
// Concatenate the numbers
|
||||
std::string result;
|
||||
for (auto number : numbers)
|
||||
{
|
||||
result += (result.empty() ? std::string() : separator) + std::to_string(number);
|
||||
}
|
||||
|
||||
// Emit the 'concatenated' signal with the resulting string
|
||||
concatenated(result);
|
||||
|
||||
// Return the resulting string
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
That's it. We now have an implementation of a D-Bus object implementing `org.sdbuscpp.Concatenator` interface. Let's now create a service
|
||||
publishing the object.
|
||||
|
||||
```cpp
|
||||
#include "Concatenator.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create D-Bus connection and requests name on it.
|
||||
const char* serviceName = "org.sdbuscpp.concatenator";
|
||||
auto connection = sdbus::createConnection(serviceName);
|
||||
|
||||
// Create concatenator D-Bus object.
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
Concatenator concatenator(*connection, objectPath);
|
||||
|
||||
// Run the loop on the connection.
|
||||
connection->enterProcessingLoop();
|
||||
}
|
||||
```
|
||||
|
||||
It's that simple!
|
||||
|
||||
### Providing client implementation based on generated proxies
|
||||
|
||||
To implement a proxy for a remote D-Bus object, we shall create a class representing the object proxy that inherits from all corresponding
|
||||
`*_proxy` classes and -- if applicable -- implements all pure virtual member functions. Specifically, the object proxy class shall inherit
|
||||
from the `ProxyInterfaces` template class. As its template arguments we shall provide all proxy classes. The `ProxyInterfaces` is just a
|
||||
convenience class that hides a few boiler-plate details. For example, in its constructor, it creates an `ObjectProxy` instance, and it takes
|
||||
care of proper initialization of all proxy superclasses.
|
||||
|
||||
```cpp
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include "concatenator-client-glue.h"
|
||||
|
||||
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::Concatenator_proxy /*, more proxy classes if there are more interfaces*/>
|
||||
{
|
||||
public:
|
||||
ConcatenatorProxy(std::string destination, std::string objectPath)
|
||||
: sdbus::ProxyInterfaces<org::sdbuscpp::Concatenator_proxy>(std::move(destination), std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void onConcatenated(const std::string& concatenatedString) override
|
||||
{
|
||||
std::cout << "Received signal with concatenated string " << concatenatedString << std::endl;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Now let's use this proxy to make remote calls and listen to signals in a real application.
|
||||
|
||||
```cpp
|
||||
#include "ConcatenatorProxy.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Create proxy object for the concatenator object on the server side
|
||||
const char* destinationName = "org.sdbuscpp.concatenator";
|
||||
const char* objectPath = "/org/sdbuscpp/concatenator";
|
||||
ConcatenatorProxy concatenatorProxy(destinationName, objectPath);
|
||||
|
||||
std::vector<int> numbers = {1, 2, 3};
|
||||
std::string separator = ":";
|
||||
|
||||
// Invoke concatenate with some numbers
|
||||
auto concatenatedString = concatenatorProxy.concatenate(numbers, separator);
|
||||
assert(concatenatedString == "1:2:3");
|
||||
|
||||
// Invoke concatenate again, this time with no numbers and we shall get an error
|
||||
try
|
||||
{
|
||||
auto concatenatedString = concatenatorProxy.concatenate(std::vector<int>(), separator);
|
||||
assert(false);
|
||||
}
|
||||
catch(const sdbus::Error& e)
|
||||
{
|
||||
std::cerr << "Got concatenate error " << e.getName() << " with message " << e.getMessage() << std::endl;
|
||||
}
|
||||
|
||||
// Give sufficient time to receive 'concatenated' signal from the first concatenate invocation
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Using D-Bus properties
|
||||
----------------------
|
||||
|
||||
Defining and working with D-Bus properties using XML description is quite easy.
|
||||
|
||||
### Defining a property in the XML
|
||||
|
||||
A property element has no arg child element. It just has the attributes name, type and access, which are all mandatory. The access attribute allows the values ‘readwrite’, ‘read’, and ‘write’.
|
||||
|
||||
An example of a read-write property `status`:
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<node name="/org/sdbuscpp/propertyprovider">
|
||||
<interface name="org.sdbuscpp.PropertyProvider">
|
||||
<!--...-->
|
||||
<property name="status" type="u" access="readwrite"/>
|
||||
<!--...-->
|
||||
</interface>
|
||||
</node>
|
||||
```
|
||||
|
||||
### Generated stubs
|
||||
|
||||
This is how generated adaptor and proxy classes would look like with the read-write `status` property. The adaptor:
|
||||
|
||||
```cpp
|
||||
class PropertyProvider_adaptor
|
||||
{
|
||||
/*...*/
|
||||
|
||||
public:
|
||||
PropertyProvider_adaptor(sdbus::IObject& object)
|
||||
: object_(object)
|
||||
{
|
||||
object_.registerProperty("status").onInterface(INTERFACE_NAME).withGetter([this](){ return this->status(); }).withSetter([this](const uint32_t& value){ this->status(value); });
|
||||
}
|
||||
|
||||
private:
|
||||
// property getter
|
||||
virtual uint32_t status() = 0;
|
||||
// property setter
|
||||
virtual void status(const uint32_t& value) = 0;
|
||||
|
||||
/*...*/
|
||||
};
|
||||
#endif
|
||||
```
|
||||
|
||||
The proxy:
|
||||
|
||||
```cpp
|
||||
class PropertyProvider_proxy
|
||||
{
|
||||
/*...*/
|
||||
|
||||
public:
|
||||
// getting the property value
|
||||
uint32_t status()
|
||||
{
|
||||
return object_.getProperty("status").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
// setting the property value
|
||||
void status(const uint32_t& value)
|
||||
{
|
||||
object_.setProperty("status").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
/*...*/
|
||||
};
|
||||
```
|
||||
|
||||
When implementing the adaptor, we simply need to provide the body for `status` getter and setter method by overriding them. Then in the proxy, we just call them.
|
||||
|
||||
Conclusion
|
||||
----------
|
||||
|
||||
There is no conclusion. Happy journeys by D-Bus with sdbus-c++!
|
23
include/Makefile.am
Normal file
23
include/Makefile.am
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
# Distribution
|
||||
# Headers will be installed to $(includedir)/sdbus-c++ directory
|
||||
|
||||
HEADER_DIR = sdbus-c++
|
||||
libsdbuscppdir = $(includedir)/$(HEADER_DIR)
|
||||
libsdbuscpp_HEADERS = \
|
||||
$(HEADER_DIR)/ConvenienceClasses.h \
|
||||
$(HEADER_DIR)/ConvenienceClasses.inl \
|
||||
$(HEADER_DIR)/Error.h \
|
||||
$(HEADER_DIR)/IConnection.h \
|
||||
$(HEADER_DIR)/Interfaces.h \
|
||||
$(HEADER_DIR)/Introspection.h \
|
||||
$(HEADER_DIR)/IObject.h \
|
||||
$(HEADER_DIR)/IObjectProxy.h \
|
||||
$(HEADER_DIR)/Message.h \
|
||||
$(HEADER_DIR)/Types.h \
|
||||
$(HEADER_DIR)/TypeTraits.h \
|
||||
$(HEADER_DIR)/sdbus-c++.h
|
||||
|
||||
EXTRA_DIST = ($libsdbuscpp_HEADERS)
|
||||
|
||||
DISTCLEANFILES = Makefile Makefile.in
|
169
include/sdbus-c++/ConvenienceClasses.h
Normal file
169
include/sdbus-c++/ConvenienceClasses.h
Normal file
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ConvenienceClasses.h
|
||||
*
|
||||
* Created on: Jan 19, 2017
|
||||
* 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_CONVENIENCECLASSES_H_
|
||||
#define SDBUS_CXX_CONVENIENCECLASSES_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <string>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IObject;
|
||||
class IObjectProxy;
|
||||
class Variant;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
class MethodRegistrator
|
||||
{
|
||||
public:
|
||||
MethodRegistrator(IObject& object, const std::string& methodName);
|
||||
MethodRegistrator& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function> void implementedAs(_Function&& callback);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& methodName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
class SignalRegistrator
|
||||
{
|
||||
public:
|
||||
SignalRegistrator(IObject& object, const std::string& signalName);
|
||||
SignalRegistrator(SignalRegistrator&& other) = default;
|
||||
SignalRegistrator& operator=(SignalRegistrator&& other) = default;
|
||||
~SignalRegistrator() noexcept(false);
|
||||
SignalRegistrator& onInterface(std::string interfaceName);
|
||||
template <typename... _Args> void withParameters();
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& signalName_;
|
||||
std::string interfaceName_;
|
||||
std::string signalSignature_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalRegistrator is constructed
|
||||
};
|
||||
|
||||
class PropertyRegistrator
|
||||
{
|
||||
public:
|
||||
PropertyRegistrator(IObject& object, const std::string& propertyName);
|
||||
PropertyRegistrator(PropertyRegistrator&& other) = default;
|
||||
PropertyRegistrator& operator=(PropertyRegistrator&& other) = default;
|
||||
~PropertyRegistrator() noexcept(false);
|
||||
PropertyRegistrator& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function> PropertyRegistrator& withGetter(_Function&& callback);
|
||||
template <typename _Function> PropertyRegistrator& withSetter(_Function&& callback);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& propertyName_;
|
||||
std::string interfaceName_;
|
||||
std::string propertySignature_;
|
||||
property_get_callback getter_;
|
||||
property_set_callback setter_;
|
||||
int exceptions_{}; // Number of active exceptions when PropertyRegistrator is constructed
|
||||
};
|
||||
|
||||
class SignalEmitter
|
||||
{
|
||||
public:
|
||||
SignalEmitter(IObject& object, const std::string& signalName);
|
||||
SignalEmitter(SignalEmitter&& other) = default;
|
||||
SignalEmitter& operator=(SignalEmitter&& other) = default;
|
||||
~SignalEmitter() noexcept(false);
|
||||
SignalEmitter& onInterface(const std::string& interfaceName);
|
||||
template <typename... _Args> void withArguments(_Args&&... args);
|
||||
|
||||
private:
|
||||
IObject& object_;
|
||||
const std::string& signalName_;
|
||||
Message signal_;
|
||||
int exceptions_{}; // Number of active exceptions when SignalEmitter is constructed
|
||||
};
|
||||
|
||||
class MethodInvoker
|
||||
{
|
||||
public:
|
||||
MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName);
|
||||
MethodInvoker(MethodInvoker&& other) = default;
|
||||
MethodInvoker& operator=(MethodInvoker&& other) = default;
|
||||
~MethodInvoker() noexcept(false);
|
||||
|
||||
MethodInvoker& onInterface(const std::string& interfaceName);
|
||||
template <typename... _Args> MethodInvoker& withArguments(_Args&&... args);
|
||||
template <typename... _Args> void storeResultsTo(_Args&... args);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
const std::string& methodName_;
|
||||
Message method_;
|
||||
int exceptions_{}; // Number of active exceptions when MethodInvoker is constructed
|
||||
bool methodCalled_{};
|
||||
};
|
||||
|
||||
class SignalSubscriber
|
||||
{
|
||||
public:
|
||||
SignalSubscriber(IObjectProxy& objectProxy, const std::string& signalName);
|
||||
SignalSubscriber& onInterface(const std::string& interfaceName);
|
||||
template <typename _Function> void call(_Function&& callback);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
std::string signalName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
class PropertyGetter
|
||||
{
|
||||
public:
|
||||
PropertyGetter(IObjectProxy& objectProxy, const std::string& propertyName);
|
||||
sdbus::Variant onInterface(const std::string& interfaceName);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
std::string propertyName_;
|
||||
};
|
||||
|
||||
class PropertySetter
|
||||
{
|
||||
public:
|
||||
PropertySetter(IObjectProxy& objectProxy, const std::string& propertyName);
|
||||
PropertySetter& onInterface(const std::string& interfaceName);
|
||||
template <typename _Value> void toValue(const _Value& value);
|
||||
|
||||
private:
|
||||
IObjectProxy& objectProxy_;
|
||||
const std::string& propertyName_;
|
||||
std::string interfaceName_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_CONVENIENCECLASSES_H_ */
|
401
include/sdbus-c++/ConvenienceClasses.inl
Normal file
401
include/sdbus-c++/ConvenienceClasses.inl
Normal file
@ -0,0 +1,401 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ConvenienceClasses.inl
|
||||
*
|
||||
* Created on: Dec 19, 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_CPP_CONVENIENCECLASSES_INL_
|
||||
#define SDBUS_CPP_CONVENIENCECLASSES_INL_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
/*#include <exception>*/
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
inline MethodRegistrator::MethodRegistrator(IObject& object, const std::string& methodName)
|
||||
: object_(object)
|
||||
, methodName_(methodName)
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodRegistrator& MethodRegistrator::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline void 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()
|
||||
, [callback = std::forward<_Function>(callback)](Message& msg, Message& reply)
|
||||
{
|
||||
// Create a tuple of callback input arguments' types, which will be used
|
||||
// as a storage for the argument values deserialized from the message.
|
||||
tuple_of_function_input_arg_types_t<_Function> inputArgs;
|
||||
|
||||
// Deserialize input arguments from the message into the tuple
|
||||
msg >> inputArgs;
|
||||
|
||||
// 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 :-(
|
||||
|
||||
// The return value is stored to the reply message.
|
||||
// In case of void functions, ret is an empty tuple and thus nothing is stored.
|
||||
reply << ret;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline SignalRegistrator::SignalRegistrator(IObject& object, std::string signalName)
|
||||
: object_(object)
|
||||
, signalName_(std::move(signalName))
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (interfaceName_.empty())
|
||||
throw sdbus::Exception("DBus interface not specified when registering a DBus signal");
|
||||
|
||||
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerSignal(interfaceName_, signalName_, signalSignature_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline SignalRegistrator& SignalRegistrator::onInterface(std::string interfaceName)
|
||||
{
|
||||
interfaceName_ = std::move(interfaceName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void SignalRegistrator::withParameters()
|
||||
{
|
||||
signalSignature_ = signature_of_function_input_arguments<void(_Args...)>::str();
|
||||
}
|
||||
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline PropertyRegistrator::PropertyRegistrator(IObject& object, std::string propertyName)
|
||||
: object_(object)
|
||||
, propertyName_(std::move(propertyName))
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus property", EINVAL);
|
||||
|
||||
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerProperty() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerProperty( std::move(interfaceName_)
|
||||
, std::move(propertyName_)
|
||||
, std::move(propertySignature_)
|
||||
, std::move(getter_)
|
||||
, std::move(setter_) );
|
||||
}
|
||||
*/
|
||||
|
||||
inline PropertyRegistrator& PropertyRegistrator::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline PropertyRegistrator& PropertyRegistrator::withGetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_traits<_Function>::arity == 0, "Property getter function must not take any arguments");
|
||||
static_assert(!std::is_void<function_result_t<_Function>>::value, "Property getter function must return property value");
|
||||
|
||||
if (propertySignature_.empty())
|
||||
propertySignature_ = signature_of_function_output_arguments<_Function>::str();
|
||||
|
||||
getter_ = [callback = std::forward<_Function>(callback)](Message& msg)
|
||||
{
|
||||
// Get the propety value and serialize it into the message
|
||||
msg << callback();
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline PropertyRegistrator& PropertyRegistrator::withSetter(_Function&& callback)
|
||||
{
|
||||
static_assert(function_traits<_Function>::arity == 1, "Property setter function must take one parameter - the property value");
|
||||
static_assert(std::is_void<function_result_t<_Function>>::value, "Property setter function must not return any value");
|
||||
|
||||
if (propertySignature_.empty())
|
||||
propertySignature_ = signature_of_function_input_arguments<_Function>::str();
|
||||
|
||||
setter_ = [callback = std::forward<_Function>(callback)](Message& msg)
|
||||
{
|
||||
// Default-construct property value
|
||||
using property_type = function_argument_t<_Function, 0>;
|
||||
std::decay_t<property_type> property;
|
||||
|
||||
// Deserialize property value from the message
|
||||
msg >> property;
|
||||
|
||||
// Invoke setter with the value
|
||||
callback(property);
|
||||
};
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't emit the signal if SignalEmitter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (!signal_.isValid())
|
||||
throw sdbus::Exception("DBus interface not specified when emitting a DBus signal");
|
||||
|
||||
// emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow emitSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.emitSignal(signal_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline SignalEmitter& SignalEmitter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
signal_ = object_.createSignal(interfaceName, signalName_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void SignalEmitter::withArguments(_Args&&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!signal_.isValid(), "DBus interface not specified when emitting a DBus signal", EINVAL);
|
||||
|
||||
detail::serialize_pack(signal_, std::forward<_Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
// Moved into the library to isolate from C++17 dependency
|
||||
/*
|
||||
inline MethodInvoker::MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName)
|
||||
: objectProxy_(objectProxy)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
inline MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't call the method if it has been called already or if MethodInvoker
|
||||
// threw an exception in one of its methods
|
||||
if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
if (!method_.isValid())
|
||||
throw sdbus::Exception("DBus interface not specified when calling a DBus method");
|
||||
|
||||
// callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow callMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
objectProxy_.callMethod(method_);
|
||||
}
|
||||
*/
|
||||
|
||||
inline MethodInvoker& MethodInvoker::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
method_ = objectProxy_.createMethodCall(interfaceName, methodName_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline MethodInvoker& MethodInvoker::withArguments(_Args&&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
|
||||
detail::serialize_pack(method_, std::forward<_Args>(args)...);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename... _Args>
|
||||
inline void MethodInvoker::storeResultsTo(_Args&... args)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
|
||||
auto reply = objectProxy_.callMethod(method_);
|
||||
methodCalled_ = true;
|
||||
|
||||
detail::deserialize_pack(reply, args...);
|
||||
}
|
||||
|
||||
|
||||
inline SignalSubscriber::SignalSubscriber(IObjectProxy& objectProxy, const std::string& signalName)
|
||||
: objectProxy_(objectProxy)
|
||||
, signalName_(signalName)
|
||||
{
|
||||
}
|
||||
|
||||
inline SignalSubscriber& SignalSubscriber::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Function>
|
||||
inline void SignalSubscriber::call(_Function&& callback)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when subscribing to a signal", EINVAL);
|
||||
|
||||
objectProxy_.registerSignalHandler( interfaceName_
|
||||
, signalName_
|
||||
, [callback = std::forward<_Function>(callback)](Message& 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.
|
||||
tuple_of_function_input_arg_types_t<_Function> signalArgs;
|
||||
|
||||
// Deserialize input arguments from the signal message into the tuple
|
||||
signal >> signalArgs;
|
||||
|
||||
// Invoke callback with input arguments from the tuple.
|
||||
apply(callback, signalArgs); // We don't yet have C++17's std::apply :-(
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
inline PropertyGetter::PropertyGetter(IObjectProxy& objectProxy, const std::string& propertyName)
|
||||
: objectProxy_(objectProxy)
|
||||
, propertyName_(propertyName)
|
||||
{
|
||||
}
|
||||
|
||||
inline sdbus::Variant PropertyGetter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
sdbus::Variant var;
|
||||
objectProxy_
|
||||
.callMethod("Get")
|
||||
.onInterface("org.freedesktop.DBus.Properties")
|
||||
.withArguments(interfaceName, propertyName_)
|
||||
.storeResultsTo(var);
|
||||
return var;
|
||||
}
|
||||
|
||||
|
||||
inline PropertySetter::PropertySetter(IObjectProxy& objectProxy, const std::string& propertyName)
|
||||
: objectProxy_(objectProxy)
|
||||
, propertyName_(propertyName)
|
||||
{
|
||||
}
|
||||
|
||||
inline PropertySetter& PropertySetter::onInterface(const std::string& interfaceName)
|
||||
{
|
||||
interfaceName_ = interfaceName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename _Value>
|
||||
inline void PropertySetter::toValue(const _Value& value)
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when setting a property", EINVAL);
|
||||
|
||||
objectProxy_
|
||||
.callMethod("Set")
|
||||
.onInterface("org.freedesktop.DBus.Properties")
|
||||
.withArguments(interfaceName_, propertyName_, sdbus::Variant{value});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CPP_CONVENIENCECLASSES_INL_ */
|
76
include/sdbus-c++/Error.h
Executable file
76
include/sdbus-c++/Error.h
Executable file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* (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_ERROR_H_
|
||||
#define SDBUS_CXX_ERROR_H_
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class Error
|
||||
*
|
||||
* Represents a common sdbus-c++ exception.
|
||||
*
|
||||
***********************************************/
|
||||
class Error
|
||||
: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
Error(const std::string& name, const std::string& message)
|
||||
: std::runtime_error("[" + name + "] " + message)
|
||||
, name_(name)
|
||||
, message_(message)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string& getName() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
const std::string& getMessage() const
|
||||
{
|
||||
return message_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::string message_;
|
||||
};
|
||||
|
||||
sdbus::Error createError(int errNo, const std::string& customMsg);
|
||||
}
|
||||
|
||||
#define SDBUS_THROW_ERROR(_MSG, _ERRNO) \
|
||||
throw sdbus::createError((_ERRNO), (_MSG)) \
|
||||
/**/
|
||||
|
||||
#define SDBUS_THROW_ERROR_IF(_COND, _MSG, _ERRNO) \
|
||||
if (_COND) SDBUS_THROW_ERROR((_MSG), (_ERRNO)) \
|
||||
/**/
|
||||
|
||||
#endif /* SDBUS_CXX_ERROR_H_ */
|
157
include/sdbus-c++/IConnection.h
Normal file
157
include/sdbus-c++/IConnection.h
Normal file
@ -0,0 +1,157 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file IConnection.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_ICONNECTION_H_
|
||||
#define SDBUS_CXX_ICONNECTION_H_
|
||||
|
||||
//#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class IConnection
|
||||
*
|
||||
* An interface to D-Bus bus connection. Incorporates implementation
|
||||
* of both synchronous and asynchronous processing loop.
|
||||
*
|
||||
* All methods throw @sdbus::Error in case of failure. The class is
|
||||
* thread-aware, but not thread-safe.
|
||||
*
|
||||
***********************************************/
|
||||
class IConnection
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Requests D-Bus name on the connection
|
||||
*
|
||||
* @param[in] name Name to request
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void requestName(const std::string& name) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Releases D-Bus name on the connection
|
||||
*
|
||||
* @param[in] name Name to release
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void releaseName(const std::string& name) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Enters the D-Bus processing loop
|
||||
*
|
||||
* The incoming D-Bus messages are processed in the loop. The method
|
||||
* blocks indefinitely, until unblocked via @leaveProcessingLoop.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void enterProcessingLoop() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Enters the D-Bus processing loop in a separate thread
|
||||
*
|
||||
* The same as @enterProcessingLoop, except that it doesn't block
|
||||
* because it runs the loop in a separate thread managed internally.
|
||||
*/
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Leaves the D-Bus processing loop
|
||||
*
|
||||
* Ends the previously started processing loop.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
|
||||
inline virtual ~IConnection() = 0;
|
||||
};
|
||||
|
||||
IConnection::~IConnection() {}
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection with a name
|
||||
*
|
||||
* @param[in] name Name to request on the connection after its opening
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus system connection with a name
|
||||
*
|
||||
* @param[in] name Name to request on the connection after its opening
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name);
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session connection
|
||||
*
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection();
|
||||
|
||||
/*!
|
||||
* @brief Creates/opens D-Bus session connection with a name
|
||||
*
|
||||
* @param[in] name Name to request on the connection after its opening
|
||||
* @return Connection instance
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name);
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_ICONNECTION_H_ */
|
279
include/sdbus-c++/IObject.h
Executable file
279
include/sdbus-c++/IObject.h
Executable file
@ -0,0 +1,279 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file IObject.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_IOBJECT_H_
|
||||
#define SDBUS_CXX_IOBJECT_H_
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class IObject
|
||||
*
|
||||
* An interface to D-Bus object. Provides API for registration
|
||||
* of methods, signals, properties, and for emitting signals.
|
||||
*
|
||||
* All methods throw @c sdbus::Error in case of failure. The class is
|
||||
* thread-aware, but not thread-safe.
|
||||
*
|
||||
***********************************************/
|
||||
class IObject
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @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] methodCallback Callback that implements the body of the method
|
||||
*
|
||||
* @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
|
||||
, method_callback methodCallback ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will emit on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal will fall under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signature D-Bus signature of signal parameters
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers read-only property that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the property will fall under
|
||||
* @param[in] propertyName Name of the property
|
||||
* @param[in] signature D-Bus signature of property parameters
|
||||
* @param[in] getCallback Callback that implements the body of the property getter
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers read/write property that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the property will fall under
|
||||
* @param[in] propertyName Name of the property
|
||||
* @param[in] signature D-Bus signature of property parameters
|
||||
* @param[in] getCallback Callback that implements the body of the property getter
|
||||
* @param[in] setCallback Callback that implements the body of the property setter
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Finishes the registration and exports object API on D-Bus
|
||||
*
|
||||
* The method exports all up to now registered methods, signals and properties on D-Bus.
|
||||
* Must be called only once, after all methods, signals and properties have been registered.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void finishRegistration() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Creates a signal message
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs under
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A signal message
|
||||
*
|
||||
* Serialize signal arguments into the returned message and emit the signal by passing
|
||||
* the message with serialized arguments to the @c emitSignal function.
|
||||
* Alternatively, use higher-level API @c emitSignal(const std::string& signalName) defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual Message createSignal(const std::string& interfaceName, const std::string& signalName) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Emits signal on D-Bus
|
||||
*
|
||||
* @param[in] message Signal message to be sent out
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void emitSignal(const sdbus::Message& message) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers method that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A helper object for convenient registration of the method
|
||||
*
|
||||
* This is a high-level, convenience way of registering D-Bus methods that abstracts
|
||||
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the parameters and return type
|
||||
* of the provided native method implementation callback.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object.registerMethod("doFoo").onInterface("com.kistler.foo").implementedAs([this](int value){ return this->doFoo(value); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
MethodRegistrator registerMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient registration of the signal
|
||||
*
|
||||
* This is a high-level, convenience way of registering D-Bus signals that abstracts
|
||||
* from the D-Bus message concept. Signal arguments are automatically (de)serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native parameters.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object.registerSignal("paramChange").onInterface("com.kistler.foo").withParameters<std::map<int32_t, std::string>>();
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
SignalRegistrator registerSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Registers property that the object will provide on D-Bus
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient registration of the property
|
||||
*
|
||||
* This is a high-level, convenience way of registering D-Bus properties that abstracts
|
||||
* from the D-Bus message concept. Property arguments are automatically (de)serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native callbacks.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.registerProperty("state").onInterface("com.kistler.foo").withGetter([this](){ return this->state(); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertyRegistrator registerProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Emits signal on D-Bus
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient emission of signals
|
||||
*
|
||||
* This is a high-level, convenience way of emitting D-Bus signals that abstracts
|
||||
* from the D-Bus message concept. Signal arguments are automatically serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native arguments.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int arg1 = ...;
|
||||
* double arg2 = ...;
|
||||
* object_.emitSignal("fooSignal").onInterface("com.kistler.foo").withArguments(arg1, arg2);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
SignalEmitter emitSignal(const std::string& signalName);
|
||||
|
||||
virtual ~IObject() = 0;
|
||||
};
|
||||
|
||||
inline MethodRegistrator IObject::registerMethod(const std::string& methodName)
|
||||
{
|
||||
return MethodRegistrator(*this, methodName);
|
||||
}
|
||||
|
||||
inline SignalRegistrator IObject::registerSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalRegistrator(*this, std::move(signalName));
|
||||
}
|
||||
|
||||
inline PropertyRegistrator IObject::registerProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertyRegistrator(*this, std::move(propertyName));
|
||||
}
|
||||
|
||||
inline SignalEmitter IObject::emitSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalEmitter(*this, signalName);
|
||||
}
|
||||
|
||||
inline IObject::~IObject() {}
|
||||
|
||||
/*!
|
||||
* @brief Creates instance representing a D-Bus object
|
||||
*
|
||||
* @param[in] connection D-Bus connection to be used by the object
|
||||
* @param[in] objectPath Path of the D-Bus object
|
||||
* @return Pointer to the object representation instance
|
||||
*
|
||||
* The provided connection will be used by the object to export methods,
|
||||
* issue signals and provide properties.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObject(connection, "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath);
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IOBJECT_H_ */
|
249
include/sdbus-c++/IObjectProxy.h
Executable file
249
include/sdbus-c++/IObjectProxy.h
Executable file
@ -0,0 +1,249 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file IObjectProxy.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_IOBJECTPROXY_H_
|
||||
#define SDBUS_CXX_IOBJECTPROXY_H_
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class IObjectProxy
|
||||
*
|
||||
* An interface to D-Bus object proxy. Provides API for calling
|
||||
* methods, getting/setting properties, and for registering to signals.
|
||||
*
|
||||
* All methods throw @c sdbus::Error in case of failure. The class is
|
||||
* thread-aware, but not thread-safe.
|
||||
*
|
||||
***********************************************/
|
||||
class IObjectProxy
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* @brief Creates a method call message
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the method is defined under
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A method call message message
|
||||
*
|
||||
* Serialize method arguments into the returned message and invoke the method by passing
|
||||
* the message with serialized arguments to the @c callMethod function.
|
||||
* Alternatively, use higher-level API @c callMethod(const std::string& methodName) defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual Message createMethodCall(const std::string& interfaceName, const std::string& methodName) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object
|
||||
*
|
||||
* @param[in] message Message representing a method call
|
||||
*
|
||||
* Note: To avoid messing with messages, use higher-level API defined below.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual Message callMethod(const sdbus::Message& message) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Registers a handler for the desired signal emitted by the proxied D-Bus object
|
||||
*
|
||||
* @param[in] interfaceName Name of an interface that the signal belongs to
|
||||
* @param[in] signalName Name of the signal
|
||||
* @param[in] signalHandler Callback that implements the body of the signal handler
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler ) = 0;
|
||||
|
||||
/*!
|
||||
* @brief Finishes the registration of signal handlers
|
||||
*
|
||||
* The method physically subscribes to the desired signals.
|
||||
* Must be called only once, after all signals have been registered already.
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
virtual void finishRegistration() = 0;
|
||||
|
||||
/*!
|
||||
* @brief Calls method on the proxied D-Bus object
|
||||
*
|
||||
* @param[in] methodName Name of the method
|
||||
* @return A helper object for convenient invocation of the method
|
||||
*
|
||||
* This is a high-level, convenience way of calling D-Bus methods that abstracts
|
||||
* from the D-Bus message concept. Method arguments/return value are automatically (de)serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the provided native arguments
|
||||
* and return values.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int result, a = ..., b = ...;
|
||||
* object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
MethodInvoker callMethod(const std::string& methodName);
|
||||
|
||||
/*!
|
||||
* @brief Registers signal handler for a given signal of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] signalName Name of the signal
|
||||
* @return A helper object for convenient registration of the signal handler
|
||||
*
|
||||
* This is a high-level, convenience way of registering to D-Bus signals that abstracts
|
||||
* from the D-Bus message concept. Signal arguments are automatically serialized
|
||||
* in a message and D-Bus signatures automatically deduced from the parameters
|
||||
* of the provided native signal callback.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* object_.uponSignal("fooSignal").onInterface("com.kistler.foo").call([this](int arg1, double arg2){ this->onFooSignal(arg1, arg2); });
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
SignalSubscriber uponSignal(const std::string& signalName);
|
||||
|
||||
/*!
|
||||
* @brief Gets value of a property of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient getting of property value
|
||||
*
|
||||
* This is a high-level, convenience way of reading D-Bus property values that abstracts
|
||||
* from the D-Bus message concept. sdbus::Variant is returned which shall then be converted
|
||||
* to the real property type (implicit conversion is supported).
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int state = object.getProperty("state").onInterface("com.kistler.foo");
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertyGetter getProperty(const std::string& propertyName);
|
||||
|
||||
/*!
|
||||
* @brief Sets value of a property of the proxied D-Bus object
|
||||
*
|
||||
* @param[in] propertyName Name of the property
|
||||
* @return A helper object for convenient setting of property value
|
||||
*
|
||||
* This is a high-level, convenience way of writing D-Bus property values that abstracts
|
||||
* from the D-Bus message concept.
|
||||
*
|
||||
* Example of use:
|
||||
* @code
|
||||
* int state = ...;
|
||||
* object_.setProperty("state").onInterface("com.kistler.foo").toValue(state);
|
||||
* @endcode
|
||||
*
|
||||
* @throws sdbus::Error in case of failure
|
||||
*/
|
||||
PropertySetter setProperty(const std::string& propertyName);
|
||||
|
||||
virtual ~IObjectProxy() = 0;
|
||||
};
|
||||
|
||||
inline MethodInvoker IObjectProxy::callMethod(const std::string& methodName)
|
||||
{
|
||||
return MethodInvoker(*this, methodName);
|
||||
}
|
||||
|
||||
inline SignalSubscriber IObjectProxy::uponSignal(const std::string& signalName)
|
||||
{
|
||||
return SignalSubscriber(*this, signalName);
|
||||
}
|
||||
|
||||
inline PropertyGetter IObjectProxy::getProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertyGetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline PropertySetter IObjectProxy::setProperty(const std::string& propertyName)
|
||||
{
|
||||
return PropertySetter(*this, propertyName);
|
||||
}
|
||||
|
||||
inline IObjectProxy::~IObjectProxy() {}
|
||||
|
||||
/*!
|
||||
* @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.
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObjectProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( sdbus::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
/*!
|
||||
* @brief Creates object proxy instance that uses its own D-Bus connection
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* This factory overload creates a proxy that manages its own D-Bus connection(s).
|
||||
*
|
||||
* Code example:
|
||||
* @code
|
||||
* auto proxy = sdbus::createObjectProxy(connection, "com.kistler.foo", "/com/kistler/foo");
|
||||
* @endcode
|
||||
*/
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::string destination
|
||||
, std::string objectPath );
|
||||
|
||||
}
|
||||
|
||||
#include <sdbus-c++/ConvenienceClasses.inl>
|
||||
|
||||
#endif /* SDBUS_CXX_IOBJECTPROXY_H_ */
|
120
include/sdbus-c++/Interfaces.h
Normal file
120
include/sdbus-c++/Interfaces.h
Normal file
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Interfaces.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_INTERFACES_H_
|
||||
#define SDBUS_CXX_INTERFACES_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class IConnection;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
template <typename _Object>
|
||||
class ObjectHolder
|
||||
{
|
||||
protected:
|
||||
ObjectHolder(std::unique_ptr<_Object>&& object)
|
||||
: object_(std::move(object))
|
||||
{
|
||||
}
|
||||
|
||||
const _Object& getObject() const
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
}
|
||||
|
||||
_Object& getObject()
|
||||
{
|
||||
assert(object_ != nullptr);
|
||||
return *object_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<_Object> object_;
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Interfaces
|
||||
*
|
||||
* A helper template class that a user class representing a D-Bus object
|
||||
* should inherit from, providing as template arguments the adaptor
|
||||
* classes representing D-Bus interfaces that the object implements.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class Interfaces
|
||||
: private ObjectHolder<IObject>
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
Interfaces(IConnection& connection, std::string objectPath)
|
||||
: ObjectHolder<IObject>(createObject(connection, std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
}
|
||||
};
|
||||
|
||||
/********************************************//**
|
||||
* @class Interfaces
|
||||
*
|
||||
* A helper template class that a user class representing a proxy of a
|
||||
* D-Bus object should inherit from, providing as template arguments the proxy
|
||||
* classes representing object D-Bus interfaces that the object implements.
|
||||
*
|
||||
***********************************************/
|
||||
template <typename... _Interfaces>
|
||||
class ProxyInterfaces
|
||||
: private ObjectHolder<IObjectProxy>
|
||||
, public _Interfaces...
|
||||
{
|
||||
public:
|
||||
ProxyInterfaces(std::string destination, std::string objectPath)
|
||||
: ObjectHolder<IObjectProxy>(createObjectProxy(std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
}
|
||||
|
||||
ProxyInterfaces(IConnection& connection, std::string destination, std::string objectPath)
|
||||
: ObjectHolder<IObjectProxy>(createObjectProxy(connection, std::move(destination), std::move(objectPath)))
|
||||
, _Interfaces(getObject())...
|
||||
{
|
||||
getObject().finishRegistration();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERFACES_H_ */
|
84
include/sdbus-c++/Introspection.h
Normal file
84
include/sdbus-c++/Introspection.h
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Introspection.h
|
||||
*
|
||||
* Created on: Dec 13, 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_INTROSPECTION_H_
|
||||
#define SDBUS_CXX_INTROSPECTION_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <string>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
// Proxy for introspection
|
||||
class introspectable_proxy
|
||||
{
|
||||
static constexpr const char* interfaceName = "org.freedesktop.DBus.Introspectable";
|
||||
|
||||
protected:
|
||||
introspectable_proxy(sdbus::IObjectProxy& object)
|
||||
: object_(object)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
std::string Introspect()
|
||||
{
|
||||
std::string xml;
|
||||
object_.callMethod("Introspect").onInterface(interfaceName).storeResultsTo(xml);
|
||||
return xml;
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObjectProxy& object_;
|
||||
};
|
||||
|
||||
// Adaptor is not necessary if we want to rely on sdbus-provided introspection
|
||||
// class introspectable_adaptor
|
||||
// {
|
||||
// static constexpr const char* interfaceName = "org.freedesktop.DBus.Introspectable";
|
||||
//
|
||||
// protected:
|
||||
// introspectable_adaptor(sdbus::IObject& object)
|
||||
// : object_(object)
|
||||
// {
|
||||
// object_.registerMethod("Introspect").onInterface(interfaceName).implementedAs([this](){ return object_.introspect(); });
|
||||
// }
|
||||
//
|
||||
// public:
|
||||
// std::string introspect()
|
||||
// {
|
||||
// std::string xml;
|
||||
// object_.callMethod("Introspect").onInterface(interfaceName).storeResultsTo(xml);
|
||||
// return xml;
|
||||
// }
|
||||
//
|
||||
// private:
|
||||
// sdbus::IObject& object_;
|
||||
// };
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTROSPECTION_H_ */
|
330
include/sdbus-c++/Message.h
Normal file
330
include/sdbus-c++/Message.h
Normal file
@ -0,0 +1,330 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Message.h
|
||||
*
|
||||
* Created on: Nov 9, 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_MESSAGE_H_
|
||||
#define SDBUS_CXX_MESSAGE_H_
|
||||
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Variant;
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class Message
|
||||
*
|
||||
* Message represents a D-Bus message, which can be either method call message,
|
||||
* method reply message, signal message, or a plain message serving as a storage
|
||||
* for serialized data.
|
||||
*
|
||||
* Serialization and deserialization functions are provided for types supported
|
||||
* by D-Bus.
|
||||
*
|
||||
* You don't need to work with this class directly if you use high-level APIs
|
||||
* of @c IObject and @c IObjectProxy.
|
||||
*
|
||||
***********************************************/
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
ePlainMessage
|
||||
, eMethodCall
|
||||
, eMethodReply
|
||||
, eSignal
|
||||
};
|
||||
|
||||
Message() = default;
|
||||
Message(void *msg, Type type = Type::ePlainMessage) noexcept;
|
||||
Message(const Message&) noexcept;
|
||||
Message& operator=(const Message&) noexcept;
|
||||
Message(Message&& other) noexcept;
|
||||
Message& operator=(Message&& other) noexcept;
|
||||
~Message();
|
||||
|
||||
Message& operator<<(bool item);
|
||||
Message& operator<<(int16_t item);
|
||||
Message& operator<<(int32_t item);
|
||||
Message& operator<<(int64_t item);
|
||||
Message& operator<<(uint8_t item);
|
||||
Message& operator<<(uint16_t item);
|
||||
Message& operator<<(uint32_t item);
|
||||
Message& operator<<(uint64_t item);
|
||||
Message& operator<<(double item);
|
||||
Message& operator<<(const char *item);
|
||||
Message& operator<<(const std::string &item);
|
||||
Message& operator<<(const Variant &item);
|
||||
Message& operator<<(const ObjectPath &item);
|
||||
Message& operator<<(const Signature &item);
|
||||
|
||||
Message& operator>>(bool& item);
|
||||
Message& operator>>(int16_t& item);
|
||||
Message& operator>>(int32_t& item);
|
||||
Message& operator>>(int64_t& item);
|
||||
Message& operator>>(uint8_t& item);
|
||||
Message& operator>>(uint16_t& item);
|
||||
Message& operator>>(uint32_t& item);
|
||||
Message& operator>>(uint64_t& item);
|
||||
Message& operator>>(double& item);
|
||||
Message& operator>>(char*& item);
|
||||
Message& operator>>(std::string &item);
|
||||
Message& operator>>(Variant &item);
|
||||
Message& operator>>(ObjectPath &item);
|
||||
Message& operator>>(Signature &item);
|
||||
|
||||
Message& openContainer(const std::string& signature);
|
||||
Message& closeContainer();
|
||||
Message& openDictEntry(const std::string& signature);
|
||||
Message& closeDictEntry();
|
||||
Message& openVariant(const std::string& signature);
|
||||
Message& closeVariant();
|
||||
Message& openStruct(const std::string& signature);
|
||||
Message& closeStruct();
|
||||
|
||||
Message& enterContainer(const std::string& signature);
|
||||
Message& exitContainer();
|
||||
Message& enterDictEntry(const std::string& signature);
|
||||
Message& exitDictEntry();
|
||||
Message& enterVariant(const std::string& signature);
|
||||
Message& exitVariant();
|
||||
Message& enterStruct(const std::string& signature);
|
||||
Message& exitStruct();
|
||||
|
||||
operator bool() const;
|
||||
void clearFlags();
|
||||
|
||||
std::string getInterfaceName() const;
|
||||
std::string getMemberName() const;
|
||||
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;
|
||||
|
||||
private:
|
||||
void* msg_{};
|
||||
Type type_{Type::ePlainMessage};
|
||||
mutable bool ok_{true};
|
||||
};
|
||||
|
||||
template <typename _Element>
|
||||
inline Message& operator<<(Message& msg, const std::vector<_Element>& items)
|
||||
{
|
||||
msg.openContainer(signature_of<_Element>::str());
|
||||
|
||||
for (const auto& item : items)
|
||||
msg << item;
|
||||
|
||||
msg.closeContainer();
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& operator<<(Message& msg, const std::map<_Key, _Value>& items)
|
||||
{
|
||||
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
|
||||
const std::string arraySignature = "{" + dictEntrySignature + "}";
|
||||
|
||||
msg.openContainer(arraySignature);
|
||||
|
||||
for (const auto& item : items)
|
||||
{
|
||||
msg.openDictEntry(dictEntrySignature);
|
||||
msg << item.first;
|
||||
msg << item.second;
|
||||
msg.closeDictEntry();
|
||||
}
|
||||
|
||||
msg.closeContainer();
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename... _Args>
|
||||
void serialize_pack(Message& msg, _Args&&... args)
|
||||
{
|
||||
// Use initializer_list because it guarantees left to right order, and can be empty
|
||||
using _ = std::initializer_list<int>;
|
||||
// We are not interested in the list itself, but in the side effects
|
||||
(void)_{(void(msg << std::forward<_Args>(args)), 0)...};
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
void serialize_tuple( Message& msg
|
||||
, const _Tuple& t
|
||||
, std::index_sequence<_Is...>)
|
||||
{
|
||||
serialize_pack(msg, std::get<_Is>(t)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator<<(Message& msg, const Struct<_ValueTypes...>& item)
|
||||
{
|
||||
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
|
||||
assert(structSignature.size() > 2);
|
||||
// Remove opening and closing parenthesis from the struct signature to get contents signature
|
||||
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
|
||||
|
||||
msg.openStruct(structContentSignature);
|
||||
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
msg.closeStruct();
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator<<(Message& msg, const std::tuple<_ValueTypes...>& item)
|
||||
{
|
||||
detail::serialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
template <typename _Element>
|
||||
inline Message& operator>>(Message& msg, std::vector<_Element>& items)
|
||||
{
|
||||
if(!msg.enterContainer(signature_of<_Element>::str()))
|
||||
return msg;
|
||||
|
||||
while (true)
|
||||
{
|
||||
_Element elem;
|
||||
if (msg >> elem)
|
||||
items.emplace_back(std::move(elem));
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
msg.clearFlags();
|
||||
|
||||
msg.exitContainer();
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
inline Message& operator>>(Message& msg, std::map<_Key, _Value>& items)
|
||||
{
|
||||
const std::string dictEntrySignature = signature_of<_Key>::str() + signature_of<_Value>::str();
|
||||
const std::string arraySignature = "{" + dictEntrySignature + "}";
|
||||
|
||||
if (!msg.enterContainer(arraySignature))
|
||||
return msg;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!msg.enterDictEntry(dictEntrySignature))
|
||||
break;
|
||||
|
||||
_Key key;
|
||||
_Value value;
|
||||
msg >> key >> value;
|
||||
|
||||
items.emplace(std::move(key), std::move(value));
|
||||
|
||||
msg.exitDictEntry();
|
||||
}
|
||||
|
||||
msg.clearFlags();
|
||||
|
||||
msg.exitContainer();
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <typename... _Args>
|
||||
void deserialize_pack(Message& msg, _Args&... args)
|
||||
{
|
||||
// Use initializer_list because it guarantees left to right order, and can be empty
|
||||
using _ = std::initializer_list<int>;
|
||||
// We are not interested in the list itself, but in the side effects
|
||||
(void)_{(void(msg >> args), 0)...};
|
||||
}
|
||||
|
||||
template <class _Tuple, std::size_t... _Is>
|
||||
void deserialize_tuple( Message& msg
|
||||
, _Tuple& t
|
||||
, std::index_sequence<_Is...> )
|
||||
{
|
||||
deserialize_pack(msg, std::get<_Is>(t)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator>>(Message& msg, Struct<_ValueTypes...>& item)
|
||||
{
|
||||
auto structSignature = signature_of<Struct<_ValueTypes...>>::str();
|
||||
// Remove opening and closing parenthesis from the struct signature to get contents signature
|
||||
auto structContentSignature = structSignature.substr(1, structSignature.size()-2);
|
||||
|
||||
if (!msg.enterStruct(structContentSignature))
|
||||
return msg;
|
||||
|
||||
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
|
||||
msg.exitStruct();
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
inline Message& operator>>(Message& msg, std::tuple<_ValueTypes...>& item)
|
||||
{
|
||||
detail::deserialize_tuple(msg, item, std::index_sequence_for<_ValueTypes...>{});
|
||||
return msg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_MESSAGE_H_ */
|
441
include/sdbus-c++/TypeTraits.h
Normal file
441
include/sdbus-c++/TypeTraits.h
Normal file
@ -0,0 +1,441 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file TypeTraits.h
|
||||
*
|
||||
* Created on: Nov 9, 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_TYPETRAITS_H_
|
||||
#define SDBUS_CXX_TYPETRAITS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus {
|
||||
class Variant;
|
||||
template <typename... _ValueTypes> class Struct;
|
||||
class ObjectPath;
|
||||
class Signature;
|
||||
class Message;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
using method_callback = std::function<void(Message& msg, Message& reply)>;
|
||||
using signal_handler = std::function<void(Message& 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 const std::string str()
|
||||
{
|
||||
// sizeof(_T) < 0 is here to make compiler not being able to figure out
|
||||
// the assertion expression before the template instantiation takes place.
|
||||
static_assert(sizeof(_T) < 0, "Unknown DBus type");
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<void>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<bool>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "b";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint8_t>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "y";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<int16_t>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "n";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint16_t>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "q";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<int32_t>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "i";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint32_t>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "u";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<int64_t>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "x";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<uint64_t>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "t";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<double>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "d";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<char*>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<const char*>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t _N>
|
||||
struct signature_of<char[_N]>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t _N>
|
||||
struct signature_of<const char[_N]>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<std::string>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "s";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
struct signature_of<Struct<_ValueTypes...>>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
std::initializer_list<std::string> signatures{signature_of<_ValueTypes>::str()...};
|
||||
std::string signature;
|
||||
signature += "(";
|
||||
for (const auto& item : signatures)
|
||||
signature += item;
|
||||
signature += ")";
|
||||
return signature;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<Variant>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "v";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<ObjectPath>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "o";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct signature_of<Signature>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "g";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Element>
|
||||
struct signature_of<std::vector<_Element>>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "a" + signature_of<_Element>::str();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Key, typename _Value>
|
||||
struct signature_of<std::map<_Key, _Value>>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return "a{" + signature_of<_Key>::str() + signature_of<_Value>::str() + "}";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits
|
||||
: public function_traits<decltype(&_Type::operator())>
|
||||
{};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits<const _Type>
|
||||
: public function_traits<_Type>
|
||||
{};
|
||||
|
||||
template <typename _Type>
|
||||
struct function_traits<_Type&>
|
||||
: public function_traits<_Type>
|
||||
{};
|
||||
|
||||
// Function traits implementation inspired by (c) kennytm,
|
||||
// https://github.com/kennytm/utils/blob/master/traits.hpp
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef _ReturnType result_type;
|
||||
typedef std::tuple<_Args...> arguments_type;
|
||||
|
||||
typedef _ReturnType function_type(_Args...);
|
||||
|
||||
static constexpr std::size_t arity = sizeof...(_Args);
|
||||
|
||||
template <size_t _Idx>
|
||||
struct arg
|
||||
{
|
||||
typedef std::tuple_element_t<_Idx, std::tuple<_Args...>> type;
|
||||
};
|
||||
|
||||
template <size_t _Idx>
|
||||
using arg_t = typename arg<_Idx>::type;
|
||||
};
|
||||
|
||||
template <typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(*)(_Args...)>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
{};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...)>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef const _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) volatile>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef volatile _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename _ClassType, typename _ReturnType, typename... _Args>
|
||||
struct function_traits<_ReturnType(_ClassType::*)(_Args...) const volatile>
|
||||
: public function_traits<_ReturnType(_Args...)>
|
||||
{
|
||||
typedef const volatile _ClassType& owner_type;
|
||||
};
|
||||
|
||||
template <typename FunctionType>
|
||||
struct function_traits<std::function<FunctionType>>
|
||||
: public function_traits<FunctionType>
|
||||
{};
|
||||
|
||||
template <typename _FunctionType, size_t _Idx>
|
||||
using function_argument_t = typename function_traits<_FunctionType>::template arg_t<_Idx>;
|
||||
|
||||
template <typename _FunctionType>
|
||||
using function_result_t = typename function_traits<_FunctionType>::result_type;
|
||||
|
||||
template <typename _Type>
|
||||
struct aggregate_signature
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return signature_of<std::decay_t<_Type>>::str();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename... _Types>
|
||||
struct aggregate_signature<std::tuple<_Types...>>
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
std::initializer_list<std::string> signatures{signature_of<std::decay_t<_Types>>::str()...};
|
||||
std::string signature;
|
||||
for (const auto& item : signatures)
|
||||
signature += item;
|
||||
return signature;
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return aggregate_signature<tuple_of_function_input_arg_types_t<_Function>>::str();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename _Function>
|
||||
struct signature_of_function_output_arguments
|
||||
{
|
||||
static const std::string str()
|
||||
{
|
||||
return aggregate_signature<function_result_t<_Function>>::str();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// 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>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...>
|
||||
, std::enable_if_t<!std::is_void<function_result_t<_Function>>::value>* = nullptr)
|
||||
{
|
||||
return std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
}
|
||||
|
||||
// Version of apply_impl for functions returning void.
|
||||
// In this case, to have uniform code on the caller side, return empty tuple, our synonym for `void'.
|
||||
template <class _Function, class _Tuple, std::size_t... _I>
|
||||
constexpr decltype(auto) apply_impl( _Function&& f
|
||||
, _Tuple&& t
|
||||
, std::index_sequence<_I...>
|
||||
, std::enable_if_t<std::is_void<function_result_t<_Function>>::value>* = nullptr)
|
||||
{
|
||||
std::forward<_Function>(f)(std::get<_I>(std::forward<_Tuple>(t))...);
|
||||
return std::tuple<>{};
|
||||
}
|
||||
}
|
||||
|
||||
// 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, _Tuple&& t)
|
||||
{
|
||||
return detail::apply_impl( std::forward<_Function>(f)
|
||||
, std::forward<_Tuple>(t)
|
||||
, std::make_index_sequence<std::tuple_size<std::decay_t<_Tuple>>::value>{} );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_TYPETRAITS_H_ */
|
137
include/sdbus-c++/Types.h
Normal file
137
include/sdbus-c++/Types.h
Normal file
@ -0,0 +1,137 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Types.h
|
||||
*
|
||||
* Created on: Nov 23, 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_TYPES_H_
|
||||
#define SDBUS_CXX_TYPES_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
/********************************************//**
|
||||
* @class Variant
|
||||
*
|
||||
* Variant can hold value of any D-Bus-supported type.
|
||||
*
|
||||
***********************************************/
|
||||
class Variant
|
||||
{
|
||||
public:
|
||||
Variant();
|
||||
|
||||
template <typename _ValueType>
|
||||
Variant(const _ValueType& value)
|
||||
: Variant()
|
||||
{
|
||||
msg_.openVariant(signature_of<_ValueType>::str());
|
||||
msg_ << value;
|
||||
msg_.closeVariant();
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
template <typename _ValueType>
|
||||
_ValueType get() const
|
||||
{
|
||||
_ValueType val;
|
||||
msg_.rewind(false);
|
||||
msg_.enterVariant(signature_of<_ValueType>::str());
|
||||
msg_ >> val;
|
||||
msg_.exitVariant();
|
||||
return val;
|
||||
}
|
||||
|
||||
template <typename _ValueType>
|
||||
operator _ValueType() const
|
||||
{
|
||||
return get<_ValueType>();
|
||||
}
|
||||
|
||||
template <typename _Type>
|
||||
bool containsValueOfType() const
|
||||
{
|
||||
return signature_of<_Type>::str() == peekValueType();
|
||||
}
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
void serializeTo(Message& msg) const;
|
||||
void deserializeFrom(Message& msg);
|
||||
std::string peekValueType() const;
|
||||
|
||||
private:
|
||||
mutable Message msg_{};
|
||||
};
|
||||
|
||||
template <typename... _ValueTypes>
|
||||
class Struct
|
||||
: public std::tuple<_ValueTypes...>
|
||||
{
|
||||
public:
|
||||
using std::tuple<_ValueTypes...>::tuple;
|
||||
|
||||
template <std::size_t _I>
|
||||
auto& get()
|
||||
{
|
||||
return std::get<_I>(*this);
|
||||
}
|
||||
|
||||
template <std::size_t _I>
|
||||
const auto& get() const
|
||||
{
|
||||
return std::get<_I>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... _Elements>
|
||||
constexpr Struct<std::decay_t<_Elements>...>
|
||||
make_struct(_Elements&&... args)
|
||||
{
|
||||
typedef Struct<std::decay_t<_Elements>...> result_type;
|
||||
return result_type(std::forward<_Elements>(args)...);
|
||||
}
|
||||
|
||||
class ObjectPath : public std::string
|
||||
{
|
||||
public:
|
||||
using std::string::string;
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
class Signature : public std::string
|
||||
{
|
||||
public:
|
||||
using std::string::string;
|
||||
using std::string::operator=;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_TYPES_H_ */
|
34
include/sdbus-c++/sdbus-c++.h
Normal file
34
include/sdbus-c++/sdbus-c++.h
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file sdbus-c++.h
|
||||
*
|
||||
* Created on: Jan 19, 2017
|
||||
* 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++/IConnection.h>
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <sdbus-c++/Interfaces.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/TypeTraits.h>
|
||||
#include <sdbus-c++/Introspection.h>
|
||||
#include <sdbus-c++/Error.h>
|
11
sdbus-c++.pc.in
Normal file
11
sdbus-c++.pc.in
Normal file
@ -0,0 +1,11 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: @PACKAGE@
|
||||
Description: C++ bindings library for sd-bus
|
||||
Requires: libsystemd
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lsdbus-c++
|
||||
Cflags: -I${includedir}
|
272
src/Connection.cpp
Executable file
272
src/Connection.cpp
Executable file
@ -0,0 +1,272 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Connection.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 "Connection.h"
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "ScopeGuard.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <unistd.h>
|
||||
#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);
|
||||
|
||||
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);
|
||||
|
||||
r = eventfd(0, EFD_SEMAPHORE);
|
||||
SDBUS_THROW_ERROR_IF(r < 0, "Failed to create event object", -errno);
|
||||
runFd_ = r;
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
leaveProcessingLoop();
|
||||
close(runFd_);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::enterProcessingLoopAsync()
|
||||
{
|
||||
asyncLoopThread_ = std::thread([this](){ enterProcessingLoop(); });
|
||||
}
|
||||
|
||||
void Connection::leaveProcessingLoop()
|
||||
{
|
||||
assert(runFd_ >= 0);
|
||||
uint64_t value = 1;
|
||||
write(runFd_, &value, sizeof(value));
|
||||
|
||||
if (asyncLoopThread_.joinable())
|
||||
asyncLoopThread_.join();
|
||||
}
|
||||
|
||||
void* Connection::addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const void* vtable
|
||||
, 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);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
sd_bus_message *sdbusMsg{};
|
||||
SCOPE_EXIT{ sd_bus_message_unref(sdbusMsg); }; // Returned message will become an owner of 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::Message 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
|
||||
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);
|
||||
}
|
||||
|
||||
void* Connection::registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, 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);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
void Connection::unregisterSignalHandler(void* handlerCookie)
|
||||
{
|
||||
sd_bus_slot_unref((sd_bus_slot *)handlerCookie);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> Connection::clone() const
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(busType_);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createConnection()
|
||||
{
|
||||
return createSystemBusConnection();
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createConnection(const std::string& name)
|
||||
{
|
||||
return createSystemBusConnection(name);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection()
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSystem);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSystemBusConnection(const std::string& name)
|
||||
{
|
||||
auto conn = createSystemBusConnection();
|
||||
conn->requestName(name);
|
||||
return conn;
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection()
|
||||
{
|
||||
return std::make_unique<sdbus::internal::Connection>(sdbus::internal::Connection::BusType::eSession);
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IConnection> createSessionBusConnection(const std::string& name)
|
||||
{
|
||||
auto conn = createSessionBusConnection();
|
||||
conn->requestName(name);
|
||||
return conn;
|
||||
}
|
||||
|
||||
}
|
98
src/Connection.h
Executable file
98
src/Connection.h
Executable file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Connection.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_INTERNAL_CONNECTION_H_
|
||||
#define SDBUS_CXX_INTERNAL_CONNECTION_H_
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "IConnection.h"
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
class Connection
|
||||
: public sdbus::IConnection // External, public interface
|
||||
, public sdbus::internal::IConnection // Internal, private interface
|
||||
{
|
||||
public:
|
||||
enum class BusType
|
||||
{
|
||||
eSystem,
|
||||
eSession
|
||||
};
|
||||
|
||||
Connection(BusType type);
|
||||
~Connection();
|
||||
|
||||
void requestName(const std::string& name) override;
|
||||
void releaseName(const std::string& name) override;
|
||||
void enterProcessingLoop() override;
|
||||
void enterProcessingLoopAsync() override;
|
||||
void leaveProcessingLoop() override;
|
||||
|
||||
void* addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const void* vtable
|
||||
, 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;
|
||||
|
||||
void* registerSignalHandler( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) override;
|
||||
void unregisterSignalHandler(void* handlerCookie) override;
|
||||
|
||||
std::unique_ptr<sdbus::internal::IConnection> clone() const override;
|
||||
|
||||
private:
|
||||
static std::string composeSignalMatchFilter( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const std::string& signalName );
|
||||
|
||||
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};
|
||||
BusType busType_;
|
||||
|
||||
static constexpr const uint64_t POLL_TIMEOUT_USEC = 500000;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_CONNECTION_H_ */
|
150
src/ConvenienceClasses.cpp
Normal file
150
src/ConvenienceClasses.cpp
Normal file
@ -0,0 +1,150 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ConvenienceClasses.cpp
|
||||
*
|
||||
* Created on: Jan 19, 2017
|
||||
* 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++/ConvenienceClasses.h>
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
SignalRegistrator::SignalRegistrator(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
SignalRegistrator::~SignalRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the signal if SignalRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus signal", EINVAL);
|
||||
|
||||
// registerSignal() can throw. But as the SignalRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerSignal(interfaceName_, signalName_, signalSignature_);
|
||||
}
|
||||
|
||||
|
||||
PropertyRegistrator::PropertyRegistrator(IObject& object, const std::string& propertyName)
|
||||
: object_(object)
|
||||
, propertyName_(propertyName)
|
||||
, exceptions_(std::uncaught_exceptions())
|
||||
{
|
||||
}
|
||||
|
||||
PropertyRegistrator::~PropertyRegistrator() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't register the property if PropertyRegistrator threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(interfaceName_.empty(), "DBus interface not specified when registering a DBus property", EINVAL);
|
||||
|
||||
// registerProperty() can throw. But as the PropertyRegistrator shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow registerProperty() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.registerProperty( std::move(interfaceName_)
|
||||
, std::move(propertyName_)
|
||||
, std::move(propertySignature_)
|
||||
, std::move(getter_)
|
||||
, std::move(setter_) );
|
||||
}
|
||||
|
||||
|
||||
SignalEmitter::SignalEmitter(IObject& object, const std::string& signalName)
|
||||
: object_(object)
|
||||
, signalName_(signalName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
SignalEmitter::~SignalEmitter() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't emit the signal if SignalEmitter threw an exception in one of its methods
|
||||
if (std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!signal_.isValid(), "DBus interface not specified when emitting a DBus signal", EINVAL);
|
||||
|
||||
// emitSignal() can throw. But as the SignalEmitter shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow emitSignal() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
object_.emitSignal(signal_);
|
||||
}
|
||||
|
||||
|
||||
MethodInvoker::MethodInvoker(IObjectProxy& objectProxy, const std::string& methodName)
|
||||
: objectProxy_(objectProxy)
|
||||
, methodName_(methodName)
|
||||
, exceptions_(std::uncaught_exceptions()) // Needs C++17
|
||||
{
|
||||
}
|
||||
|
||||
MethodInvoker::~MethodInvoker() noexcept(false) // since C++11, destructors must
|
||||
{ // explicitly be allowed to throw
|
||||
// Don't call the method if it has been called already or if MethodInvoker
|
||||
// threw an exception in one of its methods
|
||||
if (methodCalled_ || std::uncaught_exceptions() != exceptions_)
|
||||
return;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!method_.isValid(), "DBus interface not specified when calling a DBus method", EINVAL);
|
||||
|
||||
// callMethod() can throw. But as the MethodInvoker shall always be used as an unnamed,
|
||||
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
|
||||
// shall never happen. I.e. it should not happen that this destructor is directly called
|
||||
// in the stack-unwinding process of another flying exception (which would lead to immediate
|
||||
// termination). It can be called indirectly in the destructor of another object, but that's
|
||||
// fine and safe provided that the caller catches exceptions thrown from here.
|
||||
// Therefore, we can allow callMethod() to throw even if we are in the destructor.
|
||||
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
|
||||
// to the exception thrown from here if the caller is a destructor itself.
|
||||
objectProxy_.callMethod(method_);
|
||||
}
|
||||
|
||||
}
|
40
src/Error.cpp
Normal file
40
src/Error.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Error.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++/Error.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
sdbus::Error createError(int errNo, const std::string& customMsg)
|
||||
{
|
||||
sd_bus_error sdbusError = SD_BUS_ERROR_NULL;
|
||||
sd_bus_error_set_errno(&sdbusError, errNo);
|
||||
std::string name(sdbusError.name);
|
||||
std::string message(customMsg + " (" + sdbusError.message + ")");
|
||||
sd_bus_error_free(&sdbusError);
|
||||
return sdbus::Error(name, message);
|
||||
}
|
||||
}
|
77
src/IConnection.h
Executable file
77
src/IConnection.h
Executable file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file IConnection.h
|
||||
*
|
||||
* Created on: Nov 9, 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_INTERNAL_ICONNECTION_H_
|
||||
#define SDBUS_CXX_INTERNAL_ICONNECTION_H_
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declaration
|
||||
namespace sdbus {
|
||||
class Message;
|
||||
}
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
|
||||
class IConnection
|
||||
{
|
||||
public:
|
||||
virtual void* addObjectVTable( const std::string& objectPath
|
||||
, const std::string& interfaceName
|
||||
, const void* vtable
|
||||
, 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::Message 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
|
||||
, const std::string& signalName
|
||||
, sd_bus_message_handler_t callback
|
||||
, void* userData ) = 0;
|
||||
virtual void unregisterSignalHandler(void* handlerCookie) = 0;
|
||||
|
||||
virtual void enterProcessingLoopAsync() = 0;
|
||||
virtual void leaveProcessingLoop() = 0;
|
||||
|
||||
virtual std::unique_ptr<sdbus::internal::IConnection> clone() const = 0;
|
||||
|
||||
virtual ~IConnection() = default;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_ICONNECTION_H_ */
|
26
src/Makefile.am
Normal file
26
src/Makefile.am
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
lib_LTLIBRARIES = libsdbus-c++.la
|
||||
|
||||
libsdbus_c___la_SOURCES = \
|
||||
Connection.cpp \
|
||||
ConvenienceClasses.cpp \
|
||||
Message.cpp \
|
||||
Object.cpp \
|
||||
ObjectProxy.cpp \
|
||||
Types.cpp \
|
||||
Error.cpp \
|
||||
VTableUtils.c
|
||||
|
||||
libsdbus_c___la_LIBADD = @SYSTEMD_LIBS@
|
||||
|
||||
# Setting per-file flags
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/include
|
||||
AM_CXXFLAGS = @libsdbus_cpp_CFLAGS@ @SYSTEMD_CFLAGS@ -std=c++17 -pipe -pedantic -W -Wall
|
||||
AM_LDFLAGS = @libsdbus_cpp_LIBS@ @SYSTEMD_LIBS@
|
||||
|
||||
libsdbus_c___la_LDFLAGS = -version-info 0:0:0
|
||||
|
||||
# Cleaning
|
||||
CLEANFILES = *~ *.lo *.la
|
||||
MOSTLYCLEANFILES = *.o
|
||||
DISTCLEANFILES = Makefile Makefile.in
|
676
src/Message.cpp
Executable file
676
src/Message.cpp
Executable file
@ -0,0 +1,676 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Message.cpp
|
||||
*
|
||||
* Created on: Nov 9, 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++/Message.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "MessageUtils.h"
|
||||
#include "ScopeGuard.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { /*namespace internal {*/
|
||||
|
||||
Message::Message(void *msg, Type type) noexcept
|
||||
: msg_(msg)
|
||||
, type_(type)
|
||||
{
|
||||
assert(msg_ != nullptr);
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message::Message(const Message& other) noexcept
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
Message& Message::operator=(const Message& other) noexcept
|
||||
{
|
||||
msg_ = other.msg_;
|
||||
type_ = other.type_;
|
||||
ok_ = other.ok_;
|
||||
|
||||
sd_bus_message_ref((sd_bus_message*)msg_);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message::Message(Message&& other) noexcept
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
Message& Message::operator=(Message&& other) noexcept
|
||||
{
|
||||
msg_ = other.msg_;
|
||||
other.msg_ = nullptr;
|
||||
type_ = other.type_;
|
||||
other.type_ = {};
|
||||
ok_ = other.ok_;
|
||||
other.ok_ = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message::~Message()
|
||||
{
|
||||
if (msg_)
|
||||
sd_bus_message_unref((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator<<(const Variant &item)
|
||||
{
|
||||
item.serializeTo(*this);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Message& Message::operator>>(bool& item)
|
||||
{
|
||||
int intItem;
|
||||
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);
|
||||
|
||||
item = static_cast<bool>(intItem);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator>>(std::string& item)
|
||||
{
|
||||
char* str{};
|
||||
(*this) >> str;
|
||||
|
||||
if (str != nullptr)
|
||||
item = str;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator>>(Variant &item)
|
||||
{
|
||||
item.deserializeFrom(*this);
|
||||
|
||||
// Empty variant is normally prohibited. Users cannot send empty variants.
|
||||
// Therefore in this context an empty variant means that we are at the end
|
||||
// of deserializing a container, and thus we shall set ok_ flag to false.
|
||||
if (item.isEmpty())
|
||||
ok_ = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator>>(ObjectPath &item)
|
||||
{
|
||||
char* str{};
|
||||
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);
|
||||
|
||||
if (str != nullptr)
|
||||
item = str;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message& Message::operator>>(Signature &item)
|
||||
{
|
||||
char* str{};
|
||||
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);
|
||||
|
||||
if (str != nullptr)
|
||||
item = str;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
Message::operator bool() const
|
||||
{
|
||||
return ok_;
|
||||
}
|
||||
|
||||
void Message::clearFlags()
|
||||
{
|
||||
ok_ = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
std::string Message::getInterfaceName() const
|
||||
{
|
||||
return sd_bus_message_get_interface((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
std::string Message::getMemberName() const
|
||||
{
|
||||
return sd_bus_message_get_member((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
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);
|
||||
type = typeSig;
|
||||
contents = contentsSig;
|
||||
}
|
||||
|
||||
bool Message::isValid() const
|
||||
{
|
||||
return msg_ != nullptr;
|
||||
}
|
||||
|
||||
bool Message::isEmpty() const
|
||||
{
|
||||
return sd_bus_message_is_empty((sd_bus_message*)msg_);
|
||||
}
|
||||
|
||||
Message::Type Message::getType() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
Message createPlainMessage()
|
||||
{
|
||||
int r;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
return Message(sdbusMsg, Message::Type::ePlainMessage);
|
||||
}
|
||||
|
||||
/*}*/}
|
36
src/MessageUtils.h
Normal file
36
src/MessageUtils.h
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file MessageUtils.h
|
||||
*
|
||||
* Created on: Dec 5, 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_INTERNAL_MESSAGEUTILS_H_
|
||||
#define SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_
|
||||
|
||||
#include <sdbus-c++/Message.h>
|
||||
|
||||
namespace sdbus
|
||||
{
|
||||
Message createPlainMessage();
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_MESSAGEUTILS_H_ */
|
261
src/Object.cpp
Executable file
261
src/Object.cpp
Executable file
@ -0,0 +1,261 @@
|
||||
/**
|
||||
* (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 "Object.h"
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "IConnection.h"
|
||||
#include "VTableUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <utility>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
Object::Object(sdbus::internal::IConnection& connection, std::string objectPath)
|
||||
: connection_(connection), objectPath_(std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
void Object::registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback )
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!methodCallback, "Invalid method callback provided", EINVAL);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
|
||||
InterfaceData::MethodData methodData{inputSignature, outputSignature, std::move(methodCallback)};
|
||||
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::registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature )
|
||||
{
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
|
||||
InterfaceData::SignalData signalData{signature};
|
||||
auto inserted = interface.signals_.emplace(signalName, std::move(signalData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal: signal already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback )
|
||||
{
|
||||
registerProperty( interfaceName
|
||||
, propertyName
|
||||
, signature
|
||||
, getCallback
|
||||
, property_set_callback{} );
|
||||
}
|
||||
|
||||
void Object::registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback )
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!getCallback && !setCallback, "Invalid property callbacks provided", EINVAL);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
|
||||
InterfaceData::PropertyData propertyData{signature, std::move(getCallback), std::move(setCallback)};
|
||||
auto inserted = interface.properties_.emplace(propertyName, std::move(propertyData)).second;
|
||||
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register property: property already exists", EINVAL);
|
||||
}
|
||||
|
||||
void Object::finishRegistration()
|
||||
{
|
||||
for (auto& item : interfaces_)
|
||||
{
|
||||
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); };
|
||||
}
|
||||
}
|
||||
|
||||
sdbus::Message 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)
|
||||
{
|
||||
message.send();
|
||||
}
|
||||
|
||||
int Object::sdbus_method_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError)
|
||||
{
|
||||
Message message(sdbusMessage, Message::Type::eMethodCall);
|
||||
|
||||
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);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
reply.send();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Object::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 )
|
||||
{
|
||||
Message reply(sdbusReply, Message::Type::ePlainMessage);
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[interface].properties_[property].getCallback_;
|
||||
// Getter can be empty - the case of "write-only" property
|
||||
if (!callback)
|
||||
{
|
||||
sd_bus_error_set(retError, "org.freedesktop.DBus.Error.Failed", "Cannot read property as it is write-only");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
callback(reply);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Object::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 )
|
||||
{
|
||||
Message value(sdbusValue, Message::Type::ePlainMessage);
|
||||
|
||||
auto* object = static_cast<Object*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[interface].properties_[property].setCallback_;
|
||||
assert(callback);
|
||||
|
||||
try
|
||||
{
|
||||
callback(value);
|
||||
}
|
||||
catch (const sdbus::Error& e)
|
||||
{
|
||||
sd_bus_error_set(retError, e.getName().c_str(), e.getMessage().c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IObject> createObject(sdbus::IConnection& connection, std::string objectPath)
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
return std::make_unique<sdbus::internal::Object>(*sdbusConnection, std::move(objectPath));
|
||||
}
|
||||
|
||||
}
|
129
src/Object.h
Normal file
129
src/Object.h
Normal file
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Object.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_INTERNAL_OBJECT_H_
|
||||
#define SDBUS_CXX_INTERNAL_OBJECT_H_
|
||||
|
||||
#include <sdbus-c++/IObject.h>
|
||||
#include "IConnection.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
|
||||
class Object
|
||||
: public IObject
|
||||
{
|
||||
public:
|
||||
Object(sdbus::internal::IConnection& connection, std::string objectPath);
|
||||
|
||||
void registerMethod( const std::string& interfaceName
|
||||
, const std::string& methodName
|
||||
, const std::string& inputSignature
|
||||
, const std::string& outputSignature
|
||||
, method_callback methodCallback ) override;
|
||||
|
||||
void registerSignal( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, const std::string& signature ) override;
|
||||
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback ) override;
|
||||
|
||||
void registerProperty( const std::string& interfaceName
|
||||
, const std::string& propertyName
|
||||
, const std::string& signature
|
||||
, property_get_callback getCallback
|
||||
, property_set_callback setCallback ) override;
|
||||
|
||||
void finishRegistration() override;
|
||||
|
||||
sdbus::Message createSignal(const std::string& interfaceName, const std::string& signalName) override;
|
||||
void emitSignal(const sdbus::Message& message) override;
|
||||
|
||||
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
|
||||
{
|
||||
using MethodName = std::string;
|
||||
struct MethodData
|
||||
{
|
||||
std::string inputArgs_;
|
||||
std::string outputArgs_;
|
||||
method_callback callback_;
|
||||
};
|
||||
std::map<MethodName, MethodData> methods_;
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
{
|
||||
std::string signature_;
|
||||
};
|
||||
std::map<SignalName, SignalData> signals_;
|
||||
using PropertyName = std::string;
|
||||
struct PropertyData
|
||||
{
|
||||
std::string signature_;
|
||||
property_get_callback getCallback_;
|
||||
property_set_callback setCallback_;
|
||||
};
|
||||
std::map<PropertyName, PropertyData> properties_;
|
||||
std::vector<sd_bus_vtable> vtable_;
|
||||
|
||||
std::unique_ptr<void, std::function<void(void*)>> slot_;
|
||||
};
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_OBJECT_H_ */
|
182
src/ObjectProxy.cpp
Executable file
182
src/ObjectProxy.cpp
Executable file
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ObjectProxy.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 "ObjectProxy.h"
|
||||
#include <sdbus-c++/Message.h>
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "IConnection.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { namespace internal {
|
||||
|
||||
ObjectProxy::ObjectProxy(sdbus::internal::IConnection& connection, std::string destination, std::string objectPath)
|
||||
: connection_(&connection, [](sdbus::internal::IConnection *){ /* Intentionally left empty */ })
|
||||
, ownConnection_(false)
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectProxy::ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
: connection_(std::move(connection))
|
||||
, ownConnection_(true)
|
||||
, destination_(std::move(destination))
|
||||
, objectPath_(std::move(objectPath))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectProxy::~ObjectProxy()
|
||||
{
|
||||
// If the dedicated connection for signals is used, we have to stop the processing loop
|
||||
// upon this connection prior to unregistering signal slots in the interfaces_ container,
|
||||
// otherwise we might have a race condition of two threads working upon one connection.
|
||||
if (signalConnection_ != nullptr)
|
||||
signalConnection_->leaveProcessingLoop();
|
||||
}
|
||||
|
||||
Message 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)
|
||||
{
|
||||
return message.send();
|
||||
}
|
||||
|
||||
void ObjectProxy::registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler )
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(!signalHandler, "Invalid signal handler provided", EINVAL);
|
||||
|
||||
auto& interface = interfaces_[interfaceName];
|
||||
|
||||
InterfaceData::SignalData signalData{std::move(signalHandler), nullptr};
|
||||
auto insertionResult = interface.signals_.emplace(signalName, std::move(signalData));
|
||||
|
||||
auto inserted = insertionResult.second;
|
||||
SDBUS_THROW_ERROR_IF(!inserted, "Failed to register signal handler: handler already exists", EINVAL);
|
||||
}
|
||||
|
||||
void ObjectProxy::finishRegistration()
|
||||
{
|
||||
bool hasSignals = listensToSignals();
|
||||
|
||||
if (hasSignals && ownConnection_)
|
||||
{
|
||||
// Let's use dedicated signalConnection_ for signals,
|
||||
// which will then be used by the processing loop thread.
|
||||
signalConnection_ = connection_->clone();
|
||||
registerSignalHandlers(*signalConnection_);
|
||||
signalConnection_->enterProcessingLoopAsync();
|
||||
}
|
||||
else if (hasSignals)
|
||||
{
|
||||
// Let's used connection provided from the outside.
|
||||
registerSignalHandlers(*connection_);
|
||||
}
|
||||
}
|
||||
|
||||
bool ObjectProxy::listensToSignals() const
|
||||
{
|
||||
for (auto& interfaceItem : interfaces_)
|
||||
if (!interfaceItem.second.signals_.empty())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObjectProxy::registerSignalHandlers(sdbus::internal::IConnection& connection)
|
||||
{
|
||||
for (auto& interfaceItem : interfaces_)
|
||||
{
|
||||
const auto& interfaceName = interfaceItem.first;
|
||||
auto& signalsOnInterface = interfaceItem.second.signals_;
|
||||
|
||||
for (auto& signalItem : signalsOnInterface)
|
||||
{
|
||||
const auto& signalName = signalItem.first;
|
||||
auto& slot = signalItem.second.slot_;
|
||||
auto* rawSlotPtr = connection.registerSignalHandler( objectPath_
|
||||
, interfaceName
|
||||
, signalName
|
||||
, &ObjectProxy::sdbus_signal_callback
|
||||
, this );
|
||||
slot.reset(rawSlotPtr);
|
||||
slot.get_deleter() = [&connection](void *slot){ connection.unregisterSignalHandler(slot); };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ObjectProxy::sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error */*retError*/)
|
||||
{
|
||||
Message message(sdbusMessage, Message::Type::eSignal);
|
||||
|
||||
auto* object = static_cast<ObjectProxy*>(userData);
|
||||
// Note: The lookup can be optimized by using sorted vectors instead of associative containers
|
||||
auto& callback = object->interfaces_[message.getInterfaceName()].signals_[message.getMemberName()].callback_;
|
||||
assert(callback);
|
||||
|
||||
callback(message);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
namespace sdbus {
|
||||
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto* sdbusConnection = dynamic_cast<sdbus::internal::IConnection*>(&connection);
|
||||
SDBUS_THROW_ERROR_IF(!sdbusConnection, "Connection is not a real sdbus-c++ connection", EINVAL);
|
||||
|
||||
return std::make_unique<sdbus::internal::ObjectProxy>( *sdbusConnection
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
std::unique_ptr<sdbus::IObjectProxy> createObjectProxy( std::string destination
|
||||
, std::string objectPath )
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
auto sdbusConnection = std::unique_ptr<sdbus::internal::IConnection>(dynamic_cast<sdbus::internal::IConnection*>(connection.release()));
|
||||
assert(sdbusConnection != nullptr);
|
||||
|
||||
return std::make_unique<sdbus::internal::ObjectProxy>( std::move(sdbusConnection)
|
||||
, std::move(destination)
|
||||
, std::move(objectPath) );
|
||||
}
|
||||
|
||||
}
|
93
src/ObjectProxy.h
Executable file
93
src/ObjectProxy.h
Executable file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ObjectProxy.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_INTERNAL_OBJECTPROXY_H_
|
||||
#define SDBUS_CXX_INTERNAL_OBJECTPROXY_H_
|
||||
|
||||
#include <sdbus-c++/IObjectProxy.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
// Forward declarations
|
||||
namespace sdbus { namespace internal {
|
||||
class IConnection;
|
||||
}}
|
||||
|
||||
namespace sdbus {
|
||||
namespace internal {
|
||||
|
||||
class ObjectProxy
|
||||
: public IObjectProxy
|
||||
{
|
||||
public:
|
||||
ObjectProxy( sdbus::internal::IConnection& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
ObjectProxy( std::unique_ptr<sdbus::internal::IConnection>&& connection
|
||||
, std::string destination
|
||||
, std::string objectPath );
|
||||
~ObjectProxy();
|
||||
|
||||
Message createMethodCall(const std::string& interfaceName, const std::string& methodName) override;
|
||||
Message callMethod(const Message& message) override;
|
||||
|
||||
void registerSignalHandler( const std::string& interfaceName
|
||||
, const std::string& signalName
|
||||
, signal_handler signalHandler ) override;
|
||||
void finishRegistration() override;
|
||||
|
||||
private:
|
||||
bool listensToSignals() const;
|
||||
void registerSignalHandlers(sdbus::internal::IConnection& connection);
|
||||
static int sdbus_signal_callback(sd_bus_message *sdbusMessage, void *userData, sd_bus_error *retError);
|
||||
|
||||
private:
|
||||
std::unique_ptr< sdbus::internal::IConnection
|
||||
, std::function<void(sdbus::internal::IConnection*)>
|
||||
> connection_;
|
||||
bool ownConnection_{};
|
||||
std::unique_ptr<sdbus::internal::IConnection> signalConnection_;
|
||||
std::string destination_;
|
||||
std::string objectPath_;
|
||||
|
||||
using InterfaceName = std::string;
|
||||
struct InterfaceData
|
||||
{
|
||||
using SignalName = std::string;
|
||||
struct SignalData
|
||||
{
|
||||
signal_handler callback_;
|
||||
std::unique_ptr<void, std::function<void(void*)>> slot_;
|
||||
};
|
||||
std::map<SignalName, SignalData> signals_;
|
||||
};
|
||||
std::map<InterfaceName, InterfaceData> interfaces_;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_OBJECTPROXY_H_ */
|
131
src/ScopeGuard.h
Normal file
131
src/ScopeGuard.h
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ScopeGuard.h
|
||||
*
|
||||
* Created on: Apr 29, 2015
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
|
||||
#define SDBUS_CPP_INTERNAL_SCOPEGUARD_H_
|
||||
|
||||
#include <utility>
|
||||
|
||||
// Straightforward, modern, easy-to-use RAII utility to perform work on scope exit in an exception-safe manner.
|
||||
//
|
||||
// The utility helps providing basic exception safety guarantee by ensuring that the resources are always
|
||||
// released in face of an exception and released or kept when exiting the scope normally.
|
||||
//
|
||||
// Use SCOPE_EXIT if you'd like to perform an (mostly clean-up) operation when the scope ends, either due
|
||||
// to an exception or because it just ends normally.
|
||||
// Use SCOPE_EXIT_NAMED if you'd like to conditionally deactivate given scope-exit operation. This is useful
|
||||
// if, for example, we want the operation to be executed only in face of an exception.
|
||||
//
|
||||
// Example usage (maybe a bit contrived):
|
||||
// SqlDb* g_pDb = nullptr;
|
||||
// void fnc() {
|
||||
// auto* pDb = open_database(...); // Resource to be released when scope exits due to whatever reason
|
||||
// SCOPE_EXIT{ close_database(pDb); }; // Executes body when exiting the scope due to whatever reason
|
||||
// g_pDb = open_database(...); // Resource to be released only in face of an exception
|
||||
// SCOPE_EXIT_NAMED(releaseGlobalDbOnException) // Executes body when exiting the scope in face of an exception
|
||||
// {
|
||||
// close_database(g_pDb);
|
||||
// g_pDb = nullptr;
|
||||
// };
|
||||
// //... do operations (that may or may not possibly throw) on the database here
|
||||
// releaseGlobalDbOnException.dismiss(); // Don't release global DB on normal scope exit
|
||||
// return; // exiting scope normally
|
||||
// }
|
||||
|
||||
#define SCOPE_EXIT \
|
||||
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
|
||||
= ::skybase::utils::detail::ScopeGuardOnExit() + [&]() \
|
||||
/**/
|
||||
|
||||
#define SCOPE_EXIT_NAMED(NAME) \
|
||||
auto NAME \
|
||||
= ::skybase::utils::detail::ScopeGuardOnExit() + [&]() \
|
||||
/**/
|
||||
|
||||
namespace skybase {
|
||||
namespace utils {
|
||||
|
||||
template <class _Fun>
|
||||
class ScopeGuard
|
||||
{
|
||||
_Fun fnc_;
|
||||
bool active_;
|
||||
|
||||
public:
|
||||
ScopeGuard(_Fun f)
|
||||
: fnc_(std::move(f))
|
||||
, active_(true)
|
||||
{
|
||||
}
|
||||
~ScopeGuard()
|
||||
{
|
||||
if (active_)
|
||||
fnc_();
|
||||
}
|
||||
void dismiss()
|
||||
{
|
||||
active_ = false;
|
||||
}
|
||||
ScopeGuard() = delete;
|
||||
ScopeGuard(const ScopeGuard&) = delete;
|
||||
ScopeGuard& operator=(const ScopeGuard&) = delete;
|
||||
ScopeGuard(ScopeGuard&& rhs)
|
||||
: fnc_(std::move(rhs.fnc_))
|
||||
, active_(rhs.active_)
|
||||
{
|
||||
rhs.dismiss();
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
enum class ScopeGuardOnExit
|
||||
{
|
||||
};
|
||||
|
||||
// Helper function to auto-deduce type of the callable entity
|
||||
template <typename _Fun>
|
||||
ScopeGuard<_Fun> operator+(ScopeGuardOnExit, _Fun&& fnc)
|
||||
{
|
||||
return ScopeGuard<_Fun>(std::forward<_Fun>(fnc));
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
#define CONCATENATE_IMPL(s1, s2) s1##s2
|
||||
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
|
||||
|
||||
#ifdef __COUNTER__
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str, __COUNTER__) \
|
||||
/**/
|
||||
#else
|
||||
#define ANONYMOUS_VARIABLE(str) \
|
||||
CONCATENATE(str, __LINE__) \
|
||||
/**/
|
||||
#endif
|
||||
|
||||
#endif /* SDBUS_CPP_INTERNAL_SCOPEGUARD_H_ */
|
65
src/Types.cpp
Normal file
65
src/Types.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Types.cpp
|
||||
*
|
||||
* Created on: Nov 30, 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++/Types.h>
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace sdbus { /*namespace internal {*/
|
||||
|
||||
Variant::Variant()
|
||||
: msg_(createPlainMessage())
|
||||
{
|
||||
}
|
||||
|
||||
void Variant::serializeTo(Message& msg) const
|
||||
{
|
||||
SDBUS_THROW_ERROR_IF(isEmpty(), "Empty variant is not allowed", EINVAL);
|
||||
msg_.rewind(true);
|
||||
msg_.copyTo(msg, true);
|
||||
}
|
||||
|
||||
void Variant::deserializeFrom(Message& msg)
|
||||
{
|
||||
msg.copyTo(msg_, false);
|
||||
msg_.seal();
|
||||
}
|
||||
|
||||
std::string Variant::peekValueType() const
|
||||
{
|
||||
std::string type;
|
||||
std::string contents;
|
||||
msg_.peekType(type, contents);
|
||||
return contents;
|
||||
}
|
||||
|
||||
bool Variant::isEmpty() const
|
||||
{
|
||||
return msg_.isEmpty();
|
||||
}
|
||||
|
||||
}
|
72
src/VTableUtils.c
Normal file
72
src/VTableUtils.c
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file VTableUtils.c
|
||||
*
|
||||
* 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 "VTableUtils.h"
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
sd_bus_vtable createVTableStartItem()
|
||||
{
|
||||
struct sd_bus_vtable vtableStart = SD_BUS_VTABLE_START(0);
|
||||
return vtableStart;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, sd_bus_message_handler_t handler )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_METHOD(member, signature, result, handler, SD_BUS_VTABLE_UNPRIVILEGED);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableSignalItem( const char *member
|
||||
, const char *signature )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_SIGNAL(member, signature, 0);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_PROPERTY(member, signature, getter, 0, 0);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter )
|
||||
{
|
||||
struct sd_bus_vtable vtableItem = SD_BUS_WRITABLE_PROPERTY(member, signature, getter, setter, 0, 0);
|
||||
return vtableItem;
|
||||
}
|
||||
|
||||
sd_bus_vtable createVTableEndItem()
|
||||
{
|
||||
struct sd_bus_vtable vtableEnd = SD_BUS_VTABLE_END;
|
||||
return vtableEnd;
|
||||
}
|
55
src/VTableUtils.h
Normal file
55
src/VTableUtils.h
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file VTableUtils.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_INTERNAL_VTABLEUTILS_H_
|
||||
#define SDBUS_CXX_INTERNAL_VTABLEUTILS_H_
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
sd_bus_vtable createVTableStartItem();
|
||||
sd_bus_vtable createVTableMethodItem( const char *member
|
||||
, const char *signature
|
||||
, const char *result
|
||||
, sd_bus_message_handler_t handler );
|
||||
sd_bus_vtable createVTableSignalItem( const char *member
|
||||
, const char *signature );
|
||||
sd_bus_vtable createVTablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter );
|
||||
sd_bus_vtable createVTableWritablePropertyItem( const char *member
|
||||
, const char *signature
|
||||
, sd_bus_property_get_t getter
|
||||
, sd_bus_property_set_t setter );
|
||||
sd_bus_vtable createVTableEndItem();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SDBUS_CXX_INTERNAL_VTABLEUTILS_H_ */
|
244
stub-generator/AdaptorGenerator.cpp
Normal file
244
stub-generator/AdaptorGenerator.cpp
Normal file
@ -0,0 +1,244 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file AdaptorGenerator.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "generator_utils.h"
|
||||
#include "AdaptorGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
using std::endl;
|
||||
|
||||
using sdbuscpp::xml::Document;
|
||||
using sdbuscpp::xml::Node;
|
||||
using sdbuscpp::xml::Nodes;
|
||||
|
||||
/**
|
||||
* Generate adaptor code - server glue
|
||||
*/
|
||||
int AdaptorGenerator::transformXmlToFileImpl(const Document& doc, const char* filename) const
|
||||
{
|
||||
Node &root = *(doc.root);
|
||||
Nodes interfaces = root["interface"];
|
||||
|
||||
std::ostringstream code;
|
||||
code << createHeader(filename, StubType::ADAPTOR);
|
||||
|
||||
for (const auto& interface : interfaces)
|
||||
{
|
||||
code << processInterface(*interface);
|
||||
}
|
||||
|
||||
code << "#endif" << endl;
|
||||
|
||||
return writeToFile(filename, code.str());
|
||||
}
|
||||
|
||||
|
||||
std::string AdaptorGenerator::processInterface(Node& interface) const
|
||||
{
|
||||
std::string ifaceName = interface.get("name");
|
||||
std::cout << "Generating adaptor code for interface " << ifaceName << endl;
|
||||
|
||||
unsigned int namespacesCount = 0;
|
||||
std::string namespacesStr;
|
||||
std::tie(namespacesCount, namespacesStr) = generateNamespaces(ifaceName);
|
||||
|
||||
std::ostringstream body;
|
||||
body << namespacesStr;
|
||||
|
||||
std::string className = ifaceName.substr(ifaceName.find_last_of(".") + 1)
|
||||
+ "_adaptor";
|
||||
|
||||
body << "class " << className << endl
|
||||
<< "{" << endl
|
||||
<< "public:" << endl
|
||||
<< tab << "static constexpr const char* interfaceName = \"" << ifaceName << "\";" << endl << endl
|
||||
<< "protected:" << endl
|
||||
<< tab << className << "(sdbus::IObject& object)" << endl
|
||||
<< tab << tab << ": object_(object)" << endl;
|
||||
|
||||
Nodes methods = interface["method"];
|
||||
Nodes signals = interface["signal"];
|
||||
Nodes properties = interface["property"];
|
||||
|
||||
std::string methodRegistration, methodDeclaration;
|
||||
std::tie(methodRegistration, methodDeclaration) = processMethods(methods);
|
||||
|
||||
std::string signalRegistration, signalMethods;
|
||||
std::tie(signalRegistration, signalMethods) = processSignals(signals);
|
||||
|
||||
std::string propertyRegistration, propertyAccessorDeclaration;
|
||||
std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties);
|
||||
|
||||
body << tab << "{" << endl
|
||||
<< methodRegistration
|
||||
<< signalRegistration
|
||||
<< propertyRegistration
|
||||
<< tab << "}" << endl << endl;
|
||||
|
||||
if (!signalMethods.empty())
|
||||
{
|
||||
body << "public:" << endl << signalMethods;
|
||||
}
|
||||
|
||||
if (!methodDeclaration.empty())
|
||||
{
|
||||
body << "private:" << endl << methodDeclaration << endl;
|
||||
}
|
||||
|
||||
if (!propertyAccessorDeclaration.empty())
|
||||
{
|
||||
body << "private:" << endl << propertyAccessorDeclaration << endl;
|
||||
}
|
||||
|
||||
body << "private:" << endl
|
||||
<< tab << "sdbus::IObject& object_;" << endl
|
||||
<< "};" << endl << endl
|
||||
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
|
||||
|
||||
return body.str();
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
|
||||
for (const auto& method : methods)
|
||||
{
|
||||
auto methodName = method->get("name");
|
||||
|
||||
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);
|
||||
|
||||
registrationSS << tab << tab << "object_.registerMethod(\""
|
||||
<< methodName << "\")"
|
||||
<< ".onInterface(interfaceName)"
|
||||
<< ".implementedAs("
|
||||
<< "[this]("
|
||||
<< argTypeStr
|
||||
<< "){ return this->" << methodName << "("
|
||||
<< argStr << "); });" << endl;
|
||||
|
||||
declarationSS << tab
|
||||
<< "virtual " << outArgsToType(outArgs) << " " << methodName
|
||||
<< "(" << argTypeStr << ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
|
||||
{
|
||||
std::ostringstream signalRegistrationSS, signalMethodSS;
|
||||
|
||||
for (const auto& signal : signals)
|
||||
{
|
||||
auto name = signal->get("name");
|
||||
Nodes args = (*signal)["arg"];
|
||||
|
||||
std::string argStr, argTypeStr, typeStr;;
|
||||
std::tie(argStr, argTypeStr, typeStr) = argsToNamesAndTypes(args);
|
||||
|
||||
signalRegistrationSS << tab << tab
|
||||
<< "object_.registerSignal(\"" << name << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
|
||||
if (args.size() > 0)
|
||||
{
|
||||
signalRegistrationSS << ".withParameters<" << typeStr << ">()";
|
||||
}
|
||||
|
||||
signalRegistrationSS << ";" << endl;
|
||||
|
||||
signalMethodSS << tab << "void " << name << "(" << argTypeStr << ")" << endl
|
||||
<< tab << "{" << endl
|
||||
<< tab << tab << "object_.emitSignal(\"" << name << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
|
||||
if (!argStr.empty())
|
||||
{
|
||||
signalMethodSS << ".withArguments(" << argStr << ")";
|
||||
}
|
||||
|
||||
signalMethodSS << ";" << endl
|
||||
<< tab << "}" << endl << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(signalRegistrationSS.str(), signalMethodSS.str());
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
|
||||
for (const auto& property : properties)
|
||||
{
|
||||
auto propertyName = property->get("name");
|
||||
auto propertyAccess = property->get("access");
|
||||
auto propertySignature = property->get("type");
|
||||
|
||||
auto propertyType = signature_to_type(propertySignature);
|
||||
auto propertyArg = std::string("value");
|
||||
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
|
||||
|
||||
registrationSS << tab << tab << "object_.registerProperty(\""
|
||||
<< propertyName << "\")"
|
||||
<< ".onInterface(interfaceName)";
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
{
|
||||
registrationSS << ".withGetter([this](){ return this->" << propertyName << "(); })";
|
||||
}
|
||||
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
{
|
||||
registrationSS
|
||||
<< ".withSetter([this](" << propertyTypeArg << ")"
|
||||
"{ this->" << propertyName << "(" << propertyArg << "); })";
|
||||
}
|
||||
|
||||
registrationSS << ";" << endl;
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
declarationSS << tab << "virtual " << propertyType << " " << propertyName << "() = 0;" << endl;
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
declarationSS << tab << "virtual void " << propertyName << "(" << propertyTypeArg << ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
}
|
80
stub-generator/AdaptorGenerator.h
Normal file
80
stub-generator/AdaptorGenerator.h
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file AdaptorGenerator.h
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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 __SDBUSCPP_TOOLS_ADAPTOR_GENERATOR_H
|
||||
#define __SDBUSCPP_TOOLS_ADAPTOR_GENERATOR_H
|
||||
|
||||
// Own headers
|
||||
#include "xml.h"
|
||||
#include "BaseGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <tuple>
|
||||
|
||||
class AdaptorGenerator : public BaseGenerator
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Transform xml to adaptor code
|
||||
* @param doc
|
||||
* @param filename
|
||||
* @return 0 if ok
|
||||
*/
|
||||
int transformXmlToFileImpl(const sdbuscpp::xml::Document& doc, const char* filename) const override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Generate source code for interface
|
||||
* @param interface
|
||||
* @return source code
|
||||
*/
|
||||
std::string processInterface(sdbuscpp::xml::Node& interface) const;
|
||||
|
||||
/**
|
||||
* Generate source code for methods
|
||||
* @param methods
|
||||
* @return tuple: registration of methods, declaration of abstract methods
|
||||
*/
|
||||
std::tuple<std::string, std::string> processMethods(const sdbuscpp::xml::Nodes& methods) const;
|
||||
|
||||
/**
|
||||
* Generate source code for signals
|
||||
* @param signals
|
||||
* @return tuple: registration of signals, definition of signal methods
|
||||
*/
|
||||
std::tuple<std::string, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
|
||||
|
||||
/**
|
||||
* Generate source code for properties
|
||||
* @param properties
|
||||
* @return tuple: registration of properties, declaration of property accessor virtual methods
|
||||
*/
|
||||
std::tuple<std::string, std::string> processProperties(const sdbuscpp::xml::Nodes& properties) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //__SDBUSCPP_TOOLS_ADAPTOR_GENERATOR_H
|
158
stub-generator/BaseGenerator.cpp
Normal file
158
stub-generator/BaseGenerator.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file BaseGenerator.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "generator_utils.h"
|
||||
#include "BaseGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
using std::endl;
|
||||
|
||||
using sdbuscpp::xml::Document;
|
||||
using sdbuscpp::xml::Node;
|
||||
using sdbuscpp::xml::Nodes;
|
||||
|
||||
|
||||
int BaseGenerator::transformXmlToFile(const Document& doc, const char* filename) const
|
||||
{
|
||||
return transformXmlToFileImpl(doc, filename);
|
||||
}
|
||||
|
||||
|
||||
int BaseGenerator::writeToFile(const char* filename, const std::string& data) const
|
||||
{
|
||||
std::ofstream file(filename);
|
||||
if (file.bad())
|
||||
{
|
||||
std::cerr << "Unable to write file " << filename << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
file << data;
|
||||
file.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string BaseGenerator::createHeader(const char* filename, const StubType& stubType) const
|
||||
{
|
||||
std::ostringstream head;
|
||||
head << getHeaderComment();
|
||||
|
||||
std::string specialization = stubType == StubType::ADAPTOR ? "adaptor" : "proxy";
|
||||
|
||||
std::string cond_comp{"__sdbuscpp__" + underscorize(filename)
|
||||
+ "__" + specialization + "__H__"};
|
||||
|
||||
head << "#ifndef " << cond_comp << endl
|
||||
<< "#define " << cond_comp << endl << endl;
|
||||
|
||||
head << "#include <sdbus-c++/sdbus-c++.h>" << endl
|
||||
<< "#include <string>" << endl
|
||||
<< "#include <tuple>" << endl
|
||||
<< endl;
|
||||
|
||||
return head.str();
|
||||
}
|
||||
|
||||
std::tuple<unsigned, std::string> BaseGenerator::generateNamespaces(const std::string& ifaceName) const
|
||||
{
|
||||
std::stringstream ss{ifaceName};
|
||||
std::ostringstream body;
|
||||
unsigned count{0};
|
||||
|
||||
// prints the namespaces X and Y defined with <interface name="X.Y.Z">
|
||||
while (ss.str().find('.', ss.tellg()) != std::string::npos)
|
||||
{
|
||||
std::string nspace;
|
||||
getline(ss, nspace, '.');
|
||||
body << "namespace " << nspace << " {" << endl;
|
||||
++count;
|
||||
}
|
||||
body << endl;
|
||||
|
||||
return std::make_tuple(count, body.str());
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string, std::string> BaseGenerator::argsToNamesAndTypes(const Nodes& args) const
|
||||
{
|
||||
std::ostringstream argSS, argTypeSS, typeSS;
|
||||
|
||||
bool firstArg{true};
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (firstArg) firstArg = false; else { argSS << ", "; argTypeSS << ", "; typeSS << ", "; }
|
||||
|
||||
auto argName = arg->get("name");
|
||||
auto type = signature_to_type(arg->get("type"));
|
||||
argSS << argName;
|
||||
argTypeSS << "const " << type << "& " << argName;
|
||||
typeSS << type;
|
||||
}
|
||||
|
||||
return std::make_tuple(argSS.str(), argTypeSS.str(), typeSS.str());
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
std::string BaseGenerator::outArgsToType(const Nodes& args) const
|
||||
{
|
||||
std::ostringstream retTypeSS;
|
||||
|
||||
if (args.size() == 0)
|
||||
{
|
||||
retTypeSS << "void";
|
||||
}
|
||||
else if (args.size() == 1)
|
||||
{
|
||||
const auto& arg = *args.begin();
|
||||
retTypeSS << signature_to_type(arg->get("type"));
|
||||
}
|
||||
else if (args.size() >= 2)
|
||||
{
|
||||
retTypeSS << "std::tuple<";
|
||||
|
||||
bool firstArg = true;
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
if (firstArg) firstArg = false; else retTypeSS << ", ";
|
||||
retTypeSS << signature_to_type(arg->get("type"));
|
||||
}
|
||||
|
||||
retTypeSS << ">";
|
||||
}
|
||||
|
||||
return retTypeSS.str();
|
||||
}
|
||||
|
103
stub-generator/BaseGenerator.h
Normal file
103
stub-generator/BaseGenerator.h
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file BaseGenerator.h
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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 __SDBUSCPP_TOOLS_BASE_GENERATOR_H
|
||||
#define __SDBUSCPP_TOOLS_BASE_GENERATOR_H
|
||||
|
||||
// Own headers
|
||||
#include "xml.h"
|
||||
|
||||
// STL
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
class BaseGenerator
|
||||
{
|
||||
|
||||
public:
|
||||
int transformXmlToFile(const sdbuscpp::xml::Document& doc, const char* filename) const;
|
||||
|
||||
protected:
|
||||
enum class StubType
|
||||
{
|
||||
ADAPTOR,
|
||||
PROXY
|
||||
};
|
||||
|
||||
constexpr static const char *tab = " ";
|
||||
|
||||
virtual ~BaseGenerator() {}
|
||||
|
||||
/**
|
||||
* Implementation of public function that is provided by inherited class
|
||||
* @param doc
|
||||
* @param filename
|
||||
* @return
|
||||
*/
|
||||
virtual int transformXmlToFileImpl(const sdbuscpp::xml::Document& doc, const char* filename) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Write data to file
|
||||
* @param filename Written file
|
||||
* @param data Data to write
|
||||
* @return 0 if ok
|
||||
*/
|
||||
int writeToFile(const char* filename, const std::string& data) const;
|
||||
|
||||
/**
|
||||
* Crete header of file - include guard, includes
|
||||
* @param filename
|
||||
* @param stubType
|
||||
* @return
|
||||
*/
|
||||
std::string createHeader(const char* filename, const StubType& stubType) const;
|
||||
|
||||
/**
|
||||
* Namespaces according to the interface name
|
||||
* @param ifaceName
|
||||
* @return tuple: count of namespaces, string with code
|
||||
*/
|
||||
std::tuple<unsigned, std::string> generateNamespaces(const std::string& ifaceName) const;
|
||||
|
||||
/**
|
||||
* Transform arguments into source code
|
||||
* @param args
|
||||
* @return tuple: argument names, argument types and names, argument types
|
||||
*/
|
||||
std::tuple<std::string, std::string, std::string> argsToNamesAndTypes(const sdbuscpp::xml::Nodes& args) const;
|
||||
|
||||
/**
|
||||
* Output arguments to return type
|
||||
* @param args
|
||||
* @return return type
|
||||
*/
|
||||
std::string outArgsToType(const sdbuscpp::xml::Nodes& args) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //__SDBUSCPP_TOOLS_BASE_GENERATOR_H
|
13
stub-generator/CMakeLists.txt
Normal file
13
stub-generator/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
cmake_minimum_required (VERSION 3.3)
|
||||
|
||||
add_definitions(-std=c++14)
|
||||
|
||||
project (sdbuscpp-xml2cpp)
|
||||
|
||||
add_executable(${PROJECT_NAME} xml2cpp.cpp xml.cpp generator_utils.cpp BaseGenerator.cpp AdaptorGenerator.cpp ProxyGenerator.cpp)
|
||||
|
||||
find_package (EXPAT REQUIRED)
|
||||
|
||||
target_link_libraries (${PROJECT_NAME} ${EXPAT_LIBRARIES})
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME} DESTINATION bin)
|
224
stub-generator/ProxyGenerator.cpp
Normal file
224
stub-generator/ProxyGenerator.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ProxyGenerator.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "generator_utils.h"
|
||||
#include "ProxyGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
|
||||
using std::endl;
|
||||
|
||||
using sdbuscpp::xml::Document;
|
||||
using sdbuscpp::xml::Node;
|
||||
using sdbuscpp::xml::Nodes;
|
||||
|
||||
/**
|
||||
* Generate proxy code - client glue
|
||||
*/
|
||||
int ProxyGenerator::transformXmlToFileImpl(const Document &doc, const char *filename) const
|
||||
{
|
||||
Node &root = *(doc.root);
|
||||
Nodes interfaces = root["interface"];
|
||||
|
||||
std::ostringstream code;
|
||||
code << createHeader(filename, StubType::PROXY);
|
||||
|
||||
for (const auto& interface : interfaces)
|
||||
{
|
||||
code << processInterface(*interface);
|
||||
}
|
||||
|
||||
code << "#endif" << endl;
|
||||
|
||||
return writeToFile(filename, code.str());
|
||||
}
|
||||
|
||||
|
||||
std::string ProxyGenerator::processInterface(Node& interface) const
|
||||
{
|
||||
std::string ifaceName = interface.get("name");
|
||||
std::cout << "Generating proxy code for interface " << ifaceName << endl;
|
||||
|
||||
unsigned int namespacesCount = 0;
|
||||
std::string namespacesStr;
|
||||
std::tie(namespacesCount, namespacesStr) = generateNamespaces(ifaceName);
|
||||
|
||||
std::ostringstream body;
|
||||
body << namespacesStr;
|
||||
|
||||
std::string className = ifaceName.substr(ifaceName.find_last_of(".") + 1)
|
||||
+ "_proxy";
|
||||
|
||||
body << "class " << className << endl
|
||||
<< "{" << endl
|
||||
<< "public:" << endl
|
||||
<< tab << "static constexpr const char* interfaceName = \"" << ifaceName << "\";" << endl << endl
|
||||
<< "protected:" << endl
|
||||
<< tab << className << "(sdbus::IObjectProxy& object)" << endl
|
||||
<< tab << tab << ": object_(object)" << endl;
|
||||
|
||||
Nodes methods = interface["method"];
|
||||
Nodes signals = interface["signal"];
|
||||
Nodes properties = interface["property"];
|
||||
|
||||
std::string registration, declaration;
|
||||
std::tie(registration, declaration) = processSignals(signals);
|
||||
|
||||
body << tab << "{" << endl
|
||||
<< registration
|
||||
<< tab << "}" << endl << endl
|
||||
<< declaration << endl;
|
||||
|
||||
std::string methodDefinitions = processMethods(methods);
|
||||
if (!methodDefinitions.empty())
|
||||
{
|
||||
body << "public:" << endl << methodDefinitions;
|
||||
}
|
||||
|
||||
std::string propertyDefinitions = processProperties(properties);
|
||||
if (!propertyDefinitions.empty())
|
||||
{
|
||||
body << "public:" << endl << propertyDefinitions;
|
||||
}
|
||||
|
||||
body << "private:" << endl
|
||||
<< tab << "sdbus::IObjectProxy& object_;" << endl
|
||||
<< "};" << endl << endl
|
||||
<< std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
|
||||
|
||||
return body.str();
|
||||
}
|
||||
|
||||
std::string ProxyGenerator::processMethods(const Nodes& methods) const
|
||||
{
|
||||
std::ostringstream methodSS;
|
||||
for (const auto& method : methods)
|
||||
{
|
||||
auto name = method->get("name");
|
||||
Nodes args = (*method)["arg"];
|
||||
Nodes inArgs = args.select("direction" , "in");
|
||||
Nodes outArgs = args.select("direction" , "out");
|
||||
|
||||
auto retType = outArgsToType(outArgs);
|
||||
std::string argStr, argTypeStr;
|
||||
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(inArgs);
|
||||
|
||||
methodSS << tab << retType << " " << name << "(" << argTypeStr << ")" << endl
|
||||
<< tab << "{" << endl;
|
||||
|
||||
if (outArgs.size() > 0)
|
||||
{
|
||||
methodSS << tab << tab << retType << " result;" << endl;
|
||||
}
|
||||
|
||||
methodSS << tab << tab << "object_.callMethod(\"" << name << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
|
||||
if (inArgs.size() > 0)
|
||||
{
|
||||
methodSS << ".withArguments(" << argStr << ")";
|
||||
}
|
||||
|
||||
if (outArgs.size() > 0)
|
||||
{
|
||||
methodSS << ".storeResultsTo(result);" << endl
|
||||
<< tab << tab << "return result";
|
||||
}
|
||||
|
||||
methodSS << ";" << endl << tab << "}" << endl << endl;
|
||||
}
|
||||
|
||||
return methodSS.str();
|
||||
}
|
||||
|
||||
|
||||
std::tuple<std::string, std::string> ProxyGenerator::processSignals(const Nodes& signals) const
|
||||
{
|
||||
std::ostringstream registrationSS, declarationSS;
|
||||
|
||||
for (const auto& signal : signals)
|
||||
{
|
||||
auto name = signal->get("name");
|
||||
Nodes args = (*signal)["arg"];
|
||||
|
||||
auto nameBigFirst = name;
|
||||
nameBigFirst[0] = islower(nameBigFirst[0]) ? nameBigFirst[0] + 'A' - 'a' : nameBigFirst[0];
|
||||
|
||||
std::string argStr, argTypeStr;
|
||||
std::tie(argStr, argTypeStr, std::ignore) = argsToNamesAndTypes(args);
|
||||
|
||||
registrationSS << tab << tab << "object_"
|
||||
".uponSignal(\"" << name << "\")"
|
||||
".onInterface(interfaceName)"
|
||||
".call([this](" << argTypeStr << ")"
|
||||
"{ this->on" << nameBigFirst << "(" << argStr << "); });" << endl;
|
||||
|
||||
declarationSS << tab << "virtual void on" << nameBigFirst << "(" << argTypeStr << ") = 0;" << endl;
|
||||
}
|
||||
|
||||
return std::make_tuple(registrationSS.str(), declarationSS.str());
|
||||
}
|
||||
|
||||
std::string ProxyGenerator::processProperties(const Nodes& properties) const
|
||||
{
|
||||
std::ostringstream propertySS;
|
||||
for (const auto& property : properties)
|
||||
{
|
||||
auto propertyName = property->get("name");
|
||||
auto propertyAccess = property->get("access");
|
||||
auto propertySignature = property->get("type");
|
||||
|
||||
auto propertyType = signature_to_type(propertySignature);
|
||||
auto propertyArg = std::string("value");
|
||||
auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
|
||||
|
||||
if (propertyAccess == "read" || propertyAccess == "readwrite")
|
||||
{
|
||||
propertySS << tab << propertyType << " " << propertyName << "()" << endl
|
||||
<< tab << "{" << endl;
|
||||
propertySS << tab << tab << "return object_.getProperty(\"" << propertyName << "\")"
|
||||
".onInterface(interfaceName)";
|
||||
propertySS << ";" << endl << tab << "}" << endl << endl;
|
||||
}
|
||||
|
||||
if (propertyAccess == "readwrite" || propertyAccess == "write")
|
||||
{
|
||||
propertySS << tab << "void " << propertyName << "(" << propertyTypeArg << ")" << endl
|
||||
<< tab << "{" << endl;
|
||||
propertySS << tab << tab << "object_.setProperty(\"" << propertyName << "\")"
|
||||
".onInterface(interfaceName)"
|
||||
".toValue(" << propertyArg << ")";
|
||||
propertySS << ";" << endl << tab << "}" << endl << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return propertySS.str();
|
||||
}
|
81
stub-generator/ProxyGenerator.h
Normal file
81
stub-generator/ProxyGenerator.h
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file ProxyGenerator.h
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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 __SDBUSCPP_TOOLS_PROXY_GENERATOR_H
|
||||
#define __SDBUSCPP_TOOLS_PROXY_GENERATOR_H
|
||||
|
||||
// Own headers
|
||||
#include "xml.h"
|
||||
#include "BaseGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <tuple>
|
||||
|
||||
class ProxyGenerator : public BaseGenerator
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Transform xml to proxy code
|
||||
* @param doc
|
||||
* @param filename
|
||||
* @return 0 if ok
|
||||
*/
|
||||
int transformXmlToFileImpl(const sdbuscpp::xml::Document& doc, const char* filename) const override;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Generate source code for interface
|
||||
* @param interface
|
||||
* @return source code
|
||||
*/
|
||||
std::string processInterface(sdbuscpp::xml::Node& interface) const;
|
||||
|
||||
/**
|
||||
* Generate method calls
|
||||
* @param methods
|
||||
* @return source code
|
||||
*/
|
||||
std::string processMethods(const sdbuscpp::xml::Nodes& methods) const;
|
||||
|
||||
/**
|
||||
* Generate code for handling signals
|
||||
* @param signals
|
||||
* @return tuple: registration, declaration of virtual methods
|
||||
*/
|
||||
std::tuple<std::string, std::string> processSignals(const sdbuscpp::xml::Nodes& signals) const;
|
||||
|
||||
/**
|
||||
* Generate calls for properties
|
||||
* @param properties
|
||||
* @return source code
|
||||
*/
|
||||
std::string processProperties(const sdbuscpp::xml::Nodes& properties) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //__SDBUSCPP_TOOLS_PROXY_GENERATOR_H
|
143
stub-generator/generator_utils.cpp
Normal file
143
stub-generator/generator_utils.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Inspired by: http://dbus-cplusplus.sourceforge.net/
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
|
||||
#include "generator_utils.h"
|
||||
|
||||
|
||||
std::string underscorize(const std::string& str)
|
||||
{
|
||||
std::string res = str;
|
||||
for (unsigned int i = 0; i < res.length(); ++i)
|
||||
{
|
||||
if (!isalpha(res[i]) && !isdigit(res[i]))
|
||||
{
|
||||
res[i] = '_';
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string stub_name(const std::string& name)
|
||||
{
|
||||
return "_" + underscorize(name) + "_stub";
|
||||
}
|
||||
|
||||
const char *atomic_type_to_string(char t)
|
||||
{
|
||||
static std::map<char, const char*> atos
|
||||
{
|
||||
{ 'y', "uint8_t" },
|
||||
{ 'b', "bool" },
|
||||
{ 'n', "int16_t" },
|
||||
{ 'q', "uint16_t" },
|
||||
{ 'i', "int32_t" },
|
||||
{ 'u', "uint32_t" },
|
||||
{ 'x', "int64_t" },
|
||||
{ 't', "uint64_t" },
|
||||
{ 'd', "double" },
|
||||
{ 's', "std::string" },
|
||||
{ 'o', "sdbus::ObjectPath" },
|
||||
{ 'g', "sdbus::Signature" },
|
||||
{ 'v', "sdbus::Variant" },
|
||||
{ '\0', "" }
|
||||
};
|
||||
|
||||
if (atos.count(t))
|
||||
{
|
||||
return atos[t];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void _parse_signature(const std::string &signature, std::string &type, unsigned int &i, bool only_once = false)
|
||||
{
|
||||
for (; i < signature.length(); ++i)
|
||||
{
|
||||
switch (signature[i])
|
||||
{
|
||||
case 'a':
|
||||
{
|
||||
switch (signature[++i])
|
||||
{
|
||||
case '{':
|
||||
{
|
||||
type += "std::map<";
|
||||
++i;
|
||||
_parse_signature(signature, type, i);
|
||||
type += ">";
|
||||
|
||||
break;
|
||||
}
|
||||
case '(':
|
||||
{
|
||||
type += "std::vector<sdbus::Struct<";
|
||||
++i;
|
||||
_parse_signature(signature, type, i);
|
||||
type += ">>";
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
type += "std::vector<";
|
||||
_parse_signature(signature, type, i, true);
|
||||
|
||||
type += ">";
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '(':
|
||||
{
|
||||
type += "sdbus::Struct<";
|
||||
++i;
|
||||
|
||||
_parse_signature(signature, type, i);
|
||||
|
||||
type += ">";
|
||||
break;
|
||||
}
|
||||
case ')':
|
||||
case '}':
|
||||
{
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
const char *atom = atomic_type_to_string(signature[i]);
|
||||
if (!atom)
|
||||
{
|
||||
std::cerr << "Invalid signature: " << signature << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
type += atom;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (only_once)
|
||||
return;
|
||||
|
||||
if (i + 1 < signature.length() && signature[i + 1] != ')' && signature[i + 1] != '}')
|
||||
{
|
||||
type += ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string signature_to_type(const std::string& signature)
|
||||
{
|
||||
std::string type;
|
||||
unsigned int i = 0;
|
||||
_parse_signature(signature, type, i);
|
||||
return type;
|
||||
}
|
22
stub-generator/generator_utils.h
Normal file
22
stub-generator/generator_utils.h
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Inspired by: http://dbus-cplusplus.sourceforge.net/
|
||||
*/
|
||||
|
||||
#ifndef __SDBUSCPP_TOOLS_GENERATOR_UTILS_H
|
||||
#define __SDBUSCPP_TOOLS_GENERATOR_UTILS_H
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
const char *atomic_type_to_string(char t);
|
||||
|
||||
std::string stub_name(const std::string& name);
|
||||
|
||||
std::string signature_to_type(const std::string& signature);
|
||||
|
||||
std::string underscorize(const std::string& str);
|
||||
|
||||
constexpr const char* getHeaderComment() noexcept { return "\n/*\n * This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!\n */\n\n"; }
|
||||
|
||||
#endif //__SDBUSCPP_TOOLS_GENERATOR_UTILS_H
|
309
stub-generator/xml.cpp
Normal file
309
stub-generator/xml.cpp
Normal file
@ -0,0 +1,309 @@
|
||||
/**
|
||||
* Inspired by: http://dbus-cplusplus.sourceforge.net/
|
||||
*/
|
||||
|
||||
#include "xml.h"
|
||||
|
||||
#include <expat.h>
|
||||
|
||||
using namespace sdbuscpp::xml;
|
||||
|
||||
std::istream &operator >> (std::istream& in, Document& doc)
|
||||
{
|
||||
std::stringbuf xmlbuf;
|
||||
in.get(xmlbuf, '\0');
|
||||
doc.from_xml(xmlbuf.str());
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream &operator << (std::ostream& out, const Document& doc)
|
||||
{
|
||||
return out << doc.to_xml();
|
||||
}
|
||||
|
||||
|
||||
Error::Error(const char *error, int line, int column)
|
||||
{
|
||||
std::ostringstream estream;
|
||||
|
||||
estream << "line " << line << ", column " << column << ": " << error;
|
||||
|
||||
m_error = estream.str();
|
||||
}
|
||||
|
||||
Node::Node(const char *n, const char **a)
|
||||
: name(n)
|
||||
{
|
||||
if (a)
|
||||
{
|
||||
for (int i = 0; a[i]; i += 2)
|
||||
{
|
||||
m_attrs[a[i]] = a[i + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Nodes Nodes::operator[](const std::string& key) const
|
||||
{
|
||||
Nodes result;
|
||||
|
||||
for (auto it = begin(), endIt = end(); it != endIt; ++it)
|
||||
{
|
||||
Nodes part = (**it)[key];
|
||||
|
||||
result.insert(result.end(), part.begin(), part.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Nodes Nodes::select(const std::string& attr, const std::string& value) const
|
||||
{
|
||||
Nodes result;
|
||||
|
||||
for (auto it = begin(), itEnd = end(); it != itEnd; ++it)
|
||||
{
|
||||
if ((*it)->get(attr) == value)
|
||||
{
|
||||
result.insert(result.end(), *it);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Nodes Node::operator[](const std::string& key)
|
||||
{
|
||||
Nodes result;
|
||||
|
||||
if (key.length() == 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
for (auto it = children.begin(), itEnd = children.end(); it != itEnd; ++it)
|
||||
{
|
||||
if (it->name == key)
|
||||
{
|
||||
result.push_back(&(*it));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Node::get(const std::string& attribute) const
|
||||
{
|
||||
const auto it = m_attrs.find(attribute);
|
||||
if (it != m_attrs.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void Node::set(const std::string& attribute, std::string value)
|
||||
{
|
||||
if (value.length())
|
||||
{
|
||||
m_attrs[attribute] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_attrs.erase(value);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Node::to_xml() const
|
||||
{
|
||||
std::string xml;
|
||||
int depth = 0;
|
||||
|
||||
_raw_xml(xml, depth);
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
void Node::_raw_xml(std::string& xml, int& depth) const
|
||||
{
|
||||
xml.append(depth * 2, ' ');
|
||||
xml.append("<" + name);
|
||||
|
||||
for (auto it = m_attrs.begin(), itEnd = m_attrs.end(); it != itEnd; ++it)
|
||||
{
|
||||
xml.append(" " + it->first + "=\"" + it->second + "\"");
|
||||
}
|
||||
|
||||
if (cdata.length() == 0 && children.size() == 0)
|
||||
{
|
||||
xml.append("/>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
xml.append(">");
|
||||
|
||||
if (cdata.length())
|
||||
{
|
||||
xml.append(cdata);
|
||||
}
|
||||
|
||||
if (children.size())
|
||||
{
|
||||
xml.append("\n");
|
||||
++depth;
|
||||
|
||||
for (auto it = children.begin(), itEnd = children.end(); it != itEnd; ++it)
|
||||
{
|
||||
it->_raw_xml(xml, depth);
|
||||
}
|
||||
|
||||
--depth;
|
||||
xml.append(depth * 2, ' ');
|
||||
}
|
||||
|
||||
xml.append("</" + name + ">\n");
|
||||
}
|
||||
}
|
||||
|
||||
Document::Document() :
|
||||
root(nullptr),
|
||||
m_depth(0)
|
||||
{
|
||||
}
|
||||
|
||||
Document::Document(const std::string &xml) :
|
||||
root(nullptr),
|
||||
m_depth(0)
|
||||
{
|
||||
from_xml(xml);
|
||||
}
|
||||
|
||||
Document::~Document()
|
||||
{
|
||||
delete root;
|
||||
}
|
||||
|
||||
struct Document::Expat
|
||||
{
|
||||
static void start_doctype_decl_handler(
|
||||
void *data, const XML_Char *name, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset
|
||||
);
|
||||
static void end_doctype_decl_handler(void *data);
|
||||
static void start_element_handler(void *data, const XML_Char *name, const XML_Char **atts);
|
||||
static void character_data_handler(void *data, const XML_Char *chars, int len);
|
||||
static void end_element_handler(void *data, const XML_Char *name);
|
||||
};
|
||||
|
||||
void Document::from_xml(const std::string& xml)
|
||||
{
|
||||
m_depth = 0;
|
||||
if (root)
|
||||
{
|
||||
delete root;
|
||||
}
|
||||
root = nullptr;
|
||||
|
||||
XML_Parser parser = XML_ParserCreate("UTF-8");
|
||||
|
||||
XML_SetUserData(parser, this);
|
||||
|
||||
XML_SetDoctypeDeclHandler(
|
||||
parser,
|
||||
Document::Expat::start_doctype_decl_handler,
|
||||
Document::Expat::end_doctype_decl_handler
|
||||
);
|
||||
|
||||
XML_SetElementHandler(
|
||||
parser,
|
||||
Document::Expat::start_element_handler,
|
||||
Document::Expat::end_element_handler
|
||||
);
|
||||
|
||||
XML_SetCharacterDataHandler(
|
||||
parser,
|
||||
Document::Expat::character_data_handler
|
||||
);
|
||||
|
||||
XML_Status status = XML_Parse(parser, xml.c_str(), xml.length(), true);
|
||||
|
||||
if (status == XML_STATUS_ERROR)
|
||||
{
|
||||
const char* error = XML_ErrorString(XML_GetErrorCode(parser));
|
||||
int line = XML_GetCurrentLineNumber(parser);
|
||||
int column = XML_GetCurrentColumnNumber(parser);
|
||||
|
||||
XML_ParserFree(parser);
|
||||
|
||||
throw Error(error, line, column);
|
||||
}
|
||||
else
|
||||
{
|
||||
XML_ParserFree(parser);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Document::to_xml() const
|
||||
{
|
||||
return root->to_xml();
|
||||
}
|
||||
|
||||
void Document::Expat::start_doctype_decl_handler(
|
||||
void* data,
|
||||
const XML_Char* name,
|
||||
const XML_Char* sysid,
|
||||
const XML_Char *pubid,
|
||||
int has_internal_subset)
|
||||
{
|
||||
}
|
||||
|
||||
void Document::Expat::end_doctype_decl_handler(void* data)
|
||||
{
|
||||
}
|
||||
|
||||
void Document::Expat::start_element_handler(void* data, const XML_Char* name, const XML_Char** atts)
|
||||
{
|
||||
Document* doc = static_cast<Document*>(data);
|
||||
|
||||
if (!doc->root)
|
||||
{
|
||||
doc->root = new Node(name, atts);
|
||||
}
|
||||
else
|
||||
{
|
||||
Node::Children* cld = &(doc->root->children);
|
||||
|
||||
for (int i = 1; i < doc->m_depth; ++i)
|
||||
{
|
||||
cld = &(cld->back().children);
|
||||
}
|
||||
cld->push_back(Node(name, atts));
|
||||
|
||||
}
|
||||
doc->m_depth++;
|
||||
}
|
||||
|
||||
void Document::Expat::character_data_handler(void* data, const XML_Char* chars, int len)
|
||||
{
|
||||
Document* doc = static_cast<Document*>(data);
|
||||
|
||||
Node* nod = doc->root;
|
||||
|
||||
for (int i = 1; i < doc->m_depth; ++i)
|
||||
{
|
||||
nod = &(nod->children.back());
|
||||
}
|
||||
|
||||
int x = 0, y = len - 1;
|
||||
|
||||
while (isspace(chars[y]) && y > 0) --y;
|
||||
while (isspace(chars[x]) && x < y) ++x;
|
||||
|
||||
nod->cdata = std::string(chars, x, y + 1);
|
||||
}
|
||||
|
||||
void Document::Expat::end_element_handler(void* data, const XML_Char* name)
|
||||
{
|
||||
Document* doc = static_cast<Document*>(data);
|
||||
|
||||
doc->m_depth--;
|
||||
}
|
||||
|
113
stub-generator/xml.h
Normal file
113
stub-generator/xml.h
Normal file
@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Inspired by: http://dbus-cplusplus.sourceforge.net/
|
||||
*/
|
||||
|
||||
#ifndef __SDBUSCPP_TOOLS_XML_H
|
||||
#define __SDBUSCPP_TOOLS_XML_H
|
||||
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace sdbuscpp
|
||||
{
|
||||
namespace xml
|
||||
{
|
||||
|
||||
class Error : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
Error(const char *error, int line, int column);
|
||||
|
||||
~Error() {}
|
||||
|
||||
const char *what() const noexcept { return m_error.c_str(); }
|
||||
|
||||
private:
|
||||
std::string m_error;
|
||||
};
|
||||
|
||||
|
||||
class Node;
|
||||
|
||||
class Nodes : public std::vector<Node *>
|
||||
{
|
||||
public:
|
||||
Nodes operator[](const std::string &key) const;
|
||||
Nodes select(const std::string &attr, const std::string &value) const;
|
||||
};
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
|
||||
using Attributes = std::map<std::string, std::string>;
|
||||
using Children = std::vector<Node>;
|
||||
|
||||
std::string name;
|
||||
std::string cdata;
|
||||
Children children;
|
||||
|
||||
Node(std::string n, Attributes a) :
|
||||
name(n),
|
||||
m_attrs(a)
|
||||
{}
|
||||
|
||||
Node(const char* n, const char** a = nullptr);
|
||||
|
||||
Nodes operator[](const std::string& key);
|
||||
|
||||
std::string get(const std::string& attribute) const;
|
||||
|
||||
void set(const std::string &attribute, std::string value);
|
||||
|
||||
std::string to_xml() const;
|
||||
|
||||
Node& add(Node child)
|
||||
{
|
||||
children.push_back(child);
|
||||
return children.back();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void _raw_xml(std::string& xml, int& depth) const;
|
||||
|
||||
Attributes m_attrs;
|
||||
};
|
||||
|
||||
class Document
|
||||
{
|
||||
public:
|
||||
struct Expat;
|
||||
|
||||
Node* root;
|
||||
|
||||
Document();
|
||||
~Document();
|
||||
|
||||
Document(const std::string& xml);
|
||||
|
||||
void from_xml(const std::string& xml);
|
||||
|
||||
std::string to_xml() const;
|
||||
|
||||
private:
|
||||
|
||||
int m_depth;
|
||||
};
|
||||
|
||||
} // namespace xml
|
||||
|
||||
} // namespace sdbuscpp
|
||||
|
||||
std::istream &operator >> (std::istream &, sdbuscpp::xml::Document &);
|
||||
std::ostream &operator << (std::ostream &, sdbuscpp::xml::Document &);
|
||||
|
||||
#endif//__SDBUSCPP_TOOLS_XML_H
|
202
stub-generator/xml2cpp.cpp
Normal file
202
stub-generator/xml2cpp.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file xml2cpp.cpp
|
||||
*
|
||||
* Created on: Feb 1, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "xml.h"
|
||||
#include "AdaptorGenerator.h"
|
||||
#include "ProxyGenerator.h"
|
||||
|
||||
// STL
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using std::endl;
|
||||
using namespace sdbuscpp;
|
||||
|
||||
void usage(std::ostream& output, const char* programName)
|
||||
{
|
||||
output << "Usage: " << programName << " [OPTION]... [FILE]" << endl <<
|
||||
"Creates C++ stubs for DBus API for adaptor and/or client" << endl <<
|
||||
endl <<
|
||||
"Available options:" << endl <<
|
||||
" --proxy=FILE Generate header file FILE with proxy class (client)" << endl <<
|
||||
" --adaptor=FILE Generate header file FILE with stub class (server)" << endl <<
|
||||
" -h, --help " << endl <<
|
||||
" --verbose Explain what is being done" << endl <<
|
||||
endl <<
|
||||
"The stub generator takes an XML file describing DBus interface and creates" << endl <<
|
||||
"C++ header files to be used by C++ code wanting to cumminicate through that" << endl <<
|
||||
"interface. Clients of the interface (those making the calls) need header" << endl <<
|
||||
"created with the --proxy option as this header forwards the calls via DBus" << endl <<
|
||||
"to provider of the service and the returns the result to the caller. Server" << endl <<
|
||||
"implementing the service should derive from interface classes in header" << endl <<
|
||||
"generated for --adaptor option and implement their methods." << endl <<
|
||||
endl <<
|
||||
"When FILE is not specified, standard input is read. Exit status is 0 when" << endl <<
|
||||
"no error was encountered and all requested headers were sucessfully generated." << endl <<
|
||||
"Otherwise 1 is returned." << endl;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char* programName = argv[0];
|
||||
argv++;
|
||||
argc--;
|
||||
|
||||
const char* proxy = nullptr;
|
||||
const char* adaptor = nullptr;
|
||||
const char* xmlFile = nullptr;
|
||||
bool verbose = false;
|
||||
|
||||
while (argc > 0)
|
||||
{
|
||||
if (!strncmp(*argv, "--proxy=", 8))
|
||||
{
|
||||
if (proxy != nullptr)
|
||||
{
|
||||
std::cerr << "Multiple occurrencies of --proxy is not allowed" << endl;
|
||||
usage(std::cerr, programName);
|
||||
return 1;
|
||||
}
|
||||
proxy = *argv + 8;
|
||||
}
|
||||
else if (!strncmp(*argv, "--adaptor=", 10) || !strncmp(*argv, "--adapter=", 10))
|
||||
{
|
||||
if (adaptor != nullptr)
|
||||
{
|
||||
std::cerr << "Multiple occurrencies of --adaptor is not allowed" << endl;
|
||||
usage(std::cerr, programName);
|
||||
return 1;
|
||||
}
|
||||
adaptor = *argv + 10;
|
||||
}
|
||||
else if (!strcmp(*argv, "--help") || !strcmp(*argv, "-h"))
|
||||
{
|
||||
usage(std::cout, programName);
|
||||
return 0;
|
||||
}
|
||||
else if (!strcmp(*argv, "--verbose"))
|
||||
{
|
||||
verbose = true;
|
||||
}
|
||||
else if (**argv == '-')
|
||||
{
|
||||
std::cerr << "Unknown option " << *argv << endl;
|
||||
usage(std::cerr, programName);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (xmlFile != nullptr)
|
||||
{
|
||||
std::cerr << "More than one input file specified: " << *argv << endl;
|
||||
usage(std::cerr, programName);
|
||||
return 1;
|
||||
}
|
||||
xmlFile = *argv;
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
if (!proxy && !adaptor)
|
||||
{
|
||||
std::cerr << "Either --proxy or --adapter need to be specified" << endl;
|
||||
usage(std::cerr, programName);
|
||||
return 1;
|
||||
}
|
||||
|
||||
xml::Document doc;
|
||||
|
||||
try
|
||||
{
|
||||
if (xmlFile != nullptr)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "Reading DBus interface from " << xmlFile << endl;
|
||||
}
|
||||
|
||||
std::ifstream input(xmlFile);
|
||||
|
||||
if (input.bad())
|
||||
{
|
||||
std::cerr << "Unable to open file " << xmlFile << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
input >> doc;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "Reading DBus interface from standard input" << endl;
|
||||
}
|
||||
|
||||
std::cin >> doc;
|
||||
}
|
||||
}
|
||||
catch (const xml::Error& e)
|
||||
{
|
||||
std::cerr << "Parsing error: " << e.what() << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!doc.root)
|
||||
{
|
||||
std::cerr << "Empty document" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (proxy)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "Generating proxy header " << proxy << endl;
|
||||
}
|
||||
ProxyGenerator pg;
|
||||
pg.transformXmlToFile(doc, proxy);
|
||||
}
|
||||
|
||||
if (adaptor)
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "Generating adaptor header " << adaptor << endl;
|
||||
}
|
||||
AdaptorGenerator ag;
|
||||
ag.transformXmlToFile(doc, adaptor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
68
test/Makefile.am
Normal file
68
test/Makefile.am
Normal file
@ -0,0 +1,68 @@
|
||||
# Defines how to build and install libsdbus-c++ tests
|
||||
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
# Target dirs for test binaries, scripts and files
|
||||
testbindir = /opt/test/bin
|
||||
dbusconfdir = $(sysconfdir)/dbus-1/system.d
|
||||
|
||||
# ENABLE_TESTS is defined by configure when user enables tests during configuration
|
||||
if ENABLE_TESTS
|
||||
testbin_PROGRAMS = libsdbus-c++_unittests libsdbus-c++_integrationtests
|
||||
dbusconf_DATA = integrationtests/files/libsdbus-cpp-test.conf
|
||||
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_LDFLAGS = @libsdbus_cpp_LIBS@ @SYSTEMD_LIBS@
|
||||
|
||||
CLEANFILES = *~ *.lo *.la
|
||||
MOSTLYCLEANFILES = *.o
|
||||
|
||||
TESTS =
|
||||
|
||||
# Configuration for libsdbus-c++_unittests
|
||||
libsdbus_c___unittests_SOURCES = \
|
||||
unittests/libsdbus-c++_unittests.cpp \
|
||||
unittests/TypeTraits_test.cpp \
|
||||
unittests/Types_test.cpp \
|
||||
unittests/Message_test.cpp
|
||||
|
||||
libsdbus_c___unittests_LDFLAGS = -L$(top_builddir)/src
|
||||
|
||||
libsdbus_c___unittests_LDADD = \
|
||||
-lsdbus-c++ \
|
||||
@libsdbus_cpp_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
-lgmock
|
||||
|
||||
TESTS += libsdbus-c++_unittests
|
||||
|
||||
# Configuration for libsdbus-c++_integrationtests
|
||||
libsdbus_c___integrationtests_SOURCES = \
|
||||
integrationtests/libsdbus-c++_integrationtests.cpp \
|
||||
integrationtests/Connection_test.cpp \
|
||||
integrationtests/AdaptorAndProxy_test.cpp
|
||||
|
||||
libsdbus_c___integrationtests_LDFLAGS = -L$(top_builddir)/src
|
||||
|
||||
libsdbus_c___integrationtests_LDADD = \
|
||||
-lsdbus-c++ \
|
||||
@libsdbus_cpp_LIBS@ \
|
||||
@SYSTEMD_LIBS@ \
|
||||
-lgmock \
|
||||
-lpthread
|
||||
|
||||
TESTS += libsdbus-c++_integrationtests
|
||||
|
||||
check_PROGRAMS = libsdbus-c++_unittests libsdbus-c++_integrationtests
|
||||
|
||||
DISTCLEANFILES = Makefile Makefile.in
|
||||
|
||||
# Post-build action: executing tests from the IDE
|
||||
if ENABLE_TESTS
|
||||
all-local: libsdbus-c++_unittests libsdbus-c++_integrationtests
|
||||
if [ "${UNIT_TESTS_RUNNER}" ]; then "${UNIT_TESTS_RUNNER}" --deviceip="${TEST_DEVICE_IP}" --testbin=.libs/libsdbus-c++_unittests; fi; exit 0
|
||||
if [ "${UNIT_TESTS_RUNNER}" ]; then "${UNIT_TESTS_RUNNER}" --deviceip="${TEST_DEVICE_IP}" --testbin=.libs/libsdbus-c++_integrationtests; fi; exit 0
|
||||
endif
|
289
test/integrationtests/AdaptorAndProxy_test.cpp
Normal file
289
test/integrationtests/AdaptorAndProxy_test.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file AdaptorAndProxy_test.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "Connection.h"
|
||||
|
||||
#include "TestingAdaptor.h"
|
||||
#include "TestingProxy.h"
|
||||
|
||||
// sdbus
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
// gmock
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
// STL
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::Gt;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AdaptorAndProxyFixture : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
m_connection.requestName(INTERFACE_NAME);
|
||||
m_connection.enterProcessingLoopAsync();
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
m_connection.leaveProcessingLoop();
|
||||
m_connection.releaseName(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
private:
|
||||
void SetUp() override
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_proxy.reset();
|
||||
m_adaptor.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
static sdbus::internal::Connection m_connection;
|
||||
|
||||
std::unique_ptr<TestingAdaptor> m_adaptor;
|
||||
std::unique_ptr<TestingProxy> m_proxy;
|
||||
};
|
||||
|
||||
sdbus::internal::Connection AdaptorAndProxyFixture::m_connection{sdbus::internal::Connection::BusType::eSystem};
|
||||
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AdaptorAndProxy, CanBeConstructedSuccesfully)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(INTERFACE_NAME);
|
||||
|
||||
ASSERT_NO_THROW(TestingAdaptor adaptor(*connection));
|
||||
ASSERT_NO_THROW(TestingProxy proxy(INTERFACE_NAME, OBJECT_PATH));
|
||||
|
||||
connection->releaseName(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
// Methods
|
||||
|
||||
using SdbusTestObject = AdaptorAndProxyFixture;
|
||||
|
||||
TEST_F(SdbusTestObject, CallsEmptyMethodSuccesfully)
|
||||
{
|
||||
ASSERT_NO_THROW(m_proxy->noArgNoReturn());
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithBaseTypesSuccesfully)
|
||||
{
|
||||
auto resInt = m_proxy->getInt();
|
||||
ASSERT_THAT(resInt, Eq(INT32_VALUE));
|
||||
|
||||
auto multiplyRes = m_proxy->multiply(INT64_VALUE, DOUBLE_VALUE);
|
||||
ASSERT_THAT(multiplyRes, Eq(INT64_VALUE * DOUBLE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithTuplesSuccesfully)
|
||||
{
|
||||
auto resTuple = m_proxy->getTuple();
|
||||
ASSERT_THAT(std::get<0>(resTuple), Eq(UINT32_VALUE));
|
||||
ASSERT_THAT(std::get<1>(resTuple), Eq(STRING_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodsWithStructSuccesfully)
|
||||
{
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> a{};
|
||||
auto vectorRes = m_proxy->getInts16FromStruct(a);
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{0})); // because second item is by default initialized to 0
|
||||
|
||||
|
||||
sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>> b{
|
||||
UINT8_VALUE, INT16_VALUE, DOUBLE_VALUE, STRING_VALUE, {INT16_VALUE, -INT16_VALUE}
|
||||
};
|
||||
vectorRes = m_proxy->getInts16FromStruct(b);
|
||||
ASSERT_THAT(vectorRes, Eq(std::vector<int16_t>{INT16_VALUE, INT16_VALUE, -INT16_VALUE}));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithVariantSuccesfully)
|
||||
{
|
||||
sdbus::Variant v{DOUBLE_VALUE};
|
||||
auto variantRes = m_proxy->processVariant(v);
|
||||
ASSERT_THAT(variantRes.get<int32_t>(), Eq(static_cast<int32_t>(DOUBLE_VALUE)));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithStructVariantsAndGetMapSuccesfully)
|
||||
{
|
||||
std::vector<int32_t> x{-2, 0, 2};
|
||||
sdbus::Struct<sdbus::Variant, sdbus::Variant> y{false, true};
|
||||
auto mapOfVariants = m_proxy->getMapOfVariants(x, y);
|
||||
decltype(mapOfVariants) res{{-2, false}, {0, false}, {2, true}};
|
||||
|
||||
ASSERT_THAT(mapOfVariants[-2].get<bool>(), Eq(res[-2].get<bool>()));
|
||||
ASSERT_THAT(mapOfVariants[0].get<bool>(), Eq(res[0].get<bool>()));
|
||||
ASSERT_THAT(mapOfVariants[2].get<bool>(), Eq(res[2].get<bool>()));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithStructInStructSuccesfully)
|
||||
{
|
||||
auto val = m_proxy->getStructInStruct();
|
||||
ASSERT_THAT(val.get<0>(), Eq(STRING_VALUE));
|
||||
ASSERT_THAT(std::get<0>(std::get<1>(val))[INT32_VALUE], Eq(INT32_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoStructsSuccesfully)
|
||||
{
|
||||
auto val = m_proxy->sumStructItems({1, 2}, {3, 4});
|
||||
ASSERT_THAT(val, Eq(1 + 2 + 3 + 4));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithTwoVectorsSuccesfully)
|
||||
{
|
||||
auto val = m_proxy->sumVectorItems({1, 7}, {2, 3});
|
||||
ASSERT_THAT(val, Eq(1 + 7 + 2 + 3));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithSignatureSuccesfully)
|
||||
{
|
||||
auto resSignature = m_proxy->getSignature();
|
||||
ASSERT_THAT(resSignature, Eq(SIGNATURE_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithObjectPathSuccesfully)
|
||||
{
|
||||
auto resObjectPath = m_proxy->getObjectPath();
|
||||
ASSERT_THAT(resObjectPath, Eq(OBJECT_PATH_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CallsMethodWithComplexTypeSuccesfully)
|
||||
{
|
||||
auto resComplex = m_proxy->getComplex();
|
||||
ASSERT_THAT(resComplex.count(0), Eq(1));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingNonexistentMethod)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callNonexistentMethod(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentInterface)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->callMethodOnNonexistentInterface(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentDestination)
|
||||
{
|
||||
TestingProxy proxy("wrongDestination", OBJECT_PATH);
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, FailsCallingMethodOnNonexistentObject)
|
||||
{
|
||||
TestingProxy proxy(INTERFACE_NAME, "/wrong/path");
|
||||
ASSERT_THROW(proxy.getInt(), sdbus::Error);
|
||||
}
|
||||
|
||||
// Signals
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSimpleSignalSuccesfully)
|
||||
{
|
||||
auto count = m_proxy->getSimpleCallCount();
|
||||
m_adaptor->simpleSignal();
|
||||
usleep(10000);
|
||||
|
||||
ASSERT_THAT(m_proxy->getSimpleCallCount(), Eq(count + 1));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithMapSuccesfully)
|
||||
{
|
||||
m_adaptor->signalWithMap({{0, "zero"}, {1, "one"}});
|
||||
usleep(10000);
|
||||
|
||||
auto map = m_proxy->getMap();
|
||||
ASSERT_THAT(map[0], Eq("zero"));
|
||||
ASSERT_THAT(map[1], Eq("one"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithVariantSuccesfully)
|
||||
{
|
||||
double d = 3.14;
|
||||
m_adaptor->signalWithVariant(3.14);
|
||||
usleep(10000);
|
||||
|
||||
ASSERT_THAT(m_proxy->getVariantValue(), d);
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, EmitsSignalWithoutRegistrationSuccesfully)
|
||||
{
|
||||
m_adaptor->signalWithoutRegistration({"platform", {"av"}});
|
||||
usleep(10000);
|
||||
|
||||
auto signature = m_proxy->getSignatureFromSignal();
|
||||
ASSERT_THAT(signature["platform"], Eq("av"));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, failsEmitingSignalOnNonexistentInterface)
|
||||
{
|
||||
ASSERT_THROW(m_adaptor->emitSignalOnNonexistentInterface(), sdbus::Error);
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
TEST_F(SdbusTestObject, ReadsReadPropertySuccesfully)
|
||||
{
|
||||
ASSERT_THAT(m_proxy->state(), Eq(STRING_VALUE));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, WritesAndReadsReadWritePropertySuccesfully)
|
||||
{
|
||||
auto x = 42;
|
||||
ASSERT_NO_THROW(m_proxy->action(x));
|
||||
ASSERT_THAT(m_proxy->action(), Eq(x));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, WritesToWritePropertySuccesfully)
|
||||
{
|
||||
auto x = true;
|
||||
ASSERT_NO_THROW(m_proxy->blocking(x));
|
||||
}
|
||||
|
||||
TEST_F(SdbusTestObject, CannotReadFromWriteProperty)
|
||||
{
|
||||
ASSERT_THROW(m_proxy->blocking(), sdbus::Error);
|
||||
}
|
91
test/integrationtests/Connection_test.cpp
Normal file
91
test/integrationtests/Connection_test.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Connection_test.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
// Own
|
||||
#include "defs.h"
|
||||
|
||||
// sdbus
|
||||
#include <sdbus-c++/Error.h>
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
|
||||
// gmock
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
// STL
|
||||
#include <thread>
|
||||
|
||||
using ::testing::Eq;
|
||||
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(Connection, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(sdbus::createConnection());
|
||||
}
|
||||
|
||||
TEST(Connection, CanRequestRegisteredDbusName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
ASSERT_NO_THROW(connection->requestName(INTERFACE_NAME));
|
||||
connection->releaseName(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
TEST(Connection, CannotRequestNonregisteredDbusName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->requestName("some_random_not_supported_dbus_name"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanReleasedRequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
|
||||
connection->requestName(INTERFACE_NAME);
|
||||
ASSERT_NO_THROW(connection->releaseName(INTERFACE_NAME));
|
||||
}
|
||||
|
||||
TEST(Connection, CannotReleaseNonrequestedName)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
ASSERT_THROW(connection->releaseName("some_random_nonrequested_name"), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(Connection, CanEnterAndLeaveProcessingLoop)
|
||||
{
|
||||
auto connection = sdbus::createConnection();
|
||||
connection->requestName(INTERFACE_NAME);
|
||||
|
||||
std::thread t([&](){ connection->enterProcessingLoop(); });
|
||||
connection->leaveProcessingLoop();
|
||||
|
||||
t.join();
|
||||
|
||||
connection->releaseName(INTERFACE_NAME);
|
||||
}
|
146
test/integrationtests/TestingAdaptor.h
Normal file
146
test/integrationtests/TestingAdaptor.h
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file TestingAdaptor.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGADAPTOR_H_
|
||||
|
||||
#include "adaptor-glue.h"
|
||||
|
||||
class TestingAdaptor : public sdbus::Interfaces<testing_adaptor>
|
||||
{
|
||||
public:
|
||||
TestingAdaptor(sdbus::IConnection& connection) :
|
||||
sdbus::Interfaces<::testing_adaptor>(connection, OBJECT_PATH) { }
|
||||
|
||||
virtual ~TestingAdaptor() { }
|
||||
|
||||
protected:
|
||||
|
||||
void noArgNoReturn() const { }
|
||||
|
||||
int32_t getInt() const { return INT32_VALUE; }
|
||||
|
||||
std::tuple<uint32_t, std::string> getTuple() const { return std::make_tuple(UINT32_VALUE, STRING_VALUE); }
|
||||
|
||||
double multiply(const int64_t& a, const double& b) const { return a * b; }
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const
|
||||
{
|
||||
std::vector<int16_t> res{x.get<1>()};
|
||||
auto y = std::get<std::vector<int16_t>>(x);
|
||||
res.insert(res.end(), y.begin(), y.end());
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Variant processVariant(sdbus::Variant& v)
|
||||
{
|
||||
sdbus::Variant res = static_cast<int32_t>(v.get<double>());
|
||||
return res;
|
||||
}
|
||||
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const
|
||||
{
|
||||
std::map<int32_t, sdbus::Variant> res;
|
||||
for (auto item : x)
|
||||
{
|
||||
res[item] = (item <= 0) ? std::get<0>(y) : std::get<1>(y);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
{
|
||||
int32_t res{0};
|
||||
res += std::get<0>(a) + std::get<1>(a);
|
||||
res += std::get<0>(b) + std::get<1>(b);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
|
||||
{
|
||||
uint32_t res{0};
|
||||
for (auto x : a)
|
||||
{
|
||||
res += x;
|
||||
}
|
||||
for (auto x : b)
|
||||
{
|
||||
res += x;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
sdbus::Signature getSignature() const { return SIGNATURE_VALUE; }
|
||||
sdbus::ObjectPath getObjectPath() const { return OBJECT_PATH_VALUE; }
|
||||
|
||||
ComplexType getComplex() const
|
||||
{
|
||||
return { // map
|
||||
{
|
||||
0, // uint_64_t
|
||||
{ // struct
|
||||
{ // map
|
||||
{
|
||||
'a', // uint8_t
|
||||
{ // vector
|
||||
{ // struct
|
||||
"/object/path", // object path
|
||||
false,
|
||||
3.14,
|
||||
{ // map
|
||||
{0, "zero"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"a{t(a{ya(obva{is})}gs)}", // signature
|
||||
""
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::string state() { return STRING_VALUE; }
|
||||
uint32_t action() { return m_action; }
|
||||
void action(const uint32_t& value) { m_action = value; }
|
||||
bool blocking() { return m_blocking; }
|
||||
void blocking(const bool& value) { m_blocking = value; }
|
||||
|
||||
private:
|
||||
uint32_t m_action;
|
||||
bool m_blocking;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* INTEGRATIONTESTS_TESTINGADAPTOR_H_ */
|
66
test/integrationtests/TestingProxy.h
Normal file
66
test/integrationtests/TestingProxy.h
Normal file
@ -0,0 +1,66 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file TestingProxy.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_
|
||||
|
||||
#include "proxy-glue.h"
|
||||
|
||||
class TestingProxy : public sdbus::ProxyInterfaces<::testing_proxy>
|
||||
{
|
||||
public:
|
||||
using sdbus::ProxyInterfaces<::testing_proxy>::ProxyInterfaces;
|
||||
|
||||
int getSimpleCallCount() const { return m_simpleCallCounter; }
|
||||
std::map<int32_t, std::string> getMap() const { return m_map; }
|
||||
double getVariantValue() const { return m_variantValue; }
|
||||
std::map<std::string, std::string> getSignatureFromSignal() const { return m_signature; }
|
||||
|
||||
protected:
|
||||
void onSimpleSignal() override { ++m_simpleCallCounter; }
|
||||
|
||||
void onSignalWithMap(const std::map<int32_t, std::string>& m) override { m_map = m; }
|
||||
|
||||
void onSignalWithVariant(const sdbus::Variant& v) override
|
||||
{
|
||||
m_variantValue = v.get<double>();
|
||||
}
|
||||
|
||||
void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) override
|
||||
{
|
||||
m_signature[std::get<0>(s)] = static_cast<std::string>(std::get<0>(std::get<1>(s)));
|
||||
}
|
||||
|
||||
private:
|
||||
int m_simpleCallCounter{};
|
||||
std::map<int32_t, std::string> m_map;
|
||||
double m_variantValue;
|
||||
std::map<std::string, std::string> m_signature;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_TESTINGPROXY_H_ */
|
156
test/integrationtests/adaptor-glue.h
Normal file
156
test/integrationtests/adaptor-glue.h
Normal file
@ -0,0 +1,156 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file adaptor-glue.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
// sdbus
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
using ComplexType = std::map<
|
||||
uint64_t,
|
||||
sdbus::Struct<
|
||||
std::map<
|
||||
uint8_t,
|
||||
std::vector<
|
||||
sdbus::Struct<
|
||||
sdbus::ObjectPath,
|
||||
bool,
|
||||
sdbus::Variant,
|
||||
std::map<int, std::string>
|
||||
>
|
||||
>
|
||||
>,
|
||||
sdbus::Signature,
|
||||
std::string // char* leads to type and memory issues, std::string is best choice
|
||||
>
|
||||
>;
|
||||
|
||||
class testing_adaptor
|
||||
{
|
||||
|
||||
protected:
|
||||
testing_adaptor(sdbus::IObject& object) :
|
||||
object_(object)
|
||||
{
|
||||
object_.registerMethod("noArgNoReturn").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->noArgNoReturn(); });
|
||||
object_.registerMethod("getInt").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getInt(); });
|
||||
object_.registerMethod("getTuple").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getTuple(); });
|
||||
|
||||
object_.registerMethod("multiply").onInterface(INTERFACE_NAME).implementedAs([this](const int64_t& a, const double& b){ return this->multiply(a, b); });
|
||||
object_.registerMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x){ return this->getInts16FromStruct(x); });
|
||||
|
||||
object_.registerMethod("processVariant").onInterface(INTERFACE_NAME).implementedAs([this](sdbus::Variant& v){ return this->processVariant(v); });
|
||||
|
||||
object_.registerMethod("getMapOfVariants").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y){ return this->getMapOfVariants(x ,y); });
|
||||
|
||||
object_.registerMethod("getStructInStruct").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getStructInStruct(); });
|
||||
|
||||
object_.registerMethod("sumStructItems").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b){
|
||||
return this->sumStructItems(a, b);
|
||||
});
|
||||
|
||||
object_.registerMethod("sumVectorItems").onInterface(INTERFACE_NAME).implementedAs([this](
|
||||
const std::vector<uint16_t>& a, const std::vector<uint64_t>& b){
|
||||
return this->sumVectorItems(a, b);
|
||||
});
|
||||
|
||||
object_.registerMethod("getSignature").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getSignature(); });
|
||||
object_.registerMethod("getObjectPath").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getObjectPath(); });
|
||||
|
||||
object_.registerMethod("getComplex").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->getComplex(); });
|
||||
|
||||
// registration of signals is optional, it is useful because of introspection
|
||||
object_.registerSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
object_.registerSignal("signalWithMap").onInterface(INTERFACE_NAME).withParameters<std::map<int32_t, std::string>>();
|
||||
object_.registerSignal("signalWithVariant").onInterface(INTERFACE_NAME).withParameters<sdbus::Variant>();
|
||||
|
||||
object_.registerProperty("state").onInterface(INTERFACE_NAME).withGetter([this](){ return this->state(); });
|
||||
object_.registerProperty("action").onInterface(INTERFACE_NAME).withGetter([this](){ return this->action(); }).withSetter([this](const uint32_t& value){ this->action(value); });
|
||||
object_.registerProperty("blocking").onInterface(INTERFACE_NAME)./*withGetter([this](){ return this->blocking(); }).*/withSetter([this](const bool& value){ this->blocking(value); });
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
void simpleSignal()
|
||||
{
|
||||
object_.emitSignal("simpleSignal").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void signalWithMap(const std::map<int32_t, std::string>& map)
|
||||
{
|
||||
object_.emitSignal("signalWithMap").onInterface(INTERFACE_NAME).withArguments(map);
|
||||
}
|
||||
|
||||
void signalWithVariant(const sdbus::Variant& v)
|
||||
{
|
||||
object_.emitSignal("signalWithVariant").onInterface(INTERFACE_NAME).withArguments(v);
|
||||
}
|
||||
|
||||
void signalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
{
|
||||
object_.emitSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).withArguments(s);
|
||||
}
|
||||
|
||||
void emitSignalOnNonexistentInterface()
|
||||
{
|
||||
object_.emitSignal("simpleSignal").onInterface("interfaceThatDoesNotExists");
|
||||
}
|
||||
|
||||
private:
|
||||
sdbus::IObject& object_;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void noArgNoReturn() const = 0;
|
||||
virtual int32_t getInt() const = 0;
|
||||
virtual std::tuple<uint32_t, std::string> getTuple() const = 0;
|
||||
virtual double multiply(const int64_t& a, const double& b) const = 0;
|
||||
virtual std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x) const = 0;
|
||||
virtual sdbus::Variant processVariant(sdbus::Variant& v) = 0;
|
||||
virtual std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y) const = 0;
|
||||
virtual sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct() const = 0;
|
||||
virtual int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b) = 0;
|
||||
virtual uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b) = 0;
|
||||
virtual sdbus::Signature getSignature() const = 0;
|
||||
virtual sdbus::ObjectPath getObjectPath() const = 0;
|
||||
virtual ComplexType getComplex() const = 0;
|
||||
|
||||
virtual std::string state() = 0;
|
||||
virtual uint32_t action() = 0;
|
||||
virtual void action(const uint32_t& value) = 0;
|
||||
virtual bool blocking() = 0;
|
||||
virtual void blocking(const bool& value) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_ADAPTOR_GLUE_H_ */
|
46
test/integrationtests/defs.h
Normal file
46
test/integrationtests/defs.h
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file defs.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_
|
||||
|
||||
#include "sdbus-c++/Types.h"
|
||||
|
||||
const std::string INTERFACE_NAME{"com.kistler.testsdbuscpp"};
|
||||
const std::string OBJECT_PATH{"/"};
|
||||
|
||||
constexpr const uint8_t UINT8_VALUE{1};
|
||||
constexpr const int16_t INT16_VALUE{21};
|
||||
constexpr const uint32_t UINT32_VALUE{42};
|
||||
constexpr const int32_t INT32_VALUE{-42};
|
||||
constexpr const int32_t INT64_VALUE{-1024};
|
||||
|
||||
const std::string STRING_VALUE{"sdbus-c++-testing"};
|
||||
const sdbus::Signature SIGNATURE_VALUE{"a{is}"};
|
||||
const sdbus::ObjectPath OBJECT_PATH_VALUE{"/"};
|
||||
|
||||
constexpr const double DOUBLE_VALUE{3.24L};
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_DEFS_H_ */
|
17
test/integrationtests/files/libsdbus-cpp-test.conf
Normal file
17
test/integrationtests/files/libsdbus-cpp-test.conf
Normal file
@ -0,0 +1,17 @@
|
||||
<!-- This configuration file specifies the required security policies
|
||||
for the Kistler DBUS example to run core daemon to work. -->
|
||||
|
||||
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
|
||||
<!-- ../system.conf have denied everything, so we just punch some holes -->
|
||||
|
||||
<policy context="default">
|
||||
<allow own="com.kistler.testsdbuscpp"/>
|
||||
<allow send_destination="com.kistler.testsdbuscpp"/>
|
||||
<allow send_interface="com.kistler.testsdbuscpp"/>
|
||||
</policy>
|
||||
|
||||
</busconfig>
|
||||
|
33
test/integrationtests/libsdbus-c++_integrationtests.cpp
Normal file
33
test/integrationtests/libsdbus-c++_integrationtests.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file libsdbus-c++_integrationtests.cpp
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* 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 "gmock/gmock.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
190
test/integrationtests/proxy-glue.h
Normal file
190
test/integrationtests/proxy-glue.h
Normal file
@ -0,0 +1,190 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file proxy-glue.h
|
||||
*
|
||||
* Created on: Jan 2, 2017
|
||||
* Project: sdbus-c++
|
||||
* Description: High-level D-Bus IPC C++ library based on sd-bus
|
||||
*
|
||||
* This file is part of sdbus-c++.
|
||||
*
|
||||
* sdbus-c++ is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* sdbus-c++ is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_
|
||||
#define SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
// sdbus
|
||||
#include "sdbus-c++/sdbus-c++.h"
|
||||
|
||||
class testing_proxy
|
||||
{
|
||||
protected:
|
||||
testing_proxy(sdbus::IObjectProxy& object) :
|
||||
object_(object)
|
||||
{
|
||||
object_.uponSignal("simpleSignal").onInterface(INTERFACE_NAME).call([this](){ this->onSimpleSignal(); });
|
||||
object_.uponSignal("signalWithMap").onInterface(INTERFACE_NAME).call([this](const std::map<int32_t, std::string>& map){ this->onSignalWithMap(map); });
|
||||
object_.uponSignal("signalWithVariant").onInterface(INTERFACE_NAME).call([this](const sdbus::Variant& v){ this->onSignalWithVariant(v); });
|
||||
|
||||
object_.uponSignal("signalWithoutRegistration").onInterface(INTERFACE_NAME).call([this](const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s)
|
||||
{ this->onSignalWithoutRegistration(s); });
|
||||
}
|
||||
|
||||
|
||||
virtual void onSimpleSignal() = 0;
|
||||
virtual void onSignalWithMap(const std::map<int32_t, std::string>& map) = 0;
|
||||
virtual void onSignalWithVariant(const sdbus::Variant& v) = 0;
|
||||
virtual void onSignalWithoutRegistration(const sdbus::Struct<std::string, sdbus::Struct<sdbus::Signature>>& s) = 0;
|
||||
|
||||
public:
|
||||
void noArgNoReturn()
|
||||
{
|
||||
object_.callMethod("noArgNoReturn").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
int32_t getInt()
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("getInt").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<uint32_t, std::string> getTuple()
|
||||
{
|
||||
std::tuple<uint32_t, std::string> result;
|
||||
object_.callMethod("getTuple").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
double multiply(const int64_t& a, const double& b)
|
||||
{
|
||||
double result;
|
||||
object_.callMethod("multiply").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int16_t> getInts16FromStruct(const sdbus::Struct<uint8_t, int16_t, double, std::string, std::vector<int16_t>>& x)
|
||||
{
|
||||
std::vector<int16_t> result;
|
||||
object_.callMethod("getInts16FromStruct").onInterface(INTERFACE_NAME).withArguments(x).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Variant processVariant(const sdbus::Variant& v)
|
||||
{
|
||||
sdbus::Variant result;
|
||||
object_.callMethod("processVariant").onInterface(INTERFACE_NAME).withArguments(v).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<int32_t, sdbus::Variant> getMapOfVariants(const std::vector<int32_t>& x, const sdbus::Struct<sdbus::Variant, sdbus::Variant>& y)
|
||||
{
|
||||
std::map<int32_t, sdbus::Variant> result;
|
||||
object_.callMethod("getMapOfVariants").onInterface(INTERFACE_NAME).withArguments(x, y).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> getStructInStruct()
|
||||
{
|
||||
sdbus::Struct<std::string, sdbus::Struct<std::map<int32_t, int32_t>>> result;
|
||||
object_.callMethod("getStructInStruct").onInterface(INTERFACE_NAME).withArguments().storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t sumStructItems(const sdbus::Struct<uint8_t, uint16_t>& a, const sdbus::Struct<int32_t, int64_t>& b)
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("sumStructItems").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t sumVectorItems(const std::vector<uint16_t>& a, const std::vector<uint64_t>& b)
|
||||
{
|
||||
uint32_t result;
|
||||
object_.callMethod("sumVectorItems").onInterface(INTERFACE_NAME).withArguments(a, b).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::Signature getSignature()
|
||||
{
|
||||
sdbus::Signature result;
|
||||
object_.callMethod("getSignature").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
sdbus::ObjectPath getObjectPath()
|
||||
{
|
||||
sdbus::ObjectPath result;
|
||||
object_.callMethod("getObjectPath").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
ComplexType getComplex()
|
||||
{
|
||||
ComplexType result;
|
||||
object_.callMethod("getComplex").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t callNonexistentMethod()
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("callNonexistentMethod").onInterface(INTERFACE_NAME).storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t callMethodOnNonexistentInterface()
|
||||
{
|
||||
int32_t result;
|
||||
object_.callMethod("someMethod").onInterface("interfaceThatDoesNotExist").storeResultsTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string state()
|
||||
{
|
||||
return object_.getProperty("state").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
uint32_t action()
|
||||
{
|
||||
return object_.getProperty("action").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void action(const uint32_t& value)
|
||||
{
|
||||
object_.setProperty("action").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
bool blocking()
|
||||
{
|
||||
return object_.getProperty("blocking").onInterface(INTERFACE_NAME);
|
||||
}
|
||||
|
||||
void blocking(const bool& value)
|
||||
{
|
||||
object_.setProperty("blocking").onInterface(INTERFACE_NAME).toValue(value);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
sdbus::IObjectProxy& object_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif /* SDBUS_CPP_INTEGRATIONTESTS_PROXY_GLUE_H_ */
|
230
test/unittests/Message_test.cpp
Normal file
230
test/unittests/Message_test.cpp
Normal file
@ -0,0 +1,230 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Message_test.cpp
|
||||
*
|
||||
* Created on: Dec 3, 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++/Types.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::DoubleEq;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string deserializeString(sdbus::Message& msg)
|
||||
{
|
||||
std::string str;
|
||||
msg >> str;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AMessage, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(sdbus::Message());
|
||||
}
|
||||
|
||||
TEST(AMessage, IsInvalidAfterDefaultConstructed)
|
||||
{
|
||||
sdbus::Message msg;
|
||||
|
||||
ASSERT_FALSE(msg.isValid());
|
||||
}
|
||||
|
||||
TEST(AMessage, IsValidWhenConstructedAsRealMessage)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
ASSERT_TRUE(msg.isValid());
|
||||
}
|
||||
|
||||
TEST(AMessage, CreatesShallowCopyWhenCopyConstructed)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
msg << "I am a string"s;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Message msgCopy = msg;
|
||||
|
||||
std::string str;
|
||||
msgCopy >> str;
|
||||
|
||||
ASSERT_THAT(str, Eq("I am a string"));
|
||||
ASSERT_THROW(msgCopy >> str, sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(AMessage, CreatesDeepCopyWhenEplicitlyCopied)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
msg << "I am a string"s;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Message msgCopy{sdbus::createPlainMessage()};
|
||||
msg.copyTo(msgCopy, true);
|
||||
msgCopy.seal(); // Seal to be able to read from it subsequently
|
||||
msg.rewind(true); // Rewind to the beginning after copying
|
||||
|
||||
ASSERT_THAT(deserializeString(msg), Eq("I am a string"));
|
||||
ASSERT_THAT(deserializeString(msgCopy), Eq("I am a string"));
|
||||
}
|
||||
|
||||
TEST(AMessage, IsEmptyWhenContainsNoValue)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
ASSERT_TRUE(msg.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AMessage, IsNotEmptyWhenContainsAValue)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
msg << "I am a string"s;
|
||||
|
||||
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()};
|
||||
|
||||
int dataWritten = 5;
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
int dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryAVariant)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
auto dataWritten = sdbus::Variant((double)3.14);
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Variant dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead.get<double>(), Eq(dataWritten.get<double>()));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryACollectionOfEmbeddedVariants)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
auto value = std::vector<sdbus::Variant>{"hello"s, (double)3.14};
|
||||
auto dataWritten = sdbus::Variant{value};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
sdbus::Variant dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead.get<decltype(value)>()[0].get<std::string>(), Eq(value[0].get<std::string>()));
|
||||
ASSERT_THAT(dataRead.get<decltype(value)>()[1].get<double>(), Eq(value[1].get<double>()));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryAnArray)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
std::vector<int64_t> dataWritten{3545342, 43643532, 324325};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::vector<int64_t> dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryADictionary)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
std::map<int, std::string> dataWritten{{1, "one"}, {2, "two"}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
std::map<int, std::string> dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
||||
|
||||
TEST(AMessage, CanCarryAComplexType)
|
||||
{
|
||||
sdbus::Message msg{sdbus::createPlainMessage()};
|
||||
|
||||
using ComplexType = std::map<
|
||||
uint64_t,
|
||||
sdbus::Struct<
|
||||
std::map<
|
||||
uint8_t,
|
||||
std::vector<
|
||||
sdbus::Struct<
|
||||
sdbus::ObjectPath,
|
||||
bool,
|
||||
int16_t,
|
||||
/*sdbus::Variant,*/
|
||||
std::map<int, std::string>
|
||||
>
|
||||
>
|
||||
>,
|
||||
sdbus::Signature,
|
||||
double
|
||||
>
|
||||
>;
|
||||
|
||||
ComplexType dataWritten = { {1, {{{5, {{"/some/object", true, 45, {{6, "hello"}, {7, "world"}}}}}}, "av", 3.14}}};
|
||||
|
||||
msg << dataWritten;
|
||||
msg.seal();
|
||||
|
||||
ComplexType dataRead;
|
||||
msg >> dataRead;
|
||||
|
||||
ASSERT_THAT(dataRead, Eq(dataWritten));
|
||||
}
|
129
test/unittests/TypeTraits_test.cpp
Normal file
129
test/unittests/TypeTraits_test.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file TypeTraits_test.cpp
|
||||
*
|
||||
* Created on: Nov 27, 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++/TypeTraits.h>
|
||||
#include <sdbus-c++/Types.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
|
||||
using ::testing::Eq;
|
||||
|
||||
namespace
|
||||
{
|
||||
// ---
|
||||
// FIXTURE DEFINITION FOR TYPED TESTS
|
||||
// ----
|
||||
|
||||
template <typename _T>
|
||||
class Type2DBusTypeSignatureConversion
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
const std::string dbusTypeSignature_{getDBusTypeSignature()};
|
||||
private:
|
||||
static std::string getDBusTypeSignature();
|
||||
};
|
||||
|
||||
#define TYPE(...) \
|
||||
template <> \
|
||||
std::string Type2DBusTypeSignatureConversion<__VA_ARGS__>::getDBusTypeSignature() \
|
||||
/**/
|
||||
#define HAS_DBUS_TYPE_SIGNATURE(_SIG) \
|
||||
{ \
|
||||
return (_SIG); \
|
||||
} \
|
||||
/**/
|
||||
|
||||
TYPE(bool)HAS_DBUS_TYPE_SIGNATURE("b")
|
||||
TYPE(uint8_t)HAS_DBUS_TYPE_SIGNATURE("y")
|
||||
TYPE(int16_t)HAS_DBUS_TYPE_SIGNATURE("n")
|
||||
TYPE(uint16_t)HAS_DBUS_TYPE_SIGNATURE("q")
|
||||
TYPE(int32_t)HAS_DBUS_TYPE_SIGNATURE("i")
|
||||
TYPE(uint32_t)HAS_DBUS_TYPE_SIGNATURE("u")
|
||||
TYPE(int64_t)HAS_DBUS_TYPE_SIGNATURE("x")
|
||||
TYPE(uint64_t)HAS_DBUS_TYPE_SIGNATURE("t")
|
||||
TYPE(double)HAS_DBUS_TYPE_SIGNATURE("d")
|
||||
TYPE(const char*)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(std::string)HAS_DBUS_TYPE_SIGNATURE("s")
|
||||
TYPE(sdbus::ObjectPath)HAS_DBUS_TYPE_SIGNATURE("o")
|
||||
TYPE(sdbus::Signature)HAS_DBUS_TYPE_SIGNATURE("g")
|
||||
TYPE(sdbus::Variant)HAS_DBUS_TYPE_SIGNATURE("v")
|
||||
TYPE(sdbus::Struct<bool>)HAS_DBUS_TYPE_SIGNATURE("(b)")
|
||||
TYPE(sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>)HAS_DBUS_TYPE_SIGNATURE("(qdsv)")
|
||||
TYPE(std::vector<int16_t>)HAS_DBUS_TYPE_SIGNATURE("an")
|
||||
TYPE(std::map<int32_t, int64_t>)HAS_DBUS_TYPE_SIGNATURE("a{ix}")
|
||||
using ComplexType = std::map<
|
||||
uint64_t,
|
||||
sdbus::Struct<
|
||||
std::map<
|
||||
uint8_t,
|
||||
std::vector<
|
||||
sdbus::Struct<
|
||||
sdbus::ObjectPath,
|
||||
bool,
|
||||
sdbus::Variant,
|
||||
std::map<int, std::string>
|
||||
>
|
||||
>
|
||||
>,
|
||||
sdbus::Signature,
|
||||
const char*
|
||||
>
|
||||
>;
|
||||
TYPE(ComplexType)HAS_DBUS_TYPE_SIGNATURE("a{t(a{ya(obva{is})}gs)}")
|
||||
|
||||
typedef ::testing::Types< bool
|
||||
, uint8_t
|
||||
, int16_t
|
||||
, uint16_t
|
||||
, int32_t
|
||||
, uint32_t
|
||||
, int64_t
|
||||
, uint64_t
|
||||
, double
|
||||
, const char*
|
||||
, std::string
|
||||
, sdbus::ObjectPath
|
||||
, sdbus::Signature
|
||||
, sdbus::Variant
|
||||
, sdbus::Struct<bool>
|
||||
, sdbus::Struct<uint16_t, double, std::string, sdbus::Variant>
|
||||
, std::vector<int16_t>
|
||||
, std::map<int32_t, int64_t>
|
||||
, ComplexType
|
||||
> DBusSupportedTypes;
|
||||
|
||||
TYPED_TEST_CASE(Type2DBusTypeSignatureConversion, DBusSupportedTypes);
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TYPED_TEST(Type2DBusTypeSignatureConversion, ConvertsTypeToProperDBusSignature)
|
||||
{
|
||||
ASSERT_THAT(sdbus::signature_of<TypeParam>::str(), Eq(this->dbusTypeSignature_));
|
||||
}
|
205
test/unittests/Types_test.cpp
Normal file
205
test/unittests/Types_test.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file Types_test.cpp
|
||||
*
|
||||
* Created on: Dec 3, 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++/Types.h>
|
||||
#include "MessageUtils.h"
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <cstdint>
|
||||
|
||||
using ::testing::Eq;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr const uint64_t ANY_UINT64 = 84578348354;
|
||||
constexpr const double ANY_DOUBLE = 3.14;
|
||||
}
|
||||
|
||||
/*-------------------------------------*/
|
||||
/* -- TEST CASES -- */
|
||||
/*-------------------------------------*/
|
||||
|
||||
TEST(AVariant, CanBeDefaultConstructed)
|
||||
{
|
||||
ASSERT_NO_THROW(sdbus::Variant());
|
||||
}
|
||||
|
||||
TEST(AVariant, ContainsNoValueAfterDefaultConstructed)
|
||||
{
|
||||
sdbus::Variant v;
|
||||
|
||||
ASSERT_TRUE(v.isEmpty());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeConstructedFromASimpleValue)
|
||||
{
|
||||
int value = 5;
|
||||
|
||||
ASSERT_NO_THROW(sdbus::Variant{value});
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeConstructedFromAComplexValue)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
|
||||
ASSERT_NO_THROW(sdbus::Variant(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, CanBeCopied)
|
||||
{
|
||||
auto value = "hello"s;
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
auto variantCopy1{variant};
|
||||
auto variantCopy2 = variantCopy1;
|
||||
|
||||
ASSERT_THAT(variantCopy1.get<std::string>(), Eq(value));
|
||||
ASSERT_THAT(variantCopy2.get<std::string>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, IsNotEmptyWhenContainsAValue)
|
||||
{
|
||||
sdbus::Variant v("hello");
|
||||
|
||||
ASSERT_FALSE(v.isEmpty());
|
||||
}
|
||||
|
||||
TEST(ASimpleVariant, ReturnsTheSimpleValueWhenAsked)
|
||||
{
|
||||
int value = 5;
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<int>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<decltype(value)>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
|
||||
{
|
||||
std::string value{"I am a string"};
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
ASSERT_THAT(variant.get<std::string>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(AVariant, ReturnsTrueWhenAskedIfItContainsTheTypeItReallyContains)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_TRUE(variant.containsValueOfType<ComplexType>());
|
||||
}
|
||||
|
||||
TEST(ASimpleVariant, ReturnsFalseWhenAskedIfItContainsTypeItDoesntReallyContain)
|
||||
{
|
||||
int value = 5;
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_FALSE(variant.containsValueOfType<double>());
|
||||
}
|
||||
|
||||
TEST(AVariant, CanContainOtherEmbeddedVariants)
|
||||
{
|
||||
using TypeWithVariants = std::vector<sdbus::Struct<sdbus::Variant, double>>;
|
||||
TypeWithVariants value;
|
||||
value.emplace_back(sdbus::make_struct(sdbus::Variant("a string"), ANY_DOUBLE));
|
||||
value.emplace_back(sdbus::make_struct(sdbus::Variant(ANY_UINT64), ANY_DOUBLE));
|
||||
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
ASSERT_TRUE(variant.containsValueOfType<TypeWithVariants>());
|
||||
}
|
||||
|
||||
TEST(ANonEmptyVariant, SerializesSuccessfullyToAMessage)
|
||||
{
|
||||
sdbus::Variant variant("a string");
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
|
||||
ASSERT_NO_THROW(variant.serializeTo(msg));
|
||||
}
|
||||
|
||||
TEST(AnEmptyVariant, ThrowsWhenBeingSerializedToAMessage)
|
||||
{
|
||||
sdbus::Variant variant;
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
|
||||
ASSERT_THROW(variant.serializeTo(msg), sdbus::Error);
|
||||
}
|
||||
|
||||
TEST(ANonEmptyVariant, SerializesToAndDeserializesFromAMessageSuccessfully)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
sdbus::Variant variant(value);
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
variant.serializeTo(msg);
|
||||
msg.seal();
|
||||
sdbus::Variant variant2;
|
||||
variant2.deserializeFrom(msg);
|
||||
|
||||
ASSERT_THAT(variant2.get<decltype(value)>(), Eq(value));
|
||||
}
|
||||
|
||||
TEST(CopiesOfVariant, SerializeToAndDeserializeFromMessageSuccessfully)
|
||||
{
|
||||
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
|
||||
ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{sdbus::make_struct("hello", ANY_DOUBLE), sdbus::make_struct("world", ANY_DOUBLE)}} };
|
||||
sdbus::Variant variant(value);
|
||||
auto variantCopy1{variant};
|
||||
auto variantCopy2 = variant;
|
||||
|
||||
sdbus::Message msg = sdbus::createPlainMessage();
|
||||
variant.serializeTo(msg);
|
||||
variantCopy1.serializeTo(msg);
|
||||
variantCopy2.serializeTo(msg);
|
||||
msg.seal();
|
||||
sdbus::Variant receivedVariant1, receivedVariant2, receivedVariant3;
|
||||
receivedVariant1.deserializeFrom(msg);
|
||||
receivedVariant2.deserializeFrom(msg);
|
||||
receivedVariant3.deserializeFrom(msg);
|
||||
|
||||
ASSERT_THAT(receivedVariant1.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant2.get<decltype(value)>(), Eq(value));
|
||||
ASSERT_THAT(receivedVariant3.get<decltype(value)>(), Eq(value));
|
||||
}
|
33
test/unittests/libsdbus-c++_unittests.cpp
Normal file
33
test/unittests/libsdbus-c++_unittests.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* (C) 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
|
||||
*
|
||||
* @file libsdbus-c++_unittests.cpp
|
||||
*
|
||||
* Created on: Nov 27, 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 "gmock/gmock.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleMock(&argc, argv);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Reference in New Issue
Block a user