Boost logo

Boost Users :

From: Alan M. Carroll (amc_at_[hidden])
Date: 2008-07-16 14:49:05


I used boost::intrusive_ptr when I did that, along with the mixin class attached at the end. The mixin requires a couple of methods, but otherwise provides most of the standard handle/COW methods. It is presumed to be used in a PIMPL implementation where the handle class inherits from the mixin and contains a smart pointer to the implementation instance. COW is done by cloning the implementation instance when appropriate. That's basically calling unshare() before modifying the instance. I don't use smart pointers directly because I want to hide the pointer-ness from the client.

At 12:33 PM 7/16/2008, Michiel Helvensteijn wrote:
>I am looking for a simple way to implement copy-on-write classes.
>
>Several `copies' of an instantiation of such a class can coexist and share
>the same data in memory until someone changes their `copy' (calls a
>non-const member function). At that time, a deep copy is made and the
>change is only made in that one location. The deep copy is not needed, of
>course, if the instantiation has only one handle. All of this should be
>transparent to the user of the class.
>
>I think implementing such a class would be easier with a special class to
>inherit from and/or a useful set of macro's designed for this purpose.
>
>This seems like something that would (or should) exist in boost. Does boost
>provide something like this?
>
>If not, I will write it myself. But I'm still not sure how to approach it. I
>imagine shared_ptr would play a role, though. Any ideas?

/** Mixin for (some) standard handle class methods.
    This is moderately useful, but is precluded from additional utility by the
    strictures of the design. It can only use the public API because, as a
    template, its implementation is always inline. Access to any insulated
    types / methods / data destroys the insulation.

    This makes the following assumptions about the class T (which can't be
    avoided, as we can't pass in pointers to members of T).
    - There is a method named @c instance which returns a pointer to the implementation
    instance for this handle, creating it if necessary.
    - There is a method named @c ptr which returns a pointer to the implementation
    instance for this handle, returning @c NIL if there is no instance.

    Standard methods that need to be implemented but aren't by this mixin
    - @c unshare

    Typical @c unshare implementation where @c _p is the implementation pointer.
    @code
    T& T::unshare()
    {
        if (_p && _p->_reference_count > 1)
            _p = new imp_type(*_p);
        return *this;
    }
    @endcode
 */
template <typename T, typename I>
struct std_handle_mixin
{
    typedef bool (T::*psuedo_bool)() const;
    /// Make the handle feel like a handle.
    T* operator -> () { return static_cast<T*>(this); }
    /// Make the handle feel like a handle.
    T const* operator -> () const { return static_cast<T const*>(this); }
    /// Return handle to default constructed state (generally a @c nil handle).
    T& reset() { static_cast<T&>(*this) = T(); return static_cast<T&>(*this); }
    /// Force handle to be not @c nil.
    T& touch() { static_cast<T*>(this)->instance(); return static_cast<T&>(*this); }
    //! Test for not @c nil pointer.
    operator psuedo_bool () const { return static_cast<T const*>(this)->ptr() ? &std_handle_mixin::operator! : 0; }
    //! Test for @c nil pointer.
    bool operator ! () const { return static_cast<T const*>(this)->ptr() ? 0 : &std_handle_mixin::operator!; }
    /// Default equality operator, override for non-pointer comparison.
    // Not a function because it needs friendly access to T.
    bool operator == (T const& rhs) { return static_cast<T const*>(this)->ptr() == rhs.ptr(); }
    /// Default inequality operator.
    friend bool operator != (T const& lhs, T const& rhs) { return ! ( lhs == rhs ); }
protected:
    typedef I imp_type; ///< Implementation type.
    typedef boost::intrusive_ptr<I> imp_handle; ///< Handle to implementation instance.
    /* It might be nice to declare the handle here, since we require it to be named
        a specific name, but that turns out to not be feasible. It comes down to the
        constructor. Declaring it here means it can't be initialized in the subclass
        constructor via member initialization, but it's very desirable that the
        intialization is not inline, otherwise insulation is destroyed.
        So the subclass must declare the member as
        @code
            imp_handle _data;
        @endcode
     */
};


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net