xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/scope-exit.h (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
1 /* Copyright (C) 2019-2024 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   DISABLE_COPY_AND_ASSIGN (scope_exit_base);
73 
74   /* If this is called, then the wrapped function will not be called
75      on destruction.  */
76   void release () noexcept
77   {
78     m_released = true;
79   }
80 
81 private:
82 
83   /* True if released.  Mutable because of the copy ctor hack
84      above.  */
85   mutable bool m_released = false;
86 };
87 
88 /* The scope_exit class.  */
89 
90 template<typename EF>
91 class scope_exit : public scope_exit_base<scope_exit<EF>>
92 {
93   /* For access to on_exit().  */
94   friend scope_exit_base<scope_exit<EF>>;
95 
96 public:
97 
98   template<typename EFP,
99 	   typename = gdb::Requires<std::is_constructible<EF, EFP>>>
100   scope_exit (EFP &&f)
101     try : m_exit_function ((!std::is_lvalue_reference<EFP>::value
102 			    && std::is_nothrow_constructible<EF, EFP>::value)
103 			   ? std::move (f)
104 			   : f)
105   {
106   }
107   catch (...)
108     {
109       /* "If the initialization of exit_function throws an exception,
110 	 calls f()."  */
111       f ();
112     }
113 
114   template<typename EFP,
115 	   typename = gdb::Requires<std::is_constructible<EF, EFP>>>
116   scope_exit (scope_exit &&rhs)
117     noexcept (std::is_nothrow_move_constructible<EF>::value
118 	      || std::is_nothrow_copy_constructible<EF>::value)
119     : m_exit_function (std::is_nothrow_constructible<EFP>::value
120 		       ? std::move (rhs)
121 		       : rhs)
122   {
123     rhs.release ();
124   }
125 
126   DISABLE_COPY_AND_ASSIGN (scope_exit);
127   void operator= (scope_exit &&) = delete;
128 
129 private:
130   void on_exit ()
131   {
132     m_exit_function ();
133   }
134 
135   /* The function to call on scope exit.  */
136   EF m_exit_function;
137 };
138 
139 template <typename EF>
140 scope_exit<typename std::decay<EF>::type>
141 make_scope_exit (EF &&f)
142 {
143   return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f));
144 }
145 
146 namespace detail
147 {
148 
149 enum class scope_exit_lhs {};
150 
151 template<typename EF>
152 scope_exit<typename std::decay<EF>::type>
153 operator+ (scope_exit_lhs, EF &&rhs)
154 {
155   return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (rhs));
156 }
157 
158 }
159 
160 /* Register a block of code to run on scope exit.  Note that the local
161    context is captured by reference, which means you should be careful
162    to avoid inadvertently changing a captured local's value before the
163    scope exit runs.  */
164 
165 #define SCOPE_EXIT \
166   auto CONCAT(scope_exit_, __LINE__) = ::detail::scope_exit_lhs () + [&] ()
167 
168 #endif /* COMMON_SCOPE_EXIT_H */
169