qi::auto_は、各データごとに特殊化された方法で構文解析するパーサーです。
たとえば、intは以下のようにしてパースできます。
#include <boost/assert.hpp> #include <boost/spirit/include/qi.hpp> namespace qi = boost::spirit::qi; template <class Rule, class Result> bool parse(const std::string& s, const Rule& rule, Result& result) { std::string::const_iterator first = s.begin(); return qi::parse(first, s.end(), rule, result); } int main() { int n; parse("123", qi::auto_, n); BOOST_ASSERT(n == 123); }
ただ、これだけであればlexical_castとかを使えばいいので特にありがたみは感じられません。
qi::auto_は主に、これらの基本データ型のパースを基礎として、複合データ型やデータ構造をパースするのに使用します。
std::vector
#include <iostream> #include <vector> #include <boost/spirit/include/qi.hpp> #include <pstade/oven/identities.hpp> #include <pstade/oven/io.hpp> namespace qi = boost::spirit::qi; namespace oven = pstade::oven; template <class Rule, class Result> bool parse(const std::string& s, const Rule& rule, Result& result) { std::string::const_iterator first = s.begin(); return qi::parse(first, s.end(), rule, result); } int main() { { std::vector<int> v; parse("1 2 3", qi::auto_ % ' ', v); std::cout << (v | oven::identities) << std::endl; } { std::vector<int> v; parse("1,2,3", qi::auto_ % ',', v); std::cout << (v | oven::identities) << std::endl; } { std::vector<double> v; parse("1.2 2.3 3.4", qi::auto_ % ' ', v); std::cout << (v | oven::identities) << std::endl; } }
{1,2,3} {1,2,3} {1.2,2.3,3.4}
区切り文字を指定するだけで、内部に入ってる要素を自動的に推論し、簡単にパースできています。
同じ方法で、std::listにもパースすることができます。
次に、ユーザー定義の型をパースしてみます。
ちょっとめんどくさいです。
#include <iostream> #include <boost/spirit/include/qi.hpp> #include <boost/fusion/include/struct.hpp> namespace qi = boost::spirit::qi; struct person { int id; std::string name; int age; }; BOOST_FUSION_ADAPT_STRUCT( person, (int, id) (std::string, name) (int, age) ) namespace boost { namespace spirit { namespace traits { template <> struct create_parser<person> { typedef proto::result_of::deep_copy< BOOST_TYPEOF(qi::int_ >> ',' >> *(qi::char_ - ',') >> ',' >> qi::int_) >::type type; static type call() { return proto::deep_copy(qi::int_ >> ',' >> *(qi::char_ - ',') >> ',' >> qi::int_); } }; }}} template <class Rule, class Result> bool parse(const std::string& s, const Rule& rule, Result& result) { std::string::const_iterator first = s.begin(); return qi::parse(first, s.end(), rule, result); } int main() { person p; parse("123,Akira,25", qi::auto_, p); std::cout << p.id << std::endl; std::cout << p.name << std::endl; std::cout << p.age << std::endl; }
123 Akira 25
ここでは、以下のことを行っています。
1. ユーザー定義型の定義
2. Qiのoperator>>()でパースするために型をFusionのシーケンスにアダプトする
3. boost::spirit::traits::create_parserを特殊化し、型のパース方法を定義する
この方法は、何度もパースが必要になるような場合や、ライブラリとして提供する場合に必要になるでしょう。
参照:
boost::qi::auto_