In OwnedPtrDeleter<T>, store a vptr for a behavior function.
[zcpointer.git] / zcpointer.h
1 // Copyright 2016 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ZCPOINTER_ZCPOINTER_H_
16 #define ZCPOINTER_ZCPOINTER_H_
17
18 #include <limits>
19 #include <memory>
20 #include <forward_list>
21 #include <stdexcept>
22 #include <utility>
23
24 namespace zc {
25
26 class UseAfterFreeError : public std::logic_error {
27 public:
28 using std::logic_error::logic_error;
29 };
30
31 #if defined(ZCPOINTER_TRACK_REFS) && ZCPOINTER_TRACK_REFS
32
33 template <typename T> class ref;
34
35 namespace internal {
36
37 enum class OwnershipBehavior {
38 DELETE_POINTER,
39 BORROW_POINTER,
40 };
41
42 template <typename T>
43 class OwnedPtrDeleter {
44 public:
45 OwnedPtrDeleter()
46 : refs_(),
47 finalizer_(&OwnedPtrDeleter<T>::HandleDeletePointer) {}
48
49 ~OwnedPtrDeleter() {}
50
51 explicit OwnedPtrDeleter(OwnershipBehavior behavior)
52 : refs_(),
53 finalizer_(behavior == OwnershipBehavior::BORROW_POINTER
54 ? &OwnedPtrDeleter<T>::HandleBorrowPointer
55 : &OwnedPtrDeleter<T>::HandleDeletePointer) {
56 }
57
58 OwnedPtrDeleter(OwnedPtrDeleter&& other)
59 : refs_(std::move(other.refs_)),
60 finalizer_(other.finalizer_) {
61 }
62
63 void operator=(const OwnedPtrDeleter& o) {
64 refs_ = o.refs_;
65 finalizer_ = o.finalizer_;
66 }
67
68 void operator()(T* t) const {
69 for (auto& ref : refs_) {
70 ref->MarkDeleted();
71 }
72 (this->finalizer_)(t);
73 }
74
75 protected:
76 friend class ref<T>;
77
78 void AddRef(ref<T>* ref) {
79 refs_.push_front(ref);
80 }
81
82 void RemoveRef(ref<T>* ref) {
83 refs_.remove(ref);
84 }
85
86 static void HandleDeletePointer(T* t) {
87 delete t;
88 }
89
90 static void HandleBorrowPointer(T* t) {}
91
92 private:
93 void (*finalizer_)(T*);
94 std::forward_list<ref<T>*> refs_;
95 };
96
97 void RaiseUseAfterFree() __attribute__((noreturn));
98
99 } // namespace internal
100
101 template <typename T>
102 class owned : public std::unique_ptr<T, internal::OwnedPtrDeleter<T>> {
103 private:
104 using Deleter = internal::OwnedPtrDeleter<T>;
105
106 public:
107 using std::unique_ptr<T, Deleter>::unique_ptr;
108
109 ref<T> get() {
110 return ref<T>(*this);
111 }
112
113 private:
114 T* get() const {
115 return this->std::unique_ptr<T, Deleter>::get();
116 }
117 };
118
119 template <typename T>
120 class ref {
121 public:
122 ref() : ptr_(nullptr) {}
123
124 ref(std::nullptr_t) : ref() {}
125
126 explicit ref(owned<T>& o) : ptr_(nullptr) {
127 if (o != nullptr) {
128 ptr_ = &o;
129 ptr_->get_deleter().AddRef(this);
130 }
131 }
132
133 ref(const ref<T>& r) {
134 *this = r;
135 }
136
137 ref<T>& operator=(const ref<T>& o) {
138 ptr_ = o.ptr_;
139 if (ptr_ != nullptr && !IsDeleted()) {
140 ptr_->get_deleter().AddRef(this);
141 }
142 return *this;
143 }
144
145 ~ref() {
146 if (ptr_ != nullptr && !IsDeleted()) {
147 ptr_->get_deleter().RemoveRef(this);
148 }
149 MarkDeleted();
150 }
151
152 T* operator->() const {
153 CheckDeleted();
154 return ptr_->operator->();
155 }
156
157 bool operator==(const ref<T>& r) const {
158 if (ptr_ == nullptr) {
159 return r.ptr_ == nullptr;
160 } else {
161 return ptr_ == r.ptr_ && *ptr_ == *r.ptr_;
162 }
163 }
164
165 bool operator==(std::nullptr_t) const {
166 return ptr_ == nullptr;
167 }
168
169 bool operator!=(const ref<T>& r) const {
170 return !(*this == r);
171 }
172
173 protected:
174 friend class internal::OwnedPtrDeleter<T>;
175
176 void MarkDeleted() {
177 ptr_ = DeletedSentinel();
178 }
179
180 private:
181 void CheckDeleted() const {
182 if (IsDeleted()) {
183 internal::RaiseUseAfterFree();
184 }
185 }
186
187 bool IsDeleted() const {
188 return ptr_ == DeletedSentinel();
189 }
190
191 inline static owned<T>* DeletedSentinel() {
192 return reinterpret_cast<owned<T>*>(std::numeric_limits<uintptr_t>::max());
193 }
194
195 owned<T>* ptr_;
196 };
197
198 template <typename T>
199 class member : public T {
200 public:
201 using T::T;
202
203 ref<T> operator&() {
204 return ptr_.get();
205 }
206
207 private:
208 owned<T> ptr_ = owned<T>(this,
209 internal::OwnedPtrDeleter<T>(
210 internal::OwnershipBehavior::BORROW_POINTER));
211 };
212
213 #else
214
215 template <typename T>
216 using owned = std::unique_ptr<T>;
217
218 template <typename T>
219 using ref = T*;
220
221 template <typename T>
222 using member = T;
223
224 #endif
225
226 } // namespace zc
227
228 #endif // ZCPOINTER_ZCPOINTER_H_