This patch completes the transition of libcc1 from the use of separate template functions for different arities to the use of variadic functions. This is how I had wanted it to work from the very beginning, and is possible now with C++11.
I had thought that variadic callbacks required C++17, but it turns out that the approach taken here is basically equivalent to std::apply -- just a bit wordier. libcc1/ChangeLog 2021-04-27 Tom Tromey <t...@tromey.com> * rpc.hh (argument_wrapper) <get>: Replace cast operator. (argument_wrapper<T *>) <get>: Likewise. (unmarshall): Add std::tuple overloads. (callback): Remove. (class invoker): New. * libcp1plugin.cc (plugin_init): Update. * libcp1.cc (libcp1::add_callbacks): Update. * libcc1plugin.cc (plugin_init): Update. * libcc1.cc (libcc1::add_callbacks): Update. * connection.cc (cc1_plugin::connection::do_wait): Update. --- libcc1/ChangeLog | 13 +++ libcc1/connection.cc | 2 +- libcc1/libcc1.cc | 12 +-- libcc1/libcc1plugin.cc | 20 ++-- libcc1/libcp1.cc | 17 ++-- libcc1/libcp1plugin.cc | 20 ++-- libcc1/rpc.hh | 219 +++++++++++------------------------------ 7 files changed, 101 insertions(+), 202 deletions(-) diff --git a/libcc1/connection.cc b/libcc1/connection.cc index 66d573911080..45560b9b790e 100644 --- a/libcc1/connection.cc +++ b/libcc1/connection.cc @@ -129,7 +129,7 @@ cc1_plugin::connection::do_wait (bool want_result) return FAIL; callback_ftype *callback - = m_callbacks.find_callback (method_name); + = m_callbacks.find_callback (method_name.get ()); // The call to CALLBACK is where we may end up in a // reentrant call. if (callback == NULL || !callback (this)) diff --git a/libcc1/libcc1.cc b/libcc1/libcc1.cc index cbc54ee0a044..febadc8420b0 100644 --- a/libcc1/libcc1.cc +++ b/libcc1/libcc1.cc @@ -143,15 +143,13 @@ void libcc1::add_callbacks () { cc1_plugin::callback_ftype *fun - = cc1_plugin::callback<int, - enum gcc_c_oracle_request, - const char *, - c_call_binding_oracle>; + = cc1_plugin::invoker<int, + enum gcc_c_oracle_request, + const char *>::invoke<c_call_binding_oracle>; connection->add_callback ("binding_oracle", fun); - fun = cc1_plugin::callback<gcc_address, - const char *, - c_call_symbol_address>; + fun = cc1_plugin::invoker<gcc_address, + const char *>::invoke<c_call_symbol_address>; connection->add_callback ("address_oracle", fun); } diff --git a/libcc1/libcc1plugin.cc b/libcc1/libcc1plugin.cc index d6951ab1a4d2..4d6a3a11ee22 100644 --- a/libcc1/libcc1plugin.cc +++ b/libcc1/libcc1plugin.cc @@ -762,46 +762,46 @@ plugin_init (struct plugin_name_args *plugin_info, #define GCC_METHOD0(R, N) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, plugin_ ## N>; \ + = cc1_plugin::invoker<R>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD1(R, N, A) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, plugin_ ## N>; \ + = cc1_plugin::invoker<R, A>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD2(R, N, A, B) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD3(R, N, A, B, C) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD4(R, N, A, B, C, D) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, D, \ - plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C, \ + D>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD5(R, N, A, B, C, D, E) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, D, E, \ - plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C, D, \ + E>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, D, E, F, G, \ - plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C, D, \ + E, F, G>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } diff --git a/libcc1/libcp1.cc b/libcc1/libcp1.cc index d22d9dc6af8c..a93349833901 100644 --- a/libcc1/libcp1.cc +++ b/libcc1/libcp1.cc @@ -166,23 +166,18 @@ void libcp1::add_callbacks () { cc1_plugin::callback_ftype *fun - = cc1_plugin::callback<int, - enum gcc_cp_oracle_request, - const char *, - cp_call_binding_oracle>; + = cc1_plugin::invoker<int, enum gcc_cp_oracle_request, + const char *>::invoke<cp_call_binding_oracle>; connection->add_callback ("binding_oracle", fun); - fun = cc1_plugin::callback<gcc_address, - const char *, - cp_call_symbol_address>; + fun = cc1_plugin::invoker<gcc_address, + const char *>::invoke<cp_call_symbol_address>; connection->add_callback ("address_oracle", fun); - fun = cc1_plugin::callback<int, - cp_call_enter_scope>; + fun = cc1_plugin::invoker<int>::invoke<cp_call_enter_scope>; connection->add_callback ("enter_scope", fun); - fun = cc1_plugin::callback<int, - cp_call_leave_scope>; + fun = cc1_plugin::invoker<int>::invoke<cp_call_leave_scope>; connection->add_callback ("leave_scope", fun); } diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc index 64cde651139c..79694b919641 100644 --- a/libcc1/libcp1plugin.cc +++ b/libcc1/libcp1plugin.cc @@ -3509,46 +3509,46 @@ plugin_init (struct plugin_name_args *plugin_info, #define GCC_METHOD0(R, N) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, plugin_ ## N>; \ + = cc1_plugin::invoker<R>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD1(R, N, A) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, plugin_ ## N>; \ + = cc1_plugin::invoker<R, A>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD2(R, N, A, B) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD3(R, N, A, B, C) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD4(R, N, A, B, C, D) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, D, \ - plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C, \ + D>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD5(R, N, A, B, C, D, E) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, D, E, \ - plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C, \ + D, E>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } #define GCC_METHOD7(R, N, A, B, C, D, E, F, G) \ { \ cc1_plugin::callback_ftype *fun \ - = cc1_plugin::callback<R, A, B, C, D, E, F, G, \ - plugin_ ## N>; \ + = cc1_plugin::invoker<R, A, B, C, \ + D, E, F, G>::invoke<plugin_ ## N>; \ current_context->add_callback (# N, fun); \ } diff --git a/libcc1/rpc.hh b/libcc1/rpc.hh index 09cd7bdda616..8e43fa146dcc 100644 --- a/libcc1/rpc.hh +++ b/libcc1/rpc.hh @@ -43,7 +43,7 @@ namespace cc1_plugin argument_wrapper (const argument_wrapper &) = delete; argument_wrapper &operator= (const argument_wrapper &) = delete; - operator T () const { return m_object; } + T get () const { return m_object; } status unmarshall (connection *conn) { @@ -68,7 +68,7 @@ namespace cc1_plugin typedef typename std::remove_const<T>::type type; - operator const type * () const + const type *get () const { return m_object.get (); } @@ -88,17 +88,14 @@ namespace cc1_plugin }; // There are two kinds of template functions here: "call" and - // "callback". "call" is implemented with variadic templates, but - // "callback" is repeated multiple times to handle different numbers - // of arguments. (This could be improved with C++17 and - // std::apply.) + // "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 "callback" template is used when receiving a remote procedure + // 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 @@ -123,175 +120,71 @@ namespace cc1_plugin return OK; } - template<typename R, R (*func) (connection *)> - status - callback (connection *conn) + // 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...> &) { - R result; - - if (!unmarshall_check (conn, 0)) - return FAIL; - result = func (conn); - if (!conn->send ('R')) - return FAIL; - return marshall (conn, result); - } - - template<typename R, typename A, R (*func) (connection *, A)> - status - callback (connection *conn) - { - argument_wrapper<A> arg; - R result; - - if (!unmarshall_check (conn, 1)) - return FAIL; - if (!arg.unmarshall (conn)) - return FAIL; - result = func (conn, arg); - if (!conn->send ('R')) - return FAIL; - return marshall (conn, result); + return OK; } - template<typename R, typename A1, typename A2, R (*func) (connection *, - A1, A2)> - status - callback (connection *conn) + // 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) { - argument_wrapper<A1> arg1; - argument_wrapper<A2> arg2; - R result; - - if (!unmarshall_check (conn, 2)) - return FAIL; - if (!arg1.unmarshall (conn)) - return FAIL; - if (!arg2.unmarshall (conn)) + if (!std::get<I> (value).unmarshall (conn)) return FAIL; - result = func (conn, arg1, arg2); - if (!conn->send ('R')) - return FAIL; - return marshall (conn, result); + return unmarshall<I + 1, T...> (conn, value); } - template<typename R, typename A1, typename A2, typename A3, - R (*func) (connection *, A1, A2, A3)> - status - callback (connection *conn) + // 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 { - argument_wrapper<A1> arg1; - argument_wrapper<A2> arg2; - argument_wrapper<A3> arg3; - R result; + // 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...); + } - if (!unmarshall_check (conn, 3)) - return FAIL; - if (!arg1.unmarshall (conn)) - return FAIL; - if (!arg2.unmarshall (conn)) - return FAIL; - if (!arg3.unmarshall (conn)) - return FAIL; - result = func (conn, arg1, arg2, arg3); - if (!conn->send ('R')) - return FAIL; - return marshall (conn, result); - } + // 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 ()); + } - template<typename R, typename A1, typename A2, typename A3, typename A4, - R (*func) (connection *, A1, A2, A3, A4)> - status - callback (connection *conn) - { - argument_wrapper<A1> arg1; - argument_wrapper<A2> arg2; - argument_wrapper<A3> arg3; - argument_wrapper<A4> arg4; - R result; + public: - if (!unmarshall_check (conn, 4)) - return FAIL; - if (!arg1.unmarshall (conn)) - return FAIL; - if (!arg2.unmarshall (conn)) - return FAIL; - if (!arg3.unmarshall (conn)) - return FAIL; - if (!arg4.unmarshall (conn)) - return FAIL; - result = func (conn, arg1, arg2, arg3, arg4); - if (!conn->send ('R')) - return FAIL; - return marshall (conn, result); - } + // 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; - template<typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, R (*func) (connection *, A1, A2, A3, A4, A5)> - status - callback (connection *conn) - { - argument_wrapper<A1> arg1; - argument_wrapper<A2> arg2; - argument_wrapper<A3> arg3; - argument_wrapper<A4> arg4; - argument_wrapper<A5> arg5; - R result; - - if (!unmarshall_check (conn, 5)) - return FAIL; - if (!arg1.unmarshall (conn)) - return FAIL; - if (!arg2.unmarshall (conn)) - return FAIL; - if (!arg3.unmarshall (conn)) - return FAIL; - if (!arg4.unmarshall (conn)) - return FAIL; - if (!arg5.unmarshall (conn)) - return FAIL; - result = func (conn, arg1, arg2, arg3, arg4, arg5); - if (!conn->send ('R')) - return FAIL; - return marshall (conn, result); - } + R result = call<0, func> (conn, wrapped); - template<typename R, typename A1, typename A2, typename A3, typename A4, - typename A5, typename A6, typename A7, - R (*func) (connection *, A1, A2, A3, A4, A5, A6, A7)> - status - callback (connection *conn) - { - argument_wrapper<A1> arg1; - argument_wrapper<A2> arg2; - argument_wrapper<A3> arg3; - argument_wrapper<A4> arg4; - argument_wrapper<A5> arg5; - argument_wrapper<A6> arg6; - argument_wrapper<A7> arg7; - R result; - - if (!unmarshall_check (conn, 7)) - return FAIL; - if (!arg1.unmarshall (conn)) - return FAIL; - if (!arg2.unmarshall (conn)) - return FAIL; - if (!arg3.unmarshall (conn)) - return FAIL; - if (!arg4.unmarshall (conn)) - return FAIL; - if (!arg5.unmarshall (conn)) - return FAIL; - if (!arg6.unmarshall (conn)) - return FAIL; - if (!arg7.unmarshall (conn)) - return FAIL; - result = func (conn, arg1, arg2, arg3, arg4, arg5, arg6, arg7); - if (!conn->send ('R')) - return FAIL; - return marshall (conn, result); - } + if (!conn->send ('R')) + return FAIL; + return marshall (conn, result); + } + }; }; #endif // CC1_PLUGIN_RPC_HH -- 2.26.2