| /* |
| * Copyright 2024 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <cstddef> |
| |
| #include <functional> |
| #include <type_traits> |
| #include <utility> |
| |
| #include <ftl/function.h> |
| |
| namespace android::ftl { |
| |
| // An RAII wrapper that invokes a function object as a finalizer when destroyed. |
| // |
| // The function object must take no arguments, and must return void. If the function object needs |
| // any context for the call, it must store it itself, for example with a lambda capture. |
| // |
| // The stored function object will be called once (unless canceled via the `cancel()` member |
| // function) at the first of: |
| // |
| // - The Finalizer instance is destroyed. |
| // - `operator()` is used to invoke the contained function. |
| // - The Finalizer instance is move-assigned a new value. The function being replaced will be |
| // invoked, and the replacement will be stored to be called later. |
| // |
| // The intent with this class is to keep cleanup code next to the code that requires that |
| // cleanup be performed. |
| // |
| // bool read_file(std::string filename) { |
| // FILE* f = fopen(filename.c_str(), "rb"); |
| // if (f == nullptr) return false; |
| // const auto cleanup = ftl::Finalizer([f]() { fclose(f); }); |
| // // fread(...), etc |
| // return true; |
| // } |
| // |
| // The `FinalFunction` template argument to Finalizer<FinalFunction> allows a polymorphic function |
| // type for storing the finalization function, such as `std::function` or `ftl::Function`. |
| // |
| // For convenience, this header defines a few useful aliases for using those types. |
| // |
| // - `FinalizerStd`, an alias for `Finalizer<std::function<void()>>` |
| // - `FinalizerFtl`, an alias for `Finalizer<ftl::Function<void()>>` |
| // - `FinalizerFtl1`, an alias for `Finalizer<ftl::Function<void(), 1>>` |
| // - `FinalizerFtl2`, an alias for `Finalizer<ftl::Function<void(), 2>>` |
| // - `FinalizerFtl3`, an alias for `Finalizer<ftl::Function<void(), 3>>` |
| // |
| // Clients of this header are free to define other aliases they need. |
| // |
| // A Finalizer that uses a polymorphic function type can be returned from a function call and/or |
| // stored as member data (to be destroyed along with the containing class). |
| // |
| // auto register(Observer* observer) -> ftl::FinalizerStd<void()> { |
| // const auto id = observers.add(observer); |
| // return ftl::Finalizer([id]() { observers.remove(id); }); |
| // } |
| // |
| // { |
| // const auto _ = register(observer); |
| // // do the things that required the registered observer. |
| // } |
| // // the observer is removed. |
| // |
| // Cautions: |
| // |
| // 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to |
| // happen first, before the rest of the other member data is destroyed. For safety you should |
| // assume that the finalization function will access that data directly or indirectly. |
| // |
| // This means that Finalizers should be defined last, after all other normal member data in a |
| // class. |
| // |
| // class MyClass { |
| // public: |
| // bool initialize() { |
| // ready_ = true; |
| // cleanup_ = ftl::Finalizer([this]() { ready_ = false; }); |
| // return true; |
| // } |
| // |
| // bool ready_ = false; |
| // |
| // // Finalizers should be last so other class members can be accessed before being |
| // // destroyed. |
| // ftl::FinalizerStd<void()> cleanup_; |
| // }; |
| // |
| // 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you |
| // forget to do so, you are just creating a lambda that won't be automatically invoked! |
| // |
| // const auto bad = [&counter](){ ++counter; }; // Just a lambda instance |
| // const auto good = ftl::Finalizer([&counter](){ ++counter; }); |
| // |
| template <typename FinalFunction> |
| class Finalizer final { |
| // requires(std::is_invocable_r_v<void, FinalFunction>) |
| static_assert(std::is_invocable_r_v<void, FinalFunction>); |
| |
| public: |
| // A default constructed Finalizer does nothing when destroyed. |
| // requires(std::is_default_constructible_v<FinalFunction>) |
| constexpr Finalizer() = default; |
| |
| // Constructs a Finalizer from a function object. |
| // requires(std::is_invocable_v<F>) |
| template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>> |
| [[nodiscard]] explicit constexpr Finalizer(F&& function) |
| : Finalizer(std::forward<F>(function), false) {} |
| |
| constexpr ~Finalizer() { maybe_invoke(); } |
| |
| // Disallow copying. |
| Finalizer(const Finalizer& that) = delete; |
| auto operator=(const Finalizer& that) = delete; |
| |
| // Move construction |
| // requires(std::is_move_constructible_v<FinalFunction>) |
| [[nodiscard]] constexpr Finalizer(Finalizer&& that) |
| : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} |
| |
| // Implicit conversion move construction |
| // requires(!std::is_same_v<Finalizer, Finalizer<F>>) |
| template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>> |
| // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved) |
| [[nodiscard]] constexpr Finalizer(Finalizer<F>&& that) |
| : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} |
| |
| // Move assignment |
| // requires(std::is_move_assignable_v<FinalFunction>) |
| constexpr auto operator=(Finalizer&& that) -> Finalizer& { |
| maybe_invoke(); |
| |
| function_ = std::move(that.function_); |
| canceled_ = std::exchange(that.canceled_, true); |
| |
| return *this; |
| } |
| |
| // Implicit conversion move assignment |
| // requires(!std::is_same_v<Finalizer, Finalizer<F>>) |
| template <typename F, typename = std::enable_if_t<!std::is_same_v<Finalizer, Finalizer<F>>>> |
| // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) |
| constexpr auto operator=(Finalizer<F>&& that) -> Finalizer& { |
| *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)); |
| return *this; |
| } |
| |
| // Cancels the final function, preventing it from being invoked. |
| constexpr void cancel() { |
| canceled_ = true; |
| maybe_nullify_function(); |
| } |
| |
| // Invokes the final function now, if not already invoked. |
| constexpr void operator()() { maybe_invoke(); } |
| |
| private: |
| template <typename> |
| friend class Finalizer; |
| |
| template <typename F, typename = std::enable_if_t<std::is_invocable_v<F>>> |
| [[nodiscard]] explicit constexpr Finalizer(F&& function, bool canceled) |
| : function_(std::forward<F>(function)), canceled_(canceled) {} |
| |
| constexpr void maybe_invoke() { |
| if (!std::exchange(canceled_, true)) { |
| std::invoke(function_); |
| maybe_nullify_function(); |
| } |
| } |
| |
| constexpr void maybe_nullify_function() { |
| // Sets function_ to nullptr if that is supported for the backing type. |
| if constexpr (std::is_assignable_v<FinalFunction, nullptr_t>) { |
| function_ = nullptr; |
| } |
| } |
| |
| FinalFunction function_; |
| bool canceled_ = true; |
| }; |
| |
| template <typename F> |
| Finalizer(F&&) -> Finalizer<std::decay_t<F>>; |
| |
| // A standard alias for using `std::function` as the polymorphic function type. |
| using FinalizerStd = Finalizer<std::function<void()>>; |
| |
| // Helpful aliases for using `ftl::Function` as the polymorphic function type. |
| using FinalizerFtl = Finalizer<Function<void()>>; |
| using FinalizerFtl1 = Finalizer<Function<void(), 1>>; |
| using FinalizerFtl2 = Finalizer<Function<void(), 2>>; |
| using FinalizerFtl3 = Finalizer<Function<void(), 3>>; |
| |
| } // namespace android::ftl |