// 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. // // ----------------------------------------------------------------------------- // File: no_destructor.h // ----------------------------------------------------------------------------- // // This header file defines the absl::NoDestructor wrapper for defining a // static type that does not need to be destructed upon program exit. Instead, // such an object survives during program exit (and can be safely accessed at // any time). // // Objects of such type, if constructed safely and under the right conditions, // provide two main benefits over other alternatives: // // * Global objects not normally allowed due to concerns of destruction order // (i.e. no "complex globals") can be safely allowed, provided that such // objects can be constant initialized. // * Function scope static objects can be optimized to avoid heap allocation, // pointer chasing, and allow lazy construction. // // See below for complete details. #ifndef ABSL_BASE_NO_DESTRUCTOR_H_ #define ABSL_BASE_NO_DESTRUCTOR_H_ #include #include #include #include "absl/base/config.h" #include "absl/base/nullability.h" namespace absl { ABSL_NAMESPACE_BEGIN // absl::NoDestructor // // NoDestructor is a wrapper around an object of type T that behaves as an // object of type T but never calls T's destructor. NoDestructor makes it // safer and/or more efficient to use such objects in static storage contexts: // as global or function scope static variables. // // An instance of absl::NoDestructor has similar type semantics to an // instance of T: // // * Constructs in the same manner as an object of type T through perfect // forwarding. // * Provides pointer/reference semantic access to the object of type T via // `->`, `*`, and `get()`. // (Note that `const NoDestructor` works like a pointer to const `T`.) // // An object of type NoDestructor should be defined in static storage: // as either a global static object, or as a function scope static variable. // // Additionally, NoDestructor provides the following benefits: // // * Never calls T's destructor for the object // * If the object is a function-local static variable, the type can be // lazily constructed. // // An object of type NoDestructor is "trivially destructible" in the notion // that its destructor is never run. Provided that an object of this type can be // safely initialized and does not need to be cleaned up on program shutdown, // NoDestructor allows you to define global static variables, since Google's // C++ style guide ban on such objects doesn't apply to objects that are // trivially destructible. // // Usage as Global Static Variables // // NoDestructor allows declaration of a global object with a non-trivial // constructor in static storage without needing to add a destructor. // However, such objects still need to worry about initialization order, so // such objects should be const initialized: // // // Global or namespace scope. // constinit absl::NoDestructor reg{"foo", "bar", 8008}; // // Note that if your object already has a trivial destructor, you don't need to // use NoDestructor. // // Usage as Function Scope Static Variables // // Function static objects will be lazily initialized within static storage: // // // Function scope. // const std::string& MyString() { // static const absl::NoDestructor x("foo"); // return *x; // } // // For function static variables, NoDestructor avoids heap allocation and can be // inlined in static storage, resulting in exactly-once, thread-safe // construction of an object, and very fast access thereafter (the cost is a few // extra cycles). // // Using NoDestructor in this manner is generally better than other patterns // which require pointer chasing: // // // Prefer using absl::NoDestructor instead for the static variable. // const std::string& MyString() { // static const std::string* x = new std::string("foo"); // return *x; // } // template class NoDestructor { public: // Forwards arguments to the T's constructor: calls T(args...). template &...), void(NoDestructor&)>::value, int>::type = 0> explicit constexpr NoDestructor(Ts&&... args) : impl_(std::forward(args)...) {} // Forwards copy and move construction for T. Enables usage like this: // static NoDestructor> x{{{"1", "2", "3"}}}; // static NoDestructor> x{{1, 2, 3}}; explicit constexpr NoDestructor(const T& x) : impl_(x) {} explicit constexpr NoDestructor(T&& x) : impl_(std::move(x)) {} // No copying. NoDestructor(const NoDestructor&) = delete; NoDestructor& operator=(const NoDestructor&) = delete; // Pretend to be a smart pointer to T with deep constness. // Never returns a null pointer. T& operator*() { return *get(); } absl::Nonnull operator->() { return get(); } absl::Nonnull get() { return impl_.get(); } const T& operator*() const { return *get(); } absl::Nonnull operator->() const { return get(); } absl::Nonnull get() const { return impl_.get(); } private: class DirectImpl { public: template explicit constexpr DirectImpl(Args&&... args) : value_(std::forward(args)...) {} absl::Nonnull get() const { return &value_; } absl::Nonnull get() { return &value_; } private: T value_; }; class PlacementImpl { public: template explicit PlacementImpl(Args&&... args) { new (&space_) T(std::forward(args)...); } absl::Nonnull get() const { return Launder(reinterpret_cast(&space_)); } absl::Nonnull get() { return Launder(reinterpret_cast(&space_)); } private: template static absl::Nonnull Launder(absl::Nonnull p) { #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L return std::launder(p); #elif ABSL_HAVE_BUILTIN(__builtin_launder) return __builtin_launder(p); #else // When `std::launder` or equivalent are not available, we rely on // undefined behavior, which works as intended on Abseil's officially // supported platforms as of Q3 2023. #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif return p; #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif #endif } alignas(T) unsigned char space_[sizeof(T)]; }; // If the object is trivially destructible we use a member directly to avoid // potential once-init runtime initialization. It somewhat defeats the // purpose of NoDestructor in this case, but this makes the class more // friendly to generic code. std::conditional_t::value, DirectImpl, PlacementImpl> impl_; }; #ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION // Provide 'Class Template Argument Deduction': the type of NoDestructor's T // will be the same type as the argument passed to NoDestructor's constructor. template NoDestructor(T) -> NoDestructor; #endif // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_BASE_NO_DESTRUCTOR_H_