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
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef mozilla_UniqueOrNonOwningPtr_h
#define mozilla_UniqueOrNonOwningPtr_h
#include <cstdint>
#include <utility>
#include "mozilla/Assertions.h"
namespace mozilla {
template <typename T>
class UniqueOrNonOwningPtr;
namespace detail {
template <typename T>
struct UniqueOfUniqueOrNonOwningSelector {
using SingleObject = UniqueOrNonOwningPtr<T>;
};
template <typename T>
struct UniqueOfUniqueOrNonOwningSelector<T[]>;
template <typename T, decltype(sizeof(int)) N>
struct UniqueOfUniqueOrNonOwningSelector<T[N]>;
} // namespace detail
// `mozilla::MakeUnique` equivalent, with the same set of advantages.
// Non-owning case doesn't need this since there's no allocation.
// See below as to why only SingleObject case is supported.
template <typename T, typename... Args>
typename detail::UniqueOfUniqueOrNonOwningSelector<T>::SingleObject
MakeUniqueOfUniqueOrNonOwning(Args&&... aArgs) {
return UniqueOrNonOwningPtr<T>::UniquelyOwning(
new T(std::forward<Args>(aArgs)...));
}
/**
* A pointer that is either:
* * Uniquely-owning, as if `std::unique_ptr`/`mozilla::UniquePtr`, or
* * Non-owning, as if raw pointer.
*
* Overall, it behaves like `mozilla::Variant<T*, UniquePtr<T>>`, but more
* compact. It may be helpful if you are mostly referencing existing data type
* of significant size, but sometimes generate a modified copy and refer to
* that.
*
* Usage notes:
* * Ownership: This structure makes ownership tracking harder. It is the
* caller's responsibility to ensure that, in the non-owning case, the data
* outlives this pointer.
* * (Ab)using the lowest bit: Owning state is tagged inline in the lowest
* bit, which is set for uniquely-owning data. It does not work with a
* byte-aligned data types, or members of a packed struct. There are asserts to
* try and catch this as early as possible.
*
* TODO(dshin): This lacks support for things that `mozilla::UniquePtr` supports
* - however, these cases will fail to compile.
* * Deleter support (Even stateless ones)
* * Interconversion (Pointing to derived from base pointer)
* * T[]
*/
template <typename T>
class UniqueOrNonOwningPtr {
public:
// Check to make sure we can take on non-owning pointer to stack.
static_assert(alignof(T) != 1,
"Can't support data aligned to byte boundaries.");
// Standard guarantees the null pointer value to be integer 0.
UniqueOrNonOwningPtr() : mBits{0} {}
UniqueOrNonOwningPtr(const UniqueOrNonOwningPtr&) = delete;
UniqueOrNonOwningPtr(UniqueOrNonOwningPtr&& aOther) : mBits{aOther.mBits} {
// "Release" the other one.
aOther.mBits = 0;
}
~UniqueOrNonOwningPtr() {
if (IsUniquelyOwning()) {
delete get();
}
}
UniqueOrNonOwningPtr& operator=(const UniqueOrNonOwningPtr& aOther) = delete;
UniqueOrNonOwningPtr& operator=(UniqueOrNonOwningPtr&& aOther) {
mBits = aOther.mBits;
// "Release" the other one.
aOther.mBits = 0;
return *this;
}
static UniqueOrNonOwningPtr UniquelyOwning(T* aPtr) {
MOZ_ASSERT(aPtr, "Passing in null pointer as owning?");
const uintptr_t bits = reinterpret_cast<uintptr_t>(aPtr);
MOZ_ASSERT((bits & kUniquelyOwningBit) == 0, "Odd-aligned owning pointer?");
return UniqueOrNonOwningPtr{bits | kUniquelyOwningBit};
}
static UniqueOrNonOwningPtr NonOwning(T* aPtr) {
const uintptr_t bits = reinterpret_cast<uintptr_t>(aPtr);
MOZ_ASSERT((bits & kUniquelyOwningBit) == 0,
"Odd-aligned non-owning pointer?");
return UniqueOrNonOwningPtr{bits};
}
std::add_lvalue_reference_t<T> operator*() const {
MOZ_ASSERT(
get(),
"dereferencing a UniqueOrNonOwningPtr containing nullptr with *");
return *get();
}
T* operator->() const {
MOZ_ASSERT(
get(),
"dereferencing a UniqueOrNonOwningPtr containing nullptr with ->");
return get();
}
explicit operator bool() const { return get() != nullptr; }
T* get() const { return reinterpret_cast<T*>(mBits & ~kUniquelyOwningBit); }
private:
bool IsUniquelyOwning() const { return (mBits & kUniquelyOwningBit) != 0; }
// Bit for tracking uniquely-owning vs non-owning status. Check usage notes
// in the main comment block.
// NOTE: A null pointer constant has a guarantee on being integer literal 0.
static constexpr uintptr_t kUniquelyOwningBit = 1;
explicit UniqueOrNonOwningPtr(uintptr_t aValue) : mBits{aValue} {}
uintptr_t mBits;
};
// Unsupported
template <typename T>
class UniqueOrNonOwningPtr<T[]>;
} // namespace mozilla
#endif
|