| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This file provides generic method to parse function call arguments from |
| // D-Bus message buffer and subsequently invokes a provided native C++ callback |
| // with the parameter values passed as the callback arguments. |
| |
| // This functionality is achieved by parsing method arguments one by one, |
| // left to right from the C++ callback's type signature, and moving the parsed |
| // arguments to the back to the next call to DBusInvoke::Invoke's arguments as |
| // const refs. Each iteration has one fewer template specialization arguments, |
| // until there is only the return type remaining and we fall through to either |
| // the void or the non-void final specialization. |
| |
| #ifndef LIBCHROMEOS_CHROMEOS_DBUS_DBUS_PARAM_READER_H_ |
| #define LIBCHROMEOS_CHROMEOS_DBUS_DBUS_PARAM_READER_H_ |
| |
| #include <type_traits> |
| |
| #include <chromeos/dbus/data_serialization.h> |
| #include <chromeos/dbus/utils.h> |
| #include <chromeos/errors/error.h> |
| #include <chromeos/errors/error_codes.h> |
| #include <dbus/message.h> |
| |
| namespace chromeos { |
| namespace dbus_utils { |
| |
| // A generic DBusParamReader stub class which allows us to specialize on |
| // a variable list of expected function parameters later on. |
| // This struct in itself is not used. But its concrete template specializations |
| // defined below are. |
| // |allow_out_params| controls whether DBusParamReader allows the parameter |
| // list to contain OUT parameters (pointers). |
| template<bool allow_out_params, typename...> |
| struct DBusParamReader; |
| |
| // A generic specialization of DBusParamReader to handle variable function |
| // parameters. This specialization pops one parameter off the D-Bus message |
| // buffer and calls other specializations of DBusParamReader with fewer |
| // parameters to pop the remaining parameters. |
| // CurrentParam - the type of the current method parameter we are processing. |
| // RestOfParams - the types of remaining parameters to be processed. |
| template<bool allow_out_params, typename CurrentParam, typename... RestOfParams> |
| struct DBusParamReader<allow_out_params, CurrentParam, RestOfParams...> { |
| // DBusParamReader::Invoke() is a member function that actually extracts the |
| // current parameter from the message buffer. |
| // handler - the C++ callback functor to be called when all the |
| // parameters are processed. |
| // method_call - D-Bus method call object we are processing. |
| // reader - D-Bus message reader to pop the current argument value from. |
| // args... - the callback parameters processed so far. |
| template<typename CallbackType, typename... Args> |
| static bool Invoke(const CallbackType& handler, |
| dbus::MessageReader* reader, |
| ErrorPtr* error, |
| const Args&... args) { |
| return InvokeHelper<CurrentParam, CallbackType, Args...>( |
| handler, reader, error, static_cast<const Args&>(args)...); |
| } |
| |
| // |
| // There are two specializations of this function: |
| // 1. For the case where ParamType is a value type (D-Bus IN parameter). |
| // 2. For the case where ParamType is a pointer (D-Bus OUT parameter). |
| // In the second case, the parameter is not popped off the message reader, |
| // since we do not expect the client to provide any data for it. |
| // However after the final handler is called, the values for the OUT |
| // parameters should be sent back in the method call response message. |
| |
| // Overload 1: ParamType is not a pointer. |
| template<typename ParamType, typename CallbackType, typename... Args> |
| static typename std::enable_if<!std::is_pointer<ParamType>::value, bool>::type |
| InvokeHelper(const CallbackType& handler, |
| dbus::MessageReader* reader, |
| ErrorPtr* error, |
| const Args&... args) { |
| if (!reader->HasMoreData()) { |
| Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, |
| DBUS_ERROR_INVALID_ARGS, |
| "Too few parameters in a method call"); |
| return false; |
| } |
| // ParamType could be a reference type (e.g. 'const std::string&'). |
| // Here we need a value type so we can create an object of this type and |
| // pop the value off the message buffer into. Using std::decay<> to get |
| // the value type. If ParamType is already a value type, ParamValueType will |
| // be the same as ParamType. |
| using ParamValueType = typename std::decay<ParamType>::type; |
| // The variable to hold the value of the current parameter we reading from |
| // the message buffer. |
| ParamValueType current_param; |
| if (!DBusType<ParamValueType>::Read(reader, ¤t_param)) { |
| Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, |
| DBUS_ERROR_INVALID_ARGS, |
| "Method parameter type mismatch"); |
| return false; |
| } |
| // Call DBusParamReader::Invoke() to process the rest of parameters. |
| // Note that this is not a recursive call because it is calling a different |
| // method of a different class. We exclude the current parameter type |
| // (ParamType) from DBusParamReader<> template parameter list and forward |
| // all the parameters to the arguments of Invoke() and append the current |
| // parameter to the end of the parameter list. We pass it as a const |
| // reference to allow to use move-only types such as std::unique_ptr<> and |
| // to eliminate unnecessarily copying data. |
| return DBusParamReader<allow_out_params, RestOfParams...>::Invoke( |
| handler, reader, error, |
| static_cast<const Args&>(args)..., |
| static_cast<const ParamValueType&>(current_param)); |
| } |
| |
| // Overload 2: ParamType is a pointer. |
| template<typename ParamType, typename CallbackType, typename... Args> |
| static typename std::enable_if<allow_out_params && |
| std::is_pointer<ParamType>::value, bool>::type |
| InvokeHelper(const CallbackType& handler, |
| dbus::MessageReader* reader, |
| ErrorPtr* error, |
| const Args&... args) { |
| // ParamType is a pointer. This is expected to be an output parameter. |
| // Create storage for it and the handler will provide a value for it. |
| using ParamValueType = typename std::remove_pointer<ParamType>::type; |
| // The variable to hold the value of the current parameter we are passing |
| // to the handler. |
| ParamValueType current_param{}; // Default-initialize the value. |
| // Call DBusParamReader::Invoke() to process the rest of parameters. |
| // Note that this is not a recursive call because it is calling a different |
| // method of a different class. We exclude the current parameter type |
| // (ParamType) from DBusParamReader<> template parameter list and forward |
| // all the parameters to the arguments of Invoke() and append the current |
| // parameter to the end of the parameter list. |
| return DBusParamReader<allow_out_params, RestOfParams...>::Invoke( |
| handler, reader, error, |
| static_cast<const Args&>(args)..., |
| ¤t_param); |
| } |
| }; // struct DBusParamReader<ParamType, RestOfParams...> |
| |
| // The final specialization of DBusParamReader<> used when no more parameters |
| // are expected in the message buffer. Actually dispatches the call to the |
| // handler with all the accumulated arguments. |
| template<bool allow_out_params> |
| struct DBusParamReader<allow_out_params> { |
| template<typename CallbackType, typename... Args> |
| static bool Invoke(const CallbackType& handler, |
| dbus::MessageReader* reader, |
| ErrorPtr* error, |
| const Args&... args) { |
| if (reader->HasMoreData()) { |
| Error::AddTo(error, FROM_HERE, errors::dbus::kDomain, |
| DBUS_ERROR_INVALID_ARGS, |
| "Too many parameters in a method call"); |
| return false; |
| } |
| handler(args...); |
| return true; |
| } |
| }; // struct DBusParamReader<> |
| |
| } // namespace dbus_utils |
| } // namespace chromeos |
| |
| #endif // LIBCHROMEOS_CHROMEOS_DBUS_DBUS_PARAM_READER_H_ |