この猫の皮を剥ぐ方法はいくつかあります:
- あなたはおそらくバインド式の実行を延期することを考えました:
phx::bind
それはできます
- または、そのために属性の伝播を使用することもできます(セマンティックアクションをまったく使用しません) 。
- 最後に、継承された属性を使用できます(たとえば、DataContext に既定のコンストラクターがない場合や、コピーにコストがかかる場合) 。
1.フェニックスでバインドを延期する
この目的のために phoenix バインドを使用します。これにより、セマンティック アクションがトリガーされた時点で「遅延実行」される Phoenix アクターが生成されます。
以下は、あなたが探していたかもしれない不足しているコード サンプルの再構築です。
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx= boost::phoenix;
struct DataContext
{
double xy;
};
template <typename Iterator, typename Skipper>
struct my_grammar : qi::grammar<Iterator, Skipper>
{
my_grammar(DataContext& dataContext) : my_grammar::base_type(start)
{
start = qi::double_
[ phx::bind(&my_grammar::newValueForXY,
phx::ref(dataContext),
qi::_1) ];
}
private:
static void newValueForXY(DataContext& dc, double value)
{
dc.xy = value;
}
qi::rule<Iterator, Skipper> start;
};
int main()
{
const std::string s = "3.14";
DataContext ctx;
my_grammar<decltype(begin(s)), qi::space_type> p(ctx);
auto f(begin(s)), l(end(s));
if (qi::phrase_parse(f, l, p, qi::space))
std::cout << "Success: " << ctx.xy << "\n";
}
ノート:
- phx::ref() は、データコンテキストへの参照をラップします
- プレースホルダーとして boost::_1 の代わりに qi::_1
この実装を考えるnewValueForXY
と、簡単に書くことができます
start = qi::double_
[ phx::bind(&DataContext::xy, phx::ref(dataContext)) = qi::_1 ];
2. セマンティック アクションの代わりに属性文法を使用する
ただし、セマンティック アクションの代わりに属性を使用して、おそらく同じ例を記述します (基本的に属性が目的であるため)。
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx= boost::phoenix;
struct DataContext {
double xy;
};
BOOST_FUSION_ADAPT_STRUCT(DataContext, (double, xy))
template <typename Iterator, typename Skipper>
struct my_grammar : qi::grammar<Iterator, DataContext(), Skipper>
{
my_grammar() : my_grammar::base_type(start) {
start = qi::double_;
}
private:
qi::rule<Iterator, DataContext(), Skipper> start;
};
int main()
{
const std::string s = "3.14";
static const my_grammar<decltype(begin(s)), qi::space_type> p;
DataContext ctx;
if (qi::phrase_parse(begin(s), end(s), p, qi::space, ctx))
std::cout << "Success: " << ctx.xy << "\n";
}
3. 継承された属性を使用してコンテキスト参照を渡す
絶対に主張する場合inherited attributes
は、目的に使用することもできます:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
namespace phx= boost::phoenix;
struct DataContext {
double xy;
};
template <typename Iterator, typename Skipper>
struct my_grammar : qi::grammar<Iterator, void(DataContext&), Skipper> {
my_grammar() : my_grammar::base_type(start)
{
start = qi::double_ [ phx::bind(&DataContext::xy, qi::_r1) = qi::_1 ];
}
qi::rule<Iterator, void(DataContext&), Skipper> start;
};
int main() {
const std::string s = "3.14";
const static my_grammar<std::string::const_iterator, qi::space_type> p;
DataContext ctx;
if(qi::phrase_parse(begin(s), end(s), p(phx::ref(ctx)), qi::space)) {
std::cout << "Success: " << ctx.xy << "\n";
}
}
これは、呼び出しサイトでいくらか表現力があります。
qi::phrase_parse(begin(s), end(s), p(phx::ref(ctx)), qi::space));
コンテキストがデフォルトで構築可能である必要はありません。