/* RPC call and callback templates Copyright (C) 2014-2022 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC 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 General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #ifndef CC1_PLUGIN_RPC_HH #define CC1_PLUGIN_RPC_HH #include "status.hh" #include "connection.hh" #include "deleter.hh" namespace cc1_plugin { // The plugin API may contain some "const" method parameters. // However, when unmarshalling we cannot unmarshall into a const // object; and furthermore we want to be able to deallocate pointers // when finished with them. This wrapper class lets us properly // remove the "const" and handle deallocation from pointer types. template class argument_wrapper { public: argument_wrapper () { } ~argument_wrapper () { } argument_wrapper (const argument_wrapper &) = delete; argument_wrapper &operator= (const argument_wrapper &) = delete; T get () const { return m_object; } status unmarshall (connection *conn) { return ::cc1_plugin::unmarshall (conn, &m_object); } private: T m_object; }; // Specialization for any kind of pointer. template class argument_wrapper { public: argument_wrapper () = default; ~argument_wrapper () = default; argument_wrapper (const argument_wrapper &) = delete; argument_wrapper &operator= (const argument_wrapper &) = delete; typedef typename std::remove_const::type type; const type *get () const { return m_object.get (); } status unmarshall (connection *conn) { type *ptr; if (!::cc1_plugin::unmarshall (conn, &ptr)) return FAIL; m_object.reset (ptr); return OK; } private: unique_ptr m_object; }; // There are two kinds of template functions here: "call" and // "invoker". // The "call" template is used for making a remote procedure call. // It starts a query ('Q') packet, marshalls its arguments, waits // for a result, and finally reads and returns the result via an // "out" parameter. // The "invoker" template is used when receiving a remote procedure // call. This template function is suitable for use with the // "callbacks" and "connection" classes. It decodes incoming // arguments, passes them to the wrapped function, and finally // marshalls a reply packet. template status call (connection *conn, const char *method, R *result, Arg... args) { if (!conn->send ('Q')) return FAIL; if (!marshall (conn, method)) return FAIL; if (!marshall (conn, (int) sizeof... (Arg))) return FAIL; if (!marshall (conn, args...)) return FAIL; if (!conn->wait_for_result ()) return FAIL; if (!unmarshall (conn, result)) return FAIL; return OK; } // The base case -- just return OK. template typename std::enable_if::type unmarshall (connection *, std::tuple &) { return OK; } // Unmarshall this argument, then unmarshall all subsequent args. template typename std::enable_if::type unmarshall (connection *conn, std::tuple &value) { if (!std::get (value).unmarshall (conn)) return FAIL; return unmarshall (conn, value); } // Wrap a static function that is suitable for use as a callback. // This is a template function inside a template class to work // around limitations with multiple variadic packs. template class invoker { // Base case -- we can call the function. template static typename std::enable_if::type call (connection *conn, const std::tuple...> &, T... args) { return func (conn, args...); } // Unpack one argument and continue the recursion. template static typename std::enable_if::type call (connection *conn, const std::tuple...> &value, T... args) { return call (conn, value, args..., std::get (value).get ()); } public: // A callback function that reads arguments from the connection, // calls the wrapped function, and then sends the result back on // the connection. template static status invoke (connection *conn) { if (!unmarshall_check (conn, sizeof... (Arg))) return FAIL; std::tuple...> wrapped; if (!unmarshall<0> (conn, wrapped)) return FAIL; R result = call<0, func> (conn, wrapped); if (!conn->send ('R')) return FAIL; return marshall (conn, result); } }; }; #endif // CC1_PLUGIN_RPC_HH