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