#pragma once #include #include #include /** 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 { private: friend class COW; /// 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 = std::move(*x).mutate(); /// 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 class COW : public boost::intrusive_ref_counter { private: Derived * derived() { return static_cast(this); } const Derived * derived() const { return static_cast(this); } template class IntrusivePtr : public boost::intrusive_ptr { public: using boost::intrusive_ptr::intrusive_ptr; T & operator*() const & { return boost::intrusive_ptr::operator*(); } T && operator*() const && { return const_cast::type &&>(*boost::intrusive_ptr::get()); } }; protected: template class mutable_ptr : public IntrusivePtr { private: using Base = IntrusivePtr; template friend class COW; template 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; mutable_ptr & operator=(mutable_ptr &&) = default; /// Initializing from temporary of compatible type. template mutable_ptr(mutable_ptr && other) : Base(std::move(other)) {} mutable_ptr() = default; mutable_ptr(std::nullptr_t) {} }; public: using MutablePtr = mutable_ptr; protected: template class immutable_ptr : public IntrusivePtr { private: using Base = IntrusivePtr; template friend class COW; template 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 immutable_ptr(const immutable_ptr & other) : Base(other) {} /// Move: ok. immutable_ptr(immutable_ptr &&) = default; immutable_ptr & operator=(immutable_ptr &&) = default; /// Initializing from temporary of compatible type. template immutable_ptr(immutable_ptr && other) : Base(std::move(other)) {} /// Move from mutable ptr: ok. template immutable_ptr(mutable_ptr && other) : Base(std::move(other)) {} /// Copy from mutable ptr: not possible. template immutable_ptr(const mutable_ptr &) = delete; immutable_ptr() = default; immutable_ptr(std::nullptr_t) {} }; public: using Ptr = immutable_ptr; template static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward(args)...)); } template static MutablePtr create(std::initializer_list && arg) { return create(std::forward>(arg)); } public: Ptr getPtr() const { return static_cast(derived()); } MutablePtr getPtr() { return static_cast(derived()); } protected: MutablePtr shallowMutate() const { if (this->use_count() > 1) return derived()->clone(); else return assumeMutable(); } public: MutablePtr mutate() const && { return shallowMutate(); } MutablePtr assumeMutable() const { return const_cast(this)->getPtr(); } Derived & assumeMutableRef() const { return const_cast(*derived()); } protected: /// It works as immutable_ptr if it is const and as mutable_ptr if it is non const. template class chameleon_ptr { private: immutable_ptr value; public: template chameleon_ptr(Args &&... args) : value(std::forward(args)...) {} template chameleon_ptr(std::initializer_list && arg) : value(std::forward>(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 & () const { return value; } operator immutable_ptr & () { return value; } 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; }; /** Helper class to support inheritance. * Example: * * class IColumn : public COW * { * friend class COW; * virtual MutablePtr clone() const = 0; * virtual ~IColumn() {} * }; * * class ConcreteColumn : public COWHelper * { * friend class COWHelper; * }; * * Here is complete inheritance diagram: * * ConcreteColumn * COWHelper * IColumn * CowPtr * boost::intrusive_ref_counter * * See example in "cow_columns.cpp". */ template class COWHelper : public Base { private: Derived * derived() { return static_cast(this); } const Derived * derived() const { return static_cast(this); } public: using Ptr = typename Base::template immutable_ptr; using MutablePtr = typename Base::template mutable_ptr; template static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward(args)...)); } template static MutablePtr create(std::initializer_list && arg) { return create(std::forward>(arg)); } typename Base::MutablePtr clone() const override { return typename Base::MutablePtr(new Derived(*derived())); } protected: MutablePtr shallowMutate() const { return MutablePtr(static_cast(Base::shallowMutate().get())); } };