Commit | Line | Data |
---|---|---|
07e253aa PA |
1 | /* Copyright (C) 2017 Free Software Foundation, Inc. |
2 | ||
3 | This file is part of GDB. | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | #ifndef COMMON_FUNCTION_VIEW_H | |
19 | #define COMMON_FUNCTION_VIEW_H | |
20 | ||
21 | /* function_view is a polymorphic type-erasing wrapper class that | |
22 | encapsulates a non-owning reference to arbitrary callable objects. | |
23 | ||
24 | A way to put it is that function_view is to std::function like | |
25 | std::string_view is to std::string. While std::function stores a | |
26 | type-erased callable object internally, function_view holds a | |
27 | type-erased reference to an external callable object. | |
28 | ||
29 | This is meant to be used as callback type of a function that: | |
30 | ||
31 | #1 - Takes a callback as parameter. | |
32 | ||
33 | #2 - Wants to support arbitrary callable objects as callback type | |
34 | (e.g., stateful function objects, lambda closures, free | |
35 | functions). | |
36 | ||
37 | #3 - Does not store the callback anywhere; instead the function | |
38 | just calls the callback directly or forwards it to some | |
39 | other function that calls it. | |
40 | ||
41 | #4 - Can't be, or we don't want it to be, a template function | |
42 | with the callable type as template parameter. For example, | |
43 | when the callback is a parameter of a virtual member | |
44 | function, or when putting the function template in a header | |
45 | would expose too much implementation detail. | |
46 | ||
47 | Note that the C-style "function pointer" + "void *data" callback | |
48 | parameter idiom fails requirement #2 above. Please don't add new | |
49 | uses of that idiom. I.e., something like this wouldn't work; | |
50 | ||
51 | typedef bool (iterate_over_foos_cb) (foo *f, void *user_data), | |
52 | void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data); | |
53 | ||
54 | foo *find_foo_by_type (int type) | |
55 | { | |
56 | foo *found = nullptr; | |
57 | ||
58 | iterate_over_foos ([&] (foo *f, void *data) | |
59 | { | |
60 | if (foo->type == type) | |
61 | { | |
62 | found = foo; | |
63 | return true; // stop iterating | |
64 | } | |
65 | return false; // continue iterating | |
66 | }, NULL); | |
67 | ||
68 | return found; | |
69 | } | |
70 | ||
71 | The above wouldn't compile, because lambdas with captures can't be | |
72 | implicitly converted to a function pointer (because a capture means | |
73 | some context data must be passed to the lambda somehow). | |
74 | ||
75 | C++11 gave us std::function as type-erased wrapper around arbitrary | |
76 | callables, however, std::function is not an ideal fit for transient | |
77 | callbacks such as the use case above. For this use case, which is | |
78 | quite pervasive, a function_view is a better choice, because while | |
79 | function_view is light and does not require any heap allocation, | |
80 | std::function is a heavy-weight object with value semantics that | |
81 | generally requires a heap allocation on construction/assignment of | |
82 | the target callable. In addition, while it is possible to use | |
83 | std::function in such a way that avoids most of the overhead by | |
84 | making sure to only construct it with callables of types that fit | |
85 | std::function's small object optimization, such as function | |
86 | pointers and std::reference_wrapper callables, that is quite | |
87 | inconvenient in practice, because restricting to free-function | |
88 | callables would imply no state/capture/closure, which we need in | |
89 | most cases, and std::reference_wrapper implies remembering to use | |
90 | std::ref/std::cref where the callable is constructed, with the | |
91 | added inconvenience that std::ref/std::cref have deleted rvalue-ref | |
92 | overloads, meaning you can't use unnamed/temporary lambdas with | |
93 | them. | |
94 | ||
95 | Note that because function_view is a non-owning view of a callable, | |
96 | care must be taken to ensure that the callable outlives the | |
97 | function_view that calls it. This is not really a problem for the | |
98 | use case function_view is intended for, such as passing a temporary | |
99 | function object / lambda to a function that accepts a callback, | |
100 | because in those cases, the temporary is guaranteed to be live | |
101 | until the called function returns. | |
102 | ||
103 | Calling a function_view with no associated target is undefined, | |
104 | unlike with std::function, which throws std::bad_function_call. | |
105 | This is by design, to avoid the otherwise necessary NULL check in | |
106 | function_view::operator(). | |
107 | ||
108 | Since function_view objects are small (a pair of pointers), they | |
109 | should generally be passed around by value. | |
110 | ||
111 | Usage: | |
112 | ||
113 | Given this function that accepts a callback: | |
114 | ||
115 | void | |
116 | iterate_over_foos (gdb::function_view<void (foo *)> callback) | |
117 | { | |
118 | for (auto &foo : foos) | |
119 | callback (&foo); | |
120 | } | |
121 | ||
122 | you can call it like this, passing a lambda as callback: | |
123 | ||
124 | iterate_over_foos ([&] (foo *f) | |
125 | { | |
126 | process_one_foo (f); | |
127 | }); | |
128 | ||
129 | or like this, passing a function object as callback: | |
130 | ||
131 | struct function_object | |
132 | { | |
133 | void operator() (foo *f) | |
134 | { | |
135 | if (s->check ()) | |
136 | process_one_foo (f); | |
137 | } | |
138 | ||
139 | // some state | |
140 | state *s; | |
141 | }; | |
142 | ||
143 | state mystate; | |
144 | function_object matcher {&mystate}; | |
145 | iterate_over_foos (matcher); | |
146 | ||
147 | or like this, passing a function pointer as callback: | |
148 | ||
149 | iterate_over_foos (process_one_foo); | |
150 | ||
151 | You can find unit tests covering the whole API in | |
152 | unittests/function-view-selftests.c. */ | |
153 | ||
154 | namespace gdb { | |
155 | ||
156 | namespace traits { | |
157 | /* A few trait helpers. */ | |
158 | template<typename Predicate> | |
159 | struct Not : public std::integral_constant<bool, !Predicate::value> | |
160 | {}; | |
161 | ||
162 | template<typename...> | |
163 | struct Or; | |
164 | ||
165 | template<> | |
166 | struct Or<> : public std::false_type | |
167 | {}; | |
168 | ||
169 | template<typename B1> | |
170 | struct Or<B1> : public B1 | |
171 | {}; | |
172 | ||
173 | template<typename B1, typename B2> | |
174 | struct Or<B1, B2> | |
175 | : public std::conditional<B1::value, B1, B2>::type | |
176 | {}; | |
177 | ||
178 | template<typename B1,typename B2,typename B3, typename... Bn> | |
179 | struct Or<B1, B2, B3, Bn...> | |
180 | : public std::conditional<B1::value, B1, Or<B2, B3, Bn...>>::type | |
181 | {}; | |
182 | } /* namespace traits */ | |
183 | ||
184 | namespace fv_detail { | |
185 | /* Bits shared by all function_view instantiations that do not depend | |
186 | on the template parameters. */ | |
187 | ||
188 | /* Storage for the erased callable. This is a union in order to be | |
189 | able to save both a function object (data) pointer or a function | |
190 | pointer without triggering undefined behavior. */ | |
191 | union erased_callable | |
192 | { | |
193 | /* For function objects. */ | |
194 | void *data; | |
195 | ||
196 | /* For function pointers. */ | |
197 | void (*fn) (); | |
198 | }; | |
199 | ||
200 | } /* namespace fv_detail */ | |
201 | ||
202 | /* Use partial specialization to get access to the callable's | |
203 | signature. */ | |
204 | template<class Signature> | |
205 | struct function_view; | |
206 | ||
207 | template<typename Res, typename... Args> | |
208 | class function_view<Res (Args...)> | |
209 | { | |
210 | template<typename From, typename To> | |
211 | using CompatibleReturnType | |
212 | = traits::Or<std::is_void<To>, | |
213 | std::is_same<From, To>, | |
214 | std::is_convertible<From, To>>; | |
215 | ||
216 | /* True if Func can be called with Args, and either the result is | |
217 | Res, convertible to Res or Res is void. */ | |
218 | template<typename Callable, | |
219 | typename Res2 = typename std::result_of<Callable &(Args...)>::type> | |
220 | struct IsCompatibleCallable : CompatibleReturnType<Res2, Res> | |
221 | {}; | |
222 | ||
223 | /* True if Callable is a function_view. Used to avoid hijacking the | |
224 | copy ctor. */ | |
225 | template <typename Callable> | |
226 | struct IsFunctionView | |
227 | : std::is_same<function_view, typename std::decay<Callable>::type> | |
228 | {}; | |
229 | ||
230 | /* Helper to make SFINAE logic easier to read. */ | |
231 | template<typename Condition> | |
232 | using Requires = typename std::enable_if<Condition::value, void>::type; | |
233 | ||
234 | public: | |
235 | ||
236 | /* NULL by default. */ | |
237 | constexpr function_view () noexcept | |
238 | : m_erased_callable {}, | |
239 | m_invoker {} | |
240 | {} | |
241 | ||
242 | /* Default copy/assignment is fine. */ | |
243 | function_view (const function_view &) = default; | |
244 | function_view &operator= (const function_view &) = default; | |
245 | ||
246 | /* This is the main entry point. Use SFINAE to avoid hijacking the | |
247 | copy constructor and to ensure that the target type is | |
248 | compatible. */ | |
249 | template | |
250 | <typename Callable, | |
251 | typename = Requires<traits::Not<IsFunctionView<Callable>>>, | |
252 | typename = Requires<IsCompatibleCallable<Callable>>> | |
253 | function_view (Callable &&callable) noexcept | |
254 | { | |
255 | bind (callable); | |
256 | } | |
257 | ||
258 | /* Construct a NULL function_view. */ | |
259 | constexpr function_view (std::nullptr_t) noexcept | |
260 | : m_erased_callable {}, | |
261 | m_invoker {} | |
262 | {} | |
263 | ||
264 | /* Clear a function_view. */ | |
265 | function_view &operator= (std::nullptr_t) noexcept | |
266 | { | |
267 | m_invoker = nullptr; | |
268 | return *this; | |
269 | } | |
270 | ||
271 | /* Return true if the wrapper has a target, false otherwise. Note | |
272 | we check M_INVOKER instead of M_ERASED_CALLABLE because we don't | |
273 | know which member of the union is active right now. */ | |
274 | constexpr explicit operator bool () const noexcept | |
275 | { return m_invoker != nullptr; } | |
276 | ||
277 | /* Call the callable. */ | |
278 | Res operator () (Args... args) const | |
279 | { return m_invoker (m_erased_callable, std::forward<Args> (args)...); } | |
280 | ||
281 | private: | |
282 | ||
283 | /* Bind this function_view to a compatible function object | |
284 | reference. */ | |
285 | template <typename Callable> | |
286 | void bind (Callable &callable) noexcept | |
287 | { | |
288 | m_erased_callable.data = (void *) std::addressof (callable); | |
289 | m_invoker = [] (fv_detail::erased_callable ecall, Args... args) | |
290 | noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res | |
291 | { | |
292 | auto &restored_callable = *static_cast<Callable *> (ecall.data); | |
293 | /* The explicit cast to Res avoids a compile error when Res is | |
294 | void and the callable returns non-void. */ | |
295 | return (Res) restored_callable (std::forward<Args> (args)...); | |
296 | }; | |
297 | } | |
298 | ||
299 | /* Bind this function_view to a compatible function pointer. | |
300 | ||
301 | Making this a separate function allows avoiding one indirection, | |
302 | by storing the function pointer directly in the storage, instead | |
303 | of a pointer to pointer. erased_callable is then a union in | |
304 | order to avoid storing a function pointer as a data pointer here, | |
305 | which would be undefined. */ | |
306 | template<class Res2, typename... Args2> | |
307 | void bind (Res2 (*fn) (Args2...)) noexcept | |
308 | { | |
309 | m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn); | |
310 | m_invoker = [] (fv_detail::erased_callable ecall, Args... args) | |
311 | noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res | |
312 | { | |
313 | auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn); | |
314 | /* The explicit cast to Res avoids a compile error when Res is | |
315 | void and the callable returns non-void. */ | |
316 | return (Res) restored_fn (std::forward<Args> (args)...); | |
317 | }; | |
318 | } | |
319 | ||
320 | /* Storage for the erased callable. */ | |
321 | fv_detail::erased_callable m_erased_callable; | |
322 | ||
323 | /* The invoker. This is set to a capture-less lambda by one of the | |
324 | 'bind' overloads. The lambda restores the right type of the | |
325 | callable (which is passed as first argument), and forwards the | |
326 | args. */ | |
327 | Res (*m_invoker) (fv_detail::erased_callable, Args...); | |
328 | }; | |
329 | ||
330 | /* Allow comparison with NULL. Defer the work to the in-class | |
331 | operator bool implementation. */ | |
332 | ||
333 | template<typename Res, typename... Args> | |
334 | constexpr inline bool | |
335 | operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept | |
336 | { return !static_cast<bool> (f); } | |
337 | ||
338 | template<typename Res, typename... Args> | |
339 | constexpr inline bool | |
340 | operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept | |
341 | { return !static_cast<bool> (f); } | |
342 | ||
343 | template<typename Res, typename... Args> | |
344 | constexpr inline bool | |
345 | operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept | |
346 | { return static_cast<bool> (f); } | |
347 | ||
348 | template<typename Res, typename... Args> | |
349 | constexpr inline bool | |
350 | operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept | |
351 | { return static_cast<bool> (f); } | |
352 | ||
353 | } /* namespace gdb */ | |
354 | ||
355 | #endif |