2017-12-11 02:59:56 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <boost/smart_ptr/intrusive_ptr.hpp>
|
|
|
|
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
|
2017-12-15 21:46:56 +00:00
|
|
|
#include <initializer_list>
|
2017-12-11 02:59:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
/** 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 COWPtr<Column>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
friend class COWPtr<Column>;
|
|
|
|
|
2017-12-13 01:27:53 +00:00
|
|
|
/// Leave all constructors in private section. They will be avaliable through 'create' method.
|
|
|
|
Column();
|
|
|
|
|
|
|
|
/// Provide 'clone' method. It can be virtual if you want polymorphic behaviour.
|
|
|
|
virtual Column * clone() const;
|
2017-12-11 02:59:56 +00:00
|
|
|
public:
|
2017-12-13 01:27:53 +00:00
|
|
|
/// Correctly use const qualifiers in your interface.
|
2017-12-11 02:59:56 +00:00
|
|
|
|
2017-12-13 01:27:53 +00:00
|
|
|
virtual ~IColumn() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
* 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.
|
2017-12-11 02:59:56 +00:00
|
|
|
*
|
2017-12-13 01:27:53 +00:00
|
|
|
* 'create' method creates MutablePtr: you cannot share mutable objects.
|
|
|
|
* To share, move-assign to immutable pointer.
|
2017-12-11 02:59:56 +00:00
|
|
|
* '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:
|
|
|
|
*
|
2017-12-13 01:27:53 +00:00
|
|
|
/// Creating and assigning to immutable ptr.
|
2017-12-11 02:59:56 +00:00
|
|
|
Column::Ptr x = Column::create(1);
|
2017-12-13 01:27:53 +00:00
|
|
|
/// Sharing single immutable object in two ptrs.
|
2017-12-11 02:59:56 +00:00
|
|
|
Column::Ptr y = x;
|
|
|
|
|
|
|
|
/// Now x and y are shared.
|
|
|
|
|
|
|
|
/// Change value of x.
|
|
|
|
{
|
2017-12-13 01:27:53 +00:00
|
|
|
/// Creating mutable ptr. It can clone an object under the hood if it was shared.
|
2017-12-11 02:59:56 +00:00
|
|
|
Column::MutablePtr mutate_x = x->mutate();
|
2017-12-13 01:27:53 +00:00
|
|
|
/// Using non-const methods of an object.
|
2017-12-11 02:59:56 +00:00
|
|
|
mutate_x->set(2);
|
2017-12-13 01:27:53 +00:00
|
|
|
/// Assigning pointer 'x' to mutated object.
|
2017-12-11 02:59:56 +00:00
|
|
|
x = std::move(mutate_x);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Now x and y are unshared and have different values.
|
2017-12-13 01:27:53 +00:00
|
|
|
|
|
|
|
* 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, COWPtr 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.
|
2018-03-05 18:26:43 +00:00
|
|
|
*
|
|
|
|
* Caveats:
|
|
|
|
* - after a call to 'mutate' method, you can still have a reference to immutable ptr somewhere
|
|
|
|
* and it can still become shared. Also it would be better to make 'mutate' method rvalue-qualified.
|
|
|
|
* - as 'mutable_ptr' should be unique, it's refcount is redundant - probably it would be better
|
|
|
|
* to use std::unique_ptr for it, but see above.
|
2017-12-11 02:59:56 +00:00
|
|
|
*/
|
|
|
|
template <typename Derived>
|
|
|
|
class COWPtr : public boost::intrusive_ref_counter<Derived>
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
Derived * derived() { return static_cast<Derived *>(this); }
|
|
|
|
const Derived * derived() const { return static_cast<const Derived *>(this); }
|
|
|
|
|
2018-03-20 10:58:16 +00:00
|
|
|
template <typename T>
|
|
|
|
class IntrusivePtr : public boost::intrusive_ptr<T>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using boost::intrusive_ptr<T>::intrusive_ptr;
|
|
|
|
|
|
|
|
T & operator*() const & { return boost::intrusive_ptr<T>::operator*(); }
|
|
|
|
T && operator*() const && { return const_cast<typename std::remove_const<T>::type &&>(*boost::intrusive_ptr<T>::get()); }
|
|
|
|
};
|
|
|
|
|
2017-12-13 01:27:53 +00:00
|
|
|
protected:
|
2017-12-14 03:56:56 +00:00
|
|
|
template <typename T>
|
2018-03-20 10:58:16 +00:00
|
|
|
class mutable_ptr : public IntrusivePtr<T>
|
2017-12-14 03:56:56 +00:00
|
|
|
{
|
2017-12-19 00:51:12 +00:00
|
|
|
private:
|
2018-03-20 10:58:16 +00:00
|
|
|
using Base = IntrusivePtr<T>;
|
2017-12-19 00:51:12 +00:00
|
|
|
|
|
|
|
template <typename> friend class COWPtr;
|
|
|
|
template <typename, typename> friend class COWPtrHelper;
|
|
|
|
|
|
|
|
explicit mutable_ptr(T * ptr) : Base(ptr) {}
|
|
|
|
|
2017-12-14 03:56:56 +00:00
|
|
|
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 <typename U>
|
2017-12-19 00:51:12 +00:00
|
|
|
mutable_ptr(mutable_ptr<U> && other) : Base(std::move(other)) {}
|
2017-12-14 03:56:56 +00:00
|
|
|
|
|
|
|
mutable_ptr() = default;
|
2017-12-19 00:43:04 +00:00
|
|
|
|
|
|
|
mutable_ptr(const std::nullptr_t *) {}
|
2017-12-14 03:56:56 +00:00
|
|
|
};
|
2017-12-11 02:59:56 +00:00
|
|
|
|
2017-12-13 01:27:53 +00:00
|
|
|
public:
|
2017-12-14 03:56:56 +00:00
|
|
|
using MutablePtr = mutable_ptr<Derived>;
|
2017-12-13 01:27:53 +00:00
|
|
|
|
|
|
|
protected:
|
2017-12-14 03:56:56 +00:00
|
|
|
template <typename T>
|
2018-03-20 10:58:16 +00:00
|
|
|
class immutable_ptr : public IntrusivePtr<const T>
|
2017-12-11 02:59:56 +00:00
|
|
|
{
|
2017-12-19 00:51:12 +00:00
|
|
|
private:
|
2018-03-20 10:58:16 +00:00
|
|
|
using Base = IntrusivePtr<const T>;
|
2017-12-19 00:51:12 +00:00
|
|
|
|
|
|
|
template <typename> friend class COWPtr;
|
|
|
|
template <typename, typename> friend class COWPtrHelper;
|
|
|
|
|
|
|
|
explicit immutable_ptr(const T * ptr) : Base(ptr) {}
|
|
|
|
|
2017-12-11 02:59:56 +00:00
|
|
|
public:
|
2017-12-14 03:56:56 +00:00
|
|
|
/// Copy from immutable ptr: ok.
|
2017-12-19 00:43:04 +00:00
|
|
|
immutable_ptr(const immutable_ptr &) = default;
|
|
|
|
immutable_ptr & operator=(const immutable_ptr &) = default;
|
|
|
|
|
|
|
|
template <typename U>
|
2017-12-19 00:51:12 +00:00
|
|
|
immutable_ptr(const immutable_ptr<U> & other) : Base(other) {}
|
2017-12-19 00:43:04 +00:00
|
|
|
|
|
|
|
/// Move: ok.
|
|
|
|
immutable_ptr(immutable_ptr &&) = default;
|
|
|
|
immutable_ptr & operator=(immutable_ptr &&) = default;
|
|
|
|
|
|
|
|
/// Initializing from temporary of compatible type.
|
2017-12-11 02:59:56 +00:00
|
|
|
template <typename U>
|
2017-12-19 00:51:12 +00:00
|
|
|
immutable_ptr(immutable_ptr<U> && other) : Base(std::move(other)) {}
|
2017-12-11 02:59:56 +00:00
|
|
|
|
2017-12-14 03:56:56 +00:00
|
|
|
/// Move from mutable ptr: ok.
|
2017-12-11 02:59:56 +00:00
|
|
|
template <typename U>
|
2017-12-19 00:51:12 +00:00
|
|
|
immutable_ptr(mutable_ptr<U> && other) : Base(std::move(other)) {}
|
2017-12-11 02:59:56 +00:00
|
|
|
|
2017-12-14 03:56:56 +00:00
|
|
|
/// Copy from mutable ptr: not possible.
|
2017-12-11 02:59:56 +00:00
|
|
|
template <typename U>
|
2017-12-19 00:43:04 +00:00
|
|
|
immutable_ptr(const mutable_ptr<U> &) = delete;
|
2017-12-14 03:56:56 +00:00
|
|
|
|
|
|
|
immutable_ptr() = default;
|
2017-12-19 00:43:04 +00:00
|
|
|
|
|
|
|
immutable_ptr(const std::nullptr_t *) {}
|
2017-12-11 02:59:56 +00:00
|
|
|
};
|
|
|
|
|
2017-12-13 01:27:53 +00:00
|
|
|
public:
|
2017-12-11 02:59:56 +00:00
|
|
|
using Ptr = immutable_ptr<Derived>;
|
|
|
|
|
|
|
|
template <typename... Args>
|
2017-12-19 00:43:04 +00:00
|
|
|
static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward<Args>(args)...)); }
|
2017-12-11 02:59:56 +00:00
|
|
|
|
2017-12-15 21:46:56 +00:00
|
|
|
template <typename T>
|
|
|
|
static MutablePtr create(std::initializer_list<T> && arg) { return create(std::forward<std::initializer_list<T>>(arg)); }
|
|
|
|
|
2017-12-13 01:27:53 +00:00
|
|
|
public:
|
2017-12-11 02:59:56 +00:00
|
|
|
Ptr getPtr() const { return static_cast<Ptr>(derived()); }
|
|
|
|
MutablePtr getPtr() { return static_cast<MutablePtr>(derived()); }
|
|
|
|
|
|
|
|
MutablePtr mutate() const
|
|
|
|
{
|
|
|
|
if (this->use_count() > 1)
|
2017-12-13 01:27:53 +00:00
|
|
|
return derived()->clone();
|
2017-12-11 02:59:56 +00:00
|
|
|
else
|
2017-12-14 01:43:19 +00:00
|
|
|
return assumeMutable();
|
|
|
|
}
|
|
|
|
|
|
|
|
MutablePtr assumeMutable() const
|
|
|
|
{
|
|
|
|
return const_cast<COWPtr*>(this)->getPtr();
|
2017-12-11 02:59:56 +00:00
|
|
|
}
|
2018-02-12 18:58:14 +00:00
|
|
|
|
|
|
|
Derived & assumeMutableRef() const
|
|
|
|
{
|
|
|
|
return const_cast<Derived &>(*derived());
|
|
|
|
}
|
2017-12-11 02:59:56 +00:00
|
|
|
};
|
2017-12-13 01:27:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
/** Helper class to support inheritance.
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* class IColumn : public COWPtr<IColumn>
|
|
|
|
* {
|
|
|
|
* friend class COWPtr<IColumn>;
|
2017-12-16 04:29:34 +00:00
|
|
|
* virtual MutablePtr clone() const = 0;
|
2017-12-13 01:27:53 +00:00
|
|
|
* virtual ~IColumn() {}
|
|
|
|
* };
|
|
|
|
*
|
|
|
|
* class ConcreteColumn : public COWPtrHelper<IColumn, ConcreteColumn>
|
|
|
|
* {
|
|
|
|
* friend class COWPtrHelper<IColumn, ConcreteColumn>;
|
|
|
|
* };
|
|
|
|
*
|
|
|
|
* Here is complete inheritance diagram:
|
|
|
|
*
|
|
|
|
* ConcreteColumn
|
|
|
|
* COWPtrHelper<IColumn, ConcreteColumn>
|
|
|
|
* IColumn
|
|
|
|
* CowPtr<IColumn>
|
|
|
|
* boost::intrusive_ref_counter<IColumn>
|
|
|
|
*/
|
|
|
|
template <typename Base, typename Derived>
|
|
|
|
class COWPtrHelper : 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>;
|
2017-12-14 03:56:56 +00:00
|
|
|
using MutablePtr = typename Base::template mutable_ptr<Derived>;
|
2017-12-13 01:27:53 +00:00
|
|
|
|
|
|
|
template <typename... Args>
|
2017-12-19 00:43:04 +00:00
|
|
|
static MutablePtr create(Args &&... args) { return MutablePtr(new Derived(std::forward<Args>(args)...)); }
|
2017-12-13 01:27:53 +00:00
|
|
|
|
2017-12-15 21:46:56 +00:00
|
|
|
template <typename T>
|
|
|
|
static MutablePtr create(std::initializer_list<T> && arg) { return create(std::forward<std::initializer_list<T>>(arg)); }
|
|
|
|
|
2017-12-19 00:43:04 +00:00
|
|
|
typename Base::MutablePtr clone() const override { return typename Base::MutablePtr(new Derived(*derived())); }
|
2017-12-13 01:27:53 +00:00
|
|
|
};
|
2017-12-14 01:43:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
/** Compositions.
|
|
|
|
*
|
|
|
|
* Sometimes your objects contain another objects, and you have tree-like structure.
|
|
|
|
* And you want non-const methods of your object to also modify your subobjects.
|
|
|
|
*
|
|
|
|
* There are the following possible solutions:
|
|
|
|
*
|
|
|
|
* 1. Store subobjects as immutable ptrs. Call mutate method of subobjects inside non-const methods of your objects; modify them and assign back.
|
|
|
|
* Drawback: additional checks inside methods: CPU overhead on atomic ops.
|
|
|
|
*
|
|
|
|
* 2. Store subobjects as mutable ptrs. Subobjects cannot be shared in another objects.
|
|
|
|
* Drawback: it's not possible to share subobjects.
|
|
|
|
*
|
|
|
|
* 3. Store subobjects as immutable ptrs. Implement copy-constructor to do shallow copy.
|
|
|
|
* But reimplement 'mutate' method, so it will call 'mutate' of all subobjects (do deep mutate).
|
|
|
|
* It will guarantee, that mutable object have all subobjects unshared.
|
2018-02-12 18:58:14 +00:00
|
|
|
* From non-const method, you can modify subobjects with 'assumeMutableRef' method.
|
2017-12-14 01:43:19 +00:00
|
|
|
* Drawback: it's more complex than other solutions.
|
|
|
|
*/
|