header_utils
Loading...
Searching...
No Matches
scope.h
1
4
5#pragma once
6
7#include "min-cpp-version/cpp17.h"
8#include <type_traits>
9#include <utility>
10
11namespace ghassanpl
12{
16
18 template <class EF>
19 struct scope_guard
20 {
21 template <class EFP>
22 explicit scope_guard(EFP&& f) noexcept(noexcept(EFP(std::forward<EFP>(f))))
23 : exit_function(std::forward<EFP>(f))
24 {
25 }
26
27 scope_guard(scope_guard&& rhs) noexcept(noexcept(EF(std::move(rhs.exit_function))))
28 : exit_function(std::move(rhs.exit_function))
29 {
30 rhs.release();
31 }
32
33 scope_guard(const scope_guard&) = delete;
34 scope_guard& operator=(const scope_guard&) = delete;
35 scope_guard& operator=(scope_guard&&) = delete;
36
37 ~scope_guard() noexcept(noexcept(this->exit_function()))
38 {
39 if (this->execute_on_destruction)
40 this->exit_function();
41 }
42
43 void release() noexcept
44 {
45 this->execute_on_destruction = false;
46 }
47
48 bool will_execute_on_destruction() const noexcept
49 {
50 return this->execute_on_destruction;
51 }
52
53 private:
54
55 EF exit_function;
56 bool execute_on_destruction{ true };
57 };
58
60 template <class EF>
61 struct counted_scope_guard
62 {
63 template <class EFP>
64 explicit counted_scope_guard(EFP&& f, int initial_count = 0) noexcept(noexcept(EFP(std::forward<EFP>(f))))
65 : m_exit_function(std::forward<EFP>(f))
66 , m_count(initial_count)
67 {
68 }
69
70 counted_scope_guard(counted_scope_guard&& rhs) noexcept(noexcept(EF(std::move(rhs.m_exit_function))))
71 : m_exit_function(std::move(rhs.m_exit_function))
72 , m_count(std::exchange(rhs.m_count, 0))
73 {
74 }
75
76 counted_scope_guard(const counted_scope_guard&) = delete;
77 counted_scope_guard& operator=(const counted_scope_guard&) = delete;
78 counted_scope_guard& operator=(counted_scope_guard&&) = delete;
79
81 ~counted_scope_guard() noexcept(noexcept(m_exit_function()))
82 {
83 if (m_count > 0)
84 m_exit_function();
85 }
86
88 void request() noexcept
89 {
90 ++m_count;
91 }
92
94 void unrequest() noexcept
95 {
96 if (m_count > 0)
97 --m_count;
98 }
99
101 void release() noexcept
102 {
103 m_count = 0;
104 }
105
106 bool will_execute_on_destruction() const noexcept
107 {
108 return m_count > 0;
109 }
110
111 private:
112
113 EF m_exit_function;
114 int m_count = 0;
115 };
116
117 template <class EF>
119
122 template <class R, class D>
123 struct unique_resource
124 {
125 unique_resource() = default;
126
127 template <class RR, class DD>
128 unique_resource(RR&& r, DD&& d) noexcept(std::is_nothrow_constructible_v<R1, RR> && std::is_nothrow_constructible_v<D, DD>)
129 : resource(std::forward<RR>(r))
130 , deleter(std::forward<DD>(d))
131 , execute_on_reset(true)
132 {
133 }
134
135 template <class RR, class DD, class I = std::decay_t<RR>>
136 unique_resource(RR&& r, const I& invalid, DD&& d) noexcept(std::is_nothrow_constructible_v<R1, RR> && std::is_nothrow_constructible_v<D, DD>)
137 : resource(std::forward<RR>(r))
138 , deleter(std::forward<DD>(d))
139 , execute_on_reset(!bool(resource == invalid))
140 {
141 }
142
143 unique_resource(unique_resource&& rhs) noexcept(std::is_nothrow_move_constructible_v<R1> && std::is_nothrow_move_constructible_v<D>)
144 : resource(std::move(rhs.resource))
145 , deleter(std::move(rhs.deleter))
146 , execute_on_reset(std::exchange(rhs.execute_on_reset, false))
147 {
148 }
149
150 ~unique_resource() noexcept(noexcept(deleter(resource)))
151 {
152 if (execute_on_reset)
153 deleter(resource);
154 }
155
156 unique_resource& operator=(unique_resource&& rhs) noexcept(std::is_nothrow_move_assignable_v<R1> && std::is_nothrow_move_assignable_v<D> && noexcept(deleter(resource)))
157 {
158 if (std::addressof(rhs) == this)
159 return *this;
160
161 reset();
162 if constexpr (std::is_nothrow_move_assignable_v<R1>)
163 {
164 if constexpr (std::is_nothrow_move_assignable_v<D>)
165 {
166 resource = std::move(rhs.resource);
167 deleter = std::move(rhs.deleter);
168 }
169 else
170 {
171 deleter = rhs.deleter;
172 resource = std::move(rhs.resource);
173 }
174 }
175 else
176 {
177 if constexpr (std::is_nothrow_move_assignable_v<D>)
178 {
179 resource = rhs.resource;
180 deleter = std::move(rhs.deleter);
181 }
182 else
183 {
184 resource = rhs.resource;
185 deleter = rhs.deleter;
186 }
187 }
188 execute_on_reset = std::exchange(rhs.execute_on_reset, false);
189 return *this;
190 }
191
192 void reset() noexcept(noexcept(deleter(resource)))
193 {
194 if (execute_on_reset)
195 {
196 execute_on_reset = false;
197 deleter(resource);
198 }
199 }
200
201 template <class RR>
202 void reset(RR&& r) noexcept(noexcept(deleter(resource)) && std::is_nothrow_assignable_v<R1&, RR>)
203 {
204 if (std::addressof(r) == std::addressof(resource))
205 return;
206
207 reset();
208 try
209 {
210 if constexpr (std::is_nothrow_assignable_v<R1&, RR>)
211 resource = std::forward<RR>(r);
212 else
213 resource = std::as_const(r);
214 }
215 catch (...)
216 {
217 deleter(r);
218 throw;
219 }
220 execute_on_reset = true;
221 }
222
223 void release() noexcept
224 {
225 execute_on_reset = false;
226 }
227
228 const R& get() const noexcept
229 {
230 return resource;
231 }
232 explicit operator R&() noexcept { return resource; }
233
234 auto operator*() const noexcept
235 GHPL_REQUIRES(std::is_pointer_v<R> && (!std::is_void_v<std::remove_pointer_t<R>>))
236 {
237 return *get();
238 }
239
240 R operator->() const noexcept
241 GHPL_REQUIRES(std::is_pointer_v<R>)
242 {
243 return get();
244 }
245
246 const D& get_deleter() const noexcept { return deleter; }
247
248 private:
249
250 using R1 = std::conditional_t<std::is_reference_v<R>, std::reference_wrapper<std::remove_reference_t<R>>, R>;
251 R1 resource{};
252 D deleter{};
253 bool execute_on_reset{};
254 };
255
256 template<class R, class D>
258 template <class R, class D, class I = std::decay_t<R>>
260
263 template <typename T, bool IS_OPTIONAL = false>
264 struct scoped_value_change
265 {
266 template <typename U, bool OPT = IS_OPTIONAL>
267 scoped_value_change(T& ref, U new_val, typename std::enable_if_t<!OPT>* = 0) noexcept(std::is_nothrow_move_constructible_v<T>)
268 : m_ref(std::addressof(ref))
269 , m_original_value(std::exchange(ref, new_val))
270 {
271 }
272
273 template <typename U, bool OPT = IS_OPTIONAL>
274 scoped_value_change(T& ref, U new_val, typename std::enable_if_t<OPT>* = 0) noexcept(std::is_nothrow_move_constructible_v<T> && std::is_nothrow_copy_constructible_v<T>)
275 : m_ref(std::addressof(ref))
276 , m_original_value(*m_ref != new_val ? std::exchange(ref, new_val) : ref)
277 {
278 }
279
280 ~scoped_value_change() noexcept(std::is_nothrow_move_assignable_v<T>)
281 {
282 revert();
283 }
284
285 scoped_value_change(const scoped_value_change&) = delete;
286 scoped_value_change(scoped_value_change&& other) noexcept(std::is_nothrow_move_constructible_v<T>)
287 : m_ref(other.m_ref)
288 , m_original_value(std::move(other.m_original_value))
289 {
290 other.m_ref = nullptr;
291 }
292
293 scoped_value_change& operator=(const scoped_value_change&) = delete;
294
297 scoped_value_change& operator=(scoped_value_change&& other) = delete;
298
299 /*
300 scoped_value_change& operator=(scoped_value_change&& other) noexcept(std::is_nothrow_move_assignable_v<T>)
301 {
302 revert();
303
304 m_ref = other.m_ref;
305 m_original_value = std::move(other.m_original_value);
306 other.m_ref = nullptr;
307 return *this;
308 }
309 */
310
311 bool valid() const noexcept { return m_ref != nullptr; }
312
315 T const& original_value() const & noexcept
316 {
317 return m_original_value;
318 }
319
322 T original_value() && noexcept(std::is_nothrow_move_constructible_v<T>)
323 {
324 return std::move(m_original_value);
325 }
326
329 T const& current_value()
330 {
331 return *m_ref;
332 }
333
336 void revert() noexcept(std::is_nothrow_move_assignable_v<T>)
337 {
338 if (m_ref)
339 {
340 auto& ref = *m_ref;
341 m_ref = nullptr;
342
343 if constexpr (IS_OPTIONAL)
344 {
345 if (ref == m_original_value)
346 return;
347 }
348
349 ref = std::move(m_original_value);
350 }
351 }
352
357 T revert_and_return() noexcept(std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>)
358 {
359 auto& ref = *m_ref;
360 m_ref = nullptr;
361
362 if constexpr (IS_OPTIONAL)
363 {
364 if (ref != m_original_value)
365 return std::move(m_original_value);
366 }
367
368 return std::exchange(ref, std::move(m_original_value));
369 }
370
373 void release() noexcept
374 {
375 m_ref = nullptr;
376 }
377
381 T release_and_return() noexcept(std::is_nothrow_move_constructible_v<T>)
382 {
383 m_ref = nullptr;
384 return std::move(m_original_value);
385 }
386
387 private:
388
389 T* m_ref;
390 T m_original_value;
391 };
392
393 template <typename T>
394 using optional_scoped_value_change = scoped_value_change<T, true>;
395
396 template <typename T, typename U>
397 scoped_value_change(T&, U&&) -> scoped_value_change<T>;
398
399 // TODO: atomic_scoped_value_change
400
402 template <typename T>
403 struct scope_counter
404 {
405 scope_counter(T& ref) noexcept(noexcept(++ref))
406 : m_ref(std::addressof(ref))
407 {
408 ++ref;
409 }
410
411 scope_counter(const scope_counter&) = delete;
412 scope_counter(scope_counter&& other) noexcept
413 {
414 m_ref = std::exchange(other.m_ref, nullptr);
415 }
416 scope_counter& operator=(const scope_counter&) = delete;
417 scope_counter& operator=(scope_counter&&) = delete;
418
419 bool valid() const noexcept { return m_ref != nullptr; }
420
421 ~scope_counter() noexcept(noexcept(--m_ref))
422 {
423 if (m_ref)
424 --m_ref;
425 }
426
427 void release() const noexcept
428 {
429 m_ref = nullptr;
430 }
431
432 private:
433
434 T* m_ref;
435 };
436
437
439}
Primary namespace for everything in this library.
Definition align+rec2.h:10
GHPL_REQUIRES(std::is_enum_v< ENUM_TYPE >) struct circular_enum
TODO: Finish this.
Definition enums.h:23
void unrequest() noexcept
Decrements the request counter.
Definition scope.h:94
~counted_scope_guard() noexcept(noexcept(m_exit_function()))
Executes the function on destruction if the request counter is greater than zero.
Definition scope.h:81
void request() noexcept
Increments the request counter.
Definition scope.h:88
void release() noexcept
Releases the guard, so that it will not execute the function on destruction.
Definition scope.h:101
An RAII class that executes a function on destruction.
Definition scope.h:20
void revert() noexcept(std::is_nothrow_move_assignable_v< T >)
Reverts the value to the original one.
Definition scope.h:336
T revert_and_return() noexcept(std::is_nothrow_move_assignable_v< T > &&std::is_nothrow_move_constructible_v< T >)
Reverts the value to the original one.
Definition scope.h:357
scoped_value_change & operator=(scoped_value_change &&other)=delete
Move assignment is not implemented because it's not clear and obvious what the order of reversions wo...
T const & current_value()
Returns the current value.
Definition scope.h:329
T const & original_value() const &noexcept
Returns the original value.
Definition scope.h:315
void release() noexcept
Causes the value to not be reverted on destruction.
Definition scope.h:373
T original_value() &&noexcept(std::is_nothrow_move_constructible_v< T >)
Returns the original value.
Definition scope.h:322
T release_and_return() noexcept(std::is_nothrow_move_constructible_v< T >)
Causes the value to not be reverted on destruction.
Definition scope.h:381
An equivalent to unique_ptr for values that are not heap pointers.
Definition scope.h:124