14#ifndef IOX2_BB_STATIC_FUNCTION_HPP
15#define IOX2_BB_STATIC_FUNCTION_HPP
26template <
typename ReturnType,
typename... Args>
29template <u
int64_t Capacity,
typename T>
42template <uint64_t Capacity,
typename ReturnType,
typename... Args>
46 Operations m_operations;
49 void* m_callable {
nullptr };
50 ReturnType (*m_invoker)(
void*, Args&&...) {
nullptr };
57 template <
typename Functor,
58 typename = std::enable_if_t<std::is_class<Functor>::value
77 template <typename T, typename = std::enable_if_t<std::is_class<T>::value,
void>>
78 StaticFunction(T&
object, ReturnType (T::*method)(Args...)) noexcept;
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;
111 auto operator()(Args... args) const noexcept -> ReturnType;
121 template <typename CallableType>
122 static constexpr auto required_storage_size() noexcept -> uint64_t;
128 template <typename CallableType>
129 static constexpr auto is_storable() noexcept ->
bool;
136 struct Operations final {
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;
158 template <
typename Functor,
159 typename = std::enable_if_t<std::is_class<Functor>::value
162 void store_functor(
const Functor& functor)
noexcept;
165 template <
typename CallableType>
168 template <
typename CallableType>
171 template <
typename CallableType>
174 template <
typename CallableType>
175 static auto invoke(
void* callable, Args&&... args)
noexcept -> ReturnType;
184 static auto invoke_free_function(
void* callable, Args&&... args)
noexcept -> ReturnType;
186 template <
typename T>
187 static constexpr auto safe_align(
void* start_address)
noexcept ->
void*;
193template <u
int64_t Capacity,
typename T>
198template <uint64_t Capacity,
typename ReturnType,
typename... Args>
199template <
typename Functor,
typename>
201 store_functor(functor);
209template <uint64_t Capacity,
typename ReturnType,
typename... Args>
211 ReturnType (*function)(Args...)) noexcept
216 m_callable(reinterpret_cast<
void*>(function))
217 , m_invoker(&invoke_free_function) {
218 IOX2_ENFORCE(function !=
nullptr,
"parameter must not be a 'nullptr'");
220 m_operations.copy_function = ©_free_function;
221 m_operations.move_function = &move_free_function;
230template <uint64_t Capacity,
typename ReturnType,
typename... Args>
231template <
typename T,
typename>
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)...);
239 store_functor(functor);
243template <uint64_t Capacity,
typename ReturnType,
typename... Args>
244template <
typename T,
typename>
246 ReturnType (T::*method)(Args...)
248 const T* const ptr { &object };
249 const auto functor = [ptr, method](Args... args)
noexcept -> ReturnType {
250 return (*ptr.*method)(std::forward<Args>(args)...);
253 store_functor(functor);
261template <uint64_t Capacity,
typename ReturnType,
typename... Args>
263 : m_operations(other.m_operations)
264 , m_invoker(other.m_invoker) {
265 m_operations.copy(other, *
this);
271template <uint64_t Capacity,
typename ReturnType,
typename... Args>
273 : m_operations(other.m_operations)
274 , m_invoker(other.m_invoker) {
275 m_operations.move(other, *
this);
279template <uint64_t Capacity,
typename ReturnType,
typename... Args>
284 m_operations.destroy(*
this);
285 m_operations = rhs.m_operations;
286 m_invoker = rhs.m_invoker;
287 m_operations.copy(rhs, *
this);
293template <uint64_t Capacity,
typename ReturnType,
typename... Args>
298 m_operations.destroy(*
this);
299 m_operations = rhs.m_operations;
300 m_invoker = rhs.m_invoker;
301 m_operations.move(rhs, *
this);
307template <uint64_t Capacity,
typename ReturnType,
typename... Args>
309 m_operations.destroy(*
this);
315template <uint64_t Capacity,
typename ReturnType,
typename... Args>
318#if (defined(__GNUC__) && __GNUC__ >= 11 && __GNUC__ <= 12 && !defined(__clang__))
319#pragma GCC diagnostic push
320#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
322 IOX2_ENFORCE(m_callable !=
nullptr,
"should not happen unless incorrectly used after move");
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
331template <uint64_t Capacity,
typename ReturnType,
typename... Args>
334 other = std::move(*
this);
335 *
this = std::move(tmp);
338template <u
int64_t Capacity,
typename T>
343template <uint64_t Capacity,
typename ReturnType,
typename... Args>
345constexpr auto StaticFunction<Capacity, Signature<ReturnType, Args...>>::safe_align(
void* start_address)
noexcept
347 static_assert(is_storable<T>(),
"type does not fit into storage");
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);
355 const uint64_t aligned_position { align(
reinterpret_cast<uint64_t
>(start_address), alignment) };
356 return reinterpret_cast<void*
>(aligned_position);
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());
370 new (m_callable) StoredType(functor);
372 m_invoker = &invoke<StoredType>;
373 m_operations.copy_function = ©<StoredType>;
374 m_operations.move_function = &move<StoredType>;
375 m_operations.destroy_function = &destroy<StoredType>;
379template <uint64_t Capacity,
typename ReturnType,
typename... Args>
380template <
typename CallableType>
382 StaticFunction& dest)
noexcept {
383 dest.m_callable = safe_align<CallableType>(dest.m_storage.begin());
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");
391 new (dest.m_callable) CallableType(*obj);
392 dest.m_invoker = src.m_invoker;
397template <uint64_t Capacity,
typename ReturnType,
typename... Args>
398template <
typename CallableType>
400 StaticFunction& dest)
noexcept {
401 dest.m_callable = safe_align<CallableType>(dest.m_storage.begin());
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");
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;
417template <uint64_t Capacity,
typename ReturnType,
typename... Args>
418template <
typename CallableType>
420 if (func.m_callable !=
nullptr) {
422 const auto ptr =
static_cast<CallableType*
>(func.m_callable);
424 ptr->~CallableType();
429template <uint64_t Capacity,
typename ReturnType,
typename... Args>
432 StaticFunction& dest)
noexcept {
433 dest.m_invoker = src.m_invoker;
434 dest.m_callable = src.m_callable;
439template <uint64_t Capacity,
typename ReturnType,
typename... Args>
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;
451template <uint64_t Capacity,
typename ReturnType,
typename... Args>
452template <
typename CallableType>
453inline auto StaticFunction<Capacity,
Signature<ReturnType, Args...>>::invoke(
void* callable, Args&&... args)
noexcept
460 return (*
static_cast<CallableType*
>(callable))(std::forward<Args>(args)...);
468template <uint64_t Capacity,
typename ReturnType,
typename... Args>
470 Args&&... args)
noexcept
480 return (
reinterpret_cast<ReturnType (*)(Args...)
>(callable))(std::forward<Args>(args)...);
483template <uint64_t Capacity,
typename ReturnType,
typename... Args>
486 const uint64_t size {
sizeof(T) };
487 const uint64_t alignment {
alignof(T) };
488 return (size + alignment) - 1;
491template <uint64_t Capacity,
typename ReturnType,
typename... Args>
497template <uint64_t Capacity,
typename ReturnType,
typename... Args>
501 if (copy_function !=
nullptr) {
502 copy_function(src, dest);
506template <uint64_t Capacity,
typename ReturnType,
typename... Args>
508StaticFunction<Capacity, Signature<ReturnType, Args...>>::Operations::move(StaticFunction& src,
509 StaticFunction& dest)
const noexcept {
510 if (move_function !=
nullptr) {
511 move_function(src, dest);
515template <uint64_t Capacity,
typename ReturnType,
typename... Args>
517StaticFunction<Capacity,
Signature<ReturnType, Args...>>::Operations::destroy(StaticFunction& func)
const noexcept {
518 if (destroy_function !=
nullptr) {
519 destroy_function(func);
#define IOX2_ENFORCE(condition, message)
report fatal enforce violation if expression evaluates to false
Signature< ReturnType, Args... > SignatureT
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...