xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/function-view.h (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
1 /* Copyright (C) 2017-2024 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   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 
192   You can find unit tests covering the whole API in
193   unittests/function-view-selftests.c.  */
194 
195 #include <type_traits>
196 namespace gdb {
197 
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
226     = Or<std::is_void<To>,
227 	 std::is_same<From, To>,
228 	 std::is_convertible<From, To>>;
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,
233 	   typename Res2 = typename std::invoke_result<Callable &, Args...>::type>
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 
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,
261      typename = Requires<Not<IsFunctionView<Callable>>>,
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 
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 
449 } /* namespace gdb */
450 
451 #endif
452