/* * 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 #include #include #include #include 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 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>` // - `FinalizerFtl`, an alias for `Finalizer>` // - `FinalizerFtl1`, an alias for `Finalizer>` // - `FinalizerFtl2`, an alias for `Finalizer>` // - `FinalizerFtl3`, an alias for `Finalizer>` // // 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 { // 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 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 class Finalizer final { // requires(std::is_invocable_r_v) static_assert(std::is_invocable_r_v); public: // A default constructed Finalizer does nothing when destroyed. // requires(std::is_default_constructible_v) constexpr Finalizer() = default; // Constructs a Finalizer from a function object. // requires(std::is_invocable_v) template >> [[nodiscard]] explicit constexpr Finalizer(F&& function) : Finalizer(std::forward(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) [[nodiscard]] constexpr Finalizer(Finalizer&& that) : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} // Implicit conversion move construction // requires(!std::is_same_v>) template >>> // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved) [[nodiscard]] constexpr Finalizer(Finalizer&& that) : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} // Move assignment // requires(std::is_move_assignable_v) 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>) template >>> // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) constexpr auto operator=(Finalizer&& 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 friend class Finalizer; template >> [[nodiscard]] explicit constexpr Finalizer(F&& function, bool canceled) : function_(std::forward(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) { function_ = nullptr; } } FinalFunction function_; bool canceled_ = true; }; template Finalizer(F&&) -> Finalizer>; // A standard alias for using `std::function` as the polymorphic function type. using FinalizerStd = Finalizer>; // Helpful aliases for using `ftl::Function` as the polymorphic function type. using FinalizerFtl = Finalizer>; using FinalizerFtl1 = Finalizer>; using FinalizerFtl2 = Finalizer>; using FinalizerFtl3 = Finalizer>; } // namespace android::ftl