3

扩展这个较早的帖子,我想我会尝试捕获 astd::vector<boost::variant<double,std::string>>而不是 just boost::variant<double,std::string>,而是首先从相同的旧输入开始。

这是给定输入 'foo' 和 42.7 的输出:

/tmp$ g++ -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox 
<m_rule>
  <try>foo</try>
  <success></success>
  <attributes>[[f, o, o]]</attributes>
</m_rule>
<m_rule>
  <try>42.7</try>
  <success></success>
  <attributes>[42.7]</attributes>
</m_rule>
std::string='foo'
double='42.7'
/tmp$ g++ -g -std=c++11 -DDO_VECTOR sandbox.cpp -o sandbox && ./sandbox 
<m_rule>
  <try>foo</try>
  <success></success>
  <attributes>[[102, 111, 111]]</attributes>
</m_rule>
<m_rule>
  <try>42.7</try>
  <success></success>
  <attributes>[[42.7]]</attributes>
</m_rule>
double='111'
double='42.7'
/tmp$

出于某种我不明白的原因,解析器似乎正在为 'foo' 生成 ASCII 值并引起一些混乱。

打开 DO_VECTOR 时是否需要更改解析器?

我应该使用不同的容器吗?

代码

#define BOOST_SPIRIT_DEBUG

#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>

// #define DO_VECTOR

namespace {
namespace                              qi = boost::spirit::qi;
typedef   std::string::const_iterator  Iterator;

#ifdef DO_VECTOR
typedef std::vector<boost::variant<double, std::string>>  MY_TYPE;
#else
typedef boost::variant<double, std::string>               MY_TYPE;
#endif

class my_visitor
    : public boost::static_visitor<>
{
    public:
    my_visitor( std::string& result ) : m_str( result ) { }
    void operator()( double& operand )
    {
        std::ostringstream oss;
        oss << "double='" << operand << "'";
        m_str = oss.str();
    }
    void operator()( std::string& operand )
    {
        m_str = "std::string='";
        m_str.append( operand );
        m_str.append( "'" );
    }
    std::string& m_str;
};

// -----------------------------------------------------------------------------

struct variant_grammar : 
    qi::grammar<Iterator, MY_TYPE()>
{
    qi::rule<Iterator, MY_TYPE()> m_rule;

    variant_grammar() : variant_grammar::base_type(m_rule)
    {
        m_rule %= (qi::double_ | +qi::char_);

        BOOST_SPIRIT_DEBUG_NODE( m_rule );
    }
};

}

// -----------------------------------------------------------------------------

int main()
{
    const std::string    a( "foo" ), b( "42.7" );
    variant_grammar      varGrammar;

    MY_TYPE varA, varB;
    auto begA = a.begin(), endA = a.end();
    auto begB = b.begin(), endB = b.end();
    qi::parse( begA, endA, varGrammar, varA );
    qi::parse( begB, endB, varGrammar, varB );

    if ( begA!=endA )
        std::cerr << "A FAILED TO COMPLETELY PARSE" << std::endl;
    if ( begB!=endB )
        std::cerr << "B FAILED TO COMPLETELY PARSE" << std::endl;

    std::string resultA, resultB;
    my_visitor visitor1( resultA );
    my_visitor visitor2( resultB );

#ifdef DO_VECTOR
    std::for_each( varA.begin(), varA.end(), boost::apply_visitor( visitor1 ));
    std::for_each( varB.begin(), varB.end(), boost::apply_visitor( visitor2 ));
#else
    boost::apply_visitor( visitor1, varA );
    boost::apply_visitor( visitor2, varB );
#endif

    std::cout << resultA << std::endl;
    std::cout << resultB << std::endl;

    return 0;
}
4

2 回答 2

3

我想这会解决它:

struct variant_grammar : qi::grammar<Iterator, MY_TYPE()> {
    qi::rule<Iterator, MY_TYPE()> m_rule;
    qi::rule<Iterator, std::string()> m_string;

    variant_grammar() : variant_grammar::base_type(m_rule) {
        m_rule %= qi::double_ | m_string;
        m_string %= +qi::char_;

        BOOST_SPIRIT_DEBUG_NODE( m_rule );
        BOOST_SPIRIT_DEBUG_NODE( m_string );
    }
};
于 2012-11-25T19:46:56.383 回答
3

