UniqueOrNonOwningPtr.h (4782B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 #ifndef mozilla_UniqueOrNonOwningPtr_h 6 #define mozilla_UniqueOrNonOwningPtr_h 7 8 #include <cstdint> 9 #include <utility> 10 11 #include "mozilla/Assertions.h" 12 13 namespace mozilla { 14 15 template <typename T> 16 class UniqueOrNonOwningPtr; 17 18 namespace detail { 19 20 template <typename T> 21 struct UniqueOfUniqueOrNonOwningSelector { 22 using SingleObject = UniqueOrNonOwningPtr<T>; 23 }; 24 25 template <typename T> 26 struct UniqueOfUniqueOrNonOwningSelector<T[]>; 27 28 template <typename T, decltype(sizeof(int)) N> 29 struct UniqueOfUniqueOrNonOwningSelector<T[N]>; 30 31 } // namespace detail 32 33 // `mozilla::MakeUnique` equivalent, with the same set of advantages. 34 // Non-owning case doesn't need this since there's no allocation. 35 // See below as to why only SingleObject case is supported. 36 template <typename T, typename... Args> 37 typename detail::UniqueOfUniqueOrNonOwningSelector<T>::SingleObject 38 MakeUniqueOfUniqueOrNonOwning(Args&&... aArgs) { 39 return UniqueOrNonOwningPtr<T>::UniquelyOwning( 40 new T(std::forward<Args>(aArgs)...)); 41 } 42 43 /** 44 * A pointer that is either: 45 * * Uniquely-owning, as if `std::unique_ptr`/`mozilla::UniquePtr`, or 46 * * Non-owning, as if raw pointer. 47 * 48 * Overall, it behaves like `mozilla::Variant<T*, UniquePtr<T>>`, but more 49 * compact. It may be helpful if you are mostly referencing existing data type 50 * of significant size, but sometimes generate a modified copy and refer to 51 * that. 52 * 53 * Usage notes: 54 * * Ownership: This structure makes ownership tracking harder. It is the 55 * caller's responsibility to ensure that, in the non-owning case, the data 56 * outlives this pointer. 57 * * (Ab)using the lowest bit: Owning state is tagged inline in the lowest 58 * bit, which is set for uniquely-owning data. It does not work with a 59 * byte-aligned data types, or members of a packed struct. There are asserts to 60 * try and catch this as early as possible. 61 * 62 * TODO(dshin): This lacks support for things that `mozilla::UniquePtr` supports 63 * - however, these cases will fail to compile. 64 * * Deleter support (Even stateless ones) 65 * * Interconversion (Pointing to derived from base pointer) 66 * * T[] 67 */ 68 template <typename T> 69 class UniqueOrNonOwningPtr { 70 public: 71 // Check to make sure we can take on non-owning pointer to stack. 72 static_assert(alignof(T) != 1, 73 "Can't support data aligned to byte boundaries."); 74 // Standard guarantees the null pointer value to be integer 0. 75 UniqueOrNonOwningPtr() : mBits{0} {} 76 UniqueOrNonOwningPtr(const UniqueOrNonOwningPtr&) = delete; 77 UniqueOrNonOwningPtr(UniqueOrNonOwningPtr&& aOther) : mBits{aOther.mBits} { 78 // "Release" the other one. 79 aOther.mBits = 0; 80 } 81 ~UniqueOrNonOwningPtr() { 82 if (IsUniquelyOwning()) { 83 delete get(); 84 } 85 } 86 UniqueOrNonOwningPtr& operator=(const UniqueOrNonOwningPtr& aOther) = delete; 87 UniqueOrNonOwningPtr& operator=(UniqueOrNonOwningPtr&& aOther) { 88 mBits = aOther.mBits; 89 // "Release" the other one. 90 aOther.mBits = 0; 91 return *this; 92 } 93 94 static UniqueOrNonOwningPtr UniquelyOwning(T* aPtr) { 95 MOZ_ASSERT(aPtr, "Passing in null pointer as owning?"); 96 const uintptr_t bits = reinterpret_cast<uintptr_t>(aPtr); 97 MOZ_ASSERT((bits & kUniquelyOwningBit) == 0, "Odd-aligned owning pointer?"); 98 return UniqueOrNonOwningPtr{bits | kUniquelyOwningBit}; 99 } 100 101 static UniqueOrNonOwningPtr NonOwning(T* aPtr) { 102 const uintptr_t bits = reinterpret_cast<uintptr_t>(aPtr); 103 MOZ_ASSERT((bits & kUniquelyOwningBit) == 0, 104 "Odd-aligned non-owning pointer?"); 105 return UniqueOrNonOwningPtr{bits}; 106 } 107 108 std::add_lvalue_reference_t<T> operator*() const { 109 MOZ_ASSERT( 110 get(), 111 "dereferencing a UniqueOrNonOwningPtr containing nullptr with *"); 112 return *get(); 113 } 114 115 T* operator->() const { 116 MOZ_ASSERT( 117 get(), 118 "dereferencing a UniqueOrNonOwningPtr containing nullptr with ->"); 119 return get(); 120 } 121 122 explicit operator bool() const { return get() != nullptr; } 123 124 T* get() const { return reinterpret_cast<T*>(mBits & ~kUniquelyOwningBit); } 125 126 private: 127 bool IsUniquelyOwning() const { return (mBits & kUniquelyOwningBit) != 0; } 128 129 // Bit for tracking uniquely-owning vs non-owning status. Check usage notes 130 // in the main comment block. 131 // NOTE: A null pointer constant has a guarantee on being integer literal 0. 132 static constexpr uintptr_t kUniquelyOwningBit = 1; 133 explicit UniqueOrNonOwningPtr(uintptr_t aValue) : mBits{aValue} {} 134 uintptr_t mBits; 135 }; 136 137 // Unsupported 138 template <typename T> 139 class UniqueOrNonOwningPtr<T[]>; 140 141 } // namespace mozilla 142 143 #endif