1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef BASE_SCOPED_GENERIC_H_
6 #define BASE_SCOPED_GENERIC_H_
10 #include <type_traits>
12 #include "base/check.h"
13 #include "base/memory/raw_ptr.h"
17 // This class acts like unique_ptr with a custom deleter (although is slightly
18 // less fancy in some of the more escoteric respects) except that it keeps a
19 // copy of the object rather than a pointer, and we require that the contained
20 // object has some kind of "invalid" value.
22 // Defining a scoper based on this class allows you to get a scoper for
23 // non-pointer types without having to write custom code for set, reset, and
24 // move, etc. and get almost identical semantics that people are used to from
27 // It is intended that you will typedef this class with an appropriate deleter
28 // to implement clean up tasks for objects that act like pointers from a
29 // resource management standpoint but aren't, such as file descriptors and
30 // various types of operating system handles. Using unique_ptr for these
31 // things requires that you keep a pointer to the handle valid for the lifetime
32 // of the scoper (which is easy to mess up).
34 // For an object to be able to be put into a ScopedGeneric, it must support
35 // standard copyable semantics and have a specific "invalid" value. The traits
36 // must define a free function and also the invalid value to assign for
37 // default-constructed and released objects.
39 // struct FooScopedTraits {
40 // // It's assumed that this is a fast inline function with little-to-no
41 // // penalty for duplicate calls. This must be a static function even
42 // // for stateful traits.
43 // static int InvalidValue() {
47 // // This free function will not be called if f == InvalidValue()!
48 // static void Free(int f) {
53 // using ScopedFoo = ScopedGeneric<int, FooScopedTraits>;
55 // A Traits type may choose to track ownership of objects in parallel with
56 // ScopedGeneric. To do so, it must implement the Acquire and Release methods,
57 // which will be called by ScopedGeneric during ownership transfers and extend
58 // the ScopedGenericOwnershipTracking tag type.
60 // struct BarScopedTraits : public ScopedGenericOwnershipTracking {
61 // using ScopedGenericType = ScopedGeneric<int, BarScopedTraits>;
62 // static int InvalidValue() {
66 // static void Free(int b) {
70 // static void Acquire(const ScopedGenericType& owner, int b) {
71 // ::TrackAcquisition(b, owner);
74 // static void Release(const ScopedGenericType& owner, int b) {
75 // ::TrackRelease(b, owner);
79 // using ScopedBar = ScopedGeneric<int, BarScopedTraits>;
80 struct ScopedGenericOwnershipTracking {};
82 template<typename T, typename Traits>
85 // This must be first since it's used inline below.
87 // Use the empty base class optimization to allow us to have a D
88 // member, while avoiding any space overhead for it when D is an
89 // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good
90 // discussion of this technique.
91 struct Data : public Traits {
92 explicit Data(const T& in) : generic(in) {}
93 Data(const T& in, const Traits& other) : Traits(other), generic(in) {}
98 typedef T element_type;
99 typedef Traits traits_type;
101 ScopedGeneric() : data_(traits_type::InvalidValue()) {}
103 // Constructor. Takes responsibility for freeing the resource associated with
105 explicit ScopedGeneric(const element_type& value) : data_(value) {
106 TrackAcquire(data_.generic);
109 // Constructor. Allows initialization of a stateful traits object.
110 ScopedGeneric(const element_type& value, const traits_type& traits)
111 : data_(value, traits) {
112 TrackAcquire(data_.generic);
115 // Move constructor. Allows initialization from a ScopedGeneric rvalue.
116 ScopedGeneric(ScopedGeneric<T, Traits>&& rvalue)
117 : data_(rvalue.release(), rvalue.get_traits()) {
118 TrackAcquire(data_.generic);
120 ScopedGeneric(const ScopedGeneric&) = delete;
121 ScopedGeneric& operator=(const ScopedGeneric&) = delete;
123 virtual ~ScopedGeneric() {
124 CHECK(!receiving_); // ScopedGeneric destroyed with active receiver.
128 // operator=. Allows assignment from a ScopedGeneric rvalue.
129 ScopedGeneric& operator=(ScopedGeneric<T, Traits>&& rvalue) {
130 reset(rvalue.release());
134 // Frees the currently owned object, if any. Then takes ownership of a new
135 // object, if given. Self-resets are not allowd as on unique_ptr. See
136 // http://crbug.com/162971
137 void reset(const element_type& value = traits_type::InvalidValue()) {
138 if (data_.generic != traits_type::InvalidValue() && data_.generic == value)
141 data_.generic = value;
145 // Release the object. The return value is the current object held by this
146 // object. After this operation, this object will hold a null value, and
147 // will not own the object any more.
148 [[nodiscard]] element_type release() {
149 element_type old_generic = data_.generic;
150 data_.generic = traits_type::InvalidValue();
151 TrackRelease(old_generic);
155 // A helper class that provides a T* that can be used to take ownership of
156 // a value returned from a function via out-parameter. When the Receiver is
157 // destructed (which should usually be at the end of the statement in which
158 // receive is called), ScopedGeneric::reset() will be called with the
161 // In the simple case of a function that assigns the value before it returns,
162 // C++'s lifetime extension can be used as follows:
165 // bool result = GetFoo(ScopedFoo::Receiver(foo).get());
167 // Note that the lifetime of the Receiver is extended until the semicolon,
168 // and ScopedGeneric is assigned the value upon destruction of the Receiver,
169 // so the following code would not work:
173 // UseFoo(&foo, GetFoo(ScopedFoo::Receiver(foo).get()));
175 // In more complicated scenarios, you may need to provide an explicit scope
176 // for the Receiver, as in the following:
178 // std::vector<ScopedFoo> foos(64);
181 // std::vector<ScopedFoo::Receiver> foo_receivers;
182 // for (auto foo : foos) {
183 // foo_receivers_.emplace_back(foo);
185 // for (auto receiver : foo_receivers) {
186 // SubmitGetFooRequest(receiver.get());
188 // WaitForFooRequests();
193 explicit Receiver(ScopedGeneric& parent) : scoped_generic_(&parent) {
194 // Check if we attempted to construct a Receiver for ScopedGeneric with an
195 // existing Receiver.
196 CHECK(!scoped_generic_->receiving_);
197 scoped_generic_->receiving_ = true;
199 Receiver(const Receiver&) = delete;
200 Receiver& operator=(const Receiver&) = delete;
201 Receiver(Receiver&& move) {
202 CHECK(!used_); // Moving into already-used Receiver.
203 CHECK(!move.used_); // Moving from already-used Receiver.
204 scoped_generic_ = move.scoped_generic_;
205 move.scoped_generic_ = nullptr;
208 Receiver& operator=(Receiver&& move) {
209 CHECK(!used_); // Moving into already-used Receiver.
210 CHECK(!move.used_); // Moving from already-used Receiver.
211 scoped_generic_ = move.scoped_generic_;
212 move.scoped_generic_ = nullptr;
215 if (scoped_generic_) {
216 CHECK(scoped_generic_->receiving_);
217 scoped_generic_->reset(value_);
218 scoped_generic_->receiving_ = false;
221 // We hand out a pointer to a field in Receiver instead of directly to
222 // ScopedGeneric's internal storage in order to make it so that users can't
223 // accidentally silently break ScopedGeneric's invariants. This way, an
224 // incorrect use-after-scope-exit is more detectable by ASan or static
225 // analysis tools, as the pointer is only valid for the lifetime of the
226 // Receiver, not the ScopedGeneric.
233 T value_ = Traits::InvalidValue();
234 raw_ptr<ScopedGeneric<T, Traits>> scoped_generic_;
238 const element_type& get() const { return data_.generic; }
240 // Returns true if this object doesn't hold the special null value for the
241 // associated data type.
242 bool is_valid() const { return data_.generic != traits_type::InvalidValue(); }
244 bool operator==(const element_type& value) const {
245 return data_.generic == value;
247 bool operator!=(const element_type& value) const {
248 return data_.generic != value;
251 Traits& get_traits() { return data_; }
252 const Traits& get_traits() const { return data_; }
255 void FreeIfNecessary() {
256 if (data_.generic != traits_type::InvalidValue()) {
257 TrackRelease(data_.generic);
258 data_.Free(data_.generic);
259 data_.generic = traits_type::InvalidValue();
263 template <typename Void = void>
264 typename std::enable_if_t<
265 std::is_base_of_v<ScopedGenericOwnershipTracking, Traits>,
267 TrackAcquire(const T& value) {
268 if (value != traits_type::InvalidValue()) {
269 data_.Acquire(static_cast<const ScopedGeneric&>(*this), value);
273 template <typename Void = void>
274 typename std::enable_if_t<
275 !std::is_base_of_v<ScopedGenericOwnershipTracking, Traits>,
277 TrackAcquire(const T& value) {}
279 template <typename Void = void>
280 typename std::enable_if_t<
281 std::is_base_of_v<ScopedGenericOwnershipTracking, Traits>,
283 TrackRelease(const T& value) {
284 if (value != traits_type::InvalidValue()) {
285 data_.Release(static_cast<const ScopedGeneric&>(*this), value);
289 template <typename Void = void>
290 typename std::enable_if_t<
291 !std::is_base_of_v<ScopedGenericOwnershipTracking, Traits>,
293 TrackRelease(const T& value) {}
295 // Forbid comparison. If U != T, it totally doesn't make sense, and if U ==
296 // T, it still doesn't make sense because you should never have the same
297 // object owned by two different ScopedGenerics.
298 template <typename T2, typename Traits2> bool operator==(
299 const ScopedGeneric<T2, Traits2>& p2) const;
300 template <typename T2, typename Traits2> bool operator!=(
301 const ScopedGeneric<T2, Traits2>& p2) const;
304 bool receiving_ = false;
307 template<class T, class Traits>
308 void swap(const ScopedGeneric<T, Traits>& a,
309 const ScopedGeneric<T, Traits>& b) {
313 template<class T, class Traits>
314 bool operator==(const T& value, const ScopedGeneric<T, Traits>& scoped) {
315 return value == scoped.get();
318 template<class T, class Traits>
319 bool operator!=(const T& value, const ScopedGeneric<T, Traits>& scoped) {
320 return value != scoped.get();
325 #endif // BASE_SCOPED_GENERIC_H_