tetl 0.1.0
Embedded Template Library
Loading...
Searching...
No Matches
inplace_function.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: BSL-1.0
2// SPDX-FileCopyrightText: Copyright (C) 2021 Tobias Hienzsch
3
4#ifndef TETL_FUNCTIONAL_INPLACE_FUNCTION_HPP
5#define TETL_FUNCTIONAL_INPLACE_FUNCTION_HPP
6
7#include <etl/_cstddef/nullptr_t.hpp>
8#include <etl/_cstddef/size_t.hpp>
9#include <etl/_exception/exception.hpp>
10#include <etl/_exception/raise.hpp>
11#include <etl/_memory/addressof.hpp>
12#include <etl/_new/operator.hpp>
13#include <etl/_type_traits/aligned_storage.hpp>
14#include <etl/_type_traits/bool_constant.hpp>
15#include <etl/_type_traits/is_copy_constructible.hpp>
16#include <etl/_type_traits/is_invocable_r.hpp>
17#include <etl/_utility/exchange.hpp>
18#include <etl/_utility/forward.hpp>
19#include <etl/_utility/swap.hpp>
20
21namespace etl {
22
24 constexpr bad_function_call() = default;
25
26 constexpr explicit bad_function_call(char const* what)
27 : exception{what}
28 {
29 }
30};
31
32namespace detail {
33
34template <typename T>
35struct wrapper {
36 using type = T;
37};
38
39template <typename R, typename... Args>
40struct inplace_func_vtable {
41 using storage_ptr_t = void*;
42
43 using invoke_ptr_t = R (*)(storage_ptr_t, Args&&...);
44 using process_ptr_t = void (*)(storage_ptr_t, storage_ptr_t);
45 using destructor_ptr_t = void (*)(storage_ptr_t);
46
47 invoke_ptr_t const invoke_ptr;
48 process_ptr_t const copy_ptr;
49 process_ptr_t const relocate_ptr;
50 destructor_ptr_t const destructor_ptr;
51
52 explicit constexpr inplace_func_vtable()
53 : invoke_ptr{[](storage_ptr_t /*p*/, Args&&... /*args*/) -> R {
54 etl::raise<etl::bad_function_call>("empty inplace_func_vtable");
55 }}
56 , copy_ptr{[](storage_ptr_t /*p*/, storage_ptr_t /*p*/) -> void {}}
57 , relocate_ptr{[](storage_ptr_t /*p*/, storage_ptr_t /*p*/) -> void {}}
58 , destructor_ptr{[](storage_ptr_t /*p*/) -> void {}}
59 {
60 }
61
62 template <typename C>
63 explicit constexpr inplace_func_vtable(wrapper<C> /*ignore*/)
64 : invoke_ptr{[](storage_ptr_t storagePtr, Args&&... args) -> R {
65 return (*static_cast<C*>(storagePtr))(static_cast<Args&&>(args)...);
66 }}
67 , copy_ptr{[](storage_ptr_t dstPtr, storage_ptr_t srcPtr) -> void {
68 ::new (dstPtr) C{(*static_cast<C*>(srcPtr))};
69 }}
70 , relocate_ptr{[](storage_ptr_t dstPtr, storage_ptr_t srcPtr) -> void {
71 ::new (dstPtr) C{etl::move(*static_cast<C*>(srcPtr))};
72 static_cast<C*>(srcPtr)->~C();
73 }}
74 , destructor_ptr{[](storage_ptr_t srcPtr) -> void { static_cast<C*>(srcPtr)->~C(); }}
75 {
76 }
77
78 inplace_func_vtable(inplace_func_vtable const&) = delete;
79 inplace_func_vtable(inplace_func_vtable&&) = delete;
80
81 auto operator=(inplace_func_vtable const&) -> inplace_func_vtable& = delete;
82 auto operator=(inplace_func_vtable&&) -> inplace_func_vtable& = delete;
83
84 ~inplace_func_vtable() = default;
85};
86
87template <typename R, typename... Args>
88inline constexpr auto empty_vtable = inplace_func_vtable<R, Args...>{};
89
90template <size_t DstCap, size_t DstAlign, size_t SrcCap, size_t SrcAlign>
91struct is_valid_inplace_destination : etl::true_type {
92 static_assert(DstCap >= SrcCap);
93 static_assert(DstAlign % SrcAlign == 0);
94};
95
96} // namespace detail
97
98template <typename Signature, size_t Capacity = sizeof(void*), size_t Alignment = alignof(aligned_storage_t<Capacity>)>
100
101namespace detail {
102template <typename>
103struct is_inplace_function : false_type { };
104
105template <typename Sig, size_t Cap, size_t Align>
106struct is_inplace_function<inplace_function<Sig, Cap, Align>> : etl::true_type { };
107} // namespace detail
108
109template <typename R, typename... Args, size_t Capacity, size_t Alignment>
110struct inplace_function<R(Args...), Capacity, Alignment> {
111private:
112 using storage_t = aligned_storage_t<Capacity, Alignment>;
113 using vtable_t = detail::inplace_func_vtable<R, Args...>;
114 using vtable_ptr_t = vtable_t const*;
115
116 template <typename, size_t, size_t>
117 friend struct inplace_function;
118
119public:
120 using capacity = integral_constant<size_t, Capacity>;
121 using alignment = integral_constant<size_t, Alignment>;
122
123 /// \brief Creates an empty function.
125 : _vtable{etl::addressof(detail::empty_vtable<R, Args...>)}
126 {
127 }
128
129 template <typename T, typename C = decay_t<T>>
130 requires(!detail::is_inplace_function<C>::value && is_invocable_r_v<R, C&, Args...>)
131 inplace_function(T&& closure)
132 {
133 static_assert(is_copy_constructible_v<C>, "inplace_function cannot be constructed from non-copyable type");
134 static_assert(
135 sizeof(C) <= Capacity,
136 "inplace_function cannot be constructed from object with this (large) size"
137 );
138 static_assert(
139 Alignment % alignof(C) == 0,
140 "inplace_function cannot be constructed from object with this (large) alignment"
141 );
142
143 static constexpr vtable_t const vt{detail::wrapper<C>{}};
144 _vtable = etl::addressof(vt);
145
146 ::new (etl::addressof(_storage)) C{etl::forward<T>(closure)};
147 }
148
149 template <size_t Cap, size_t Align>
150 inplace_function(inplace_function<R(Args...), Cap, Align> const& other)
151 : inplace_function{other._vtable, other._vtable->copy_ptr, etl::addressof(other._storage)}
152 {
153 static_assert(
154 detail::is_valid_inplace_destination<Capacity, Alignment, Cap, Align>::value,
155 "conversion not allowed"
156 );
157 }
158
159 template <size_t Cap, size_t Align>
160 inplace_function(inplace_function<R(Args...), Cap, Align>&& other) noexcept
161 : inplace_function{other._vtable, other._vtable->relocate_ptr, etl::addressof(other._storage)}
162 {
163 static_assert(
164 detail::is_valid_inplace_destination<Capacity, Alignment, Cap, Align>::value,
165 "conversion not allowed"
166 );
167 other._vtable = etl::addressof(detail::empty_vtable<R, Args...>);
168 }
169
170 /// \brief Creates an empty function.
171 inplace_function(nullptr_t /*ignore*/) noexcept
172 : _vtable{etl::addressof(detail::empty_vtable<R, Args...>)}
173 {
174 }
175
177 : _vtable{other._vtable}
178 {
179 _vtable->copy_ptr(etl::addressof(_storage), etl::addressof(other._storage));
180 }
181
183 : _vtable{exchange(other._vtable, etl::addressof(detail::empty_vtable<R, Args...>))}
184 {
185 _vtable->relocate_ptr(etl::addressof(_storage), etl::addressof(other._storage));
186 }
187
188 /// \brief Assigns a new target to etl::inplace_function. Drops the current target. *this is empty
189 /// after the call.
190 auto operator=(nullptr_t) noexcept -> inplace_function&
191 {
192 _vtable->destructor_ptr(etl::addressof(_storage));
193 _vtable = etl::addressof(detail::empty_vtable<R, Args...>);
194 return *this;
195 }
196
198 {
199 _vtable->destructor_ptr(etl::addressof(_storage));
200 _vtable = exchange(other._vtable, etl::addressof(detail::empty_vtable<R, Args...>));
201 _vtable->relocate_ptr(etl::addressof(_storage), etl::addressof(other._storage));
202 return *this;
203 }
204
205 /// \brief Destroys the etl::inplace_function instance.
206 /// If the etl::inplace_function is not empty, its target is destroyed also.
208 {
209 _vtable->destructor_ptr(etl::addressof(_storage));
210 }
211
212 /// \brief Invokes the stored callable function target with the parameters args.
213 auto operator()(Args... args) const -> R
214 {
215 return _vtable->invoke_ptr(etl::addressof(_storage), etl::forward<Args>(args)...);
216 }
217
218 /// \brief Checks whether *this stores a callable function target, i.e. is not empty.
219 [[nodiscard]] explicit constexpr operator bool() const noexcept
220 {
221 return _vtable != etl::addressof(detail::empty_vtable<R, Args...>);
222 }
223
224 /// \brief Exchanges the stored callable objects of *this and other.
225 auto swap(inplace_function& other) noexcept -> void
226 {
227 auto tmp = storage_t{};
228 _vtable->relocate_ptr(etl::addressof(tmp), etl::addressof(_storage));
229 other._vtable->relocate_ptr(etl::addressof(_storage), etl::addressof(other._storage));
230 _vtable->relocate_ptr(etl::addressof(other._storage), etl::addressof(tmp));
231 etl::swap(_vtable, other._vtable);
232 }
233
234private:
235 inplace_function(
236 vtable_ptr_t vtable,
237 typename vtable_t::process_ptr_t process,
238 typename vtable_t::storage_ptr_t storage
239 )
240 : _vtable{vtable}
241 {
242 process(etl::addressof(_storage), storage);
243 }
244
245 vtable_ptr_t _vtable;
246 storage_t mutable _storage;
247};
248
249/// \brief Overloads the etl::swap algorithm for etl::inplace_function.
250/// Exchanges the state of lhs with that of rhs. Effectively calls
251/// lhs.swap(rhs).
252template <typename R, typename... Args, size_t Capacity, size_t Alignment>
253auto swap(
254 inplace_function<R(Args...), Capacity, Alignment>& lhs,
255 inplace_function<R(Args...), Capacity, Alignment>& rhs
256) noexcept -> void
257{
258 lhs.swap(rhs);
259}
260
261/// \brief Compares a etl::inplace_function with a null pointer. Empty functions
262/// (that is, functions without a callable target) compare equal, non-empty
263/// functions compare non-equal.
264template <typename R, typename... Args, size_t Capacity, size_t Alignment>
265[[nodiscard]] constexpr auto
266operator==(inplace_function<R(Args...), Capacity, Alignment> const& f, nullptr_t /*ignore*/) noexcept -> bool
267{
268 return !static_cast<bool>(f);
269}
270
271/// \brief Compares a etl::inplace_function with a null pointer. Empty functions
272/// (that is, functions without a callable target) compare equal, non-empty
273/// functions compare non-equal.
274template <typename R, typename... Args, size_t Capacity, size_t Alignment>
275[[nodiscard]] constexpr auto
276operator!=(inplace_function<R(Args...), Capacity, Alignment> const& f, nullptr_t /*ignore*/) noexcept -> bool
277{
278 return static_cast<bool>(f);
279}
280
281/// \brief Compares a etl::inplace_function with a null pointer. Empty functions
282/// (that is, functions without a callable target) compare equal, non-empty
283/// functions compare non-equal.
284template <typename R, typename... Args, size_t Capacity, size_t Alignment>
285[[nodiscard]] constexpr auto
286operator==(nullptr_t /*ignore*/, inplace_function<R(Args...), Capacity, Alignment> const& f) noexcept -> bool
287{
288 return !static_cast<bool>(f);
289}
290
291/// \brief Compares a etl::inplace_function with a null pointer. Empty functions
292/// (that is, functions without a callable target) compare equal, non-empty
293/// functions compare non-equal.
294template <typename R, typename... Args, size_t Capacity, size_t Alignment>
295[[nodiscard]] constexpr auto
296operator!=(nullptr_t /*ignore*/, inplace_function<R(Args...), Capacity, Alignment> const& f) noexcept -> bool
297{
298 return static_cast<bool>(f);
299}
300
301} // namespace etl
302
303#endif // TETL_FUNCTIONAL_INPLACE_FUNCTION_HPP
Definition adjacent_find.hpp:9
constexpr auto operator==(inplace_function< R(Args...), Capacity, Alignment > const &f, nullptr_t) noexcept -> bool
Compares a etl::inplace_function with a null pointer. Empty functions (that is, functions without a c...
Definition inplace_function.hpp:266
constexpr auto operator==(nullptr_t, inplace_function< R(Args...), Capacity, Alignment > const &f) noexcept -> bool
Compares a etl::inplace_function with a null pointer. Empty functions (that is, functions without a c...
Definition inplace_function.hpp:286
constexpr auto operator!=(inplace_function< R(Args...), Capacity, Alignment > const &f, nullptr_t) noexcept -> bool
Compares a etl::inplace_function with a null pointer. Empty functions (that is, functions without a c...
Definition inplace_function.hpp:276
TETL_NO_INLINE TETL_COLD auto raise(char const *msg, etl::source_location const loc=etl::source_location::current()) -> void
Definition raise.hpp:17
auto swap(inplace_function< R(Args...), Capacity, Alignment > &lhs, inplace_function< R(Args...), Capacity, Alignment > &rhs) noexcept -> void
Overloads the etl::swap algorithm for etl::inplace_function. Exchanges the state of lhs with that of ...
Definition inplace_function.hpp:253
constexpr auto operator!=(nullptr_t, inplace_function< R(Args...), Capacity, Alignment > const &f) noexcept -> bool
Compares a etl::inplace_function with a null pointer. Empty functions (that is, functions without a c...
Definition inplace_function.hpp:296
Definition inplace_function.hpp:23
constexpr bad_function_call(char const *what)
Definition inplace_function.hpp:26
constexpr bad_function_call()=default
Definition exception.hpp:9
constexpr exception(char const *what)
Definition exception.hpp:12
auto operator()(Args... args) const -> R
Invokes the stored callable function target with the parameters args.
Definition inplace_function.hpp:213
inplace_function(inplace_function< R(Args...), Cap, Align > const &other)
Definition inplace_function.hpp:150
inplace_function(inplace_function &&other) noexcept
Definition inplace_function.hpp:182
inplace_function(inplace_function const &other)
Definition inplace_function.hpp:176
inplace_function(inplace_function< R(Args...), Cap, Align > &&other) noexcept
Definition inplace_function.hpp:160
inplace_function() noexcept
Creates an empty function.
Definition inplace_function.hpp:124
auto operator=(inplace_function other) noexcept -> inplace_function &
Definition inplace_function.hpp:197
constexpr operator bool() const noexcept
Checks whether *this stores a callable function target, i.e. is not empty.
Definition inplace_function.hpp:219
inplace_function(nullptr_t) noexcept
Creates an empty function.
Definition inplace_function.hpp:171
~inplace_function()
Destroys the etl::inplace_function instance. If the etl::inplace_function is not empty,...
Definition inplace_function.hpp:207
inplace_function(T &&closure)
Definition inplace_function.hpp:131
auto swap(inplace_function &other) noexcept -> void
Exchanges the stored callable objects of *this and other.
Definition inplace_function.hpp:225
friend struct inplace_function
Definition inplace_function.hpp:117
auto operator=(nullptr_t) noexcept -> inplace_function &
Assigns a new target to etl::inplace_function. Drops the current target. *this is empty after the cal...
Definition inplace_function.hpp:190
Definition integral_constant.hpp:10