Skip to content

Commit a8c9a54

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 2db6667 commit a8c9a54

File tree

5 files changed

+972
-0
lines changed

5 files changed

+972
-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: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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+
36+
#include "../error_handler.h"
37+
#include "../exception.h"
38+
#include "../type_traits.h"
39+
#include "../utility.h"
40+
41+
#ifndef ETL_INPLACE_FUNCTION_DEFAULT_CAPACITY
42+
#define ETL_INPLACE_FUNCTION_DEFAULT_CAPACITY (8 * sizeof(void*))
43+
#endif
44+
45+
namespace etl
46+
{
47+
template <
48+
typename Signature,
49+
size_t Capacity = ETL_INPLACE_FUNCTION_DEFAULT_CAPACITY>
50+
class inplace_function;
51+
52+
namespace private_inplace_function
53+
{
54+
template <typename>
55+
struct is_inplace_function : etl::false_type
56+
{
57+
};
58+
template <typename Sig, size_t Cap>
59+
struct is_inplace_function<inplace_function<Sig, Cap>> : etl::true_type
60+
{
61+
};
62+
} // namespace private_inplace_function
63+
64+
class inplace_function_exception : public etl::exception
65+
{
66+
public:
67+
inplace_function_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
68+
: exception(reason_, file_name_, line_number_)
69+
{
70+
}
71+
};
72+
73+
class bad_inplace_function_call : public inplace_function_exception
74+
{
75+
public:
76+
bad_inplace_function_call(string_type file_name_, numeric_type line_number_)
77+
: inplace_function_exception(ETL_ERROR_TEXT("inplace_function:call", ETL_INPLACE_FUNCTION_FILE_ID "A"), file_name_, line_number_)
78+
{
79+
}
80+
};
81+
82+
template <
83+
typename R,
84+
typename... Args,
85+
size_t Capacity>
86+
class inplace_function<R(Args...), Capacity> ETL_FINAL
87+
{
88+
public:
89+
using capacity = etl::integral_constant<size_t, Capacity>;
90+
91+
inplace_function() = default;
92+
93+
template <
94+
typename T,
95+
typename C = etl::decay_t<T>,
96+
typename = etl::enable_if_t<
97+
!private_inplace_function::is_inplace_function<C>::value && etl::is_invocable_r<R, C&, Args...>::value>>
98+
inplace_function(T&& closure)
99+
{
100+
ETL_STATIC_ASSERT(etl::is_copy_constructible<C>::value,
101+
"cannot be constructed from non-copyable types");
102+
ETL_STATIC_ASSERT(sizeof(C) <= Capacity,
103+
"internal storage too small");
104+
105+
static const vtable_t vt{etl::type_identity<C>{}};
106+
107+
vtable_ptr = &vt;
108+
::new (obj) C{etl::forward<T>(closure)};
109+
}
110+
111+
template <size_t Cap>
112+
inplace_function(const inplace_function<R(Args...), Cap>& rhs)
113+
: inplace_function(rhs.vtable_ptr, rhs.vtable_ptr->copy_func, rhs.obj)
114+
{
115+
ETL_STATIC_ASSERT(Cap <= Capacity,
116+
"internal storage too small");
117+
}
118+
119+
template <size_t Cap>
120+
inplace_function(inplace_function<R(Args...), Cap>&& rhs) ETL_NOEXCEPT
121+
: inplace_function(rhs.vtable_ptr, rhs.vtable_ptr->move_func, rhs.obj)
122+
{
123+
ETL_STATIC_ASSERT(Cap <= Capacity,
124+
"internal storage too small");
125+
rhs.vtable_ptr = &default_vtable;
126+
}
127+
128+
inplace_function(const inplace_function& rhs)
129+
: vtable_ptr{rhs.vtable_ptr}
130+
{
131+
vtable_ptr->copy_func(
132+
obj,
133+
rhs.obj);
134+
}
135+
136+
inplace_function(inplace_function&& rhs) ETL_NOEXCEPT
137+
: vtable_ptr{rhs.vtable_ptr}
138+
{
139+
rhs.vtable_ptr = &default_vtable;
140+
vtable_ptr->move_func(
141+
obj,
142+
rhs.obj);
143+
}
144+
145+
inplace_function& operator=(etl::nullptr_t) ETL_NOEXCEPT
146+
{
147+
vtable_ptr->dtor_func(&obj);
148+
vtable_ptr = ETL_NULLPTR;
149+
return *this;
150+
}
151+
152+
inplace_function& operator=(inplace_function rhs) ETL_NOEXCEPT
153+
{
154+
vtable_ptr->dtor_func(obj);
155+
vtable_ptr = rhs.vtable_ptr;
156+
rhs.vtable_ptr = &default_vtable;
157+
vtable_ptr->move_func(
158+
obj,
159+
rhs.obj);
160+
return *this;
161+
}
162+
163+
~inplace_function()
164+
{
165+
vtable_ptr->dtor_func(obj);
166+
}
167+
168+
R operator()(Args... args) const
169+
{
170+
ETL_ASSERT(vtable_ptr->invoke_func, ETL_ERROR(bad_inplace_function_call));
171+
return vtable_ptr->invoke_func(
172+
obj,
173+
etl::forward<Args>(args)...);
174+
}
175+
176+
explicit ETL_CONSTEXPR operator bool() const ETL_NOEXCEPT
177+
{
178+
return vtable_ptr != &default_vtable;
179+
}
180+
181+
private:
182+
template <typename, size_t>
183+
friend class inplace_function;
184+
185+
struct vtable_t ETL_FINAL
186+
{
187+
using invoke_func_t = R (*)(char*, Args&&...);
188+
using copy_or_move_func_t = void (*)(char*, char*);
189+
using dtor_func_t = void (*)(char*);
190+
191+
const invoke_func_t invoke_func{ETL_NULLPTR};
192+
const copy_or_move_func_t copy_func{[](char*, char*) -> void {}};
193+
const copy_or_move_func_t move_func{[](char*, char*) -> void {}};
194+
const dtor_func_t dtor_func{[](char*) -> void {}};
195+
196+
~vtable_t() = default;
197+
vtable_t() = default;
198+
199+
template <typename T>
200+
explicit ETL_CONSTEXPR vtable_t(etl::type_identity<T>) ETL_NOEXCEPT
201+
: invoke_func{[](char* pobj, Args&&... args) -> R
202+
{ return (*reinterpret_cast<T*>(pobj))(
203+
etl::forward<Args&&>(args)...); }},
204+
copy_func{[](char* dst_obj, char* src_obj) -> void
205+
{ ::new (dst_obj) T{*reinterpret_cast<T*>(src_obj)}; }},
206+
move_func{[](char* dst_obj, char* src_obj) -> void
207+
{
208+
::new (dst_obj) T{etl::move(*reinterpret_cast<T*>(src_obj))};
209+
reinterpret_cast<T*>(src_obj)->~T();
210+
}},
211+
dtor_func{[](char* pobj) -> void
212+
{ reinterpret_cast<T*>(pobj)->~T(); }}
213+
{
214+
}
215+
216+
private:
217+
vtable_t(const vtable_t&) ETL_DELETE;
218+
vtable_t(vtable_t&&) ETL_NOEXCEPT ETL_DELETE;
219+
220+
vtable_t& operator=(const vtable_t&) ETL_DELETE;
221+
vtable_t& operator=(vtable_t&&) ETL_DELETE;
222+
};
223+
224+
static const vtable_t default_vtable;
225+
226+
const vtable_t* vtable_ptr{&default_vtable};
227+
mutable char obj[Capacity];
228+
229+
explicit inplace_function(
230+
const vtable_t* vtable,
231+
typename vtable_t::copy_or_move_func_t copy_or_move_func,
232+
char* obj_ptr)
233+
: vtable_ptr{vtable}
234+
{
235+
copy_or_move_func(obj, obj_ptr);
236+
}
237+
};
238+
239+
template <
240+
typename R,
241+
typename... Args,
242+
size_t Capacity>
243+
const typename inplace_function<R(Args...), Capacity>::vtable_t
244+
inplace_function<R(Args...), Capacity>::default_vtable{};
245+
} // namespace etl
246+
247+
#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)