2017-11-27 14:13:55 +01:00
/**
2022-07-05 17:08:35 +02:00
* (C) 2016 - 2021 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
* (C) 2016 - 2022 Stanislav Angelovic <stanislav.angelovic@protonmail.com>
2017-11-27 14:13:55 +01:00
*
2019-04-09 21:28:07 +02:00
* @file ConvenienceApiClasses.inl
2017-11-27 14:13:55 +01:00
*
* 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/>.
*/
2019-04-09 21:28:07 +02:00
#ifndef SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
#define SDBUS_CPP_CONVENIENCEAPICLASSES_INL_
2017-11-27 14:13:55 +01:00
#include <sdbus-c++/IObject.h>
2019-04-09 21:28:07 +02:00
#include <sdbus-c++/IProxy.h>
2017-11-27 14:13:55 +01:00
#include <sdbus-c++/Message.h>
2018-07-02 11:22:00 +02:00
#include <sdbus-c++/MethodResult.h>
2017-11-27 14:13:55 +01:00
#include <sdbus-c++/Types.h>
#include <sdbus-c++/TypeTraits.h>
#include <sdbus-c++/Error.h>
#include <string>
#include <tuple>
2019-11-10 17:31:58 +01:00
#include <exception>
#include <cassert>
2017-11-27 14:13:55 +01:00
namespace sdbus {
2019-11-03 13:54:13 +01:00
/*** ----------------- ***/
/*** MethodRegistrator ***/
/*** ----------------- ***/
2023-08-03 14:17:13 +02:00
inline MethodRegistrator :: MethodRegistrator ( IObject & object , std :: string methodName )
2017-11-27 14:13:55 +01:00
: object_ ( object )
2023-08-03 14:17:13 +02:00
, methodName_ ( std :: move ( methodName ))
2019-11-10 17:31:58 +01:00
, exceptions_ ( std :: uncaught_exceptions ())
2017-11-27 14:13:55 +01:00
{
}
2019-01-10 08:47:59 +01:00
inline MethodRegistrator ::~ MethodRegistrator () noexcept ( false ) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't register the method if MethodRegistrator threw an exception in one of its methods
if ( std :: uncaught_exceptions () != exceptions_ )
return ;
2019-11-10 17:31:58 +01:00
assert ( ! interfaceName_ . empty ()); // onInterface() must be placed/called prior to this function
assert ( methodCallback_ ); // implementedAs() must be placed/called prior to this function
2019-01-10 08:47:59 +01:00
// registerMethod() can throw. But as the MethodRegistrator shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow registerMethod() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
2020-01-27 07:57:38 +01:00
object_ . registerMethod ( interfaceName_
, std :: move ( methodName_ )
, std :: move ( inputSignature_ )
2023-08-03 14:17:13 +02:00
, inputParamNames_
2020-01-27 07:57:38 +01:00
, std :: move ( outputSignature_ )
2023-08-03 14:17:13 +02:00
, outputParamNames_
2020-01-27 07:57:38 +01:00
, std :: move ( methodCallback_ )
, std :: move ( flags_ ));
2019-01-10 08:47:59 +01:00
}
2019-11-10 17:31:58 +01:00
inline MethodRegistrator & MethodRegistrator :: onInterface ( std :: string interfaceName )
2017-11-27 14:13:55 +01:00
{
2019-11-10 17:31:58 +01:00
interfaceName_ = std :: move ( interfaceName );
2017-11-27 14:13:55 +01:00
return * this ;
}
template < typename _Function >
2020-02-01 22:47:04 +01:00
MethodRegistrator & MethodRegistrator :: implementedAs ( _Function && callback )
2018-07-02 11:22:00 +02:00
{
2019-01-10 08:47:59 +01:00
inputSignature_ = signature_of_function_input_arguments < _Function >:: str ();
outputSignature_ = signature_of_function_output_arguments < _Function >:: str ();
2019-04-03 00:05:20 +02:00
methodCallback_ = [ callback = std :: forward < _Function > ( callback )]( MethodCall call )
2018-07-02 11:22:00 +02:00
{
// 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 ;
2019-03-25 14:45:48 +01:00
// Deserialize input arguments from the message into the tuple.
2019-04-03 00:05:20 +02:00
call >> inputArgs ;
2018-07-02 11:22:00 +02:00
2020-02-01 22:47:04 +01:00
if constexpr ( ! is_async_method_v < _Function > )
{
// Invoke callback with input arguments from the tuple.
auto ret = sdbus :: apply ( callback , inputArgs );
// Store output arguments to the reply message and send it back.
auto reply = call . createReply ();
reply << ret ;
reply . send ();
}
else
{
// Invoke callback with input arguments from the tuple and with result object to be set later
using AsyncResult = typename function_traits < _Function >:: async_result_t ;
sdbus :: apply ( callback , AsyncResult { std :: move ( call )}, std :: move ( inputArgs ));
}
2019-01-10 08:47:59 +01:00
};
return * this ;
2018-07-02 11:22:00 +02:00
}
2017-11-27 14:13:55 +01:00
2020-01-27 07:57:38 +01:00
inline MethodRegistrator & MethodRegistrator :: withInputParamNames ( std :: vector < std :: string > paramNames )
{
inputParamNames_ = std :: move ( paramNames );
return * this ;
}
template < typename ... _String >
2020-02-01 22:47:04 +01:00
inline MethodRegistrator & MethodRegistrator :: withInputParamNames ( _String ... paramNames )
2020-01-27 07:57:38 +01:00
{
2020-02-01 22:47:04 +01:00
static_assert ( std :: conjunction_v < std :: is_convertible < _String , std :: string > ... > , "Parameter names must be (convertible to) strings" );
2020-01-27 07:57:38 +01:00
return withInputParamNames ({ paramNames ...});
}
inline MethodRegistrator & MethodRegistrator :: withOutputParamNames ( std :: vector < std :: string > paramNames )
{
outputParamNames_ = std :: move ( paramNames );
return * this ;
}
template < typename ... _String >
2020-02-01 22:47:04 +01:00
inline MethodRegistrator & MethodRegistrator :: withOutputParamNames ( _String ... paramNames )
2020-01-27 07:57:38 +01:00
{
2020-02-01 22:47:04 +01:00
static_assert ( std :: conjunction_v < std :: is_convertible < _String , std :: string > ... > , "Parameter names must be (convertible to) strings" );
2020-01-27 07:57:38 +01:00
return withOutputParamNames ({ paramNames ...});
}
2019-01-10 08:47:59 +01:00
inline MethodRegistrator & MethodRegistrator :: markAsDeprecated ()
{
flags_ . set ( Flags :: DEPRECATED );
return * this ;
}
inline MethodRegistrator & MethodRegistrator :: markAsPrivileged ()
{
flags_ . set ( Flags :: PRIVILEGED );
return * this ;
}
inline MethodRegistrator & MethodRegistrator :: withNoReply ()
{
flags_ . set ( Flags :: METHOD_NO_REPLY );
return * this ;
}
2019-11-03 13:54:13 +01:00
/*** ----------------- ***/
/*** SignalRegistrator ***/
/*** ----------------- ***/
2019-01-10 08:47:59 +01:00
2023-08-03 14:17:13 +02:00
inline SignalRegistrator :: SignalRegistrator ( IObject & object , std :: string signalName )
2017-11-27 14:13:55 +01:00
: object_ ( object )
2023-08-03 14:17:13 +02:00
, signalName_ ( std :: move ( signalName ))
2017-11-27 14:13:55 +01:00
, 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 ;
2019-11-10 17:31:58 +01:00
assert ( ! interfaceName_ . empty ()); // onInterface() must be placed/called prior to this function
2017-11-27 14:13:55 +01:00
// 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.
2020-01-27 07:57:38 +01:00
object_ . registerSignal ( interfaceName_
, std :: move ( signalName_ )
, std :: move ( signalSignature_ )
2023-08-03 14:17:13 +02:00
, paramNames_
2020-01-27 07:57:38 +01:00
, std :: move ( flags_ ) );
2017-11-27 14:13:55 +01:00
}
inline SignalRegistrator & SignalRegistrator :: onInterface ( std :: string interfaceName )
{
interfaceName_ = std :: move ( interfaceName );
return * this ;
}
template < typename ... _Args >
2019-01-10 08:47:59 +01:00
inline SignalRegistrator & SignalRegistrator :: withParameters ()
2017-11-27 14:13:55 +01:00
{
signalSignature_ = signature_of_function_input_arguments < void ( _Args ...) >:: str ();
2019-01-10 08:47:59 +01:00
return * this ;
}
2020-01-27 07:57:38 +01:00
template < typename ... _Args >
inline SignalRegistrator & SignalRegistrator :: withParameters ( std :: vector < std :: string > paramNames )
{
paramNames_ = std :: move ( paramNames );
return withParameters < _Args ... > ();
}
template < typename ... _Args , typename ... _String >
2020-02-01 22:47:04 +01:00
inline SignalRegistrator & SignalRegistrator :: withParameters ( _String ... paramNames )
2020-01-27 07:57:38 +01:00
{
2020-02-01 22:47:04 +01:00
static_assert ( std :: conjunction_v < std :: is_convertible < _String , std :: string > ... > , "Parameter names must be (convertible to) strings" );
2020-01-27 07:57:38 +01:00
static_assert ( sizeof ...( _Args ) == sizeof ...( _String ), "Numbers of signal parameters and their names don't match" );
return withParameters < _Args ... > ({ paramNames ...});
}
2019-01-10 08:47:59 +01:00
inline SignalRegistrator & SignalRegistrator :: markAsDeprecated ()
{
flags_ . set ( Flags :: DEPRECATED );
return * this ;
2017-11-27 14:13:55 +01:00
}
2019-11-03 13:54:13 +01:00
/*** ------------------- ***/
/*** PropertyRegistrator ***/
/*** ------------------- ***/
2017-11-27 14:13:55 +01:00
2019-11-10 17:31:58 +01:00
inline PropertyRegistrator :: PropertyRegistrator ( IObject & object , const std :: string & propertyName )
2017-11-27 14:13:55 +01:00
: object_ ( object )
2019-11-10 17:31:58 +01:00
, propertyName_ ( propertyName )
2017-11-27 14:13:55 +01:00
, 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 ;
2019-11-10 17:31:58 +01:00
assert ( ! interfaceName_ . empty ()); // onInterface() must be placed/called prior to this function
2017-11-27 14:13:55 +01:00
// 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.
2019-11-10 17:31:58 +01:00
object_ . registerProperty ( interfaceName_
, propertyName_
, propertySignature_
2017-11-27 14:13:55 +01:00
, std :: move ( getter_ )
2019-11-10 17:31:58 +01:00
, std :: move ( setter_ )
, flags_ );
2017-11-27 14:13:55 +01:00
}
2019-11-10 17:31:58 +01:00
inline PropertyRegistrator & PropertyRegistrator :: onInterface ( std :: string interfaceName )
2017-11-27 14:13:55 +01:00
{
2019-11-10 17:31:58 +01:00
interfaceName_ = std :: move ( interfaceName );
2017-11-27 14:13:55 +01:00
return * this ;
}
template < typename _Function >
inline PropertyRegistrator & PropertyRegistrator :: withGetter ( _Function && callback )
{
2023-09-14 10:54:57 +02:00
static_assert ( function_argument_count_v < _Function > == 0 , "Property getter function must not take any arguments" );
2017-11-27 14:13:55 +01:00
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 ();
2019-06-10 21:38:30 +02:00
getter_ = [ callback = std :: forward < _Function > ( callback )]( PropertyGetReply & reply )
2017-11-27 14:13:55 +01:00
{
2019-06-10 21:38:30 +02:00
// Get the propety value and serialize it into the pre-constructed reply message
reply << callback ();
2017-11-27 14:13:55 +01:00
};
return * this ;
}
template < typename _Function >
inline PropertyRegistrator & PropertyRegistrator :: withSetter ( _Function && callback )
{
2023-09-14 10:54:57 +02:00
static_assert ( function_argument_count_v < _Function > == 1 , "Property setter function must take one parameter - the property value" );
2017-11-27 14:13:55 +01:00
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 ();
2023-10-24 21:56:47 +02:00
setter_ = [ callback = std :: forward < _Function > ( callback )]( PropertySetCall call )
2017-11-27 14:13:55 +01:00
{
// Default-construct property value
using property_type = function_argument_t < _Function , 0 > ;
std :: decay_t < property_type > property ;
2019-06-10 21:38:30 +02:00
// Deserialize property value from the incoming call message
call >> property ;
2017-11-27 14:13:55 +01:00
// Invoke setter with the value
callback ( property );
};
return * this ;
}
2019-01-10 08:47:59 +01:00
inline PropertyRegistrator & PropertyRegistrator :: markAsDeprecated ()
{
flags_ . set ( Flags :: DEPRECATED );
return * this ;
}
inline PropertyRegistrator & PropertyRegistrator :: markAsPrivileged ()
{
flags_ . set ( Flags :: PRIVILEGED );
return * this ;
}
inline PropertyRegistrator & PropertyRegistrator :: withUpdateBehavior ( Flags :: PropertyUpdateBehaviorFlags behavior )
{
flags_ . set ( behavior );
return * this ;
}
2019-11-03 13:54:13 +01:00
/*** -------------------- ***/
/*** InterfaceFlagsSetter ***/
/*** -------------------- ***/
2019-01-10 08:47:59 +01:00
inline InterfaceFlagsSetter :: InterfaceFlagsSetter ( IObject & object , const std :: string & interfaceName )
: object_ ( object )
, interfaceName_ ( interfaceName )
, exceptions_ ( std :: uncaught_exceptions ())
{
}
inline InterfaceFlagsSetter ::~ InterfaceFlagsSetter () noexcept ( false ) // since C++11, destructors must
{ // explicitly be allowed to throw
// Don't set any flags if InterfaceFlagsSetter threw an exception in one of its methods
if ( std :: uncaught_exceptions () != exceptions_ )
return ;
// setInterfaceFlags() can throw. But as the InterfaceFlagsSetter shall always be used as an unnamed,
// temporary object, i.e. not as a stack-allocated object, the double-exception situation
// shall never happen. I.e. it should not happen that this destructor is directly called
// in the stack-unwinding process of another flying exception (which would lead to immediate
// termination). It can be called indirectly in the destructor of another object, but that's
// fine and safe provided that the caller catches exceptions thrown from here.
// Therefore, we can allow setInterfaceFlags() to throw even if we are in the destructor.
// Bottomline is, to be on the safe side, the caller must take care of catching and reacting
// to the exception thrown from here if the caller is a destructor itself.
2019-11-10 17:31:58 +01:00
object_ . setInterfaceFlags ( interfaceName_ , std :: move ( flags_ ));
2019-01-10 08:47:59 +01:00
}
inline InterfaceFlagsSetter & InterfaceFlagsSetter :: markAsDeprecated ()
{
flags_ . set ( Flags :: DEPRECATED );
return * this ;
}
inline InterfaceFlagsSetter & InterfaceFlagsSetter :: markAsPrivileged ()
{
flags_ . set ( Flags :: PRIVILEGED );
return * this ;
}
inline InterfaceFlagsSetter & InterfaceFlagsSetter :: withNoReplyMethods ()
{
flags_ . set ( Flags :: METHOD_NO_REPLY );
return * this ;
}
inline InterfaceFlagsSetter & InterfaceFlagsSetter :: withPropertyUpdateBehavior ( Flags :: PropertyUpdateBehaviorFlags behavior )
{
flags_ . set ( behavior );
return * this ;
}
2019-11-03 13:54:13 +01:00
/*** ------------- ***/
/*** SignalEmitter ***/
/*** ------------- ***/
2017-11-27 14:13:55 +01:00
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 ;
// 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 )
{
2019-11-10 17:31:58 +01:00
assert ( signal_ . isValid ()); // onInterface() must be placed/called prior to withArguments()
2017-11-27 14:13:55 +01:00
detail :: serialize_pack ( signal_ , std :: forward < _Args > ( args )...);
}
2019-11-03 13:54:13 +01:00
/*** ------------- ***/
/*** MethodInvoker ***/
/*** ------------- ***/
2017-11-27 14:13:55 +01:00
2019-11-10 17:31:58 +01:00
inline MethodInvoker :: MethodInvoker ( IProxy & proxy , const std :: string & methodName )
2019-04-09 21:28:07 +02:00
: proxy_ ( proxy )
2017-11-27 14:13:55 +01:00
, 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 ;
// 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.
2019-11-10 17:31:58 +01:00
proxy_ . callMethod ( method_ , timeout_ );
2017-11-27 14:13:55 +01:00
}
inline MethodInvoker & MethodInvoker :: onInterface ( const std :: string & interfaceName )
{
2019-04-09 21:28:07 +02:00
method_ = proxy_ . createMethodCall ( interfaceName , methodName_ );
2017-11-27 14:13:55 +01:00
return * this ;
}
2019-11-03 13:54:13 +01:00
inline MethodInvoker & MethodInvoker :: withTimeout ( uint64_t usec )
{
timeout_ = usec ;
return * this ;
}
template < typename _Rep , typename _Period >
inline MethodInvoker & MethodInvoker :: withTimeout ( const std :: chrono :: duration < _Rep , _Period >& timeout )
{
auto microsecs = std :: chrono :: duration_cast < std :: chrono :: microseconds > ( timeout );
return withTimeout ( microsecs . count ());
}
2017-11-27 14:13:55 +01:00
template < typename ... _Args >
inline MethodInvoker & MethodInvoker :: withArguments ( _Args && ... args )
{
2019-11-10 17:31:58 +01:00
assert ( method_ . isValid ()); // onInterface() must be placed/called prior to this function
2017-11-27 14:13:55 +01:00
detail :: serialize_pack ( method_ , std :: forward < _Args > ( args )...);
return * this ;
}
template < typename ... _Args >
inline void MethodInvoker :: storeResultsTo ( _Args & ... args )
{
2019-11-10 17:31:58 +01:00
assert ( method_ . isValid ()); // onInterface() must be placed/called prior to this function
2017-11-27 14:13:55 +01:00
2019-11-03 13:54:13 +01:00
auto reply = proxy_ . callMethod ( method_ , timeout_ );
2017-11-27 14:13:55 +01:00
methodCalled_ = true ;
detail :: deserialize_pack ( reply , args ...);
}
2019-01-10 08:47:59 +01:00
inline void MethodInvoker :: dontExpectReply ()
{
2019-11-10 17:31:58 +01:00
assert ( method_ . isValid ()); // onInterface() must be placed/called prior to this function
2019-01-10 08:47:59 +01:00
method_ . dontExpectReply ();
}
2019-11-03 13:54:13 +01:00
/*** ------------------ ***/
/*** AsyncMethodInvoker ***/
/*** ------------------ ***/
2017-11-27 14:13:55 +01:00
2019-04-09 21:28:07 +02:00
inline AsyncMethodInvoker :: AsyncMethodInvoker ( IProxy & proxy , const std :: string & methodName )
: proxy_ ( proxy )
2019-03-25 14:45:48 +01:00
, methodName_ ( methodName )
{
}
inline AsyncMethodInvoker & AsyncMethodInvoker :: onInterface ( const std :: string & interfaceName )
{
2020-01-12 13:10:34 +01:00
method_ = proxy_ . createMethodCall ( interfaceName , methodName_ );
2019-03-25 14:45:48 +01:00
return * this ;
}
2019-11-03 13:54:13 +01:00
inline AsyncMethodInvoker & AsyncMethodInvoker :: withTimeout ( uint64_t usec )
{
timeout_ = usec ;
return * this ;
}
template < typename _Rep , typename _Period >
inline AsyncMethodInvoker & AsyncMethodInvoker :: withTimeout ( const std :: chrono :: duration < _Rep , _Period >& timeout )
{
auto microsecs = std :: chrono :: duration_cast < std :: chrono :: microseconds > ( timeout );
return withTimeout ( microsecs . count ());
}
2019-03-25 14:45:48 +01:00
template < typename ... _Args >
inline AsyncMethodInvoker & AsyncMethodInvoker :: withArguments ( _Args && ... args )
{
2019-11-10 17:31:58 +01:00
assert ( method_ . isValid ()); // onInterface() must be placed/called prior to this function
2019-03-25 14:45:48 +01:00
detail :: serialize_pack ( method_ , std :: forward < _Args > ( args )...);
return * this ;
}
template < typename _Function >
2020-03-29 21:43:20 +02:00
PendingAsyncCall AsyncMethodInvoker :: uponReplyInvoke ( _Function && callback )
2019-03-25 14:45:48 +01:00
{
2019-11-10 17:31:58 +01:00
assert ( method_ . isValid ()); // onInterface() must be placed/called prior to this function
2019-03-25 14:45:48 +01:00
2023-10-24 21:56:47 +02:00
auto asyncReplyHandler = [ callback = std :: forward < _Function > ( callback )]( MethodReply reply , const Error * error )
2019-03-25 14:45:48 +01:00
{
// 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 > args ;
// Deserialize input arguments from the message into the tuple (if no error occurred).
if ( error == nullptr )
2021-06-17 15:07:26 -07:00
{
try
{
reply >> args ;
}
2021-07-22 14:33:38 +02:00
catch ( const Error & e )
2021-06-17 15:07:26 -07:00
{
2023-11-03 18:03:01 +01:00
// Pass message deserialization exceptions to the client via callback error parameter,
// instead of propagating them up the message loop call stack.
2021-06-17 15:07:26 -07:00
sdbus :: apply ( callback , & e , args );
return ;
}
}
2019-03-25 14:45:48 +01:00
// Invoke callback with input arguments from the tuple.
2020-02-01 22:47:04 +01:00
sdbus :: apply ( callback , error , args );
2019-11-03 13:54:13 +01:00
};
2020-03-29 21:43:20 +02:00
return proxy_ . callMethod ( method_ , std :: move ( asyncReplyHandler ), timeout_ );
2019-03-25 14:45:48 +01:00
}
2023-01-29 22:12:10 +01:00
template < typename ... _Args >
std :: future < future_return_t < _Args ... >> AsyncMethodInvoker :: getResultAsFuture ()
{
auto promise = std :: make_shared < std :: promise < future_return_t < _Args ... >>> ();
auto future = promise -> get_future ();
uponReplyInvoke ([ promise = std :: move ( promise )]( const Error * error , _Args ... args )
{
if ( error == nullptr )
if constexpr ( ! std :: is_void_v < future_return_t < _Args ... >> )
promise -> set_value ({ std :: move ( args )...});
else
promise -> set_value ();
else
promise -> set_exception ( std :: make_exception_ptr ( * error ));
});
// Will be std::future<void> for no D-Bus method return value
// or std::future<T> for single D-Bus method return value
// or std::future<std::tuple<...>> for multiple method return values
return future ;
}
2019-11-03 13:54:13 +01:00
/*** ---------------- ***/
/*** SignalSubscriber ***/
/*** ---------------- ***/
2019-03-25 14:45:48 +01:00
2019-04-09 21:28:07 +02:00
inline SignalSubscriber :: SignalSubscriber ( IProxy & proxy , const std :: string & signalName )
: proxy_ ( proxy )
2017-11-27 14:13:55 +01:00
, signalName_ ( signalName )
{
}
2019-11-10 17:31:58 +01:00
inline SignalSubscriber & SignalSubscriber :: onInterface ( std :: string interfaceName )
2017-11-27 14:13:55 +01:00
{
2019-11-10 17:31:58 +01:00
interfaceName_ = std :: move ( interfaceName );
2017-11-27 14:13:55 +01:00
return * this ;
}
template < typename _Function >
inline void SignalSubscriber :: call ( _Function && callback )
{
2019-11-10 17:31:58 +01:00
assert ( ! interfaceName_ . empty ()); // onInterface() must be placed/called prior to this function
2017-11-27 14:13:55 +01:00
2019-04-09 21:28:07 +02:00
proxy_ . registerSignalHandler ( interfaceName_
, signalName_
2023-10-24 21:56:47 +02:00
, [ callback = std :: forward < _Function > ( callback )]( Signal signal )
2017-11-27 14:13:55 +01:00
{
// 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 ;
2021-07-22 15:48:50 +02:00
// The signal handler can take pure signal parameters only, or an additional `const Error*` as its first
// parameter. In the former case, if the deserialization fails (e.g. due to signature mismatch),
// the failure is ignored (and signal simply dropped). In the latter case, the deserialization failure
// will be communicated as a non-zero Error pointer to the client's signal handler.
if constexpr ( has_error_param_v < _Function > )
{
// Deserialize input arguments from the signal message into the tuple
try
{
signal >> signalArgs ;
}
catch ( const sdbus :: Error & e )
{
2023-11-03 18:03:01 +01:00
// Pass message deserialization exceptions to the client via callback error parameter,
// instead of propagating them up the message loop call stack.
2021-07-22 15:48:50 +02:00
sdbus :: apply ( callback , & e , signalArgs );
2023-11-03 18:03:01 +01:00
return ;
2021-07-22 15:48:50 +02:00
}
2017-11-27 14:13:55 +01:00
2021-07-22 15:48:50 +02:00
// Invoke callback with no error and input arguments from the tuple.
sdbus :: apply ( callback , nullptr , signalArgs );
}
else
{
// Deserialize input arguments from the signal message into the tuple
signal >> signalArgs ;
// Invoke callback with input arguments from the tuple.
sdbus :: apply ( callback , signalArgs );
}
2017-11-27 14:13:55 +01:00
});
}
2021-11-28 16:30:49 +02:00
/*** ------------------ ***/
/*** SignalUnsubscriber ***/
/*** ------------------ ***/
inline SignalUnsubscriber :: SignalUnsubscriber ( IProxy & proxy , const std :: string & signalName )
: proxy_ ( proxy )
, signalName_ ( signalName )
{
}
2023-09-14 10:54:57 +02:00
inline void SignalUnsubscriber :: onInterface ( const std :: string & interfaceName )
2021-11-28 16:30:49 +02:00
{
proxy_ . unregisterSignalHandler ( interfaceName , signalName_ );
}
2019-11-03 13:54:13 +01:00
/*** -------------- ***/
/*** PropertyGetter ***/
/*** -------------- ***/
2017-11-27 14:13:55 +01:00
2019-04-09 21:28:07 +02:00
inline PropertyGetter :: PropertyGetter ( IProxy & proxy , const std :: string & propertyName )
: proxy_ ( proxy )
2017-11-27 14:13:55 +01:00
, propertyName_ ( propertyName )
{
}
2023-09-14 10:54:57 +02:00
inline Variant PropertyGetter :: onInterface ( const std :: string & interfaceName )
2017-11-27 14:13:55 +01:00
{
2023-09-14 10:54:57 +02:00
Variant var ;
proxy_ . callMethod ( "Get" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( interfaceName , propertyName_ )
. storeResultsTo ( var );
2017-11-27 14:13:55 +01:00
return var ;
}
2023-09-14 10:54:57 +02:00
/*** ------------------- ***/
/*** AsyncPropertyGetter ***/
/*** ------------------- ***/
inline AsyncPropertyGetter :: AsyncPropertyGetter ( IProxy & proxy , const std :: string & propertyName )
: proxy_ ( proxy )
, propertyName_ ( propertyName )
{
}
inline AsyncPropertyGetter & AsyncPropertyGetter :: onInterface ( const std :: string & interfaceName )
{
interfaceName_ = & interfaceName ;
return * this ;
}
template < typename _Function >
PendingAsyncCall AsyncPropertyGetter :: uponReplyInvoke ( _Function && callback )
{
static_assert ( std :: is_invocable_r_v < void , _Function , const Error * , Variant > , "Property get callback function must accept Error* and property value as Variant" );
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
return proxy_ . callMethodAsync ( "Get" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ , propertyName_ )
. uponReplyInvoke ( std :: forward < _Function > ( callback ));
}
inline std :: future < Variant > AsyncPropertyGetter :: getResultAsFuture ()
{
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
return proxy_ . callMethodAsync ( "Get" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ , propertyName_ )
. getResultAsFuture < Variant > ();
}
2019-11-03 13:54:13 +01:00
/*** -------------- ***/
/*** PropertySetter ***/
/*** -------------- ***/
2017-11-27 14:13:55 +01:00
2019-04-09 21:28:07 +02:00
inline PropertySetter :: PropertySetter ( IProxy & proxy , const std :: string & propertyName )
: proxy_ ( proxy )
2017-11-27 14:13:55 +01:00
, propertyName_ ( propertyName )
{
}
2023-09-14 10:54:57 +02:00
inline PropertySetter & PropertySetter :: onInterface ( const std :: string & interfaceName )
2017-11-27 14:13:55 +01:00
{
2023-09-14 10:54:57 +02:00
interfaceName_ = & interfaceName ;
2017-11-27 14:13:55 +01:00
return * this ;
}
template < typename _Value >
inline void PropertySetter :: toValue ( const _Value & value )
2019-05-29 22:28:15 +02:00
{
2023-09-14 10:54:57 +02:00
PropertySetter :: toValue ( Variant { value });
2019-05-29 22:28:15 +02:00
}
2023-09-14 10:54:57 +02:00
template < typename _Value >
inline void PropertySetter :: toValue ( const _Value & value , dont_expect_reply_t )
2017-11-27 14:13:55 +01:00
{
2023-09-14 10:54:57 +02:00
PropertySetter :: toValue ( Variant { value }, dont_expect_reply );
}
2017-11-27 14:13:55 +01:00
2023-09-14 10:54:57 +02:00
inline void PropertySetter :: toValue ( const Variant & value )
{
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
proxy_ . callMethod ( "Set" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ , propertyName_ , value );
}
inline void PropertySetter :: toValue ( const Variant & value , dont_expect_reply_t )
{
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
proxy_ . callMethod ( "Set" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ , propertyName_ , value )
. dontExpectReply ();
}
/*** ------------------- ***/
/*** AsyncPropertySetter ***/
/*** ------------------- ***/
inline AsyncPropertySetter :: AsyncPropertySetter ( IProxy & proxy , const std :: string & propertyName )
: proxy_ ( proxy )
, propertyName_ ( propertyName )
{
}
inline AsyncPropertySetter & AsyncPropertySetter :: onInterface ( const std :: string & interfaceName )
{
interfaceName_ = & interfaceName ;
return * this ;
}
template < typename _Value >
inline AsyncPropertySetter & AsyncPropertySetter :: toValue ( _Value && value )
{
return AsyncPropertySetter :: toValue ( Variant { std :: forward < _Value > ( value )});
}
inline AsyncPropertySetter & AsyncPropertySetter :: toValue ( Variant value )
{
value_ = std :: move ( value );
return * this ;
}
template < typename _Function >
PendingAsyncCall AsyncPropertySetter :: uponReplyInvoke ( _Function && callback )
{
static_assert ( std :: is_invocable_r_v < void , _Function , const Error *> , "Property set callback function must accept Error* only" );
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
return proxy_ . callMethodAsync ( "Set" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ , propertyName_ , std :: move ( value_ ))
. uponReplyInvoke ( std :: forward < _Function > ( callback ));
}
inline std :: future < void > AsyncPropertySetter :: getResultAsFuture ()
{
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
return proxy_ . callMethodAsync ( "Set" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ , propertyName_ , std :: move ( value_ ))
. getResultAsFuture <> ();
}
/*** ------------------- ***/
/*** AllPropertiesGetter ***/
/*** ------------------- ***/
inline AllPropertiesGetter :: AllPropertiesGetter ( IProxy & proxy )
: proxy_ ( proxy )
{
}
inline std :: map < std :: string , Variant > AllPropertiesGetter :: onInterface ( const std :: string & interfaceName )
{
std :: map < std :: string , Variant > props ;
proxy_ . callMethod ( "GetAll" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( interfaceName )
. storeResultsTo ( props );
return props ;
}
/*** ------------------------ ***/
/*** AsyncAllPropertiesGetter ***/
/*** ------------------------ ***/
inline AsyncAllPropertiesGetter :: AsyncAllPropertiesGetter ( IProxy & proxy )
: proxy_ ( proxy )
{
}
inline AsyncAllPropertiesGetter & AsyncAllPropertiesGetter :: onInterface ( const std :: string & interfaceName )
{
interfaceName_ = & interfaceName ;
return * this ;
}
template < typename _Function >
PendingAsyncCall AsyncAllPropertiesGetter :: uponReplyInvoke ( _Function && callback )
{
static_assert ( std :: is_invocable_r_v < void , _Function , const Error * , std :: map < std :: string , Variant >>
, "All properties get callback function must accept Error* and a map of property names to their values" );
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
return proxy_ . callMethodAsync ( "GetAll" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ )
. uponReplyInvoke ( std :: forward < _Function > ( callback ));
}
inline std :: future < std :: map < std :: string , Variant >> AsyncAllPropertiesGetter :: getResultAsFuture ()
{
assert ( interfaceName_ != nullptr ); // onInterface() must be placed/called prior to this function
return proxy_ . callMethodAsync ( "GetAll" )
. onInterface ( "org.freedesktop.DBus.Properties" )
. withArguments ( * interfaceName_ )
. getResultAsFuture < std :: map < std :: string , Variant >> ();
2017-11-27 14:13:55 +01:00
}
}
2019-04-09 21:28:07 +02:00
#endif /* SDBUS_CPP_CONVENIENCEAPICLASSES_INL_ */