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 */
75template <typename Derived>
76class COW : public boost::intrusive_ref_counter<Derived>
77{
78private:
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
92protected:
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
121public:
122 using MutablePtr = mutable_ptr<Derived>;
123
124protected:
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
165public:
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
174public:
175 Ptr getPtr() const { return static_cast<Ptr>(derived()); }
176 MutablePtr getPtr() { return static_cast<MutablePtr>(derived()); }
177
178protected:
179 MutablePtr shallowMutate() const
180 {
181 if (this->use_count() > 1)
182 return derived()->clone();
183 else
184 return assumeMutable();
185 }
186
187public:
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
203protected:
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
237public:
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 */
280template <typename Base, typename Derived>
281class COWHelper : public Base
282{
283private:
284 Derived * derived() { return static_cast<Derived *>(this); }
285 const Derived * derived() const { return static_cast<const Derived *>(this); }
286
287public:
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
299protected:
300 MutablePtr shallowMutate() const { return MutablePtr(static_cast<Derived *>(Base::shallowMutate().get())); }
301};
302