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
| #include <type_traits>
#include <utility>
#include <assert.h>
#include <stddef.h>
enum NoRefs {};
enum HasMutRef {};
enum HasRefs {};
template <class T, typename Mode>
class Own;
template <class T>
class MutRef;
template <class T>
class Ref;
template <class T, typename... Args>
inline Own<T, NoRefs> make(Args... args) {
return Own<T, NoRefs>(std::forward<Args>(args)...);
}
template <class T>
inline Own<T, NoRefs> consume(Own<T, HasMutRef> own, MutRef<T> ref) {
return Own<T, NoRefs>(std::move(own));
}
template <class T>
inline Own<T, NoRefs> consume(Own<T, HasRefs> own) {
return Own<T, NoRefs>(std::move(own));
}
template <class T>
std::pair<Own<T, HasMutRef>, MutRef<T>> mut(Own<T, NoRefs> own) {
T* t = own.t_;
own.t_ = nullptr;
return std::make_pair(Own<T, HasMutRef>(t), MutRef<T>(t));
}
template <class T>
std::pair<Own<T, HasRefs>, MutRef<T>> ref(Own<T, NoRefs> own) {
T* t = own.t_;
own.t_ = nullptr;
return std::make_pair(Own<T, HasRefs>(t), Ref<T>(t));
}
// No refs exist.
template <class T>
class [[clang::trivial_abi]] Own<T, NoRefs> {
public:
template <typename... Args>
Own(Args... args) : t_(new T(std::forward<Args>(args)...)) {}
~Own() { delete t_; }
Own(Own<T, NoRefs>&& other) : t_(other.t_) { other.t_ = nullptr; }
T& operator*() const noexcept { return *t_; }
T* operator->() const noexcept { return t_; }
private:
friend Own<T, NoRefs> consume<T>(Own<T, HasMutRef> own, MutRef<T> ref);
friend Own<T, NoRefs> consume<T>(Own<T, HasRefs> own);
friend std::pair<Own<T, HasMutRef>, MutRef<T>> mut(Own<T, NoRefs> own);
friend std::pair<Own<T, HasRefs>, Ref<T>> ref(Own<T, NoRefs> own);
Own(Own<T, HasMutRef>&& own) : t_(own.t_) {}
Own(Own<T, HasRefs>&& own) : t_(own.t_) {}
T* t_;
};
// A mut ref exists.
template <class T>
class [[clang::trivial_abi]] Own<T, HasMutRef> {
public:
T& operator*() const noexcept { return *t_; }
T* operator->() const noexcept { return t_; }
private:
friend class Own<T, NoRefs>;
Own(T* t) : t_(t) {}
~Own() {}
T* t_;
};
// Non-mut refs exist.
template <class T>
class [[clang::trivial_abi]] Own<T, HasRefs> {
public:
T& operator*() const noexcept { return *t_; }
T* operator->() const noexcept { return t_; }
Ref<T> ref() { return Ref<T>(t_, &count_); }
private:
friend std::pair<Own<T, HasRefs>, Ref<T>> ref(Own<T, NoRefs> own);
explicit Own(T* t) : t_(t) {}
~Own() { assert(count_ == 0u); }
T* t_;
uint32_t count_;
};
template <class T>
class MutRef {
public:
T& operator*() const noexcept { return *t_; }
T* operator->() const noexcept { return t_; }
~MutRef() = default;
MutRef(MutRef&& other) : t_(other.t_) {}
private:
friend std::pair<Own<T, HasMutRef>, MutRef<T>> mut(Own<T, NoRefs> own);
MutRef(T* t) : t_(t) {}
T* t_;
};
template <class T>
class Ref {
public:
T& operator*() const noexcept { return *t_; }
T* operator->() const noexcept { return t_; }
~Ref() { --(*count_); }
Ref(const Ref& other) : t_(other.t_), count_(other.count_) { ++(*count_); }
Ref(Ref&& other) : t_(other.t_), count_(other.count_) {}
private:
friend std::pair<Own<T, HasRefs>, Ref<T>> ref(Own<T, NoRefs> own);
Ref(T* t, uint32_t* count) : t_(t), count_(count) { ++(*count); }
T* t_;
uint32_t* count_;
};
MutRef<int> Borrows(MutRef<int> i) {
(*i)++;
return i;
}
TEST(Borrow, HelloWorld) {
// Can't do this. The HasMutRefs type is not destructible outside of
// consume()in order to have compiler check it is re-owned, but it won't
// compile. To pass the HasMutRefs to consume() it has to be destroyed both
// inside and outside of consume(). This is true even if trivial_abi is
// used and only one destructor would actually run.
Own<int, NoRefs> i = make<int>(5);
auto& hasmut = mut(std::move(i));
MutRef<int> ref = Borrows(std::move(hasmut.second));
Own<int, NoRefs> i2 = consume(std::move(hasmut.first), std::move(ref));
} |
Partager