1*5ba1f45fSchristos /* Copyright (C) 2019-2024 Free Software Foundation, Inc. 28dffb485Schristos 38dffb485Schristos This file is part of GDB. 48dffb485Schristos 58dffb485Schristos This program is free software; you can redistribute it and/or modify 68dffb485Schristos it under the terms of the GNU General Public License as published by 78dffb485Schristos the Free Software Foundation; either version 3 of the License, or 88dffb485Schristos (at your option) any later version. 98dffb485Schristos 108dffb485Schristos This program is distributed in the hope that it will be useful, 118dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of 128dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138dffb485Schristos GNU General Public License for more details. 148dffb485Schristos 158dffb485Schristos You should have received a copy of the GNU General Public License 168dffb485Schristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 178dffb485Schristos 188dffb485Schristos #ifndef COMMON_SCOPE_EXIT_H 198dffb485Schristos #define COMMON_SCOPE_EXIT_H 208dffb485Schristos 218dffb485Schristos #include <functional> 228dffb485Schristos #include <type_traits> 238dffb485Schristos #include "gdbsupport/preprocessor.h" 248dffb485Schristos 258dffb485Schristos /* scope_exit is a general-purpose scope guard that calls its exit 268dffb485Schristos function at the end of the current scope. A scope_exit may be 278dffb485Schristos canceled by calling the "release" method. The API is modeled on 288dffb485Schristos P0052R5 - Generic Scope Guard and RAII Wrapper for the Standard 298dffb485Schristos Library, which is itself based on Andrej Alexandrescu's 308dffb485Schristos ScopeGuard/SCOPE_EXIT. 318dffb485Schristos 328dffb485Schristos There are two forms available: 338dffb485Schristos 348dffb485Schristos - The "make_scope_exit" form allows canceling the scope guard. Use 358dffb485Schristos it like this: 368dffb485Schristos 378dffb485Schristos auto cleanup = make_scope_exit ( <function, function object, lambda> ); 388dffb485Schristos ... 398dffb485Schristos cleanup.release (); // cancel 408dffb485Schristos 418dffb485Schristos - If you don't need to cancel the guard, you can use the SCOPE_EXIT 428dffb485Schristos macro, like this: 438dffb485Schristos 448dffb485Schristos SCOPE_EXIT 458dffb485Schristos { 468dffb485Schristos // any code you like here. 478dffb485Schristos } 488dffb485Schristos 498dffb485Schristos See also forward_scope_exit. 508dffb485Schristos */ 518dffb485Schristos 528dffb485Schristos /* CRTP base class for cancelable scope_exit-like classes. Implements 538dffb485Schristos the common call-custom-function-from-dtor functionality. Classes 548dffb485Schristos that inherit this implement the on_exit() method, which is called 558dffb485Schristos from scope_exit_base's dtor. */ 568dffb485Schristos 578dffb485Schristos template <typename CRTP> 588dffb485Schristos class scope_exit_base 598dffb485Schristos { 608dffb485Schristos public: 618dffb485Schristos scope_exit_base () = default; 628dffb485Schristos 638dffb485Schristos ~scope_exit_base () 648dffb485Schristos { 658dffb485Schristos if (!m_released) 668dffb485Schristos { 678dffb485Schristos auto *self = static_cast<CRTP *> (this); 688dffb485Schristos self->on_exit (); 698dffb485Schristos } 708dffb485Schristos } 718dffb485Schristos 72*5ba1f45fSchristos DISABLE_COPY_AND_ASSIGN (scope_exit_base); 738dffb485Schristos 748dffb485Schristos /* If this is called, then the wrapped function will not be called 758dffb485Schristos on destruction. */ 768dffb485Schristos void release () noexcept 778dffb485Schristos { 788dffb485Schristos m_released = true; 798dffb485Schristos } 808dffb485Schristos 818dffb485Schristos private: 828dffb485Schristos 838dffb485Schristos /* True if released. Mutable because of the copy ctor hack 848dffb485Schristos above. */ 858dffb485Schristos mutable bool m_released = false; 868dffb485Schristos }; 878dffb485Schristos 888dffb485Schristos /* The scope_exit class. */ 898dffb485Schristos 908dffb485Schristos template<typename EF> 918dffb485Schristos class scope_exit : public scope_exit_base<scope_exit<EF>> 928dffb485Schristos { 938dffb485Schristos /* For access to on_exit(). */ 948dffb485Schristos friend scope_exit_base<scope_exit<EF>>; 958dffb485Schristos 968dffb485Schristos public: 978dffb485Schristos 988dffb485Schristos template<typename EFP, 998dffb485Schristos typename = gdb::Requires<std::is_constructible<EF, EFP>>> 1008dffb485Schristos scope_exit (EFP &&f) 1018dffb485Schristos try : m_exit_function ((!std::is_lvalue_reference<EFP>::value 1028dffb485Schristos && std::is_nothrow_constructible<EF, EFP>::value) 1038dffb485Schristos ? std::move (f) 1048dffb485Schristos : f) 1058dffb485Schristos { 1068dffb485Schristos } 1078dffb485Schristos catch (...) 1088dffb485Schristos { 1098dffb485Schristos /* "If the initialization of exit_function throws an exception, 1108dffb485Schristos calls f()." */ 1118dffb485Schristos f (); 1128dffb485Schristos } 1138dffb485Schristos 1148dffb485Schristos template<typename EFP, 1158dffb485Schristos typename = gdb::Requires<std::is_constructible<EF, EFP>>> 1168dffb485Schristos scope_exit (scope_exit &&rhs) 1178dffb485Schristos noexcept (std::is_nothrow_move_constructible<EF>::value 1188dffb485Schristos || std::is_nothrow_copy_constructible<EF>::value) 1198dffb485Schristos : m_exit_function (std::is_nothrow_constructible<EFP>::value 1208dffb485Schristos ? std::move (rhs) 1218dffb485Schristos : rhs) 1228dffb485Schristos { 1238dffb485Schristos rhs.release (); 1248dffb485Schristos } 1258dffb485Schristos 126*5ba1f45fSchristos DISABLE_COPY_AND_ASSIGN (scope_exit); 1278dffb485Schristos void operator= (scope_exit &&) = delete; 1288dffb485Schristos 1298dffb485Schristos private: 1308dffb485Schristos void on_exit () 1318dffb485Schristos { 1328dffb485Schristos m_exit_function (); 1338dffb485Schristos } 1348dffb485Schristos 1358dffb485Schristos /* The function to call on scope exit. */ 1368dffb485Schristos EF m_exit_function; 1378dffb485Schristos }; 1388dffb485Schristos 1398dffb485Schristos template <typename EF> 1408dffb485Schristos scope_exit<typename std::decay<EF>::type> 1418dffb485Schristos make_scope_exit (EF &&f) 1428dffb485Schristos { 1438dffb485Schristos return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f)); 1448dffb485Schristos } 1458dffb485Schristos 1468dffb485Schristos namespace detail 1478dffb485Schristos { 1488dffb485Schristos 1498dffb485Schristos enum class scope_exit_lhs {}; 1508dffb485Schristos 1518dffb485Schristos template<typename EF> 1528dffb485Schristos scope_exit<typename std::decay<EF>::type> 1538dffb485Schristos operator+ (scope_exit_lhs, EF &&rhs) 1548dffb485Schristos { 1558dffb485Schristos return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs)); 1568dffb485Schristos } 1578dffb485Schristos 1588dffb485Schristos } 1598dffb485Schristos 1608dffb485Schristos /* Register a block of code to run on scope exit. Note that the local 1618dffb485Schristos context is captured by reference, which means you should be careful 1628dffb485Schristos to avoid inadvertently changing a captured local's value before the 1638dffb485Schristos scope exit runs. */ 1648dffb485Schristos 1658dffb485Schristos #define SCOPE_EXIT \ 1668dffb485Schristos auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] () 1678dffb485Schristos 1688dffb485Schristos #endif /* COMMON_SCOPE_EXIT_H */ 169