Loading...
Searching...
No Matches
inplace_function.h
Go to the documentation of this 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
{
//*************************************************************************
//*************************************************************************
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_)
{
}
};
//*************************************************************************
//*************************************************************************
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
{
//*************************************************************************
//*************************************************************************
template <typename TReturn, typename... TArgs>
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 <typename TObject>
struct member_target
{
TReturn (TObject::*member)(TArgs...);
TObject obj;
};
//*****************************************
// Const member function
//*****************************************
template <typename TObject>
struct const_member_target
{
TReturn (TObject::*member)(TArgs...) const;
TObject obj;
};
//*****************************************
// Storage helpers
//*****************************************
// Copy construct
//*****************************************
template <typename T>
static void copy_construct(const void* src, void* dst)
{
::new (dst) T(*static_cast<const T*>(src));
}
//*****************************************
// Move construct
//*****************************************
template <typename T, bool DestroySrc>
static void move_construct(void* src, void* dst)
{
::new (dst) T(etl::move(*static_cast<T*>(src)));
if (DestroySrc)
{
static_cast<T*>(src)->~T();
}
}
//*****************************************
// Stub functions
//*****************************************
// Destroy function
//*****************************************
template <typename T>
static void destroy_stub(void* p)
{
static_cast<T*>(p)->~T();
}
//*****************************************
// Free function - Returning value
//*****************************************
template <typename F, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_function_ptr(void* p, TArgs... a)
{
return (*static_cast<F*>(p))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Free function - returning void
//*****************************************
template <typename F, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_function_ptr(void* p, TArgs... a)
{
(*static_cast<F*>(p))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Member target - Returning value
//*****************************************
template <typename Target, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_member(void* p, TArgs... a)
{
auto* s = static_cast<Target*>(p);
return (s->obj.*(s->member))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Member target - returning void
//*****************************************
template <typename Target, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_member(void* p, TArgs... a)
{
auto* s = static_cast<Target*>(p);
(s->obj.*(s->member))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Functor / lambda stored in payload - returning value
//*****************************************
template <typename T, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_functor(void* p, TArgs... a)
{
return (*static_cast<T*>(p))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Functor / lambda stored in payload - returning void
//*****************************************
template <typename T, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_functor(void* p, TArgs... a)
{
(*static_cast<T*>(p))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Const functor / lambda stored in payload - returning value
//*****************************************
template <typename T, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_const_functor(void* p, TArgs... a)
{
return (*static_cast<const T*>(p))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Const functor / lambda stored in payload - returning void
//*****************************************
template <typename T, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_const_functor(void* p, TArgs... a)
{
(*static_cast<const T*>(p))(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound free function - returning value
//*****************************************
template <TReturn (*Function)(TArgs...), typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_ct_function(void*, TArgs... a)
{
return Function(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound free function - returning void
//*****************************************
template <TReturn (*Function)(TArgs...), typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_ct_function(void*, TArgs... a)
{
Function(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound member + object - returning value
//*****************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject* Object, typename R = TReturn,
etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_ct_member(void*, TArgs... a)
{
return (Object->*Method)(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound member + object - returning void
//*****************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject* Object, typename R = TReturn,
etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_ct_member(void*, TArgs... a)
{
(Object->*Method)(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound const member + object - returning value
//*****************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject* Object, typename R = TReturn,
etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_ct_const_member(void*, TArgs... a)
{
return (Object->*Method)(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound const member + object - returning void
//*****************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject* Object, typename R = TReturn,
etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_ct_const_member(void*, TArgs... a)
{
(Object->*Method)(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound operator() + object - returning value
//*****************************************
template <typename TObject, TObject* Object, typename R = TReturn, etl::enable_if_t<!etl::is_void<R>::value, int> = 0>
static R stub_ct_operator(void*, TArgs... a)
{
return (*Object)(etl::forward<TArgs>(a)...);
}
//*****************************************
// Compile-time bound operator() + object - returning void
//*****************************************
template <typename TObject, TObject* Object, typename R = TReturn, etl::enable_if_t<etl::is_void<R>::value, int> = 0>
static void stub_ct_operator(void*, TArgs... a)
{
(*Object)(etl::forward<TArgs>(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<function_type>, nullptr,
&inplace_function_vtable::template move_construct<function_type, false>,
&inplace_function_vtable::template copy_construct<function_type>);
return &vtable;
}
//*****************************************
// Member function pointer
//*****************************************
template <typename TObject>
static const inplace_function_vtable* for_member()
{
using target_t = member_target<TObject>;
constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
constexpr destroy_type destroy_ptr =
etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<target_t>;
static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_member<target_t>, destroy_ptr,
&inplace_function_vtable::template move_construct< target_t, destroy_src_on_move>,
&inplace_function_vtable::template copy_construct<target_t>);
return &vtable;
}
//*****************************************
// Const member function pointer
//*****************************************
template <typename TObject>
static const inplace_function_vtable* for_const_member()
{
using target_t = const_member_target<TObject>;
constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
constexpr destroy_type destroy_ptr =
etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<target_t>;
static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_member<target_t>, destroy_ptr,
&inplace_function_vtable::template move_construct< target_t, destroy_src_on_move>,
&inplace_function_vtable::template copy_construct<target_t>);
return &vtable;
}
//*****************************************
// Functor / lambda
//*****************************************
template <typename TObject>
static const inplace_function_vtable* for_functor()
{
constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
constexpr destroy_type destroy_ptr =
etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<TObject>;
static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_functor<TObject>, destroy_ptr,
&inplace_function_vtable::template move_construct< TObject, destroy_src_on_move>,
&inplace_function_vtable::template copy_construct<TObject>);
return &vtable;
}
//*****************************************
// Const functor / lambda
//*****************************************
template <typename TObject>
static const inplace_function_vtable* for_const_functor()
{
constexpr bool destroy_src_on_move = !etl::is_trivially_destructible<TObject>::value;
constexpr destroy_type destroy_ptr =
etl::is_trivially_destructible<TObject>::value ? nullptr : &inplace_function_vtable::template destroy_stub<TObject>;
static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_const_functor<TObject>, destroy_ptr,
&inplace_function_vtable::template move_construct< TObject, destroy_src_on_move>,
&inplace_function_vtable::template copy_construct<TObject>);
return &vtable;
}
//*****************************************
// Compile-time bound free function
//*****************************************
template <TReturn (*Function)(TArgs...)>
static const inplace_function_vtable* for_compile_time_function()
{
static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_function<Function>, nullptr, nullptr, nullptr);
return &vtable;
}
//*****************************************
// Compile-time bound member function + object
//*****************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject* Object>
static const inplace_function_vtable* for_compile_time_member()
{
static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_member<TObject, Method, Object>, nullptr, nullptr,
nullptr);
return &vtable;
}
//*****************************************
// Compile-time bound const member function + object
//*****************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject* Object>
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 <typename TObject, TObject* Object>
static const inplace_function_vtable* for_compile_time_operator()
{
static const inplace_function_vtable vtable(&inplace_function_vtable::template stub_ct_operator<TObject, Object>, nullptr, nullptr, nullptr);
return &vtable;
}
};
} // namespace private_inplace_function
//*************************************************************************
//*************************************************************************
template <typename TSignature, size_t Object_Size = ETL_DEFAULT_INPLACE_FUNCTION_SIZE,
size_t Object_Alignment = ETL_DEFAULT_INPLACE_FUNCTION_ALIGNMENT>
class inplace_function;
//*************************************************************************
// Is not an etl::inplace_function
//*************************************************************************
template <typename T>
struct is_inplace_function : etl::false_type
{
};
//*************************************************************************
// Is an etl::inplace_function
//*************************************************************************
template <typename TReturn, typename... TArgs, size_t Object_Size, size_t Object_Alignment>
struct is_inplace_function< etl::inplace_function<TReturn(TArgs...), Object_Size, Object_Alignment>> : etl::true_type
{
};
//*************************************************************************
//*************************************************************************
template <typename TReturn, typename... TArgs, size_t Object_Size, size_t Object_Alignment>
class inplace_function<TReturn(TArgs...), Object_Size, Object_Alignment>
{
private:
using this_type = inplace_function<TReturn(TArgs...), Object_Size, Object_Alignment>;
using storage_type = etl::uninitialized_buffer<Object_Size, 1, Object_Alignment>;
using vtable_type = private_inplace_function::inplace_function_vtable<TReturn, TArgs...>;
using function_ptr = TReturn (*)(TArgs...);
public:
using function_type = TReturn(TArgs...);
using return_type = TReturn;
using argument_types = etl::type_list<TArgs...>;
//*************************************************************************
//*************************************************************************
inplace_function() noexcept = default;
//*************************************************************************
//*************************************************************************
inplace_function(const inplace_function& other)
{
clone_from(other);
}
//*************************************************************************
template <size_t Other_Object_Size, size_t Other_Object_Alignment>
inplace_function(const etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>& 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);
}
//*************************************************************************
//*************************************************************************
inplace_function(inplace_function&& other) noexcept
{
move_from(other);
}
//*************************************************************************
//*************************************************************************
template <size_t Other_Object_Size, size_t Other_Object_Alignment>
inplace_function(etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>&& 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);
}
//*************************************************************************
//*************************************************************************
~inplace_function() noexcept
{
clear();
}
//*************************************************************************
//*************************************************************************
inplace_function(function_ptr f)
{
set(f);
}
//*************************************************************************
//*************************************************************************
inplace_function(etl::nullptr_t) noexcept
{
clear();
}
//*************************************************************************
//*************************************************************************
template <typename TObject, typename TObjectArg>
inplace_function(TReturn (TObject::*method)(TArgs...), TObjectArg&& obj)
{
set(method, etl::forward<TObjectArg>(obj));
}
//*************************************************************************
//*************************************************************************
template <typename TObject, typename TObjectArg>
inplace_function(TReturn (TObject::*method)(TArgs...) const, TObjectArg&& obj)
{
set(method, etl::forward<TObjectArg>(obj));
}
//*************************************************************************
//*************************************************************************
template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
inplace_function(TLambda& lambda)
{
set(lambda);
}
//*************************************************************************
//*************************************************************************
template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
inplace_function(const TLambda& lambda)
{
set(lambda);
}
//*************************************************************************
//*************************************************************************
void set(function_ptr f)
{
// Validate that 'f' is invocable with (TArgs...) and returns TReturn
static_assert(etl::is_invocable_r<TReturn, function_ptr, TArgs...>::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();
}
//*************************************************************************
//*************************************************************************
template <typename TObject, typename TObjectArg>
void set(TReturn (TObject::*method)(TArgs...), TObjectArg&& obj)
{
using D = etl::decay_t<TObjectArg>;
static_assert(etl::is_invocable_r<TReturn, decltype(method), D&, TArgs...>::value,
"etl::inplace_function: bound member function is not compatible with the inplace_function signature");
using target_t = typename vtable_type::template member_target<D>;
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<TObjectArg>(obj)};
vtable = vtable_type::template for_member<D>();
}
//*************************************************************************
//*************************************************************************
template <typename TObject, typename TObjectArg>
void set(TReturn (TObject::*method)(TArgs...) const, TObjectArg&& obj)
{
using D = etl::decay_t<TObjectArg>;
static_assert(etl::is_invocable_r<TReturn, decltype(method), D&, TArgs...>::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<D>;
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<TObjectArg>(obj)};
vtable = vtable_type::template for_const_member<D>();
}
//*************************************************************************
//*************************************************************************
template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
void set(TLambda& lambda)
{
// Validate that 'method' is invocable with (TObject&, TArgs...) and
// returns TReturn
static_assert(etl::is_invocable_r<TReturn, T, TArgs...>::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<T>();
}
//*************************************************************************
//*************************************************************************
template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
void set(const TLambda& lambda)
{
static_assert(etl::is_invocable_r<TReturn, const T, TArgs...>::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<T>();
}
//*************************************************************************
//*************************************************************************
template <TReturn (*Function)(TArgs...)>
void set()
{
static_assert(etl::is_invocable_r<TReturn, decltype(Function), TArgs...>::value,
"etl::inplace_function: function pointer is not compatible with the inplace_function signature");
clear();
vtable = vtable_type::template for_compile_time_function<Function>();
}
//*************************************************************************
//*************************************************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject& Instance>
void set()
{
static_assert(etl::is_invocable_r<TReturn, decltype(Method), TObject&, TArgs...>::value,
"etl::inplace_function: bound member function is not compatible with the inplace_function signature");
clear();
vtable = vtable_type::template for_compile_time_member<TObject, Method, &Instance>();
}
//*************************************************************************
//*************************************************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject& Instance>
void set()
{
static_assert(etl::is_invocable_r<TReturn, decltype(Method), const TObject&, TArgs...>::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<TObject, Method, &Instance>();
}
//*************************************************************************
//*************************************************************************
template < typename TObject, TObject& Instance, typename T = etl::decay_t<TObject>,
typename = etl::enable_if_t< etl::is_class<T>::value && etl::has_call_operator<T>::value
&& !etl::function_traits<decltype(&T::operator())>::is_const>>
void set()
{
static_assert(etl::is_invocable_r<TReturn, T, TArgs...>::value,
"etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature");
clear();
vtable = vtable_type::template for_compile_time_operator<TObject, &Instance>();
}
//*************************************************************************
//*************************************************************************
template < typename TObject, const TObject& Instance, typename T = etl::decay_t<TObject>,
typename = etl::enable_if_t< etl::is_class<T>::value && etl::has_call_operator<T>::value
&& etl::function_traits<decltype(&T::operator())>::is_const>>
void set()
{
static_assert(etl::is_invocable_r<TReturn, const T, TArgs...>::value,
"etl::inplace_function: bound lambda/functor is not compatible with the inplace_function signature");
clear();
vtable = vtable_type::template for_compile_time_operator<const TObject, &Instance>();
}
//*************************************************************************
//*************************************************************************
template <TReturn (*Function)(TArgs...)>
static this_type create()
{
return this_type(vtable_type::template for_compile_time_function<Function>());
}
//*************************************************************************
//*************************************************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...), TObject& Instance>
static this_type create()
{
return this_type(vtable_type::template for_compile_time_member<TObject, Method, &Instance>());
}
//*************************************************************************
//*************************************************************************
template <typename TObject, TReturn (TObject::*Method)(TArgs...) const, const TObject& Instance>
static this_type create()
{
return this_type(vtable_type::template for_compile_time_const_member<TObject, Method, &Instance>());
}
//*************************************************************************
//*************************************************************************
template <typename TObject, TObject& Instance>
static this_type create()
{
return this_type(vtable_type::template for_compile_time_operator<TObject, &Instance>());
}
//*************************************************************************
//*************************************************************************
inplace_function& operator=(const inplace_function& rhs)
{
if (this != &rhs)
{
clear();
clone_from(rhs);
}
return *this;
}
//*************************************************************************
//*************************************************************************
inplace_function& operator=(inplace_function&& rhs) noexcept
{
if (this != &rhs)
{
clear();
move_from(rhs);
}
return *this;
}
//*************************************************************************
//*************************************************************************
inplace_function& operator=(etl::nullptr_t) noexcept
{
clear();
return *this;
}
//*************************************************************************
//*************************************************************************
inplace_function& operator=(function_ptr f)
{
set(f);
return *this;
}
//*************************************************************************
//*************************************************************************
template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
typename = etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>>
inplace_function& operator=(TLambda&& lambda)
{
set(etl::forward<TLambda>(lambda));
return *this;
}
//*************************************************************************
//*************************************************************************
void swap(inplace_function& other) noexcept
{
if (this == &other)
{
return;
}
do_swap(vtable, storage_ptr(), other.vtable, other.storage_ptr());
}
//*************************************************************************
//*************************************************************************
ETL_NODISCARD
bool is_valid() const noexcept
{
return (vtable != nullptr);
}
//*************************************************************************
//*************************************************************************
ETL_NODISCARD
explicit operator bool() const noexcept
{
return is_valid();
}
//*************************************************************************
//*************************************************************************
TReturn operator()(TArgs... args) const
{
ETL_ASSERT(is_valid(), ETL_ERROR(inplace_function_uninitialized));
return vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
}
//*************************************************************************
//*************************************************************************
template <typename TRet = TReturn>
typename etl::enable_if_t<etl::is_same<TRet, void>::value, bool> call_if(TArgs... args) const
{
if (is_valid())
{
vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
return true;
}
else
{
return false;
}
}
//*************************************************************************
//*************************************************************************
template <typename TRet = TReturn>
typename etl::enable_if_t<!etl::is_same<TRet, void>::value, etl::optional<TReturn>> call_if(TArgs... args) const
{
etl::optional<TReturn> result;
if (is_valid())
{
result = vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
}
return result;
}
//*************************************************************************
//*************************************************************************
template <typename TAlternative>
TReturn call_or(TAlternative&& alternative, TArgs... args) const
{
if (is_valid())
{
return vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
}
else
{
return etl::forward<TAlternative>(alternative)(etl::forward<TArgs>(args)...);
}
}
//*************************************************************************
//*************************************************************************
template <TReturn (*Alternative)(TArgs...)>
TReturn call_or(TArgs... args) const
{
if (is_valid())
{
return vtable->invoke(storage_ptr(), etl::forward<TArgs>(args)...);
}
else
{
return (Alternative)(etl::forward<TArgs>(args)...);
}
}
//*************************************************************************
//*************************************************************************
void clear() noexcept
{
if (is_valid())
{
if (vtable->destroy)
{
vtable->destroy(storage_ptr());
}
vtable = nullptr;
}
}
//*************************************************************************
//*************************************************************************
ETL_NODISCARD
static constexpr size_t size() noexcept
{
return Object_Size;
}
//*************************************************************************
//*************************************************************************
ETL_NODISCARD
static constexpr size_t alignment() noexcept
{
return Object_Alignment;
}
private:
// Allow cross-size access to internals
template <typename, size_t, size_t>
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 <size_t Other_Object_Size, size_t Other_Object_Alignment>
void clone_from(const etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>& other)
{
vtable = other.vtable;
if (vtable && vtable->copy)
{
vtable->copy(&other.storage, &storage);
}
}
//*************************************************************************
// move_from
//*************************************************************************
template <size_t Other_Object_Size, size_t Other_Object_Alignment>
void move_from(etl::inplace_function<TReturn(TArgs...), Other_Object_Size, Other_Object_Alignment>& 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<void*>(static_cast<const void*>(&storage));
}
const vtable_type* vtable = nullptr;
storage_type storage;
};
//*************************************************************************
//*************************************************************************
template <typename TSignature, typename TStorage>
using inplace_function_for = etl::inplace_function<TSignature, sizeof(etl::decay_t<TStorage>), alignof(etl::decay_t<TStorage>)>;
//*************************************************************************
//*************************************************************************
template <typename TSignature, typename T0, typename... TRest>
using inplace_function_for_any = etl::inplace_function< TSignature, etl::largest<etl::decay_t<T0>, etl::decay_t<TRest>...>::size,
etl::largest<etl::decay_t<T0>, etl::decay_t<TRest>...>::alignment>;
//*************************************************************************
//*************************************************************************
template <typename TReturn, typename... TArgs>
ETL_NODISCARD
etl::inplace_function<TReturn(TArgs...), sizeof(TReturn (*)(TArgs...)), alignof(TReturn (*)(TArgs...))>
make_inplace_function(TReturn (*function)(TArgs...))
{
using function_ptr = TReturn (*)(TArgs...);
return etl::inplace_function_for<TReturn(TArgs...), function_ptr>(function);
}
//*************************************************************************
//*************************************************************************
template <typename TObject, typename TReturn, typename... TArgs,
typename TTarget = typename etl::private_inplace_function::inplace_function_vtable< TReturn, TArgs...>::template member_target<TObject>>
ETL_NODISCARD
etl::inplace_function<TReturn(TArgs...), sizeof(TTarget), alignof(TTarget)> make_inplace_function(TReturn (TObject::*method)(TArgs...),
TObject& obj)
{
return etl::inplace_function_for<TReturn(TArgs...), TTarget>(method, obj);
}
//*************************************************************************
//*************************************************************************
template <
typename TObject, typename TReturn, typename... TArgs,
typename TTarget = typename etl::private_inplace_function::inplace_function_vtable< TReturn, TArgs...>::template const_member_target<TObject>>
ETL_NODISCARD
etl::inplace_function<TReturn(TArgs...), sizeof(TTarget), alignof(TTarget)> make_inplace_function(TReturn (TObject::*method)(TArgs...) const,
const TObject& obj)
{
return etl::inplace_function_for<TReturn(TArgs...), TTarget>(method, obj);
}
//*************************************************************************
//*************************************************************************
template < typename TLambda, typename T = typename etl::decay<TLambda>::type,
typename = typename etl::enable_if_t< etl::is_class<T>::value && !is_inplace_function<T>::value, void>,
typename TSignature = typename etl::function_traits<T>::function_type>
ETL_NODISCARD
etl::inplace_function<TSignature, sizeof(T), alignof(T)> make_inplace_function(TLambda&& lambda)
{
return etl::inplace_function_for<TSignature, T>(etl::forward<TLambda>(lambda));
}
//*************************************************************************
//*************************************************************************
template <typename TSignature, typename TType, typename T = typename etl::decay<TType>::type,
typename = typename etl::enable_if_t<!etl::is_class<T>::value, int>>
ETL_NODISCARD
inplace_function_for<TSignature, T> make_inplace_function(TType&& function)
{
// If T is a function type, use a function pointer for storage sizing.
using storage_t = typename etl::conditional<etl::is_function<T>::value, typename etl::add_pointer<T>::type, T>::type;
return inplace_function_for<TSignature, storage_t>(etl::forward<TType>(function));
}
#if ETL_USING_CPP17
//*************************************************************************
//*************************************************************************
template < auto Function, typename F = decltype(Function),
typename = etl::enable_if_t< etl::is_pointer<F>::value && etl::is_function<etl::remove_pointer_t<F>>::value>>
ETL_NODISCARD
auto make_inplace_function()
{
using function_type = typename etl::function_traits<F>::function_type;
return etl::inplace_function<function_type, 1, 1>::template create<Function>();
}
//*************************************************************************
//*************************************************************************
template < typename TObject, auto Method, TObject& Instance, typename T = decltype(Method),
typename = etl::enable_if_t<etl::is_member_function_pointer<T>::value>, typename = etl::enable_if_t<!etl::function_traits<T>::is_const>>
ETL_NODISCARD
auto make_inplace_function()
{
using function_type = typename etl::function_traits<decltype(Method)>::function_type;
return etl::inplace_function<function_type, 1, 1>::template create< TObject, Method, Instance>();
}
//*************************************************************************
//*************************************************************************
template < typename TObject, auto Method, const TObject& Instance, typename T = decltype(Method),
typename = etl::enable_if_t<etl::is_member_function_pointer<T>::value>, typename = etl::enable_if_t<etl::function_traits<T>::is_const>>
ETL_NODISCARD
auto make_inplace_function()
{
using function_type = typename etl::function_traits<T>::function_type;
return etl::inplace_function<function_type, 1, 1>::template create< TObject, Method, Instance>();
}
//*************************************************************************
//*************************************************************************
template < typename TObject, TObject& Instance, typename T = etl::decay_t<TObject>,
typename = etl::enable_if_t<etl::is_class<T>::value && etl::has_call_operator<T>::value>>
ETL_NODISCARD
auto make_inplace_function()
{
using function_type = typename etl::function_traits< decltype(&TObject::operator())>::function_type;
return etl::inplace_function<function_type, 1, 1>::template create<TObject, Instance>();
}
#endif
//*************************************************************************
//*************************************************************************
template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
void swap(etl::inplace_function<TSignature, Object_Size, Object_Alignment>& lhs,
etl::inplace_function<TSignature, Object_Size, Object_Alignment>& rhs) noexcept
{
lhs.swap(rhs);
}
//*************************************************************************
//*************************************************************************
template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
ETL_NODISCARD
bool operator==(const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& lhs, etl::nullptr_t)
{
return !lhs.is_valid();
}
//*************************************************************************
//*************************************************************************
template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
ETL_NODISCARD
bool operator==(etl::nullptr_t, const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& rhs)
{
return !rhs.is_valid();
}
//*************************************************************************
//*************************************************************************
template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
ETL_NODISCARD
bool operator!=(const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& lhs, etl::nullptr_t)
{
return lhs.is_valid();
}
//*************************************************************************
//*************************************************************************
template <typename TSignature, size_t Object_Size, size_t Object_Alignment>
ETL_NODISCARD
bool operator!=(etl::nullptr_t, const etl::inplace_function<TSignature, Object_Size, Object_Alignment>& rhs)
{
return rhs.is_valid();
}
} // namespace etl
#endif
#endif
swap
void swap(etl::array_view< T > &lhs, etl::array_view< T > &rhs) ETL_NOEXCEPT
Swaps the values.
Definition array_view.h:692
error_handler.h
exception.h
file_error_numbers.h
function_traits.h
ETL_ASSERT
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
invoke.h
etl
Definition absolute.h:40
etl::false_type
integral_constant< bool, false > false_type
integral_constant specialisations
Definition type_traits.h:80
etl::size
ETL_CONSTEXPR TContainer::size_type size(const TContainer &container)
Definition iterator.h:1434
optional.h
placement_new.h
platform.h
operator==
ETL_CONSTEXPR14 bool operator==(const etl::to_arithmetic_result< T > &lhs, const etl::to_arithmetic_result< T > &rhs)
Equality test for etl::to_arithmetic_result.
Definition to_arithmetic.h:903
operator!=
ETL_CONSTEXPR14 bool operator!=(const etl::to_arithmetic_result< T > &lhs, const etl::to_arithmetic_result< T > &rhs)
Inequality test for etl::to_arithmetic_result.
Definition to_arithmetic.h:937
type_traits.h
utility.h