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