cleanup.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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. // `y_absl::Cleanup` implements the scope guard idiom, invoking the contained
  20. // callback's `operator()() &&` on scope exit.
  21. //
  22. // Example:
  23. //
  24. // ```
  25. // y_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 y_absl::NotFoundError("No source file"); // No cleanups execute
  29. // }
  30. //
  31. // // C++17 style cleanup using class template argument deduction
  32. // y_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 y_absl::NotFoundError("No sink file"); // First cleanup executes
  37. // }
  38. //
  39. // // C++11 style cleanup using the factory function
  40. // auto sink_closer = y_absl::MakeCleanup([sink_file] { fclose(sink_file); });
  41. //
  42. // Data data;
  43. // while (ReadData(source_file, &data)) {
  44. // if (!data.IsGood()) {
  45. // y_absl::Status result = y_absl::FailedPreconditionError("Read bad data");
  46. // return result; // Both cleanups execute
  47. // }
  48. // SaveData(sink_file, &data);
  49. // }
  50. //
  51. // return y_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. // `y_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. #ifndef Y_ABSL_CLEANUP_CLEANUP_H_
  68. #define Y_ABSL_CLEANUP_CLEANUP_H_
  69. #include <utility>
  70. #include "y_absl/base/config.h"
  71. #include "y_absl/base/macros.h"
  72. #include "y_absl/cleanup/internal/cleanup.h"
  73. namespace y_absl {
  74. Y_ABSL_NAMESPACE_BEGIN
  75. template <typename Arg, typename Callback = void()>
  76. class Y_ABSL_MUST_USE_RESULT Cleanup final {
  77. static_assert(cleanup_internal::WasDeduced<Arg>(),
  78. "Explicit template parameters are not supported.");
  79. static_assert(cleanup_internal::ReturnsVoid<Callback>(),
  80. "Callbacks that return values are not supported.");
  81. public:
  82. Cleanup(Callback callback) : storage_(std::move(callback)) {} // NOLINT
  83. Cleanup(Cleanup&& other) = default;
  84. void Cancel() && {
  85. Y_ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
  86. storage_.DestroyCallback();
  87. }
  88. void Invoke() && {
  89. Y_ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
  90. storage_.InvokeCallback();
  91. storage_.DestroyCallback();
  92. }
  93. ~Cleanup() {
  94. if (storage_.IsCallbackEngaged()) {
  95. storage_.InvokeCallback();
  96. storage_.DestroyCallback();
  97. }
  98. }
  99. private:
  100. cleanup_internal::Storage<Callback> storage_;
  101. };
  102. // `y_absl::Cleanup c = /* callback */;`
  103. //
  104. // C++17 type deduction API for creating an instance of `y_absl::Cleanup`
  105. #if defined(Y_ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
  106. template <typename Callback>
  107. Cleanup(Callback callback) -> Cleanup<cleanup_internal::Tag, Callback>;
  108. #endif // defined(Y_ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
  109. // `auto c = y_absl::MakeCleanup(/* callback */);`
  110. //
  111. // C++11 type deduction API for creating an instance of `y_absl::Cleanup`
  112. template <typename... Args, typename Callback>
  113. y_absl::Cleanup<cleanup_internal::Tag, Callback> MakeCleanup(Callback callback) {
  114. static_assert(cleanup_internal::WasDeduced<cleanup_internal::Tag, Args...>(),
  115. "Explicit template parameters are not supported.");
  116. static_assert(cleanup_internal::ReturnsVoid<Callback>(),
  117. "Callbacks that return values are not supported.");
  118. return {std::move(callback)};
  119. }
  120. Y_ABSL_NAMESPACE_END
  121. } // namespace y_absl
  122. #endif // Y_ABSL_CLEANUP_CLEANUP_H_