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