本文介绍了是否可以将动作附加到boost :: spirit :: rule解析器,该解析器将解析的结果分配给(尚未)未知实例的成员?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试从boost :: spirit规则定义的操作中引用一个(尚未)未知实例的成员,所以在伪代码中,

I'm trying to reference a member of a (yet) unknown instance from within a boost::spirit rule definitions' action, so in pseudocode,

而不是 double_ [ref(rN)= _1]我正在寻找类似的东西 X ** ppx; double_ [ref(& X :: rN,ppx)= _1]

instead of double_[ref(rN) = _1]I'm looking for something like X** ppx; double_[ref(&X::rN, ppx) = _1]

一种解决方法可能是一个简单的语义动作",其中包含一个知道实例并能够写入实例的参数,例如

A workaround for it could be a simple "semantic action" with a parameter which knows the instance and would be able to write to it, like

qi::rule<Iterator, Skipper> start;
my_grammar(DataContext*& dataContext) : my_grammar::base_type(start) , execContext(execContext) {
    start = qi::double_[ boost::bind(&my_grammar::newValueForXY, dataContext, ::_1) ];

但是,我想知道是否有可能直接绑定"到成员变量,就像可以通过使用"phoenix :: ref(...)= value"绑定到局部"变量一样.

However, I'm wondering if there is a possibility to "bind" directly to the member variable like it's possible to bind to a "local" variable by using "phoenix::ref(...) = value".

我尝试了以下语法:

start = qi::int_[ boost::bind<int&>(&DataContext::newValueForXY, boost::ref(dataContext))() = ::_1] ];

但由于VS2010SP1和错误消息而失败

but failed with VS2010SP1 and the error message

错误C2440:'=':'boost :: arg'无法转换为...

error C2440: '=': 'boost::arg' cannot be converted into ...

推荐答案

有几种方法可以给这只猫换皮:

There are several ways to skin this cat:

  1. 您可能想到了延迟 bind表达式的执行:phx::bind可以做到
  2. 或者,您可以仅使用属性传播(并且完全不使用语义动作)
  3. 最后,您可以使用继承的属性(例如,当DataContext没有默认构造函数或复制昂贵时)
  1. you probably thought of deferring execution of the bind expression: phx::bind can do that
  2. alternatively, you could just use attribute propagation for that (and do without semantic actions altogether)
  3. lastly, you could use inherited attributes (e.g. when the DataContext has no default constructor or copying is expensive)

1.延迟与Phoenix的绑定

为此目的使用凤凰绑定:这将导致Phoenix演员在触发语义动作时被推迟执行".

1. Deferring the Bind with Phoenix

Use phoenix bind for the purpose: this will result in a Phoenix actor, that will be "deferred executed" at the time the semantic action is triggered.

这是您可能想要的缺少代码示例的重构:

Here's a reconstruction of the missing code sample you might have been after:

#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()将引用包装到数据上下文
  • qi :: _ 1而不是boost :: _ 1作为占位符
  • 使用newValueForXY的这种实现,您可以很容易地编写

  • phx::ref() to wrap the reference to the datacontext
  • qi::_1 instead of boost::_1 as a placeholder
  • given this implementation of newValueForXY you could just as easily have written

    start = qi::double_
        [ phx::bind(&DataContext::xy, phx::ref(dataContext)) = qi::_1 ];

但是,我可能会使用属性而不是语义动作来编写相同的示例(因为这基本上就是它们的用途):

However, I'd probably write the same example using attributes instead of semantic actions (because that's basically what they are for):

#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 :

3. Use inherited attributes to pass in the context reference

If you absolutely insist, you can even use inherited attributes for the purpose:

#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";
    }
}

这在呼叫站点上更具表现力:

This is somewhat more expressive at the call site:

qi::phrase_parse(begin(s), end(s), p(phx::ref(ctx)), qi::space));

,并且不需要上下文是默认可构造的.

and doesn't require the context to be default constructible.

这篇关于是否可以将动作附加到boost :: spirit :: rule解析器,该解析器将解析的结果分配给(尚未)未知实例的成员?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 07:26