GDB (xrefs)
function-view.h
Go to the documentation of this file.
1 /* Copyright (C) 2017-2018 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 fv_detail {
157 /* Bits shared by all function_view instantiations that do not depend
158  on the template parameters. */
159 
160 /* Storage for the erased callable. This is a union in order to be
161  able to save both a function object (data) pointer or a function
162  pointer without triggering undefined behavior. */
164 {
165  /* For function objects. */
166  void *data;
167 
168  /* For function pointers. */
169  void (*fn) ();
170 };
171 
172 } /* namespace fv_detail */
173 
174 /* Use partial specialization to get access to the callable's
175  signature. */
176 template<class Signature>
178 
179 template<typename Res, typename... Args>
180 class function_view<Res (Args...)>
181 {
182  template<typename From, typename To>
185  std::is_same<From, To>,
186  std::is_convertible<From, To>>;
187 
188  /* True if Func can be called with Args, and either the result is
189  Res, convertible to Res or Res is void. */
190  template<typename Callable,
191  typename Res2 = typename std::result_of<Callable &(Args...)>::type>
192  struct IsCompatibleCallable : CompatibleReturnType<Res2, Res>
193  {};
194 
195  /* True if Callable is a function_view. Used to avoid hijacking the
196  copy ctor. */
197  template <typename Callable>
198  struct IsFunctionView
199  : std::is_same<function_view, typename std::decay<Callable>::type>
200  {};
201 
202  public:
203 
204  /* NULL by default. */
205  constexpr function_view () noexcept
206  : m_erased_callable {},
207  m_invoker {}
208  {}
209 
210  /* Default copy/assignment is fine. */
211  function_view (const function_view &) = default;
212  function_view &operator= (const function_view &) = default;
213 
214  /* This is the main entry point. Use SFINAE to avoid hijacking the
215  copy constructor and to ensure that the target type is
216  compatible. */
217  template
218  <typename Callable,
219  typename = Requires<Not<IsFunctionView<Callable>>>,
220  typename = Requires<IsCompatibleCallable<Callable>>>
221  function_view (Callable &&callable) noexcept
222  {
223  bind (callable);
224  }
225 
226  /* Construct a NULL function_view. */
227  constexpr function_view (std::nullptr_t) noexcept
228  : m_erased_callable {},
229  m_invoker {}
230  {}
231 
232  /* Clear a function_view. */
233  function_view &operator= (std::nullptr_t) noexcept
234  {
235  m_invoker = nullptr;
236  return *this;
237  }
238 
239  /* Return true if the wrapper has a target, false otherwise. Note
240  we check M_INVOKER instead of M_ERASED_CALLABLE because we don't
241  know which member of the union is active right now. */
242  constexpr explicit operator bool () const noexcept
243  { return m_invoker != nullptr; }
244 
245  /* Call the callable. */
246  Res operator () (Args... args) const
247  { return m_invoker (m_erased_callable, std::forward<Args> (args)...); }
248 
249  private:
250 
251  /* Bind this function_view to a compatible function object
252  reference. */
253  template <typename Callable>
254  void bind (Callable &callable) noexcept
255  {
256  m_erased_callable.data = (void *) std::addressof (callable);
257  m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
258  noexcept (noexcept (callable (std::forward<Args> (args)...))) -> Res
259  {
260  auto &restored_callable = *static_cast<Callable *> (ecall.data);
261  /* The explicit cast to Res avoids a compile error when Res is
262  void and the callable returns non-void. */
263  return (Res) restored_callable (std::forward<Args> (args)...);
264  };
265  }
266 
267  /* Bind this function_view to a compatible function pointer.
268 
269  Making this a separate function allows avoiding one indirection,
270  by storing the function pointer directly in the storage, instead
271  of a pointer to pointer. erased_callable is then a union in
272  order to avoid storing a function pointer as a data pointer here,
273  which would be undefined. */
274  template<class Res2, typename... Args2>
275  void bind (Res2 (*fn) (Args2...)) noexcept
276  {
277  m_erased_callable.fn = reinterpret_cast<void (*) ()> (fn);
278  m_invoker = [] (fv_detail::erased_callable ecall, Args... args)
279  noexcept (noexcept (fn (std::forward<Args> (args)...))) -> Res
280  {
281  auto restored_fn = reinterpret_cast<Res2 (*) (Args2...)> (ecall.fn);
282  /* The explicit cast to Res avoids a compile error when Res is
283  void and the callable returns non-void. */
284  return (Res) restored_fn (std::forward<Args> (args)...);
285  };
286  }
287 
288  /* Storage for the erased callable. */
290 
291  /* The invoker. This is set to a capture-less lambda by one of the
292  'bind' overloads. The lambda restores the right type of the
293  callable (which is passed as first argument), and forwards the
294  args. */
295  Res (*m_invoker) (fv_detail::erased_callable, Args...);
296 };
297 
298 /* Allow comparison with NULL. Defer the work to the in-class
299  operator bool implementation. */
300 
301 template<typename Res, typename... Args>
302 constexpr inline bool
303 operator== (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
304 { return !static_cast<bool> (f); }
305 
306 template<typename Res, typename... Args>
307 constexpr inline bool
308 operator== (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
309 { return !static_cast<bool> (f); }
310 
311 template<typename Res, typename... Args>
312 constexpr inline bool
313 operator!= (const function_view<Res (Args...)> &f, std::nullptr_t) noexcept
314 { return static_cast<bool> (f); }
315 
316 template<typename Res, typename... Args>
317 constexpr inline bool
318 operator!= (std::nullptr_t, const function_view<Res (Args...)> &f) noexcept
319 { return static_cast<bool> (f); }
320 
321 } /* namespace gdb */
322 
323 #endif
constexpr bool operator==(const function_view< Res(Args...)> &f, std::nullptr_t) noexcept
constexpr bool operator!=(const function_view< Res(Args...)> &f, std::nullptr_t) noexcept
void bind(Res2(*fn)(Args2...)) noexcept
Definition: traits.h:55
Definition: gdbtypes.h:749
function_view(Callable &&callable) noexcept
Definition: array-view.h:66
constexpr function_view() noexcept
constexpr function_view(std::nullptr_t) noexcept
void bind(Callable &callable) noexcept
fv_detail::erased_callable m_erased_callable