另一种解决方案是使用qi::as_string[].

struct variant_grammar : 
    qi::grammar<Iterator, MY_TYPE()>
{
    qi::rule<Iterator, MY_TYPE()> m_rule;

    variant_grammar() : variant_grammar::base_type(m_rule)
    {
        m_rule %= (qi::double_ | qi::as_string[+qi::char_]);

        BOOST_SPIRIT_DEBUG_NODE( m_rule );
    }
};

让我们暂时忘记双倍。你的规则有一个std::vector<std::string>简化的属性vector<vector<char>>,你+qi::char_有一个属性vector<char>。你想要的 "foo" 是 a vector1<vector3<char>>,你得到的是 a vector3<vector1<char>>。上面的链接对此进行了解释:当您遇到这种情况时,+qi::char调用traits::push_back_container它解析的每个字符。您可以使用辅助规则(如 sharth 的回答)来消除歧义,也可以使用原子解析指令之一(在本例中为 qi::as_string[])。

编辑:

这是解决您的新问题的代码:

#define BOOST_SPIRIT_DEBUG

#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>

// #define DO_VECTOR

namespace {
namespace                              qi = boost::spirit::qi;
typedef   std::string::const_iterator  Iterator;

#ifdef DO_VECTOR
typedef std::vector<boost::variant<double, std::string>>  MY_TYPE;
#else
typedef boost::variant<double, std::string>               MY_TYPE;
#endif

class my_visitor
    : public boost::static_visitor<>
{
    public:
    my_visitor( std::string& result ) : m_str( result ) { }
    void operator()( double& operand )
    {
        std::ostringstream oss;
        oss << "double='" << operand << "'";
        m_str += oss.str();
    }
    void operator()( std::string& operand )
    {
        m_str += "std::string='";
        m_str.append( operand );
        m_str.append( "'" );
    }
    std::string& m_str;
};

// -----------------------------------------------------------------------------

struct variant_grammar : 
    qi::grammar<Iterator, MY_TYPE(), qi::space_type> //added a skipper to the grammar
{
    qi::rule<Iterator, MY_TYPE(), qi::space_type> m_rule; //and to the rule. It's specially important that the starting rule and your grammar have the exact same template parameters

    variant_grammar() : variant_grammar::base_type(m_rule)
    {
        m_rule %= +(qi::double_ | qi::as_string[+(qi::char_-qi::digit)]);//Limited the string parser and added a `+` in order to parse more than one element

        BOOST_SPIRIT_DEBUG_NODE( m_rule );
    }
};

}

// -----------------------------------------------------------------------------

int main()
{
    const std::string    a( "foo 4.9 bar" ), b( "42.7" );
    variant_grammar      varGrammar;

    MY_TYPE varA, varB;
    auto begA = a.begin(), endA = a.end();
    auto begB = b.begin(), endB = b.end();
    qi::phrase_parse( begA, endA, varGrammar, qi::space, varA ); //when you have a skipper in your rule/grammar you need to use phrase_parse
    qi::phrase_parse( begB, endB, varGrammar, qi::space, varB );

    if ( begA!=endA )
        std::cerr << "A FAILED TO COMPLETELY PARSE" << std::endl;
    if ( begB!=endB )
        std::cerr << "B FAILED TO COMPLETELY PARSE" << std::endl;

    std::string resultA, resultB;
    my_visitor visitor1( resultA );
    my_visitor visitor2( resultB );

#ifdef DO_VECTOR
    std::for_each( varA.begin(), varA.end(), boost::apply_visitor( visitor1 ));
    std::for_each( varB.begin(), varB.end(), boost::apply_visitor( visitor2 ));
#else
    boost::apply_visitor( visitor1, varA );
    boost::apply_visitor( visitor2, varB );
#endif

    std::cout << resultA << std::endl;
    std::cout << resultB << std::endl;

    return 0;
}

几个小的变化:在语法中添加了一个skipper,因此使用了phrase_parse而不是parse。限制字符串解析器。将打印机更改为附加到您的字符串而不是覆盖它。

于 2012-11-25T20:39:53.840 回答