source: mainline/uspace/lib/cpp/include/internal/functional/function.hpp@ c866a83

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c866a83 was c866a83, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: split too big files into smaller (loosely related) sub files

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_INTERNAL_FUNCTIONAL_FUNCTION
30#define LIBCPP_INTERNAL_FUNCTIONAL_FUNCTION
31
32#include <internal/functional/reference_wrapper.hpp>
33#include <typeinfo>
34#include <type_traits>
35
36namespace std
37{
38 /**
39 * 20.9.12, polymorphic function adaptors:
40 */
41
42 namespace aux
43 {
44 // TODO: fix this
45 /* template<class, class T, class... Args> */
46 /* struct is_callable_impl: false_type */
47 /* { /1* DUMMY BODY *1/ }; */
48
49 /* template<class, class R, class... Args> */
50 /* struct is_callable_impl< */
51 /* void_t<decltype(aux::invoke(declval<R(Args...)>(), declval<Args>()..., R))>, */
52 /* R, Args... */
53 /* > : true_type */
54 /* { /1* DUMMY BODY *1/ }; */
55
56 /* template<class T> */
57 /* struct is_callable: is_callable_impl<void_t<>, T> */
58 /* { /1* DUMMY BODY *1/ }; */
59
60 template<class Callable, class R, class... Args>
61 R invoke_callable(Callable* clbl, Args&&... args)
62 {
63 return (*clbl)(forward<Args>(args)...);
64 }
65
66 template<class Callable>
67 void copy_callable(Callable* to, Callable* from)
68 {
69 new(to) Callable{*from};
70 }
71
72 template<class Callable>
73 void destroy_callable(Callable* clbl)
74 {
75 if (clbl)
76 clbl->~Callable();
77 }
78 }
79
80 // TODO: implement
81 class bad_function_call;
82
83 template<class>
84 class function; // undefined
85
86 /**
87 * Note: Ideally, this implementation wouldn't
88 * copy the target if it was a pointer to
89 * a function, but for the simplicity of the
90 * implementation, we do copy even in that
91 * case for now. It would be a nice optimization
92 * if this was changed in the future.
93 */
94 template<class R, class... Args>
95 class function<R(Args...)>
96 {
97 public:
98 using result_type = R;
99 // TODO: conditional typedefs
100
101 /**
102 * 20.9.12.2.1, construct/copy/destroy:
103 */
104
105 function() noexcept
106 : callable_{}, callable_size_{}, call_{},
107 copy_{}, dest_{}
108 { /* DUMMY BODY */ }
109
110 function(nullptr_t) noexcept
111 : function{}
112 { /* DUMMY BODY */ }
113
114 function(const function& other)
115 : callable_{}, callable_size_{other.callable_size_},
116 call_{other.call_}, copy_{other.copy_}, dest_{other.dest_}
117 {
118 callable_ = new uint8_t[callable_size_];
119 (*copy_)(callable_, other.callable_);
120 }
121
122 function(function&& other)
123 : callable_{other.callable_}, callable_size_{other.callable_size_},
124 call_{other.call_}, copy_{other.copy_}, dest_{other.dest_}
125 {
126 other.callable_ = nullptr;
127 other.callable_size_ = size_t{};
128 other.call_ = nullptr;
129 other.copy_ = nullptr;
130 other.dest_ = nullptr;
131 }
132
133 // TODO: shall not participate in overloading unless aux::is_callable<F>
134 template<class F>
135 function(F f)
136 : callable_{}, callable_size_{sizeof(F)},
137 call_{(call_t)aux::invoke_callable<F, R, Args...>},
138 copy_{(copy_t)aux::copy_callable<F>},
139 dest_{(dest_t)aux::destroy_callable<F>}
140 {
141 callable_ = new uint8_t[callable_size_];
142 (*copy_)(callable_, (uint8_t*)&f);
143 }
144
145 /**
146 * Note: For the moment we're ignoring the allocator
147 * for simplicity of the implementation.
148 */
149
150 template<class A>
151 function(allocator_arg_t, const A& a) noexcept
152 : function{}
153 { /* DUMMY BODY */ }
154
155 template<class A>
156 function(allocator_arg_t, const A& a, nullptr_t) noexcept
157 : function{}
158 { /* DUMMY BODY */ }
159
160 template<class A>
161 function(allocator_arg_t, const A& a, const function& other)
162 : function{other}
163 { /* DUMMY BODY */ }
164
165 template<class A>
166 function(allocator_arg_t, const A& a, function&& other)
167 : function{move(other)}
168 { /* DUMMY BODY */ }
169
170 // TODO: shall not participate in overloading unless aux::is_callable<F>
171 template<class F, class A>
172 function(allocator_arg_t, const A& a, F f)
173 : function{f}
174 { /* DUMMY BODY */ }
175
176 function& operator=(const function& rhs)
177 {
178 function{rhs}.swap(*this);
179
180 return *this;
181 }
182
183 /**
184 * Note: We have to copy call_, copy_
185 * and dest_ because they can be templated
186 * by a type F we don't know.
187 */
188 function& operator=(function&& rhs)
189 {
190 clear_();
191
192 callable_ = rhs.callable_;
193 callable_size_ = rhs.callable_size_;
194 call_ = rhs.call_;
195 copy_ = rhs.copy_;
196 dest_ = rhs.dest_;
197
198 rhs.callable_ = nullptr;
199 rhs.callable_size_ = size_t{};
200 rhs.call_ = nullptr;
201 rhs.copy_ = nullptr;
202 rhs.dest_ = nullptr;
203
204 return *this;
205 }
206
207 function& operator=(nullptr_t) noexcept
208 {
209 clear_();
210
211 return *this;
212 }
213
214 // TODO: shall not participate in overloading unless aux::is_callable<F>
215 template<class F>
216 function& operator=(F&& f)
217 {
218 callable_size_ = sizeof(F);
219 callable_ = new uint8_t[callable_size_];
220 call_ = aux::invoke_callable<F, R, Args...>;
221 copy_ = aux::copy_callable<F>;
222 dest_ = aux::destroy_callable<F>;
223
224 (*copy_)(callable_, (uint8_t*)&f);
225 }
226
227 template<class F>
228 function& operator=(reference_wrapper<F> ref) noexcept
229 {
230 return (*this) = ref.get();
231 }
232
233 ~function()
234 {
235 if (callable_)
236 {
237 (*dest_)(callable_);
238 delete[] callable_;
239 }
240 }
241
242 /**
243 * 20.9.12.2.2, function modifiers:
244 */
245
246 void swap(function& other) noexcept
247 {
248 std::swap(callable_, other.callable_);
249 std::swap(callable_size_, other.callable_size_);
250 std::swap(call_, other.call_);
251 std::swap(copy_, other.copy_);
252 std::swap(dest_, other.dest_);
253 }
254
255 template<class F, class A>
256 void assign(F&& f, const A& a)
257 {
258 function{allocator_arg, a, forward<F>(f)}.swap(*this);
259 }
260
261 /**
262 * 20.9.12.2.3, function capacity:
263 */
264
265 explicit operator bool() const noexcept
266 {
267 return callable_ != nullptr;
268 }
269
270 /**
271 * 20.9.12.2.4, function invocation:
272 */
273
274 result_type operator()(Args... args) const
275 {
276 // TODO: throw bad_function_call if !callable_ || !call_
277 if constexpr (is_same_v<R, void>)
278 (*call_)(callable_, forward<Args>(args)...);
279 else
280 return (*call_)(callable_, forward<Args>(args)...);
281 }
282
283 /**
284 * 20.9.12.2.5, function target access:
285 */
286
287 const type_info& target_type() const noexcept
288 {
289 return typeid(*callable_);
290 }
291
292 template<class T>
293 T* target() noexcept
294 {
295 if (target_type() == typeid(T))
296 return (T*)callable_;
297 else
298 return nullptr;
299 }
300
301 template<class T>
302 const T* target() const noexcept
303 {
304 if (target_type() == typeid(T))
305 return (T*)callable_;
306 else
307 return nullptr;
308 }
309
310 private:
311 using call_t = R(*)(uint8_t*, Args&&...);
312 using copy_t = void (*)(uint8_t*, uint8_t*);
313 using dest_t = void (*)(uint8_t*);
314
315 uint8_t* callable_;
316 size_t callable_size_;
317 call_t call_;
318 copy_t copy_;
319 dest_t dest_;
320
321 void clear_()
322 {
323 if (callable_)
324 {
325 (*dest_)(callable_);
326 delete[] callable_;
327 callable_ = nullptr;
328 }
329 }
330 };
331
332 /**
333 * 20.9.12.2.6, null pointer comparisons:
334 */
335
336 template<class R, class... Args>
337 bool operator==(const function<R(Args...)>& f, nullptr_t) noexcept
338 {
339 return !f;
340 }
341
342 template<class R, class... Args>
343 bool operator==(nullptr_t, const function<R(Args...)>& f) noexcept
344 {
345 return !f;
346 }
347
348 template<class R, class... Args>
349 bool operator!=(const function<R(Args...)>& f, nullptr_t) noexcept
350 {
351 return (bool)f;
352 }
353
354 template<class R, class... Args>
355 bool operator!=(nullptr_t, const function<R(Args...)>& f) noexcept
356 {
357 return (bool)f;
358 }
359
360 /**
361 * 20.9.12.2.7, specialized algorithms:
362 */
363
364 template<class R, class... Args>
365 void swap(function<R(Args...)>& f1, function<R(Args...)>& f2)
366 {
367 f1.swap(f2);
368 }
369
370 template<class R, class... Args, class Alloc>
371 struct uses_allocator<function<R(Args...)>, Alloc>
372 : true_type
373 { /* DUMMY BODY */ };
374}
375
376#endif
Note: See TracBrowser for help on using the repository browser.