| // 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 <array> | |
| #include <initializer_list> | |
| #include <string> | |
| #include <type_traits> | |
| #include <vector> | |
| #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<int> 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<TypeWithDeletedDestructor> a; | |
| (void)a; | |
| } | |
| TEST(NoDestructorTest, Noncopyable) { | |
| using T = absl::NoDestructor<int>; | |
| EXPECT_FALSE((std::is_constructible_v<T, T>)); | |
| EXPECT_FALSE((std::is_constructible_v<T, const T>)); | |
| EXPECT_FALSE((std::is_constructible_v<T, T&>)); | |
| EXPECT_FALSE((std::is_constructible_v<T, const T&>)); | |
| EXPECT_FALSE((std::is_assignable_v<T&, T>)); | |
| EXPECT_FALSE((std::is_assignable_v<T&, const T>)); | |
| EXPECT_FALSE((std::is_assignable_v<T&, T&>)); | |
| EXPECT_FALSE((std::is_assignable_v<T&, const T&>)); | |
| } | |
| TEST(NoDestructorTest, Interface) { | |
| EXPECT_TRUE(std::is_trivially_destructible_v<absl::NoDestructor<Blob>>); | |
| EXPECT_TRUE(std::is_trivially_destructible_v<absl::NoDestructor<const Blob>>); | |
| { | |
| absl::NoDestructor<Blob> 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<const Blob> 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<Blob> b{ | |
| {20, 28, 40}}; // init-list c-tor, deep const | |
| // This only works in clang, not in gcc: | |
| // const absl::NoDestructor<Blob> 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<UsesAbstractInConstructor> foo1(input); | |
| EXPECT_EQ(foo1->i, 17); | |
| absl::NoDestructor<UsesAbstractInConstructor> foo2( | |
| static_cast<const Abstract&>(input)); | |
| EXPECT_EQ(foo2->i, 17); | |
| } | |
| // ========================================================================= // | |
| std::string* Str0() { | |
| static absl::NoDestructor<std::string> x; | |
| return x.get(); | |
| } | |
| extern const std::string& Str2(); | |
| const char* Str1() { | |
| static absl::NoDestructor<std::string> x(Str2() + "_Str1"); | |
| return x->c_str(); | |
| } | |
| const std::string& Str2() { | |
| static absl::NoDestructor<std::string> x("Str2"); | |
| return *x; | |
| } | |
| const std::string& Str2Copy() { | |
| // Exercise copy construction | |
| static absl::NoDestructor<std::string> x(Str2()); | |
| return *x; | |
| } | |
| typedef std::array<std::string, 3> MyArray; | |
| const MyArray& Array() { | |
| static absl::NoDestructor<MyArray> x{{{"foo", "bar", "baz"}}}; | |
| // This only works in clang, not in gcc: | |
| // static absl::NoDestructor<MyArray> x({{"foo", "bar", "baz"}}); | |
| return *x; | |
| } | |
| typedef std::vector<int> MyVector; | |
| const MyVector& Vector() { | |
| static absl::NoDestructor<MyVector> x{{1, 2, 3}}; | |
| return *x; | |
| } | |
| const int& Int() { | |
| static absl::NoDestructor<int> x; | |
| return *x; | |
| } | |
| TEST(NoDestructorTest, StaticPattern) { | |
| EXPECT_TRUE( | |
| std::is_trivially_destructible_v<absl::NoDestructor<std::string>>); | |
| EXPECT_TRUE(std::is_trivially_destructible_v<absl::NoDestructor<MyArray>>); | |
| EXPECT_TRUE(std::is_trivially_destructible_v<absl::NoDestructor<MyVector>>); | |
| EXPECT_TRUE(std::is_trivially_destructible_v<absl::NoDestructor<int>>); | |
| 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 | |
| } | |
| TEST(NoDestructorTest, ClassTemplateArgumentDeduction) { | |
| absl::NoDestructor i(1); | |
| static_assert(std::is_same_v<decltype(i), absl::NoDestructor<int>>, | |
| "Expected deduced type to be int."); | |
| } | |
| } // namespace |