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