Fix ref::operator= for non-const lvalue binding to a temporary.
[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 #include <memory>
16 #include <forward_list>
17
18 namespace zc {
19
20 #if defined(ZCPOINTER_TRACK_REFS) && ZCPOINTER_TRACK_REFS
21
22 template <typename T> class ref;
23
24 namespace internal {
25
26 template <typename T>
27 class OwnedPtrDeleter {
28 public:
29 OwnedPtrDeleter() {}
30 ~OwnedPtrDeleter() {}
31
32 OwnedPtrDeleter(OwnedPtrDeleter&& other) : refs_(std::move(other.refs_)) {
33 }
34
35 void operator()(T* t) const {
36 for (auto& ref : refs_) {
37 ref->MarkDeleted();
38 }
39 delete t;
40 }
41
42 protected:
43 friend class ref<T>;
44
45 void AddRef(ref<T>* ref) {
46 refs_.push_front(ref);
47 }
48
49 void RemoveRef(ref<T>* ref) {
50 refs_.remove(ref);
51 }
52
53 private:
54 std::forward_list<ref<T>*> refs_;
55 };
56
57 void RaiseUseAfterFree(const char* error) __attribute__((noreturn));
58
59 } // namespace internal
60
61 template <typename T>
62 class owned : public std::unique_ptr<T, internal::OwnedPtrDeleter<T>> {
63 private:
64 using Deleter = internal::OwnedPtrDeleter<T>;
65
66 public:
67 using std::unique_ptr<T, Deleter>::unique_ptr;
68
69 ref<T> get() {
70 return ref<T>(*this);
71 }
72
73 protected:
74 friend class ref<T>;
75
76 T* GetRawPointer() const {
77 return get();
78 }
79
80 private:
81 T* get() const {
82 return this->std::unique_ptr<T, Deleter>::get();
83 }
84 };
85
86 template <typename T>
87 class ref {
88 public:
89 ref() : ptr_(nullptr), deleter_(nullptr), deleted_(true) {}
90
91 explicit ref(owned<T>& o) : ptr_(o.GetRawPointer()), deleter_(&o.get_deleter()) {
92 deleter_->AddRef(this);
93 }
94
95 ref(const ref<T>& o) {
96 *this = o;
97 }
98
99 ref<T>& operator=(const ref<T>& o) {
100 ptr_ = o.ptr_;
101 deleter_ = o.deleter_;
102 deleted_ = o.deleted_;
103 if (!deleted_) {
104 deleter_->AddRef(this);
105 }
106 return *this;
107 }
108
109 ~ref() {
110 MarkDeleted();
111 deleter_->RemoveRef(this);
112 }
113
114 T* operator->() const {
115 CheckDeleted();
116 return ptr_;
117 }
118
119 protected:
120 friend class internal::OwnedPtrDeleter<T>;
121
122 void MarkDeleted() {
123 deleted_ = true;
124 }
125
126 private:
127 void CheckDeleted() const {
128 if (deleted_) {
129 internal::RaiseUseAfterFree("attempt to access deleted pointer");
130 }
131 }
132
133 T* ptr_;
134 internal::OwnedPtrDeleter<T>* deleter_;
135 bool deleted_ = false;
136 };
137
138 #else
139
140 template <typename T>
141 using owned = std::unique_ptr<T>;
142
143 template <typename T>
144 using ref = T*;
145
146 #endif
147
148 } // namespace zc