Sen API
Sen Libraries
Loading...
Searching...
No Matches
move_only_function.h
Go to the documentation of this file.
1// === move_only_function.h ============================================================================================
2// Sen Infrastructure
3// Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
4// See the LICENSE.txt file for more information.
5// © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
6// =====================================================================================================================
7
8#ifndef SEN_CORE_BASE_MOVE_ONLY_FUNCTION_H
9#define SEN_CORE_BASE_MOVE_ONLY_FUNCTION_H
10
11// std
12#include <cstddef>
13#include <functional>
14#include <type_traits>
15#include <utility>
16
17#if defined(__cpp_lib_move_only_function)
18namespace sen::std_util
19{
20using std::move_only_function;
21} // namespace sen::std_util
22#else
24{
25
26// This function implements std::invoke_r as a forward port from C++23 following P2136R3
27template <class R, class F, class... Args, std::enable_if_t<std::is_invocable_r_v<R, F, Args...>, bool> = true>
28// NOLINTNEXTLINE(readability-identifier-naming): name defined by std
29constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>)
30{
31 if constexpr (std::is_void_v<R>)
32 {
33 std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
34 }
35 else
36 {
37 return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
38 }
39}
40
41template <typename T>
42struct IsInPlaceType: std::false_type
43{
44};
45template <typename T>
47{
48};
49
50// These classes implement std::move_only_function as a forward port from C++23 following P0288R9
51template <typename... Signature>
53
54// NOLINTNEXTLINE(hicpp-special-member-functions): not needed as this base class is hidden behind MoveOnlyFunctionImpl
56{
57protected:
59 template <typename CallableType, typename... ArgTypes>
60 static constexpr bool isNothrowInit() noexcept
61 {
62 if constexpr (isSmallSizeOptimized<CallableType>)
63 {
64 return std::is_nothrow_constructible_v<CallableType, ArgTypes...>;
65 }
66 return false;
67 }
68
70 template <typename CallableType, typename QualifiedThisType>
71 static CallableType* getCallableAs(QualifiedThisType* thisPtr) noexcept
72 {
73 if constexpr (isSmallSizeOptimized<std::remove_const_t<CallableType>>)
74 {
75 return static_cast<CallableType*>(thisPtr->buffer_.rawBufferAddress());
76 }
77 else
78 {
79 // * union access valid due to checked alignment requirements
80 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
81 return static_cast<CallableType*>(thisPtr->buffer_.ptrToStoredCallable);
82 }
83 }
84
85protected:
86 MoveOnlyFunctionBase() noexcept: resourceHandler_(emptyResourceHandler) {}
88 {
89 resourceHandler_ = std::exchange(other.resourceHandler_, emptyResourceHandler);
90 resourceHandler_(buffer_, &other.buffer_);
91 }
92
93 template <typename CallableType, typename... ArgTypes>
94 void initializeCallable(ArgTypes&&... args) noexcept(isNothrowInit<CallableType, ArgTypes...>())
95 {
96 if constexpr (isSmallSizeOptimized<CallableType>)
97 {
98 ::new (buffer_.rawBufferAddress()) CallableType(std::forward<ArgTypes>(args)...);
99 }
100 else
101 {
102 // * allocation handled by the resource handler function
103 // * union access valid due to checked alignment requirements
104 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-owning-memory)
105 buffer_.ptrToStoredCallable = new CallableType(std::forward<ArgTypes>(args)...);
106 }
107
108 resourceHandler_ = &callableResourceHandler<CallableType>;
109 }
110
112 {
113 resourceHandler_(buffer_, nullptr);
114 resourceHandler_ = std::exchange(other.resourceHandler_, emptyResourceHandler);
115 resourceHandler_(buffer_, &other.buffer_);
116 return *this;
117 }
118
119 MoveOnlyFunctionBase& operator=(std::nullptr_t) noexcept
120 {
121 resourceHandler_(buffer_, nullptr);
122 resourceHandler_ = emptyResourceHandler;
123 return *this;
124 }
125
126 ~MoveOnlyFunctionBase() { resourceHandler_(buffer_, nullptr); }
127
128 void swap(MoveOnlyFunctionBase& other) noexcept
129 {
130 SmallSizeBuffer storage;
131 other.resourceHandler_(storage, &other.buffer_);
132 resourceHandler_(other.buffer_, &buffer_);
133 other.resourceHandler_(buffer_, &storage);
134 std::swap(resourceHandler_, other.resourceHandler_);
135 }
136
137private:
138 struct SmallSizeBuffer
139 { // Allows us to store small callables inside of the function
140 static constexpr size_t bufferSize = 3 * sizeof(void*);
141 static constexpr size_t bufferAlignment = alignof(void*);
142
143 union // NOLINT(misc-non-private-member-variables-in-classes)
144 {
146 alignas(bufferAlignment) std::byte data[bufferSize];
147 };
148
149 [[nodiscard]] void* rawBufferAddress()
150 {
151 return &data[0]; // NOLINT(cppcoreguidelines-pro-type-union-access)
152 }
153 [[nodiscard]] const void* rawBufferAddress() const
154 {
155 return &data[0]; // NOLINT(cppcoreguidelines-pro-type-union-access)
156 }
157 };
158
159 template <typename CallableType>
160 static constexpr bool isSmallSizeOptimized =
161 alignof(CallableType) <= SmallSizeBuffer::bufferAlignment && // Check for correct alignment
162 sizeof(CallableType) <= SmallSizeBuffer::bufferSize && // Check for correct size
163 std::is_nothrow_move_constructible_v<CallableType>; // Does not throw on construction
164
165 static void emptyResourceHandler(SmallSizeBuffer& target, SmallSizeBuffer* source) noexcept
166 {
167 std::ignore = target;
168 std::ignore = source;
169 }
170
171 template <typename CallableType>
172 static void callableResourceHandler(SmallSizeBuffer& target, SmallSizeBuffer* source) noexcept
173 {
174 if constexpr (isSmallSizeOptimized<CallableType>)
175 {
176 if (source)
177 {
178 auto* sourceCallable = static_cast<CallableType*>(source->rawBufferAddress());
179 ::new (target.rawBufferAddress()) CallableType(std::move(*sourceCallable));
180 sourceCallable->~CallableType();
181 }
182 else
183 {
184 static_cast<CallableType*>(target.rawBufferAddress())->~CallableType();
185 }
186 }
187 else
188 {
189 if (source)
190 {
191 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
192 target.ptrToStoredCallable = source->ptrToStoredCallable;
193 }
194 else
195 {
196 // * allocation handled by the resource handler function
197 // * union access valid due to checked alignment requirements
198 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access,cppcoreguidelines-owning-memory)
199 delete static_cast<CallableType*>(target.ptrToStoredCallable);
200 }
201 }
202 }
203
204 SmallSizeBuffer buffer_;
205 using ResourceHandlerFunctionType = void (*)(SmallSizeBuffer& target, SmallSizeBuffer* src) noexcept;
206 ResourceHandlerFunctionType resourceHandler_;
207};
208
209template <typename FunctionType>
210inline constexpr bool is_move_only_function_v = false; // NOLINT(readability-identifier-naming)
211template <typename FunctionType>
212// NOLINTNEXTLINE(readability-identifier-naming)
214
215} // namespace sen::std_util::detail
216
217// default
218# define GENERATE_WITH_CV
219# define GENERATE_WITH_REF
220# define GENERATE_WITH_SELF_QUALIFIERS &
222
223// for const
224# define GENERATE_WITH_CV const
225# define GENERATE_WITH_REF
226# define GENERATE_WITH_SELF_QUALIFIERS GENERATE_WITH_CV&
228
229// for ref
230# define GENERATE_WITH_CV
231# define GENERATE_WITH_REF &
232# define GENERATE_WITH_SELF_QUALIFIERS GENERATE_WITH_CV GENERATE_WITH_REF
234
235// for rvalue ref
236# define GENERATE_WITH_CV
237# define GENERATE_WITH_REF &&
238# define GENERATE_WITH_SELF_QUALIFIERS GENERATE_WITH_CV GENERATE_WITH_REF
240
241// for const and ref
242# define GENERATE_WITH_CV const
243# define GENERATE_WITH_REF &
244# define GENERATE_WITH_SELF_QUALIFIERS GENERATE_WITH_CV GENERATE_WITH_REF
246
247// for const and rvalue ref
248# define GENERATE_WITH_CV const
249# define GENERATE_WITH_REF &&
250# define GENERATE_WITH_SELF_QUALIFIERS GENERATE_WITH_CV GENERATE_WITH_REF
252
253namespace sen::std_util
254{
255template <typename... FwdArgs>
256using move_only_function = detail::MoveOnlyFunctionImpl<FwdArgs...>; // NOLINT(readability-identifier-naming)
257} // namespace sen::std_util
258
259#endif
260
261#endif // SEN_CORE_BASE_MOVE_ONLY_FUNCTION_H
MoveOnlyFunctionBase() noexcept
Definition move_only_function.h:86
static constexpr bool isNothrowInit() noexcept
Returns true if the given CallableType can be initialized without throwing given the specified ArgTyp...
Definition move_only_function.h:60
void swap(MoveOnlyFunctionBase &other) noexcept
Definition move_only_function.h:128
static CallableType * getCallableAs(QualifiedThisType *thisPtr) noexcept
Casts the smallsize/allocated callable into the specified type.
Definition move_only_function.h:71
~MoveOnlyFunctionBase()
Definition move_only_function.h:126
void initializeCallable(ArgTypes &&... args) noexcept(isNothrowInit< CallableType, ArgTypes... >())
Definition move_only_function.h:94
MoveOnlyFunctionBase & operator=(MoveOnlyFunctionBase &&other) noexcept
Definition move_only_function.h:111
MoveOnlyFunctionBase & operator=(std::nullptr_t) noexcept
Definition move_only_function.h:119
MoveOnlyFunctionBase(MoveOnlyFunctionBase &&other) noexcept
Definition move_only_function.h:87
Definition move_only_function_impl.h:21
constexpr bool is_move_only_function_v
Definition move_only_function.h:210
constexpr R invoke_r(F &&f, Args &&... args) noexcept(std::is_nothrow_invocable_r_v< R, F, Args... >)
Definition move_only_function.h:29
Definition move_only_function.h:52
Definition bits.h:26
detail::MoveOnlyFunctionImpl< FwdArgs... > move_only_function
Definition move_only_function.h:256
STL namespace.
Definition move_only_function.h:43