Export of internal Abseil changes

--
8c77b14bdee3f4cafb8ba520d4d050b15a949fd4 by Derek Mauro <dmauro@google.com>:

Fix absl::Cleanup usage example

PiperOrigin-RevId: 354702001

--
10365da7a0aacaa0c4774a4b618a76dff328611b by CJ Johnson <johnsoncj@google.com>:

Swap the order of the C++11 and C++17 interfaces for absl::Cleanup to mirror the order used in the comment example

PiperOrigin-RevId: 354675180
GitOrigin-RevId: 8c77b14bdee3f4cafb8ba520d4d050b15a949fd4
Change-Id: Ia2054b725ed737ff9e557cb3d973de7c34bc51b0
pull/898/head
Abseil Team 4 years ago committed by Derek Mauro
parent a9a4956020
commit 184d2f8364
  1. 42
      absl/cleanup/cleanup.h
  2. 117
      absl/cleanup/cleanup_test.cc
  3. 2
      absl/cleanup/internal/cleanup.h

@ -24,36 +24,36 @@
// ``` // ```
// void CopyGoodData(const char* input_path, const char* output_path) { // void CopyGoodData(const char* input_path, const char* output_path) {
// FILE* in_file = fopen(input_path, "r"); // FILE* in_file = fopen(input_path, "r");
// FILE* out_file = fopen(output_path, "w"); // if (in_file == nullptr) return;
// if (in_file == nullptr || out_file == nullptr) return;
// //
// // C++17 style using class template argument deduction // // C++17 style using class template argument deduction
// absl::Cleanup in_closer = [&in_file] { fclose(in_file); }; // absl::Cleanup in_closer = [in_file] { fclose(in_file); };
// //
// // C++11 style using the factory function // FILE* out_file = fopen(output_path, "w");
// auto out_closer = absl::MakeCleanup([&out_file] { fclose(out_file); }); // if (out_file == nullptr) return; // `in_closer` will run
// //
// // `fclose` will be called on all exit paths by the cleanup instances // // C++11 style using the factory function
// auto out_closer = absl::MakeCleanup([out_file] { fclose(out_file); });
// //
// Data data; // Data data;
// while (ReadData(in_file, &data)) { // while (ReadData(in_file, &data)) {
// if (data.IsBad()) { // if (data.IsBad()) {
// LOG(ERROR) << "Found bad data."; // LOG(ERROR) << "Found bad data.";
// return; // `in_closer` and `out_closer` will call their callbacks // return; // `in_closer` and `out_closer` will run
// } // }
// SaveData(out_file, &data); // SaveData(out_file, &data);
// } // }
// return; // `in_closer` and `out_closer` will call their callbacks //
// // `in_closer` and `out_closer` will run
// } // }
// ``` // ```
// //
// `std::move(cleanup).Invoke()` will execute the callback early, before // Methods:
// destruction, and prevent the callback from executing in the destructor.
// //
// Alternatively, `std::move(cleanup).Cancel()` will prevent the callback from // `std::move(cleanup).Cancel()` will prevent the callback from executing.
// ever executing at all.
// //
// Once a cleanup object has been `std::move(...)`-ed, it may not be used again. // `std::move(cleanup).Invoke()` will execute the callback early, before
// destruction, and prevent the callback from executing in the destructor.
#ifndef ABSL_CLEANUP_CLEANUP_H_ #ifndef ABSL_CLEANUP_CLEANUP_H_
#define ABSL_CLEANUP_CLEANUP_H_ #define ABSL_CLEANUP_CLEANUP_H_
@ -101,6 +101,14 @@ class ABSL_MUST_USE_RESULT Cleanup {
cleanup_internal::Storage<Callback> storage_; 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 */);` // `auto c = absl::MakeCleanup(/* callback */);`
// //
// C++11 type deduction API for creating an instance of `absl::Cleanup`. // C++11 type deduction API for creating an instance of `absl::Cleanup`.
@ -115,14 +123,6 @@ absl::Cleanup<cleanup_internal::Tag, Callback> MakeCleanup(Callback callback) {
return {std::move(callback)}; return {std::move(callback)};
} }
// `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)
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl

