immutable な string クラスを作りたい(4)

前回 の続き。
作った immutable_string に文字列結合を追加し、簡単なベンチマークを行ってみます。


さて文字列を結合する方法ですが、今回は単純に immutable_string_buffer を拡張します。

namespace gintenlib
{
  // 本題
  class immutable_string_buffer
    : public intrusive_hook<immutable_string_buffer, variable_region_deleter>
  {
    // ...(略)
    
    // オブジェクト製作
    
    // 領域サイズとイテレータから製作
    template<typename InIter>
    static pointer create( std::size_t size, InIter const& src )
    {
      // intrusive_ptr に格納しておけば、例外が投げられてもリークしない
      boost::intrusive_ptr<this_type> const p = create_(size);
      
      // バッファを初期化する
      gintenlib::assign( &p->buf[0], &p->buf[size], src );
      // NULL終端
      p->buf[size] = 0;
      
      // ハッシュ値を計算する
      p->hashed = boost::hash_range( &p->buf[0], &p->buf[size] );
      
      return p;
      
    }
    // より一般的な方。イテレータ対から製作
    template<typename FwdIter>
    static pointer create( FwdIter const& begin, FwdIter const& end )
    {
      return create( std::distance( begin, end ), begin );
    }
    // 空文字列
    // 少し変更
    static pointer create()
    {
      static const char* const str0 = "";
      static pointer const p = create( 0, str0 );
      return p;
    }
    // +++++++++++++++++++++++++++++++++++++++++++++++++++
    // 連結(追加した部分)
    template<typename InIter1, typename InIter2>
    static pointer create( std::size_t n1, InIter1 const& src1, std::size_t n2, InIter2 const& src2 )
    {
      std::size_t const size = n1 + n2;
      boost::intrusive_ptr<this_type> const p = create_(size);
      
      // バッファを初期化する
      gintenlib::assign( &p->buf[0], &p->buf[n1], src1 );
      gintenlib::assign( &p->buf[n1], &p->buf[size], src2 );
      // NULL終端
      p->buf[size] = 0;
      
      // ハッシュ値を計算する
      p->hashed = boost::hash_range( &p->buf[0], &p->buf[size] );
      
      return p;
      
    }
    // 追加はここまで
    // +++++++++++++++++++++++++++++++++++++++++++++++++++
    
    // ...(略)
    
  }; // immutable_string_buffer
  
  // 実装用クラス
  class immutable_string_impl
  {
    // ...(略)
    
    // +++++++++++++++++++++++++++++++++++++++++++++++++++
    // 追加
    template<typename InIter1, typename InIter2>
    immutable_string_impl( std::size_t n1, InIter1 const& src1, std::size_t n2, InIter2 const& src2 )
      : p( buffer::create( n1, src1, n2, src2 ) ) {}
    // 追加はここまで
    // +++++++++++++++++++++++++++++++++++++++++++++++++++
    
    // ...(略)
   
  };  // immutable_string_impl
  
} // namespace gintenlib

やってることは見れば分かるでしょう。
二つの領域をマージしたバッファを作っています。 impl の方では、それを呼ぶコンストラクタを定義。以上。


後は、こいつを用いて演算子を多重定義します。
その際、新しいコンストラクタを作るのは少しばかり気が引けたので、裏口的なコンストラクタを用意してみました:

namespace gintenlib
{
  // 本体
  class immutable_string
    : boost::totally_ordered<immutable_string,
      boost::totally_ordered<immutable_string, std::string,
      boost::totally_ordered<immutable_string, char const*> > >
  {
    typedef immutable_string this_type;
    typedef immutable_string_impl impl_type;
    
   public:
    // 型定義(略)
    
    // 構築(略)
    
    // swap(略)
    
    // その他メンバ(略)
    
    // ストリーム出力(略)
    
    // +++++++++++++++++++++++++++++++++++++++++++++++++++
    // 文字列連結(追加)
    friend this_type operator+( this_type const& lhs, this_type const& rhs )
    {
      return this_type( impl_type( lhs.size(), lhs.begin(), rhs.size(), rhs.begin() ) );
    }
    friend this_type operator+( this_type const& lhs, std::string const& rhs )
    {
      return this_type( impl_type( lhs.size(), lhs.begin(), rhs.size(), rhs.begin() ) );
    }
    friend this_type operator+( this_type const& lhs, char const* rhs )
    {
      return this_type( impl_type( lhs.size(), lhs.begin(), std::strlen(rhs), rhs ) );
    }
    friend this_type operator+( std::string const& lhs, this_type const& rhs )
    {
      return this_type( impl_type( lhs.size(), lhs.begin(), rhs.size(), rhs.begin() ) );
    }
    friend this_type operator+( char const* lhs, this_type const& rhs )
    {
      return this_type( impl_type( std::strlen(lhs), lhs, rhs.size(), rhs.begin() ) );
    }
    
    template<typename T>
    this_type& operator+=( T const& rhs )
    {
      ( *this + rhs ).swap( *this );
      return *this;
    }
    // 追加はここまで
    // +++++++++++++++++++++++++++++++++++++++++++++++++++
    
    // 比較(略)
    
   private:
    impl_type impl;
    
    // 裏口(追加)
    immutable_string( const impl_type& impl_ )
      : impl( impl_ ) {}
    
  };  // immutable_string

} // namespace gintenlib

以上。これで殆ど完成です。完全なコードは http://ideone.com/zCqUHeDh をご覧下さい。


さて、本当はベンチマークをしようかと思ったのですが、それは面倒なので省略して、
このコードは十分に機能してますが、一点だけ、どうにも気に食わないことがあります。
それは、イテレータ対から生成する際に、いったん distance で距離を測ってからコピーしている点。
出来ることなら vector のように、一回の走査で初期化を行いたいところ。


それを行うには、可変領域の再確保という、少しばかり面倒な処理をしなければなりません。
しかし、面倒ですが、快適に使うためにはこの辺りの処理は必須とも言えます。
・・・というわけで、次回はその辺りをいじってみることにします。