This repository was archived by the owner on Oct 17, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathstate_saver.hpp
320 lines (270 loc) · 12.1 KB
/
state_saver.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
// _____ _ _ _____ _____
// / ____| | | | / ____| / ____|_ _
// | (___ | |_ __ _| |_ ___ | (___ __ ___ _____ _ __ | | _| |_ _| |_
// \___ \| __/ _` | __/ _ \ \___ \ / _` \ \ / / _ \ '__| | | |_ _|_ _|
// ____) | || (_| | || __/ ____) | (_| |\ V / __/ | | |____|_| |_|
// |_____/ \__\__,_|\__\___| |_____/ \__,_| \_/ \___|_| \_____|
// https://github.com/Neargye/state_saver
// version 0.9.1
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2018 - 2021 Daniil Goncharov <[email protected]>.
//
// 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_STATE_SAVER_HPP
#define NEARGYE_STATE_SAVER_HPP
#define STATE_SAVER_VERSION_MAJOR 0
#define STATE_SAVER_VERSION_MINOR 9
#define STATE_SAVER_VERSION_PATCH 1
#include <type_traits>
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || ((defined(__clang__) || defined(__GNUC__)) && __cplusplus >= 201700L)
#include <exception>
#endif
// state_saver throwable settings:
// STATE_SAVER_NO_THROW_CONSTRUCTIBLE requires nothrow constructible action.
// STATE_SAVER_MAY_THROW_RESTORE restore may throw exceptions.
// STATE_SAVER_NO_THROW_RESTORE requires noexcept restore.
// STATE_SAVER_SUPPRESS_THROW_RESTORE exceptions during restore will be suppressed.
// STATE_SAVER_CATCH_HANDLER exceptions handler. If STATE_SAVER_SUPPRESS_THROW_RESTORE is not defined, it will do nothing.
// state_saver assignable settings:
// STATE_SAVER_FORCE_MOVE_ASSIGNABLE restore on scope exit will be move assigned.
// STATE_SAVER_FORCE_COPY_ASSIGNABLE restore on scope exit will be copy assigned.
#if !defined(STATE_SAVER_MAY_THROW_RESTORE) && !defined(STATE_SAVER_NO_THROW_RESTORE) && !defined(STATE_SAVER_SUPPRESS_THROW_RESTORE)
# define STATE_SAVER_MAY_THROW_RESTORE
#elif (defined(STATE_SAVER_MAY_THROW_RESTORE) + defined(STATE_SAVER_NO_THROW_RESTORE) + defined(STATE_SAVER_SUPPRESS_THROW_RESTORE)) > 1
# error Only one of STATE_SAVER_MAY_THROW_RESTORE and STATE_SAVER_NO_THROW_RESTORE and STATE_SAVER_SUPPRESS_THROW_RESTORE may be defined.
#endif
#if (defined(STATE_SAVER_FORCE_MOVE_ASSIGNABLE) + defined(STATE_SAVER_FORCE_COPY_ASSIGNABLE)) > 1
# error Only one of STATE_SAVER_FORCE_MOVE_ASSIGNABLE and STATE_SAVER_FORCE_COPY_ASSIGNABLE may be defined.
#endif
#if !defined(STATE_SAVER_CATCH_HANDLER)
# define STATE_SAVER_CATCH_HANDLER /* Suppress exception.*/
#endif
namespace state_saver {
namespace detail {
#if defined(STATE_SAVER_SUPPRESS_THROW_RESTORE) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
# 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
#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 U, typename P>
class state_saver {
using T = typename std::remove_reference<U>::type;
#if defined(STATE_SAVER_FORCE_MOVE_ASSIGNABLE)
using assignable_t = T&&;
#elif defined(STATE_SAVER_FORCE_COPY_ASSIGNABLE)
using assignable_t = T&;
#else
using assignable_t = typename std::conditional<
std::is_nothrow_assignable<T&, T&&>::value ||
!std::is_assignable<T&, T&>::value ||
(!std::is_nothrow_assignable<T&, T&>::value && std::is_assignable<T&, T&&>::value),
T&&, T&>::type;
#endif
static_assert(!std::is_const<T>::value,
"state_saver requires not const type.");
static_assert(!std::is_rvalue_reference<U>::value && (std::is_lvalue_reference<U>::value || std::is_same<T, U>::value),
"state_saver requires lvalue type.");
static_assert(!std::is_array<T>::value,
"state_saver requires not array type.");
static_assert(!std::is_pointer<T>::value,
"state_saver requires not pointer type.");
static_assert(!std::is_function<T>::value,
"state_saver requires not function type.");
static_assert(std::is_constructible<T, T&>::value,
"state_saver requires copy constructible.");
static_assert(std::is_assignable<T&, assignable_t>::value,
"state_saver requires operator=.");
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,
"state_saver requires on_exit_policy, on_fail_policy or on_success_policy.");
#if defined(STATE_SAVER_NO_THROW_RESTORE)
static_assert(std::is_nothrow_assignable<T&, assignable_t>::value,
"state_saver requires noexcept operator=.");
#endif
#if defined(STATE_SAVER_NO_THROW_CONSTRUCTIBLE)
static_assert(std::is_nothrow_constructible<T, T&>::value,
"state_saver requires nothrow constructible.");
#endif
P policy_;
T& previous_ref_;
T previous_value_;
public:
state_saver() = delete;
state_saver(const state_saver&) = delete;
state_saver(state_saver&&) = delete;
state_saver& operator=(const state_saver&) = delete;
state_saver& operator=(state_saver&&) = delete;
state_saver(T&&) = delete;
state_saver(const T&) = delete;
explicit state_saver(T& object) noexcept(std::is_nothrow_constructible<T, T&>::value)
: policy_{true},
previous_ref_{object},
previous_value_{object} {}
void dismiss() noexcept {
policy_.dismiss();
}
template <typename O = T>
auto restore() NEARGYE_NOEXCEPT(std::is_nothrow_assignable<O&, O&>::value) -> typename std::enable_if<std::is_same<T, O>::value && std::is_assignable<O&, O&>::value>::type {
static_assert(std::is_assignable<O&, O&>::value, "state_saver::restore requires copy operator=.");
#if defined(STATE_SAVER_NO_THROW_RESTORE)
static_assert(std::is_nothrow_assignable<O&, O&>::value, "state_saver::restore requires noexcept copy operator=.");
#endif
NEARGYE_TRY
previous_ref_ = previous_value_;
NEARGYE_CATCH
}
~state_saver() NEARGYE_NOEXCEPT(std::is_nothrow_assignable<T&, assignable_t>::value) {
if (policy_.should_execute()) {
NEARGYE_TRY
previous_ref_ = static_cast<assignable_t>(previous_value_);
NEARGYE_CATCH
}
}
};
#undef NEARGYE_NOEXCEPT
#undef NEARGYE_TRY
#undef NEARGYE_CATCH
} // namespace state_saver::detail
template <typename U>
class saver_exit : public detail::state_saver<U, detail::on_exit_policy> {
public:
using detail::state_saver<U, detail::on_exit_policy>::state_saver;
};
template <typename U>
class saver_fail : public detail::state_saver<U, detail::on_fail_policy> {
public:
using detail::state_saver<U, detail::on_fail_policy>::state_saver;
};
template <typename U>
class saver_success : public detail::state_saver<U, detail::on_success_policy> {
public:
using detail::state_saver<U, detail::on_success_policy>::state_saver;
};
#if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201611L
template <typename U>
saver_exit(U&) -> saver_exit<U>;
template <typename U>
saver_fail(U&) -> saver_fail<U>;
template <typename U>
saver_success(U&) -> saver_success<U>;
#endif
} // namespace state_saver
// 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
#define NEARGYE_STATE_SAVER_WITH_(s, i) for (int i = 1; i--; s)
#define NEARGYE_STATE_SAVER_WITH(s) NEARGYE_STATE_SAVER_WITH_(s, NEARGYE_STR_CONCAT(NEARGYE_INTERNAL_OBJECT_, NEARGYE_COUNTER))
// SAVER_EXIT saves the original variable value and restores on scope exit.
#define MAKE_SAVER_EXIT(name, x) ::state_saver::saver_exit<decltype(x)> name{x}
#define SAVER_EXIT(x) NEARGYE_MAYBE_UNUSED const MAKE_SAVER_EXIT(NEARGYE_STR_CONCAT(SAVER_EXIT_, NEARGYE_COUNTER), x)
#define WITH_SAVER_EXIT(x) NEARGYE_STATE_SAVER_WITH(::state_saver::saver_exit<decltype(x)>{x})
// SAVER_FAIL saves the original variable value and restores on scope exit when an exception has been thrown.
#define MAKE_SAVER_FAIL(name, x) ::state_saver::saver_fail<decltype(x)> name{x}
#define SAVER_FAIL(x) NEARGYE_MAYBE_UNUSED const MAKE_SAVER_FAIL(NEARGYE_STR_CONCAT(SAVER_FAIL_, NEARGYE_COUNTER), x)
#define WITH_SAVER_FAIL(x) NEARGYE_STATE_SAVER_WITH(::state_saver::saver_fail<decltype(x)>{x})
// SAVER_SUCCESS saves the original variable value and restores on scope exit when no exceptions have been thrown.
#define MAKE_SAVER_SUCCESS(name, x) ::state_saver::saver_success<decltype(x)> name{x}
#define SAVER_SUCCESS(x) NEARGYE_MAYBE_UNUSED const MAKE_SAVER_SUCCESS(NEARGYE_STR_CONCAT(SAVER_SUCCES_, NEARGYE_COUNTER), x)
#define WITH_SAVER_SUCCESS(x) NEARGYE_STATE_SAVER_WITH(::state_saver::saver_success<decltype(x)>{x})
#endif // NEARGYE_STATE_SAVER_HPP