aboutsummaryrefslogtreecommitdiff
path: root/gdbsupport/function-view.h
blob: ed113c74cfdd3c897b8a24b36054b0dd43f6402b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/* Copyright (C) 2017-2023 Free Software Foundation, Inc.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef COMMON_FUNCTION_VIEW_H
#define COMMON_FUNCTION_VIEW_H

/* function_view is a polymorphic type-erasing wrapper class that
   encapsulates a non-owning reference to arbitrary callable objects.

   A way to put it is that function_view is to std::function like
   std::string_view is to std::string.  While std::function stores a
   type-erased callable object internally, function_view holds a
   type-erased reference to an external callable object.

   This is meant to be used as callback type of a function that:

     #1 - Takes a callback as parameter.

     #2 - Wants to support arbitrary callable objects as callback type
	  (e.g., stateful function objects, lambda closures, free
	  functions).

     #3 - Does not store the callback anywhere; instead the function
	  just calls the callback directly or forwards it to some
	  other function that calls it.

     #4 - Can't be, or we don't want it to be, a template function
	  with the callable type as template parameter.  For example,
	  when the callback is a parameter of a virtual member
	  function, or when putting the function template in a header
	  would expose too much implementation detail.

   Note that the C-style "function pointer" + "void *data" callback
   parameter idiom fails requirement #2 above.  Please don't add new
   uses of that idiom.  I.e., something like this wouldn't work;

    typedef bool (iterate_over_foos_cb) (foo *f, void *user_data),
    void iterate_over_foos (iterate_over_foos_cb *callback, void *user_data);

    foo *find_foo_by_type (int type)
    {
      foo *found = nullptr;

      iterate_over_foos ([&] (foo *f, void *data)
	{
	  if (foo->type == type)
	    {
	      found = foo;
	      return true; // stop iterating
	    }
	  return false; // continue iterating
	}, NULL);

      return found;
    }

   The above wouldn't compile, because lambdas with captures can't be
   implicitly converted to a function pointer (because a capture means
   some context data must be passed to the lambda somehow).

   C++11 gave us std::function as type-erased wrapper around arbitrary
   callables, however, std::function is not an ideal fit for transient
   callbacks such as the use case above.  For this use case, which is
   quite pervasive, a function_view is a better choice, because while
   function_view is light and does not require any heap allocation,
   std::function is a heavy-weight object with value semantics that
   generally requires a heap allocation on construction/assignment of
   the target callable.  In addition, while it is possible to use
   std::function in such a way that avoids most of the overhead by
   making sure to only construct it with callables of types that fit
   std::function's small object optimization, such as function
   pointers and std::reference_wrapper callables, that is quite
   inconvenient in practice, because restricting to free-function
   callables would imply no state/capture/closure, which we need in
   most cases, and std::reference_wrapper implies remembering to use
   std::ref/std::cref where the callable is constructed, with the
   added inconvenience that std::ref/std::cref have deleted rvalue-ref
   overloads, meaning you can't use unnamed/temporary lambdas with
   them.

   Note that because function_view is a non-owning view of a callable,
   care must be taken to ensure that the callable outlives the
   function_view that calls it.  This is not really a problem for the
   use case function_view is intended for, such as passing a temporary
   function object / lambda to a function that accepts a callback,
   because in those cases, the temporary is guaranteed to be live
   until the called function returns.

   Calling a function_view with no associated target is undefined,
   unlike with std::function, which throws std::bad_function_call.
   This is by design, to avoid the otherwise necessary NULL check in
   function_view::operator().

   Since function_view objects are small (a pair of pointers), they
   should generally be passed around by value.

   Usage:

   Given this function that accepts a callback:

    void
    iterate_over_foos (gdb::function_view<void (foo *)> callback)
    {
       for (auto &foo : foos)
	 callback (&foo);
    }

   you can call it like this, passing a lambda as callback:

    iterate_over_foos ([&] (foo *f)
      {
	process_one_foo (f);
      });

   or like this, passing a function object as callback:

    struct function_object
    {
      void operator() (foo *f)
      {
	if (s->check ())
	  process_one_foo (f);
      }

      // some state
      state *s;
    };

    state mystate;
    function_object matcher {&mystate};
    iterate_over_foos (matcher);

  or like this, passing a function pointer as callback:

    iterate_over_foos (process_one_foo);

  There's also a gdb::make_function_view function that you can use to
  automatically create a function_view from a callable without having
  to specify the function_view's template parameter.  E.g.:

    auto lambda = [&] (int) { ... };
    auto fv = gdb::make_function_view (lambda);

  This can be useful for example when calling a template function
  whose function_view parameter type depends on the function's
  template parameters.  In such case, you can't rely on implicit
  callable->function_view conversion for the function_view argument.
  You must pass a function_view argument already of the right type to
  the template function.  E.g., with this:

    template<typename T>
    void my_function (T v, gdb::function_view<void(T)> callback = nullptr);

  this wouldn't compile:

    auto lambda = [&] (int) { ... };
    my_function (1, lambda);

  Note that this immediately dangles the temporary lambda object:

    gdb::function_view<void(int)> fv = [&] (int) { ... };  // dangles
    my_function (fv);

  To avoid the dangling you'd have to use a named temporary for the
  lambda:

    auto lambda = [&] (int) { ... };
    gdb::function_view<void(int)> fv = lambda;
    my_function (fv);

  Using gdb::make_function_view instead automatically deduces the
  function_view's full type, and, avoids worrying about dangling.  For
  the example above, we could write instead:

    auto lambda = [&] (int) { ... };
    my_function (1, gdb::make_function_view (lambda));

  You can find unit tests covering the whole API in
  unittests/function-view-selftests.c.  */

