parsing command-line options with sequence containers?

后端 未结 1 482
终归单人心
终归单人心 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 <boost/fusion/adapted/struct.hpp>
    #include <boost/fusion/include/io.hpp>
    #include <boost/spirit/include/qi.hpp>
    
    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 <typename It, typename Skipper = qi::blank_type> 
       struct expression_grammar : qi::grammar<It, std::vector<Operation>(), 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<char, Operation::Kind> opkinds;
           qi::rule<It, Operation(), Skipper> option;
           qi::rule<It, std::vector<Operation>(), Skipper> start;
       };
    
    int main(int argc, char const** argv) {
        std::stringstream iss;
        if (argc)
            std::copy(argv+1, argv+argc, std::ostream_iterator<const char*>(iss, "\n"));
    
        typedef boost::spirit::istream_iterator It;
        expression_grammar<It> grammar;
    
        It first(iss >> std::noskipws), last;
        std::vector<Operation> 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)

      <start>
        <try>-a\n8\n-m\n7\n-a\n32\n</try>
        <option>
          <try>-a\n8\n-m\n7\n-a\n32\n</try>
          <success>\n-m\n7\n-a\n32\n</success>
          <attributes>[[--add, 8]]</attributes>
        </option>
        <option>
          <try>-m\n7\n-a\n32\n</try>
          <success>\n-a\n32\n</success>
          <attributes>[[--multiply, 7]]</attributes>
        </option>
        <option>
          <try>-a\n32\n</try>
          <success>\n</success>
          <attributes>[[--add, 32]]</attributes>
        </option>
        <option>
          <try></try>
          <fail/>
        </option>
        <success></success>
        <attributes>[[[--add, 8], [--multiply, 7], [--add, 32]]]</attributes>
      </start>
      Parse success
      (--add 8)
      (--multiply 7)
      (--add 32)
      
    0 讨论(0)
提交回复
热议问题