1 /* Copyright (C) 2017-2022 Free Software Foundation, Inc.
3 This file is part of GDB.
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.
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.
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/>. */
18 #ifndef COMMON_FUNCTION_VIEW_H
19 #define COMMON_FUNCTION_VIEW_H
21 /* function_view is a polymorphic type-erasing wrapper class that
22 encapsulates a non-owning reference to arbitrary callable objects.
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.
29 This is meant to be used as callback type of a function that:
31 #1 - Takes a callback as parameter.
33 #2 - Wants to support arbitrary callable objects as callback type
34 (e.g., stateful function objects, lambda closures, free
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.
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.
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;
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);
54 foo *find_foo_by_type (int type)
58 iterate_over_foos ([&] (foo *f, void *data)
60 if (foo->type == type)
63 return true; // stop iterating
65 return false; // continue iterating
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).
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
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.
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().
108 Since function_view objects are small (a pair of pointers), they
109 should generally be passed around by value.
113 Given this function that accepts a callback:
116 iterate_over_foos (gdb::function_view<void (foo *)> callback)
118 for (auto &foo : foos)
122 you can call it like this, passing a lambda as callback:
124 iterate_over_foos ([&] (foo *f)
129 or like this, passing a function object as callback:
131 struct function_object
133 void operator() (foo *f)
144 function_object matcher {&mystate};
145 iterate_over_foos (matcher);
147 or like this, passing a function pointer as callback:
149 iterate_over_foos (process_one_foo);
151 You can find unit tests covering the whole API in
152 unittests/function-view-selftests.c. */
156 namespace fv_detail
{
157 /* Bits shared by all function_view instantiations that do not depend
158 on the template parameters. */
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. */
163 union erased_callable
165 /* For function objects. */
168 /* For function pointers. */
172 } /* namespace fv_detail */
174 /* Use partial specialization to get access to the callable's
176 template<class Signature
>
177 struct function_view
;
179 template<typename Res
, typename
... Args
>
180 class function_view
<Res (Args
...)>
182 template<typename From
, typename To
>
183 using CompatibleReturnType
184 = Or
<std::is_void
<To
>,
185 std::is_same
<From
, To
>,
186 std::is_convertible
<From
, To
>>;
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
>
195 /* True if Callable is a function_view. Used to avoid hijacking the
197 template <typename Callable
>
198 struct IsFunctionView
199 : std::is_same
<function_view
, typename
std::decay
<Callable
>::type
>
204 /* NULL by default. */
205 constexpr function_view () noexcept
206 : m_erased_callable
{},
210 /* Default copy/assignment is fine. */
211 function_view (const function_view
&) = default;
212 function_view
&operator= (const function_view
&) = default;
214 /* This is the main entry point. Use SFINAE to avoid hijacking the
215 copy constructor and to ensure that the target type is
219 typename
= Requires
<Not
<IsFunctionView
<Callable
>>>,
220 typename
= Requires
<IsCompatibleCallable
<Callable
>>>
221 function_view (Callable
&&callable
) noexcept
226 /* Construct a NULL function_view. */
227 constexpr function_view (std::nullptr_t
) noexcept
228 : m_erased_callable
{},
232 /* Clear a function_view. */
233 function_view
&operator= (std::nullptr_t
) noexcept
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; }
245 /* Call the callable. */
246 Res
operator () (Args
... args
) const
247 { return m_invoker (m_erased_callable
, std::forward
<Args
> (args
)...); }
251 /* Bind this function_view to a compatible function object
253 template <typename Callable
>
254 void bind (Callable
&callable
) noexcept
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
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
)...);
267 /* Bind this function_view to a compatible function pointer.
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
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
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
)...);
288 /* Storage for the erased callable. */
289 fv_detail::erased_callable m_erased_callable
;
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
295 Res (*m_invoker
) (fv_detail::erased_callable
, Args
...);
298 /* Allow comparison with NULL. Defer the work to the in-class
299 operator bool implementation. */
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
); }
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
); }
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
); }
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
); }
321 } /* namespace gdb */