1*6881a400Schristos /* Copyright (C) 2019-2023 Free Software Foundation, Inc. 27d62b00eSchristos 37d62b00eSchristos This file is part of GDB. 47d62b00eSchristos 57d62b00eSchristos This program is free software; you can redistribute it and/or modify 67d62b00eSchristos it under the terms of the GNU General Public License as published by 77d62b00eSchristos the Free Software Foundation; either version 3 of the License, or 87d62b00eSchristos (at your option) any later version. 97d62b00eSchristos 107d62b00eSchristos This program is distributed in the hope that it will be useful, 117d62b00eSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of 127d62b00eSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 137d62b00eSchristos GNU General Public License for more details. 147d62b00eSchristos 157d62b00eSchristos You should have received a copy of the GNU General Public License 167d62b00eSchristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 177d62b00eSchristos 187d62b00eSchristos #ifndef COMMON_SCOPE_EXIT_H 197d62b00eSchristos #define COMMON_SCOPE_EXIT_H 207d62b00eSchristos 217d62b00eSchristos #include <functional> 227d62b00eSchristos #include <type_traits> 237d62b00eSchristos #include "gdbsupport/preprocessor.h" 247d62b00eSchristos 257d62b00eSchristos /* scope_exit is a general-purpose scope guard that calls its exit 267d62b00eSchristos function at the end of the current scope. A scope_exit may be 277d62b00eSchristos canceled by calling the "release" method. The API is modeled on 287d62b00eSchristos P0052R5 - Generic Scope Guard and RAII Wrapper for the Standard 297d62b00eSchristos Library, which is itself based on Andrej Alexandrescu's 307d62b00eSchristos ScopeGuard/SCOPE_EXIT. 317d62b00eSchristos 327d62b00eSchristos There are two forms available: 337d62b00eSchristos 347d62b00eSchristos - The "make_scope_exit" form allows canceling the scope guard. Use 357d62b00eSchristos it like this: 367d62b00eSchristos 377d62b00eSchristos auto cleanup = make_scope_exit ( <function, function object, lambda> ); 387d62b00eSchristos ... 397d62b00eSchristos cleanup.release (); // cancel 407d62b00eSchristos 417d62b00eSchristos - If you don't need to cancel the guard, you can use the SCOPE_EXIT 427d62b00eSchristos macro, like this: 437d62b00eSchristos 447d62b00eSchristos SCOPE_EXIT 457d62b00eSchristos { 467d62b00eSchristos // any code you like here. 477d62b00eSchristos } 487d62b00eSchristos 497d62b00eSchristos See also forward_scope_exit. 507d62b00eSchristos */ 517d62b00eSchristos 527d62b00eSchristos /* CRTP base class for cancelable scope_exit-like classes. Implements 537d62b00eSchristos the common call-custom-function-from-dtor functionality. Classes 547d62b00eSchristos that inherit this implement the on_exit() method, which is called 557d62b00eSchristos from scope_exit_base's dtor. */ 567d62b00eSchristos 577d62b00eSchristos template <typename CRTP> 587d62b00eSchristos class scope_exit_base 597d62b00eSchristos { 607d62b00eSchristos public: 617d62b00eSchristos scope_exit_base () = default; 627d62b00eSchristos 637d62b00eSchristos ~scope_exit_base () 647d62b00eSchristos { 657d62b00eSchristos if (!m_released) 667d62b00eSchristos { 677d62b00eSchristos auto *self = static_cast<CRTP *> (this); 687d62b00eSchristos self->on_exit (); 697d62b00eSchristos } 707d62b00eSchristos } 717d62b00eSchristos 727d62b00eSchristos /* This is needed for make_scope_exit because copy elision isn't 737d62b00eSchristos guaranteed until C++17. An optimizing compiler will usually skip 747d62b00eSchristos calling this, but it must exist. */ 757d62b00eSchristos scope_exit_base (const scope_exit_base &other) 767d62b00eSchristos : m_released (other.m_released) 777d62b00eSchristos { 787d62b00eSchristos other.m_released = true; 797d62b00eSchristos } 807d62b00eSchristos 817d62b00eSchristos void operator= (const scope_exit_base &) = delete; 827d62b00eSchristos 837d62b00eSchristos /* If this is called, then the wrapped function will not be called 847d62b00eSchristos on destruction. */ 857d62b00eSchristos void release () noexcept 867d62b00eSchristos { 877d62b00eSchristos m_released = true; 887d62b00eSchristos } 897d62b00eSchristos 907d62b00eSchristos private: 917d62b00eSchristos 927d62b00eSchristos /* True if released. Mutable because of the copy ctor hack 937d62b00eSchristos above. */ 947d62b00eSchristos mutable bool m_released = false; 957d62b00eSchristos }; 967d62b00eSchristos 977d62b00eSchristos /* The scope_exit class. */ 987d62b00eSchristos 997d62b00eSchristos template<typename EF> 1007d62b00eSchristos class scope_exit : public scope_exit_base<scope_exit<EF>> 1017d62b00eSchristos { 1027d62b00eSchristos /* For access to on_exit(). */ 1037d62b00eSchristos friend scope_exit_base<scope_exit<EF>>; 1047d62b00eSchristos 1057d62b00eSchristos public: 1067d62b00eSchristos 1077d62b00eSchristos template<typename EFP, 1087d62b00eSchristos typename = gdb::Requires<std::is_constructible<EF, EFP>>> 1097d62b00eSchristos scope_exit (EFP &&f) 1107d62b00eSchristos try : m_exit_function ((!std::is_lvalue_reference<EFP>::value 1117d62b00eSchristos && std::is_nothrow_constructible<EF, EFP>::value) 1127d62b00eSchristos ? std::move (f) 1137d62b00eSchristos : f) 1147d62b00eSchristos { 1157d62b00eSchristos } 1167d62b00eSchristos catch (...) 1177d62b00eSchristos { 1187d62b00eSchristos /* "If the initialization of exit_function throws an exception, 1197d62b00eSchristos calls f()." */ 1207d62b00eSchristos f (); 1217d62b00eSchristos } 1227d62b00eSchristos 1237d62b00eSchristos template<typename EFP, 1247d62b00eSchristos typename = gdb::Requires<std::is_constructible<EF, EFP>>> 1257d62b00eSchristos scope_exit (scope_exit &&rhs) 1267d62b00eSchristos noexcept (std::is_nothrow_move_constructible<EF>::value 1277d62b00eSchristos || std::is_nothrow_copy_constructible<EF>::value) 1287d62b00eSchristos : m_exit_function (std::is_nothrow_constructible<EFP>::value 1297d62b00eSchristos ? std::move (rhs) 1307d62b00eSchristos : rhs) 1317d62b00eSchristos { 1327d62b00eSchristos rhs.release (); 1337d62b00eSchristos } 1347d62b00eSchristos 1357d62b00eSchristos /* This is needed for make_scope_exit because copy elision isn't 1367d62b00eSchristos guaranteed until C++17. An optimizing compiler will usually skip 1377d62b00eSchristos calling this, but it must exist. */ 1387d62b00eSchristos scope_exit (const scope_exit &other) 1397d62b00eSchristos : scope_exit_base<scope_exit<EF>> (other), 1407d62b00eSchristos m_exit_function (other.m_exit_function) 1417d62b00eSchristos { 1427d62b00eSchristos } 1437d62b00eSchristos 1447d62b00eSchristos void operator= (const scope_exit &) = delete; 1457d62b00eSchristos void operator= (scope_exit &&) = delete; 1467d62b00eSchristos 1477d62b00eSchristos private: 1487d62b00eSchristos void on_exit () 1497d62b00eSchristos { 1507d62b00eSchristos m_exit_function (); 1517d62b00eSchristos } 1527d62b00eSchristos 1537d62b00eSchristos /* The function to call on scope exit. */ 1547d62b00eSchristos EF m_exit_function; 1557d62b00eSchristos }; 1567d62b00eSchristos 1577d62b00eSchristos template <typename EF> 1587d62b00eSchristos scope_exit<typename std::decay<EF>::type> 1597d62b00eSchristos make_scope_exit (EF &&f) 1607d62b00eSchristos { 1617d62b00eSchristos return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); 1627d62b00eSchristos } 1637d62b00eSchristos 1647d62b00eSchristos namespace detail 1657d62b00eSchristos { 1667d62b00eSchristos 1677d62b00eSchristos enum class scope_exit_lhs {}; 1687d62b00eSchristos 1697d62b00eSchristos template<typename EF> 1707d62b00eSchristos scope_exit<typename std::decay<EF>::type> 1717d62b00eSchristos operator+ (scope_exit_lhs, EF &&rhs) 1727d62b00eSchristos { 1737d62b00eSchristos return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs)); 1747d62b00eSchristos } 1757d62b00eSchristos 1767d62b00eSchristos } 1777d62b00eSchristos 1787d62b00eSchristos /* Register a block of code to run on scope exit. Note that the local 1797d62b00eSchristos context is captured by reference, which means you should be careful 1807d62b00eSchristos to avoid inadvertently changing a captured local's value before the 1817d62b00eSchristos scope exit runs. */ 1827d62b00eSchristos 1837d62b00eSchristos #define SCOPE_EXIT \ 1847d62b00eSchristos auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] () 1857d62b00eSchristos 1867d62b00eSchristos #endif /* COMMON_SCOPE_EXIT_H */ 187