xref: /netbsd-src/external/gpl3/gdb.old/dist/gdbsupport/scope-exit.h (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
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