///\file /****************************************************************************** The MIT License(MIT) Embedded Template Library. https://github.com/ETLCPP/etl https://www.etlcpp.com Copyright(c) 2025 John Wellbelove Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions : The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ #ifndef ETL_INPLACE_FUNCTION_INCLUDED #define ETL_INPLACE_FUNCTION_INCLUDED #include "platform.h" #if ETL_USING_CPP11 #include "error_handler.h" #include "exception.h" #include "file_error_numbers.h" #include "function_traits.h" #include "invoke.h" #include "optional.h" #include "placement_new.h" #include "type_traits.h" #include "utility.h" #if !defined(ETL_DEFAULT_INPLACE_FUNCTION_SIZE) #define ETL_DEFAULT_INPLACE_FUNCTION_SIZE 32 #endif #if !defined(ETL_DEFAULT_INPLACE_FUNCTION_ALIGNMENT) #define ETL_DEFAULT_INPLACE_FUNCTION_ALIGNMENT alignof(void*) #endif namespace etl { //************************************************************************* /// Exception base for inplace_function //************************************************************************* class inplace_function_exception : public etl::exception { public: inplace_function_exception(string_type reason_, string_type file_name_, numeric_type line_number_) : exception(reason_, file_name_, line_number_) { } }; //************************************************************************* /// Inplace function uninitialized exception. //************************************************************************* class inplace_function_uninitialized : public inplace_function_exception { public: inplace_function_uninitialized(string_type file_name_, numeric_type line_number_) : inplace_function_exception(ETL_ERROR_TEXT("inplace_function:uninitialized", ETL_INPLACE_FUNCTION_FILE_ID"A"), file_name_, line_number_) { } }; namespace private_inplace_function { //************************************************************************* /// The vtable for inplace_function //************************************************************************* template struct inplace_function_vtable { using invoke_type = TReturn (*)(void*, TArgs...); using destroy_type = void (*)(void*); using move_type = void (*)(void*, void*); using copy_type = void (*)(const void*, void*); invoke_type invoke = nullptr; destroy_type destroy = nullptr; move_type move = nullptr; copy_type copy = nullptr; //***************************************** // Constructor //***************************************** ETL_CONSTEXPR inplace_function_vtable(invoke_type i, destroy_type d, move_type m, copy_type c) : invoke(i) , destroy(d) , move(m) , copy(c) { } //***************************************** // Payload records for member bindings //***************************************** // Non-const member function //***************************************** template struct member_target { TReturn (TObject::*member)(TArgs...); TObject obj; }; //***************************************** // Const member function //***************************************** template struct const_member_target { TReturn (TObject::*member)(TArgs...) const; TObject obj; }; //***************************************** // Storage helpers //***************************************** // Copy construct //***************************************** template static void copy_construct(const void* src, void* dst) { ::new (dst) T(*static_cast(src)); } //***************************************** // Move construct //***************************************** template static void move_construct(void* src, void* dst) { ::new (dst) T(etl::move(*static_cast(src))); if (DestroySrc) { static_cast(src)->~T(); } } //***************************************** // Stub functions //***************************************** // Destroy function //***************************************** template static void destroy_stub(void* p) { static_cast(p)->~T(); } //***************************************** // Free function - Returning value //***************************************** template ::value, int> = 0> static R stub_function_ptr(void* p, TArgs... a) { return (*static_cast(p))(etl::forward(a)...); } //***************************************** // Free function - returning void //***************************************** template ::value, int> = 0> static void stub_function_ptr(void* p, TArgs... a) { (*static_cast(p))(etl::forward(a)...); } //***************************************** // Member target - Returning value //***************************************** template ::value, int> = 0> static R stub_member(void* p, TArgs... a) { auto* s = static_cast(p); return (s->obj.*(s->member))(etl::forward(a)...); } //***************************************** // Member target - returning void //***************************************** template ::value, int> = 0> static void stub_member(void* p, TArgs... a) { auto* s = static_cast(p); (s->obj.*(s->member))(etl::forward(a)...); } //***************************************** // Functor / lambda stored in payload - returning value //***************************************** template ::value, int> = 0> static R stub_functor(void* p, TArgs... a) { return (*static_cast(p))(etl::forward(a)...); } //***************************************** // Functor / lambda stored in payload - returning void //***************************************** template ::value, int> = 0> static void stub_functor(void* p, TArgs... a) { (*static_cast(p))(etl::forward(a)...); } //***************************************** // Const functor / lambda stored in payload - returning value //***************************************** template ::value, int> = 0> static R stub_const_functor(void* p, TArgs... a) { return (*static_cast(p))(etl::forward(a)...); } //***************************************** // Const functor / lambda stored in payload - returning void //***************************************** template ::value, int> = 0> static void stub_const_functor(void* p, TArgs... a) { (*static_cast(p))(etl::forward(a)...); } //***************************************** // Compile-time bound free function - returning value //***************************************** template ::value, int> = 0> static R stub_ct_function(void*, TArgs... a) { return Function(etl::forward(a)...); } //***************************************** // Compile-time bound free function - returning void //***************************************** template ::value, int> = 0> static void stub_ct_function(void*, TArgs... a) { Function(etl::forward(a)...); } //***************************************** // Compile-time bound member + object - returning value //***************************************** template ::value, int> = 0> static R stub_ct_member(void*, TArgs... a) { return (Object->*Method)(etl::forward(a)...); } //***************************************** // Compile-time bound member + object - returning void //***************************************** template ::value, int> = 0> static void stub_ct_member(void*, TArgs... a) { (Object->*Method)(etl::forward(a)...); } //***************************************** // Compile-time bound const member + object - returning value //***************************************** template ::value, int> = 0> static R stub_ct_const_member(void*, TArgs... a) { return (Object->*Method)(etl::forward(a)...); } //***************************************** // Compile-time bound const member + object - returning void //***************************************** template ::value, int> = 0> static void stub_ct_const_member(void*, TArgs... a) { (Object->*Method)(etl::forward(a)...); } //***************************************** // Compile-time bound operator() + object - returning value //***************************************** template ::value, int> = 0> static R stub_ct_operator(void*, TArgs... a) { return (*Object)(etl::forward(a)...); } //***************************************** // Compile-time bound operator() + object - returning void //***************************************** template ::value, int> = 0> static void stub_ct_operator(void*, TArgs... a) { (*Object)(etl::forward(a)...); } //***************************************** // vtable factories //***************************************** // Free function pointer //***************************************** static const inplace_function_vtable* for_function_ptr() { using function_type = TReturn (*)(TArgs...); static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_function_ptr, nullptr, &inplace_function_vtable::template move_construct, &inplace_function_vtable::template copy_construct); return &vtable; } //***************************************** // Member function pointer //***************************************** template static const inplace_function_vtable* for_member() { using target_t = member_target; constexpr bool destroy_src_on_move = !etl::is_trivially_destructible::value; constexpr destroy_type destroy_ptr = etl::is_trivially_destructible::value ? nullptr : &inplace_function_vtable::template destroy_stub; static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_member, destroy_ptr, &inplace_function_vtable::template move_construct< target_t, destroy_src_on_move>, &inplace_function_vtable::template copy_construct); return &vtable; } //***************************************** // Const member function pointer //***************************************** template static const inplace_function_vtable* for_const_member() { using target_t = const_member_target; constexpr bool destroy_src_on_move = !etl::is_trivially_destructible::value; constexpr destroy_type destroy_ptr = etl::is_trivially_destructible::value ? nullptr : &inplace_function_vtable::template destroy_stub; static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_member, destroy_ptr, &inplace_function_vtable::template move_construct< target_t, destroy_src_on_move>, &inplace_function_vtable::template copy_construct); return &vtable; } //***************************************** // Functor / lambda //***************************************** template static const inplace_function_vtable* for_functor() { constexpr bool destroy_src_on_move = !etl::is_trivially_destructible::value; constexpr destroy_type destroy_ptr = etl::is_trivially_destructible::value ? nullptr : &inplace_function_vtable::template destroy_stub; static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_functor, destroy_ptr, &inplace_function_vtable::template move_construct< TObject, destroy_src_on_move>, &inplace_function_vtable::template copy_construct); return &vtable; } //***************************************** // Const functor / lambda //***************************************** template static const inplace_function_vtable* for_const_functor() { constexpr bool destroy_src_on_move = !etl::is_trivially_destructible::value; constexpr destroy_type destroy_ptr = etl::is_trivially_destructible::value ? nullptr : &inplace_function_vtable::template destroy_stub; static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_const_functor, destroy_ptr, &inplace_function_vtable::template move_construct< TObject, destroy_src_on_move>, &inplace_function_vtable::template copy_construct); return &vtable; } //***************************************** // Compile-time bound free function //***************************************** template static const inplace_function_vtable* for_compile_time_function() { static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_function, nullptr, nullptr, nullptr); return &vtable; } //***************************************** // Compile-time bound member function + object //***************************************** template static const inplace_function_vtable* for_compile_time_member() { static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_member, nullptr, nullptr, nullptr); return &vtable; } //***************************************** // Compile-time bound const member function + object //***************************************** template static const inplace_function_vtable* for_compile_time_const_member() { static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_const_member< TObject, Method, Object>, nullptr, nullptr, nullptr); return &vtable; } //***************************************** // Compile-time bound operator() + object //***************************************** template static const inplace_function_vtable* for_compile_time_operator() { static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_operator, nullptr, nullptr, nullptr); return &vtable; } }; } // namespace private_inplace_function //************************************************************************* /// inplace_function //************************************************************************* template class inplace_function; //************************************************************************* // Is not an etl::inplace_function //************************************************************************* template struct is_inplace_function : etl::false_type { }; //************************************************************************* // Is an etl::inplace_function //************************************************************************* template struct is_inplace_function< etl::inplace_function> : etl::true_type { }; //************************************************************************* /// inplace_function /// A function wrapper with in-place storage for the callable object. /// \tparam TReturn The return type of the function signature. /// \tparam TArgs The argument types of the function signature. /// \tparam Object_Size The size of the in-place storage for the callable /// object. \tparam Object_Alignment The alignment of the in-place storage for /// the callable object. //************************************************************************* template class inplace_function { private: using this_type = inplace_function; using storage_type = etl::uninitialized_buffer; using vtable_type = private_inplace_function::inplace_function_vtable; using function_ptr = TReturn (*)(TArgs...); public: using function_type = TReturn(TArgs...); using return_type = TReturn; using argument_types = etl::type_list; //************************************************************************* /// Default constructor //************************************************************************* inplace_function() noexcept = default; //************************************************************************* /// Copy constructor ///\param other The other inplace_function to copy from. //************************************************************************* inplace_function(const inplace_function& other) { clone_from(other); } //************************************************************************* /// Copy constructor for different object buffer sizes /// Static asserts if the destination buffer is too small. /// Static assert if the destination alignment is too small. /// \param other The other inplace_function to copy from. ///************************************************************************* template inplace_function(const etl::inplace_function& other) { static_assert(Object_Size >= Other_Object_Size, "etl::inplace_function: Destination object size too small"); static_assert(Object_Alignment >= Other_Object_Alignment, "etl::inplace_function: Destination object alignment too small"); clone_from(other); } //************************************************************************* /// Move constructor /// \param other The other inplace_function to move from. //************************************************************************* inplace_function(inplace_function&& other) noexcept { move_from(other); } //************************************************************************* /// Move constructor for different object buffer /// Static asserts if the destination buffer is too small. /// Static assert if the destination alignment is too small. /// \param other The other inplace_function to move from. //************************************************************************* template inplace_function(etl::inplace_function&& other) noexcept { static_assert(Object_Size >= Other_Object_Size, "etl::inplace_function: Destination object size too small"); static_assert(Object_Alignment >= Other_Object_Alignment, "etl::inplace_function: Destination object alignment too small"); move_from(other); } //************************************************************************* /// Destructor //************************************************************************* ~inplace_function() noexcept { clear(); } //************************************************************************* /// Construct from function pointer. /// \param f The function pointer. //************************************************************************* inplace_function(function_ptr f) { set(f); } //************************************************************************* /// Construct from a nullptr. /// Clears the inplace_function. /// \param nullptr_t Null pointer //************************************************************************* inplace_function(etl::nullptr_t) noexcept { clear(); } //************************************************************************* /// Construct from object + non-const member function (runtime). /// \tparam TObject The object type. /// \param method The member function pointer. /// \param obj The object. //************************************************************************* template inplace_function(TReturn (TObject::*method)(TArgs...), TObjectArg&& obj) { set(method, etl::forward(obj)); } //************************************************************************* /// Construct from object + const member function (runtime). /// \tparam TObject The object type. /// \param method The member function pointer. /// \param obj The object. //************************************************************************* template inplace_function(TReturn (TObject::*method)(TArgs...) const, TObjectArg&& obj) { set(method, etl::forward(obj)); } //************************************************************************* /// Construct from lambda/functor (runtime). /// \tparam TLambda The lambda/functor type. /// \param lambda The lambda/functor. //************************************************************************* template < typename TLambda, typename T = typename etl::decay::type, typename = etl::enable_if_t< etl::is_class::value && !is_inplace_function::value, void>> inplace_function(TLambda& lambda) { set(lambda); } //************************************************************************* /// Construct from const lambda/functor (runtime). /// \tparam TLambda The lambda/functor type. /// \param lambda The lambda/functor. //************************************************************************* template < typename TLambda, typename T = typename etl::decay::type, typename = etl::enable_if_t< etl::is_class::value && !is_inplace_function::value, void>> inplace_function(const TLambda& lambda) { set(lambda); } //************************************************************************* /// Create from function pointer (runtime). /// \param f The function pointer. //************************************************************************* void set(function_ptr f) { // Validate that 'f' is invocable with (TArgs...) and returns TReturn static_assert(etl::is_invocable_r::value, "etl::inplace_function: function pointer is not compatible with the inplace_function signature"); static_assert(Object_Size >= sizeof(function_ptr), "etl::inplace_function: storage size too small"); static_assert(Object_Alignment >= alignof(function_ptr), "etl::inplace_function: storage alignment too small"); clear(); // Construct the object in the storage. ::new (storage_ptr()) function_ptr(f); vtable = vtable_type::for_function_ptr(); } //************************************************************************* /// Create from object + member function (runtime). /// \tparam TObject The object type. /// \param method The member function pointer. /// \param obj The object. //************************************************************************* template void set(TReturn (TObject::*method)(TArgs...), TObjectArg&& obj) { using D = etl::decay_t; static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound member function is not compatible with the inplace_function signature"); using target_t = typename vtable_type::template member_target; static_assert(Object_Size >= sizeof(target_t), "etl::inplace_function: storage size too small"); static_assert(Object_Alignment >= alignof(target_t), "etl::inplace_function: storage alignment too small"); clear(); ::new (storage_ptr()) target_t{method, etl::forward(obj)}; vtable = vtable_type::template for_member(); } //************************************************************************* /// Create from object + const member function (runtime). /// \tparam TObject The object type. /// \param method The member function pointer. /// \param obj The object. //************************************************************************* template void set(TReturn (TObject::*method)(TArgs...) const, TObjectArg&& obj) { using D = etl::decay_t; static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound member function is not compatible with the inplace_function signature"); using target_t = typename vtable_type::template const_member_target; static_assert(Object_Size >= sizeof(target_t), "etl::inplace_function: storage size too small"); static_assert(Object_Alignment >= alignof(target_t), "etl::inplace_function: storage alignment too small"); clear(); ::new (storage_ptr()) target_t{method, etl::forward(obj)}; vtable = vtable_type::template for_const_member(); } //************************************************************************* /// Create from lambda/functor (runtime). /// \tparam TLambda The lambda/functor type. /// \param lambda The lambda/functor. //************************************************************************* template < typename TLambda, typename T = typename etl::decay::type, typename = etl::enable_if_t< etl::is_class::value && !is_inplace_function::value, void>> void set(TLambda& lambda) { // Validate that 'method' is invocable with (TObject&, TArgs...) and // returns TReturn static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature"); static_assert(Object_Size >= sizeof(T), "etl::inplace_function: Object size too small"); static_assert(Object_Alignment >= alignof(T), "etl::inplace_function: Object alignment too small"); clear(); // Construct the object in the storage. ::new (storage_ptr()) T(lambda); vtable = vtable_type::template for_functor(); } //************************************************************************* /// Create from const lambda/functor (runtime). /// \tparam TLambda The lambda/functor type. /// \param lambda The lambda/functor. //************************************************************************* template < typename TLambda, typename T = typename etl::decay::type, typename = etl::enable_if_t< etl::is_class::value && !is_inplace_function::value, void>> void set(const TLambda& lambda) { static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature"); static_assert(Object_Size >= sizeof(T), "etl::inplace_function: Object size too small"); static_assert(Object_Alignment >= alignof(T), "etl::inplace_function: Object alignment too small"); clear(); // Construct the object in the storage. ::new (storage_ptr()) T(lambda); vtable = vtable_type::template for_const_functor(); } //************************************************************************* /// Set from a compile-time bound free function. /// Function must have external linkage. /// Usage: ipf.template set<&FreeFn>(); /// \tparam Function The function pointer. //************************************************************************* template void set() { static_assert(etl::is_invocable_r::value, "etl::inplace_function: function pointer is not compatible with the inplace_function signature"); clear(); vtable = vtable_type::template for_compile_time_function(); } //************************************************************************* /// Set from a compile-time bound non-const member + instance reference. /// Instance must have external linkage when used as an NTTP. /// \tparam TObject The object type. /// \tparam Method The member function pointer. /// \tparam Instance The instance reference. //************************************************************************* template void set() { static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound member function is not compatible with the inplace_function signature"); clear(); vtable = vtable_type::template for_compile_time_member(); } //************************************************************************* /// Set from a compile-time bound const member + instance reference. /// Instance must have external linkage when used as an NTTP. /// \tparam TObject The object type. /// \tparam Method The member function pointer. /// \tparam Instance The instance reference. //************************************************************************* template void set() { static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound member function is not compatible with the inplace_function signature"); clear(); vtable = vtable_type::template for_compile_time_const_member(); } //************************************************************************* /// operator() + instance reference (compile-time, non-const) /// \tparam TObject The object type. /// \tparam Instance The instance reference. //************************************************************************* template < typename TObject, TObject& Instance, typename T = etl::decay_t, typename = etl::enable_if_t< etl::is_class::value && etl::has_call_operator::value && !etl::function_traits::is_const>> void set() { static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature"); clear(); vtable = vtable_type::template for_compile_time_operator(); } //************************************************************************* /// operator() + instance reference (compile-time, const) /// \tparam TObject The object type. /// \tparam Instance The instance reference. //************************************************************************* template < typename TObject, const TObject& Instance, typename T = etl::decay_t, typename = etl::enable_if_t< etl::is_class::value && etl::has_call_operator::value && etl::function_traits::is_const>> void set() { static_assert(etl::is_invocable_r::value, "etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature"); clear(); vtable = vtable_type::template for_compile_time_operator(); } //************************************************************************* /// Free function (external linkage) /// \tparam Function The function pointer. /// \return The constructed inplace_function. //************************************************************************* template static this_type create() { return this_type(vtable_type::template for_compile_time_function()); } //************************************************************************* /// Non-const member + instance reference (compile time) /// \tparam TObject The object type. /// \tparam Method The member function pointer. /// \tparam Instance The instance reference. /// \return The constructed inplace_function. //************************************************************************* template static this_type create() { return this_type(vtable_type::template for_compile_time_member()); } //************************************************************************* /// Const member + instance reference (compile time) /// \tparam TObject The object type. /// \tparam Method The member function pointer. /// \tparam Instance The instance reference. /// \return The constructed inplace_function. //************************************************************************* template static this_type create() { return this_type(vtable_type::template for_compile_time_const_member()); } //************************************************************************* /// operator() + instance reference (compile time, non-const) /// \tparam TObject The object type. /// \tparam Instance The instance reference. /// \return The constructed inplace_function. //************************************************************************* template static this_type create() { return this_type(vtable_type::template for_compile_time_operator()); } //************************************************************************* /// Assignment operator /// \param rhs The other inplace_function to copy from. /// \return The current inplace_function. //************************************************************************* inplace_function& operator=(const inplace_function& rhs) { if (this != &rhs) { clear(); clone_from(rhs); } return *this; } //************************************************************************* /// Move assignment operator /// \param rhs The other inplace_function to move from. /// \return The current inplace_function. //************************************************************************* inplace_function& operator=(inplace_function&& rhs) noexcept { if (this != &rhs) { clear(); move_from(rhs); } return *this; } //************************************************************************* /// Assignment from nullptr /// Clears the inplace_function. /// \param nullptr_t Null pointer /// \return The current inplace_function. //************************************************************************* inplace_function& operator=(etl::nullptr_t) noexcept { clear(); return *this; } //************************************************************************* /// Assignment from function pointer /// \param f The function pointer. /// \return The current inplace_function. //************************************************************************* inplace_function& operator=(function_ptr f) { set(f); return *this; } //************************************************************************* /// Assignment from lambda/functor (runtime). /// Enabled only for class types that are not etl::inplace_function. /// \tparam TLambda The lambda/functor type. /// \param lambda The lambda/functor. /// \return The current inplace_function. //************************************************************************* template < typename TLambda, typename T = typename etl::decay::type, typename = etl::enable_if_t< etl::is_class::value && !is_inplace_function::value, void>> inplace_function& operator=(TLambda&& lambda) { set(etl::forward(lambda)); return *this; } //************************************************************************* /// Swap with another inplace_function /// \param other The other inplace_function. //************************************************************************* void swap(inplace_function& other) noexcept { if (this == &other) { return; } do_swap(vtable, storage_ptr(), other.vtable, other.storage_ptr()); } //************************************************************************* /// Checks if the inplace_function is valid (has a target). /// \return true if the inplace_function is valid, false if /// not. //************************************************************************* ETL_NODISCARD bool is_valid() const noexcept { return (vtable != nullptr); } //************************************************************************* /// Boolean conversion operator. /// Checks if the inplace_function is valid (has a target). /// \return true if the inplace_function is valid, false if /// not. //************************************************************************* ETL_NODISCARD explicit operator bool() const noexcept { return is_valid(); } //************************************************************************* /// Execute the inplace_function. /// Triggers inplace_function_uninitialized assertion if not valid. /// \param args The arguments to pass to the inplace_function. /// \return The result of the invocation. //************************************************************************* TReturn operator()(TArgs... args) const { ETL_ASSERT(is_valid(), ETL_ERROR(inplace_function_uninitialized)); return vtable->invoke(storage_ptr(), etl::forward(args)...); } //************************************************************************* /// Execute the inplace_function, if valid. /// 'void' return. /// \param args The arguments to pass to the inplace_function. /// \return true if executed, false if not valid. //************************************************************************* template typename etl::enable_if_t::value, bool> call_if(TArgs... args) const { if (is_valid()) { vtable->invoke(storage_ptr(), etl::forward(args)...); return true; } else { return false; } } //************************************************************************* /// Execute the inplace_function if valid. /// Non 'void' return. /// \param args The arguments to pass to the inplace_function. /// \return etl::optional with result if executed, empty etl::optional if /// not valid. //************************************************************************* template typename etl::enable_if_t::value, etl::optional> call_if(TArgs... args) const { etl::optional result; if (is_valid()) { result = vtable->invoke(storage_ptr(), etl::forward(args)...); } return result; } //************************************************************************* /// Execute the inplace_function if valid or call alternative. /// Run time alternative. /// \tparam TAlternative The alternative callable. /// \param alternative The alternative callable. /// \param args The arguments to pass to the inplace_function or /// alternative. \return The result of the invocation. //************************************************************************* template TReturn call_or(TAlternative&& alternative, TArgs... args) const { if (is_valid()) { return vtable->invoke(storage_ptr(), etl::forward(args)...); } else { return etl::forward(alternative)(etl::forward(args)...); } } //************************************************************************* /// Execute the inplace_function if valid or call alternative. /// Compile time alternative. /// \tparam Alternative The alternative callable. /// \param args The arguments to pass to the inplace_function or /// alternative. \return The result of the invocation. //************************************************************************* template TReturn call_or(TArgs... args) const { if (is_valid()) { return vtable->invoke(storage_ptr(), etl::forward(args)...); } else { return (Alternative)(etl::forward(args)...); } } //************************************************************************* /// Clears the inplace_function /// Destroys any stored callable object. //************************************************************************* void clear() noexcept { if (is_valid()) { if (vtable->destroy) { vtable->destroy(storage_ptr()); } vtable = nullptr; } } //************************************************************************* /// Get the storage size /// \return The storage size. //************************************************************************* ETL_NODISCARD static constexpr size_t size() noexcept { return Object_Size; } //************************************************************************* /// Get the storage alignment /// \return The storage alignment. //************************************************************************* ETL_NODISCARD static constexpr size_t alignment() noexcept { return Object_Alignment; } private: // Allow cross-size access to internals template friend class inplace_function; //************************************************************************* // Direct-initialization constructor for CT-bound vtables (no payload). //************************************************************************* explicit inplace_function(const vtable_type* vt) noexcept : vtable(vt) , storage() { } //************************************************************************* // clone_from //************************************************************************* template void clone_from(const etl::inplace_function& other) { vtable = other.vtable; if (vtable && vtable->copy) { vtable->copy(&other.storage, &storage); } } //************************************************************************* // move_from //************************************************************************* template void move_from(etl::inplace_function& other) { vtable = other.vtable; if (vtable && vtable->move) { vtable->move(&other.storage, &storage); } other.vtable = nullptr; } //************************************************************************* // Internal swap implementation //************************************************************************* static void do_swap(const vtable_type*& vt_a, void* storage_a, const vtable_type*& vt_b, void* storage_b) noexcept { const bool a_valid = (vt_a != nullptr); const bool b_valid = (vt_b != nullptr); if (!a_valid && !b_valid) { return; } if (!a_valid) { // Only 'b' is valid if (vt_b->move) { vt_b->move(storage_b, storage_a); } else if (vt_b->copy) { vt_b->copy(storage_b, storage_a); } vt_a = vt_b; vt_b = nullptr; return; } // Only 'a' is valid if (!b_valid) { if (vt_a->move) { vt_a->move(storage_a, storage_b); } else if (vt_a->copy) { vt_a->copy(storage_a, storage_b); } vt_b = vt_a; vt_a = nullptr; return; } // Both valid. // If both have no payload (compile-time bound: no move/copy) just swap // vtable pointers. if (!vt_a->move && !vt_a->copy && !vt_b->move && !vt_b->copy) { const vtable_type* tmp = vt_a; vt_a = vt_b; vt_b = tmp; return; } // General case alignas(Object_Alignment) unsigned char temp[Object_Size]; // a -> temp if (vt_a->move) { vt_a->move(storage_a, temp); } else if (vt_a->copy) { vt_a->copy(storage_a, temp); } // b -> a if (vt_b->move) { vt_b->move(storage_b, storage_a); } else if (vt_b->copy) { vt_b->copy(storage_b, storage_a); } // temp -> b if (vt_a->move) { vt_a->move(temp, storage_b); } else if (vt_a->copy) { vt_a->copy(temp, storage_b); } // Swap vtable pointers const vtable_type* tmp = vt_a; vt_a = vt_b; vt_b = tmp; } //************************************************************************* // Get pointer to storage as a void* //************************************************************************* void* storage_ptr() const noexcept { return const_cast(static_cast(&storage)); } const vtable_type* vtable = nullptr; storage_type storage; }; //************************************************************************* /// Declare an inplace_function from a single type. /// \tparam TSignature The function signature. /// \tparam TStorage The storage type. //************************************************************************* template using inplace_function_for = etl::inplace_function), alignof(etl::decay_t)>; //************************************************************************* /// Declare an inplace_function from multiple candidates. Picks the largest /// size/alignment. \tparam TSignature The function signature. \tparam /// T... The stored candidates types. //************************************************************************* template using inplace_function_for_any = etl::inplace_function< TSignature, etl::largest, etl::decay_t...>::size, etl::largest, etl::decay_t...>::alignment>; //************************************************************************* /// Helper to build an inplace_function from a free function pointer. /// Usage: auto ipf = etl::make_inplace_function(); /// \param function The function pointer. /// \return The constructed inplace_function. //************************************************************************* template ETL_NODISCARD etl::inplace_function make_inplace_function(TReturn (*function)(TArgs...)) { using function_ptr = TReturn (*)(TArgs...); return etl::inplace_function_for(function); } //************************************************************************* /// Helper to build an inplace_function bound to a non-const member function. /// Usage: auto ipf = etl::make_inplace_function(obj, &Type::Method); /// \param method The member function pointer. /// \param obj The object. /// \return The constructed inplace_function. //************************************************************************* template ::template member_target> ETL_NODISCARD etl::inplace_function make_inplace_function(TReturn (TObject::*method)(TArgs...), TObject& obj) { return etl::inplace_function_for(method, obj); } //************************************************************************* /// Helper to build an inplace_function bound to a const member function. /// Usage: auto ipf = etl::make_inplace_function(obj, &Type::Method) // obj /// is const \param method The member function pointer. \param obj The /// object. \return The constructed inplace_function. //************************************************************************* template < typename TObject, typename TReturn, typename... TArgs, typename TTarget = typename etl::private_inplace_function::inplace_function_vtable< TReturn, TArgs...>::template const_member_target> ETL_NODISCARD etl::inplace_function make_inplace_function(TReturn (TObject::*method)(TArgs...) const, const TObject& obj) { return etl::inplace_function_for(method, obj); } //************************************************************************* /// Helper to build an inplace_function from a lambda/functor (non-generic, /// non-overloaded). Deduces R(Args...) from &T::operator(). Prefer this when /// operator() is not overloaded/templated. \param lambda The lambda/functor. /// \return The constructed inplace_function. //************************************************************************* template < typename TLambda, typename T = typename etl::decay::type, typename = typename etl::enable_if_t< etl::is_class::value && !is_inplace_function::value, void>, typename TSignature = typename etl::function_traits::function_type> ETL_NODISCARD etl::inplace_function make_inplace_function(TLambda&& lambda) { return etl::inplace_function_for(etl::forward(lambda)); } //************************************************************************* /// Storage-deducing maker (non-class only) to avoid colliding with functor /// maker \param function The function-like object. \return The constructed /// inplace_function. //************************************************************************* template ::type, typename = typename etl::enable_if_t::value, int>> ETL_NODISCARD inplace_function_for make_inplace_function(TType&& function) { // If T is a function type, use a function pointer for storage sizing. using storage_t = typename etl::conditional::value, typename etl::add_pointer::type, T>::type; return inplace_function_for(etl::forward(function)); } #if ETL_USING_CPP17 //************************************************************************* /// Make an inplace_function from a function at compile time. /// Only participates for free function pointers (not member function /// pointers). \tparam Function The function pointer. \return The constructed /// inplace_function. //************************************************************************* template < auto Function, typename F = decltype(Function), typename = etl::enable_if_t< etl::is_pointer::value && etl::is_function>::value>> ETL_NODISCARD auto make_inplace_function() { using function_type = typename etl::function_traits::function_type; return etl::inplace_function::template create(); } //************************************************************************* /// Make an inplace_function from a member function at compile time. /// \tparam TObject The object type. /// \tparam Method The member function pointer. /// \tparam Instance The instance reference. /// \return The constructed inplace_function. //************************************************************************* template < typename TObject, auto Method, TObject& Instance, typename T = decltype(Method), typename = etl::enable_if_t::value>, typename = etl::enable_if_t::is_const>> ETL_NODISCARD auto make_inplace_function() { using function_type = typename etl::function_traits::function_type; return etl::inplace_function::template create< TObject, Method, Instance>(); } //************************************************************************* /// Make an inplace_function from a const member function at compile time. /// \tparam TObject The object type. /// \tparam Method The member function pointer. /// \tparam Instance The instance reference. /// \return The constructed inplace_function. //************************************************************************* template < typename TObject, auto Method, const TObject& Instance, typename T = decltype(Method), typename = etl::enable_if_t::value>, typename = etl::enable_if_t::is_const>> ETL_NODISCARD auto make_inplace_function() { using function_type = typename etl::function_traits::function_type; return etl::inplace_function::template create< TObject, Method, Instance>(); } //************************************************************************* /// Make an inplace_function from operator() at compile time. /// \tparam TObject The object type. /// \tparam Instance The instance reference. /// \return The constructed inplace_function. //************************************************************************* template < typename TObject, TObject& Instance, typename T = etl::decay_t, typename = etl::enable_if_t::value && etl::has_call_operator::value>> ETL_NODISCARD auto make_inplace_function() { using function_type = typename etl::function_traits< decltype(&TObject::operator())>::function_type; return etl::inplace_function::template create(); } #endif //************************************************************************* /// Swap two inplace_functions. /// \param lhs The first inplace_function. /// \param rhs The second inplace_function. //************************************************************************* template void swap(etl::inplace_function& lhs, etl::inplace_function& rhs) noexcept { lhs.swap(rhs); } //************************************************************************* /// Check inplace_function for equality with nullptr. /// \param lhs The inplace_function. /// \param rhs The nullptr. /// \returns true if equal. //************************************************************************* template ETL_NODISCARD bool operator==(const etl::inplace_function& lhs, etl::nullptr_t) { return !lhs.is_valid(); } //************************************************************************* /// Check inplace_function for equality with nullptr. /// \param lhs The nullptr. /// \param rhs The inplace_function. /// \returns true if equal. //************************************************************************* template ETL_NODISCARD bool operator==(etl::nullptr_t, const etl::inplace_function& rhs) { return !rhs.is_valid(); } //************************************************************************* /// Check inplace_function for inequality with nullptr. /// \param lhs The inplace_function. /// \param rhs The nullptr. /// \returns true if not equal. //************************************************************************* template ETL_NODISCARD bool operator!=(const etl::inplace_function& lhs, etl::nullptr_t) { return lhs.is_valid(); } //************************************************************************* /// Check inplace_function for inequality with nullptr. /// \param lhs The nullptr. /// \param rhs The inplace_function. /// \returns true if not equal. //************************************************************************* template ETL_NODISCARD bool operator!=(etl::nullptr_t, const etl::inplace_function& rhs) { return rhs.is_valid(); } } // namespace etl #endif #endif