2010-07-09
■[C++] qi::auto_で簡易パース
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<T>にパースしてみましょう。
#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を特殊化し、型のパース方法を定義する
この方法は、何度もパースが必要になるような場合や、ライブラリとして提供する場合に必要になるでしょう。
参照:
トラックバック - http://d.hatena.ne.jp/faith_and_brave/20100709/1278651653
リンク元
- 17 http://anond.hatelabo.jp/keyword/C++0x
- 14 http://twitter.com/
- 13 http://www.google.co.jp/search?sourceid=navclient&hl=ja&ie=UTF-8&rlz=1T4ADFA_jaJP384JP385&q=c+++メンバ 配列+初期化
- 11 http://www.google.co.jp/search?q=faith+and+brave&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a
- 10 http://anond.hatelabo.jp/20100222033058
- 9 http://d.hatena.ne.jp/
- 9 http://www.google.co.jp/search?hl=ja&lr=lang_ja&tbs=lr:lang_1ja&q=asp.net+C#+web+messagebox&aq=f&aqi=&aql=&oq=&gs_rfai=
- 8 http://www.google.co.jp/hws/search?hl=ja&client=fenrir-sub&channel=selection&adsafe=off&safe=off&lr=lang_ja&q=ユーザー定義リテラル
- 8 http://www.google.co.jp/search?sourceid=chrome&ie=UTF-8&q=C+++魔道書
- 8 http://www.google.com/search?hl=ja&lr=lang_ja&ie=UTF-8&oe=UTF-8&q=C+++move&num=100