-
Notifications
You must be signed in to change notification settings - Fork 0
/
ref.hpp
131 lines (94 loc) · 2.93 KB
/
ref.hpp
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
#ifndef CPP_REF_HPP
#define CPP_REF_HPP
#include <stdexcept>
#include <utility>
#include <cassert>
struct ref_counted {
virtual ~ref_counted() { }
std::size_t rc = 0;
friend void incref(ref_counted* self) { self->rc++; }
friend void decref(ref_counted* self) { if(--self->rc == 0) delete self; }
};
template<class T> class ref;
class ref_any {
using ptr_type = ref_counted*;
ptr_type ptr;
public:
ref_any() : ptr(nullptr) { }
ref_any(ptr_type ptr) : ptr(ptr) {
if(ptr) incref(ptr);
}
ptr_type get() const { return ptr; }
~ref_any() { if(ptr) decref(ptr); }
ref_any(const ref_any& other) noexcept : ptr(other.ptr) {
if(ptr) incref(ptr);
}
ref_any(ref_any&& other) noexcept : ptr(std::move(other.ptr)) {
other.ptr = nullptr;
}
ref_any& operator=(const ref_any& other) noexcept {
if(this == &other) return *this;
if(ptr) decref(ptr);
ptr = other.ptr;
if(ptr) incref(ptr);
return *this;
}
ref_any& operator=(ref_any&& other) noexcept{
if(this == &other) return *this;
if(ptr) decref(ptr);
ptr = std::move(other.ptr);
other.ptr = nullptr;
return *this;
}
explicit operator bool() const { return ptr; }
bool operator==(const ref_any& other) const { return ptr == other.ptr; }
bool operator!=(const ref_any& other) const { return ptr != other.ptr; }
bool operator<(const ref_any& other) const { return ptr < other.ptr; }
std::size_t rc() const {
if(ptr) return ptr->rc;
throw std::invalid_argument("null pointer");
}
// type recovery
template<class T> ref<T> cast() const;
};
template<class T>
struct control_block : ref_counted, T {
template<class ... Args>
control_block(Args&& ... args) : T(std::forward<Args>(args)...) { }
};
template<class T>
class ref {
ref_any impl;
protected:
ref(control_block<T>* block) : impl(block) { }
friend class ref_any;
public:
template<class ... Args>
static ref make(Args&& ... args) {
return new control_block<T>(std::forward<Args>(args)...);
}
T* get() const {
if(impl) return static_cast< control_block<T>* >(impl.get());
return nullptr;
}
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
ref() = default;
ref(const ref& ) = default;
ref(ref&& ) = default;
ref& operator=(const ref& ) = default;
ref& operator=(ref&& ) = default;
// type erasure
ref_any any() const { return impl; }
explicit operator bool() const { return bool(impl); }
bool operator==(const ref& other) const { return impl == other.impl; }
bool operator!=(const ref& other) const { return impl != other.impl; }
bool operator<(const ref& other) const { return impl < other.impl; }
};
template<class T>
ref<T> ref_any::cast() const {
return static_cast<control_block<T>*>(ptr);
}
template<class T, class ... Args>
static ref<T> make_ref(Args&& ... args) { return ref<T>::make(std::forward<Args>(args)...); }
#endif