-
Notifications
You must be signed in to change notification settings - Fork 16
/
scope_guard.hpp
369 lines (304 loc) · 13.7 KB
/
scope_guard.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
// _____ _____ _ _____
// / ____| / ____| | | / ____|_ _
// | (___ ___ ___ _ __ ___ | | __ _ _ __ _ _ __ __| | | | _| |_ _| |_
// \___ \ / __/ _ \| '_ \ / _ \ | | |_ | | | |/ _` | '__/ _` | | | |_ _|_ _|
// ____) | (_| (_) | |_) | __/ | |__| | |_| | (_| | | | (_| | | |____|_| |_|
// |_____/ \___\___/| .__/ \___| \_____|\__,_|\__,_|_| \__,_| \_____|
// | | https://github.com/Neargye/scope_guard
// |_| version 0.9.1
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2018 - 2024 Daniil Goncharov <neargye@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_SCOPE_GUARD_HPP
#define NEARGYE_SCOPE_GUARD_HPP
#define SCOPE_GUARD_VERSION_MAJOR 0
#define SCOPE_GUARD_VERSION_MINOR 9
#define SCOPE_GUARD_VERSION_PATCH 1
#include <type_traits>
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || ((defined(__clang__) || defined(__GNUC__)) && __cplusplus >= 201700L)
#include <exception>
#endif
// scope_guard throwable settings:
// SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE requires nothrow constructible action.
// SCOPE_GUARD_MAY_THROW_ACTION action may throw exceptions.
// SCOPE_GUARD_NO_THROW_ACTION requires noexcept action.
// SCOPE_GUARD_SUPPRESS_THROW_ACTION exceptions during action will be suppressed.
// SCOPE_GUARD_CATCH_HANDLER exceptions handler. If SCOPE_GUARD_SUPPRESS_THROW_ACTIONS is not defined, it will do nothing.
#if !defined(SCOPE_GUARD_MAY_THROW_ACTION) && !defined(SCOPE_GUARD_NO_THROW_ACTION) && !defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION)
# define SCOPE_GUARD_MAY_THROW_ACTION
#elif (defined(SCOPE_GUARD_MAY_THROW_ACTION) + defined(SCOPE_GUARD_NO_THROW_ACTION) + defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION)) > 1
# error Only one of SCOPE_GUARD_MAY_THROW_ACTION and SCOPE_GUARD_NO_THROW_ACTION and SCOPE_GUARD_SUPPRESS_THROW_ACTION may be defined.
#endif
#if !defined(SCOPE_GUARD_CATCH_HANDLER)
# define SCOPE_GUARD_CATCH_HANDLER /* Suppress exception.*/
#endif
namespace scope_guard {
namespace detail {
#if defined(SCOPE_GUARD_SUPPRESS_THROW_ACTION) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS))
# define NEARGYE_NOEXCEPT(...) noexcept
# define NEARGYE_TRY try {
# define NEARGYE_CATCH } catch (...) { SCOPE_GUARD_CATCH_HANDLER }
#else
# define NEARGYE_NOEXCEPT(...) noexcept(__VA_ARGS__)
# define NEARGYE_TRY
# define NEARGYE_CATCH
#endif
#define NEARGYE_MOV(...) static_cast<typename std::remove_reference<decltype(__VA_ARGS__)>::type&&>(__VA_ARGS__)
#define NEARGYE_FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
// NEARGYE_NODISCARD encourages the compiler to issue a warning if the return value is discarded.
#if !defined(NEARGYE_NODISCARD)
# if defined(__clang__)
# if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L
# define NEARGYE_NODISCARD [[nodiscard]]
# else
# define NEARGYE_NODISCARD __attribute__((__warn_unused_result__))
# endif
# elif defined(__GNUC__)
# if __GNUC__ >= 7 && __cplusplus >= 201703L
# define NEARGYE_NODISCARD [[nodiscard]]
# else
# define NEARGYE_NODISCARD __attribute__((__warn_unused_result__))
# endif
# elif defined(_MSC_VER)
# if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
# define NEARGYE_NODISCARD [[nodiscard]]
# elif defined(_Check_return_)
# define NEARGYE_NODISCARD _Check_return_
# else
# define NEARGYE_NODISCARD
# endif
# else
# define NEARGYE_NODISCARD
# endif
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
inline int uncaught_exceptions() noexcept {
return *(reinterpret_cast<int*>(static_cast<char*>(static_cast<void*>(_getptd())) + (sizeof(void*) == 8 ? 0x100 : 0x90)));
}
#elif (defined(__clang__) || defined(__GNUC__)) && __cplusplus < 201700L
struct __cxa_eh_globals;
extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
inline int uncaught_exceptions() noexcept {
return static_cast<int>(*(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxa_get_globals())) + sizeof(void*))));
}
#else
inline int uncaught_exceptions() noexcept {
return std::uncaught_exceptions();
}
#endif
class on_exit_policy {
bool execute_;
public:
explicit on_exit_policy(bool execute) noexcept : execute_{execute} {}
void dismiss() noexcept {
execute_ = false;
}
bool should_execute() const noexcept {
return execute_;
}
};
class on_fail_policy {
int ec_;
public:
explicit on_fail_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {}
void dismiss() noexcept {
ec_ = -1;
}
bool should_execute() const noexcept {
return ec_ != -1 && ec_ < uncaught_exceptions();
}
};
class on_success_policy {
int ec_;
public:
explicit on_success_policy(bool execute) noexcept : ec_{execute ? uncaught_exceptions() : -1} {}
void dismiss() noexcept {
ec_ = -1;
}
bool should_execute() const noexcept {
return ec_ != -1 && ec_ >= uncaught_exceptions();
}
};
template <typename T, typename = void>
struct is_noarg_returns_void_action
: std::false_type {};
template <typename T>
struct is_noarg_returns_void_action<T, decltype((std::declval<T>())())>
: std::true_type {};
template <typename T, bool = is_noarg_returns_void_action<T>::value>
struct is_nothrow_invocable_action
: std::false_type {};
template <typename T>
struct is_nothrow_invocable_action<T, true>
: std::integral_constant<bool, noexcept((std::declval<T>())())> {};
template <typename F, typename P>
class scope_guard {
using A = typename std::decay<F>::type;
static_assert(is_noarg_returns_void_action<A>::value,
"scope_guard requires no-argument action, that returns void.");
static_assert(std::is_same<P, on_exit_policy>::value || std::is_same<P, on_fail_policy>::value || std::is_same<P, on_success_policy>::value,
"scope_guard requires on_exit_policy, on_fail_policy or on_success_policy.");
#if defined(SCOPE_GUARD_NO_THROW_ACTION)
static_assert(is_nothrow_invocable_action<A>::value,
"scope_guard requires noexcept invocable action.");
#endif
#if defined(SCOPE_GUARD_NO_THROW_CONSTRUCTIBLE)
static_assert(std::is_nothrow_move_constructible<A>::value,
"scope_guard requires nothrow constructible action.");
#endif
P policy_;
A action_;
void* operator new(std::size_t) = delete;
void operator delete(void*) = delete;
public:
scope_guard() = delete;
scope_guard(const scope_guard&) = delete;
scope_guard& operator=(const scope_guard&) = delete;
scope_guard& operator=(scope_guard&&) = delete;
scope_guard(scope_guard&& other) noexcept(std::is_nothrow_move_constructible<A>::value)
: policy_{false},
action_{NEARGYE_MOV(other.action_)} {
policy_ = NEARGYE_MOV(other.policy_);
other.policy_.dismiss();
}
scope_guard(const A& action) = delete;
scope_guard(A& action) = delete;
explicit scope_guard(A&& action) noexcept(std::is_nothrow_move_constructible<A>::value)
: policy_{true},
action_{NEARGYE_MOV(action)} {}
void dismiss() noexcept {
policy_.dismiss();
}
~scope_guard() NEARGYE_NOEXCEPT(is_nothrow_invocable_action<A>::value) {
if (policy_.should_execute()) {
NEARGYE_TRY
action_();
NEARGYE_CATCH
}
}
};
template <typename F>
using scope_exit = scope_guard<F, on_exit_policy>;
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
NEARGYE_NODISCARD scope_exit<F> make_scope_exit(F&& action) noexcept(noexcept(scope_exit<F>{NEARGYE_FWD(action)})) {
return scope_exit<F>{NEARGYE_FWD(action)};
}
template <typename F>
using scope_fail = scope_guard<F, on_fail_policy>;
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
NEARGYE_NODISCARD scope_fail<F> make_scope_fail(F&& action) noexcept(noexcept(scope_fail<F>{NEARGYE_FWD(action)})) {
return scope_fail<F>{NEARGYE_FWD(action)};
}
template <typename F>
using scope_success = scope_guard<F, on_success_policy>;
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
NEARGYE_NODISCARD scope_success<F> make_scope_success(F&& action) noexcept(noexcept(scope_success<F>{NEARGYE_FWD(action)})) {
return scope_success<F>{NEARGYE_FWD(action)};
}
struct scope_exit_tag {};
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
scope_exit<F> operator<<(scope_exit_tag, F&& action) noexcept(noexcept(scope_exit<F>{NEARGYE_FWD(action)})) {
return scope_exit<F>{NEARGYE_FWD(action)};
}
struct scope_fail_tag {};
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
scope_fail<F> operator<<(scope_fail_tag, F&& action) noexcept(noexcept(scope_fail<F>{NEARGYE_FWD(action)})) {
return scope_fail<F>{NEARGYE_FWD(action)};
}
struct scope_success_tag {};
template <typename F, typename std::enable_if<is_noarg_returns_void_action<F>::value, int>::type = 0>
scope_success<F> operator<<(scope_success_tag, F&& action) noexcept(noexcept(scope_success<F>{NEARGYE_FWD(action)})) {
return scope_success<F>{NEARGYE_FWD(action)};
}
#undef NEARGYE_MOV
#undef NEARGYE_FWD
#undef NEARGYE_NOEXCEPT
#undef NEARGYE_TRY
#undef NEARGYE_CATCH
#undef NEARGYE_NODISCARD
} // namespace scope_guard::detail
using detail::make_scope_exit;
using detail::make_scope_fail;
using detail::make_scope_success;
} // namespace scope_guard
// NEARGYE_MAYBE_UNUSED suppresses compiler warnings on unused entities, if any.
#if !defined(NEARGYE_MAYBE_UNUSED)
# if defined(__clang__)
# if (__clang_major__ * 10 + __clang_minor__) >= 39 && __cplusplus >= 201703L
# define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
# else
# define NEARGYE_MAYBE_UNUSED __attribute__((__unused__))
# endif
# elif defined(__GNUC__)
# if __GNUC__ >= 7 && __cplusplus >= 201703L
# define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
# else
# define NEARGYE_MAYBE_UNUSED __attribute__((__unused__))
# endif
# elif defined(_MSC_VER)
# if _MSC_VER >= 1911 && defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
# define NEARGYE_MAYBE_UNUSED [[maybe_unused]]
# else
# define NEARGYE_MAYBE_UNUSED __pragma(warning(suppress : 4100 4101 4189))
# endif
# else
# define NEARGYE_MAYBE_UNUSED
# endif
#endif
#if !defined(NEARGYE_STR_CONCAT)
# define NEARGYE_STR_CONCAT_(s1, s2) s1##s2
# define NEARGYE_STR_CONCAT(s1, s2) NEARGYE_STR_CONCAT_(s1, s2)
#endif
#if !defined(NEARGYE_COUNTER)
# if defined(__COUNTER__)
# define NEARGYE_COUNTER __COUNTER__
# elif defined(__LINE__)
# define NEARGYE_COUNTER __LINE__
# endif
#endif
#if defined(SCOPE_GUARD_NO_THROW_ACTION)
# define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() noexcept -> void
#else
# define NEARGYE_MAKE_SCOPE_GUARD_ACTION [&]() -> void
#endif
#define NEARGYE_MAKE_SCOPE_EXIT ::scope_guard::detail::scope_exit_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
#define NEARGYE_MAKE_SCOPE_FAIL ::scope_guard::detail::scope_fail_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
#define NEARGYE_MAKE_SCOPE_SUCCESS ::scope_guard::detail::scope_success_tag{} << NEARGYE_MAKE_SCOPE_GUARD_ACTION
#define NEARGYE_SCOPE_GUARD_WITH_(g, i) for (int i = 1; i--; g)
#define NEARGYE_SCOPE_GUARD_WITH(g) NEARGYE_SCOPE_GUARD_WITH_(g, NEARGYE_STR_CONCAT(NEARGYE_INTERNAL_OBJECT_, NEARGYE_COUNTER))
// SCOPE_EXIT executing action on scope exit.
#define MAKE_SCOPE_EXIT(name) auto name = NEARGYE_MAKE_SCOPE_EXIT
#define SCOPE_EXIT NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_EXIT(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_EXIT_, NEARGYE_COUNTER))
#define WITH_SCOPE_EXIT(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_EXIT{ guard })
// SCOPE_FAIL executing action on scope exit when an exception has been thrown before scope exit.
#define MAKE_SCOPE_FAIL(name) auto name = NEARGYE_MAKE_SCOPE_FAIL
#define SCOPE_FAIL NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_FAIL(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_FAIL_, NEARGYE_COUNTER))
#define WITH_SCOPE_FAIL(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_FAIL{ guard })
// SCOPE_SUCCESS executing action on scope exit when no exceptions have been thrown before scope exit.
#define MAKE_SCOPE_SUCCESS(name) auto name = NEARGYE_MAKE_SCOPE_SUCCESS
#define SCOPE_SUCCESS NEARGYE_MAYBE_UNUSED const MAKE_SCOPE_SUCCESS(NEARGYE_STR_CONCAT(NEARGYE_SCOPE_SUCCESS_, NEARGYE_COUNTER))
#define WITH_SCOPE_SUCCESS(guard) NEARGYE_SCOPE_GUARD_WITH(NEARGYE_MAKE_SCOPE_SUCCESS{ guard })
// DEFER executing action on scope exit.
#define MAKE_DEFER(name) MAKE_SCOPE_EXIT(name)
#define DEFER SCOPE_EXIT
#define WITH_DEFER(guard) WITH_SCOPE_EXIT(guard)
#endif // NEARGYE_SCOPE_GUARD_HPP