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
57template <typename CRTP>
58class scope_exit_base
59{
60public:
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
90private:
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
99template<typename EF>
100class 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
105public:
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
147private:
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
157template <typename EF>
158scope_exit<typename std::decay<EF>::type>
159make_scope_exit (EF &&f)
160{
161  return scope_exit<typename std::decay<EF>::type> (std::forward<EF> (f));
162}
163
164namespace detail
165{
166
167enum class scope_exit_lhs {};
168
169template<typename EF>
170scope_exit<typename std::decay<EF>::type>
171operator+ (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