• 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 
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>
NoDestructor(Ts &&...args)126   explicit constexpr NoDestructor(Ts&&... args)
127       : impl_(std::forward<Ts>(args)...) {}
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}};
NoDestructor(const T & x)132   explicit constexpr NoDestructor(const T& x) : impl_(x) {}
NoDestructor(T && 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(); }
get()144   T* get() { return impl_.get(); }
145   const T& operator*() const { return *get(); }
146   const T* operator->() const { return get(); }
get()147   const T* get() const { return impl_.get(); }
148 
149  private:
150   class DirectImpl {
151    public:
152     template <typename... Args>
DirectImpl(Args &&...args)153     explicit constexpr DirectImpl(Args&&... args)
154         : value_(std::forward<Args>(args)...) {}
get()155     const T* get() const { return &value_; }
get()156     T* get() { return &value_; }
157 
158    private:
159     T value_;
160   };
161 
162   class PlacementImpl {
163    public:
164     template <typename... Args>
PlacementImpl(Args &&...args)165     explicit PlacementImpl(Args&&... args) {
166       new (&space_) T(std::forward<Args>(args)...);
167     }
get()168     const T* get() const {
169       return Launder(reinterpret_cast<const T*>(&space_));
170     }
get()171     T* get() { return Launder(reinterpret_cast<T*>(&space_)); }
172 
173    private:
174     template <typename P>
Launder(P * p)175     static P* Launder(P* p) {
176 #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
177       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     }
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_
218