| 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 | |