Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
fixed_function.hpp
Go to the documentation of this file.
1#pragma once
2
32
33#include <cstddef>
34#include <new>
35#include <type_traits>
36#include <utility>
37
38namespace pulp::runtime {
39
40template <typename Signature, std::size_t Capacity = 32,
41 std::size_t Alignment = alignof(std::max_align_t)>
43
44template <typename R, typename... Args, std::size_t Capacity, std::size_t Alignment>
45class FixedFunction<R(Args...), Capacity, Alignment> {
46 static_assert(Capacity >= sizeof(void*), "Capacity must hold at least a pointer");
47 static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0,
48 "Alignment must be a power of two");
49
50 public:
51 static constexpr std::size_t capacity = Capacity;
52 static constexpr std::size_t alignment = Alignment;
53
55 template <typename F>
56 static constexpr bool fits =
57 sizeof(std::decay_t<F>) <= Capacity && alignof(std::decay_t<F>) <= Alignment &&
58 std::is_nothrow_move_constructible_v<std::decay_t<F>>;
59
60 FixedFunction() noexcept = default;
61 FixedFunction(std::nullptr_t) noexcept {}
62
63 template <typename F, typename D = std::decay_t<F>,
64 typename = std::enable_if_t<!std::is_same_v<D, FixedFunction> &&
65 !std::is_same_v<D, std::nullptr_t> &&
66 std::is_invocable_r_v<R, D&, Args...>>>
67 FixedFunction(F&& f) noexcept(
68 std::is_nothrow_constructible_v<D, F&&>) { // NOLINT: implicit like std::function
69 static_assert(sizeof(D) <= Capacity,
70 "callable is too large for this FixedFunction: capture less (pointers or "
71 "indices instead of objects) or raise Capacity");
72 static_assert(alignof(D) <= Alignment,
73 "callable needs stricter alignment than this FixedFunction provides");
74 static_assert(std::is_nothrow_move_constructible_v<D>,
75 "callable must be nothrow move constructible so moving a FixedFunction "
76 "cannot throw");
77 ::new (static_cast<void*>(storage_)) D(std::forward<F>(f));
78 vtable_ = &vtable_for<D>;
79 }
80
81 FixedFunction(const FixedFunction&) = delete;
83
85 take(other);
86 }
87
89 if (this != &other) {
90 reset();
91 take(other);
92 }
93 return *this;
94 }
95
96 FixedFunction& operator=(std::nullptr_t) noexcept {
97 reset();
98 return *this;
99 }
100
101 template <typename F, typename D = std::decay_t<F>,
102 typename = std::enable_if_t<!std::is_same_v<D, FixedFunction> &&
103 !std::is_same_v<D, std::nullptr_t> &&
104 std::is_invocable_r_v<R, D&, Args...>>>
105 FixedFunction& operator=(F&& f) noexcept(std::is_nothrow_constructible_v<D, F&&>) {
106 FixedFunction replacement(std::forward<F>(f)); // reuses the checked constructor
107 return *this = std::move(replacement);
108 }
109
111 reset();
112 }
113
115 void reset() noexcept {
116 if (vtable_) {
117 vtable_->destroy(storage_);
118 vtable_ = nullptr;
119 }
120 }
121
122 void swap(FixedFunction& other) noexcept {
123 FixedFunction tmp(std::move(other));
124 other = std::move(*this);
125 *this = std::move(tmp);
126 }
127
128 [[nodiscard]] explicit operator bool() const noexcept {
129 return vtable_ != nullptr;
130 }
131
132 friend bool operator==(const FixedFunction& f, std::nullptr_t) noexcept {
133 return !f;
134 }
135 friend bool operator!=(const FixedFunction& f, std::nullptr_t) noexcept {
136 return static_cast<bool>(f);
137 }
138
140 R operator()(Args... args) {
141 return vtable_->invoke(storage_, std::forward<Args>(args)...);
142 }
143
144 private:
145 struct VTable {
146 R (*invoke)(void* target, Args&&... args);
147 void (*relocate)(void* dst, void* src) noexcept; // move-construct into dst, destroy src
148 void (*destroy)(void* target) noexcept;
149 };
150
151 template <typename D>
152 static constexpr VTable vtable_for = {
153 [](void* target, Args&&... args) -> R {
154 D& callable = *static_cast<D*>(target);
155 if constexpr (std::is_void_v<R>) {
156 callable(std::forward<Args>(args)...);
157 } else {
158 return callable(std::forward<Args>(args)...);
159 }
160 },
161 [](void* dst, void* src) noexcept {
162 ::new (dst) D(std::move(*static_cast<D*>(src)));
163 static_cast<D*>(src)->~D();
164 },
165 [](void* target) noexcept { static_cast<D*>(target)->~D(); },
166 };
167
168 void take(FixedFunction& other) noexcept {
169 if (other.vtable_) {
170 other.vtable_->relocate(storage_, other.storage_);
171 vtable_ = other.vtable_;
172 other.vtable_ = nullptr;
173 }
174 }
175
176 alignas(Alignment) unsigned char storage_[Capacity];
177 const VTable* vtable_ = nullptr;
178};
179
180} // namespace pulp::runtime
FixedFunction(FixedFunction &&other) noexcept
Definition fixed_function.hpp:84
FixedFunction(F &&f) noexcept(std::is_nothrow_constructible_v< D, F && >)
Definition fixed_function.hpp:67
FixedFunction & operator=(std::nullptr_t) noexcept
Definition fixed_function.hpp:96
FixedFunction & operator=(FixedFunction &&other) noexcept
Definition fixed_function.hpp:88
static constexpr std::size_t capacity
Definition fixed_function.hpp:51
FixedFunction & operator=(const FixedFunction &)=delete
R operator()(Args... args)
Call the target. Precondition: not empty (undefined behaviour otherwise).
Definition fixed_function.hpp:140
static constexpr bool fits
True if a callable of type F can be stored.
Definition fixed_function.hpp:56
friend bool operator!=(const FixedFunction &f, std::nullptr_t) noexcept
Definition fixed_function.hpp:135
friend bool operator==(const FixedFunction &f, std::nullptr_t) noexcept
Definition fixed_function.hpp:132
void reset() noexcept
Destroy the target and become empty.
Definition fixed_function.hpp:115
FixedFunction & operator=(F &&f) noexcept(std::is_nothrow_constructible_v< D, F && >)
Definition fixed_function.hpp:105
static constexpr std::size_t alignment
Definition fixed_function.hpp:52
void swap(FixedFunction &other) noexcept
Definition fixed_function.hpp:122
Definition fixed_function.hpp:42
Definition generated_event_source.hpp:33
@ other
Definition udev_monitor.hpp:26
STL namespace.