Skip to content

Commit 41517ae

Browse files
committed
Support etl::owning_function
Provide similar api to std::function. Store a callable inside the 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.
1 parent 78dc812 commit 41517ae

File tree

5 files changed

+978
-0
lines changed

5 files changed

+978
-0
lines changed

include/etl/file_error_numbers.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,5 @@ SOFTWARE.
107107
#define ETL_UNALIGNED_TYPE_FILE_ID "74"
108108
#define ETL_SPAN_FILE_ID "75"
109109
#define ETL_ALGORITHM_FILE_ID "76"
110+
#define ETL_OWNING_CALLABLE_FILE_ID "77"
110111
#endif

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

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ add_executable(etl_tests
220220
test_observer.cpp
221221
test_optional.cpp
222222
test_overload.cpp
223+
test_owning_callable.cpp
223224
test_packet.cpp
224225
test_parameter_pack.cpp
225226
test_parameter_type.cpp

0 commit comments

Comments
 (0)