1+ /*
2+ * Copyright 2024 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+ #pragma once
18+
19+ #include < cstddef>
20+
21+ #include < functional>
22+ #include < type_traits>
23+ #include < utility>
24+
25+ #include < ftl/function.h>
26+
27+ namespace android ::ftl {
28+
29+ // An RAII wrapper that invokes a function object as a finalizer when destroyed.
30+ //
31+ // The function object must take no arguments, and must return void. If the function object needs
32+ // any context for the call, it must store it itself, for example with a lambda capture.
33+ //
34+ // The stored function object will be called once (unless canceled via the `cancel()` member
35+ // function) at the first of:
36+ //
37+ // - The Finalizer instance is destroyed.
38+ // - `operator()` is used to invoke the contained function.
39+ // - The Finalizer instance is move-assigned a new value. The function being replaced will be
40+ // invoked, and the replacement will be stored to be called later.
41+ //
42+ // The intent with this class is to keep cleanup code next to the code that requires that
43+ // cleanup be performed.
44+ //
45+ // bool read_file(std::string filename) {
46+ // FILE* f = fopen(filename.c_str(), "rb");
47+ // if (f == nullptr) return false;
48+ // const auto cleanup = ftl::Finalizer([f]() { fclose(f); });
49+ // // fread(...), etc
50+ // return true;
51+ // }
52+ //
53+ // The `FinalFunction` template argument to Finalizer<FinalFunction> allows a polymorphic function
54+ // type for storing the finalization function, such as `std::function` or `ftl::Function`.
55+ //
56+ // For convenience, this header defines a few useful aliases for using those types.
57+ //
58+ // - `FinalizerStd`, an alias for `Finalizer<std::function<void()>>`
59+ // - `FinalizerFtl`, an alias for `Finalizer<ftl::Function<void()>>`
60+ // - `FinalizerFtl1`, an alias for `Finalizer<ftl::Function<void(), 1>>`
61+ // - `FinalizerFtl2`, an alias for `Finalizer<ftl::Function<void(), 2>>`
62+ // - `FinalizerFtl3`, an alias for `Finalizer<ftl::Function<void(), 3>>`
63+ //
64+ // Clients of this header are free to define other aliases they need.
65+ //
66+ // A Finalizer that uses a polymorphic function type can be returned from a function call and/or
67+ // stored as member data (to be destroyed along with the containing class).
68+ //
69+ // auto register(Observer* observer) -> ftl::FinalizerStd<void()> {
70+ // const auto id = observers.add(observer);
71+ // return ftl::Finalizer([id]() { observers.remove(id); });
72+ // }
73+ //
74+ // {
75+ // const auto _ = register(observer);
76+ // // do the things that required the registered observer.
77+ // }
78+ // // the observer is removed.
79+ //
80+ // Cautions:
81+ //
82+ // 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to
83+ // happen first, before the rest of the other member data is destroyed. For safety you should
84+ // assume that the finalization function will access that data directly or indirectly.
85+ //
86+ // This means that Finalizers should be defined last, after all other normal member data in a
87+ // class.
88+ //
89+ // class MyClass {
90+ // public:
91+ // bool initialize() {
92+ // ready_ = true;
93+ // cleanup_ = ftl::Finalizer([this]() { ready_ = false; });
94+ // return true;
95+ // }
96+ //
97+ // bool ready_ = false;
98+ //
99+ // // Finalizers should be last so other class members can be accessed before being
100+ // // destroyed.
101+ // ftl::FinalizerStd<void()> cleanup_;
102+ // };
103+ //
104+ // 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you
105+ // forget to do so, you are just creating a lambda that won't be automatically invoked!
106+ //
107+ // const auto bad = [&counter](){ ++counter; }; // Just a lambda instance
108+ // const auto good = ftl::Finalizer([&counter](){ ++counter; });
109+ //
110+ template <typename FinalFunction>
111+ class Finalizer final {
112+ // requires(std::is_invocable_r_v<void, FinalFunction>)
113+ static_assert (std::is_invocable_r_v<void , FinalFunction>);
114+
115+ public:
116+ // A default constructed Finalizer does nothing when destroyed.
117+ // requires(std::is_default_constructible_v<FinalFunction>)
118+ constexpr Finalizer () = default;
119+
120+ // Constructs a Finalizer from a function object.
121+ // requires(std::is_invocable_v<F>)
122+ template <typename F, typename = std::enable_if_t <std::is_invocable_v<F>>>
123+ [[nodiscard]] explicit constexpr Finalizer (F&& function)
124+ : Finalizer(std::forward<F>(function), false) {}
125+
126+ constexpr ~Finalizer () { maybe_invoke (); }
127+
128+ // Disallow copying.
129+ Finalizer (const Finalizer& that) = delete ;
130+ auto operator =(const Finalizer& that) = delete ;
131+
132+ // Move construction
133+ // requires(std::is_move_constructible_v<FinalFunction>)
134+ [[nodiscard]] constexpr Finalizer (Finalizer&& that)
135+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true )) {}
136+
137+ // Implicit conversion move construction
138+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
139+ template <typename F, typename = std::enable_if_t <!std::is_same_v<Finalizer, Finalizer<F>>>>
140+ // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved)
141+ [[nodiscard]] constexpr Finalizer (Finalizer<F>&& that)
142+ : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true )) {}
143+
144+ // Move assignment
145+ // requires(std::is_move_assignable_v<FinalFunction>)
146+ constexpr auto operator =(Finalizer&& that) -> Finalizer& {
147+ maybe_invoke ();
148+
149+ function_ = std::move (that.function_ );
150+ canceled_ = std::exchange (that.canceled_ , true );
151+
152+ return *this ;
153+ }
154+
155+ // Implicit conversion move assignment
156+ // requires(!std::is_same_v<Finalizer, Finalizer<F>>)
157+ template <typename F, typename = std::enable_if_t <!std::is_same_v<Finalizer, Finalizer<F>>>>
158+ // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
159+ constexpr auto operator =(Finalizer<F>&& that) -> Finalizer& {
160+ *this = Finalizer (std::move (that.function_ ), std::exchange (that.canceled_ , true ));
161+ return *this ;
162+ }
163+
164+ // Cancels the final function, preventing it from being invoked.
165+ constexpr void cancel () {
166+ canceled_ = true ;
167+ maybe_nullify_function ();
168+ }
169+
170+ // Invokes the final function now, if not already invoked.
171+ constexpr void operator ()() { maybe_invoke (); }
172+
173+ private:
174+ template <typename >
175+ friend class Finalizer ;
176+
177+ template <typename F, typename = std::enable_if_t <std::is_invocable_v<F>>>
178+ [[nodiscard]] explicit constexpr Finalizer (F&& function, bool canceled)
179+ : function_(std::forward<F>(function)), canceled_(canceled) {}
180+
181+ constexpr void maybe_invoke () {
182+ if (!std::exchange (canceled_, true )) {
183+ std::invoke (function_);
184+ maybe_nullify_function ();
185+ }
186+ }
187+
188+ constexpr void maybe_nullify_function () {
189+ // Sets function_ to nullptr if that is supported for the backing type.
190+ if constexpr (std::is_assignable_v<FinalFunction, nullptr_t >) {
191+ function_ = nullptr ;
192+ }
193+ }
194+
195+ FinalFunction function_;
196+ bool canceled_ = true ;
197+ };
198+
199+ template <typename F>
200+ Finalizer (F&&) -> Finalizer<std::decay_t <F>>;
201+
202+ // A standard alias for using `std::function` as the polymorphic function type.
203+ using FinalizerStd = Finalizer<std::function<void ()>>;
204+
205+ // Helpful aliases for using `ftl::Function` as the polymorphic function type.
206+ using FinalizerFtl = Finalizer<Function<void ()>>;
207+ using FinalizerFtl1 = Finalizer<Function<void (), 1 >>;
208+ using FinalizerFtl2 = Finalizer<Function<void (), 2 >>;
209+ using FinalizerFtl3 = Finalizer<Function<void (), 3 >>;
210+
211+ } // namespace android::ftl
0 commit comments