ReXGlue SDK
Loading...
Searching...
No Matches
utility.h
Go to the documentation of this file.
1
11
12#pragma once
13
14#include <cstddef>
15#include <functional>
16#include <memory>
17#include <new>
18#include <type_traits>
19#include <utility>
20#include <version>
21
22#include <rex/assert.h>
23#include <rex/platform.h>
24#include <rex/types.h>
25
26namespace rex {
27
28class MoveOnly {
29 protected:
30 MoveOnly() = default;
31 MoveOnly(MoveOnly&&) = default;
32 MoveOnly& operator=(MoveOnly&&) = default;
33 MoveOnly(const MoveOnly&) = delete;
34 MoveOnly& operator=(const MoveOnly&) = delete;
35};
36
37class NonCopyable {
38 protected:
39 NonCopyable() = default;
40 NonCopyable(const NonCopyable&) = delete;
41 NonCopyable& operator=(const NonCopyable&) = delete;
42};
43
44namespace detail {
45
51template <bool Const, typename R, typename... Args>
52class MoveOnlyCallable {
53 static constexpr std::size_t kStorageSize = 3 * sizeof(void*);
54 static constexpr std::size_t kStorageAlign = alignof(std::max_align_t);
55
56 template <typename D>
57 using TargetRef = std::conditional_t<Const, const D&, D&>;
58
59 template <typename D>
60 static constexpr bool kInline = sizeof(D) <= kStorageSize && alignof(D) <= kStorageAlign &&
61 std::is_nothrow_move_constructible_v<D>;
62
63 struct Ops {
64 R (*invoke)(void* storage, Args&&... args);
65 void (*relocate)(void* dst, void* src) noexcept;
66 void (*destroy)(void* storage) noexcept;
67 };
68
69 template <typename D>
70 static D* Target(void* storage) noexcept {
71 if constexpr (kInline<D>) {
72 return std::launder(static_cast<D*>(storage));
73 } else {
74 return *std::launder(static_cast<D**>(storage));
75 }
76 }
77
78 template <typename D>
79 static R Invoke(void* storage, Args&&... args) {
80 TargetRef<D> target = *Target<D>(storage);
81 if constexpr (std::is_void_v<R>) {
82 static_cast<void>(std::invoke(target, std::forward<Args>(args)...));
83 } else {
84 return std::invoke(target, std::forward<Args>(args)...);
85 }
86 }
87
88 template <typename D>
89 static void Relocate(void* dst, void* src) noexcept {
90 if constexpr (kInline<D>) {
91 D* from = Target<D>(src);
92 ::new (dst) D(std::move(*from));
93 from->~D();
94 } else {
95 ::new (dst) D*(Target<D>(src));
96 }
97 }
98
99 template <typename D>
100 static void Destroy(void* storage) noexcept {
101 if constexpr (kInline<D>) {
102 Target<D>(storage)->~D();
103 } else {
104 delete Target<D>(storage);
105 }
106 }
107
108 template <typename D>
109 static constexpr Ops kOps{&Invoke<D>, &Relocate<D>, &Destroy<D>};
110
111 // Checks the call before the construction, as std::move_only_function does, so a type that is
112 // constructible from this class does not recurse.
113 template <typename F>
114 static constexpr bool kAccepts =
115 std::conjunction_v<std::negation<std::is_same<std::remove_cvref_t<F>, MoveOnlyCallable>>,
116 std::is_invocable_r<R, TargetRef<std::decay_t<F>>, Args...>,
117 std::is_constructible<std::decay_t<F>, F>>;
118
119 public:
120 MoveOnlyCallable() noexcept = default;
121
122 MoveOnlyCallable(std::nullptr_t) noexcept {}
123
124 template <typename F>
125 requires kAccepts<F>
126 MoveOnlyCallable(F&& f) {
127 using D = std::decay_t<F>;
128 if constexpr (std::is_pointer_v<D> || std::is_member_pointer_v<D>) {
129 if (f == nullptr) {
130 return;
131 }
132 }
133 if constexpr (kInline<D>) {
134 ::new (static_cast<void*>(storage_)) D(std::forward<F>(f));
135 } else {
136 ::new (static_cast<void*>(storage_)) D*(new D(std::forward<F>(f)));
137 }
138 ops_ = &kOps<D>;
139 }
140
141 MoveOnlyCallable(MoveOnlyCallable&& other) noexcept { TakeFrom(other); }
142
143 MoveOnlyCallable(const MoveOnlyCallable&) = delete;
144 MoveOnlyCallable& operator=(const MoveOnlyCallable&) = delete;
145
146 ~MoveOnlyCallable() { Reset(); }
147
148 MoveOnlyCallable& operator=(MoveOnlyCallable&& other) noexcept {
149 if (this != &other) {
150 Reset();
151 TakeFrom(other);
152 }
153 return *this;
154 }
155
156 MoveOnlyCallable& operator=(std::nullptr_t) noexcept {
157 Reset();
158 return *this;
159 }
160
161 template <typename F>
162 requires kAccepts<F>
163 MoveOnlyCallable& operator=(F&& f) {
164 *this = MoveOnlyCallable(std::forward<F>(f));
165 return *this;
166 }
167
168 void swap(MoveOnlyCallable& other) noexcept {
169 MoveOnlyCallable held(std::move(other));
170 other = std::move(*this);
171 *this = std::move(held);
172 }
173
174 friend void swap(MoveOnlyCallable& a, MoveOnlyCallable& b) noexcept { a.swap(b); }
175
176 explicit operator bool() const noexcept { return ops_ != nullptr; }
177
178 friend bool operator==(const MoveOnlyCallable& f, std::nullptr_t) noexcept {
179 return f.ops_ == nullptr;
180 }
181
182 R operator()(Args... args)
183 requires(!Const)
184 {
185 REX_ASSERT(ops_ != nullptr);
186 return ops_->invoke(storage_, std::forward<Args>(args)...);
187 }
188
189 R operator()(Args... args) const
190 requires Const
191 {
192 REX_ASSERT(ops_ != nullptr);
193 return ops_->invoke(const_cast<std::byte*>(storage_), std::forward<Args>(args)...);
194 }
195
196 private:
197 void TakeFrom(MoveOnlyCallable& other) noexcept {
198 if (other.ops_ != nullptr) {
199 other.ops_->relocate(storage_, other.storage_);
200 ops_ = std::exchange(other.ops_, nullptr);
201 }
202 }
203
204 void Reset() noexcept {
205 if (const Ops* ops = std::exchange(ops_, nullptr)) {
206 ops->destroy(storage_);
207 }
208 }
209
210 alignas(kStorageAlign) std::byte storage_[kStorageSize]{};
211 const Ops* ops_ = nullptr;
212};
213
219template <typename Signature>
220struct MoveOnlyFunctionFor;
221
223template <typename R, typename... Args>
224struct MoveOnlyFunctionFor<R(Args...)> {
225 using type = MoveOnlyCallable<false, R, Args...>;
226};
227
229template <typename R, typename... Args>
230struct MoveOnlyFunctionFor<R(Args...) const> {
231 using type = MoveOnlyCallable<true, R, Args...>;
232};
233
234} // namespace detail
235
236#if defined(__cpp_lib_move_only_function) && __cpp_lib_move_only_function >= 202110L
237
243template <typename Signature>
244using MoveOnlyFunction = std::move_only_function<Signature>;
245
246#else
247
253template <typename Signature>
254using MoveOnlyFunction = typename detail::MoveOnlyFunctionFor<Signature>::type;
255
256#endif
257
263template <typename T>
264class PropagateConst : public MoveOnly {
265 public:
266 using element_type = std::remove_reference_t<decltype(*std::declval<T&>())>;
267
268 PropagateConst() = default;
269
270 PropagateConst(T pointer) noexcept(std::is_nothrow_move_constructible_v<T>)
271 : pointer_(std::move(pointer)) {}
272
273 PropagateConst(PropagateConst&& other) noexcept = default;
274
275 PropagateConst& operator=(PropagateConst&& other) noexcept = default;
276
277 PropagateConst& operator=(T pointer) noexcept(std::is_nothrow_move_assignable_v<T>) {
278 pointer_ = std::move(pointer);
279 return *this;
280 }
281
282 element_type* get() noexcept { return std::to_address(pointer_); }
283
284 const element_type* get() const noexcept { return std::to_address(pointer_); }
285
286 element_type* operator->() noexcept { return get(); }
287
288 const element_type* operator->() const noexcept { return get(); }
289
290 element_type& operator*() noexcept { return *get(); }
291
292 const element_type& operator*() const noexcept { return *get(); }
293
294 explicit operator bool() const noexcept { return get() != nullptr; }
295
296 friend bool operator==(const PropagateConst& value, std::nullptr_t) noexcept {
297 return value.get() == nullptr;
298 }
299
300 private:
301 T pointer_{};
302};
303
305class REX_API ScopeGuard {
306 public:
307 ScopeGuard() = default;
308
314 explicit ScopeGuard(MoveOnlyFunction<void()> release);
315
321 ScopeGuard(ScopeGuard&& other) noexcept;
322
328 ScopeGuard& operator=(ScopeGuard&& other) noexcept;
329
330 ~ScopeGuard();
331
333 void Reset();
334
335 private:
336 MoveOnlyFunction<void()> release_;
337};
338} // namespace rex
#define REX_ASSERT(cond,...)
Definition assert.h:84
ScopeGuard(MoveOnlyFunction< void()> release)
ScopeGuard & operator=(ScopeGuard &&other) noexcept
ScopeGuard(ScopeGuard &&other) noexcept
Definition app.h:36
typename detail::MoveOnlyFunctionFor< Signature >::type MoveOnlyFunction
Definition utility.h:254
Platform flags, packing macros and export markers.
#define REX_API
Definition platform.h:177
Integer types, byte order wrappers and size literals.