123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- /* 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
- <http://www.gnu.org/licenses/>. */
- #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<typename T>
- 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<typename T>
- class argument_wrapper<T *>
- {
- 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<T>::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<type> 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<typename R, typename... Arg>
- 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<int I, typename... T>
- typename std::enable_if<I == sizeof... (T), status>::type
- unmarshall (connection *, std::tuple<T...> &)
- {
- return OK;
- }
- // Unmarshall this argument, then unmarshall all subsequent args.
- template<int I, typename... T>
- typename std::enable_if<I < sizeof... (T), status>::type
- unmarshall (connection *conn, std::tuple<T...> &value)
- {
- if (!std::get<I> (value).unmarshall (conn))
- return FAIL;
- return unmarshall<I + 1, T...> (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<typename R, typename... Arg>
- class invoker
- {
- // Base case -- we can call the function.
- template<int I, R func (connection *, Arg...), typename... T>
- static typename std::enable_if<I == sizeof... (Arg), R>::type
- call (connection *conn, const std::tuple<argument_wrapper<Arg>...> &,
- T... args)
- {
- return func (conn, args...);
- }
- // Unpack one argument and continue the recursion.
- template<int I, R func (connection *, Arg...), typename... T>
- static typename std::enable_if<I < sizeof... (Arg), R>::type
- call (connection *conn, const std::tuple<argument_wrapper<Arg>...> &value,
- T... args)
- {
- return call<I + 1, func> (conn, value, args...,
- std::get<I> (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<R func (connection *, Arg...)>
- static status
- invoke (connection *conn)
- {
- if (!unmarshall_check (conn, sizeof... (Arg)))
- return FAIL;
- std::tuple<argument_wrapper<Arg>...> 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
|