Boost.Spirit そのに from stream

http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit/qi/tutorials.html は Boost.Spirit.Qi を使って parser を書く上ですばらしい資料なんだけど入力が std::string を想定してるようであんまり広がらない。

というわけで stream から流し込める方法ないのかなーと思って探した結果 2 つ方法があった。

  1. boost::spirit::qi::match()
  2. boost::spirit::istream_iterator + boost::spirit::qi::parse()

まず boost::spirit::qi::match() 関数を使う方から。 http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit/qi/reference/parse_api/stream_api.html に書いてあるんだけどそのまま stream に流し込める関数みたい。

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_match.hpp>

int main(void) {
    namespace qi = boost::spirit::qi;

    std::cin.unsetf(std::ios_base::skipws);

    int n;
    std::cin >> qi::match('"' >> qi::int_ >> '"', n);

    std::cout << n << std::endl;

    return 0;
}

で、もうひとつが boost::spirit::istream_iterator なんだけど名前の通り istream を wrap して iterator として扱えるようにするための class 。 http://boost-spirit.com/home/2010/01/05/stream-based-parsing-made-easy/ に解説が載ってる。

#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/support_istream_iterator.hpp>

int main(void) {
    namespace spirit = boost::spirit;
    namespace qi = boost::spirit::qi;

    std::cin.unsetf(std::ios_base::skipws);

    spirit::istream_iterator begin(std::cin);
    spirit::istream_iterator end;

    int n;
    qi::parse(begin, end, '"' >> qi::int_ >> '"', n);

    std::cout << n << std::endl;

    return 0;
}

で、どっちがいいの ? ていう疑問が出てくると思うんだけど

  • boost::spirit::qi::match() は stream に対してしか使えないけど手軽
  • boost::spirit::istream_iterator + boost::spirit::qi::parse() はちょっと code が長くなるけど iterator という間口の広い interface が使える

というあたりを考慮して使いまわすつもりがあるなら parse() を使った code を書いてしまって stream から流しこむ必要があるときに別途 istream_iterator で wrap する、という方向。たとえば http://www.boost.org/doc/libs/1_44_0/libs/spirit/doc/html/spirit/qi/tutorials/warming_up.html の下の方の parse_numbers() 関数は iterator を引数にとるので std::string だろうが istream_iterator で wrap した stream だろうが問題ないということね。

それ以外の場合、書き捨てるとか stream からしか流し込まないことがわかってるとかは match() を使えばいいんじゃないかな。