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 COMMON_SCOPE_EXIT_H 19 #define COMMON_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 } 113 114 template<typename EFP, 115 typename = gdb::Requires<std::is_constructible<EF, EFP>>> 116 scope_exit (scope_exit &&rhs) 117 noexcept (std::is_nothrow_move_constructible<EF>::value 118 || std::is_nothrow_copy_constructible<EF>::value) 119 : m_exit_function (std::is_nothrow_constructible<EFP>::value 120 ? std::move (rhs) 121 : rhs) 122 { 123 rhs.release (); 124 } 125 126 DISABLE_COPY_AND_ASSIGN (scope_exit); 127 void operator= (scope_exit &&) = delete; 128 129 private: 130 void on_exit () 131 { 132 m_exit_function (); 133 } 134 135 /* The function to call on scope exit. */ 136 EF m_exit_function; 137 }; 138 139 template <typename EF> 140 scope_exit<typename std::decay<EF>::type> 141 make_scope_exit (EF &&f) 142 { 143 return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); 144 } 145 146 namespace detail 147 { 148 149 enum class scope_exit_lhs {}; 150 151 template<typename EF> 152 scope_exit<typename std::decay<EF>::type> 153 operator+ (scope_exit_lhs, EF &&rhs) 154 { 155 return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs)); 156 } 157 158 } 159 160 /* Register a block of code to run on scope exit. Note that the local 161 context is captured by reference, which means you should be careful 162 to avoid inadvertently changing a captured local's value before the 163 scope exit runs. */ 164 165 #define SCOPE_EXIT \ 166 auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] () 167 168 #endif /* COMMON_SCOPE_EXIT_H */ 169