1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
// 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<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).
//
// absl::NoDestructor<T> is useful when when a variable has static storage
// duration but its type has a non-trivial destructor. Global constructors are
// not recommended because of the C++'s static initialization order fiasco (See
// https://en.cppreference.com/w/cpp/language/siof). Global destructors are not
// allowed due to similar concerns about destruction ordering. Using
// absl::NoDestructor<T> as a function-local static prevents both of these
// issues.
//
// See below for complete details.
#ifndef ABSL_BASE_NO_DESTRUCTOR_H_
#define ABSL_BASE_NO_DESTRUCTOR_H_
#include <new>
#include <type_traits>
#include <utility>
#include "absl/base/config.h"
#include "absl/base/nullability.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
// 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,
// ideally as function scope static variables.
//
// An instance of 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`.)
//
// 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.
//
// 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<std::string> 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 absl::NoDestructor<T> instead for the static variable.
// const std::string& MyString() {
// static const std::string* x = new std::string("foo");
// return *x;
// }
//
// Usage as Global Static Variables
//
// NoDestructor<T> allows declaration of a global object of type T that has a
// non-trivial destructor since its destructor is never run. However, such
// objects still need to worry about initialization order, so such use is not
// recommended, strongly discouraged by the Google C++ Style Guide, and outright
// banned in Chromium.
// See https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables
//
// // Global or namespace scope.
// 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>.
//
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* absl_nonnull operator->() { return get(); }
T* absl_nonnull get() { return impl_.get(); }
const T& operator*() const { return *get(); }
const T* absl_nonnull operator->() const { return get(); }
const T* absl_nonnull get() const { return impl_.get(); }
private:
class DirectImpl {
public:
template <typename... Args>
explicit constexpr DirectImpl(Args&&... args)
: value_(std::forward<Args>(args)...) {}
const T* absl_nonnull get() const { return &value_; }
T* absl_nonnull 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* absl_nonnull get() const {
return std::launder(reinterpret_cast<const T*>(&space_));
}
T* absl_nonnull get() {
return std::launder(reinterpret_cast<T*>(&space_));
}
private:
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_;
};
// 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>;
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_BASE_NO_DESTRUCTOR_H_
|