Home | History | Annotate | Line # | Download | only in gdbsupport
scope-exit.h revision 1.1.1.1
      1 /* Copyright (C) 2019-2020 Free Software Foundation, Inc.
      2 
      3    This file is part of GDB.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 3 of the License, or
      8    (at your option) any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     17 
     18 #ifndef COMMON_SCOPE_EXIT_H
     19 #define COMMON_SCOPE_EXIT_H
     20 
     21 #include <functional>
     22 #include <type_traits>
     23 #include "gdbsupport/preprocessor.h"
     24 
     25 /* scope_exit is a general-purpose scope guard that calls its exit
     26    function at the end of the current scope.  A scope_exit may be
     27    canceled by calling the "release" method.  The API is modeled on
     28    P0052R5 - Generic Scope Guard and RAII Wrapper for the Standard
     29    Library, which is itself based on Andrej Alexandrescu's
     30    ScopeGuard/SCOPE_EXIT.
     31 
     32    There are two forms available:
     33 
     34    - The "make_scope_exit" form allows canceling the scope guard.  Use
     35      it like this:
     36 
     37      auto cleanup = make_scope_exit ( <function, function object, lambda> );
     38      ...
     39      cleanup.release (); // cancel
     40 
     41    - If you don't need to cancel the guard, you can use the SCOPE_EXIT
     42      macro, like this:
     43 
     44      SCOPE_EXIT
     45        {
     46 	 // any code you like here.
     47        }
     48 
     49    See also forward_scope_exit.
     50 */
     51 
     52 /* CRTP base class for cancelable scope_exit-like classes.  Implements
     53    the common call-custom-function-from-dtor functionality.  Classes
     54    that inherit this implement the on_exit() method, which is called
     55    from scope_exit_base's dtor.  */
     56 
     57 template <typename CRTP>
     58 class scope_exit_base
     59 {
     60 public:
     61   scope_exit_base () = default;
     62 
     63   ~scope_exit_base ()
     64   {
     65     if (!m_released)
     66       {
     67 	auto *self = static_cast<CRTP *> (this);
     68 	self->on_exit ();
     69       }
     70   }
     71 
     72   /* This is needed for make_scope_exit because copy elision isn't
     73      guaranteed until C++17.  An optimizing compiler will usually skip
     74      calling this, but it must exist.  */
     75   scope_exit_base (const scope_exit_base &other)
     76     : m_released (other.m_released)
     77   {
     78     other.m_released = true;
     79   }
     80 
     81   void operator= (const scope_exit_base &) = delete;
     82 
     83   /* If this is called, then the wrapped function will not be called
     84      on destruction.  */
     85   void release () noexcept
     86   {
     87     m_released = true;
     88   }
     89 
     90 private:
     91 
     92   /* True if released.  Mutable because of the copy ctor hack
     93      above.  */
     94   mutable bool m_released = false;
     95 };
     96 
     97 /* The scope_exit class.  */
     98 
     99 template<typename EF>
    100 class scope_exit : public scope_exit_base<scope_exit<EF>>
    101 {
    102   /* For access to on_exit().  */
    103   friend scope_exit_base<scope_exit<EF>>;
    104 
    105 public:
    106 
    107   template<typename EFP,
    108 	   typename = gdb::Requires<std::is_constructible<EF, EFP>>>
    109   scope_exit (EFP &&f)
    110     try : m_exit_function ((!std::is_lvalue_reference<EFP>::value
    111 			    && std::is_nothrow_constructible<EF, EFP>::value)
    112 			   ? std::move (f)
    113 			   : f)
    114   {
    115   }
    116   catch (...)
    117     {
    118       /* "If the initialization of exit_function throws an exception,
    119 	 calls f()."  */
    120       f ();
    121     }
    122 
    123   template<typename EFP,
    124 	   typename = gdb::Requires<std::is_constructible<EF, EFP>>>
    125   scope_exit (scope_exit &&rhs)
    126     noexcept (std::is_nothrow_move_constructible<EF>::value
    127 	      || std::is_nothrow_copy_constructible<EF>::value)
    128     : m_exit_function (std::is_nothrow_constructible<EFP>::value
    129 		       ? std::move (rhs)
    130 		       : rhs)
    131   {
    132     rhs.release ();
    133   }
    134 
    135   /* This is needed for make_scope_exit because copy elision isn't
    136      guaranteed until C++17.  An optimizing compiler will usually skip
    137      calling this, but it must exist.  */
    138   scope_exit (const scope_exit &other)
    139     : scope_exit_base<scope_exit<EF>> (other),
    140       m_exit_function (other.m_exit_function)
    141   {
    142   }
    143 
    144   void operator= (const scope_exit &) = delete;
    145   void operator= (scope_exit &&) = delete;
    146 
    147 private:
    148   void on_exit ()
    149   {
    150     m_exit_function ();
    151   }
    152 
    153   /* The function to call on scope exit.  */
    154   EF m_exit_function;
    155 };
    156 
    157 template <typename EF>
    158 scope_exit<typename std::decay<EF>::type>
    159 make_scope_exit (EF &&f)
    160 {
    161   return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f));
    162 }
    163 
    164 namespace detail
    165 {
    166 
    167 enum class scope_exit_lhs {};
    168 
    169 template<typename EF>
    170 scope_exit<typename std::decay<EF>::type>
    171 operator+ (scope_exit_lhs, EF &&rhs)
    172 {
    173   return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs));
    174 }
    175 
    176 }
    177 
    178 /* Register a block of code to run on scope exit.  Note that the local
    179    context is captured by reference, which means you should be careful
    180    to avoid inadvertently changing a captured local's value before the
    181    scope exit runs.  */
    182 
    183 #define SCOPE_EXIT \
    184   auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] ()
    185 
    186 #endif /* COMMON_SCOPE_EXIT_H */
    187