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