parsing command-line options with sequence containers?

后端 未结 1 502
终归单人心
终归单人心 2021-01-19 06:32

This question has come up before, but it seems that none of the answers provide alternatives with boost-style generic programming.

Like many I use boost

1条回答
  •  一个人的身影
    2021-01-19 06:38

    Actually what you have there is more akin to an expression grammar. I'd suggest writing a grammar/parser for that instead of (ab?)using program_options for this.

    • If your program takes options: use program options.

    • If your program takes an expression: use an expression parser.

    An example:

    Live On Coliru

    // #define BOOST_SPIRIT_DEBUG
    #include 
    #include 
    #include 
    
    namespace qi  = boost::spirit::qi;
    
    struct Operation {
        enum Kind { add, multiply } kind;
        double operand;
    
        friend std::ostream& operator<<(std::ostream& os, Kind k) {
            switch (k) {
                case add:      return os << "--add";
                case multiply: return os << "--multiply";
            };
            return os << "??";
        }
    };
    
    BOOST_FUSION_ADAPT_STRUCT(Operation, (Operation::Kind,kind)(double,operand))
    
    template  
       struct expression_grammar : qi::grammar(), Skipper> {
           expression_grammar() : expression_grammar::base_type(start) {
               using namespace qi;
    
               opkinds.add
                   ("-a",         Operation::add)
                   ("--add",      Operation::add)
                   ("-m",         Operation::multiply)
                   ("--multiply", Operation::multiply)
                   ;
    
               option = opkinds > eol > double_;
    
               start  = *(option > eol);
    
               BOOST_SPIRIT_DEBUG_NODES((start)(option))
           }
         private:
           qi::symbols opkinds;
           qi::rule option;
           qi::rule(), Skipper> start;
       };
    
    int main(int argc, char const** argv) {
        std::stringstream iss;
        if (argc)
            std::copy(argv+1, argv+argc, std::ostream_iterator(iss, "\n"));
    
        typedef boost::spirit::istream_iterator It;
        expression_grammar grammar;
    
        It first(iss >> std::noskipws), last;
        std::vector operations;
        bool ok = qi::phrase_parse(first, last, grammar, qi::blank, operations);
    
        if (ok)
        {
            std::cout << "Parse success\n";
            for (auto const& op : operations)
                std::cout << boost::fusion::as_vector(op) << "\n";
        } else
            std::cout << "Parse failed\n";
    
        if (first!=last)
           std::cout << "Remaining input: '" << std::string(first,last) << "'\n";
    }
    

    Note

    • I choose, gratuitously, to use eol as the option separator. You might want to use '\0' instead. This was easiest because the blank skipper already skips whitespace /except/ eol. I'm lazy :)
    • You might wnat to mix-and-match (not treat all parameters as part of the expression). A common pattern would be

      myprogram -x option1 -v -o filename -- my expression grammar follows
      

      A common alternative pattern is to make the expression a single parameter:

      myprogram -e 'add 5; multiply 32;' -x option1
      

      See this approach Live on Coliru too

    • I was lazy again with the "Parse success" printing (I didn't want to implement operator<< for the Operation type)

    • enable debug info with #define BOOST_SPIRIT_DEBUG (uncomment the first line)

      
        -a\n8\n-m\n7\n-a\n32\n
        
        
        
        
        
        [[[--add, 8], [--multiply, 7], [--add, 32]]]
      
      Parse success
      (--add 8)
      (--multiply 7)
      (--add 32)
      

    0 讨论(0)
提交回复
热议问题