iceoryx2
C++ Language Bindings
Loading...
Searching...
No Matches
static_function.hpp
Go to the documentation of this file.
1// Copyright (c) 2020 - 2023 by Apex.AI Inc. All rights reserved.
2// Copyright (c) 2025 Contributors to the Eclipse Foundation
3//
4// See the NOTICE file(s) distributed with this work for additional
5// information regarding copyright ownership.
6//
7// This program and the accompanying materials are made available under the
8// terms of the Apache Software License 2.0 which is available at
9// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
10// which is available at https://opensource.org/licenses/MIT.
11//
12// SPDX-License-Identifier: Apache-2.0 OR MIT
13
14#ifndef IOX2_BB_STATIC_FUNCTION_HPP
15#define IOX2_BB_STATIC_FUNCTION_HPP
16
20
21#include <utility>
22
23namespace iox2 {
24namespace bb {
25namespace detail {
26template <typename ReturnType, typename... Args>
27using Signature = ReturnType(Args...);
28
29template <uint64_t Capacity, typename T>
31
33
38
42template <uint64_t Capacity, typename ReturnType, typename... Args>
43class StaticFunction<Capacity, Signature<ReturnType, Args...>> final {
44 private:
45 struct Operations;
46 Operations m_operations; // operations depending on type-erased callable (copy, move, destroy)
47
48 legacy::UninitializedArray<char, Capacity> m_storage; // storage for the callable
49 void* m_callable { nullptr }; // pointer to stored type-erased callable
50 ReturnType (*m_invoker)(void*, Args&&...) { nullptr }; // indirection to invoke the stored callable,
51 // nullptr if no callable is stored
52
53 public:
54 using SignatureT = Signature<ReturnType, Args...>;
55
57 template <typename Functor,
58 typename = std::enable_if_t<std::is_class<Functor>::value
59 && legacy::is_invocable_r<ReturnType, Functor, Args...>::value,
60 void>>
61 // AXIVION Next Construct AutosarC++19_03-A12.1.4: implicit conversion of functors is intentional,
62 // the static function should implicitly behave like any generic constructor, adding
63 // explicit would require a static_cast. Furthermore, the 'StaticFunction' stores a copy
64 // which avoids implicit misbehaviors or ownership problems caused by implicit conversion.
65 // NOLINTNEXTLINE(hicpp-explicit-conversions)
66 StaticFunction(const Functor& functor) noexcept;
67
69 // NOLINTJUSTIFICATION the static function should implicitly behave like any generic constructor, adding
70 // explicit would require a static_cast. Furthermore, the 'StaticFunction' stores a copy
71 // which avoids implicit misbehaviors or ownership problems caused by implicit conversion.
72 // NOLINTNEXTLINE(hicpp-explicit-conversions)
73 StaticFunction(ReturnType (*function)(Args...)) noexcept;
74
77 template <typename T, typename = std::enable_if_t<std::is_class<T>::value, void>>
78 StaticFunction(T& object, ReturnType (T::*method)(Args...)) noexcept;
79
82 template <typename T, typename = std::enable_if_t<std::is_class<T>::value, void>>
83 StaticFunction(const T& object, ReturnType (T::*method)(Args...) const) noexcept;
84
85 StaticFunction(const StaticFunction& other) noexcept;
86
87 StaticFunction(StaticFunction&& other) noexcept;
88
89 auto operator=(const StaticFunction& rhs) noexcept -> StaticFunction&;
90
91 auto operator=(StaticFunction&& rhs) noexcept -> StaticFunction&;
92
93 ~StaticFunction() noexcept;
94
111 auto operator()(Args... args) const noexcept -> ReturnType;
112
115 void swap(StaticFunction& other) noexcept;
116
121 template <typename CallableType>
122 static constexpr auto required_storage_size() noexcept -> uint64_t;
123
128 template <typename CallableType>
129 static constexpr auto is_storable() noexcept -> bool;
130
131 private:
132 // Required to perform the correct operations with the underlying erased type
133 // This means 'StaticFunction' cannot be used where pointers become invalid, e.g. across process boundaries
134 // Therefore we cannot store a 'StaticFunction' in shared memory (the same holds for std::function).
135 // This is inherent to the type erasure technique we (have to) use.
136 struct Operations final {
137 // NOLINTBEGIN(misc-non-private-member-variables-in-classes): this is an internal helper class
138 // function pointers defining copy, move and destroy semantics
139 void (*copy_function)(const StaticFunction& src, StaticFunction& dest) { nullptr };
140 void (*move_function)(StaticFunction& src, StaticFunction& dest) { nullptr };
141 void (*destroy_function)(StaticFunction& func) { nullptr };
142 // NOLINTEND(misc-non-private-member-variables-in-classes)
143
144 Operations() noexcept = default;
145 Operations(const Operations& other) noexcept = default;
146 auto operator=(const Operations& other) noexcept -> Operations& = default;
147 Operations(Operations&& other) noexcept = default;
148 auto operator=(Operations&& other) noexcept -> Operations& = default;
149 ~Operations() = default;
150
151 void copy(const StaticFunction& src, StaticFunction& dest) const noexcept;
152
153 void move(StaticFunction& src, StaticFunction& dest) const noexcept;
154
155 void destroy(StaticFunction& func) const noexcept;
156 };
157
158 template <typename Functor,
159 typename = std::enable_if_t<std::is_class<Functor>::value
160 && legacy::is_invocable_r<ReturnType, Functor, Args...>::value,
161 void>>
162 void store_functor(const Functor& functor) noexcept;
163
164 // we need these templates to preserve the actual CallableType for the underlying call
165 template <typename CallableType>
166 static void copy(const StaticFunction& src, StaticFunction& dest) noexcept;
167
168 template <typename CallableType>
169 static void move(StaticFunction& src, StaticFunction& dest) noexcept;
170
171 template <typename CallableType>
172 static void destroy(StaticFunction& func) noexcept;
173
174 template <typename CallableType>
175 static auto invoke(void* callable, Args&&... args) noexcept -> ReturnType;
176
177 static void copy_free_function(const StaticFunction& src, StaticFunction& dest) noexcept;
178
179 static void move_free_function(StaticFunction& src, StaticFunction& dest) noexcept;
180
181 // AXIVION Next Construct AutosarC++19_03-M7.1.2: callable cannot be const void* since
182 // m_invoker is initialized with this function and has to work with functors as well
183 // (functors may change due to invocation)
184 static auto invoke_free_function(void* callable, Args&&... args) noexcept -> ReturnType;
185
186 template <typename T>
187 static constexpr auto safe_align(void* start_address) noexcept -> void*;
188};
189
193template <uint64_t Capacity, typename T>
195
196// AXIVION DISABLE STYLE AutosarC++19_03-A12.6.1: members are initialized before read access
197// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
198template <uint64_t Capacity, typename ReturnType, typename... Args>
199template <typename Functor, typename>
200inline StaticFunction<Capacity, Signature<ReturnType, Args...>>::StaticFunction(const Functor& functor) noexcept {
201 store_functor(functor);
202}
203
204// AXIVION Next Construct AutosarC++19_03-A12.1.5: constructor delegation is not feasible here due
205// to lack of sufficient common initialization
206// AXIVION Next Construct AutosarC++19_03-M5.2.6: the converted pointer is only used
207// as its original function pointer type after reconversion (type erasure)
208// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init) members are default initialized
209template <uint64_t Capacity, typename ReturnType, typename... Args>
210inline StaticFunction<Capacity, Signature<ReturnType, Args...>>::StaticFunction(
211 ReturnType (*function)(Args...)) noexcept
212 : // AXIVION Next Construct AutosarC++19_03-A5.2.4: reinterpret_cast is required for type erasure,
213 // we use type erasure in combination with compile time template arguments to restore
214 // the correct type whenever the callable is used
215 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
216 m_callable(reinterpret_cast<void*>(function))
217 , m_invoker(&invoke_free_function) {
218 IOX2_ENFORCE(function != nullptr, "parameter must not be a 'nullptr'");
219
220 m_operations.copy_function = &copy_free_function;
221 m_operations.move_function = &move_free_function;
222 // destroy is not needed for free functions
223}
224
225// AXIVION DISABLE STYLE AutosarC++19_03-M0.3.1: Pointer p aliases a reference and method is a member function pointer that cannot be null (*)
226// AXIVION DISABLE STYLE AutosarC++19_03-A5.3.2: see rule 'M0.3.1' above
227// AXIVION DISABLE STYLE FaultDetection-NullPointerDereference: see rule 'M0.3.1' above
228
229// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init) members are default initialized
230template <uint64_t Capacity, typename ReturnType, typename... Args>
231template <typename T, typename>
232inline StaticFunction<Capacity, Signature<ReturnType, Args...>>::StaticFunction(
233 T& object, ReturnType (T::*method)(Args...)) noexcept {
234 T* const ptr { &object };
235 const auto functor = [ptr, method](Args... args) noexcept -> ReturnType {
236 return (*ptr.*method)(std::forward<Args>(args)...);
237 };
238
239 store_functor(functor);
240}
241
242// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init)
243template <uint64_t Capacity, typename ReturnType, typename... Args>
244template <typename T, typename>
245inline StaticFunction<Capacity, Signature<ReturnType, Args...>>::StaticFunction(const T& object,
246 ReturnType (T::*method)(Args...)
247 const) noexcept {
248 const T* const ptr { &object };
249 const auto functor = [ptr, method](Args... args) noexcept -> ReturnType {
250 return (*ptr.*method)(std::forward<Args>(args)...);
251 };
252
253 store_functor(functor);
254}
255
256// AXIVION ENABLE STYLE FaultDetection-NullPointerDereference
257// AXIVION ENABLE STYLE AutosarC++19_03-A5.3.2
258// AXIVION ENABLE STYLE AutosarC++19_03-M0.3.1
259
260// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init) m_storage is default initialized
261template <uint64_t Capacity, typename ReturnType, typename... Args>
262inline StaticFunction<Capacity, Signature<ReturnType, Args...>>::StaticFunction(const StaticFunction& other) noexcept
263 : m_operations(other.m_operations)
264 , m_invoker(other.m_invoker) {
265 m_operations.copy(other, *this);
266}
267
268// AXIVION Next Construct AutosarC++19_03-A12.8.4: we copy only the operation pointer table
269// (required) and will perform a move with its type erased move function
270// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init, hicpp-member-init) m_storage is default initialized
271template <uint64_t Capacity, typename ReturnType, typename... Args>
272inline StaticFunction<Capacity, Signature<ReturnType, Args...>>::StaticFunction(StaticFunction&& other) noexcept
273 : m_operations(other.m_operations)
274 , m_invoker(other.m_invoker) {
275 m_operations.move(other, *this);
276}
277// AXIVION ENABLE STYLE AutosarC++19_03-A12.6.1
278
279template <uint64_t Capacity, typename ReturnType, typename... Args>
280inline auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::operator=(const StaticFunction& rhs) noexcept
282 if (&rhs != this) {
283 // this operations is needed for destroy, then changed to source (rhs) operations
284 m_operations.destroy(*this);
285 m_operations = rhs.m_operations;
286 m_invoker = rhs.m_invoker;
287 m_operations.copy(rhs, *this);
288 }
289
290 return *this;
291}
292
293template <uint64_t Capacity, typename ReturnType, typename... Args>
294inline auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::operator=(StaticFunction&& rhs) noexcept
296 if (&rhs != this) {
297 // this operations is needed for destroy, then changed to source (rhs) operations
298 m_operations.destroy(*this);
299 m_operations = rhs.m_operations;
300 m_invoker = rhs.m_invoker;
301 m_operations.move(rhs, *this);
302 }
303
304 return *this;
305}
306
307template <uint64_t Capacity, typename ReturnType, typename... Args>
308inline StaticFunction<Capacity, Signature<ReturnType, Args...>>::~StaticFunction() noexcept {
309 m_operations.destroy(*this);
310}
311
312// AXIVION Next Construct AutosarC++19_03-A7.5.2: false positive, operator() does not call itself
313// but the invoked function can be recursive in general (entirely controllable by caller)
314// AXIVION Next Construct AutosarC++19_03-A2.10.1: false positive, args does not hide anything
315template <uint64_t Capacity, typename ReturnType, typename... Args>
316inline auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::operator()(Args... args) const noexcept
317 -> ReturnType {
318#if (defined(__GNUC__) && __GNUC__ >= 11 && __GNUC__ <= 12 && !defined(__clang__))
319#pragma GCC diagnostic push
320#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
321#endif
322 IOX2_ENFORCE(m_callable != nullptr, "should not happen unless incorrectly used after move");
323 // AXIVION Next Construct AutosarC++19_03-M0.3.1, FaultDetection-NullPointerDereference: m_invoker is initialized in ctor or assignment,
324 // can only be nullptr if this was moved from (calling operator() is illegal in this case)
325 return m_invoker(m_callable, std::forward<Args>(args)...);
326#if (defined(__GNUC__) && __GNUC__ >= 11 && __GNUC__ <= 12 && !defined(__clang__))
327#pragma GCC diagnostic pop
328#endif
329}
330
331template <uint64_t Capacity, typename ReturnType, typename... Args>
332inline void StaticFunction<Capacity, Signature<ReturnType, Args...>>::swap(StaticFunction& other) noexcept {
333 StaticFunction tmp { std::move(other) };
334 other = std::move(*this);
335 *this = std::move(tmp);
336}
337
338template <uint64_t Capacity, typename T>
340 left.swap(right);
341}
342
343template <uint64_t Capacity, typename ReturnType, typename... Args>
344template <typename T>
345constexpr auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::safe_align(void* start_address) noexcept
346 -> void* {
347 static_assert(is_storable<T>(), "type does not fit into storage");
348 // AXIVION DISABLE STYLE AutosarC++19_03-A5.2.4 : Cast required for low level pointer alignment
349 // AXIVION DISABLE STYLE AutosarC++19_03-M5.2.9 : Conversion required for low level pointer alignment
350 // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast, performance-no-int-to-ptr)
351 const uint64_t alignment { alignof(T) };
352 const auto align = [](const uint64_t value, const uint64_t alignment) -> auto {
353 return (value + (alignment - 1U)) & (~alignment + 1U);
354 };
355 const uint64_t aligned_position { align(reinterpret_cast<uint64_t>(start_address), alignment) };
356 return reinterpret_cast<void*>(aligned_position);
357 // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast, performance-no-int-to-ptr)
358 // AXIVION ENABLE STYLE AutosarC++19_03-M5.2.9
359 // AXIVION ENABLE STYLE AutosarC++19_03-A5.2.4
360}
361
362template <uint64_t Capacity, typename ReturnType, typename... Args>
363template <typename Functor, typename>
364inline void StaticFunction<Capacity, Signature<ReturnType, Args...>>::store_functor(const Functor& functor) noexcept {
365 using StoredType = std::remove_reference_t<Functor>;
366 m_callable = safe_align<StoredType>(m_storage.begin());
367
368 // erase the functor type and store as reference to the call in storage
369 // AXIVION Next Construct AutosarC++19_03-A18.5.10: False positive! 'safeAlign' takes care of proper alignment and size
370 new (m_callable) StoredType(functor);
371
372 m_invoker = &invoke<StoredType>;
373 m_operations.copy_function = &copy<StoredType>;
374 m_operations.move_function = &move<StoredType>;
375 m_operations.destroy_function = &destroy<StoredType>;
376}
377
378// AXIVION Next Construct AutosarC++19_03-A8.4.8: output parameter required by design and for efficiency
379template <uint64_t Capacity, typename ReturnType, typename... Args>
380template <typename CallableType>
381inline void StaticFunction<Capacity, Signature<ReturnType, Args...>>::copy(const StaticFunction& src,
382 StaticFunction& dest) noexcept {
383 dest.m_callable = safe_align<CallableType>(dest.m_storage.begin());
384
385 // AXIVION Next Construct AutosarC++19_03-M5.2.8: type erasure - conversion to compatible type
386 const auto obj = static_cast<CallableType*>(src.m_callable);
387 IOX2_ENFORCE(obj != nullptr, "should not happen unless src is incorrectly used after move");
388
389 // AXIVION Next Construct AutosarC++19_03-A18.5.10: False positive! 'safeAlign' takes care of proper alignment and size
390 // NOLINTNEXTLINE(clang-analyzer-core.NonNullParamChecker) checked two lines above
391 new (dest.m_callable) CallableType(*obj);
392 dest.m_invoker = src.m_invoker;
393}
394
395// AXIVION Next Construct AutosarC++19_03-A8.4.4, AutosarC++19_03-A8.4.8: output parameter required by design and for
396// efficiency
397template <uint64_t Capacity, typename ReturnType, typename... Args>
398template <typename CallableType>
399inline void StaticFunction<Capacity, Signature<ReturnType, Args...>>::move(StaticFunction& src,
400 StaticFunction& dest) noexcept {
401 dest.m_callable = safe_align<CallableType>(dest.m_storage.begin());
402
403 // AXIVION Next Construct AutosarC++19_03-M5.2.8: type erasure - conversion to compatible type
404 const auto obj = static_cast<CallableType*>(src.m_callable);
405 IOX2_ENFORCE(obj != nullptr, "should not happen unless src is incorrectly used after move");
406
407 // AXIVION Next Construct AutosarC++19_03-A18.5.10: False positive! 'safeAlign' takes care of proper alignment and size
408 // NOLINTNEXTLINE(clang-analyzer-core.NonNullParamChecker) checked two lines above
409 new (dest.m_callable) CallableType(std::move(*obj));
410 dest.m_invoker = src.m_invoker;
411 src.m_operations.destroy(src);
412 src.m_callable = nullptr;
413 src.m_invoker = nullptr;
414}
415
416// AXIVION Next Construct AutosarC++19_03-M0.1.8: False positive! The function calls the destructor of a member of the parameter
417template <uint64_t Capacity, typename ReturnType, typename... Args>
418template <typename CallableType>
419inline void StaticFunction<Capacity, Signature<ReturnType, Args...>>::destroy(StaticFunction& func) noexcept {
420 if (func.m_callable != nullptr) {
421 // AXIVION Next Construct AutosarC++19_03-M5.2.8: type erasure - conversion to compatible type
422 const auto ptr = static_cast<CallableType*>(func.m_callable);
423 // AXIVION Next Construct AutosarC++19_03-A5.3.2: ptr is guaranteed not to be nullptr
424 ptr->~CallableType();
425 }
426}
427
428// AXIVION Next Construct AutosarC++19_03-A8.4.8: Out parameter is required for the intended functionality of the internal helper function
429template <uint64_t Capacity, typename ReturnType, typename... Args>
430inline void
431StaticFunction<Capacity, Signature<ReturnType, Args...>>::copy_free_function(const StaticFunction& src,
432 StaticFunction& dest) noexcept {
433 dest.m_invoker = src.m_invoker;
434 dest.m_callable = src.m_callable;
435}
436
437// AXIVION Next Construct AutosarC++19_03-A8.4.4, AutosarC++19_03-A8.4.8: output parameter required by design and for
438// efficiency
439template <uint64_t Capacity, typename ReturnType, typename... Args>
440inline void
441StaticFunction<Capacity, Signature<ReturnType, Args...>>::move_free_function(StaticFunction& src,
442 StaticFunction& dest) noexcept {
443 dest.m_invoker = src.m_invoker;
444 dest.m_callable = src.m_callable;
445 src.m_invoker = nullptr;
446 src.m_callable = nullptr;
447}
448
449// AXIVION Next Construct AutosarC++19_03-M7.1.2: callable cannot be const void* since
450// m_invoker is initialized with this function and has to work with functors as well
451template <uint64_t Capacity, typename ReturnType, typename... Args>
452template <typename CallableType>
453inline auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::invoke(void* callable, Args&&... args) noexcept
454 -> ReturnType {
455 // AXIVION DISABLE STYLE AutosarC++19_03-A18.9.2: we use idiomatic perfect forwarding
456 // AXIVION Next Construct AutosarC++19_03-M5.2.8: type erasure - conversion to compatible type
457 // AXIVION Next Construct AutosarC++19_03-A5.3.2: callable is guaranteed not to be nullptr
458 // when invoke is called (it is private and only used for type erasure)
459 // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) see justification above
460 return (*static_cast<CallableType*>(callable))(std::forward<Args>(args)...);
461 // AXIVION ENABLE STYLE AutosarC++19_03-A18.9.2
462}
463
464// AXIVION Next Construct AutosarC++19_03-A2.10.1: false positive, args does not hide anything
465// AXIVION Next Construct AutosarC++19_03-M7.1.2: callable cannot be const void* since
466// m_invoker is initialized with this function and has to work with functors as well
467// (functors may change due to invocation)
468template <uint64_t Capacity, typename ReturnType, typename... Args>
469inline auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::invoke_free_function(void* callable,
470 Args&&... args) noexcept
471 -> ReturnType {
472 // AXIVION Next Construct AutosarC++19_03-A18.9.2: we use idiomatic perfect forwarding
473 // AXIVION Next Construct AutosarC++19_03-A5.3.2: callable is guaranteed not to be nullptr
474 // when invokeFreeFunction is called (it is private and only used for type erasure)
475 // AXIVION Next Construct AutosarC++19_03-M5.2.8: type erasure - conversion to compatible type
476 // AXIVION Next Construct AutosarC++19_03-A5.2.4: reinterpret_cast is required for type erasure
477 // type erasure in combination with compile time template arguments to restore the correct type
478 // when the callable is called
479 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
480 return (reinterpret_cast<ReturnType (*)(Args...)>(callable))(std::forward<Args>(args)...);
481}
482
483template <uint64_t Capacity, typename ReturnType, typename... Args>
484template <typename T>
485constexpr auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::required_storage_size() noexcept -> uint64_t {
486 const uint64_t size { sizeof(T) };
487 const uint64_t alignment { alignof(T) };
488 return (size + alignment) - 1;
489}
490
491template <uint64_t Capacity, typename ReturnType, typename... Args>
492template <typename T>
493constexpr auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::is_storable() noexcept -> bool {
494 return (required_storage_size<T>() <= Capacity) && legacy::is_invocable_r<ReturnType, T, Args...>::value;
495}
496
497template <uint64_t Capacity, typename ReturnType, typename... Args>
498inline void
499StaticFunction<Capacity, Signature<ReturnType, Args...>>::Operations::copy(const StaticFunction& src,
500 StaticFunction& dest) const noexcept {
501 if (copy_function != nullptr) {
502 copy_function(src, dest);
503 }
504}
505
506template <uint64_t Capacity, typename ReturnType, typename... Args>
507inline void
508StaticFunction<Capacity, Signature<ReturnType, Args...>>::Operations::move(StaticFunction& src,
509 StaticFunction& dest) const noexcept {
510 if (move_function != nullptr) {
511 move_function(src, dest);
512 }
513}
514
515template <uint64_t Capacity, typename ReturnType, typename... Args>
516inline void
517StaticFunction<Capacity, Signature<ReturnType, Args...>>::Operations::destroy(StaticFunction& func) const noexcept {
518 if (destroy_function != nullptr) {
519 destroy_function(func);
520 }
521}
522
523} // namespace detail
524} // namespace bb
525} // namespace iox2
526
527#endif // IOX2_BB_STATIC_FUNCTION_HPP
#define IOX2_ENFORCE(condition, message)
report fatal enforce violation if expression evaluates to false
Wrapper class for a C-style array of type ElementType and size Capacity. Per default it is uninitiali...
iterator begin() noexcept
returns an iterator to the beginning of the UninitializedArray
ReturnType(Args...) Signature
void swap(StaticFunction< Capacity, T > &left, StaticFunction< Capacity, T > &right) noexcept
swap two static functions
detail::StaticFunction< Capacity, Signature > StaticFunction
A static memory replacement for std::function.
Verifies whether the passed Callable type is in fact invocable with the given arguments and the resul...