Skip to content

Commit 2a9fb5d

Browse files
committed
Support etl::inplace_function
Provide similar api to std::function. Store a callable inside the inplace_function object's member field in a type-erasure way. The member field, i.e. storage or buffer, has a compile-time fixed size. The size is specify either by the macro ETL_INPLACE_FUNCTION_DEFAULT_CAPACITY or a non-type template parameter. The implementation is inspired by: 1. SG14 inplace_function 2. folly::Function 3. function2
1 parent d9f3d09 commit 2a9fb5d

File tree

5 files changed

+939
-0
lines changed

5 files changed

+939
-0
lines changed

include/etl/file_error_numbers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,5 @@ SOFTWARE.
105105
#define ETL_BASE64_FILE_ID "72"
106106
#define ETL_SINGLETON_BASE_FILE_ID "73"
107107
#define ETL_UNALIGNED_TYPE_FILE_ID "74"
108+
#define ETL_INPLACE_FUNCTION_FILE_ID "75"
108109
#endif

include/etl/inplace_function.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
///\file
2+
3+
/******************************************************************************
4+
The MIT License(MIT)
5+
6+
Embedded Template Library.
7+
https://github.com/ETLCPP/etl
8+
https://www.etlcpp.com
9+
10+
Copyright(c) 2025 BMW AG
11+
12+
Permission is hereby granted, free of charge, to any person obtaining a copy
13+
of this software and associated documentation files(the "Software"), to deal
14+
in the Software without restriction, including without limitation the rights
15+
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16+
copies of the Software, and to permit persons to whom the Software is
17+
furnished to do so, subject to the following conditions :
18+
19+
The above copyright notice and this permission notice shall be included in all
20+
copies or substantial portions of the Software.
21+
22+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28+
SOFTWARE.
29+
******************************************************************************/
30+
31+
#ifndef ETL_INPLACE_FUNCTION_INCLUDED
32+
#define ETL_INPLACE_FUNCTION_INCLUDED
33+
34+
#include "platform.h"
35+
36+
#if ETL_USING_CPP11
37+
#include "private/inplace_function_cpp11.h"
38+
#endif
39+
40+
#endif
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
///\file
2+
3+
/******************************************************************************
4+
The MIT License(MIT)
5+
6+
Embedded Template Library.
7+
https://github.com/ETLCPP/etl
8+
https://www.etlcpp.com
9+
10+
Copyright(c) 2025 BMW AG
11+
12+
Permission is hereby granted, free of charge, to any person obtaining a copy
13+
of this software and associated documentation files(the "Software"), to deal
14+
in the Software without restriction, including without limitation the rights
15+
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
16+
copies of the Software, and to permit persons to whom the Software is
17+
furnished to do so, subject to the following conditions :
18+
19+
The above copyright notice and this permission notice shall be included in all
20+
copies or substantial portions of the Software.
21+
22+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
25+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR rhs
26+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR rhsWISE, ARISING FROM,
27+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR rhs DEALINGS IN THE
28+
SOFTWARE.
29+
******************************************************************************/
30+
31+
#ifndef ETL_INPLACE_FUNCTION_CPP11_INCLUDED
32+
#define ETL_INPLACE_FUNCTION_CPP11_INCLUDED
33+
34+
#include "../platform.h"
35+
#include "../error_handler.h"
36+
#include "../exception.h"
37+
#include "../type_traits.h"
38+
#include "../utility.h"
39+
40+
#ifndef ETL_INPLACE_FUNCTION_DEFAULT_CAPACITY
41+
#define ETL_INPLACE_FUNCTION_DEFAULT_CAPACITY 32
42+
#endif
43+
namespace etl
44+
{
45+
template <
46+
typename Signature,
47+
size_t Capacity = ETL_INPLACE_FUNCTION_DEFAULT_CAPACITY>
48+
class inplace_function;
49+
50+
namespace private_inplace_function
51+
{
52+
template <typename>
53+
struct is_inplace_function : etl::false_type
54+
{
55+
};
56+
template <typename Sig, size_t Cap>
57+
struct is_inplace_function<inplace_function<Sig, Cap>> : etl::true_type
58+
{
59+
};
60+
} // namespace private_inplace_function
61+
62+
class inplace_function_exception : public etl::exception
63+
{
64+
public:
65+
inplace_function_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
66+
: exception(reason_, file_name_, line_number_)
67+
{
68+
}
69+
};
70+
71+
class bad_inplace_function_call : public inplace_function_exception
72+
{
73+
public:
74+
bad_inplace_function_call(string_type file_name_, numeric_type line_number_)
75+
: inplace_function_exception(ETL_ERROR_TEXT("inplace_function:call", ETL_INPLACE_FUNCTION_FILE_ID "A"), file_name_, line_number_)
76+
{
77+
}
78+
};
79+
80+
template <
81+
typename R,
82+
typename... Args,
83+
size_t Capacity>
84+
class inplace_function<R(Args...), Capacity> ETL_FINAL
85+
{
86+
public:
87+
using capacity = etl::integral_constant<size_t, Capacity>;
88+
89+
inplace_function() = default;
90+
91+
template <
92+
typename T,
93+
typename C = etl::decay_t<T>,
94+
typename = etl::enable_if_t<
95+
!private_inplace_function::is_inplace_function<C>::value && etl::is_invocable_r<R, C&, Args...>::value>>
96+
inplace_function(T&& closure)
97+
{
98+
ETL_STATIC_ASSERT(etl::is_copy_constructible<C>::value,
99+
"cannot be constructed from non-copyable types");
100+
ETL_STATIC_ASSERT(sizeof(C) <= Capacity,
101+
"internal storage too small");
102+
103+
static const vtable_t vt{etl::type_identity<C>{}};
104+
105+
vtable_ptr = &vt;
106+
::new (obj) C{etl::forward<T>(closure)};
107+
}
108+
109+
template <size_t Cap>
110+
inplace_function(const inplace_function<R(Args...), Cap>& rhs)
111+
: inplace_function(rhs.vtable_ptr, rhs.vtable_ptr->copy_func, rhs.obj)
112+
{
113+
ETL_STATIC_ASSERT(Cap <= Capacity,
114+
"internal storage too small");
115+
}
116+
117+
template <size_t Cap>
118+
inplace_function(inplace_function<R(Args...), Cap>&& rhs) ETL_NOEXCEPT
119+
: inplace_function(rhs.vtable_ptr, rhs.vtable_ptr->move_func, rhs.obj)
120+
{
121+
ETL_STATIC_ASSERT(Cap <= Capacity,
122+
"internal storage too small");
123+
rhs.vtable_ptr = &default_vtable;
124+
}
125+
126+
inplace_function(const inplace_function& rhs)
127+
: vtable_ptr{rhs.vtable_ptr}
128+
{
129+
vtable_ptr->copy_func(
130+
obj,
131+
rhs.obj);
132+
}
133+
134+
inplace_function(inplace_function&& rhs) ETL_NOEXCEPT
135+
: vtable_ptr{rhs.vtable_ptr}
136+
{
137+
rhs.vtable_ptr = &default_vtable;
138+
vtable_ptr->move_func(
139+
obj,
140+
rhs.obj);
141+
}
142+
143+
inplace_function& operator=(etl::nullptr_t) ETL_NOEXCEPT
144+
{
145+
vtable_ptr->dtor_func(&obj);
146+
vtable_ptr = ETL_NULLPTR;
147+
return *this;
148+
}
149+
150+
inplace_function& operator=(inplace_function rhs) ETL_NOEXCEPT
151+
{
152+
vtable_ptr->dtor_func(obj);
153+
vtable_ptr = rhs.vtable_ptr;
154+
rhs.vtable_ptr = &default_vtable;
155+
vtable_ptr->move_func(
156+
obj,
157+
rhs.obj);
158+
return *this;
159+
}
160+
161+
~inplace_function()
162+
{
163+
vtable_ptr->dtor_func(obj);
164+
}
165+
166+
R operator()(Args... args) const
167+
{
168+
ETL_ASSERT(vtable_ptr->invoke_func, ETL_ERROR(bad_inplace_function_call));
169+
return vtable_ptr->invoke_func(
170+
obj,
171+
etl::forward<Args>(args)...);
172+
}
173+
174+
explicit ETL_CONSTEXPR operator bool() const ETL_NOEXCEPT
175+
{
176+
return vtable_ptr != &default_vtable;
177+
}
178+
179+
private:
180+
template <typename, size_t>
181+
friend class inplace_function;
182+
183+
struct vtable_t ETL_FINAL
184+
{
185+
using invoke_func_t = R (*)(char*, Args&&...);
186+
using copy_or_move_func_t = void (*)(char*, char*);
187+
using dtor_func_t = void (*)(char*);
188+
189+
const invoke_func_t invoke_func{ETL_NULLPTR};
190+
const copy_or_move_func_t copy_func{[](char*, char*) -> void {}};
191+
const copy_or_move_func_t move_func{[](char*, char*) -> void {}};
192+
const dtor_func_t dtor_func{[](char*) -> void {}};
193+
194+
~vtable_t() = default;
195+
vtable_t() = default;
196+
197+
template <typename T>
198+
explicit ETL_CONSTEXPR vtable_t(etl::type_identity<T>) ETL_NOEXCEPT
199+
: invoke_func{[](char* pobj, Args&&... args) -> R
200+
{ return (*reinterpret_cast<T*>(pobj))(
201+
etl::forward<Args&&>(args)...); }},
202+
copy_func{[](char* dst_obj, char* src_obj) -> void
203+
{ ::new (dst_obj) T{*reinterpret_cast<T*>(src_obj)}; }},
204+
move_func{[](char* dst_obj, char* src_obj) -> void
205+
{
206+
::new (dst_obj) T{etl::move(*reinterpret_cast<T*>(src_obj))};
207+
reinterpret_cast<T*>(src_obj)->~T();
208+
}},
209+
dtor_func{[](char* pobj) -> void
210+
{ reinterpret_cast<T*>(pobj)->~T(); }}
211+
{
212+
}
213+
214+
private:
215+
vtable_t(const vtable_t&) ETL_DELETE;
216+
vtable_t(vtable_t&&) ETL_NOEXCEPT ETL_DELETE;
217+
218+
vtable_t& operator=(const vtable_t&) ETL_DELETE;
219+
vtable_t& operator=(vtable_t&&) ETL_DELETE;
220+
};
221+
222+
static const vtable_t default_vtable;
223+
224+
const vtable_t* vtable_ptr{&default_vtable};
225+
mutable char obj[Capacity];
226+
227+
explicit inplace_function(
228+
const vtable_t* vtable,
229+
typename vtable_t::copy_or_move_func_t copy_or_move_func,
230+
char* obj_ptr)
231+
: vtable_ptr{vtable}
232+
{
233+
copy_or_move_func(obj, obj_ptr);
234+
}
235+
};
236+
237+
template <
238+
typename R,
239+
typename... Args,
240+
size_t Capacity>
241+
const typename inplace_function<R(Args...), Capacity>::vtable_t
242+
inplace_function<R(Args...), Capacity>::default_vtable{};
243+
} // namespace etl
244+
245+
#endif

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ add_executable(etl_tests
151151
test_histogram.cpp
152152
test_indirect_vector.cpp
153153
test_indirect_vector_external_buffer.cpp
154+
test_inplace_function.cpp
154155
test_instance_count.cpp
155156
test_integral_limits.cpp
156157
test_intrusive_forward_list.cpp

0 commit comments

Comments
 (0)