Expanding on this earlier post, I thought I would try to capture a std::vector<boost::variant<double,std::string>> instead of just boost::variant<double,std::string>, but starting with the same-old-inputs first.
Here is my output given inputs ‘foo’ and 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$
For some reason that I don’t understand, the parser seems to be generating ASCII values for ‘foo’ and causing some confusion.
Do I need to change the parser when I turn on DO_VECTOR?
Should I be using a different container?
CODE
#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;
}
Another solution is using
qi::as_string[].Let’s forget for a moment about the double. Your rule has an attribute of
std::vector<std::string>that simplified isvector<vector<char>>and your+qi::char_has an attributevector<char>. What you want for “foo” is avector1<vector3<char>>and what you get is avector3<vector1<char>>. This is explained in the link above: when you have a situation like this,+qi::charcallstraits::push_back_containerfor 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:
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.