// Copyright 2023 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/base/no_destructor.h" #include #include #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/config.h" #include "absl/base/internal/raw_logging.h" namespace { struct Blob { Blob() : val(42) {} Blob(int x, int y) : val(x + y) {} Blob(std::initializer_list xs) { val = 0; for (auto& x : xs) val += x; } Blob(const Blob& /*b*/) = delete; Blob(Blob&& b) noexcept : val(b.val) { b.moved_out = true; } // moving is fine // no crash: NoDestructor indeed does not destruct (the moved-out Blob // temporaries do get destroyed though) ~Blob() { ABSL_INTERNAL_CHECK(moved_out, "~Blob"); } int val; bool moved_out = false; }; struct TypeWithDeletedDestructor { ~TypeWithDeletedDestructor() = delete; }; TEST(NoDestructorTest, DestructorNeverCalled) { absl::NoDestructor a; (void)a; } TEST(NoDestructorTest, Noncopyable) { using T = absl::NoDestructor; EXPECT_FALSE((std::is_constructible::value)); EXPECT_FALSE((std::is_constructible::value)); EXPECT_FALSE((std::is_constructible::value)); EXPECT_FALSE((std::is_constructible::value)); EXPECT_FALSE((std::is_assignable::value)); EXPECT_FALSE((std::is_assignable::value)); EXPECT_FALSE((std::is_assignable::value)); EXPECT_FALSE((std::is_assignable::value)); } TEST(NoDestructorTest, Interface) { EXPECT_TRUE(std::is_trivially_destructible>::value); EXPECT_TRUE( std::is_trivially_destructible>::value); { absl::NoDestructor b; // default c-tor // access: *, ->, get() EXPECT_EQ(42, (*b).val); (*b).val = 55; EXPECT_EQ(55, b->val); b->val = 66; EXPECT_EQ(66, b.get()->val); b.get()->val = 42; // NOLINT EXPECT_EQ(42, (*b).val); } { absl::NoDestructor b(70, 7); // regular c-tor, const EXPECT_EQ(77, (*b).val); EXPECT_EQ(77, b->val); EXPECT_EQ(77, b.get()->val); } { const absl::NoDestructor b{ {20, 28, 40}}; // init-list c-tor, deep const // This only works in clang, not in gcc: // const absl::NoDestructor b({20, 28, 40}); EXPECT_EQ(88, (*b).val); EXPECT_EQ(88, b->val); EXPECT_EQ(88, b.get()->val); } } TEST(NoDestructorTest, SfinaeRegressionAbstractArg) { struct Abstract { virtual ~Abstract() = default; virtual int foo() const = 0; }; struct Concrete : Abstract { int foo() const override { return 17; } }; struct UsesAbstractInConstructor { explicit UsesAbstractInConstructor(const Abstract& abstract) : i(abstract.foo()) {} int i; }; Concrete input; absl::NoDestructor foo1(input); EXPECT_EQ(foo1->i, 17); absl::NoDestructor foo2( static_cast(input)); EXPECT_EQ(foo2->i, 17); } // ========================================================================= // std::string* Str0() { static absl::NoDestructor x; return x.get(); } extern const std::string& Str2(); const char* Str1() { static absl::NoDestructor x(Str2() + "_Str1"); return x->c_str(); } const std::string& Str2() { static absl::NoDestructor x("Str2"); return *x; } const std::string& Str2Copy() { // Exercise copy construction static absl::NoDestructor x(Str2()); return *x; } typedef std::array MyArray; const MyArray& Array() { static absl::NoDestructor x{{{"foo", "bar", "baz"}}}; // This only works in clang, not in gcc: // static absl::NoDestructor x({{"foo", "bar", "baz"}}); return *x; } typedef std::vector MyVector; const MyVector& Vector() { static absl::NoDestructor x{{1, 2, 3}}; return *x; } const int& Int() { static absl::NoDestructor x; return *x; } TEST(NoDestructorTest, StaticPattern) { EXPECT_TRUE( std::is_trivially_destructible>::value); EXPECT_TRUE( std::is_trivially_destructible>::value); EXPECT_TRUE( std::is_trivially_destructible>::value); EXPECT_TRUE(std::is_trivially_destructible>::value); EXPECT_EQ(*Str0(), ""); Str0()->append("foo"); EXPECT_EQ(*Str0(), "foo"); EXPECT_EQ(std::string(Str1()), "Str2_Str1"); EXPECT_EQ(Str2(), "Str2"); EXPECT_EQ(Str2Copy(), "Str2"); EXPECT_THAT(Array(), testing::ElementsAre("foo", "bar", "baz")); EXPECT_THAT(Vector(), testing::ElementsAre(1, 2, 3)); EXPECT_EQ(0, Int()); // should get zero-initialized } #ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION // This would fail to compile if Class Template Argument Deduction was not // provided for absl::NoDestructor. TEST(NoDestructorTest, ClassTemplateArgumentDeduction) { absl::NoDestructor i(1); static_assert(std::is_same>::value, "Expected deduced type to be int."); } #endif } // namespace