#include <type_traits>
namespace gdb {

namespace fv_detail {
/* Bits shared by all function_view instantiations that do not depend
   on the template parameters.  */

/* Storage for the erased callable.  This is a union in order to be
   able to save both a function object (data) pointer or a function
   pointer without triggering undefined behavior.  */
union erased_callable
{
  /* For function objects.  */
  void *data;

    /* For function pointers.  */
  void (*fn) ();
};

} /* namespace fv_detail */

/* Use partial specialization to get access to the callable's
   signature. */
template<class Signature>
struct function_view;

template<typename Res, typename... Args>
class function_view<Res (Args...)>
{
  template<typename From, typename To>
  using CompatibleReturnType
    = Or<std::is_void<To>,
	 std::is_same<From, To>,
	 std::is_convertible<From, To>>;

  /* True if Func can be called with Args, and either the result is
     Res, convertible to Res or Res is void.  */
  template<typename Callable,
	   typename Res2 = typename std::invoke_result<Callable &, Args...>::type>
  struct IsCompatibleCallable : CompatibleReturnType<Res2, Res>
  {};

  /* True if Callable is a function_view.  Used to avoid hijacking the
     copy ctor.  */
  template <typename Callable>
  struct IsFunctionView
    : std::is_same<function_view, typename std::decay<Callable>::type>
  {};

 public:

  /* NULL by default.  */
  constexpr function_view () noexcept
    : m_erased_callable {},
      m_invoker {}
  {}

  /* Default copy/assignment is fine.  */
  function_view (const function_view &) = default;
  function_view &operator= (const function_view &) = default;

  /* This is the main entry point.  Use SFINAE to avoid hijacking the
     copy constructor and to ensure that the target type is
     compatible.  */
  template
    <typename Callable,
     typename = Requires<Not<IsFunctionView<Callable>>>,
     typename = Requires<IsCompatibleCallable<Callable>>>
  function_view (Callable &&callable) noexcept
  {
    bind (callable);
  }

  /* Construct a NULL function_view.  */
  constexpr function_view (std::nullptr_t) noexcept
    : m_erased_callable {},
      m_invoker {}
  {}

  /* Clear a function_view.  */
  function_view &operator= (std::nullptr_t) noexcept
  {
    m_invoker = nullptr;
    return *this;
  }

  /* Return true if the wrapper has a target, false otherwise.  Note
     we check M_INVOKER instead of M_ERASED_CALLABLE because we don't
     know which member of the union is active right now.  */
  constexpr explicit operator bool () const noexcept
  { return m_invoker != nullptr; }

  /* Call the callable.  */
  Res operator () (Args... args) const
  { return m_invoker (m_erased_callable, std::forward<Args> (args)...); }

