3

この以前の投稿を拡張して、私std::vector<boost::variant<double,std::string>>は単にではなくキャプチャしようと思いました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

Another solution is using 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 );
    }
};

Let's forget for a moment about the double. Your rule has an attribute of std::vector<std::string> that simplified is vector<vector<char>> and your +qi::char_ has an attribute vector<char>. What you want for "foo" is a vector1<vector3<char>> and what you get is a vector3<vector1<char>>. This is explained in the link above: when you have a situation like this, +qi::char calls traits::push_back_container for every char it parses. You can either use an auxiliary rule, as in sharth's answer, to disambiguate the situation or you can use one of the atomic parsing directives (qi::as_string[] in this case).

Edit:

Here is the code that solves your new problem:

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

Several small changes: added a skipper to the grammar and used phrase_parse instead of parse consequently. Limited the string parser. Changed the printers to append to your string not overwrite it.

于 2012-11-25T20:39:53.840 に答える