1/* RPC call and callback templates
2 Copyright (C) 2014-2023 Free Software Foundation, Inc.
3
4This file is part of GCC.
5
6GCC is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free
8Software Foundation; either version 3, or (at your option) any later
9version.
10
11GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or
13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14for more details.
15
16You should have received a copy of the GNU General Public License
17along 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
27namespace 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
40 argument_wrapper () { }
41 ~argument_wrapper () { }
42
43 argument_wrapper (const argument_wrapper &) = delete;
44 argument_wrapper &operator= (const argument_wrapper &) = delete;
45
46 T get () const { return m_object; }
47
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
71 const type *get () const
72 {
73 return m_object.get ();
74 }
75
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
106 call (connection *conn, const char *method, R *result, Arg... args)
107 {
108 if (!conn->send (c: 'Q'))
109 return FAIL;
110 if (!marshall (conn, method))
111 return FAIL;
112 if (!marshall (conn, scalar: (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
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
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
173 invoke (connection *conn)
174 {
175 if (!unmarshall_check (conn, v: 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 (c: 'R'))
184 return FAIL;
185 return marshall (conn, result);
186 }
187 };
188};
189
190#endif // CC1_PLUGIN_RPC_HH
191

source code of libcc1/rpc.hh