]>
Commit | Line | Data |
---|---|---|
1d506c26 | 1 | /* Copyright (C) 2017-2024 Free Software Foundation, Inc. |
07e253aa PA |
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 | ||
377c3a9c PA |
151 | There's also a gdb::make_function_view function that you can use to |
152 | automatically create a function_view from a callable without having | |
153 | to specify the function_view's template parameter. E.g.: | |
154 | ||
155 | auto lambda = [&] (int) { ... }; | |
156 | auto fv = gdb::make_function_view (lambda); | |
157 | ||
158 | This can be useful for example when calling a template function | |
159 | whose function_view parameter type depends on the function's | |
160 | template parameters. In such case, you can't rely on implicit | |
161 | callable->function_view conversion for the function_view argument. | |
162 | You must pass a function_view argument already of the right type to | |
163 | the template function. E.g., with this: | |
164 | ||
165 | template<typename T> | |
166 | void my_function (T v, gdb::function_view<void(T)> callback = nullptr); | |
167 | ||
168 | this wouldn't compile: | |
169 | ||
170 | auto lambda = [&] (int) { ... }; | |
171 | my_function (1, lambda); | |
172 | ||
173 | Note that this immediately dangles the temporary lambda object: | |
174 | ||
175 | gdb::function_view<void(int)> fv = [&] (int) { ... }; // dangles | |
176 | my_function (fv); | |
177 | ||
178 | To avoid the dangling you'd have to use a named temporary for the | |
179 | lambda: | |
180 | ||
181 | auto lambda = [&] (int) { ... }; | |
182 | gdb::function_view<void(int)> fv = lambda; | |
183 | my_function (fv); | |
184 | ||
185 | Using gdb::make_function_view instead automatically deduces the | |
186 | function_view's full type, and, avoids worrying about dangling. For | |
187 | the example above, we could write instead: | |
188 | ||
189 | auto lambda = [&] (int) { ... }; | |
190 | my_function (1, gdb::make_function_view (lambda)); | |
191 | ||
07e253aa PA |
192 | You can find unit tests covering the whole API in |
193 | unittests/function-view-selftests.c. */ | |
194 | ||
70d02be7 | 195 | #include <type_traits> |
07e253aa PA |
196 | namespace gdb { |
197 | ||
07e253aa PA |
198 | namespace fv_detail { |
199 | /* Bits shared by all function_view instantiations that do not depend | |
200 | on the template parameters. */ | |
201 | ||
202 | /* Storage for the erased callable. This is a union in order to be | |
203 | able to save both a function object (data) pointer or a function | |
204 | pointer without triggering undefined behavior. */ | |
205 | union erased_callable | |
206 | { | |
207 | /* For function objects. */ | |
208 | void *data; | |
209 | ||
210 | /* For function pointers. */ | |
211 | void (*fn) (); | |
212 | }; | |
213 | ||
214 | } /* namespace fv_detail */ | |
215 | ||
216 | /* Use partial specialization to get access to the callable's | |
217 | signature. */ | |
218 | template<class Signature> | |
219 | struct function_view; | |
220 | ||
221 | template<typename Res, typename... Args> | |
222 | class function_view<Res (Args...)> | |
223 | { | |
224 | template<typename From, typename To> | |
225 | using CompatibleReturnType | |
b0b92aeb PA |
226 | = Or<std::is_void<To>, |
227 | std::is_same<From, To>, | |
228 | std::is_convertible<From, To>>; | |
07e253aa PA |
229 | |
230 | /* True if Func can be called with Args, and either the result is | |
231 | Res, convertible to Res or Res is void. */ | |
232 | template<typename Callable, | |
70d02be7 | 233 | typename Res2 = typename std::invoke_result<Callable &, Args...>::type> |
07e253aa PA |
234 | struct IsCompatibleCallable : CompatibleReturnType<Res2, Res> |
235 | {}; | |
236 | ||
237 | /* True if Callable is a function_view. Used to avoid hijacking the | |
238 | copy ctor. */ | |
239 | template <typename Callable> | |
240 | struct IsFunctionView | |
241 | : std::is_same<function_view, typename std::decay<Callable>::type> | |
242 | {}; | |
243 | ||
07e253aa PA |
244 | public: |
245 | ||
246 | /* NULL by default. */ | |
247 | constexpr function_view () noexcept | |
248 | : m_erased_callable {}, | |
249 | m_invoker {} | |
250 | {} | |
251 | ||
252 | /* Default copy/assignment is fine. */ | |
253 | function_view (const function_view &) = default; | |
254 | function_view &operator= (const function_view &) = default; | |
255 | ||
256 | /* This is the main entry point. Use SFINAE to avoid hijacking the | |
257 | copy constructor and to ensure that the target type is | |
258 | compatible. */ | |
259 | template | |
260 | <typename Callable, | |
b0b92aeb | 261 | typename = Requires<Not<IsFunctionView<Callable>>>, |
07e253aa PA |
262 | typename = Requires<IsCompatibleCallable<Callable>>> |
263 | function_view (Callable &&callable) noexcept | |
264 | { | |
265 | bind (callable); | |
266 | } | |
267 | ||
268 | /* Construct a NULL function_view. */ | |
269 | constexpr function_view (std::nullptr_t) noexcept | |
270 | : m_erased_callable {}, | |
271 | m_invoker {} | |
272 | {} | |
273 | ||
274 | /* Clear a function_view. */ | |
275 | function_view &operator= (std::nullptr_t) noexcept | |
276 | { | |
277 | m_invoker = nullptr; | |
278 | return *this; | |
279 | } | |
280 | ||
281 | /* Return true if the wrapper has a target, false otherwise. Note | |
282 | we check M_INVOKER instead of M_ERASED_CALLABLE because we don't | |
283 | know which member of the union is active right now. */ | |
284 | constexpr explicit operator bool () const noexcept | |
285 | { return m_invoker != nullptr; } | |
286 | ||
287 | /* Call the callable. */ | |
288 | Res operator () (Args... args) const | |
289 | { return m_invoker (m_erased_callable, std::forward<Args> (args)...); } | |
290 | ||
291 | private: | |
292 | ||
293 | /* Bind this function_view to a compatible function object | |
294 | reference. */ | |
295 | template <typename Callable> | |
296 | void bind (Callable &callable) noexcept | |
297 | { | |
298 | m_erased_callable.data = (void *) std::addressof (callable); | |
299 | m_invoker = [] (fv_detail::erased_callable ecall, Args... args) | |
300 | noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res | |
301 | { | |
302 | auto &restored_callable = *static_cast<Callable *> (ecall.data); | |
303 | /* The explicit cast to Res avoids a compile error when Res is | |
304 | void and the callable returns non-void. */ | |
305 | return (Res) restored_callable (std::forward<Args> (args)...); | |
306 | }; | |
307 | } | |
308 | ||
309 | /* Bind this function_view to a compatible function pointer. | |
310 | ||
311 | Making this a separate function allows avoiding one indirection, | |
312 | by storing the function pointer directly in the storage, instead | |
313 | of a pointer to pointer. erased_callable is then a union in | |
314 | order to avoid storing a function pointer as a data pointer here, | |
315 | which would be undefined. */ | |
316 | template<class Res2, typename... Args2> | |
317 | void bind (Res2 (*fn) (Args2...)) noexcept | |
318 | { | |
319 | m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn); | |
320 | m_invoker = [] (fv_detail::erased_callable ecall, Args... args) | |
321 | noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res | |
322 | { | |
323 | auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn); | |
324 | /* The explicit cast to Res avoids a compile error when Res is | |
325 | void and the callable returns non-void. */ | |
326 | return (Res) restored_fn (std::forward<Args> (args)...); | |
327 | }; | |
328 | } | |
329 | ||
330 | /* Storage for the erased callable. */ | |
331 | fv_detail::erased_callable m_erased_callable; | |
332 | ||
333 | /* The invoker. This is set to a capture-less lambda by one of the | |
334 | 'bind' overloads. The lambda restores the right type of the | |
335 | callable (which is passed as first argument), and forwards the | |
336 | args. */ | |
337 | Res (*m_invoker) (fv_detail::erased_callable, Args...); | |
338 | }; | |
339 | ||
340 | /* Allow comparison with NULL. Defer the work to the in-class | |
341 | operator bool implementation. */ | |
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 | template<typename Res, typename... Args> | |
354 | constexpr inline bool | |
355 | operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept | |
356 | { return static_cast<bool> (f); } | |
357 | ||
358 | template<typename Res, typename... Args> | |
359 | constexpr inline bool | |
360 | operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept | |
361 | { return static_cast<bool> (f); } | |
362 | ||
377c3a9c PA |
363 | namespace fv_detail { |
364 | ||
365 | /* Helper traits type to automatically find the right function_view | |
366 | type for a callable. */ | |
367 | ||
368 | /* Use partial specialization to get access to the callable's | |
369 | signature, for all the different callable variants. */ | |
370 | ||
371 | template<typename> | |
372 | struct function_view_traits; | |
373 | ||
374 | /* Main partial specialization with plain function signature type. | |
375 | All others end up redirected here. */ | |
376 | template<typename Res, typename... Args> | |
377 | struct function_view_traits<Res (Args...)> | |
378 | { | |
379 | using type = gdb::function_view<Res (Args...)>; | |
380 | }; | |
381 | ||
382 | /* Function pointers. */ | |
383 | template<typename Res, typename... Args> | |
384 | struct function_view_traits<Res (*) (Args...)> | |
385 | : function_view_traits<Res (Args...)> | |
386 | { | |
387 | }; | |
388 | ||
389 | /* Function references. */ | |
390 | template<typename Res, typename... Args> | |
391 | struct function_view_traits<Res (&) (Args...)> | |
392 | : function_view_traits<Res (Args...)> | |
393 | { | |
394 | }; | |
395 | ||
396 | /* Reference to function pointers. */ | |
397 | template<typename Res, typename... Args> | |
398 | struct function_view_traits<Res (*&) (Args...)> | |
399 | : function_view_traits<Res (Args...)> | |
400 | { | |
401 | }; | |
402 | ||
403 | /* Reference to const function pointers. */ | |
404 | template<typename Res, typename... Args> | |
405 | struct function_view_traits<Res (* const &) (Args...)> | |
406 | : function_view_traits<Res (Args...)> | |
407 | { | |
408 | }; | |
409 | ||
410 | /* Const member functions. function_view doesn't support these, but | |
411 | we need this in order to extract the type of function objects. | |
412 | Lambdas pass here, after starting at the operator() case, | |
413 | below. */ | |
414 | template<typename Res, typename Class, typename... Args> | |
415 | struct function_view_traits<Res (Class::*) (Args...) const> | |
416 | : function_view_traits<Res (Args...)> | |
417 | { | |
418 | }; | |
419 | ||
420 | /* Member functions. Ditto, for function objects with non-const | |
421 | operator(). */ | |
422 | template<typename Res, typename Class, typename... Args> | |
423 | struct function_view_traits<Res (Class::*) (Args...)> | |
424 | : function_view_traits<Res (Args...)> | |
425 | { | |
426 | }; | |
427 | ||
428 | /* Function objects, lambdas, std::function, any type that defines | |
429 | operator(). */ | |
430 | template<typename FuncObj> | |
431 | struct function_view_traits | |
432 | : function_view_traits <decltype | |
433 | (&std::remove_reference<FuncObj>::type::operator())> | |
434 | { | |
435 | }; | |
436 | ||
437 | } /* namespace fv_detail */ | |
438 | ||
439 | /* Make a function_view from a callable. Useful to automatically | |
440 | deduce the function_view's template argument type. */ | |
441 | template<typename Callable> | |
442 | auto make_function_view (Callable &&callable) | |
443 | -> typename fv_detail::function_view_traits<Callable>::type | |
444 | { | |
445 | using fv = typename fv_detail::function_view_traits<Callable>::type; | |
446 | return fv (std::forward<Callable> (callable)); | |
447 | } | |
448 | ||
07e253aa PA |
449 | } /* namespace gdb */ |
450 | ||
451 | #endif |