scope-exit.h revision 1.1.1.1
1/* Copyright (C) 2019-2020 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 57template <typename CRTP> 58class scope_exit_base 59{ 60public: 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 90private: 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 99template<typename EF> 100class 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 105public: 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 147private: 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 157template <typename EF> 158scope_exit<typename std::decay<EF>::type> 159make_scope_exit (EF &&f) 160{ 161 return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); 162} 163 164namespace detail 165{ 166 167enum class scope_exit_lhs {}; 168 169template<typename EF> 170scope_exit<typename std::decay<EF>::type> 171operator+ (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 */ 187