Boost.Spirit.X3のご紹介 - C++ Advent Calendar 2014(16日目)
この記事はC++ Advent Calendar 2014 16日目の記事です.
おはようございます,@yutoppです.
去年のC++ Advent Calendar 2013ではBoost.Spirit.QiのTipsを書きました.今年は,Boost.Spirit.X3の紹介をしたいと思います!
環境は,Antergos Linux(x64) / Clang 3.5.0 / Boost 1.56.0 です.
また,Boost.Spirit.X3はexperimentalとのことなので,この記事の内容はすぐに古くなる可能性があります.
Boost.Spirit.X3とは?
C++11やC++14の機能を用いて再設計されたBoost.Spirit.Qi
の次のexperimentalな実装です.
Classic Spiritのようなシンプルさを取り戻すことを目標にしているとのことです.
spirit/include/boost/spirit/home/x3
がお目当てのX3の内容になります.
X3を使ったコードには以下のものがありました.
X3の利点
コンパイルが速い
試しにサンプルをビルドして計測してみました.
Qi Calc1 /
Qi Calc2 /
Qi Calc4
X3 Calc1 /
X3 Calc2 /
X3 Calc4
以下の表は,
clang++ -ftime-report -std=c++14 -I /usr/include/boost/ -c 各サンプル名
を10回繰り返し計測したClang front-end timer
のUser Time
の値(sec)の平均です.
calc1 | calc2 | calc4 | |
---|---|---|---|
Qi | 4.283 | 4.409 | 4.96335 |
X3 | 1.87333 | 1.89767 | 2.34934 |
大体今までの2倍の速さでコンパイル出来るようになっているようです.体感でわかる程度に速いので良いですね.
エラーがわかりやすい(Qi比)
内部実装がシンプルになったことによって,エラーも分かりやすくなった気がします.
宣言と定義を同時に書けるようになる(?)
X3からruleの内容を先に定義してから,rule自体を定義するようなスタイルになったので,謎マクロを噛ますことで同時に宣言できます(後述します).
C++14を使いまくれる
ライブラリ自体が最低でもC++14を要求するので,このライブラリをincludeした時点でC++14の機能を使わないともったいないという気持ちになれます.
X3の現状の問題点
undocumented
ドキュメントが無いのでつらいです…が,X3の実装がとても読みやすいので,コードを読みながら何とか使えます.
特に変わったのはruleの定義とセマンティックアクションの記述,attr
とval
の扱いかな,と思います.
定義済みパーサが少ない
仕方がないので自分で書きます.
Tips
X3では,以下のようにruleを定義します.(サンプルより抜粋)
x3::rule<class expression, ast::program> const expression("expression"); auto const expression_def = term >> *( ('+' >> term) | ('-' >> term) ) ; BOOST_SPIRIT_DEFINE( expression = expression_def );
しかし,個人的にはruleとdefを同時に書きたいわけです.
そこで,マクロを噛まします.
実際のコード
#define RULES_BEGIN( struct_name, entrypoint_name ) \ struct struct_name \ { \ using self_type = struct_name; \ \ static auto instance() \ -> struct_name const& \ { \ static struct_name self; \ return self; \ } \ \ static auto entrypoint() \ { \ return instance().entrypoint_name; \ } \ #define RULES_END \ }; #define ANNOTATOR_COL() : #define ANNOTAROR_BASE_SPEC( r, _unused, index, elem ) \ BOOST_PP_IIF( BOOST_PP_EQUAL(index, 0), \ ANNOTATOR_COL, \ BOOST_PP_COMMA \ )() public elem #define RULE( name, type, ... ) \ RULE_WITH_ANNOTATOR( name, type, BOOST_PP_SEQ_NIL, __VA_ARGS__ ) #define RULE_WITH_ANNOTATOR( name, type, bases, ... ) \ class name \ BOOST_PP_SEQ_FOR_EACH_I( ANNOTAROR_BASE_SPEC, _, bases ) \ {}; \ struct PP_ ## name { \ using rule_type = x3::rule<name, type>; \ static auto def( self_type const& t ) { \ return __VA_ARGS__; \ } \ auto operator()() const -> rule_type \ { \ return rule_type( #name ); \ } \ }; \ decltype(( std::declval<PP_ ## name>()) ()) name \ = PP_ ## name()(); \ template <typename Iterator, typename Context, typename Attribute> \ friend inline bool parse_rule( \ decltype(( std::declval<PP_ ## name>()) ()) rule_, \ Iterator& first, Iterator const& last, \ Context const& context, Attribute& attr \ ) \ { \ using boost::spirit::x3::unused; \ auto const& ri = self_type::instance(); \ auto const& raw_def = PP_ ## name :: def( ri ); \ auto const& def_ = ( ri.name = raw_def ); \ return def_.parse(first, last, context, unused, attr); \ }
classの中であれば前方参照できるので,ruleは全体的にclassの中で定義するようにします.
ruleの中身はstruct PP_ ## name
のdef
で定義します.引数のt
がキモで,ruleを定義する際は,t
のメンバ変数として参照するようにします.
また,parse_rule
が呼べればX3から使えるようなので,BOOST_SPIRIT_DEFINE
の定義から抜き出してfriendとして定義しておきます.
実際の使い方はこのような感じになります.
実際のコード
RULES_BEGIN( rules, program ) RULE_WITH_ANNOTATOR( program, ast::module_ptr, ( on_error_annotator_base ), t.module > ( x3::eol | x3::eoi ) ) RULES_END
RULES_BEGIN
では,rules
というクラスを定義して,program
から始まるrule群を定義しています.
RULE_WITH_ANNOTATOR
では,on_error_annotator_base
に従ってアノテーションを行い,ast::module_ptr
型の属性を持つ,program
というruleを定義しています.
t.
でmodule
というrule(ここでは定義していませんが…)を参照しています.残りはQiのときと同じですね.
あとはこのように使うだけです.
このマクロを用いると,ruleの定義を1箇所で行えるようになります.X3自体の効果でコンパイル時間も短くなりますし,良さがありますね.
実際に使ってみた
去年の記事にも書いた文鳥言語で,実際にX3を使ってみました.
(文鳥言語はC++14とBoost.Spirit.X3,LLVMを用いて,C++とDlangライクな言語を目指して開発している言語です)
Boost.Spirit.X3を使ってみて大きいのは,コンパイル時間が短くなったこと,それとコンパイル時間が短くなったこと,後はコンパイル時間が短くなったことが挙げられます.
エラーメッセージが分かりやすくなったこと,マクロで定義を1箇所に書けるようになったのも大きいかもしれません.
C++14のgeneric lambdaと組み合わせると,セマンティックアクションがとても書きやすいのも魅力でした.
Spirit.Qiでコンパイル時間がBoostするのがつらい皆様,ぜひX3を使ってみませんか.
そしてついでに文鳥言語も開発しませんか?とてもアットホームな鳥小屋です.
おわり
experimentalだけど現段階でも十分魅力的なBoost.Spirit.X3の紹介でした.
後日もう少し書き足します…