Embedded Template Library 1.0
Loading...
Searching...
No Matches
inplace_function.h
Go to the documentation of this file.
1
2
3/******************************************************************************
4The MIT License(MIT)
5
6Embedded Template Library.
7https://github.com/ETLCPP/etl
8https://www.etlcpp.com
9
10Copyright(c) 2025 John Wellbelove
11
12Permission is hereby granted, free of charge, to any person obtaining a copy
13of this software and associated documentation files(the "Software"), to deal
14in the Software without restriction, including without limitation the rights
15to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16copies of the Software, and to permit persons to whom the Software is
17furnished to do so, subject to the following conditions :
18
19The above copyright notice and this permission notice shall be included in all
20copies or substantial portions of the Software.
21
22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28SOFTWARE.
29******************************************************************************/
30
31#ifndef ETL_INPLACE_FUNCTION_INCLUDED
32#define ETL_INPLACE_FUNCTION_INCLUDED
33
34#include "platform.h"
35
36#if ETL_USING_CPP11
37
38 #include "error_handler.h"
39 #include "exception.h"
40 #include "file_error_numbers.h"
41 #include "function_traits.h"
42 #include "invoke.h"
43 #include "optional.h"
44 #include "placement_new.h"
45 #include "type_traits.h"
46 #include "utility.h"
47
48 #if !defined(ETL_DEFAULT_INPLACE_FUNCTION_SIZE)
49 #define ETL_DEFAULT_INPLACE_FUNCTION_SIZE 32
50 #endif
51
52 #if !defined(ETL_DEFAULT_INPLACE_FUNCTION_ALIGNMENT)
53 #define ETL_DEFAULT_INPLACE_FUNCTION_ALIGNMENT alignof(void*)
54 #endif
55
56namespace etl
57{
58 //*************************************************************************
60 //*************************************************************************
61 class inplace_function_exception : public etl::exception
62 {
63 public:
64
65 inplace_function_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
66 : exception(reason_, file_name_, line_number_)
67 {
68 }
69 };
70
71 //*************************************************************************
73 //*************************************************************************
74 class inplace_function_uninitialized : public inplace_function_exception
75 {
76 public:
77
78 inplace_function_uninitialized(string_type file_name_, numeric_type line_number_)
79 : inplace_function_exception(ETL_ERROR_TEXT("inplace_function:uninitialized", ETL_INPLACE_FUNCTION_FILE_ID"A"), file_name_, line_number_)
80 {
81 }
82 };
83
84 namespace private_inplace_function
85 {
86 //*************************************************************************
88 //*************************************************************************
89 template <typename TReturn, typename... TArgs>
90 struct inplace_function_vtable
91 {
92 using invoke_type = TReturn (*)(void*, TArgs...);
93 using destroy_type = void (*)(void*);
94 using move_type = void (*)(void*, void*);
95 using copy_type = void (*)(const void*, void*);
96
97 invoke_type invoke = nullptr;
98 destroy_type destroy = nullptr;
99 move_type move = nullptr;
100 copy_type copy = nullptr;
101
102 //*****************************************
103 // Constructor
104 //*****************************************
105 ETL_CONSTEXPR inplace_function_vtable(invoke_type i, destroy_type d, move_type m, copy_type c)
106 : invoke(i)
107 , destroy(d)
108 , move(m)
109 , copy(c)
110 {
111 }
112
113 //*****************************************
114 // Payload records for member bindings
115 //*****************************************
116 // Non-const member function
117 //*****************************************
118 template <typename TObject>
119 struct member_target
120 {
121 TReturn (TObject::*member)(TArgs...);
122 TObject obj;
123 };
124
125 //*****************************************
126 // Const member function
127 //*****************************************
128 template <typename TObject>
129 struct const_member_target
130 {
131 TReturn (TObject::*member)(TArgs...) const;
132 TObject obj;
133 };
134
135 //*****************************************
136 // Storage helpers
137 //*****************************************
138 // Copy construct
139 //*****************************************
140 template <typename T>
141 static void copy_construct(const void* src, void* dst)
142 {
143 ::new (dst) T(*static_cast<const T*>(src));
144 }
145
146 //*****************************************
147 // Move construct
148 //*****************************************
149 template <typename T, bool DestroySrc>
150 static void move_construct(void* src, void* dst)
151 {
152 ::new (dst) T(etl::move(*static_cast<T*>(src)));
153
154 if (DestroySrc)
155 {
156 static_cast<T*>(src)->~T();
157 }
158 }
159
160 //*****************************************
161 // Stub functions
162 //*****************************************
163 // Destroy function
164 //*****************************************
165 template <typename T>
166 static void destroy_stub(void* p)
167 {
168 static_cast<T*>(p)->~T();
169 }
170
171 //*****************************************
172 // Free function - Returning value
173 //*****************************************
174 template <typename F, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
175 static R stub_function_ptr(void* p, TArgs... a)
176 {
177 return (*static_cast<F*>(p))(etl::forward<TArgs>(a)...);
178 }
179
180 //*****************************************
181 // Free function - returning void
182 //*****************************************
183 template <typename F, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
184 static void stub_function_ptr(void* p, TArgs... a)
185 {
186 (*static_cast<F*>(p))(etl::forward<TArgs>(a)...);
187 }
188
189 //*****************************************
190 // Member target - Returning value
191 //*****************************************
192 template <typename Target, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
193 static R stub_member(void* p, TArgs... a)
194 {
195 auto* s = static_cast<Target*>(p);
196 return (s->obj.*(s->member))(etl::forward<TArgs>(a)...);
197 }
198
199 //*****************************************
200 // Member target - returning void
201 //*****************************************
202 template <typename Target, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
203 static void stub_member(void* p, TArgs... a)
204 {
205 auto* s = static_cast<Target*>(p);
206 (s->obj.*(s->member))(etl::forward<TArgs>(a)...);
207 }
208
209 //*****************************************
210 // Functor / lambda stored in payload - returning value
211 //*****************************************
212 template <typename T, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
213 static R stub_functor(void* p, TArgs... a)
214 {
215 return (*static_cast<T*>(p))(etl::forward<TArgs>(a)...);
216 }
217
218 //*****************************************
219 // Functor / lambda stored in payload - returning void
220 //*****************************************
221 template <typename T, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
222 static void stub_functor(void* p, TArgs... a)
223 {
224 (*static_cast<T*>(p))(etl::forward<TArgs>(a)...);
225 }
226
227 //*****************************************
228 // Const functor / lambda stored in payload - returning value
229 //*****************************************
230 template <typename T, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
231 static R stub_const_functor(void* p, TArgs... a)
232 {
233 return (*static_cast<const T*>(p))(etl::forward<TArgs>(a)...);
234 }
235
236 //*****************************************
237 // Const functor / lambda stored in payload - returning void
238 //*****************************************
239 template <typename T, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
240 static void stub_const_functor(void* p, TArgs... a)
241 {
242 (*static_cast<const T*>(p))(etl::forward<TArgs>(a)...);
243 }
244
245 //*****************************************
246 // Compile-time bound free function - returning value
247 //*****************************************
248 template <TReturn (*Function)(TArgs...), typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
249 static R stub_ct_function(void*, TArgs... a)
250 {
251 return Function(etl::forward<TArgs>(a)...);
252 }
253
254 //*****************************************
255 // Compile-time bound free function - returning void
256 //*****************************************
257 template <TReturn (*Function)(TArgs...), typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
258 static void stub_ct_function(void*, TArgs... a)
259 {
260 Function(etl::forward<TArgs>(a)...);
261 }
262
263 //*****************************************
264 // Compile-time bound member + object - returning value
265 //*****************************************
266 template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject* Object, typename R = TReturn,
267 etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
268 static R stub_ct_member(void*, TArgs... a)
269 {
270 return (Object->*Method)(etl::forward<TArgs>(a)...);
271 }
272
273 //*****************************************
274 // Compile-time bound member + object - returning void
275 //*****************************************
276 template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject* Object, typename R = TReturn,
277 etl::enable_if_t<etl::is_void<R>::value, int> = 0>
278 static void stub_ct_member(void*, TArgs... a)
279 {
280 (Object->*Method)(etl::forward<TArgs>(a)...);
281 }
282
283 //*****************************************
284 // Compile-time bound const member + object - returning value
285 //*****************************************
286 template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject* Object, typename R = TReturn,
287 etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
288 static R stub_ct_const_member(void*, TArgs... a)
289 {
290 return (Object->*Method)(etl::forward<TArgs>(a)...);
291 }
292
293 //*****************************************
294 // Compile-time bound const member + object - returning void
295 //*****************************************
296 template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject* Object, typename R = TReturn,
297 etl::enable_if_t<etl::is_void<R>::value, int> = 0>
298 static void stub_ct_const_member(void*, TArgs... a)
299 {
300 (Object->*Method)(etl::forward<TArgs>(a)...);
301 }
302
303 //*****************************************
304 // Compile-time bound operator() + object - returning value
305 //*****************************************
306 template <typename TObject, TObject* Object, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
307 static R stub_ct_operator(void*, TArgs... a)
308 {
309 return (*Object)(etl::forward<TArgs>(a)...);
310 }
311
312 //*****************************************
313 // Compile-time bound operator() + object - returning void
314 //*****************************************
315 template <typename TObject, TObject* Object, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
316 static void stub_ct_operator(void*, TArgs... a)
317 {
318 (*Object)(etl::forward<TArgs>(a)...);
319 }
320
321 //*****************************************
322 // vtable factories
323 //*****************************************
324 // Free function pointer
325 //*****************************************
326 static const inplace_function_vtable* for_function_ptr()
327 {
328 using function_type = TReturn (*)(TArgs...);
329 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_function_ptr<function_type>, nullptr,
330 &inplace_function_vtable::template move_construct<function_type, false>,
331 &inplace_function_vtable::template copy_construct<function_type>);
332 return &vtable;
333 }
334
335 //*****************************************
336 // Member function pointer
337 //*****************************************
338 template <typename TObject>
339 static const inplace_function_vtable* for_member()
340 {
341 using target_t = member_target<TObject>;
342 constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
343
344 constexpr destroy_type destroy_ptr =
345 etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<target_t>;
346
347 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_member<target_t>, destroy_ptr,
348 &inplace_function_vtable::template move_construct< target_t, destroy_src_on_move>,
349 &inplace_function_vtable::template copy_construct<target_t>);
350 return &vtable;
351 }
352
353 //*****************************************
354 // Const member function pointer
355 //*****************************************
356 template <typename TObject>
357 static const inplace_function_vtable* for_const_member()
358 {
359 using target_t = const_member_target<TObject>;
360 constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
361
362 constexpr destroy_type destroy_ptr =
363 etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<target_t>;
364
365 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_member<target_t>, destroy_ptr,
366 &inplace_function_vtable::template move_construct< target_t, destroy_src_on_move>,
367 &inplace_function_vtable::template copy_construct<target_t>);
368 return &vtable;
369 }
370
371 //*****************************************
372 // Functor / lambda
373 //*****************************************
374 template <typename TObject>
375 static const inplace_function_vtable* for_functor()
376 {
377 constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
378
379 constexpr destroy_type destroy_ptr =
380 etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<TObject>;
381
382 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_functor<TObject>, destroy_ptr,
383 &inplace_function_vtable::template move_construct< TObject, destroy_src_on_move>,
384 &inplace_function_vtable::template copy_construct<TObject>);
385 return &vtable;
386 }
387
388 //*****************************************
389 // Const functor / lambda
390 //*****************************************
391 template <typename TObject>
392 static const inplace_function_vtable* for_const_functor()
393 {
394 constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
395
396 constexpr destroy_type destroy_ptr =
397 etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<TObject>;
398
399 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_const_functor<TObject>, destroy_ptr,
400 &inplace_function_vtable::template move_construct< TObject, destroy_src_on_move>,
401 &inplace_function_vtable::template copy_construct<TObject>);
402 return &vtable;
403 }
404
405 //*****************************************
406 // Compile-time bound free function
407 //*****************************************
408 template <TReturn (*Function)(TArgs...)>
409 static const inplace_function_vtable* for_compile_time_function()
410 {
411 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_function<Function>, nullptr, nullptr, nullptr);
412 return &vtable;
413 }
414
415 //*****************************************
416 // Compile-time bound member function + object
417 //*****************************************
418 template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject* Object>
419 static const inplace_function_vtable* for_compile_time_member()
420 {
421 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_member<TObject, Method, Object>, nullptr, nullptr,
422 nullptr);
423 return &vtable;
424 }
425
426 //*****************************************
427 // Compile-time bound const member function + object
428 //*****************************************
429 template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject* Object>
430 static const inplace_function_vtable* for_compile_time_const_member()
431 {
432 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_const_member< TObject, Method, Object>, nullptr,
433 nullptr, nullptr);
434 return &vtable;
435 }
436
437 //*****************************************
438 // Compile-time bound operator() + object
439 //*****************************************
440 template <typename TObject, TObject* Object>
441 static const inplace_function_vtable* for_compile_time_operator()
442 {
443 static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_operator<TObject, Object>, nullptr, nullptr, nullptr);
444 return &vtable;
445 }
446 };
447 } // namespace private_inplace_function
448
449 //*************************************************************************
451 //*************************************************************************
452 template <typename TSignature, size_t Object_Size = ETL_DEFAULT_INPLACE_FUNCTION_SIZE,
453 size_t Object_Alignment = ETL_DEFAULT_INPLACE_FUNCTION_ALIGNMENT>
454 class inplace_function;
455
456 //*************************************************************************
457 // Is not an etl::inplace_function
458 //*************************************************************************
459 template <typename T>
460 struct is_inplace_function : etl::false_type
461 {
462 };
463
464 //*************************************************************************
465 // Is an etl::inplace_function
466 //*************************************************************************
467 template <typename TReturn, typename... TArgs, size_t Object_Size, size_t Object_Alignment>
468 struct is_inplace_function< etl::inplace_function<TReturn(TArgs...), Object_Size, Object_Alignment>> : etl::true_type
469 {
470 };
471
472 //*************************************************************************
480 //*************************************************************************
481 template <typename TReturn, typename... TArgs, size_t Object_Size, size_t Object_Alignment>
482 class inplace_function<TReturn(TArgs...), Object_Size, Object_Alignment>
483 {
484 private:
485
486 using this_type = inplace_function<TReturn(TArgs...), Object_Size, Object_Alignment>;
487 using storage_type = etl::uninitialized_buffer<Object_Size, 1, Object_Alignment>;
488 using vtable_type = private_inplace_function::inplace_function_vtable<TReturn, TArgs...>;
489 using function_ptr = TReturn (*)(TArgs...);
490
491 public:
492
493 using function_type = TReturn(TArgs...);
494 using return_type = TReturn;
495 using argument_types = etl::type_list<TArgs...>;
496
497 //*************************************************************************
499 //*************************************************************************
500 inplace_function() noexcept = default;
501
502 //*************************************************************************
505 //*************************************************************************
506 inplace_function(const inplace_function& other)
507 {
508 clone_from(other);
509 }
510
511 //*************************************************************************
517 template <size_t Other_Object_Size, size_t Other_Object_Alignment>
518 inplace_function(const etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>& other)
519 {
520 static_assert(Object_Size >= Other_Object_Size, "etl::inplace_function: Destination object size too small");
521 static_assert(Object_Alignment >= Other_Object_Alignment, "etl::inplace_function: Destination object alignment too small");
522
523 clone_from(other);
524 }
525
526 //*************************************************************************
529 //*************************************************************************
530 inplace_function(inplace_function&& other) noexcept
531 {
532 move_from(other);
533 }
534
535 //*************************************************************************
540 //*************************************************************************
541 template <size_t Other_Object_Size, size_t Other_Object_Alignment>
542 inplace_function(etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>&& other) noexcept
543 {
544 static_assert(Object_Size >= Other_Object_Size, "etl::inplace_function: Destination object size too small");
545 static_assert(Object_Alignment >= Other_Object_Alignment, "etl::inplace_function: Destination object alignment too small");
546
547 move_from(other);
548 }
549
550 //*************************************************************************
552 //*************************************************************************
553 ~inplace_function() noexcept
554 {
555 clear();
556 }
557
558 //*************************************************************************
561 //*************************************************************************
562 inplace_function(function_ptr f)
563 {
564 set(f);
565 }
566
567 //*************************************************************************
571 //*************************************************************************
572 inplace_function(etl::nullptr_t) noexcept
573 {
574 clear();
575 }
576
577 //*************************************************************************
582 //*************************************************************************
583 template <typename TObject, typename TObjectArg>
584 inplace_function(TReturn (TObject::*method)(TArgs...), TObjectArg&& obj)
585 {
586 set(method, etl::forward<TObjectArg>(obj));
587 }
588
589 //*************************************************************************
594 //*************************************************************************
595 template <typename TObject, typename TObjectArg>
596 inplace_function(TReturn (TObject::*method)(TArgs...) const, TObjectArg&& obj)
597 {
598 set(method, etl::forward<TObjectArg>(obj));
599 }
600
601 //*************************************************************************
605 //*************************************************************************
606 template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
607 typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
608 inplace_function(TLambda& lambda)
609 {
610 set(lambda);
611 }
612
613 //*************************************************************************
617 //*************************************************************************
618 template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
619 typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
620 inplace_function(const TLambda& lambda)
621 {
622 set(lambda);
623 }
624
625 //*************************************************************************
628 //*************************************************************************
629 void set(function_ptr f)
630 {
631 // Validate that 'f' is invocable with (TArgs...) and returns TReturn
632 static_assert(etl::is_invocable_r<TReturn, function_ptr, TArgs...>::value,
633 "etl::inplace_function: function pointer is not compatible with the inplace_function signature");
634
635 static_assert(Object_Size >= sizeof(function_ptr), "etl::inplace_function: storage size too small");
636 static_assert(Object_Alignment >= alignof(function_ptr), "etl::inplace_function: storage alignment too small");
637
638 clear();
639
640 // Construct the object in the storage.
641 ::new (storage_ptr()) function_ptr(f);
642
643 vtable = vtable_type::for_function_ptr();
644 }
645
646 //*************************************************************************
651 //*************************************************************************
652 template <typename TObject, typename TObjectArg>
653 void set(TReturn (TObject::*method)(TArgs...), TObjectArg&& obj)
654 {
655 using D = etl::decay_t<TObjectArg>;
656 static_assert(etl::is_invocable_r<TReturn, decltype(method), D&, TArgs...>::value,
657 "etl::inplace_function: bound member function is not compatible with the inplace_function signature");
658
659 using target_t = typename vtable_type::template member_target<D>;
660
661 static_assert(Object_Size >= sizeof(target_t), "etl::inplace_function: storage size too small");
662 static_assert(Object_Alignment >= alignof(target_t), "etl::inplace_function: storage alignment too small");
663
664 clear();
665 ::new (storage_ptr()) target_t{method, etl::forward<TObjectArg>(obj)};
666 vtable = vtable_type::template for_member<D>();
667 }
668
669 //*************************************************************************
674 //*************************************************************************
675 template <typename TObject, typename TObjectArg>
676 void set(TReturn (TObject::*method)(TArgs...) const, TObjectArg&& obj)
677 {
678 using D = etl::decay_t<TObjectArg>;
679 static_assert(etl::is_invocable_r<TReturn, decltype(method), D&, TArgs...>::value,
680 "etl::inplace_function: bound member function is not compatible with the inplace_function signature");
681
682 using target_t = typename vtable_type::template const_member_target<D>;
683
684 static_assert(Object_Size >= sizeof(target_t), "etl::inplace_function: storage size too small");
685 static_assert(Object_Alignment >= alignof(target_t), "etl::inplace_function: storage alignment too small");
686
687 clear();
688 ::new (storage_ptr()) target_t{method, etl::forward<TObjectArg>(obj)};
689 vtable = vtable_type::template for_const_member<D>();
690 }
691
692 //*************************************************************************
696 //*************************************************************************
697 template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
698 typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
699 void set(TLambda& lambda)
700 {
701 // Validate that 'method' is invocable with (TObject&, TArgs...) and
702 // returns TReturn
703 static_assert(etl::is_invocable_r<TReturn, T, TArgs...>::value,
704 "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature");
705
706 static_assert(Object_Size >= sizeof(T), "etl::inplace_function: Object size too small");
707 static_assert(Object_Alignment >= alignof(T), "etl::inplace_function: Object alignment too small");
708
709 clear();
710
711 // Construct the object in the storage.
712 ::new (storage_ptr()) T(lambda);
713
714 vtable = vtable_type::template for_functor<T>();
715 }
716
717 //*************************************************************************
721 //*************************************************************************
722 template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
723 typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
724 void set(const TLambda& lambda)
725 {
726 static_assert(etl::is_invocable_r<TReturn, const T, TArgs...>::value,
727 "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature");
728
729 static_assert(Object_Size >= sizeof(T), "etl::inplace_function: Object size too small");
730 static_assert(Object_Alignment >= alignof(T), "etl::inplace_function: Object alignment too small");
731
732 clear();
733
734 // Construct the object in the storage.
735 ::new (storage_ptr()) T(lambda);
736
737 vtable = vtable_type::template for_const_functor<T>();
738 }
739
740 //*************************************************************************
745 //*************************************************************************
746 template <TReturn (*Function)(TArgs...)>
747 void set()
748 {
749 static_assert(etl::is_invocable_r<TReturn, decltype(Function), TArgs...>::value,
750 "etl::inplace_function: function pointer is not compatible with the inplace_function signature");
751
752 clear();
753 vtable = vtable_type::template for_compile_time_function<Function>();
754 }
755
756 //*************************************************************************
762 //*************************************************************************
763 template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject& Instance>
764 void set()
765 {
766 static_assert(etl::is_invocable_r<TReturn, decltype(Method), TObject&, TArgs...>::value,
767 "etl::inplace_function: bound member function is not compatible with the inplace_function signature");
768
769 clear();
770 vtable = vtable_type::template for_compile_time_member<TObject, Method, &Instance>();
771 }
772
773 //*************************************************************************
779 //*************************************************************************
780 template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject& Instance>
781 void set()
782 {
783 static_assert(etl::is_invocable_r<TReturn, decltype(Method), const TObject&, TArgs...>::value,
784 "etl::inplace_function: bound member function is not compatible with the inplace_function signature");
785
786 clear();
787 vtable = vtable_type::template for_compile_time_const_member<TObject, Method, &Instance>();
788 }
789
790 //*************************************************************************
794 //*************************************************************************
795 template < typename TObject, TObject& Instance, typename T = etl::decay_t<TObject>,
796 typename = etl::enable_if_t< etl::is_class<T>::value && etl::has_call_operator<T>::value
797 && !etl::function_traits<decltype(&T::operator())>::is_const>>
798 void set()
799 {
800 static_assert(etl::is_invocable_r<TReturn, T, TArgs...>::value,
801 "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature");
802
803 clear();
804 vtable = vtable_type::template for_compile_time_operator<TObject, &Instance>();
805 }
806
807 //*************************************************************************
811 //*************************************************************************
812 template < typename TObject, const TObject& Instance, typename T = etl::decay_t<TObject>,
813 typename = etl::enable_if_t< etl::is_class<T>::value && etl::has_call_operator<T>::value
814 && etl::function_traits<decltype(&T::operator())>::is_const>>
815 void set()
816 {
817 static_assert(etl::is_invocable_r<TReturn, const T, TArgs...>::value,
818 "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature");
819
820 clear();
821 vtable = vtable_type::template for_compile_time_operator<const TObject, &Instance>();
822 }
823
824 //*************************************************************************
828 //*************************************************************************
829 template <TReturn (*Function)(TArgs...)>
830 static this_type create()
831 {
832 return this_type(vtable_type::template for_compile_time_function<Function>());
833 }
834
835 //*************************************************************************
841 //*************************************************************************
842 template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject& Instance>
843 static this_type create()
844 {
845 return this_type(vtable_type::template for_compile_time_member<TObject, Method, &Instance>());
846 }
847
848 //*************************************************************************
854 //*************************************************************************
855 template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject& Instance>
856 static this_type create()
857 {
858 return this_type(vtable_type::template for_compile_time_const_member<TObject, Method, &Instance>());
859 }
860
861 //*************************************************************************
866 //*************************************************************************
867 template <typename TObject, TObject& Instance>
868 static this_type create()
869 {
870 return this_type(vtable_type::template for_compile_time_operator<TObject, &Instance>());
871 }
872
873 //*************************************************************************
877 //*************************************************************************
878 inplace_function& operator=(const inplace_function& rhs)
879 {
880 if (this != &rhs)
881 {
882 clear();
883 clone_from(rhs);
884 }
885
886 return *this;
887 }
888
889 //*************************************************************************
893 //*************************************************************************
894 inplace_function& operator=(inplace_function&& rhs) noexcept
895 {
896 if (this != &rhs)
897 {
898 clear();
899 move_from(rhs);
900 }
901
902 return *this;
903 }
904
905 //*************************************************************************
910 //*************************************************************************
911 inplace_function& operator=(etl::nullptr_t) noexcept
912 {
913 clear();
914
915 return *this;
916 }
917
918 //*************************************************************************
922 //*************************************************************************
923 inplace_function& operator=(function_ptr f)
924 {
925 set(f);
926
927 return *this;
928 }
929
930 //*************************************************************************
936 //*************************************************************************
937 template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
938 typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
939 inplace_function& operator=(TLambda&& lambda)
940 {
941 set(etl::forward<TLambda>(lambda));
942
943 return *this;
944 }
945
946 //*************************************************************************
949 //*************************************************************************
950 void swap(inplace_function& other) noexcept
951 {
952 if (this == &other)
953 {
954 return;
955 }
956
957 do_swap(vtable, storage_ptr(), other.vtable, other.storage_ptr());
958 }
959
960 //*************************************************************************
964 //*************************************************************************
965 ETL_NODISCARD
966 bool is_valid() const noexcept
967 {
968 return (vtable != nullptr);
969 }
970
971 //*************************************************************************
976 //*************************************************************************
977 ETL_NODISCARD
978 explicit operator bool() const noexcept
979 {
980 return is_valid();
981 }
982
983 //*************************************************************************
988 //*************************************************************************
989 TReturn operator()(TArgs... args) const
990 {
991 ETL_ASSERT(is_valid(), ETL_ERROR(inplace_function_uninitialized));
992
993 return vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
994 }
995
996 //*************************************************************************
1001 //*************************************************************************
1002 template <typename TRet = TReturn>
1003 typename etl::enable_if_t<etl::is_same<TRet, void>::value, bool> call_if(TArgs... args) const
1004 {
1005 if (is_valid())
1006 {
1007 vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
1008 return true;
1009 }
1010 else
1011 {
1012 return false;
1013 }
1014 }
1015
1016 //*************************************************************************
1022 //*************************************************************************
1023 template <typename TRet = TReturn>
1024 typename etl::enable_if_t<!etl::is_same<TRet, void>::value, etl::optional<TReturn>> call_if(TArgs... args) const
1025 {
1026 etl::optional<TReturn> result;
1027
1028 if (is_valid())
1029 {
1030 result = vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
1031 }
1032
1033 return result;
1034 }
1035
1036 //*************************************************************************
1043 //*************************************************************************
1044 template <typename TAlternative>
1045 TReturn call_or(TAlternative&& alternative, TArgs... args) const
1046 {
1047 if (is_valid())
1048 {
1049 return vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
1050 }
1051 else
1052 {
1053 return etl::forward<TAlternative>(alternative)(etl::forward<TArgs>(args)...);
1054 }
1055 }
1056
1057 //*************************************************************************
1063 //*************************************************************************
1064 template <TReturn (*Alternative)(TArgs...)>
1065 TReturn call_or(TArgs... args) const
1066 {
1067 if (is_valid())
1068 {
1069 return vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
1070 }
1071 else
1072 {
1073 return (Alternative)(etl::forward<TArgs>(args)...);
1074 }
1075 }
1076
1077 //*************************************************************************
1080 //*************************************************************************
1081 void clear() noexcept
1082 {
1083 if (is_valid())
1084 {
1085 if (vtable->destroy)
1086 {
1087 vtable->destroy(storage_ptr());
1088 }
1089
1090 vtable = nullptr;
1091 }
1092 }
1093
1094 //*************************************************************************
1097 //*************************************************************************
1098 ETL_NODISCARD
1099 static constexpr size_t size() noexcept
1100 {
1101 return Object_Size;
1102 }
1103
1104 //*************************************************************************
1107 //*************************************************************************
1108 ETL_NODISCARD
1109 static constexpr size_t alignment() noexcept
1110 {
1111 return Object_Alignment;
1112 }
1113
1114 private:
1115
1116 // Allow cross-size access to internals
1117 template <typename, size_t, size_t>
1118 friend class inplace_function;
1119
1120 //*************************************************************************
1121 // Direct-initialization constructor for CT-bound vtables (no payload).
1122 //*************************************************************************
1123 explicit inplace_function(const vtable_type* vt) noexcept
1124 : vtable(vt)
1125 , storage()
1126 {
1127 }
1128
1129 //*************************************************************************
1130 // clone_from
1131 //*************************************************************************
1132 template <size_t Other_Object_Size, size_t Other_Object_Alignment>
1133 void clone_from(const etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>& other)
1134 {
1135 vtable = other.vtable;
1136
1137 if (vtable && vtable->copy)
1138 {
1139 vtable->copy(&other.storage, &storage);
1140 }
1141 }
1142
1143 //*************************************************************************
1144 // move_from
1145 //*************************************************************************
1146 template <size_t Other_Object_Size, size_t Other_Object_Alignment>
1147 void move_from(etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>& other)
1148 {
1149 vtable = other.vtable;
1150
1151 if (vtable && vtable->move)
1152 {
1153 vtable->move(&other.storage, &storage);
1154 }
1155
1156 other.vtable = nullptr;
1157 }
1158
1159 //*************************************************************************
1160 // Internal swap implementation
1161 //*************************************************************************
1162 static void do_swap(const vtable_type*& vt_a, void* storage_a, const vtable_type*& vt_b, void* storage_b) noexcept
1163 {
1164 const bool a_valid = (vt_a != nullptr);
1165 const bool b_valid = (vt_b != nullptr);
1166
1167 if (!a_valid && !b_valid)
1168 {
1169 return;
1170 }
1171
1172 if (!a_valid)
1173 {
1174 // Only 'b' is valid
1175 if (vt_b->move)
1176 {
1177 vt_b->move(storage_b, storage_a);
1178 }
1179 else if (vt_b->copy)
1180 {
1181 vt_b->copy(storage_b, storage_a);
1182 }
1183 vt_a = vt_b;
1184 vt_b = nullptr;
1185 return;
1186 }
1187
1188 // Only 'a' is valid
1189 if (!b_valid)
1190 {
1191 if (vt_a->move)
1192 {
1193 vt_a->move(storage_a, storage_b);
1194 }
1195 else if (vt_a->copy)
1196 {
1197 vt_a->copy(storage_a, storage_b);
1198 }
1199 vt_b = vt_a;
1200 vt_a = nullptr;
1201 return;
1202 }
1203
1204 // Both valid.
1205 // If both have no payload (compile-time bound: no move/copy) just swap
1206 // vtable pointers.
1207 if (!vt_a->move && !vt_a->copy && !vt_b->move && !vt_b->copy)
1208 {
1209 const vtable_type* tmp = vt_a;
1210 vt_a = vt_b;
1211 vt_b = tmp;
1212 return;
1213 }
1214
1215 // General case
1216 alignas(Object_Alignment) unsigned char temp[Object_Size];
1217
1218 // a -> temp
1219 if (vt_a->move)
1220 {
1221 vt_a->move(storage_a, temp);
1222 }
1223 else if (vt_a->copy)
1224 {
1225 vt_a->copy(storage_a, temp);
1226 }
1227
1228 // b -> a
1229 if (vt_b->move)
1230 {
1231 vt_b->move(storage_b, storage_a);
1232 }
1233 else if (vt_b->copy)
1234 {
1235 vt_b->copy(storage_b, storage_a);
1236 }
1237
1238 // temp -> b
1239 if (vt_a->move)
1240 {
1241 vt_a->move(temp, storage_b);
1242 }
1243 else if (vt_a->copy)
1244 {
1245 vt_a->copy(temp, storage_b);
1246 }
1247
1248 // Swap vtable pointers
1249 const vtable_type* tmp = vt_a;
1250 vt_a = vt_b;
1251 vt_b = tmp;
1252 }
1253
1254 //*************************************************************************
1255 // Get pointer to storage as a void*
1256 //*************************************************************************
1257 void* storage_ptr() const noexcept
1258 {
1259 return const_cast<void*>(static_cast<const void*>(&storage));
1260 }
1261
1262 const vtable_type* vtable = nullptr;
1263 storage_type storage;
1264 };
1265
1266 //*************************************************************************
1270 //*************************************************************************
1271 template <typename TSignature, typename TStorage>
1272 using inplace_function_for = etl::inplace_function<TSignature, sizeof(etl::decay_t<TStorage>), alignof(etl::decay_t<TStorage>)>;
1273
1274 //*************************************************************************
1278 //*************************************************************************
1279 template <typename TSignature, typename T0, typename... TRest>
1280 using inplace_function_for_any = etl::inplace_function< TSignature, etl::largest<etl::decay_t<T0>, etl::decay_t<TRest>...>::size,
1281 etl::largest<etl::decay_t<T0>, etl::decay_t<TRest>...>::alignment>;
1282
1283 //*************************************************************************
1288 //*************************************************************************
1289 template <typename TReturn, typename... TArgs>
1290 ETL_NODISCARD
1291 etl::inplace_function<TReturn(TArgs...), sizeof(TReturn (*)(TArgs...)), alignof(TReturn (*)(TArgs...))>
1292 make_inplace_function(TReturn (*function)(TArgs...))
1293 {
1294 using function_ptr = TReturn (*)(TArgs...);
1295
1296 return etl::inplace_function_for<TReturn(TArgs...), function_ptr>(function);
1297 }
1298
1299 //*************************************************************************
1305 //*************************************************************************
1306 template <typename TObject, typename TReturn, typename... TArgs,
1307 typename TTarget = typename etl::private_inplace_function::inplace_function_vtable< TReturn, TArgs...>::template member_target<TObject>>
1308 ETL_NODISCARD
1309 etl::inplace_function<TReturn(TArgs...), sizeof(TTarget), alignof(TTarget)> make_inplace_function(TReturn (TObject::*method)(TArgs...),
1310 TObject& obj)
1311 {
1312 return etl::inplace_function_for<TReturn(TArgs...), TTarget>(method, obj);
1313 }
1314
1315 //*************************************************************************
1320 //*************************************************************************
1321 template <
1322 typename TObject, typename TReturn, typename... TArgs,
1323 typename TTarget = typename etl::private_inplace_function::inplace_function_vtable< TReturn, TArgs...>::template const_member_target<TObject>>
1324 ETL_NODISCARD
1325 etl::inplace_function<TReturn(TArgs...), sizeof(TTarget), alignof(TTarget)> make_inplace_function(TReturn (TObject::*method)(TArgs...) const,
1326 const TObject& obj)
1327 {
1328 return etl::inplace_function_for<TReturn(TArgs...), TTarget>(method, obj);
1329 }
1330
1331 //*************************************************************************
1336 //*************************************************************************
1337 template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
1338 typename = typename etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>,
1339 typename TSignature = typename etl::function_traits<T>::function_type>
1340 ETL_NODISCARD
1341 etl::inplace_function<TSignature, sizeof(T), alignof(T)> make_inplace_function(TLambda&& lambda)
1342 {
1343 return etl::inplace_function_for<TSignature, T>(etl::forward<TLambda>(lambda));
1344 }
1345
1346 //*************************************************************************
1350 //*************************************************************************
1351 template <typename TSignature, typename TType, typename T = typename etl::decay<TType>::type,
1352 typename = typename etl::enable_if_t<!etl::is_class<T>::value, int>>
1353 ETL_NODISCARD
1354 inplace_function_for<TSignature, T> make_inplace_function(TType&& function)
1355 {
1356 // If T is a function type, use a function pointer for storage sizing.
1357 using storage_t = typename etl::conditional<etl::is_function<T>::value, typename etl::add_pointer<T>::type, T>::type;
1358
1359 return inplace_function_for<TSignature, storage_t>(etl::forward<TType>(function));
1360 }
1361
1362 #if ETL_USING_CPP17
1363 //*************************************************************************
1368 //*************************************************************************
1369 template < auto Function, typename F = decltype(Function),
1370 typename = etl::enable_if_t< etl::is_pointer<F>::value && etl::is_function<etl::remove_pointer_t<F>>::value>>
1371 ETL_NODISCARD
1372 auto make_inplace_function()
1373 {
1374 using function_type = typename etl::function_traits<F>::function_type;
1375
1376 return etl::inplace_function<function_type, 1, 1>::template create<Function>();
1377 }
1378
1379 //*************************************************************************
1385 //*************************************************************************
1386 template < typename TObject, auto Method, TObject& Instance, typename T = decltype(Method),
1387 typename = etl::enable_if_t<etl::is_member_function_pointer<T>::value>, typename = etl::enable_if_t<!etl::function_traits<T>::is_const>>
1388 ETL_NODISCARD
1389 auto make_inplace_function()
1390 {
1391 using function_type = typename etl::function_traits<decltype(Method)>::function_type;
1392
1393 return etl::inplace_function<function_type, 1, 1>::template create< TObject, Method, Instance>();
1394 }
1395
1396 //*************************************************************************
1402 //*************************************************************************
1403 template < typename TObject, auto Method, const TObject& Instance, typename T = decltype(Method),
1404 typename = etl::enable_if_t<etl::is_member_function_pointer<T>::value>, typename = etl::enable_if_t<etl::function_traits<T>::is_const>>
1405 ETL_NODISCARD
1406 auto make_inplace_function()
1407 {
1408 using function_type = typename etl::function_traits<T>::function_type;
1409
1410 return etl::inplace_function<function_type, 1, 1>::template create< TObject, Method, Instance>();
1411 }
1412
1413 //*************************************************************************
1418 //*************************************************************************
1419 template < typename TObject, TObject& Instance, typename T = etl::decay_t<TObject>,
1420 typename = etl::enable_if_t<etl::is_class<T>::value && etl::has_call_operator<T>::value>>
1421 ETL_NODISCARD
1422 auto make_inplace_function()
1423 {
1424 using function_type = typename etl::function_traits< decltype(&TObject::operator())>::function_type;
1425
1426 return etl::inplace_function<function_type, 1, 1>::template create<TObject, Instance>();
1427 }
1428 #endif
1429
1430 //*************************************************************************
1434 //*************************************************************************
1435 template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
1436 void swap(etl::inplace_function<TSignature, Object_Size, Object_Alignment>& lhs,
1437 etl::inplace_function<TSignature, Object_Size, Object_Alignment>& rhs) noexcept
1438 {
1439 lhs.swap(rhs);
1440 }
1441
1442 //*************************************************************************
1447 //*************************************************************************
1448 template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
1449 ETL_NODISCARD
1450 bool operator==(const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& lhs, etl::nullptr_t)
1451 {
1452 return !lhs.is_valid();
1453 }
1454
1455 //*************************************************************************
1460 //*************************************************************************
1461 template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
1462 ETL_NODISCARD
1463 bool operator==(etl::nullptr_t, const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& rhs)
1464 {
1465 return !rhs.is_valid();
1466 }
1467
1468 //*************************************************************************
1473 //*************************************************************************
1474 template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
1475 ETL_NODISCARD
1476 bool operator!=(const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& lhs, etl::nullptr_t)
1477 {
1478 return lhs.is_valid();
1479 }
1480
1481 //*************************************************************************
1486 //*************************************************************************
1487 template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
1488 ETL_NODISCARD
1489 bool operator!=(etl::nullptr_t, const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& rhs)
1490 {
1491 return rhs.is_valid();
1492 }
1493} // namespace etl
1494
1495#endif
1496#endif
void swap(etl::array_view< T > &lhs, etl::array_view< T > &rhs) ETL_NOEXCEPT
Swaps the values.
Definition array_view.h:692
ETL_CONSTEXPR14 bool operator!=(const etl::bitset< Active_Bits, TElement > &lhs, const etl::bitset< Active_Bits, TElement > &rhs) ETL_NOEXCEPT
Definition bitset_new.h:2529
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
bitset_ext
Definition absolute.h:40
ETL_CONSTEXPR14 bool operator==(const etl::array< T, SIZE > &lhs, const etl::array< T, SIZE > &rhs)
Definition array.h:1081
integral_constant< bool, false > false_type
integral_constant specialisations
Definition type_traits.h:80
ETL_CONSTEXPR TContainer::size_type size(const TContainer &container)
Definition iterator.h:1192