 private:

  /* Bind this function_view to a compatible function object
     reference.  */
  template <typename Callable>
  void bind (Callable &callable) noexcept
  {
    m_erased_callable.data = (void *) std::addressof (callable);
    m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
      noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res
      {
	auto &restored_callable = *static_cast<Callable *> (ecall.data);
	/* The explicit cast to Res avoids a compile error when Res is
	   void and the callable returns non-void.  */
	return (Res) restored_callable (std::forward<Args> (args)...);
      };
  }

  /* Bind this function_view to a compatible function pointer.

     Making this a separate function allows avoiding one indirection,
     by storing the function pointer directly in the storage, instead
     of a pointer to pointer.  erased_callable is then a union in
     order to avoid storing a function pointer as a data pointer here,
     which would be undefined.  */
  template<class Res2, typename... Args2>
  void bind (Res2 (*fn) (Args2...)) noexcept
  {
    m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn);
    m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
      noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res
      {
	auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn);
	/* The explicit cast to Res avoids a compile error when Res is
	   void and the callable returns non-void.  */
	return (Res) restored_fn (std::forward<Args> (args)...);
      };
  }

  /* Storage for the erased callable.  */
  fv_detail::erased_callable m_erased_callable;

  /* The invoker.  This is set to a capture-less lambda by one of the
     'bind' overloads.  The lambda restores the right type of the
     callable (which is passed as first argument), and forwards the
     args.  */
  Res (*m_invoker) (fv_detail::erased_callable, Args...);
};

/* Allow comparison with NULL.  Defer the work to the in-class
   operator bool implementation.  */

template<typename Res, typename... Args>
constexpr inline bool
operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
{ return !static_cast<bool> (f); }

template<typename Res, typename... Args>
constexpr inline bool
operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
{ return !static_cast<bool> (f); }

template<typename Res, typename... Args>
constexpr inline bool
operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
{ return static_cast<bool> (f); }

template<typename Res, typename... Args>
constexpr inline bool
operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
{ return static_cast<bool> (f); }

namespace fv_detail {

/* Helper traits type to automatically find the right function_view
   type for a callable.  */

/* Use partial specialization to get access to the callable's
   signature, for all the different callable variants.  */

template<typename>
struct function_view_traits;

/* Main partial specialization with plain function signature type.
   All others end up redirected here.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (Args...)>
{
  using type = gdb::function_view<Res (Args...)>;
};

/* Function pointers.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (*) (Args...)>
  : function_view_traits<Res (Args...)>
{
};

/* Function references.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (&) (Args...)>
  : function_view_traits<Res (Args...)>
{
};

/* Reference to function pointers.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (*&) (Args...)>
  : function_view_traits<Res (Args...)>
{
};

/* Reference to const function pointers.  */
template<typename Res, typename... Args>
struct function_view_traits<Res (* const &) (Args...)>
  : function_view_traits<Res (Args...)>
{
};

/* Const member functions.  function_view doesn't support these, but
   we need this in order to extract the type of function objects.
   Lambdas pass here, after starting at the operator() case,
   below.  */
template<typename Res, typename Class, typename... Args>
struct function_view_traits<Res (Class::*) (Args...) const>
  : function_view_traits<Res (Args...)>
{
};

/* Member functions.  Ditto, for function objects with non-const
   operator().  */
template<typename Res, typename Class, typename... Args>
struct function_view_traits<Res (Class::*) (Args...)>
  : function_view_traits<Res (Args...)>
{
};

/* Function objects, lambdas, std::function, any type that defines
   operator().  */
template<typename FuncObj>
struct function_view_traits
  : function_view_traits <decltype
			  (&std::remove_reference<FuncObj>::type::operator())>
{
};

} /* namespace fv_detail */

/* Make a function_view from a callable.  Useful to automatically
   deduce the function_view's template argument type.  */
template<typename Callable>
auto make_function_view (Callable &&callable)
  -> typename fv_detail::function_view_traits<Callable>::type
{
  using fv = typename fv_detail::function_view_traits<Callable>::type;
  return fv (std::forward<Callable> (callable));
}

} /* namespace gdb */

#endif