1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_APPLE_SCOPED_TYPEREF_H_
#define BASE_APPLE_SCOPED_TYPEREF_H_
#include "base/check_op.h"
#include "base/memory/scoped_policy.h"
namespace base::apple {
// ScopedTypeRef<> is patterned after std::shared_ptr<>, but maintains ownership
// of a reference to any type that is maintained by Retain and Release methods.
//
// The Traits structure must provide the Retain and Release methods for type T.
// A default ScopedTypeRefTraits is used but not defined, and should be defined
// for each type to use this interface. For example, an appropriate definition
// of ScopedTypeRefTraits for CGLContextObj would be:
//
// template<>
// struct ScopedTypeRefTraits<CGLContextObj> {
// static CGLContextObj InvalidValue() { return nullptr; }
// static CGLContextObj Retain(CGLContextObj object) {
// CGLContextRetain(object);
// return object;
// }
// static void Release(CGLContextObj object) { CGLContextRelease(object); }
// };
//
// For the many types that have pass-by-pointer create functions, the function
// InitializeInto() is provided to allow direct initialization and assumption
// of ownership of the object. For example, continuing to use the above
// CGLContextObj specialization:
//
// base::apple::ScopedTypeRef<CGLContextObj> context;
// CGLCreateContext(pixel_format, share_group, context.InitializeInto());
//
// For initialization with an existing object, the caller may specify whether
// the ScopedTypeRef<> being initialized is assuming the caller's existing
// ownership of the object (and should not call Retain in initialization) or if
// it should not assume this ownership and must create its own (by calling
// Retain in initialization). This behavior is based on the `policy` parameter,
// with `ASSUME` for the former and `RETAIN` for the latter. The default policy
// is to `ASSUME`.
template <typename T>
struct ScopedTypeRefTraits;
template <typename T, typename Traits = ScopedTypeRefTraits<T>>
class ScopedTypeRef {
public:
using element_type = T;
// Construction from underlying type
explicit constexpr ScopedTypeRef(
element_type object = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
: object_(object) {
if (object_ != Traits::InvalidValue() &&
policy == base::scoped_policy::RETAIN) {
object_ = Traits::Retain(object_);
}
}
// The pattern in the four [copy|move] [constructors|assignment operators]
// below is that for each of them there is the standard version for use by
// scopers wrapping objects of this type, and a templated version to handle
// scopers wrapping objects of subtypes. One might think that one could get
// away only the templated versions, as their templates should match the
// usage, but that doesn't work. Having a templated function that matches the
// types of, say, a copy constructor, doesn't count as a copy constructor, and
// the compiler's generated copy constructor is incorrect.
// Copy construction
ScopedTypeRef(const ScopedTypeRef<T, Traits>& that) : object_(that.get()) {
if (object_ != Traits::InvalidValue()) {
object_ = Traits::Retain(object_);
}
}
template <typename R, typename RTraits>
ScopedTypeRef(const ScopedTypeRef<R, RTraits>& that) : object_(that.get()) {
if (object_ != Traits::InvalidValue()) {
object_ = Traits::Retain(object_);
}
}
// Copy assignment
ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) {
reset(that.get(), base::scoped_policy::RETAIN);
return *this;
}
template <typename R, typename RTraits>
ScopedTypeRef& operator=(const ScopedTypeRef<R, RTraits>& that) {
reset(that.get(), base::scoped_policy::RETAIN);
return *this;
}
// Move construction
ScopedTypeRef(ScopedTypeRef<T, Traits>&& that) : object_(that.release()) {}
template <typename R, typename RTraits>
ScopedTypeRef(ScopedTypeRef<R, RTraits>&& that) : object_(that.release()) {}
// Move assignment
ScopedTypeRef& operator=(ScopedTypeRef<T, Traits>&& that) {
reset(that.release(), base::scoped_policy::ASSUME);
return *this;
}
template <typename R, typename RTraits>
ScopedTypeRef& operator=(ScopedTypeRef<R, RTraits>&& that) {
reset(that.release(), base::scoped_policy::ASSUME);
return *this;
}
// Resetting
template <typename R, typename RTraits>
void reset(const ScopedTypeRef<R, RTraits>& that) {
reset(that.get(), base::scoped_policy::RETAIN);
}
void reset(element_type object = Traits::InvalidValue(),
base::scoped_policy::OwnershipPolicy policy =
base::scoped_policy::ASSUME) {
if (object != Traits::InvalidValue() &&
policy == base::scoped_policy::RETAIN) {
object = Traits::Retain(object);
}
if (object_ != Traits::InvalidValue()) {
Traits::Release(object_);
}
object_ = object;
}
// Destruction
~ScopedTypeRef() {
if (object_ != Traits::InvalidValue()) {
Traits::Release(object_);
}
}
// This is to be used only to take ownership of objects that are created by
// pass-by-pointer create functions. To enforce this, require that this object
// be empty before use.
[[nodiscard]] element_type* InitializeInto() {
CHECK_EQ(object_, Traits::InvalidValue());
return &object_;
}
bool operator==(const ScopedTypeRef& that) const {
return object_ == that.object_;
}
explicit operator bool() const { return object_ != Traits::InvalidValue(); }
element_type get() const { return object_; }
void swap(ScopedTypeRef& that) {
element_type temp = that.object_;
that.object_ = object_;
object_ = temp;
}
// ScopedTypeRef<>::release() is like std::unique_ptr<>::release. It is NOT
// a wrapper for Release(). To force a ScopedTypeRef<> object to call
// Release(), use ScopedTypeRef<>::reset().
[[nodiscard]] element_type release() {
element_type temp = object_;
object_ = Traits::InvalidValue();
return temp;
}
private:
element_type object_;
};
} // namespace base::apple
#endif // BASE_APPLE_SCOPED_TYPEREF_H_
|