xref: /aosp_15_r20/external/abseil-cpp/absl/base/no_destructor.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
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>
NoDestructor(Ts &&...args)121   explicit constexpr NoDestructor(Ts&&... args)
122       : impl_(std::forward<Ts>(args)...) {}
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}};
NoDestructor(const T & x)127   explicit constexpr NoDestructor(const T& x) : impl_(x) {}
NoDestructor(T && 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   T& operator*() { return *get(); }
138   absl::Nonnull<T*> operator->() { return get(); }
get()139   absl::Nonnull<T*> get() { return impl_.get(); }
140   const T& operator*() const { return *get(); }
141   absl::Nonnull<const T*> operator->() const { return get(); }
get()142   absl::Nonnull<const T*> get() const { return impl_.get(); }
143 
144  private:
145   class DirectImpl {
146    public:
147     template <typename... Args>
DirectImpl(Args &&...args)148     explicit constexpr DirectImpl(Args&&... args)
149         : value_(std::forward<Args>(args)...) {}
get()150     absl::Nonnull<const T*> get() const { return &value_; }
get()151     absl::Nonnull<T*> get() { return &value_; }
152 
153    private:
154     T value_;
155   };
156 
157   class PlacementImpl {
158    public:
159     template <typename... Args>
PlacementImpl(Args &&...args)160     explicit PlacementImpl(Args&&... args) {
161       new (&space_) T(std::forward<Args>(args)...);
162     }
get()163     absl::Nonnull<const T*> get() const {
164       return Launder(reinterpret_cast<const T*>(&space_));
165     }
get()166     absl::Nonnull<T*> get() { return Launder(reinterpret_cast<T*>(&space_)); }
167 
168    private:
169     template <typename P>
Launder(absl::Nonnull<P * > p)170     static absl::Nonnull<P*> Launder(absl::Nonnull<P*> p) {
171 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
172       return std::launder(p);
173 #elif ABSL_HAVE_BUILTIN(__builtin_launder)
174       return __builtin_launder(p);
175 #else
176       // When `std::launder` or equivalent are not available, we rely on
177       // undefined behavior, which works as intended on Abseil's officially
178       // supported platforms as of Q3 2023.
179 #if defined(__GNUC__) && !defined(__clang__)
180 #pragma GCC diagnostic push
181 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
182 #endif
183       return p;
184 #if defined(__GNUC__) && !defined(__clang__)
185 #pragma GCC diagnostic pop
186 #endif
187 #endif
188     }
189 
190     alignas(T) unsigned char space_[sizeof(T)];
191   };
192 
193   // If the object is trivially destructible we use a member directly to avoid
194   // potential once-init runtime initialization. It somewhat defeats the
195   // purpose of NoDestructor in this case, but this makes the class more
196   // friendly to generic code.
197   std::conditional_t<std::is_trivially_destructible<T>::value, DirectImpl,
198                      PlacementImpl>
199       impl_;
200 };
201 
202 #ifdef ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
203 // Provide 'Class Template Argument Deduction': the type of NoDestructor's T
204 // will be the same type as the argument passed to NoDestructor's constructor.
205 template <typename T>
206 NoDestructor(T) -> NoDestructor<T>;
207 #endif  // ABSL_HAVE_CLASS_TEMPLATE_ARGUMENT_DEDUCTION
208 
209 ABSL_NAMESPACE_END
210 }  // namespace absl
211 
212 #endif  // ABSL_BASE_NO_DESTRUCTOR_H_
213