Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,140 @@
// Copyright 2021 The Abseil Authors.
//
// 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
//
// https://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.
//
// -----------------------------------------------------------------------------
// File: cleanup.h
// -----------------------------------------------------------------------------
//
// `absl::Cleanup` implements the scope guard idiom, invoking the contained
// callback's `operator()() &&` on scope exit.
//
// Example:
//
// ```
// absl::Status CopyGoodData(const char* source_path, const char* sink_path) {
// FILE* source_file = fopen(source_path, "r");
// if (source_file == nullptr) {
// return absl::NotFoundError("No source file"); // No cleanups execute
// }
//
// // C++17 style cleanup using class template argument deduction
// absl::Cleanup source_closer = [source_file] { fclose(source_file); };
//
// FILE* sink_file = fopen(sink_path, "w");
// if (sink_file == nullptr) {
// return absl::NotFoundError("No sink file"); // First cleanup executes
// }
//
// // C++11 style cleanup using the factory function
// auto sink_closer = absl::MakeCleanup([sink_file] { fclose(sink_file); });
//
// Data data;
// while (ReadData(source_file, &data)) {
// if (!data.IsGood()) {
// absl::Status result = absl::FailedPreconditionError("Read bad data");
// return result; // Both cleanups execute
// }
// SaveData(sink_file, &data);
// }
//
// return absl::OkStatus(); // Both cleanups execute
// }
// ```
//
// Methods:
//
// `std::move(cleanup).Cancel()` will prevent the callback from executing.
//
// `std::move(cleanup).Invoke()` will execute the callback early, before
// destruction, and prevent the callback from executing in the destructor.
//
// Usage:
//
// `absl::Cleanup` is not an interface type. It is only intended to be used
// within the body of a function. It is not a value type and instead models a
// control flow construct. Check out `defer` in Golang for something similar.
#ifndef ABSL_CLEANUP_CLEANUP_H_
#define ABSL_CLEANUP_CLEANUP_H_
#include <utility>
#include "absl/base/config.h"
#include "absl/base/macros.h"
#include "absl/cleanup/internal/cleanup.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
template <typename Arg, typename Callback = void()>
class ABSL_MUST_USE_RESULT Cleanup final {
static_assert(cleanup_internal::WasDeduced<Arg>(),
"Explicit template parameters are not supported.");
static_assert(cleanup_internal::ReturnsVoid<Callback>(),
"Callbacks that return values are not supported.");
public:
Cleanup(Callback callback) : storage_(std::move(callback)) {} // NOLINT
Cleanup(Cleanup&& other) = default;
void Cancel() && {
ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
storage_.DestroyCallback();
}
void Invoke() && {
ABSL_HARDENING_ASSERT(storage_.IsCallbackEngaged());
storage_.InvokeCallback();
storage_.DestroyCallback();
}
~Cleanup() {
if (storage_.IsCallbackEngaged()) {
storage_.InvokeCallback();
storage_.DestroyCallback();
}
}
private:
cleanup_internal::Storage<Callback> storage_;
};
// `absl::Cleanup c = /* callback */;`
//
// C++17 type deduction API for creating an instance of `absl::Cleanup`
#if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
template <typename Callback>
Cleanup(Callback callback) -> Cleanup<cleanup_internal::Tag, Callback>;
#endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
// `auto c = absl::MakeCleanup(/* callback */);`
//
// C++11 type deduction API for creating an instance of `absl::Cleanup`
template <typename... Args, typename Callback>
absl::Cleanup<cleanup_internal::Tag, Callback> MakeCleanup(Callback callback) {
static_assert(cleanup_internal::WasDeduced<cleanup_internal::Tag, Args...>(),
"Explicit template parameters are not supported.");
static_assert(cleanup_internal::ReturnsVoid<Callback>(),
"Callbacks that return values are not supported.");
return {std::move(callback)};
}
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CLEANUP_CLEANUP_H_

View file

