The problem you are facing is that the attribute of your combined rule is basically:
tuple< tuple<size_t,bool,size_t>, std::string, std::string >
and by placing the variables one at a time in the parse_phrase call, you basically:
tuple< size_t, bool, size_t, std::string, std::string >
Due to how attribute propagation works in spirit, this is what happens:
the integer tuple<size_t,bool,size_t> assigned to your num1 (ignoring bool and second size_t), after which the spirit tries to assign the first row to your bool, as a result you get.
I believe that the cleanest way to solve this problem is to create a custom structure to store your result, which reflects the structure of your rules.
#include <iostream> #include <string> #include <vector> #include <iterator> #include <boost/spirit/include/qi.hpp> #include <boost/fusion/include/adapt_struct.hpp> struct optional_command_line_options { int num1; bool bool1; int num2; }; struct command_line_options { optional_command_line_options opt; std::string str1; std::string str2; }; BOOST_FUSION_ADAPT_STRUCT( optional_command_line_options, (int, num1) (bool, bool1) (int, num2) ) BOOST_FUSION_ADAPT_STRUCT( command_line_options, (optional_command_line_options, opt) (std::string, str1) (std::string, str2) ) bool parse_line( const std::string&str ) { bool rc=false; namespace qi = boost::spirit::qi; using boost::spirit::ascii::space; using boost::spirit::ascii::char_; std::string::const_iterator iter( str.begin() ); command_line_options options; options.opt.num1=88; options.opt.bool1=false; options.opt.num2=88; qi::rule< std::string::const_iterator, std::string() > rstring=+~space; qi::rule<std::string::const_iterator, boost::spirit::ascii::space_type,optional_command_line_options() > trule; trule= ( qi::lit( "-p" ) >> qi::int_ ) ^ ( qi::lit( "-j" ) >> qi::attr(true) ) ^ ( qi::lit( "--jobs" ) >> qi::int_ ) ; qi::rule< std::string::const_iterator, boost::spirit::ascii::space_type, command_line_options() >arule; arule = -trule >> rstring >> rstring; bool result=qi::phrase_parse( iter,str.end(), arule, space, options ); if(result && iter==str.end()) { std::cout << "Parse successful." << std::endl; rc=true; } else { std::cerr<<"syntax error: "<<std::string(iter,str.end())<<"!\n\n"; } std::cout << std::boolalpha; std::cout << "num1:" << options.opt.num1 << std::endl; std::cout << "bool1:"<< options.opt.bool1 << std::endl; std::cout << "num2:" << options.opt.num2 << std::endl; std::cout << "str1:" << options.str1 << std::endl; std::cout << "str2:" << options.str2 << std::endl; return rc; } int main( int /*argc*/,char**/*argv*/ ) { std::vector< std::string > testData; testData.push_back( "-p 100 -j ifile ofile" ); testData.push_back( "-j -p 100 --jobs 16 ifile ofile" ); testData.push_back( "--jobs 16 -j -p 100 ifile ofile" ); testData.push_back( "--jobs 16 -p 100 ifile ofile" ); testData.push_back( "ifile ofile" ); for( std::vector< std::string >::const_iterator it=testData.begin(); it!=testData.end(); ++it ) { std::cout << "\nparsing string:" << *it << std::endl; parse_line( *it ); } return 0; }
Running on LWS .
PS: you cannot directly assign rules declared using auto that have literals embedded in them (for example, strings or numbers) without using boost :: proto :: deep_copy;
auto trule = boost::proto::deep_copy(qi::lit( "-p" ) >> qi::int_);
There is a macro BOOST_SPIRIT_AUTO that simplifies its use:
#define BOOST_SPIRIT_AUTO(domain_, name, expr) \ typedef boost::proto::result_of:: \ deep_copy<BOOST_TYPEOF(expr)>::type name##_expr_type; \ BOOST_SPIRIT_ASSERT_MATCH( \ boost::spirit::domain_::domain, name##_expr_type); \ BOOST_AUTO(name, boost::proto::deep_copy(expr)); BOOST_SPIRIT_AUTO(qi,trule,qi::lit( "-p" ) >> qi::int_);