前回 の続き。
作った 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 のように、一回の走査で初期化を行いたいところ。
それを行うには、可変領域の再確保という、少しばかり面倒な処理をしなければなりません。
しかし、面倒ですが、快適に使うためにはこの辺りの処理は必須とも言えます。
・・・というわけで、次回はその辺りをいじってみることにします。