1 /* RPC call and callback templates 2 Copyright (C) 2014-2022 Free Software Foundation, Inc. 3 4 This file is part of GCC. 5 6 GCC is free software; you can redistribute it and/or modify it under 7 the terms of the GNU General Public License as published by the Free 8 Software Foundation; either version 3, or (at your option) any later 9 version. 10 11 GCC is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GCC; see the file COPYING3. If not see 18 <http://www.gnu.org/licenses/>. */ 19 20 #ifndef CC1_PLUGIN_RPC_HH 21 #define CC1_PLUGIN_RPC_HH 22 23 #include "status.hh" 24 #include "connection.hh" 25 #include "deleter.hh" 26 27 namespace cc1_plugin 28 { 29 // The plugin API may contain some "const" method parameters. 30 // However, when unmarshalling we cannot unmarshall into a const 31 // object; and furthermore we want to be able to deallocate pointers 32 // when finished with them. This wrapper class lets us properly 33 // remove the "const" and handle deallocation from pointer types. 34 35 template<typename T> 36 class argument_wrapper 37 { 38 public: 39 argument_wrapper()40 argument_wrapper () { } ~argument_wrapper()41 ~argument_wrapper () { } 42 43 argument_wrapper (const argument_wrapper &) = delete; 44 argument_wrapper &operator= (const argument_wrapper &) = delete; 45 get() const46 T get () const { return m_object; } 47 unmarshall(connection * conn)48 status unmarshall (connection *conn) 49 { 50 return ::cc1_plugin::unmarshall (conn, &m_object); 51 } 52 53 private: 54 55 T m_object; 56 }; 57 58 // Specialization for any kind of pointer. 59 template<typename T> 60 class argument_wrapper<T *> 61 { 62 public: 63 argument_wrapper () = default; 64 ~argument_wrapper () = default; 65 66 argument_wrapper (const argument_wrapper &) = delete; 67 argument_wrapper &operator= (const argument_wrapper &) = delete; 68 69 typedef typename std::remove_const<T>::type type; 70 get() const71 const type *get () const 72 { 73 return m_object.get (); 74 } 75 unmarshall(connection * conn)76 status unmarshall (connection *conn) 77 { 78 type *ptr; 79 if (!::cc1_plugin::unmarshall (conn, &ptr)) 80 return FAIL; 81 m_object.reset (ptr); 82 return OK; 83 } 84 85 private: 86 87 unique_ptr<type> m_object; 88 }; 89 90 // There are two kinds of template functions here: "call" and 91 // "invoker". 92 93 // The "call" template is used for making a remote procedure call. 94 // It starts a query ('Q') packet, marshalls its arguments, waits 95 // for a result, and finally reads and returns the result via an 96 // "out" parameter. 97 98 // The "invoker" template is used when receiving a remote procedure 99 // call. This template function is suitable for use with the 100 // "callbacks" and "connection" classes. It decodes incoming 101 // arguments, passes them to the wrapped function, and finally 102 // marshalls a reply packet. 103 104 template<typename R, typename... Arg> 105 status call(connection * conn,const char * method,R * result,Arg...args)106 call (connection *conn, const char *method, R *result, Arg... args) 107 { 108 if (!conn->send ('Q')) 109 return FAIL; 110 if (!marshall (conn, method)) 111 return FAIL; 112 if (!marshall (conn, (int) sizeof... (Arg))) 113 return FAIL; 114 if (!marshall (conn, args...)) 115 return FAIL; 116 if (!conn->wait_for_result ()) 117 return FAIL; 118 if (!unmarshall (conn, result)) 119 return FAIL; 120 return OK; 121 } 122 123 // The base case -- just return OK. 124 template<int I, typename... T> 125 typename std::enable_if<I == sizeof... (T), status>::type unmarshall(connection *,std::tuple<T...> &)126 unmarshall (connection *, std::tuple<T...> &) 127 { 128 return OK; 129 } 130 131 // Unmarshall this argument, then unmarshall all subsequent args. 132 template<int I, typename... T> 133 typename std::enable_if<I < sizeof... (T), status>::type 134 unmarshall (connection *conn, std::tuple<T...> &value) 135 { 136 if (!std::get<I> (value).unmarshall (conn)) 137 return FAIL; 138 return unmarshall<I + 1, T...> (conn, value); 139 } 140 141 // Wrap a static function that is suitable for use as a callback. 142 // This is a template function inside a template class to work 143 // around limitations with multiple variadic packs. 144 template<typename R, typename... Arg> 145 class invoker 146 { 147 // Base case -- we can call the function. 148 template<int I, R func (connection *, Arg...), typename... T> 149 static typename std::enable_if<I == sizeof... (Arg), R>::type call(connection * conn,const std::tuple<argument_wrapper<Arg>...> &,T...args)150 call (connection *conn, const std::tuple<argument_wrapper<Arg>...> &, 151 T... args) 152 { 153 return func (conn, args...); 154 } 155 156 // Unpack one argument and continue the recursion. 157 template<int I, R func (connection *, Arg...), typename... T> 158 static typename std::enable_if<I < sizeof... (Arg), R>::type 159 call (connection *conn, const std::tuple<argument_wrapper<Arg>...> &value, 160 T... args) 161 { 162 return call<I + 1, func> (conn, value, args..., 163 std::get<I> (value).get ()); 164 } 165 166 public: 167 168 // A callback function that reads arguments from the connection, 169 // calls the wrapped function, and then sends the result back on 170 // the connection. 171 template<R func (connection *, Arg...)> 172 static status invoke(connection * conn)173 invoke (connection *conn) 174 { 175 if (!unmarshall_check (conn, sizeof... (Arg))) 176 return FAIL; 177 std::tuple<argument_wrapper<Arg>...> wrapped; 178 if (!unmarshall<0> (conn, wrapped)) 179 return FAIL; 180 181 R result = call<0, func> (conn, wrapped); 182 183 if (!conn->send ('R')) 184 return FAIL; 185 return marshall (conn, result); 186 } 187 }; 188 }; 189 190 #endif // CC1_PLUGIN_RPC_HH 191