@ -0,0 +1,311 @@
// Copyright 2021 The Abseil Authors.
//
// 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
//
// https://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.
#include "absl/cleanup/cleanup.h"
#include <functional>
#include <type_traits>
#include <utility>
#include "gtest/gtest.h"
#include "absl/base/config.h"
#include "absl/utility/utility.h"
namespace {
using Tag = absl::cleanup_internal::Tag;
template <typename Type1, typename Type2>
constexpr bool IsSame() {
return (std::is_same<Type1, Type2>::value);
}
struct IdentityFactory {
template <typename Callback>
static Callback AsCallback(Callback callback) {
return Callback(std::move(callback));
}
};
// `FunctorClass` is a type used for testing `absl::Cleanup`. It is intended to
// represent users that make their own move-only callback types outside of
// `std::function` and lambda literals.
class FunctorClass {
using Callback = std::function<void()>;
public:
explicit FunctorClass(Callback callback) : callback_(std::move(callback)) {}
FunctorClass(FunctorClass&& other)
: callback_(std::exchange(other.callback_, Callback())) {}
FunctorClass(const FunctorClass&) = delete;
FunctorClass& operator=(const FunctorClass&) = delete;
FunctorClass& operator=(FunctorClass&&) = delete;
void operator()() const& = delete;
void operator()() && {
ASSERT_TRUE(callback_);
callback_();
callback_ = nullptr;
}
private:
Callback callback_;
};
struct FunctorClassFactory {
template <typename Callback>
static FunctorClass AsCallback(Callback callback) {
return FunctorClass(std::move(callback));
}
};
struct StdFunctionFactory {
template <typename Callback>
static std::function<void()> AsCallback(Callback callback) {
return std::function<void()>(std::move(callback));
}
};
using CleanupTestParams =
::testing::Types<IdentityFactory, FunctorClassFactory, StdFunctionFactory>;
template <typename>
struct CleanupTest : public ::testing::Test {};
TYPED_TEST_SUITE(CleanupTest, CleanupTestParams);
bool fn_ptr_called = false;
void FnPtrFunction() { fn_ptr_called = true; }
TYPED_TEST(CleanupTest, FactoryProducesCorrectType) {
{
auto callback = TypeParam::AsCallback([] {});
auto cleanup = absl::MakeCleanup(std::move(callback));
static_assert(
IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
"");
}
{
auto cleanup = absl::MakeCleanup(&FnPtrFunction);
static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
}
{
auto cleanup = absl::MakeCleanup(FnPtrFunction);
static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
}
}
#if defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
TYPED_TEST(CleanupTest, CTADProducesCorrectType) {
{
auto callback = TypeParam::AsCallback([] {});
absl::Cleanup cleanup = std::move(callback);
static_assert(
IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
"");
}
{
absl::Cleanup cleanup = &FnPtrFunction;
static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
}
{
absl::Cleanup cleanup = FnPtrFunction;
static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
}
}
TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) {
{
auto callback = IdentityFactory::AsCallback([] {});
auto factory_cleanup = absl::MakeCleanup(callback);
absl::Cleanup deduction_cleanup = callback;
static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
}
{
auto factory_cleanup =
absl::MakeCleanup(FunctorClassFactory::AsCallback([] {}));
absl::Cleanup deduction_cleanup = FunctorClassFactory::AsCallback([] {});
static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
}
{
auto factory_cleanup =
absl::MakeCleanup(StdFunctionFactory::AsCallback([] {}));
absl::Cleanup deduction_cleanup = StdFunctionFactory::AsCallback([] {});
static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
}
{
auto factory_cleanup = absl::MakeCleanup(&FnPtrFunction);
absl::Cleanup deduction_cleanup = &FnPtrFunction;
static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
}
{
auto factory_cleanup = absl::MakeCleanup(FnPtrFunction);
absl::Cleanup deduction_cleanup = FnPtrFunction;
static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
}
}
#endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
TYPED_TEST(CleanupTest, BasicUsage) {
bool called = false;
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
}
EXPECT_TRUE(called); // Destructor should invoke the callback
}
TYPED_TEST(CleanupTest, BasicUsageWithFunctionPointer) {
fn_ptr_called = false;
{
auto cleanup = absl::MakeCleanup(TypeParam::AsCallback(&FnPtrFunction));
EXPECT_FALSE(fn_ptr_called); // Constructor shouldn't invoke the callback
}
EXPECT_TRUE(fn_ptr_called); // Destructor should invoke the callback
}
TYPED_TEST(CleanupTest, Cancel) {
bool called = false;
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
std::move(cleanup).Cancel();
EXPECT_FALSE(called); // Cancel shouldn't invoke the callback
}
EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
}
TYPED_TEST(CleanupTest, Invoke) {
bool called = false;
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
std::move(cleanup).Invoke();
EXPECT_TRUE(called); // Invoke should invoke the callback
called = false; // Reset tracker before destructor runs
}
EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
}
TYPED_TEST(CleanupTest, Move) {
bool called = false;
{
auto moved_from_cleanup =
absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
{
auto moved_to_cleanup = std::move(moved_from_cleanup);
EXPECT_FALSE(called); // Move shouldn't invoke the callback
}
EXPECT_TRUE(called); // Destructor should invoke the callback
called = false; // Reset tracker before destructor runs
}
EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
}
int DestructionCount = 0;
struct DestructionCounter {
void operator()() {}
~DestructionCounter() { ++DestructionCount; }
};
TYPED_TEST(CleanupTest, DestructorDestroys) {
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
DestructionCount = 0;
}
EXPECT_EQ(DestructionCount, 1); // Engaged cleanup destroys
}
TYPED_TEST(CleanupTest, CancelDestroys) {
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
DestructionCount = 0;
std::move(cleanup).Cancel();
EXPECT_EQ(DestructionCount, 1); // Cancel destroys
}
EXPECT_EQ(DestructionCount, 1); // Canceled cleanup does not double destroy
}
TYPED_TEST(CleanupTest, InvokeDestroys) {
{
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback(DestructionCounter()));
DestructionCount = 0;
std::move(cleanup).Invoke();
EXPECT_EQ(DestructionCount, 1); // Invoke destroys
}
EXPECT_EQ(DestructionCount, 1); // Invoked cleanup does not double destroy
}
} // namespace

