1 | #pragma once |
2 | |
3 | #include <boost/smart_ptr/intrusive_ptr.hpp> |
4 | #include <boost/smart_ptr/intrusive_ref_counter.hpp> |
5 | #include <initializer_list> |
6 | |
7 | |
8 | /** Copy-on-write shared ptr. |
9 | * Allows to work with shared immutable objects and sometimes unshare and mutate you own unique copy. |
10 | * |
11 | * Usage: |
12 | |
13 | class Column : public COW<Column> |
14 | { |
15 | private: |
16 | friend class COW<Column>; |
17 | |
18 | /// Leave all constructors in private section. They will be avaliable through 'create' method. |
19 | Column(); |
20 | |
21 | /// Provide 'clone' method. It can be virtual if you want polymorphic behaviour. |
22 | virtual Column * clone() const; |
23 | public: |
24 | /// Correctly use const qualifiers in your interface. |
25 | |
26 | virtual ~Column() {} |
27 | }; |
28 | |
29 | * It will provide 'create' and 'mutate' methods. |
30 | * And 'Ptr' and 'MutablePtr' types. |
31 | * Ptr is refcounted pointer to immutable object. |
32 | * MutablePtr is refcounted noncopyable pointer to mutable object. |
33 | * MutablePtr can be assigned to Ptr through move assignment. |
34 | * |
35 | * 'create' method creates MutablePtr: you cannot share mutable objects. |
36 | * To share, move-assign to immutable pointer. |
37 | * 'mutate' method allows to create mutable noncopyable object from immutable object: |
38 | * either by cloning or by using directly, if it is not shared. |
39 | * These methods are thread-safe. |
40 | * |
41 | * Example: |
42 | * |
43 | /// Creating and assigning to immutable ptr. |
44 | Column::Ptr x = Column::create(1); |
45 | /// Sharing single immutable object in two ptrs. |
46 | Column::Ptr y = x; |
47 | |
48 | /// Now x and y are shared. |
49 | |
50 | /// Change value of x. |
51 | { |
52 | /// Creating mutable ptr. It can clone an object under the hood if it was shared. |
53 | Column::MutablePtr mutate_x = std::move(*x).mutate(); |
54 | /// Using non-const methods of an object. |
55 | mutate_x->set(2); |
56 | /// Assigning pointer 'x' to mutated object. |
57 | x = std::move(mutate_x); |
58 | } |
59 | |
60 | /// Now x and y are unshared and have different values. |
61 | |
62 | * Note. You may have heard that COW is bad practice. |
63 | * Actually it is, if your values are small or if copying is done implicitly. |
64 | * This is the case for string implementations. |
65 | * |
66 | * In contrast, COW is intended for the cases when you need to share states of large objects, |
67 | * (when you usually will use std::shared_ptr) but you also want precise control over modification |
68 | * of this shared state. |
69 | * |
70 | * Caveats: |
71 | * - after a call to 'mutate' method, you can still have a reference to immutable ptr somewhere. |
72 | * - as 'mutable_ptr' should be unique, it's refcount is redundant - probably it would be better |
73 | * to use std::unique_ptr for it somehow. |
74 | */ |
75 | template <typename Derived> |
76 | class COW : public boost::intrusive_ref_counter<Derived> |
77 | { |
78 | private: |
79 | Derived * derived() { return static_cast<Derived *>(this); } |
80 | const Derived * derived() const { return static_cast<const Derived *>(this); } |
81 | |
82 | template <typename T> |
83 | class IntrusivePtr : public boost::intrusive_ptr<T> |
84 | { |
85 | public: |
86 | using boost::intrusive_ptr<T>::intrusive_ptr; |
87 | |
88 | T & operator*() const & { return boost::intrusive_ptr<T>::operator*(); } |
89 | T && operator*() const && { return const_cast<typename std::remove_const<T>::type &&>(*boost::intrusive_ptr<T>::get()); } |
90 | }; |
91 | |
92 | protected: |
93 | template <typename T> |
94 | class mutable_ptr : public IntrusivePtr<T> |
95 | { |
96 | private: |
97 | using Base = IntrusivePtr<T>; |
98 | |
99 | template <typename> friend class COW; |
100 | template <typename, typename> friend class COWHelper; |
101 | |
102 | explicit mutable_ptr(T * ptr) : Base(ptr) {} |
103 | |
104 | public: |
105 | /// Copy: not possible. |
106 | mutable_ptr(const mutable_ptr &) = delete; |
107 | |
108 | /// Move: ok. |
109 | mutable_ptr(mutable_ptr &&) = default; |
110 | mutable_ptr & operator=(mutable_ptr &&) = default; |
111 | |
112 | /// Initializing from temporary of compatible type. |
113 | template <typename U> |
114 | mutable_ptr(mutable_ptr<U> && other) : Base(std::move(other)) {} |
115 | |
116 | mutable_ptr() = default; |
117 | |
118 | mutable_ptr(std::nullptr_t) {} |
119 | }; |
120 | |
121 | public: |
122 | using MutablePtr = mutable_ptr<Derived>; |
123 | |
124 | protected: |
125 | template <typename T> |
126 | class immutable_ptr : public IntrusivePtr<const T> |
127 | { |
128 | private: |
129 | using Base = IntrusivePtr<const T>; |
130 | |
131 | template <typename> friend class COW; |
132 | template <typename, typename> friend class COWHelper; |
133 | |
134 | explicit immutable_ptr(const T * ptr) : Base(ptr) {} |
135 | |
136 | public: |
137 | /// Copy from immutable ptr: ok. |
138 | immutable_ptr(const immutable_ptr &) = default; |
139 | immutable_ptr & operator=(const immutable_ptr &) = default; |
140 | |
141 | template <typename U> |
142 | immutable_ptr(const immutable_ptr<U> & other) : Base(other) {} |
143 | |
144 | /// Move: ok. |
145 | immutable_ptr(immutable_ptr &&) = default; |
146 | immutable_ptr & operator=(immutable_ptr &&) = default; |
147 | |
148 | /// Initializing from temporary of compatible type. |
149 | template <typename U> |
150 | immutable_ptr(immutable_ptr<U> && other) : Base(std::move(other)) {} |
151 | |
152 | /// Move from mutable ptr: ok. |
153 | template <typename U> |
154 | immutable_ptr(mutable_ptr<U> && other) : Base(std::move(other)) {} |
155 | |
156 | /// Copy from mutable ptr: not possible. |
157 | template <typename U> |
158 | immutable_ptr(const mutable_ptr<U> &) = delete; |
159 | |
160 | immutable_ptr() = default; |
161 | |
162 | immutable_ptr(std::nullptr_t) {} |
163 | }; |
164 | |
165 | public: |
166 | using Ptr = immutable_ptr<Derived>; |
167 | |
168 | template <typename... Args> |
169 | static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward<Args>(args)...)); } |
170 | |
171 | template <typename T> |
172 | static MutablePtr create(std::initializer_list<T> && arg) { return create(std::forward<std::initializer_list<T>>(arg)); } |
173 | |
174 | public: |
175 | Ptr getPtr() const { return static_cast<Ptr>(derived()); } |
176 | MutablePtr getPtr() { return static_cast<MutablePtr>(derived()); } |
177 | |
178 | protected: |
179 | MutablePtr shallowMutate() const |
180 | { |
181 | if (this->use_count() > 1) |
182 | return derived()->clone(); |
183 | else |
184 | return assumeMutable(); |
185 | } |
186 | |
187 | public: |
188 | MutablePtr mutate() const && |
189 | { |
190 | return shallowMutate(); |
191 | } |
192 | |
193 | MutablePtr assumeMutable() const |
194 | { |
195 | return const_cast<COW*>(this)->getPtr(); |
196 | } |
197 | |
198 | Derived & assumeMutableRef() const |
199 | { |
200 | return const_cast<Derived &>(*derived()); |
201 | } |
202 | |
203 | protected: |
204 | /// It works as immutable_ptr if it is const and as mutable_ptr if it is non const. |
205 | template <typename T> |
206 | class chameleon_ptr |
207 | { |
208 | private: |
209 | immutable_ptr<T> value; |
210 | |
211 | public: |
212 | template <typename... Args> |
213 | chameleon_ptr(Args &&... args) : value(std::forward<Args>(args)...) {} |
214 | |
215 | template <typename U> |
216 | chameleon_ptr(std::initializer_list<U> && arg) : value(std::forward<std::initializer_list<U>>(arg)) {} |
217 | |
218 | const T * get() const { return value.get(); } |
219 | T * get() { return &value->assumeMutableRef(); } |
220 | |
221 | const T * operator->() const { return get(); } |
222 | T * operator->() { return get(); } |
223 | |
224 | const T & operator*() const { return *value; } |
225 | T & operator*() { return value->assumeMutableRef(); } |
226 | |
227 | operator const immutable_ptr<T> & () const { return value; } |
228 | operator immutable_ptr<T> & () { return value; } |
229 | |
230 | operator bool() const { return value != nullptr; } |
231 | bool operator! () const { return value == nullptr; } |
232 | |
233 | bool operator== (const chameleon_ptr & rhs) const { return value == rhs.value; } |
234 | bool operator!= (const chameleon_ptr & rhs) const { return value != rhs.value; } |
235 | }; |
236 | |
237 | public: |
238 | /** Use this type in class members for compositions. |
239 | * |
240 | * NOTE: |
241 | * For classes with WrappedPtr members, |
242 | * you must reimplement 'mutate' method, so it will call 'mutate' of all subobjects (do deep mutate). |
243 | * It will guarantee, that mutable object have all subobjects unshared. |
244 | * |
245 | * NOTE: |
246 | * If you override 'mutate' method in inherited classes, don't forget to make it virtual in base class or to make it call a virtual method. |
247 | * (COW itself doesn't force any methods to be virtual). |
248 | * |
249 | * See example in "cow_compositions.cpp". |
250 | */ |
251 | using WrappedPtr = chameleon_ptr<Derived>; |
252 | }; |
253 | |
254 | |
255 | /** Helper class to support inheritance. |
256 | * Example: |
257 | * |
258 | * class IColumn : public COW<IColumn> |
259 | * { |
260 | * friend class COW<IColumn>; |
261 | * virtual MutablePtr clone() const = 0; |
262 | * virtual ~IColumn() {} |
263 | * }; |
264 | * |
265 | * class ConcreteColumn : public COWHelper<IColumn, ConcreteColumn> |
266 | * { |
267 | * friend class COWHelper<IColumn, ConcreteColumn>; |
268 | * }; |
269 | * |
270 | * Here is complete inheritance diagram: |
271 | * |
272 | * ConcreteColumn |
273 | * COWHelper<IColumn, ConcreteColumn> |
274 | * IColumn |
275 | * CowPtr<IColumn> |
276 | * boost::intrusive_ref_counter<IColumn> |
277 | * |
278 | * See example in "cow_columns.cpp". |
279 | */ |
280 | template <typename Base, typename Derived> |
281 | class COWHelper : public Base |
282 | { |
283 | private: |
284 | Derived * derived() { return static_cast<Derived *>(this); } |
285 | const Derived * derived() const { return static_cast<const Derived *>(this); } |
286 | |
287 | public: |
288 | using Ptr = typename Base::template immutable_ptr<Derived>; |
289 | using MutablePtr = typename Base::template mutable_ptr<Derived>; |
290 | |
291 | template <typename... Args> |
292 | static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward<Args>(args)...)); } |
293 | |
294 | template <typename T> |
295 | static MutablePtr create(std::initializer_list<T> && arg) { return create(std::forward<std::initializer_list<T>>(arg)); } |
296 | |
297 | typename Base::MutablePtr clone() const override { return typename Base::MutablePtr(new Derived(*derived())); } |
298 | |
299 | protected: |
300 | MutablePtr shallowMutate() const { return MutablePtr(static_cast<Derived *>(Base::shallowMutate().get())); } |
301 | }; |
302 | |