shared_ptrでCOW

昨日はベクタのコピーを避けるようにしたわけですが、bjam_grammarでクロージャを使い出すとどうしてもコピーが発生してしまうので、Copy On Write(COW)の最適化を施すことにしました。
<hamigaki/bjam/util/list.hpp>
実装にはboost::shared_ptrを使っていて、必要に応じてオブジェクトをクローニングしています。

class string_list
{
private:
    typedef std::vector<std::string> impl_type;

public:
    void push_back(const std::string& s)
    {
        if (pimpl_.get() == 0)
            pimpl_.reset(new impl_type(1u, s));
        else if (pimpl_.unique())
            pimpl_->push_back(s);
        else
        {
            // オブジェクトを共有しているので、コピーを作成
            boost::shared_ptr<impl_type> tmp(new impl_type(*pimpl_));
            tmp->push_back(s);
            pimpl_.swap(tmp);   // コミット
        }
    }

    // ...

private:
    boost::shared_ptr<impl_type> pimpl_;
};

ちなみに最初に書いたバージョンは、

class string_list
{
private:
    typedef std::vector<std::string> impl_type;

public:
    void push_back(const std::string& s)
    {
        if (pimpl_.get() == 0)
            pimpl_.reset(new impl_type(1u, s));
        else
        {
            unique();
            pimpl_->push_back(s);   // ここで例外が発生したら取り消せない
        }
    }

    // ...

private:
    boost::shared_ptr<impl_type> pimpl_;

    // オブジェクトを変更する前に呼ぶ関数(のつもり)
    void unique()
    {
        if (!pimpl_.unique())
        {
            boost::shared_ptr<impl_type> tmp(new impl_type(*pimpl_));
            pimpl_.swap(tmp);
        }
    }
};

となっていて、例外安全性に問題があったので修正しました。


std::vectorではなくなったので、好き勝手に演算子メンバ関数を追加できるのもよいですね。