/src/abseil-cpp/absl/base/no_destructor.h
Line | Count | Source (jump to first uncovered line) |
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 | | // absl::NoDestructor<T> is useful when when a variable has static storage |
25 | | // duration but its type has a non-trivial destructor. Global constructors are |
26 | | // not recommended because of the C++'s static initialization order fiasco (See |
27 | | // https://en.cppreference.com/w/cpp/language/siof). Global destructors are not |
28 | | // allowed due to similar concerns about destruction ordering. Using |
29 | | // absl::NoDestructor<T> as a function-local static prevents both of these |
30 | | // issues. |
31 | | // |
32 | | // See below for complete details. |
33 | | |
34 | | |
35 | | #ifndef ABSL_BASE_NO_DESTRUCTOR_H_ |
36 | | #define ABSL_BASE_NO_DESTRUCTOR_H_ |
37 | | |
38 | | #include <new> |
39 | | #include <type_traits> |
40 | | #include <utility> |
41 | | |
42 | | #include "absl/base/config.h" |
43 | | #include "absl/base/nullability.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 | | // ideally as 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 | | // Additionally, NoDestructor<T> provides the following benefits: |
65 | | // |
66 | | // * Never calls T's destructor for the object |
67 | | // * If the object is a function-local static variable, the type can be |
68 | | // lazily constructed. |
69 | | // |
70 | | // An object of type NoDestructor<T> is "trivially destructible" in the notion |
71 | | // that its destructor is never run. |
72 | | // |
73 | | // Usage as Function Scope Static Variables |
74 | | // |
75 | | // Function static objects will be lazily initialized within static storage: |
76 | | // |
77 | | // // Function scope. |
78 | | // const std::string& MyString() { |
79 | | // static const absl::NoDestructor<std::string> x("foo"); |
80 | | // return *x; |
81 | | // } |
82 | | // |
83 | | // For function static variables, NoDestructor avoids heap allocation and can be |
84 | | // inlined in static storage, resulting in exactly-once, thread-safe |
85 | | // construction of an object, and very fast access thereafter (the cost is a few |
86 | | // extra cycles). |
87 | | // |
88 | | // Using NoDestructor<T> in this manner is generally better than other patterns |
89 | | // which require pointer chasing: |
90 | | // |
91 | | // // Prefer using absl::NoDestructor<T> instead for the static variable. |
92 | | // const std::string& MyString() { |
93 | | // static const std::string* x = new std::string("foo"); |
94 | | // return *x; |
95 | | // } |
96 | | // |
97 | | // Usage as Global Static Variables |
98 | | // |
99 | | // NoDestructor<T> allows declaration of a global object of type T that has a |
100 | | // non-trivial destructor since its destructor is never run. However, such |
101 | | // objects still need to worry about initialization order, so such use is not |
102 | | // recommended, strongly discouraged by the Google C++ Style Guide, and outright |
103 | | // banned in Chromium. |
104 | | // See https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables |
105 | | // |
106 | | // // Global or namespace scope. |
107 | | // absl::NoDestructor<MyRegistry> reg{"foo", "bar", 8008}; |
108 | | // |
109 | | // Note that if your object already has a trivial destructor, you don't need to |
110 | | // use NoDestructor<T>. |
111 | | // |
112 | | template <typename T> |
113 | | class NoDestructor { |
114 | | public: |
115 | | // Forwards arguments to the T's constructor: calls T(args...). |
116 | | template <typename... Ts, |
117 | | // Disable this overload when it might collide with copy/move. |
118 | | typename std::enable_if<!std::is_same<void(std::decay_t<Ts>&...), |
119 | | void(NoDestructor&)>::value, |
120 | | int>::type = 0> |
121 | | explicit constexpr NoDestructor(Ts&&... args) |
122 | 0 | : impl_(std::forward<Ts>(args)...) {} Unexecuted instantiation: log_sink_set.cc:_ZN4absl12NoDestructorINS_12log_internal12_GLOBAL__N_116GlobalLogSinkSetEEC2IJETnNSt3__19enable_ifIXntsr3std7is_sameIFvDpRu7__decayIT_EEFvRS4_EEE5valueEiE4typeELi0EEEDpOS8_ Unexecuted instantiation: log_sink_set.cc:_ZN4absl12NoDestructorINS_12log_internal12_GLOBAL__N_113StderrLogSinkEEC2IJETnNSt3__19enable_ifIXntsr3std7is_sameIFvDpRu7__decayIT_EEFvRS4_EEE5valueEiE4typeELi0EEEDpOS8_ Unexecuted instantiation: _ZN4absl12NoDestructorINS_18profiling_internal14SampleRecorderINS_18container_internal14HashtablezInfoEEEEC2IJETnNSt3__19enable_ifIXntsr3std7is_sameIFvDpRu7__decayIT_EEFvRS6_EEE5valueEiE4typeELi0EEEDpOSA_ |
123 | | |
124 | | // Forwards copy and move construction for T. Enables usage like this: |
125 | | // static NoDestructor<std::array<string, 3>> x{{{"1", "2", "3"}}}; |
126 | | // static NoDestructor<std::vector<int>> x{{1, 2, 3}}; |
127 | | explicit constexpr NoDestructor(const T& x) : impl_(x) {} |
128 | | explicit constexpr NoDestructor(T&& x) |
129 | | : impl_(std::move(x)) {} |
130 | | |
131 | | // No copying. |
132 | | NoDestructor(const NoDestructor&) = delete; |
133 | | NoDestructor& operator=(const NoDestructor&) = delete; |
134 | | |
135 | | // Pretend to be a smart pointer to T with deep constness. |
136 | | // Never returns a null pointer. |
137 | 0 | T& operator*() { return *get(); } Unexecuted instantiation: log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::operator*() Unexecuted instantiation: absl::NoDestructor<absl::profiling_internal::SampleRecorder<absl::container_internal::HashtablezInfo> >::operator*() |
138 | | T* absl_nonnull operator->() { return get(); } |
139 | 0 | T* absl_nonnull get() { return impl_.get(); } Unexecuted instantiation: log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::get() Unexecuted instantiation: log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::get() Unexecuted instantiation: absl::NoDestructor<absl::profiling_internal::SampleRecorder<absl::container_internal::HashtablezInfo> >::get() |
140 | | const T& operator*() const { return *get(); } |
141 | | const T* absl_nonnull operator->() const { return get(); } |
142 | | const T* absl_nonnull get() const { return impl_.get(); } |
143 | | |
144 | | private: |
145 | | class DirectImpl { |
146 | | public: |
147 | | template <typename... Args> |
148 | | explicit constexpr DirectImpl(Args&&... args) |
149 | | : value_(std::forward<Args>(args)...) {} |
150 | | const T* absl_nonnull get() const { return &value_; } |
151 | | T* absl_nonnull get() { return &value_; } |
152 | | |
153 | | private: |
154 | | T value_; |
155 | | }; |
156 | | |
157 | | class PlacementImpl { |
158 | | public: |
159 | | template <typename... Args> |
160 | 0 | explicit PlacementImpl(Args&&... args) { |
161 | 0 | new (&space_) T(std::forward<Args>(args)...); |
162 | 0 | } Unexecuted instantiation: log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::PlacementImpl::PlacementImpl<>() Unexecuted instantiation: log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::PlacementImpl::PlacementImpl<>() Unexecuted instantiation: absl::NoDestructor<absl::profiling_internal::SampleRecorder<absl::container_internal::HashtablezInfo> >::PlacementImpl::PlacementImpl<>() |
163 | | const T* absl_nonnull get() const { |
164 | | return std::launder(reinterpret_cast<const T*>(&space_)); |
165 | | } |
166 | 0 | T* absl_nonnull get() { |
167 | 0 | return std::launder(reinterpret_cast<T*>(&space_)); |
168 | 0 | } Unexecuted instantiation: log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::StderrLogSink>::PlacementImpl::get() Unexecuted instantiation: log_sink_set.cc:absl::NoDestructor<absl::log_internal::(anonymous namespace)::GlobalLogSinkSet>::PlacementImpl::get() Unexecuted instantiation: absl::NoDestructor<absl::profiling_internal::SampleRecorder<absl::container_internal::HashtablezInfo> >::PlacementImpl::get() |
169 | | |
170 | | private: |
171 | | alignas(T) unsigned char space_[sizeof(T)]; |
172 | | }; |
173 | | |
174 | | // If the object is trivially destructible we use a member directly to avoid |
175 | | // potential once-init runtime initialization. It somewhat defeats the |
176 | | // purpose of NoDestructor in this case, but this makes the class more |
177 | | // friendly to generic code. |
178 | | std::conditional_t<std::is_trivially_destructible<T>::value, DirectImpl, |
179 | | PlacementImpl> |
180 | | impl_; |
181 | | }; |
182 | | |
183 | | // Provide 'Class Template Argument Deduction': the type of NoDestructor's T |
184 | | // will be the same type as the argument passed to NoDestructor's constructor. |
185 | | template <typename T> |
186 | | NoDestructor(T) -> NoDestructor<T>; |
187 | | |
188 | | ABSL_NAMESPACE_END |
189 | | } // namespace absl |
190 | | |
191 | | #endif // ABSL_BASE_NO_DESTRUCTOR_H_ |