no_destructor_test.cc (5661B)
1 // Copyright 2023 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 #include "absl/base/no_destructor.h" 16 17 #include <array> 18 #include <initializer_list> 19 #include <string> 20 #include <type_traits> 21 #include <vector> 22 23 #include "gmock/gmock.h" 24 #include "gtest/gtest.h" 25 #include "absl/base/config.h" 26 #include "absl/base/internal/raw_logging.h" 27 28 namespace { 29 30 struct Blob { 31 Blob() : val(42) {} 32 Blob(int x, int y) : val(x + y) {} 33 Blob(std::initializer_list<int> xs) { 34 val = 0; 35 for (auto& x : xs) val += x; 36 } 37 38 Blob(const Blob& /*b*/) = delete; 39 Blob(Blob&& b) noexcept : val(b.val) { 40 b.moved_out = true; 41 } // moving is fine 42 43 // no crash: NoDestructor indeed does not destruct (the moved-out Blob 44 // temporaries do get destroyed though) 45 ~Blob() { ABSL_INTERNAL_CHECK(moved_out, "~Blob"); } 46 47 int val; 48 bool moved_out = false; 49 }; 50 51 struct TypeWithDeletedDestructor { 52 ~TypeWithDeletedDestructor() = delete; 53 }; 54 55 TEST(NoDestructorTest, DestructorNeverCalled) { 56 absl::NoDestructor<TypeWithDeletedDestructor> a; 57 (void)a; 58 } 59 60 TEST(NoDestructorTest, Noncopyable) { 61 using T = absl::NoDestructor<int>; 62 63 EXPECT_FALSE((std::is_constructible<T, T>::value)); 64 EXPECT_FALSE((std::is_constructible<T, const T>::value)); 65 EXPECT_FALSE((std::is_constructible<T, T&>::value)); 66 EXPECT_FALSE((std::is_constructible<T, const T&>::value)); 67 68 EXPECT_FALSE((std::is_assignable<T&, T>::value)); 69 EXPECT_FALSE((std::is_assignable<T&, const T>::value)); 70 EXPECT_FALSE((std::is_assignable<T&, T&>::value)); 71 EXPECT_FALSE((std::is_assignable<T&, const T&>::value)); 72 } 73 74 TEST(NoDestructorTest, Interface) { 75 EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<Blob>>::value); 76 EXPECT_TRUE( 77 std::is_trivially_destructible<absl::NoDestructor<const Blob>>::value); 78 { 79 absl::NoDestructor<Blob> b; // default c-tor 80 // access: *, ->, get() 81 EXPECT_EQ(42, (*b).val); 82 (*b).val = 55; 83 EXPECT_EQ(55, b->val); 84 b->val = 66; 85 EXPECT_EQ(66, b.get()->val); 86 b.get()->val = 42; // NOLINT 87 EXPECT_EQ(42, (*b).val); 88 } 89 { 90 absl::NoDestructor<const Blob> b(70, 7); // regular c-tor, const 91 EXPECT_EQ(77, (*b).val); 92 EXPECT_EQ(77, b->val); 93 EXPECT_EQ(77, b.get()->val); 94 } 95 { 96 const absl::NoDestructor<Blob> b{ 97 {20, 28, 40}}; // init-list c-tor, deep const 98 // This only works in clang, not in gcc: 99 // const absl::NoDestructor<Blob> b({20, 28, 40}); 100 EXPECT_EQ(88, (*b).val); 101 EXPECT_EQ(88, b->val); 102 EXPECT_EQ(88, b.get()->val); 103 } 104 } 105 106 TEST(NoDestructorTest, SfinaeRegressionAbstractArg) { 107 struct Abstract { 108 virtual ~Abstract() = default; 109 virtual int foo() const = 0; 110 }; 111 112 struct Concrete : Abstract { 113 int foo() const override { return 17; } 114 }; 115 116 struct UsesAbstractInConstructor { 117 explicit UsesAbstractInConstructor(const Abstract& abstract) 118 : i(abstract.foo()) {} 119 int i; 120 }; 121 122 Concrete input; 123 absl::NoDestructor<UsesAbstractInConstructor> foo1(input); 124 EXPECT_EQ(foo1->i, 17); 125 absl::NoDestructor<UsesAbstractInConstructor> foo2( 126 static_cast<const Abstract&>(input)); 127 EXPECT_EQ(foo2->i, 17); 128 } 129 130 // ========================================================================= // 131 132 std::string* Str0() { 133 static absl::NoDestructor<std::string> x; 134 return x.get(); 135 } 136 137 extern const std::string& Str2(); 138 139 const char* Str1() { 140 static absl::NoDestructor<std::string> x(Str2() + "_Str1"); 141 return x->c_str(); 142 } 143 144 const std::string& Str2() { 145 static absl::NoDestructor<std::string> x("Str2"); 146 return *x; 147 } 148 149 const std::string& Str2Copy() { 150 // Exercise copy construction 151 static absl::NoDestructor<std::string> x(Str2()); 152 return *x; 153 } 154 155 typedef std::array<std::string, 3> MyArray; 156 const MyArray& Array() { 157 static absl::NoDestructor<MyArray> x{{{"foo", "bar", "baz"}}}; 158 // This only works in clang, not in gcc: 159 // static absl::NoDestructor<MyArray> x({{"foo", "bar", "baz"}}); 160 return *x; 161 } 162 163 typedef std::vector<int> MyVector; 164 const MyVector& Vector() { 165 static absl::NoDestructor<MyVector> x{{1, 2, 3}}; 166 return *x; 167 } 168 169 const int& Int() { 170 static absl::NoDestructor<int> x; 171 return *x; 172 } 173 174 TEST(NoDestructorTest, StaticPattern) { 175 EXPECT_TRUE( 176 std::is_trivially_destructible<absl::NoDestructor<std::string>>::value); 177 EXPECT_TRUE( 178 std::is_trivially_destructible<absl::NoDestructor<MyArray>>::value); 179 EXPECT_TRUE( 180 std::is_trivially_destructible<absl::NoDestructor<MyVector>>::value); 181 EXPECT_TRUE(std::is_trivially_destructible<absl::NoDestructor<int>>::value); 182 183 EXPECT_EQ(*Str0(), ""); 184 Str0()->append("foo"); 185 EXPECT_EQ(*Str0(), "foo"); 186 187 EXPECT_EQ(std::string(Str1()), "Str2_Str1"); 188 189 EXPECT_EQ(Str2(), "Str2"); 190 EXPECT_EQ(Str2Copy(), "Str2"); 191 192 EXPECT_THAT(Array(), testing::ElementsAre("foo", "bar", "baz")); 193 194 EXPECT_THAT(Vector(), testing::ElementsAre(1, 2, 3)); 195 196 EXPECT_EQ(0, Int()); // should get zero-initialized 197 } 198 199 TEST(NoDestructorTest, ClassTemplateArgumentDeduction) { 200 absl::NoDestructor i(1); 201 static_assert(std::is_same<decltype(i), absl::NoDestructor<int>>::value, 202 "Expected deduced type to be int."); 203 } 204 205 } // namespace