diff options
Diffstat (limited to 'brillo/dbus/dbus_param_reader.h')
-rw-r--r-- | brillo/dbus/dbus_param_reader.h | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/brillo/dbus/dbus_param_reader.h b/brillo/dbus/dbus_param_reader.h new file mode 100644 index 0000000..f72f93c --- /dev/null +++ b/brillo/dbus/dbus_param_reader.h @@ -0,0 +1,165 @@ +// 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_BRILLO_DBUS_DBUS_PARAM_READER_H_ +#define LIBCHROMEOS_BRILLO_DBUS_DBUS_PARAM_READER_H_ + +#include <type_traits> + +#include <brillo/dbus/data_serialization.h> +#include <brillo/dbus/utils.h> +#include <brillo/errors/error.h> +#include <brillo/errors/error_codes.h> +#include <dbus/message.h> + +namespace brillo { +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 brillo + +#endif // LIBCHROMEOS_BRILLO_DBUS_DBUS_PARAM_READER_H_ |