1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 1993-2021 The Octave Project Developers 4 // 5 // See the file COPYRIGHT.md in the top-level directory of this 6 // distribution or <https://octave.org/copyright/>. 7 // 8 // This file is part of Octave. 9 // 10 // Octave is free software: you can redistribute it and/or modify it 11 // under the terms of the GNU General Public License as published by 12 // the Free Software Foundation, either version 3 of the License, or 13 // (at your option) any later version. 14 // 15 // Octave is distributed in the hope that it will be useful, but 16 // WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Octave; see the file COPYING. If not, see 22 // <https://www.gnu.org/licenses/>. 23 // 24 //////////////////////////////////////////////////////////////////////// 25 26 #if ! defined (octave_unwind_prot_h) 27 #define octave_unwind_prot_h 1 28 29 #include "octave-config.h" 30 31 #include <cstddef> 32 33 #include <stack> 34 #include <memory> 35 36 #include "action-container.h" 37 38 namespace octave 39 { 40 class 41 OCTAVE_API 42 unwind_protect : public action_container 43 { 44 public: 45 unwind_protect(void)46 unwind_protect (void) : m_lifo () { } 47 48 // No copying! 49 50 unwind_protect (const unwind_protect&) = delete; 51 52 unwind_protect& operator = (const unwind_protect&) = delete; 53 54 // Destructor should not raise an exception, so all actions 55 // registered should be exception-safe. If you're not sure, see 56 // unwind_protect_safe. 57 ~unwind_protect(void)58 ~unwind_protect (void) { run (); } 59 60 operator bool (void) const { return ! empty (); } 61 run_first(void)62 void run_first (void) 63 { 64 if (! empty ()) 65 { 66 // No leak on exception! 67 std::unique_ptr<elem> ptr (m_lifo.top ()); 68 m_lifo.pop (); 69 ptr->run (); 70 } 71 } 72 discard_first(void)73 void discard_first (void) 74 { 75 if (! empty ()) 76 { 77 elem *ptr = m_lifo.top (); 78 m_lifo.pop (); 79 delete ptr; 80 } 81 } 82 size(void)83 std::size_t size (void) const { return m_lifo.size (); } 84 85 protected: 86 add_action(elem * new_elem)87 virtual void add_action (elem *new_elem) 88 { 89 m_lifo.push (new_elem); 90 } 91 92 std::stack<elem *> m_lifo; 93 }; 94 95 // Like unwind_protect, but this one will guard against the possibility 96 // of seeing an exception (or interrupt) in the cleanup actions. 97 // Not that we can do much about it, but at least we won't crash. 98 99 class 100 OCTAVE_API 101 unwind_protect_safe : public unwind_protect 102 { 103 private: 104 105 void warn_unhandled_exception (void) const; 106 107 public: 108 unwind_protect_safe(void)109 unwind_protect_safe (void) : unwind_protect () { } 110 111 // No copying! 112 113 unwind_protect_safe (const unwind_protect_safe&) = delete; 114 115 unwind_protect_safe& operator = (const unwind_protect_safe&) = delete; 116 ~unwind_protect_safe(void)117 ~unwind_protect_safe (void) 118 { 119 while (! empty ()) 120 { 121 try 122 { 123 run_first (); 124 } 125 catch (...) // Yes, the black hole. Remember we're in a destructor. 126 { 127 warn_unhandled_exception (); 128 } 129 } 130 } 131 }; 132 133 // In most cases, the following are preferred for efficiency. Some 134 // cases may require the flexibility of the general unwind_protect 135 // mechanism defined above. 136 137 // Perform action at end of the current scope when unwind_action 138 // object destructor is called. 139 // 140 // For example: 141 // 142 // void fcn (int val) { ... } 143 // 144 // ... 145 // 146 // { 147 // int val = 42; 148 // 149 // // template parameters, std::bind and std::function provide 150 // // flexibility in calling forms: 151 // 152 // unwind_action act1 (fcn, val); 153 // unwind_action act2 ([val] (void) { fcn (val); }); 154 // } 155 // 156 // NOTE: Don't forget to provide a name for the unwind_action 157 // variable. If you write 158 // 159 // unwind_action /* NO NAME! */ (...); 160 // 161 // then the destructor for the temporary anonymous object will be 162 // called immediately after the object is constructed instead of at 163 // the end of the current scope. 164 165 class unwind_action 166 { 167 public: 168 169 template <typename F, typename... Args> unwind_action(F && fcn,Args &&...args)170 unwind_action (F&& fcn, Args&&... args) 171 : m_fcn (std::bind (fcn, args...)) 172 { } 173 174 // No copying! 175 176 unwind_action (const unwind_action&) = delete; 177 178 unwind_action& operator = (const unwind_action&) = delete; 179 ~unwind_action(void)180 ~unwind_action (void) { m_fcn (); } 181 182 private: 183 184 std::function<void (void)> m_fcn; 185 }; 186 187 // Reset a variable value at the end of the current scope when 188 // unwind_protect_var object destructor is called. 189 // 190 // For example: 191 // 192 // { 193 // int x = 42; 194 // unwind_protect_var<int> upv (x); // X will be reset at end of scope 195 // x = 13; // Set temporary value. 196 // } 197 // 198 // Temporary value may be set at construction: 199 // 200 // { 201 // int x = ...; 202 // unwind_protect_var<int> upv (x, 13); // X will be reset. 203 // // temporary value is 13. 204 // } 205 // 206 // NOTE: Don't forget to provide a name for the unwind_protect_var 207 // variable. If you write 208 // 209 // unwind_protect_var<type> /* NO NAME! */ (...); 210 // 211 // then the destructor for the temporary anonymous object will be 212 // called immediately after the object is constructed instead of at 213 // the end of the current scope. 214 // 215 // FIXME: Once we are able to use C++17, class template argument 216 // deduction will allow us to omit the explicit template type from the 217 // constructor expression: 218 // 219 // unwind_protect_var upv (...); 220 221 template <typename T> 222 class unwind_protect_var 223 { 224 public: 225 226 // Ensure that the value referenced by REF will be reset when this 227 // unwind_protect_var object goes out of scope. 228 unwind_protect_var(T & ref)229 explicit unwind_protect_var (T& ref) 230 : m_ref (ref), m_val (ref) 231 { } 232 233 // Set the value referenced by REF to NEW_VAL and ensure that it 234 // will be reset to its original value when this 235 // unwind_protect_var object goes out of scope. 236 unwind_protect_var(T & ref,const T & new_val)237 unwind_protect_var (T& ref, const T& new_val) 238 : m_ref (ref), m_val (ref) 239 { 240 m_ref = new_val; 241 } 242 243 // No copying! 244 245 unwind_protect_var (const unwind_protect_var&) = delete; 246 247 unwind_protect_var& operator = (const unwind_protect_var&) = delete; 248 ~unwind_protect_var(void)249 ~unwind_protect_var (void) 250 { 251 m_ref = m_val; 252 } 253 254 private: 255 256 T& m_ref; 257 T m_val; 258 }; 259 } 260 261 #endif 262