C++ Talk Tipp & copy_on_write Code (dies ist keine Frage)



  • Hi!

    Inspiriert von Sean Parent's Vortrag "Value Semantics and Concepts-based Polymorphism", den ich übrigens empfehlen kann, habe ich mich just-4-fun mal dran gesetzt, einen copy_on_write-Wrapper zu basteln, so wie er im Vortrag verwendet wurde. Das Ding ist mit Hilfe von shared_ptr und make_shared auch relativ easy umzusetzen und könnte hier ja irgendjemandem nutzen oder einen Denkanstoß verpassen.

    Warum wieso weshalb copy_on_write? --> Wertesemantik mit "Sharing" als Optimierung, effizientes Kopieren und Moven ist unabhängig von T möglich.

    copy_on_write.hpp:

    #ifndef COPY_ON_WRITE_HPP_INCLUDED
    #define COPY_ON_WRITE_HPP_INCLUDED
    
    #include <cassert>
    #include <type_traits>
    #include <utility>
    #include <memory>
    
    enum cow_dont_initialize_tag { cow_dont_initialize };
    
    /// Copy-On-Write wrapper for a T object. It supports a "null state"
    /// (you have to ask for it via an explicit std::move or the
    /// cow_dont_initialize constructor parameter, though!).
    template<class T>
    class copy_on_write
    {
    public:
    
    	/// Construct copy_on_write object without initializing a T object.
    	/// The function initialized() will return false and you're not allowed
    	/// to access the T object (because it doesn't exist!).
    	explicit copy_on_write(cow_dont_initialize_tag) noexcept
    	: ptr(nullptr)
    	{}
    
    	/// Construct copy_on_write object which holds a default-constructed T object
    	copy_on_write()
    	{
    		static const std::shared_ptr<T> default_constructed = std::make_shared<T>();
    		this->ptr = default_constructed;
    	}
    
    	/// Construct copy_on_write object to store a given T object initialized
    	/// with an initializer_list.
    	template<class E
    		,class=typename std::enable_if<(
    			std::is_constructible<T,std::initializer_list<E>>::value
    		)>::type
    	>
    	copy_on_write(std::initializer_list<E> il)
    	: ptr(std::make_shared<T>(il))
    	{}
    
    	/// Construct copy_on_write object to hold a T object constructed by
    	/// forwarding parameters
    	template<class Arg, class...Args
    		,class=typename std::enable_if<(
    			std::is_constructible<T,Arg,Args...>::value &&
    			( sizeof...(Args)>0 || // exclude copy/move ctor for copy_on_write
    			  !std::is_same<typename std::decay<Arg>::type,copy_on_write<T>>::value )
    		)>::type
    	>
    	copy_on_write(Arg&& arg, Args&&...args)
    	: ptr(std::make_shared<T>(
    		std::forward<Arg>(arg),
    		std::forward<Args>(args)... ))
    	{}
    
    	/// copy ctor
    	copy_on_write(copy_on_write const& x) noexcept
    	: ptr(x.ptr)
    	{}
    
    	/// move ctor
    	copy_on_write(copy_on_write && x) noexcept
    	: ptr(std::move(x.ptr))
    	{}
    
    	// assignment
    	copy_on_write& operator=(copy_on_write temp) noexcept
    	{ this->swap(temp); return *this; }
    
    	void swap(copy_on_write& that) noexcept
    	{ this->ptr.swap(that.ptr); }
    
    	friend void swap(copy_on_write& a, copy_on_write& b) noexcept
    	{ a.swap(b); }
    
    	// returns true if and only if this object owns an initialized T object
    	bool initialized() const noexcept
    	{ return ptr != nullptr; }
    
    	// destroys the owned T object
    	void destroy() noexcept
    	{ ptr.reset(nullptr); }
    
    	explicit operator bool() const noexcept
    	{ return initialized(); }
    
    	// read access ...
    	T const& read() const noexcept{ assert(initialized()); return *ptr; }
    	T const& operator*() const noexcept { assert(initialized()); return *ptr; }
    	T const* operator->() const noexcept { assert(initialized()); return ptr.get(); }
    
    	// write access ...
    	T& write()
    	{
    		assert(initialized());
    		if (!ptr.unique()) uniqify();
    		assert(ptr.unique());
    		return *ptr;
    	}
    
    private:
    	std::shared_ptr<T> ptr;
    
    	void uniqify();
    };
    
    template<class T>
    void copy_on_write<T>::uniqify()
    {
    	copy_on_write tmp (this->read());
    	this->swap(tmp);
    }
    
    #endif
    

    Simples, etwas realitätsfernes Beispiel:

    #include <cassert>
    #include <vector>
    #include "copy_on_write.hpp"
    
    using namespace std;
    
    int main()
    {
        copy_on_write<vector<int>> x = {2,3,5,7,11};
        copy_on_write<vector<int>> y = x;
        assert( &x.read() == &y.read() );
        y.write()[2] += 10;
        assert( &x.read() != &y.read() );
    }
    

    (kompiliert mit G++ 4.6.1 und C++0x-Schalter)



  • krümelkacker schrieb:

    Inspiriert von Sean Parent's Vortrag "Value Semantics and Concepts-based Polymorphism"

    Gibts das auch als Text? Ich kann mit Videos nichts anfangen.



  • Ja:
    https://github.com/boostcon/cppnow_presentations_2012/tree/master/fri/value_semantics

    Da gibt's sogar Quellcode. Allerdings hat Sean Parent hier nicht erkannt, dass man sich das Leben da mit shared_ptr für die copy_on_write-Implementierung etwas einfacher machen kann. Dann erübrigt sich das auch mit der thread-sicheren Referenzzählung, die ja in shared_ptr schon eingebaut ist. 😃


Anmelden zum Antworten