@ -27,8 +27,8 @@ namespace {
using Tag = absl::cleanup_internal::Tag; using Tag = absl::cleanup_internal::Tag;
template <typename Type1, typename Type2> template <typename Type1, typename Type2>
void AssertSameType() { constexpr bool IsSame() {
static_assert(std::is_same<Type1, Type2>::value, ""); return (std::is_same<Type1, Type2>::value);
} }
struct IdentityFactory { struct IdentityFactory {
@ -88,27 +88,31 @@ template <typename>
struct CleanupTest : public ::testing::Test {}; struct CleanupTest : public ::testing::Test {};
TYPED_TEST_SUITE(CleanupTest, CleanupTestParams); TYPED_TEST_SUITE(CleanupTest, CleanupTestParams);
bool function_pointer_called = false; bool fn_ptr_called = false;
void FunctionPointerFunction() { function_pointer_called = true; } void FnPtrFunction() { fn_ptr_called = true; }
TYPED_TEST(CleanupTest, FactoryProducesCorrectType) { TYPED_TEST(CleanupTest, FactoryProducesCorrectType) {
{ {
auto callback = TypeParam::AsCallback([] {}); auto callback = TypeParam::AsCallback([] {});
auto cleanup = absl::MakeCleanup(std::move(callback)); auto cleanup = absl::MakeCleanup(std::move(callback));
AssertSameType<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(); static_assert(
IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
"");
} }
{ {
auto cleanup = absl::MakeCleanup(&FunctionPointerFunction); auto cleanup = absl::MakeCleanup(&FnPtrFunction);
AssertSameType<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(); static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
} }
{ {
auto cleanup = absl::MakeCleanup(FunctionPointerFunction); auto cleanup = absl::MakeCleanup(FnPtrFunction);
AssertSameType<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(); static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
} }
} }
@ -118,19 +122,23 @@ TYPED_TEST(CleanupTest, CTADProducesCorrectType) {
auto callback = TypeParam::AsCallback([] {}); auto callback = TypeParam::AsCallback([] {});
absl::Cleanup cleanup = std::move(callback); absl::Cleanup cleanup = std::move(callback);
AssertSameType<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(); static_assert(
IsSame<absl::Cleanup<Tag, decltype(callback)>, decltype(cleanup)>(),
"");
} }
{ {
absl::Cleanup cleanup = &FunctionPointerFunction; absl::Cleanup cleanup = &FnPtrFunction;
AssertSameType<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(); static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
} }
{ {
absl::Cleanup cleanup = FunctionPointerFunction; absl::Cleanup cleanup = FnPtrFunction;
AssertSameType<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(); static_assert(IsSame<absl::Cleanup<Tag, void (*)()>, decltype(cleanup)>(),
"");
} }
} }
@ -140,7 +148,8 @@ TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) {
auto factory_cleanup = absl::MakeCleanup(callback); auto factory_cleanup = absl::MakeCleanup(callback);
absl::Cleanup deduction_cleanup = callback; absl::Cleanup deduction_cleanup = callback;
AssertSameType<decltype(factory_cleanup), decltype(deduction_cleanup)>(); static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
} }
{ {
@ -148,7 +157,8 @@ TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) {
absl::MakeCleanup(FunctorClassFactory::AsCallback([] {})); absl::MakeCleanup(FunctorClassFactory::AsCallback([] {}));
absl::Cleanup deduction_cleanup = FunctorClassFactory::AsCallback([] {}); absl::Cleanup deduction_cleanup = FunctorClassFactory::AsCallback([] {});
AssertSameType<decltype(factory_cleanup), decltype(deduction_cleanup)>(); static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
} }
{ {
@ -156,21 +166,24 @@ TYPED_TEST(CleanupTest, FactoryAndCTADProduceSameType) {
absl::MakeCleanup(StdFunctionFactory::AsCallback([] {})); absl::MakeCleanup(StdFunctionFactory::AsCallback([] {}));
absl::Cleanup deduction_cleanup = StdFunctionFactory::AsCallback([] {}); absl::Cleanup deduction_cleanup = StdFunctionFactory::AsCallback([] {});
AssertSameType<decltype(factory_cleanup), decltype(deduction_cleanup)>(); static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
} }
{ {
auto factory_cleanup = absl::MakeCleanup(&FunctionPointerFunction); auto factory_cleanup = absl::MakeCleanup(&FnPtrFunction);
absl::Cleanup deduction_cleanup = &FunctionPointerFunction; absl::Cleanup deduction_cleanup = &FnPtrFunction;
AssertSameType<decltype(factory_cleanup), decltype(deduction_cleanup)>(); static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
} }
{ {
auto factory_cleanup = absl::MakeCleanup(FunctionPointerFunction); auto factory_cleanup = absl::MakeCleanup(FnPtrFunction);
absl::Cleanup deduction_cleanup = FunctionPointerFunction; absl::Cleanup deduction_cleanup = FnPtrFunction;
AssertSameType<decltype(factory_cleanup), decltype(deduction_cleanup)>(); static_assert(
IsSame<decltype(factory_cleanup), decltype(deduction_cleanup)>(), "");
} }
} }
#endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION) #endif // defined(ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION)
@ -179,62 +192,76 @@ TYPED_TEST(CleanupTest, BasicUsage) {
bool called = false; bool called = false;
{ {
EXPECT_FALSE(called);
auto cleanup = auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
EXPECT_FALSE(called);
} }
EXPECT_TRUE(called); EXPECT_TRUE(called); // Destructor should invoke the callback
} }
TYPED_TEST(CleanupTest, BasicUsageWithFunctionPointer) { TYPED_TEST(CleanupTest, BasicUsageWithFunctionPointer) {
function_pointer_called = false; fn_ptr_called = false;
{ {
EXPECT_FALSE(function_pointer_called); auto cleanup = absl::MakeCleanup(TypeParam::AsCallback(&FnPtrFunction));
EXPECT_FALSE(fn_ptr_called); // Constructor shouldn't invoke the callback
auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback(&FunctionPointerFunction));
EXPECT_FALSE(function_pointer_called);
} }
EXPECT_TRUE(function_pointer_called); EXPECT_TRUE(fn_ptr_called); // Destructor should invoke the callback
} }
TYPED_TEST(CleanupTest, Cancel) { TYPED_TEST(CleanupTest, Cancel) {
bool called = false; bool called = false;
{ {
EXPECT_FALSE(called);
auto cleanup = auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
std::move(cleanup).Cancel(); EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
EXPECT_FALSE(called); std::move(cleanup).Cancel();
EXPECT_FALSE(called); // Cancel shouldn't invoke the callback
} }
EXPECT_FALSE(called); EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
} }
TYPED_TEST(CleanupTest, Invoke) { TYPED_TEST(CleanupTest, Invoke) {
bool called = false; bool called = false;
{ {
EXPECT_FALSE(called);
auto cleanup = auto cleanup =
absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; })); absl::MakeCleanup(TypeParam::AsCallback([&called] { called = true; }));
EXPECT_FALSE(called); // Constructor shouldn't invoke the callback
std::move(cleanup).Invoke(); 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
EXPECT_TRUE(called); called = false; // Reset tracker before destructor runs
} }
EXPECT_TRUE(called); EXPECT_FALSE(called); // Destructor shouldn't invoke the callback
} }
} // namespace } // namespace

@ -43,6 +43,8 @@ constexpr bool ReturnsVoid() {
template <typename Callback> template <typename Callback>
class Storage { class Storage {
public: public:
Storage() = delete;
explicit Storage(Callback callback) explicit Storage(Callback callback)
: engaged_(true), callback_(std::move(callback)) {} : engaged_(true), callback_(std::move(callback)) {}

Loading…
Cancel
Save