View file

@ -0,0 +1,100 @@
// Copyright 2021 The Abseil Authors.
//
// 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
//
// https://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.
#ifndef ABSL_CLEANUP_INTERNAL_CLEANUP_H_
#define ABSL_CLEANUP_INTERNAL_CLEANUP_H_
#include <new>
#include <type_traits>
#include <utility>
#include "absl/base/internal/invoke.h"
#include "absl/base/macros.h"
#include "absl/base/thread_annotations.h"
#include "absl/utility/utility.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace cleanup_internal {
struct Tag {};
template <typename Arg, typename... Args>
constexpr bool WasDeduced() {
return (std::is_same<cleanup_internal::Tag, Arg>::value) &&
(sizeof...(Args) == 0);
}
template <typename Callback>
constexpr bool ReturnsVoid() {
return (std::is_same<base_internal::invoke_result_t<Callback>, void>::value);
}
template <typename Callback>
class Storage {
public:
Storage() = delete;
explicit Storage(Callback callback) {
// Placement-new into a character buffer is used for eager destruction when
// the cleanup is invoked or cancelled. To ensure this optimizes well, the
// behavior is implemented locally instead of using an absl::optional.
::new (GetCallbackBuffer()) Callback(std::move(callback));
is_callback_engaged_ = true;
}
Storage(Storage&& other) {
ABSL_HARDENING_ASSERT(other.IsCallbackEngaged());
::new (GetCallbackBuffer()) Callback(std::move(other.GetCallback()));
is_callback_engaged_ = true;
other.DestroyCallback();
}
Storage(const Storage& other) = delete;
Storage& operator=(Storage&& other) = delete;
Storage& operator=(const Storage& other) = delete;
void* GetCallbackBuffer() { return static_cast<void*>(callback_buffer_); }
Callback& GetCallback() {
return *reinterpret_cast<Callback*>(GetCallbackBuffer());
}
bool IsCallbackEngaged() const { return is_callback_engaged_; }
void DestroyCallback() {
is_callback_engaged_ = false;
GetCallback().~Callback();
}
void InvokeCallback() ABSL_NO_THREAD_SAFETY_ANALYSIS {
std::move(GetCallback())();
}
private:
bool is_callback_engaged_;
alignas(Callback) char callback_buffer_[sizeof(Callback)];
};
} // namespace cleanup_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_CLEANUP_INTERNAL_CLEANUP_H_