Commit | Line | Data |
---|---|---|
07e253aa PA |
1 | /* Copyright (C) 2017 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 | ||
07e253aa PA |
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. */ | |
163 | union erased_callable | |
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> | |
177 | struct function_view; | |
178 | ||
179 | template<typename Res, typename... Args> | |
180 | class function_view<Res (Args...)> | |
181 | { | |
182 | template<typename From, typename To> | |
183 | using CompatibleReturnType | |
b0b92aeb PA |
184 | = Or<std::is_void<To>, |
185 | std::is_same<From, To>, | |
186 | std::is_convertible<From, To>>; | |
07e253aa PA |
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 | ||
07e253aa PA |
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, | |
b0b92aeb | 219 | typename = Requires<Not<IsFunctionView<Callable>>>, |
07e253aa PA |
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. */ | |
289 | fv_detail::erased_callable m_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 |