xref: /aosp_15_r20/external/pigweed/pw_function/public/pw_function/scope_guard.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <utility>
17 
18 namespace pw {
19 
20 /// `ScopeGuard` ensures that the specified functor is executed no matter how
21 /// the current scope exits, unless it is dismissed.
22 ///
23 /// Example:
24 ///
25 /// @code
26 ///   pw::Status SomeFunction() {
27 ///     PW_TRY(OperationOne());
28 ///     ScopeGuard undo_operation_one(UndoOperationOne);
29 ///     PW_TRY(OperationTwo());
30 ///     ScopeGuard undo_operation_two(UndoOperationTwo);
31 ///     PW_TRY(OperationThree());
32 ///     undo_operation_one.Dismiss();
33 ///     undo_operation_two.Dismiss();
34 ///     return pw::OkStatus();
35 ///   }
36 /// @endcode
37 template <typename Functor>
38 class ScopeGuard {
39  public:
ScopeGuard(Functor && functor)40   constexpr ScopeGuard(Functor&& functor)
41       : functor_(std::forward<Functor>(functor)), dismissed_(false) {}
42 
ScopeGuard(ScopeGuard && other)43   constexpr ScopeGuard(ScopeGuard&& other) noexcept
44       : functor_(std::move(other.functor_)), dismissed_(other.dismissed_) {
45     other.dismissed_ = true;
46   }
47 
48   template <typename OtherFunctor>
ScopeGuard(ScopeGuard<OtherFunctor> && other)49   constexpr ScopeGuard(ScopeGuard<OtherFunctor>&& other)
50       : functor_(std::move(other.functor_)), dismissed_(other.dismissed_) {
51     other.dismissed_ = true;
52   }
53 
~ScopeGuard()54   ~ScopeGuard() {
55     if (dismissed_) {
56       return;
57     }
58     functor_();
59   }
60 
61   ScopeGuard& operator=(ScopeGuard&& other) noexcept {
62     functor_ = std::move(other.functor_);
63     dismissed_ = std::move(other.dismissed_);
64     other.dismissed_ = true;
65   }
66 
67   ScopeGuard() = delete;
68   ScopeGuard(const ScopeGuard&) = delete;
69   ScopeGuard& operator=(const ScopeGuard&) = delete;
70 
71   /// Dismisses the `ScopeGuard`, meaning it will no longer execute the
72   /// `Functor` when it goes out of scope.
Dismiss()73   void Dismiss() { dismissed_ = true; }
74 
75  private:
76   template <typename OtherFunctor>
77   friend class ScopeGuard;
78 
79   Functor functor_;
80   bool dismissed_;
81 };
82 
83 // Enable type deduction for a compatible function pointer.
84 template <typename Function>
85 ScopeGuard(Function()) -> ScopeGuard<Function (*)()>;
86 
87 }  // namespace pw
88