UniquePtr.h (6654B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* Smart pointer managing sole ownership of a resource. */ 8 9 #ifndef mozilla_UniquePtr_h 10 #define mozilla_UniquePtr_h 11 12 #include <memory> 13 #include <utility> 14 15 #include "mozilla/Attributes.h" 16 17 namespace mozilla { 18 19 template <typename T> 20 using DefaultDelete = std::default_delete<T>; 21 22 template <typename T, class D = DefaultDelete<T>> 23 using UniquePtr = std::unique_ptr<T, D>; 24 25 } // namespace mozilla 26 27 namespace mozilla { 28 29 namespace detail { 30 31 template <typename T> 32 struct UniqueSelector { 33 typedef UniquePtr<T> SingleObject; 34 }; 35 36 template <typename T> 37 struct UniqueSelector<T[]> { 38 typedef UniquePtr<T[]> UnknownBound; 39 }; 40 41 template <typename T, decltype(sizeof(int)) N> 42 struct UniqueSelector<T[N]> { 43 typedef UniquePtr<T[N]> KnownBound; 44 }; 45 46 } // namespace detail 47 48 /** 49 * MakeUnique is a helper function for allocating new'd objects and arrays, 50 * returning a UniquePtr containing the resulting pointer. The semantics of 51 * MakeUnique<Type>(...) are as follows. 52 * 53 * If Type is an array T[n]: 54 * Disallowed, deleted, no overload for you! 55 * If Type is an array T[]: 56 * MakeUnique<T[]>(size_t) is the only valid overload. The pointer returned 57 * is as if by |new T[n]()|, which value-initializes each element. (If T 58 * isn't a class type, this will zero each element. If T is a class type, 59 * then roughly speaking, each element will be constructed using its default 60 * constructor. See C++11 [dcl.init]p7 for the full gory details.) 61 * If Type is non-array T: 62 * The arguments passed to MakeUnique<T>(...) are forwarded into a 63 * |new T(...)| call, initializing the T as would happen if executing 64 * |T(...)|. 65 * 66 * There are various benefits to using MakeUnique instead of |new| expressions. 67 * 68 * First, MakeUnique eliminates use of |new| from code entirely. If objects are 69 * only created through UniquePtr, then (assuming all explicit release() calls 70 * are safe, including transitively, and no type-safety casting funniness) 71 * correctly maintained ownership of the UniquePtr guarantees no leaks are 72 * possible. (This pays off best if a class is only ever created through a 73 * factory method on the class, using a private constructor.) 74 * 75 * Second, initializing a UniquePtr using a |new| expression requires repeating 76 * the name of the new'd type, whereas MakeUnique in concert with the |auto| 77 * keyword names it only once: 78 * 79 * UniquePtr<char> ptr1(new char()); // repetitive 80 * auto ptr2 = MakeUnique<char>(); // shorter 81 * 82 * Of course this assumes the reader understands the operation MakeUnique 83 * performs. In the long run this is probably a reasonable assumption. In the 84 * short run you'll have to use your judgment about what readers can be expected 85 * to know, or to quickly look up. 86 * 87 * Third, a call to MakeUnique can be assigned directly to a UniquePtr. In 88 * contrast you can't assign a pointer into a UniquePtr without using the 89 * cumbersome reset(). 90 * 91 * UniquePtr<char> p; 92 * p = new char; // ERROR 93 * p.reset(new char); // works, but fugly 94 * p = MakeUnique<char>(); // preferred 95 * 96 * (And third, although not relevant to Mozilla: MakeUnique is exception-safe. 97 * An exception thrown after |new T| succeeds will leak that memory, unless the 98 * pointer is assigned to an object that will manage its ownership. UniquePtr 99 * ably serves this function.) 100 */ 101 102 template <typename T, typename... Args> 103 auto MakeUnique(Args&&... aArgs) { 104 return std::make_unique<T>(std::forward<Args>(aArgs)...); 105 } 106 107 /** 108 * WrapUnique is a helper function to transfer ownership from a raw pointer 109 * into a UniquePtr<T>. It can only be used with a single non-array type. 110 * 111 * It is generally used this way: 112 * 113 * auto p = WrapUnique(new char); 114 * 115 * It can be used when MakeUnique is not usable, for example, when the 116 * constructor you are using is private, or you want to use aggregate 117 * initialization. 118 */ 119 120 template <typename T> 121 typename detail::UniqueSelector<T>::SingleObject WrapUnique(T* aPtr) { 122 return UniquePtr<T>(aPtr); 123 } 124 125 } // namespace mozilla 126 127 /** 128 TempPtrToSetter(UniquePtr<T>*) -> T**-ish 129 TempPtrToSetter(std::unique_ptr<T>*) -> T**-ish 130 131 Make a temporary class to support assigning to UniquePtr/unique_ptr via passing 132 a pointer to the callee. 133 134 Often, APIs will be shaped like this trivial example: 135 ``` 136 nsresult Foo::NewChildBar(Bar** out) { 137 if (!IsOk()) return NS_ERROR_FAILURE; 138 *out = new Bar(this); 139 return NS_OK; 140 } 141 ``` 142 143 In order to make this work with unique ptrs, it's often either risky or 144 overwrought: 145 ``` 146 Bar* bar = nullptr; 147 const auto cleanup = MakeScopeExit([&]() { 148 if (bar) { 149 delete bar; 150 } 151 }); 152 if (FAILED(foo->NewChildBar(&bar)) { 153 // handle it 154 } 155 ``` 156 157 ``` 158 UniquePtr<Bar> bar; 159 { 160 Bar* raw = nullptr; 161 const auto res = foo->NewChildBar(&bar); 162 bar.reset(raw); 163 if (FAILED(res) { 164 // handle it 165 } 166 } 167 ``` 168 TempPtrToSettable is a shorthand for the latter approach, allowing something 169 cleaner but also safe: 170 171 ``` 172 UniquePtr<Bar> bar; 173 if (FAILED(foo->NewChildBar(TempPtrToSetter(&bar))) { 174 // handle it 175 } 176 ``` 177 */ 178 179 namespace mozilla { 180 namespace detail { 181 182 template <class T, class UniquePtrT> 183 class MOZ_TEMPORARY_CLASS TempPtrToSetterT final { 184 private: 185 UniquePtrT* const mDest; 186 T* mNewVal; 187 188 public: 189 explicit TempPtrToSetterT(UniquePtrT* dest) 190 : mDest(dest), mNewVal(mDest->get()) {} 191 192 operator T**() { return &mNewVal; } 193 194 ~TempPtrToSetterT() { 195 if (mDest->get() != mNewVal) { 196 mDest->reset(mNewVal); 197 } 198 } 199 }; 200 201 } // namespace detail 202 203 template <class T, class Deleter> 204 auto TempPtrToSetter(UniquePtr<T, Deleter>* const p) { 205 return detail::TempPtrToSetterT<T, UniquePtr<T, Deleter>>{p}; 206 } 207 208 } // namespace mozilla 209 210 namespace std { 211 212 // No operator<, operator>, operator<=, operator>= for now because simplicity. 213 214 template <typename T, class D> 215 bool operator==(const mozilla::UniquePtr<T, D>& aX, const T* aY) { 216 return aX.get() == aY; 217 } 218 219 template <typename T, class D> 220 bool operator==(const T* aY, const mozilla::UniquePtr<T, D>& aX) { 221 return aY == aX.get(); 222 } 223 224 template <typename T, class D> 225 bool operator!=(const mozilla::UniquePtr<T, D>& aX, const T* aY) { 226 return aX.get() != aY; 227 } 228 229 template <typename T, class D> 230 bool operator!=(const T* aY, const mozilla::UniquePtr<T, D>& aX) { 231 return aY != aX.get(); 232 } 233 } // namespace std 234 235 #endif /* mozilla_UniquePtr_h */