diff options
Diffstat (limited to 'contrib/restricted/abseil-cpp-tstring/y_absl/base/no_destructor.h')
-rw-r--r-- | contrib/restricted/abseil-cpp-tstring/y_absl/base/no_destructor.h | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/contrib/restricted/abseil-cpp-tstring/y_absl/base/no_destructor.h b/contrib/restricted/abseil-cpp-tstring/y_absl/base/no_destructor.h new file mode 100644 index 0000000000..03dff922c6 --- /dev/null +++ b/contrib/restricted/abseil-cpp-tstring/y_absl/base/no_destructor.h @@ -0,0 +1,217 @@ +// 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 y_absl::NoDestructor<T> 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 Y_ABSL_BASE_NO_DESTRUCTOR_H_ +#define Y_ABSL_BASE_NO_DESTRUCTOR_H_ + +#include <new> +#include <type_traits> +#include <utility> + +#include "y_absl/base/config.h" + +namespace y_absl { +Y_ABSL_NAMESPACE_BEGIN + +// y_absl::NoDestructor<T> +// +// NoDestructor<T> is a wrapper around an object of type T that behaves as an +// object of type T but never calls T's destructor. NoDestructor<T> 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 y_absl::NoDestructor<T> 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<T>` works like a pointer to const `T`.) +// +// An object of type NoDestructor<T> should be defined in static storage: +// as either a global static object, or as a function scope static variable. +// +// Additionally, NoDestructor<T> 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<T> 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<T> 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<T> 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. +// Y_ABSL_CONST_INIT y_absl::NoDestructor<MyRegistry> reg{"foo", "bar", 8008}; +// +// Note that if your object already has a trivial destructor, you don't need to +// use NoDestructor<T>. +// +// Usage as Function Scope Static Variables +// +// Function static objects will be lazily initialized within static storage: +// +// // Function scope. +// const TString& MyString() { +// static const y_absl::NoDestructor<TString> 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<T> in this manner is generally better than other patterns +// which require pointer chasing: +// +// // Prefer using y_absl::NoDestructor<T> instead for the static variable. +// const TString& MyString() { +// static const TString* x = new TString("foo"); +// return *x; +// } +// +template <typename T> +class NoDestructor { + public: + // Forwards arguments to the T's constructor: calls T(args...). + template <typename... Ts, + // Disable this overload when it might collide with copy/move. + typename std::enable_if<!std::is_same<void(std::decay_t<Ts>&...), + void(NoDestructor&)>::value, + int>::type = 0> + explicit constexpr NoDestructor(Ts&&... args) + : impl_(std::forward<Ts>(args)...) {} + + // Forwards copy and move construction for T. Enables usage like this: + // static NoDestructor<std::array<string, 3>> x{{{"1", "2", "3"}}}; + // static NoDestructor<std::vector<int>> 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(); } + T* operator->() { return get(); } + T* get() { return impl_.get(); } + const T& operator*() const { return *get(); } + const T* operator->() const { return get(); } + const T* get() const { return impl_.get(); } + + private: + class DirectImpl { + public: + template <typename... Args> + explicit constexpr DirectImpl(Args&&... args) + : value_(std::forward<Args>(args)...) {} + const T* get() const { return &value_; } + T* get() { return &value_; } + + private: + T value_; + }; + + class PlacementImpl { + public: + template <typename... Args> + explicit PlacementImpl(Args&&... args) { + new (&space_) T(std::forward<Args>(args)...); + } + const T* get() const { + return Launder(reinterpret_cast<const T*>(&space_)); + } + T* get() { return Launder(reinterpret_cast<T*>(&space_)); } + + private: + template <typename P> + static P* Launder(P* p) { +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L + return std::launder(p); +#elif Y_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<std::is_trivially_destructible<T>::value, DirectImpl, + PlacementImpl> + impl_; +}; + +#ifdef Y_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 <typename T> +NoDestructor(T) -> NoDestructor<T>; +#endif // Y_ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION + +Y_ABSL_NAMESPACE_END +} // namespace y_absl + +#endif // Y_ABSL_BASE_NO_DESTRUCTOR_H_ |