1/*
2 * Distributed under the Boost Software License, Version 1.0.
3 * (See accompanying file LICENSE_1_0.txt or copy at
4 * https://www.boost.org/LICENSE_1_0.txt)
5 *
6 * Copyright (c) 2023 Andrey Semashev
7 */
8/*!
9 * \file scope/scope_exit.hpp
10 *
11 * This header contains definition of \c scope_exit template.
12 */
13
14#ifndef BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_
15#define BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_
16
17#include <type_traits>
18#include <boost/scope/detail/config.hpp>
19#include <boost/scope/detail/is_not_like.hpp>
20#include <boost/scope/detail/compact_storage.hpp>
21#include <boost/scope/detail/move_or_copy_construct_ref.hpp>
22#include <boost/scope/detail/is_nonnull_default_constructible.hpp>
23#include <boost/scope/detail/type_traits/conjunction.hpp>
24#include <boost/scope/detail/type_traits/is_invocable.hpp>
25#include <boost/scope/detail/type_traits/is_nothrow_invocable.hpp>
26#include <boost/scope/detail/header.hpp>
27
28#ifdef BOOST_HAS_PRAGMA_ONCE
29#pragma once
30#endif
31
32namespace boost {
33namespace scope {
34
35template< typename Func, typename Cond >
36class scope_exit;
37
38namespace detail {
39
40// Workaround for clang < 5.0 which can't pass scope_exit as a template template parameter from within scope_exit definition
41template< typename T >
42using is_not_like_scope_exit = detail::is_not_like< T, scope_exit >;
43
44//! The scope guard used to invoke the condition and action functions in case of exception during scope guard construction
45template< typename Func, typename Cond >
46class init_guard
47{
48private:
49 Func& m_func;
50 Cond& m_cond;
51 bool m_active;
52
53public:
54 init_guard(Func& func, Cond& cond, bool active) noexcept :
55 m_func(func),
56 m_cond(cond),
57 m_active(active)
58 {
59 }
60
61 init_guard(init_guard const&) = delete;
62 init_guard& operator= (init_guard const&) = delete;
63
64 ~init_guard()
65 noexcept(detail::conjunction<
66 detail::is_nothrow_invocable< Func& >,
67 detail::is_nothrow_invocable< Cond& >
68 >::value)
69 {
70 if (m_active && m_cond())
71 m_func();
72 }
73
74 Func&& get_func() noexcept
75 {
76 return static_cast< Func&& >(m_func);
77 }
78
79 Cond&& get_cond() noexcept
80 {
81 return static_cast< Cond&& >(m_cond);
82 }
83
84 void deactivate() noexcept
85 {
86 m_active = false;
87 }
88};
89
90} // namespace detail
91
92/*!
93 * \brief A predicate that always returns \c true.
94 *
95 * This predicate can be used as the default condition function object for
96 * \c scope_exit and similar scope guards.
97 */
98class always_true
99{
100public:
101 //! Predicate result type
102 using result_type = bool;
103
104 /*!
105 * **Throws:** Nothing.
106 *
107 * \returns \c true.
108 */
109 result_type operator()() const noexcept
110 {
111 return true;
112 }
113};
114
115/*!
116 * \brief Scope exit guard that conditionally invokes a function upon leaving the scope.
117 *
118 * The scope guard wraps two function objects: the scope guard action and
119 * a condition for invoking the action. Both function objects must be
120 * callable with no arguments and can be one of:
121 *
122 * \li A user-defined class with a public `operator()`.
123 * \li An lvalue reference to such class.
124 * \li An lvalue reference or pointer to function taking no arguments.
125 *
126 * The condition function object `operator()` must return a value
127 * contextually convertible to \c true, if the action function object
128 * is allowed to be executed, and \c false otherwise. Additionally,
129 * the condition function object `operator()` must not throw, as
130 * otherwise the action function object may not be called.
131 *
132 * The condition function object is optional, and if not specified in
133 * template parameters, the scope guard will operate as if the condition
134 * always returns \c true.
135 *
136 * The scope guard can be in either active or inactive state. By default,
137 * the constructed scope guard is active. When active, and condition
138 * function object returns \c true, the scope guard invokes the wrapped
139 * action function object on destruction. Otherwise, the scope guard
140 * does not call the wrapped action function object.
141 *
142 * The scope guard can be made inactive by moving-from the scope guard
143 * or calling `set_active(false)`. An inactive scope guard can be made
144 * active by calling `set_active(true)`. If a moved-from scope guard
145 * is active on destruction, the behavior is undefined.
146 *
147 * \tparam Func Scope guard action function object type.
148 * \tparam Cond Scope guard condition function object type.
149 */
150template< typename Func, typename Cond = always_true >
151class scope_exit
152{
153//! \cond
154private:
155 struct func_holder :
156 public detail::compact_storage< Func >
157 {
158 using func_base = detail::compact_storage< Func >;
159
160 template<
161 typename F,
162 typename C,
163 typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type
164 >
165 explicit func_holder(F&& func, C&& cond, bool active, std::true_type) noexcept :
166 func_base(static_cast< F&& >(func))
167 {
168 }
169
170 template<
171 typename F,
172 typename C,
173 typename = typename std::enable_if< std::is_constructible< Func, F >::value >::type
174 >
175 explicit func_holder(F&& func, C&& cond, bool active, std::false_type) :
176 func_holder(detail::init_guard< F, C >(func, cond, active))
177 {
178 }
179
180 private:
181 template< typename F, typename C >
182 explicit func_holder(detail::init_guard< F, C >&& init) :
183 func_base(init.get_func())
184 {
185 init.deactivate();
186 }
187 };
188
189 struct cond_holder :
190 public detail::compact_storage< Cond >
191 {
192 using cond_base = detail::compact_storage< Cond >;
193
194 template<
195 typename C,
196 typename = typename std::enable_if< std::is_constructible< Cond, C >::value >::type
197 >
198 explicit cond_holder(C&& cond, Func& func, bool active, std::true_type) noexcept :
199 cond_base(static_cast< C&& >(cond))
200 {
201 }
202
203 template<
204 typename C,
205 typename = typename std::enable_if< std::is_constructible< Cond, C >::value >::type
206 >
207 explicit cond_holder(C&& cond, Func& func, bool active, std::false_type) :
208 cond_holder(detail::init_guard< Func&, C >(func, cond, active))
209 {
210 }
211
212 private:
213 template< typename C >
214 explicit cond_holder(detail::init_guard< Func&, C >&& init) :
215 cond_base(init.get_cond())
216 {
217 init.deactivate();
218 }
219 };
220
221 struct data :
222 public func_holder,
223 public cond_holder
224 {
225 bool m_active;
226
227 template<
228 typename F,
229 typename C,
230 typename = typename std::enable_if< detail::conjunction<
231 std::is_constructible< func_holder, F, C, bool, typename std::is_nothrow_constructible< Func, F >::type >,
232 std::is_constructible< cond_holder, C, Func&, bool, typename std::is_nothrow_constructible< Cond, C >::type >
233 >::value >::type
234 >
235 explicit data(F&& func, C&& cond, bool active)
236 noexcept(detail::conjunction< std::is_nothrow_constructible< Func, F >, std::is_nothrow_constructible< Cond, C > >::value) :
237 func_holder(static_cast< F&& >(func), static_cast< C&& >(cond), active, typename std::is_nothrow_constructible< Func, F >::type()),
238 cond_holder(static_cast< C&& >(cond), func_holder::get(), active, typename std::is_nothrow_constructible< Cond, C >::type()),
239 m_active(active)
240 {
241 }
242
243 Func& get_func() noexcept
244 {
245 return func_holder::get();
246 }
247
248 Func const& get_func() const noexcept
249 {
250 return func_holder::get();
251 }
252
253 Cond& get_cond() noexcept
254 {
255 return cond_holder::get();
256 }
257
258 Cond const& get_cond() const noexcept
259 {
260 return cond_holder::get();
261 }
262
263 bool deactivate() noexcept
264 {
265 bool active = m_active;
266 m_active = false;
267 return active;
268 }
269 };
270
271 data m_data;
272
273//! \endcond
274public:
275 /*!
276 * \brief Constructs a scope guard with a given callable action function object.
277 *
278 * **Requires:** \c Func is constructible from \a func. \c Cond is nothrow default-constructible
279 * and is not a pointer to function.
280 *
281 * \note The requirement for \c Cond default constructor to be non-throwing is to allow for
282 * the condition function object to be called in case if constructing either function
283 * object throws.
284 *
285 * **Effects:** Constructs the scope guard as if by calling
286 * `scope_exit(std::forward< F >(func), Cond(), active)`.
287 *
288 * **Throws:** Nothing, unless construction of the function objects throw.
289 *
290 * \param func The callable action function object to invoke on destruction.
291 * \param active Indicates whether the scope guard should be active upon construction.
292 *
293 * \post `this->active() == active`
294 */
295 template<
296 typename F
297 //! \cond
298 , typename = typename std::enable_if< detail::conjunction<
299 detail::is_nothrow_nonnull_default_constructible< Cond >,
300 std::is_constructible<
301 data,
302 typename detail::move_or_copy_construct_ref< F, Func >::type,
303 typename detail::move_or_copy_construct_ref< Cond >::type,
304 bool
305 >,
306 detail::is_not_like_scope_exit< F >
307 >::value >::type
308 //! \endcond
309 >
310 explicit scope_exit(F&& func, bool active = true)
311 noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
312 std::is_nothrow_constructible<
313 data,
314 typename detail::move_or_copy_construct_ref< F, Func >::type,
315 typename detail::move_or_copy_construct_ref< Cond >::type,
316 bool
317 >::value
318 )) :
319 m_data
320 (
321 static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func),
322 static_cast< typename detail::move_or_copy_construct_ref< Cond >::type >(Cond()),
323 active
324 )
325 {
326 }
327
328 /*!
329 * \brief Constructs a scope guard with a given callable action and condition function objects.
330 *
331 * **Requires:** \c Func is constructible from \a func. \c Cond is constructible from \a cond.
332 *
333 * **Effects:** If \c Func is nothrow constructible from `F&&` then constructs \c Func from
334 * `std::forward< F >(func)`, otherwise constructs from `func`. If \c Cond is
335 * nothrow constructible from `C&&` then constructs \c Cond from
336 * `std::forward< C >(cond)`, otherwise constructs from `cond`.
337 *
338 * If \c Func or \c Cond construction throws and \a active is \c true, invokes
339 * \a cond and, if it returns \c true, \a func before returning with the exception.
340 *
341 * **Throws:** Nothing, unless construction of the function objects throw.
342 *
343 * \param func The callable action function object to invoke on destruction.
344 * \param cond The callable condition function object.
345 * \param active Indicates whether the scope guard should be active upon construction.
346 *
347 * \post `this->active() == active`
348 */
349 template<
350 typename F,
351 typename C
352 //! \cond
353 , typename = typename std::enable_if< detail::conjunction<
354 detail::is_invocable< C const& >,
355 std::is_constructible<
356 data,
357 typename detail::move_or_copy_construct_ref< F, Func >::type,
358 typename detail::move_or_copy_construct_ref< C, Cond >::type,
359 bool
360 >
361 >::value >::type
362 //! \endcond
363 >
364 explicit scope_exit(F&& func, C&& cond, bool active = true)
365 noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
366 std::is_nothrow_constructible<
367 data,
368 typename detail::move_or_copy_construct_ref< F, Func >::type,
369 typename detail::move_or_copy_construct_ref< C, Cond >::type,
370 bool
371 >::value
372 )) :
373 m_data
374 (
375 static_cast< typename detail::move_or_copy_construct_ref< F, Func >::type >(func),
376 static_cast< typename detail::move_or_copy_construct_ref< C, Cond >::type >(cond),
377 active
378 )
379 {
380 }
381
382 /*!
383 * \brief Move-constructs a scope guard.
384 *
385 * **Requires:** \c Func and \c Cond are nothrow move-constructible or copy-constructible.
386 *
387 * **Effects:** If \c Func is nothrow move-constructible then move-constructs \c Func from
388 * a member of \a that, otherwise copy-constructs \c Func. If \c Cond is nothrow
389 * move-constructible then move-constructs \c Cond from a member of \a that,
390 * otherwise copy-constructs \c Cond.
391 *
392 * If \c Func or \c Cond construction throws and `that.active() == true`, invokes
393 * \c Cond object stored in \a that and, if it returns \c true, \a Func object
394 * (either the newly constructed one, if its construction succeeded, or the original
395 * one stored in \a that) before returning with the exception.
396 *
397 * If the construction succeeds, marks \a that as inactive.
398 *
399 * **Throws:** Nothing, unless move-construction of the function objects throw.
400 *
401 * \param that Move source.
402 *
403 * \post `that.active() == false`
404 */
405 //! \cond
406 template<
407 bool Requires = std::is_constructible<
408 data,
409 typename detail::move_or_copy_construct_ref< Func >::type,
410 typename detail::move_or_copy_construct_ref< Cond >::type,
411 bool
412 >::value,
413 typename = typename std::enable_if< Requires >::type
414 >
415 //! \endcond
416 scope_exit(scope_exit&& that)
417 noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
418 std::is_nothrow_constructible<
419 data,
420 typename detail::move_or_copy_construct_ref< Func >::type,
421 typename detail::move_or_copy_construct_ref< Cond >::type,
422 bool
423 >::value
424 )) :
425 m_data
426 (
427 static_cast< typename detail::move_or_copy_construct_ref< Func >::type >(that.m_data.get_func()),
428 static_cast< typename detail::move_or_copy_construct_ref< Cond >::type >(that.m_data.get_cond()),
429 that.m_data.deactivate()
430 )
431 {
432 }
433
434 scope_exit& operator= (scope_exit&&) = delete;
435
436 scope_exit(scope_exit const&) = delete;
437 scope_exit& operator= (scope_exit const&) = delete;
438
439 /*!
440 * \brief If `active() == true`, and invoking the condition function object returns \c true, invokes
441 * the wrapped callable action function object. Destroys the function objects.
442 *
443 * **Throws:** Nothing, unless invoking a function object throws.
444 */
445 ~scope_exit()
446 noexcept(BOOST_SCOPE_DETAIL_DOC_HIDDEN(
447 detail::conjunction<
448 detail::is_nothrow_invocable< Func& >,
449 detail::is_nothrow_invocable< Cond& >
450 >::value
451 ))
452 {
453 if (BOOST_LIKELY(m_data.m_active && m_data.get_cond()()))
454 m_data.get_func()();
455 }
456
457 /*!
458 * \brief Returns \c true if the scope guard is active, otherwise \c false.
459 *
460 * \note This method does not call the condition function object specified on construction.
461 *
462 * **Throws:** Nothing.
463 */
464 bool active() const noexcept
465 {
466 return m_data.m_active;
467 }
468
469 /*!
470 * \brief Activates or deactivates the scope guard.
471 *
472 * **Throws:** Nothing.
473 *
474 * \param active The active status to set.
475 *
476 * \post `this->active() == active`
477 */
478 void set_active(bool active) noexcept
479 {
480 m_data.m_active = active;
481 }
482};
483
484#if !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
485template< typename Func >
486explicit scope_exit(Func) -> scope_exit< Func >;
487
488template< typename Func >
489explicit scope_exit(Func, bool) -> scope_exit< Func >;
490
491template< typename Func, typename Cond >
492explicit scope_exit(Func, Cond) -> scope_exit< Func, Cond >;
493
494template< typename Func, typename Cond >
495explicit scope_exit(Func, Cond, bool) -> scope_exit< Func, Cond >;
496#endif // !defined(BOOST_NO_CXX17_DEDUCTION_GUIDES)
497
498/*!
499 * \brief Creates a scope guard with a given action function object.
500 *
501 * **Effects:** Constructs a scope guard as if by calling
502 * `scope_exit< std::decay_t< F > >(std::forward< F >(func), active)`.
503 *
504 * \param func The callable action function object to invoke on destruction.
505 * \param active Indicates whether the scope guard should be active upon construction.
506 */
507template< typename F >
508inline scope_exit< typename std::decay< F >::type > make_scope_exit(F&& func, bool active = true)
509 noexcept(std::is_nothrow_constructible<
510 scope_exit< typename std::decay< F >::type >,
511 F,
512 bool
513 >::value)
514{
515 return scope_exit< typename std::decay< F >::type >(static_cast< F&& >(func), active);
516}
517
518/*!
519 * \brief Creates a conditional scope guard with given callable function objects.
520 *
521 * **Effects:** Constructs a scope guard as if by calling
522 * `scope_exit< std::decay_t< F >, std::decay_t< C > >(
523 * std::forward< F >(func), std::forward< C >(cond), active)`.
524 *
525 * \param func The callable action function object to invoke on destruction.
526 * \param cond The callable condition function object.
527 * \param active Indicates whether the scope guard should be active upon construction.
528 */
529template< typename F, typename C >
530inline
531#if !defined(BOOST_SCOPE_DOXYGEN)
532typename std::enable_if<
533 std::is_constructible<
534 scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >,
535 F,
536 C,
537 bool
538 >::value,
539 scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >
540>::type
541#else
542scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >
543#endif
544make_scope_exit(F&& func, C&& cond, bool active = true)
545 noexcept(std::is_nothrow_constructible<
546 scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >,
547 F,
548 C,
549 bool
550 >::value)
551{
552 return scope_exit< typename std::decay< F >::type, typename std::decay< C >::type >(static_cast< F&& >(func), static_cast< C&& >(cond), active);
553}
554
555} // namespace scope
556} // namespace boost
557
558#include <boost/scope/detail/footer.hpp>
559
560#endif // BOOST_SCOPE_SCOPE_EXIT_HPP_INCLUDED_
561

source code of boost/libs/scope/include/boost/scope/scope_exit.hpp