1 // Copyright 2021 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of 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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // -----------------------------------------------------------------------------
16 // File: cleanup.h
17 // -----------------------------------------------------------------------------
18 //
19 // `absl::Cleanup` implements the scope guard idiom, invoking the contained
20 // callback's `operator()() &&` on scope exit.
21 //
22 // Example:
23 //
24 // ```
25 // absl::Status CopyGoodData(const char* source_path, const char* sink_path) {
26 // FILE* source_file = fopen(source_path, "r");
27 // if (source_file == nullptr) {
28 // return absl::NotFoundError("No source file"); // No cleanups execute
29 // }
30 //
31 // // C++17 style cleanup using class template argument deduction
32 // absl::Cleanup source_closer = [source_file] { fclose(source_file); };
33 //
34 // FILE* sink_file = fopen(sink_path, "w");
35 // if (sink_file == nullptr) {
36 // return absl::NotFoundError("No sink file"); // First cleanup executes
37 // }
38 //
39 // // C++11 style cleanup using the factory function
40 // auto sink_closer = absl::MakeCleanup([sink_file] { fclose(sink_file); });
41 //
42 // Data data;
43 // while (ReadData(source_file, &data)) {
44 // if (!data.IsGood()) {
45 // absl::Status result = absl::FailedPreconditionError("Read bad data");
46 // return result; // Both cleanups execute
47 // }
48 // SaveData(sink_file, &data);
49 // }
50 //
51 // return absl::OkStatus(); // Both cleanups execute
52 // }
53 // ```
54 //
55 // Methods:
56 //
57 // `std::move(cleanup).Cancel()` will prevent the callback from executing.
58 //
59 // `std::move(cleanup).Invoke()` will execute the callback early, before
60 // destruction, and prevent the callback from executing in the destructor.
61 //
62 // Usage:
63 //
64 // `absl::Cleanup` is not an interface type. It is only intended to be used
65 // within the body of a function. It is not a value type and instead models a
66 // control flow construct. Check out `defer` in Golang for something similar.
67
68 #ifndef ABSL_CLEANUP_CLEANUP_H_
69 #define ABSL_CLEANUP_CLEANUP_H_
70
71 #include <utility>
72
73 #include "absl/base/config.h"
74 #include "absl/base/macros.h"
75 #include "absl/cleanup/internal/cleanup.h"
76
77 namespace absl {
78 ABSL_NAMESPACE_BEGIN
79
80 template <typename Arg, typename Callback = void()>
81 class ABSL_MUST_USE_RESULT Cleanup final {
82 static_assert(cleanup_internal::WasDeduced<Arg>(),
83 "Explicit template parameters are not supported.");
84
85 static_assert(cleanup_internal::ReturnsVoid<Callback>(),
86 "Callbacks that return values are not supported.");
87
88 public:
Cleanup(Callback callback)89 Cleanup(Callback callback) // NOLINT
90 : storage_(std::move(callback), /* is_callback_engaged = */ true) {}
91
92 Cleanup(Cleanup&& other) = default;
93
Cancel()94 void Cancel() && {
95 ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
96 storage_.DisengageCallback();
97 }
98
Invoke()99 void Invoke() && {
100 ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
101 storage_.DisengageCallback();
102 storage_.InvokeCallback();
103 }
104
~Cleanup()105 ~Cleanup() {
106 if (storage_.IsCallbackEngaged()) {
107 storage_.InvokeCallback();
108 }
109 }
110
111 private:
112 cleanup_internal::Storage<Callback> storage_;
113 };
114
115 // `absl::Cleanup c = /* callback */;`
116 //
117 // C++17 type deduction API for creating an instance of `absl::Cleanup`
118 #if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
119 template <typename Callback>
120 Cleanup(Callback callback) -> Cleanup<cleanup_internal::Tag, Callback>;
121 #endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
122
123 // `auto c = absl::MakeCleanup(/* callback */);`
124 //
125 // C++11 type deduction API for creating an instance of `absl::Cleanup`
126 template <typename... Args, typename Callback>
MakeCleanup(Callback callback)127 absl::Cleanup<cleanup_internal::Tag, Callback> MakeCleanup(Callback callback) {
128 static_assert(cleanup_internal::WasDeduced<cleanup_internal::Tag, Args...>(),
129 "Explicit template parameters are not supported.");
130
131 static_assert(cleanup_internal::ReturnsVoid<Callback>(),
132 "Callbacks that return values are not supported.");
133
134 return {std::move(callback)};
135 }
136
137 ABSL_NAMESPACE_END
138 } // namespace absl
139
140 #endif // ABSL_CLEANUP_CLEANUP_H_
141