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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
#pragma once
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
#include <initializer_list>
/** Copy-on-write shared ptr.
* Allows to work with shared immutable objects and sometimes unshare and mutate you own unique copy.
*
* Usage:
class Column : public COW<Column>
{
private:
friend class COW<Column>;
/// Leave all constructors in private section. They will be available through 'create' method.
Column();
/// Provide 'clone' method. It can be virtual if you want polymorphic behaviour.
virtual Column * clone() const;
public:
/// Correctly use const qualifiers in your interface.
virtual ~Column() {}
};
* It will provide 'create' and 'mutate' methods.
* And 'Ptr' and 'MutablePtr' types.
* Ptr is refcounted pointer to immutable object.
* MutablePtr is refcounted noncopyable pointer to mutable object.
* MutablePtr can be assigned to Ptr through move assignment.
*
* 'create' method creates MutablePtr: you cannot share mutable objects.
* To share, move-assign to immutable pointer.
* 'mutate' method allows to create mutable noncopyable object from immutable object:
* either by cloning or by using directly, if it is not shared.
* These methods are thread-safe.
*
* Example:
*
/// Creating and assigning to immutable ptr.
Column::Ptr x = Column::create(1);
/// Sharing single immutable object in two ptrs.
Column::Ptr y = x;
/// Now x and y are shared.
/// Change value of x.
{
/// Creating mutable ptr. It can clone an object under the hood if it was shared.
Column::MutablePtr mutate_x = IColumn::mutate(std::move(x));
/// Using non-const methods of an object.
mutate_x->set(2);
/// Assigning pointer 'x' to mutated object.
x = std::move(mutate_x);
}
/// Now x and y are unshared and have different values.
* Note. You may have heard that COW is bad practice.
* Actually it is, if your values are small or if copying is done implicitly.
* This is the case for string implementations.
*
* In contrast, COW is intended for the cases when you need to share states of large objects,
* (when you usually will use std::shared_ptr) but you also want precise control over modification
* of this shared state.
*
* Caveats:
* - after a call to 'mutate' method, you can still have a reference to immutable ptr somewhere.
* - as 'mutable_ptr' should be unique, it's refcount is redundant - probably it would be better
* to use std::unique_ptr for it somehow.
*/
template <typename Derived>
class COW : public boost::intrusive_ref_counter<Derived>
{
private:
Derived * derived() { return static_cast<Derived *>(this); }
const Derived * derived() const { return static_cast<const Derived *>(this); }
protected:
template <typename T>
class mutable_ptr : public boost::intrusive_ptr<T> /// NOLINT
{
private:
using Base = boost::intrusive_ptr<T>;
template <typename> friend class COW;
template <typename, typename> friend class COWHelper;
explicit mutable_ptr(T * ptr) : Base(ptr) {}
public:
/// Copy: not possible.
mutable_ptr(const mutable_ptr &) = delete;
/// Move: ok.
mutable_ptr(mutable_ptr &&) = default; /// NOLINT
mutable_ptr & operator=(mutable_ptr &&) = default; /// NOLINT
/// Initializing from temporary of compatible type.
template <typename U>
mutable_ptr(mutable_ptr<U> && other) : Base(std::move(other)) {} /// NOLINT
mutable_ptr() = default;
mutable_ptr(std::nullptr_t) {} /// NOLINT
};
public:
using MutablePtr = mutable_ptr<Derived>;
protected:
template <typename T>
class immutable_ptr : public boost::intrusive_ptr<const T> /// NOLINT
{
private:
using Base = boost::intrusive_ptr<const T>;
template <typename> friend class COW;
template <typename, typename> friend class COWHelper;
explicit immutable_ptr(const T * ptr) : Base(ptr) {}
public:
/// Copy from immutable ptr: ok.
immutable_ptr(const immutable_ptr &) = default;
immutable_ptr & operator=(const immutable_ptr &) = default;
template <typename U>
immutable_ptr(const immutable_ptr<U> & other) : Base(other) {} /// NOLINT
/// Move: ok.
immutable_ptr(immutable_ptr &&) = default; /// NOLINT
immutable_ptr & operator=(immutable_ptr &&) = default; /// NOLINT
/// Initializing from temporary of compatible type.
template <typename U>
immutable_ptr(immutable_ptr<U> && other) : Base(std::move(other)) {} /// NOLINT
/// Move from mutable ptr: ok.
template <typename U>
immutable_ptr(mutable_ptr<U> && other) : Base(std::move(other)) {} /// NOLINT
/// Copy from mutable ptr: not possible.
template <typename U>
immutable_ptr(const mutable_ptr<U> &) = delete;
immutable_ptr() = default;
immutable_ptr(std::nullptr_t) {} /// NOLINT
};
public:
using Ptr = immutable_ptr<Derived>;
template <typename... Args>
static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward<Args>(args)...)); }
template <typename T>
static MutablePtr create(std::initializer_list<T> && arg) { return create(std::forward<std::initializer_list<T>>(arg)); }
Ptr getPtr() const { return static_cast<Ptr>(derived()); }
MutablePtr getPtr() { return static_cast<MutablePtr>(derived()); }
protected:
MutablePtr shallowMutate() const
{
if (this->use_count() > 1)
return derived()->clone();
else
return assumeMutable();
}
public:
static MutablePtr mutate(Ptr ptr)
{
return ptr->shallowMutate();
}
MutablePtr assumeMutable() const
{
return const_cast<COW*>(this)->getPtr();
}
Derived & assumeMutableRef() const
{
return const_cast<Derived &>(*derived());
}
protected:
/// It works as immutable_ptr if it is const and as mutable_ptr if it is non const.
template <typename T>
class chameleon_ptr /// NOLINT
{
private:
immutable_ptr<T> value;
public:
template <typename... Args>
chameleon_ptr(Args &&... args) : value(std::forward<Args>(args)...) {} /// NOLINT
template <typename U>
chameleon_ptr(std::initializer_list<U> && arg) : value(std::forward<std::initializer_list<U>>(arg)) {}
const T * get() const { return value.get(); }
T * get() { return &value->assumeMutableRef(); }
const T * operator->() const { return get(); }
T * operator->() { return get(); }
const T & operator*() const { return *value; }
T & operator*() { return value->assumeMutableRef(); }
operator const immutable_ptr<T> & () const { return value; } /// NOLINT
operator immutable_ptr<T> & () { return value; } /// NOLINT
/// Get internal immutable ptr. Does not change internal use counter.
immutable_ptr<T> detach() && { return std::move(value); }
explicit operator bool() const { return value != nullptr; }
bool operator! () const { return value == nullptr; }
bool operator== (const chameleon_ptr & rhs) const { return value == rhs.value; }
bool operator!= (const chameleon_ptr & rhs) const { return value != rhs.value; }
};
public:
/** Use this type in class members for compositions.
*
* NOTE:
* For classes with WrappedPtr members,
* you must reimplement 'mutate' method, so it will call 'mutate' of all subobjects (do deep mutate).
* It will guarantee, that mutable object have all subobjects unshared.
*
* NOTE:
* 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.
* (COW itself doesn't force any methods to be virtual).
*
* See example in "cow_compositions.cpp".
*/
using WrappedPtr = chameleon_ptr<Derived>;
};
/** Helper class to support inheritance.
* Example:
*
* class IColumn : public COW<IColumn>
* {
* friend class COW<IColumn>;
* virtual MutablePtr clone() const = 0;
* virtual ~IColumn() {}
* };
*
* class ConcreteColumn : public COWHelper<IColumn, ConcreteColumn>
* {
* friend class COWHelper<IColumn, ConcreteColumn>;
* };
*
* Here is complete inheritance diagram:
*
* ConcreteColumn
* COWHelper<IColumn, ConcreteColumn>
* IColumn
* CowPtr<IColumn>
* boost::intrusive_ref_counter<IColumn>
*
* See example in "cow_columns.cpp".
*/
template <typename Base, typename Derived>
class COWHelper : public Base
{
private:
Derived * derived() { return static_cast<Derived *>(this); }
const Derived * derived() const { return static_cast<const Derived *>(this); }
public:
using Ptr = typename Base::template immutable_ptr<Derived>;
using MutablePtr = typename Base::template mutable_ptr<Derived>;
template <typename... Args>
static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward<Args>(args)...)); }
template <typename T>
static MutablePtr create(std::initializer_list<T> && arg) { return MutablePtr(new Derived(std::forward<std::initializer_list<T>>(arg))); }
typename Base::MutablePtr clone() const override { return typename Base::MutablePtr(new Derived(*derived())); }
protected:
MutablePtr shallowMutate() const { return MutablePtr(static_cast<Derived *>(Base::shallowMutate().get())); }
};
|