почему std :: vector из boost :: variable вызывает проблемы с разбором в boost

Расширяя это ранее сообщение, Я думал, что я попытаюсь поймать 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$

По некоторым причинам, которые я не понимаю, парсер генерирует значения ASCII для ‘foo’ и вызывает некоторую путаницу.

Нужно ли менять парсер при включении 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;
}

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» является vector1<vector3<char>> и что вы получаете это vector3<vector1<char>>, Это объясняется в ссылке выше: когда у вас такая ситуация, +qi::char звонки traits::push_back_container для каждого символа он анализирует. Вы можете использовать вспомогательное правило, как в ответе Шарта, для устранения неоднозначности ситуации, или вы можете использовать одну из директив атомарного анализа (в этом случае 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;
}

Несколько небольших изменений: добавлен шкипер в грамматику и, следовательно, вместо фразы parse используется фраза_парс. Ограничен синтаксический анализатор строк. Изменены принтеры, чтобы они добавлялись к вашей строке, а не перезаписывали ее.

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 );
}
};
3

По вопросам рекламы [email protected]