vectorstreamの話

今日はinterprocessのstreamsの話。vector/bufferstreamと2つあるのですが、今回はvectorstreamだけ。

vectorの定義

interprocessの当該章では"vectorとは要素が連続しているコンテナである"ようです(意訳かも)。std::vectorは言わずもがなですが、std::basic_stringはそれを満たしていません(0xにおいては保障されます)が、boost.interprocessが提供するbasic_stringはこれを満たしている、と書かれていました。この記事において、特に指定しない限りvectorとはこの定義に従います。
ドキュメントではshared memoryなどを使っていましたが、特に使う必要がなかったので今回はstd::vectorやstd::stringを使いました。

vectorstream?

vectorstreamはvectorをバッファとして用いたiostreamベースの入出力クラスです。そのバッファをvectorメソッドで取得できます。例えば、ここにstringを渡しておけばstringstreamのようなものになるわけです。basic_vectorstream, basic_ivectorstream, basic_ovectorstreamの3つが利用できます。テンプレートの引数は使用するvectorとchar_traitsで、char_traitsはデフォルトでvector::value_typeを使用するようになっています。

template<class CharVector, class CharTraits = std::char_traits< typename CharVector::value_type >>
class basic_vectorstream  : public std::basic_iostream<typename CharVector::value_type, CharTraits>;

template<class CharVector, class CharTraits = std::char_traits< typename CharVector::value_type >>
class basic_ivectorstream : public std::basic_istream<typename CharVector::value_type, CharTraits>;

template<class CharVector, class CharTraits = std::char_traits< typename CharVector::value_type >>
class basic_ovectorstream : public std::basic_ostream<typename CharVector::value_type, CharTraits>;

CharVectorとして指定したクラスがメモリ確保なども行うのでアロケータを指定する必要はありません。STLコンテナを指定すれば実メモリ空間を使用、interprocessコンテナを使用すれば、shared memory, mapped files上の空間を使用する、と言ったことができますね。
使い方は簡単で作成方法は異なりますが、入出力方法はSTLのiostreamと同じです。以下C++0xを使ったコード。

#include <boost/interprocess/streams/vectorstream.hpp>
#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm/equal.hpp>
#include <boost/range/algorithm_ext/iota.hpp>>
#include <boost/range/istream_range.hpp>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>

void stream_test();

int main()
{
    try {
        stream_test();
    } catch( std::exception& e ) {
        std::cerr << e.what() << std::endl;
        return -1;
    }
}

void stream_test()
{
    namespace bip = boost::interprocess;
    {
        // 今回はstd::stringをvectorとして使用 (0xではstd::basic_stringは要素の連続性が保証)
        typedef bip::basic_vectorstream<std::string> myVectorStream;
        std::vector<int> src(10,0);
        boost::iota(src,0);

        // write
        // srcより大きいバッファを確保して出力
        myVectorStream vector_stream;
        vector_stream.reserve(src.size() * 5);
        boost::for_each(src,[&vector_stream](int i){ vector_stream << i << " "; });

        // vectorを取得して出力
        std::string const& i = vector_stream.vector();
        std::cout << i << std::endl;

        // read
        // 各要素をintとしてvectorにコピー
        std::vector<int> dest;
        boost::copy(boost::istream_range<int>(vector_stream),std::back_inserter(dest));
        BOOST_ASSERT(boost::equal(src,dest));

        // 他のvectorとのswap
        std::string other_vector("100 101 102 103 104");
        vector_stream.swap_vector(other_vector);
        std::cout << vector_stream.vector() << std::endl;
        std::cout << other_vector << std::endl;
    }
}
<出力結果>
    0 1 2 3 4 5 6 7 8 9
    100 101 102 103 104
    0 1 2 3 4 5 6 7 8 9

vectorをバッファとして使用すること以外は通常のiostreamと同じですね。interprocessは共有メモリの強力なラッパとして作られていますから、データの連続性を持ったコンテナを使用しての入出力は結構重宝しそうです。ただ、固定長バッファを書き込む、ということが分かっているときにvectorstreamはちょっと不向きだそうで、その場合はbufferstreamを使用するそうですが、その記事はまた次回。