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 // ----------------------------------------------------------------------------- 16 // File: no_destructor.h 17 // ----------------------------------------------------------------------------- 18 // 19 // This header file defines the absl::NoDestructor<T> wrapper for defining a 20 // static type that does not need to be destructed upon program exit. Instead, 21 // such an object survives during program exit (and can be safely accessed at 22 // any time). 23 // 24 // absl::NoDestructor<T> is useful when when a variable has static storage 25 // duration but its type has a non-trivial destructor. Global constructors are 26 // not recommended because of the C++'s static initialization order fiasco (See 27 // https://en.cppreference.com/w/cpp/language/siof). Global destructors are not 28 // allowed due to similar concerns about destruction ordering. Using 29 // absl::NoDestructor<T> as a function-local static prevents both of these 30 // issues. 31 // 32 // See below for complete details. 33 34 35 #ifndef ABSL_BASE_NO_DESTRUCTOR_H_ 36 #define ABSL_BASE_NO_DESTRUCTOR_H_ 37 38 #include <new> 39 #include <type_traits> 40 #include <utility> 41 42 #include "absl/base/config.h" 43 #include "absl/base/nullability.h" 44 45 namespace absl { 46 ABSL_NAMESPACE_BEGIN 47 48 // absl::NoDestructor<T> 49 // 50 // NoDestructor<T> is a wrapper around an object of type T that behaves as an 51 // object of type T but never calls T's destructor. NoDestructor<T> makes it 52 // safer and/or more efficient to use such objects in static storage contexts, 53 // ideally as function scope static variables. 54 // 55 // An instance of absl::NoDestructor<T> has similar type semantics to an 56 // instance of T: 57 // 58 // * Constructs in the same manner as an object of type T through perfect 59 // forwarding. 60 // * Provides pointer/reference semantic access to the object of type T via 61 // `->`, `*`, and `get()`. 62 // (Note that `const NoDestructor<T>` works like a pointer to const `T`.) 63 // 64 // Additionally, NoDestructor<T> provides the following benefits: 65 // 66 // * Never calls T's destructor for the object 67 // * If the object is a function-local static variable, the type can be 68 // lazily constructed. 69 // 70 // An object of type NoDestructor<T> is "trivially destructible" in the notion 71 // that its destructor is never run. 72 // 73 // Usage as Function Scope Static Variables 74 // 75 // Function static objects will be lazily initialized within static storage: 76 // 77 // // Function scope. 78 // const std::string& MyString() { 79 // static const absl::NoDestructor<std::string> x("foo"); 80 // return *x; 81 // } 82 // 83 // For function static variables, NoDestructor avoids heap allocation and can be 84 // inlined in static storage, resulting in exactly-once, thread-safe 85 // construction of an object, and very fast access thereafter (the cost is a few 86 // extra cycles). 87 // 88 // Using NoDestructor<T> in this manner is generally better than other patterns 89 // which require pointer chasing: 90 // 91 // // Prefer using absl::NoDestructor<T> instead for the static variable. 92 // const std::string& MyString() { 93 // static const std::string* x = new std::string("foo"); 94 // return *x; 95 // } 96 // 97 // Usage as Global Static Variables 98 // 99 // NoDestructor<T> allows declaration of a global object of type T that has a 100 // non-trivial destructor since its destructor is never run. However, such 101 // objects still need to worry about initialization order, so such use is not 102 // recommended, strongly discouraged by the Google C++ Style Guide, and outright 103 // banned in Chromium. 104 // See https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables 105 // 106 // // Global or namespace scope. 107 // absl::NoDestructor<MyRegistry> reg{"foo", "bar", 8008}; 108 // 109 // Note that if your object already has a trivial destructor, you don't need to 110 // use NoDestructor<T>. 111 // 112 template <typename T> 113 class NoDestructor { 114 public: 115 // Forwards arguments to the T's constructor: calls T(args...). 116 template <typename... Ts, 117 // Disable this overload when it might collide with copy/move. 118 typename std::enable_if<!std::is_same<void(std::decay_t<Ts>&...), 119 void(NoDestructor&)>::value, 120 int>::type = 0> NoDestructor(Ts &&...args)121 explicit constexpr NoDestructor(Ts&&... args) 122 : impl_(std::forward<Ts>(args)...) {} 123 124 // Forwards copy and move construction for T. Enables usage like this: 125 // static NoDestructor<std::array<string, 3>> x{{{"1", "2", "3"}}}; 126 // static NoDestructor<std::vector<int>> x{{1, 2, 3}}; NoDestructor(const T & x)127 explicit constexpr NoDestructor(const T& x) : impl_(x) {} NoDestructor(T && x)128 explicit constexpr NoDestructor(T&& x) 129 : impl_(std::move(x)) {} 130 131 // No copying. 132 NoDestructor(const NoDestructor&) = delete; 133 NoDestructor& operator=(const NoDestructor&) = delete; 134 135 // Pretend to be a smart pointer to T with deep constness. 136 // Never returns a null pointer. 137 T& operator*() { return *get(); } 138 absl::Nonnull<T*> operator->() { return get(); } get()139 absl::Nonnull<T*> get() { return impl_.get(); } 140 const T& operator*() const { return *get(); } 141 absl::Nonnull<const T*> operator->() const { return get(); } get()142 absl::Nonnull<const T*> get() const { return impl_.get(); } 143 144 private: 145 class DirectImpl { 146 public: 147 template <typename... Args> DirectImpl(Args &&...args)148 explicit constexpr DirectImpl(Args&&... args) 149 : value_(std::forward<Args>(args)...) {} get()150 absl::Nonnull<const T*> get() const { return &value_; } get()151 absl::Nonnull<T*> get() { return &value_; } 152 153 private: 154 T value_; 155 }; 156 157 class PlacementImpl { 158 public: 159 template <typename... Args> PlacementImpl(Args &&...args)160 explicit PlacementImpl(Args&&... args) { 161 new (&space_) T(std::forward<Args>(args)...); 162 } get()163 absl::Nonnull<const T*> get() const { 164 return Launder(reinterpret_cast<const T*>(&space_)); 165 } get()166 absl::Nonnull<T*> get() { return Launder(reinterpret_cast<T*>(&space_)); } 167 168 private: 169 template <typename P> Launder(absl::Nonnull<P * > p)170 static absl::Nonnull<P*> Launder(absl::Nonnull<P*> p) { 171 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L 172 return std::launder(p); 173 #elif ABSL_HAVE_BUILTIN(__builtin_launder) 174 return __builtin_launder(p); 175 #else 176 // When `std::launder` or equivalent are not available, we rely on 177 // undefined behavior, which works as intended on Abseil's officially 178 // supported platforms as of Q3 2023. 179 #if defined(__GNUC__) && !defined(__clang__) 180 #pragma GCC diagnostic push 181 #pragma GCC diagnostic ignored "-Wstrict-aliasing" 182 #endif 183 return p; 184 #if defined(__GNUC__) && !defined(__clang__) 185 #pragma GCC diagnostic pop 186 #endif 187 #endif 188 } 189 190 alignas(T) unsigned char space_[sizeof(T)]; 191 }; 192 193 // If the object is trivially destructible we use a member directly to avoid 194 // potential once-init runtime initialization. It somewhat defeats the 195 // purpose of NoDestructor in this case, but this makes the class more 196 // friendly to generic code. 197 std::conditional_t<std::is_trivially_destructible<T>::value, DirectImpl, 198 PlacementImpl> 199 impl_; 200 }; 201 202 #ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 203 // Provide 'Class Template Argument Deduction': the type of NoDestructor's T 204 // will be the same type as the argument passed to NoDestructor's constructor. 205 template <typename T> 206 NoDestructor(T) -> NoDestructor<T>; 207 #endif // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION 208 209 ABSL_NAMESPACE_END 210 } // namespace absl 211 212 #endif // ABSL_BASE_NO_DESTRUCTOR_H_ 213