-
Notifications
You must be signed in to change notification settings - Fork 1
/
scope_guard.hpp
98 lines (86 loc) · 2.75 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
/** @file Defines the scope guard macros @c CU_SCOPE_EXIT, @c CU_SCOPE_FAIL
* and @c CU_SCOPE_SUCCESS.
*
* These macros ensure, that certain statements are executed at the end of
* the scope. This can be used for clean-up functions.
*
* These avoid user-defined RAII types and try catch block code
* cluttering and provide good ways to accomplish exception-safety
* and especially the strong exception guarantee.
*
* With scope guards, the code
* @code
* allocate(p);
* try
* {
* doSomething(p);
* }
* catch(...)
* {
* deallocate(p);
* throw;
* }
* deallocate(p);
* @endcode
* can be rewritten to
* @code
* allocate(p);
* CU_SCOPE_EXIT { deallocate(p); }; // note the semicolon!
* doSomething(p);
* @endcode
* Behind the scenes @c SCOPE_EXIT creates a temporary variable that calls
* @c deallocate(p) in its destructor. This technique has several advantages:
* 1. It clarifies intent.
* 2. Resource acquisition and release are in one place.
* 3. It avoids a deeply nested control flow.
* Other than CU_SCOPE_EXIT, there is also the macro @c CU_SCOPE_FAIL with
* the same syntax, but the code block behind the macro is only executed,
* if the scope is left with an exception in flight. For completeness also
* @c CU_SCOPE_SUCCESS is provided which calls the codeblock on scope exit
* only in the case that no exception is in flight.
*
* @author Ralph Tandetzky
*/
#pragma once
#include <exception>
#include <utility>
namespace cu
{
#define CU_DETAIL_SCOPE_GUARD_CONCAT(a,b) CU_DETAIL_SCOPE_GUARD_CONCAT2(a,b)
#define CU_DETAIL_SCOPE_GUARD_CONCAT2(a,b) a##b
#define CU_DETAIL_SCOPE_GUARD_IMPL(Tag) \
const auto CU_DETAIL_SCOPE_GUARD_CONCAT(scopeGuardVarName, __COUNTER__) = \
::cu::detail::ScopeGuardImpl<::cu::detail::Tag>() += [&]
#define CU_SCOPE_EXIT CU_DETAIL_SCOPE_GUARD_IMPL(OnExitTag )
#define CU_SCOPE_FAIL CU_DETAIL_SCOPE_GUARD_IMPL(OnFailTag )
#define CU_SCOPE_SUCCESS CU_DETAIL_SCOPE_GUARD_IMPL(OnSuccessTag)
namespace detail
{
struct OnExitTag {};
struct OnFailTag {};
struct OnSuccessTag {};
inline bool shallExecuteScopeGuard( OnExitTag ) { return true ; }
inline bool shallExecuteScopeGuard( OnFailTag ) { return std::uncaught_exception(); }
inline bool shallExecuteScopeGuard( OnSuccessTag ) { return !std::uncaught_exception(); }
template <typename Tag>
class ScopeGuardImpl
{
template <typename F>
struct Type
{
~Type()
{
if ( shallExecuteScopeGuard( Tag() ) )
f();
}
F f;
};
public:
template <typename F>
Type<F> operator+=( F && f )
{
return {std::forward<F>(f)};
}
};
} // namespace detail
} // namespace cu