1 /* Copyright (C) 2019-2024 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 GDBSUPPORT_SCOPE_EXIT_H 19 #define GDBSUPPORT_SCOPE_EXIT_H 20 21 #include <functional> 22 #include <type_traits> 23 #include "gdbsupport/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 DISABLE_COPY_AND_ASSIGN (scope_exit_base); 73 74 /* If this is called, then the wrapped function will not be called 75 on destruction. */ 76 void release () noexcept 77 { 78 m_released = true; 79 } 80 81 private: 82 83 /* True if released. Mutable because of the copy ctor hack 84 above. */ 85 mutable bool m_released = false; 86 }; 87 88 /* The scope_exit class. */ 89 90 template<typename EF> 91 class scope_exit : public scope_exit_base<scope_exit<EF>> 92 { 93 /* For access to on_exit(). */ 94 friend scope_exit_base<scope_exit<EF>>; 95 96 public: 97 98 template<typename EFP, 99 typename = gdb::Requires<std::is_constructible<EF, EFP>>> 100 scope_exit (EFP &&f) 101 try : m_exit_function ((!std::is_lvalue_reference<EFP>::value 102 && std::is_nothrow_constructible<EF, EFP>::value) 103 ? std::move (f) 104 : f) 105 { 106 } 107 catch (...) 108 { 109 /* "If the initialization of exit_function throws an exception, 110 calls f()." */ 111 f (); 112 /* "throws: Nothing, unless the initialization of exit_function 113 throws." */ 114 throw; 115 } 116 117 template<typename EFP, 118 typename = gdb::Requires<std::is_constructible<EF, EFP>>> 119 scope_exit (scope_exit &&rhs) 120 noexcept (std::is_nothrow_move_constructible<EF>::value 121 || std::is_nothrow_copy_constructible<EF>::value) 122 : m_exit_function (std::is_nothrow_constructible<EFP>::value 123 ? std::move (rhs) 124 : rhs) 125 { 126 rhs.release (); 127 } 128 129 DISABLE_COPY_AND_ASSIGN (scope_exit); 130 void operator= (scope_exit &&) = delete; 131 132 private: 133 void on_exit () 134 { 135 m_exit_function (); 136 } 137 138 /* The function to call on scope exit. */ 139 EF m_exit_function; 140 }; 141 142 template <typename EF> 143 scope_exit<typename std::decay<EF>::type> 144 make_scope_exit (EF &&f) 145 { 146 return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); 147 } 148 149 namespace detail 150 { 151 152 enum class scope_exit_lhs {}; 153 154 template<typename EF> 155 scope_exit<typename std::decay<EF>::type> 156 operator+ (scope_exit_lhs, EF &&rhs) 157 { 158 return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs)); 159 } 160 161 } 162 163 /* Register a block of code to run on scope exit. Note that the local 164 context is captured by reference, which means you should be careful 165 to avoid inadvertently changing a captured local's value before the 166 scope exit runs. */ 167 168 #define SCOPE_EXIT \ 169 auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] () 170 171 #endif /* GDBSUPPORT_SCOPE_EXIT_H */ 172