Boost.Spiritというアレなもの使ってみたのですが、1つ気になったことがあります。int_pとかch_pに対するセマンティックアクションはintやcharを受け渡しするように、自作のパーサからも好きなデータを渡せないのかということです。試行錯誤してGrammar<>派生の自作クラスでできるようになりましたが、これよりもっとスマートな方法があるのでは、という思いがまだ頭から離れません(実際、これ書き始めてからもっとスマートな方法を思い付きました)。

というわけでサンプル2つです。「これよりもっとスマートな方法があるのでは」と言っていた頃のものがspirit1.cppで、その後に思いついたのがsprit2.cppです。自分もやりたいというだけならspirit2.cppだけ見れば十分です。ろくな解説ではありませんが、ソースコードから適当に汲み取ってください。

pair_grammerが今回の対象で、これにセマンティックアクションを使うとstd::pair<std::string, std::string>が渡されます。dictionary_grammarがそれを使っている側です。insert_mapがセマンティックアクションでstd::pair<>を受け取っています。定義済みのアクションにinsert_aがありますが、分かりやすく示すため、使っていません。


セマンティックアクションがどのような引数で呼ばれるかが決まるまでの過程を追った結果、次のことがわかりました。まず、grammarのテンプレート引数は、派生クラスを表す1つ目のほかに、2つ目があります。これはcontext<nil>という既定引数があるので省略可能だったのです。これがnilだとセマンティックアクションを呼ぶときにイテレータのペアが引数になります。そして、nilでなければ、その型のインスタンスがセマンティックアクションの引数になります。つまり、例えばstruct g : grammar<g>としていたところをstruct g : grammar<g, context<T> >とすれば良さそうです。しかし、これではまだいけません。

さて、セマンティックアクションで渡すデータ型を決めたところで、どうやってデータを渡すのかという壁が立ちはだかります。Spiritの文書を眺めていると、パーサコンテキストに気が付きました。解析前と後にコールバックを受け取れるようです。そして、解析後に呼ばれるときの引数で、セマンティックアクションへ渡すデータを与えられるようです。そのためのクラスparser_transfer_contextを作ってできたのがspirit1.cppです。

しかし、そももそもparse関数を上書き(オーバーライド)すればと思い付いたのがspirit2.cppです。だいぶすっきりしました。


さて、公式の文書でこのようなことが全く触れられていないのはどうしてでしょう。作るのが多少面倒かもしれないですが、例に使ったpairや日付・時刻などほどほどに使う分には良さそうに思えるんですけどね。


2012年3月20日追記:この記述は現在のBoost.Spiritには当てはまりません。この記事の内容は、現在のBoost.Spirit.Classicに対応するものです。ついでに言うと、現在のBoost.Spirit (Qi)では、こういうことが当たり前のようにできます。


スポンサード リンク

この記事のカテゴリ

  • ⇒ セマンティックアクションで好きなオブジェクトを渡せるようにする