Commit | Line | Data |
---|---|---|
54b65c9b PA |
1 | /* Copyright (C) 2019 Free Software Foundation, Inc. |
2 | ||
3 | This file is part of GDB. | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | #ifndef COMMON_SCOPE_EXIT_H | |
19 | #define COMMON_SCOPE_EXIT_H | |
20 | ||
21 | #include <functional> | |
22 | #include <type_traits> | |
23 | #include "common/preprocessor.h" | |
24 | ||
25 | /* scope_exit is a general-purpose scope guard that calls its exit | |
26 | function at the end of the current scope. A scope_exit may be | |
27 | canceled by calling the "release" method. The API is modeled on | |
28 | P0052R5 - Generic Scope Guard and RAII Wrapper for the Standard | |
29 | Library, which is itself based on Andrej Alexandrescu's | |
30 | ScopeGuard/SCOPE_EXIT. | |
31 | ||
32 | There are two forms available: | |
33 | ||
34 | - The "make_scope_exit" form allows canceling the scope guard. Use | |
35 | it like this: | |
36 | ||
37 | auto cleanup = make_scope_exit ( <function, function object, lambda> ); | |
38 | ... | |
39 | cleanup.release (); // cancel | |
40 | ||
41 | - If you don't need to cancel the guard, you can use the SCOPE_EXIT | |
42 | macro, like this: | |
43 | ||
44 | SCOPE_EXIT | |
45 | { | |
46 | // any code you like here. | |
47 | } | |
48 | ||
49 | See also forward_scope_exit. | |
50 | */ | |
51 | ||
52 | /* CRTP base class for cancelable scope_exit-like classes. Implements | |
53 | the common call-custom-function-from-dtor functionality. Classes | |
54 | that inherit this implement the on_exit() method, which is called | |
55 | from scope_exit_base's dtor. */ | |
56 | ||
57 | template <typename CRTP> | |
58 | class scope_exit_base | |
59 | { | |
60 | public: | |
61 | scope_exit_base () = default; | |
62 | ||
63 | ~scope_exit_base () | |
64 | { | |
65 | if (!m_released) | |
66 | { | |
67 | auto *self = static_cast<CRTP *> (this); | |
68 | self->on_exit (); | |
69 | } | |
70 | } | |
71 | ||
72 | /* This is needed for make_scope_exit because copy elision isn't | |
73 | guaranteed until C++17. An optimizing compiler will usually skip | |
74 | calling this, but it must exist. */ | |
75 | scope_exit_base (const scope_exit_base &other) | |
76 | : m_released (other.m_released) | |
77 | { | |
78 | other.m_released = true; | |
79 | } | |
80 | ||
81 | void operator= (const scope_exit_base &) = delete; | |
82 | ||
83 | /* If this is called, then the wrapped function will not be called | |
84 | on destruction. */ | |
85 | void release () noexcept | |
86 | { | |
87 | m_released = true; | |
88 | } | |
89 | ||
90 | private: | |
91 | ||
92 | /* True if released. Mutable because of the copy ctor hack | |
93 | above. */ | |
94 | mutable bool m_released = false; | |
95 | }; | |
96 | ||
97 | /* The scope_exit class. */ | |
98 | ||
99 | template<typename EF> | |
100 | class scope_exit : public scope_exit_base<scope_exit<EF>> | |
101 | { | |
102 | /* For access to on_exit(). */ | |
103 | friend scope_exit_base<scope_exit<EF>>; | |
104 | ||
105 | public: | |
106 | ||
107 | template<typename EFP, | |
108 | typename = gdb::Requires<std::is_constructible<EF, EFP>>> | |
109 | scope_exit (EFP &&f) | |
110 | try : m_exit_function ((!std::is_lvalue_reference<EFP>::value | |
111 | && std::is_nothrow_constructible<EF, EFP>::value) | |
112 | ? std::move (f) | |
113 | : f) | |
114 | { | |
115 | } | |
116 | catch (...) | |
117 | { | |
118 | /* "If the initialization of exit_function throws an exception, | |
119 | calls f()." */ | |
120 | f (); | |
121 | } | |
122 | ||
123 | template<typename EFP, | |
124 | typename = gdb::Requires<std::is_constructible<EF, EFP>>> | |
125 | scope_exit (scope_exit &&rhs) | |
126 | noexcept (std::is_nothrow_move_constructible<EF>::value | |
127 | || std::is_nothrow_copy_constructible<EF>::value) | |
128 | : m_exit_function (std::is_nothrow_constructible<EFP>::value | |
129 | ? std::move (rhs) | |
130 | : rhs) | |
131 | { | |
132 | rhs.release (); | |
133 | } | |
134 | ||
135 | /* This is needed for make_scope_exit because copy elision isn't | |
136 | guaranteed until C++17. An optimizing compiler will usually skip | |
137 | calling this, but it must exist. */ | |
138 | scope_exit (const scope_exit &other) | |
139 | : scope_exit_base<scope_exit<EF>> (other), | |
140 | m_exit_function (other.m_exit_function) | |
141 | { | |
142 | } | |
143 | ||
144 | void operator= (const scope_exit &) = delete; | |
145 | void operator= (scope_exit &&) = delete; | |
146 | ||
147 | private: | |
148 | void on_exit () | |
149 | { | |
150 | m_exit_function (); | |
151 | } | |
152 | ||
153 | /* The function to call on scope exit. */ | |
154 | EF m_exit_function; | |
155 | }; | |
156 | ||
157 | template <typename EF> | |
158 | scope_exit<typename std::decay<EF>::type> | |
159 | make_scope_exit (EF &&f) | |
160 | { | |
161 | return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); | |
162 | } | |
163 | ||
164 | namespace detail | |
165 | { | |
166 | ||
167 | enum class scope_exit_lhs {}; | |
168 | ||
169 | template<typename EF> | |
170 | scope_exit<typename std::decay<EF>::type> | |
171 | operator+ (scope_exit_lhs, EF &&rhs) | |
172 | { | |
173 | return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs)); | |
174 | } | |
175 | ||
176 | } | |
177 | ||
178 | /* Register a block of code to run on scope exit. Note that the local | |
179 | context is captured by reference, which means you should be careful | |
180 | to avoid inadvertently changing a captured local's value before the | |
181 | scope exit runs. */ | |
182 | ||
183 | #define SCOPE_EXIT \ | |
184 | auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] () | |
185 | ||
186 | #endif /* COMMON_SCOPE_EXIT_H */ |