| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef IPC_IPC_MESSAGE_TEMPLATES_H_ |
| #define IPC_IPC_MESSAGE_TEMPLATES_H_ |
| |
| #include <stdint.h> |
| |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/tuple.h" |
| #include "build/build_config.h" |
| #include "ipc/ipc_message.h" |
| #include "ipc/ipc_message_utils.h" |
| |
| namespace IPC { |
| |
| template <typename Tuple, size_t... Ns> |
| auto TupleForwardImpl(Tuple&& tuple, std::index_sequence<Ns...>) -> decltype( |
| std::forward_as_tuple(std::get<Ns>(std::forward<Tuple>(tuple))...)) { |
| return std::forward_as_tuple(std::get<Ns>(std::forward<Tuple>(tuple))...); |
| } |
| |
| // Transforms std::tuple contents to the forwarding form. |
| // Example: |
| // std::tuple<int, int&, const int&, int&&>&& |
| // -> std::tuple<int&&, int&, const int&, int&&>. |
| // const std::tuple<int, const int&, int&&>& |
| // -> std::tuple<const int&, int&, const int&, int&>. |
| // |
| // TupleForward(std::make_tuple(a, b, c)) is equivalent to |
| // std::forward_as_tuple(a, b, c). |
| template <typename Tuple> |
| auto TupleForward(Tuple&& tuple) -> decltype(TupleForwardImpl( |
| std::forward<Tuple>(tuple), |
| std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>())) { |
| return TupleForwardImpl( |
| std::forward<Tuple>(tuple), |
| std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()); |
| } |
| |
| // This function is for all the async IPCs that don't pass an extra parameter |
| // using IPC_BEGIN_MESSAGE_MAP_WITH_PARAM. |
| template <typename ObjT, typename Method, typename P, typename Tuple> |
| void DispatchToMethod(ObjT* obj, Method method, P*, Tuple&& tuple) { |
| base::DispatchToMethod(obj, method, std::forward<Tuple>(tuple)); |
| } |
| |
| template <typename ObjT, |
| typename Method, |
| typename P, |
| typename Tuple, |
| size_t... Ns> |
| void DispatchToMethodImpl(ObjT* obj, |
| Method method, |
| P* parameter, |
| Tuple&& tuple, |
| std::index_sequence<Ns...>) { |
| (obj->*method)(parameter, std::get<Ns>(std::forward<Tuple>(tuple))...); |
| } |
| |
| // The following function is for async IPCs which have a dispatcher with an |
| // extra parameter specified using IPC_BEGIN_MESSAGE_MAP_WITH_PARAM. |
| template <typename ObjT, typename P, typename... Args, typename Tuple> |
| std::enable_if_t<sizeof...(Args) == std::tuple_size<std::decay_t<Tuple>>::value> |
| DispatchToMethod(ObjT* obj, |
| void (ObjT::*method)(P*, Args...), |
| P* parameter, |
| Tuple&& tuple) { |
| constexpr size_t size = std::tuple_size<std::decay_t<Tuple>>::value; |
| DispatchToMethodImpl(obj, method, parameter, std::forward<Tuple>(tuple), |
| std::make_index_sequence<size>()); |
| } |
| |
| enum class MessageKind { |
| CONTROL, |
| ROUTED, |
| }; |
| |
| // Routing is a helper struct so MessageT's private common constructor has a |
| // different type signature than the public "int32_t routing_id" one. |
| struct Routing { |
| explicit Routing(int32_t id) : id(id) {} |
| int32_t id; |
| }; |
| |
| // We want to restrict MessageT's constructors so that a routing_id is always |
| // provided for ROUTED messages and never provided for CONTROL messages, so |
| // use the SFINAE technique from N4387's "Implementation Hint" section. |
| #if defined(COMPILER_MSVC) |
| // MSVC 2013 doesn't support default arguments for template member functions |
| // of templated classes, so there we have to rely on the DCHECKs instead. |
| // TODO(mdempsky): Reevaluate once MSVC 2015. |
| #define IPC_MESSAGET_SFINAE(x) |
| #else |
| #define IPC_MESSAGET_SFINAE(x) \ |
| template <bool X = (x), typename std::enable_if<X, bool>::type = false> |
| #endif |
| |
| // MessageT is the common template used for all user-defined message types. |
| // It's intended to be used via the macros defined in ipc_message_macros.h. |
| template <typename Meta, |
| typename InTuple = typename Meta::InTuple, |
| typename OutTuple = typename Meta::OutTuple> |
| class MessageT; |
| |
| // Asynchronous message partial specialization. |
| template <typename Meta, typename... Ins> |
| class MessageT<Meta, std::tuple<Ins...>, void> : public Message { |
| public: |
| using Param = std::tuple<Ins...>; |
| enum { ID = Meta::ID }; |
| |
| // TODO(mdempsky): Remove. Uses of MyMessage::Schema::Param can be replaced |
| // with just MyMessage::Param. |
| using Schema = MessageT; |
| |
| IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::CONTROL) |
| MessageT(const Ins&... ins) : MessageT(Routing(MSG_ROUTING_CONTROL), ins...) { |
| DCHECK(Meta::kKind == MessageKind::CONTROL) << Meta::kName; |
| } |
| |
| IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::ROUTED) |
| MessageT(int32_t routing_id, const Ins&... ins) |
| : MessageT(Routing(routing_id), ins...) { |
| DCHECK(Meta::kKind == MessageKind::ROUTED) << Meta::kName; |
| } |
| |
| static bool Read(const Message* msg, Param* p); |
| static void Log(std::string* name, const Message* msg, std::string* l); |
| |
| template <class T, class S, class P, class Method> |
| static bool Dispatch(const Message* msg, |
| T* obj, |
| S* sender, |
| P* parameter, |
| Method func) { |
| TRACE_EVENT0("ipc", Meta::kName); |
| Param p; |
| if (Read(msg, &p)) { |
| DispatchToMethod(obj, func, parameter, std::move(p)); |
| return true; |
| } |
| return false; |
| } |
| |
| private: |
| MessageT(Routing routing, const Ins&... ins); |
| }; |
| |
| // Synchronous message partial specialization. |
| template <typename Meta, typename... Ins, typename... Outs> |
| class MessageT<Meta, std::tuple<Ins...>, std::tuple<Outs...>> |
| : public SyncMessage { |
| public: |
| using SendParam = std::tuple<Ins...>; |
| using ReplyParam = std::tuple<Outs...>; |
| enum { ID = Meta::ID }; |
| |
| // TODO(mdempsky): Remove. Uses of MyMessage::Schema::{Send,Reply}Param can |
| // be replaced with just MyMessage::{Send,Reply}Param. |
| using Schema = MessageT; |
| |
| IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::CONTROL) |
| MessageT(const Ins&... ins, Outs*... outs) |
| : MessageT(Routing(MSG_ROUTING_CONTROL), ins..., outs...) { |
| DCHECK(Meta::kKind == MessageKind::CONTROL) << Meta::kName; |
| } |
| |
| IPC_MESSAGET_SFINAE(Meta::kKind == MessageKind::ROUTED) |
| MessageT(int32_t routing_id, const Ins&... ins, Outs*... outs) |
| : MessageT(Routing(routing_id), ins..., outs...) { |
| DCHECK(Meta::kKind == MessageKind::ROUTED) << Meta::kName; |
| } |
| |
| static bool ReadSendParam(const Message* msg, SendParam* p); |
| static bool ReadReplyParam(const Message* msg, ReplyParam* p); |
| static void WriteReplyParams(Message* reply, const Outs&... outs); |
| static void Log(std::string* name, const Message* msg, std::string* l); |
| |
| template <class T, class S, class P, class Method> |
| static bool Dispatch(const Message* msg, |
| T* obj, |
| S* sender, |
| P* /* parameter */, |
| Method func) { |
| TRACE_EVENT0("ipc", Meta::kName); |
| SendParam send_params; |
| bool ok = ReadSendParam(msg, &send_params); |
| Message* reply = SyncMessage::GenerateReply(msg); |
| if (!ok) { |
| NOTREACHED() << "Error deserializing message " << msg->type(); |
| reply->set_reply_error(); |
| sender->Send(reply); |
| return false; |
| } |
| |
| ReplyParam reply_params; |
| base::DispatchToMethod(obj, func, std::move(send_params), &reply_params); |
| WriteParam(reply, reply_params); |
| LogReplyParamsToMessage(reply_params, msg); |
| sender->Send(reply); |
| return true; |
| } |
| |
| template <class T, class P, class Method> |
| static bool DispatchDelayReply(const Message* msg, |
| T* obj, |
| P* /* parameter */, |
| Method func) { |
| TRACE_EVENT0("ipc", Meta::kName); |
| SendParam send_params; |
| bool ok = ReadSendParam(msg, &send_params); |
| Message* reply = SyncMessage::GenerateReply(msg); |
| if (!ok) { |
| NOTREACHED() << "Error deserializing message " << msg->type(); |
| reply->set_reply_error(); |
| obj->Send(reply); |
| return false; |
| } |
| |
| std::tuple<Message&> t = std::tie(*reply); |
| ConnectMessageAndReply(msg, reply); |
| base::DispatchToMethod(obj, func, std::move(send_params), &t); |
| return true; |
| } |
| |
| template <class T, class P, class Method> |
| static bool DispatchWithParamDelayReply(const Message* msg, |
| T* obj, |
| P* parameter, |
| Method func) { |
| TRACE_EVENT0("ipc", Meta::kName); |
| SendParam send_params; |
| bool ok = ReadSendParam(msg, &send_params); |
| Message* reply = SyncMessage::GenerateReply(msg); |
| if (!ok) { |
| NOTREACHED() << "Error deserializing message " << msg->type(); |
| reply->set_reply_error(); |
| obj->Send(reply); |
| return false; |
| } |
| |
| std::tuple<Message&> t = std::tie(*reply); |
| ConnectMessageAndReply(msg, reply); |
| std::tuple<P*> parameter_tuple(parameter); |
| base::DispatchToMethod( |
| obj, func, |
| std::tuple_cat(std::move(parameter_tuple), TupleForward(send_params)), |
| &t); |
| return true; |
| } |
| |
| private: |
| MessageT(Routing routing, const Ins&... ins, Outs*... outs); |
| }; |
| |
| } // namespace IPC |
| |
| #if defined(IPC_MESSAGE_IMPL) |
| #include "ipc/ipc_message_templates_impl.h" |
| #endif |
| |
| #endif // IPC_IPC_MESSAGE_TEMPLATES_H_ |