#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 = 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 class COW : public boost::intrusive_ref_counter { private: Derived * derived() { return static_cast(this); } const Derived * derived() const { return static_cast(this); } protected: template class mutable_ptr : public boost::intrusive_ptr /// NOLINT { private: using Base = boost::intrusive_ptr; 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; /// NOLINT mutable_ptr & operator=(mutable_ptr &&) = default; /// NOLINT /// Initializing from temporary of compatible type. template mutable_ptr(mutable_ptr && other) : Base(std::move(other)) {} /// NOLINT mutable_ptr() = default; mutable_ptr(std::nullptr_t) {} /// NOLINT }; public: using MutablePtr = mutable_ptr; protected: template class immutable_ptr : public boost::intrusive_ptr /// NOLINT { private: using Base = boost::intrusive_ptr; 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) {} /// NOLINT /// Move: ok. immutable_ptr(immutable_ptr &&) = default; /// NOLINT immutable_ptr & operator=(immutable_ptr &&) = default; /// NOLINT /// Initializing from temporary of compatible type. template immutable_ptr(immutable_ptr && other) : Base(std::move(other)) {} /// NOLINT /// Move from mutable ptr: ok. template immutable_ptr(mutable_ptr && other) : Base(std::move(other)) {} /// NOLINT /// Copy from mutable ptr: not possible. template immutable_ptr(const mutable_ptr &) = delete; immutable_ptr() = default; immutable_ptr(std::nullptr_t) {} /// NOLINT }; 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)); } 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: static MutablePtr mutate(Ptr ptr) { return ptr->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 /// NOLINT { private: immutable_ptr value; public: template chameleon_ptr(Args &&... args) : value(std::forward(args)...) {} /// NOLINT 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; } /// NOLINT operator immutable_ptr & () { return value; } /// NOLINT /// Get internal immutable ptr. Does not change internal use counter. immutable_ptr 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; }; /** 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 MutablePtr(new Derived(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())); } };