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
/*
* This file is part of the source code of the software program
* Vampire. It is protected by applicable
* copyright laws.
*
* This source code is distributed under the licence found here
* https://vprover.github.io/license.html
* and in the source directory
*/
#ifndef SCOPEGUARD_HPP
#define SCOPEGUARD_HPP
#include <exception>
#include <utility>
namespace Lib
{
/// Calls the given function when the ScopeGuard goes out of scope (i.e., is destructed).
/// The given function is called exactly once.
template <typename Callable>
class ScopeGuard final
{
public:
explicit ScopeGuard(Callable&& f)
: active{true}
, f(std::forward<Callable>(f))
{ }
ScopeGuard() = delete;
// Disallow copy
ScopeGuard(ScopeGuard const&) = delete;
ScopeGuard& operator=(ScopeGuard const&) = delete;
ScopeGuard(ScopeGuard&& other)
: active{std::exchange(other.active, false)}
, f(std::move(other.f))
{ }
// Disallow moving into a ScopeGuard.
// The reason is that if the LHS is still active, we should call execute() before moving
// (to uphold the guarantee that f() is called exactly once).
// However, this behaviour is somewhat unexpected for the user since it is not the point
// where the guard goes out of scope.
ScopeGuard& operator=(ScopeGuard&& other) = delete;
// noexcept clause: throws an exception if Callable::operator() throws
~ScopeGuard() noexcept(noexcept(std::declval<Callable>()))
{
if (active) {
execute();
}
}
private:
void execute() noexcept(noexcept(std::declval<Callable>()))
{
active = false;
if (!stackUnwindingInProgress()) {
// ~ScopeGuard called normally
f();
} else {
// ~ScopeGuard called during stack unwinding (this means we must not throw an exception)
try {
f();
}
catch(...) {
/* do nothing */
// TODO
// It's bad to just swallow exceptions silently, but if it's just some not-so-important cleanup function we might not care.
// For more important cases, maybe we should add a parameter that controls what to do in this case: terminate, ignore, something else???
// std::terminate();
#if VDEBUG
// In debug mode, just terminate so we notice there's a problem.
std::cerr << "Exception in ~ScopeGuard during stack unwinding!" << std::endl;
ASSERTION_VIOLATION;
#endif
}
}
}
private:
bool active;
Callable f;
int exception_count = std::uncaught_exceptions();
bool stackUnwindingInProgress() const {
return exception_count != std::uncaught_exceptions();
}
};
#define ON_SCOPE_EXIT_CONCAT_HELPER(X,Y) X ## Y
#define ON_SCOPE_EXIT_CONCAT(X,Y) ON_SCOPE_EXIT_CONCAT_HELPER(X,Y)
#define ON_SCOPE_EXIT(stmt) \
ScopeGuard ON_SCOPE_EXIT_CONCAT(on_scope_exit_guard_on_line_,__LINE__){[&]() { stmt; }};
}
#endif /* !SCOPEGUARD_HPP */