lemon/arg_parser.h
author Peter Kovacs <kpeter@inf.elte.hu>
Mon, 16 Jul 2018 16:21:40 +0200
changeset 1183 cd72eae05bdf
parent 877 141f9c0db4a3
child 1123 18c89646185e
permissions -rw-r--r--
Change typenames to avoid Windows-specific compile issue (#612)
     1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
     2  *
     3  * This file is a part of LEMON, a generic C++ optimization library.
     4  *
     5  * Copyright (C) 2003-2010
     6  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7  * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8  *
     9  * Permission to use, modify and distribute this software is granted
    10  * provided that this copyright notice appears in all copies. For
    11  * precise terms see the accompanying LICENSE file.
    12  *
    13  * This software is provided "AS IS" with no warranty of any kind,
    14  * express or implied, and with no claim as to its suitability for any
    15  * purpose.
    16  *
    17  */
    18 
    19 #ifndef LEMON_ARG_PARSER_H
    20 #define LEMON_ARG_PARSER_H
    21 
    22 #include <vector>
    23 #include <map>
    24 #include <list>
    25 #include <string>
    26 #include <iostream>
    27 #include <sstream>
    28 #include <algorithm>
    29 #include <lemon/assert.h>
    30 
    31 ///\ingroup misc
    32 ///\file
    33 ///\brief A tool to parse command line arguments.
    34 
    35 namespace lemon {
    36 
    37   ///Exception used by ArgParser
    38 
    39   ///Exception used by ArgParser.
    40   ///
    41   class ArgParserException : public Exception {
    42   public:
    43     /// Reasons for failure
    44 
    45     /// Reasons for failure.
    46     ///
    47     enum Reason {
    48       HELP,         ///< <tt>--help</tt> option was given.
    49       UNKNOWN_OPT,  ///< Unknown option was given.
    50       INVALID_OPT   ///< Invalid combination of options.
    51     };
    52 
    53   private:
    54     Reason _reason;
    55 
    56   public:
    57     ///Constructor
    58     ArgParserException(Reason r) throw() : _reason(r) {}
    59     ///Virtual destructor
    60     virtual ~ArgParserException() throw() {}
    61     ///A short description of the exception
    62     virtual const char* what() const throw() {
    63       switch(_reason)
    64         {
    65         case HELP:
    66           return "lemon::ArgParseException: ask for help";
    67           break;
    68         case UNKNOWN_OPT:
    69           return "lemon::ArgParseException: unknown option";
    70           break;
    71         case INVALID_OPT:
    72           return "lemon::ArgParseException: invalid combination of options";
    73           break;
    74         }
    75       return "";
    76     }
    77     ///Return the reason for the failure
    78     Reason reason() const {return _reason; }
    79   };
    80 
    81 
    82   ///Command line arguments parser
    83 
    84   ///\ingroup misc
    85   ///Command line arguments parser.
    86   ///
    87   ///For a complete example see the \ref arg_parser_demo.cc demo file.
    88   class ArgParser {
    89 
    90     static void _showHelp(void *p);
    91   protected:
    92 
    93     int _argc;
    94     const char * const *_argv;
    95 
    96     enum OptType { UNKNOWN=0, BOOL=1, STRING=2, DOUBLE=3, INTEGER=4, FUNC=5 };
    97 
    98     class ParData {
    99     public:
   100       union {
   101         bool *bool_p;
   102         int *int_p;
   103         double *double_p;
   104         std::string *string_p;
   105         struct {
   106           void (*p)(void *);
   107           void *data;
   108         } func_p;
   109 
   110       };
   111       std::string help;
   112       bool mandatory;
   113       OptType type;
   114       bool set;
   115       bool ingroup;
   116       bool has_syn;
   117       bool syn;
   118       bool self_delete;
   119       ParData() : mandatory(false), type(UNKNOWN), set(false), ingroup(false),
   120                   has_syn(false), syn(false), self_delete(false) {}
   121     };
   122 
   123     typedef std::map<std::string,ParData> Opts;
   124     Opts _opts;
   125 
   126     class GroupData
   127     {
   128     public:
   129       typedef std::list<std::string> Opts;
   130       Opts opts;
   131       bool only_one;
   132       bool mandatory;
   133       GroupData() :only_one(false), mandatory(false) {}
   134     };
   135 
   136     typedef std::map<std::string,GroupData> Groups;
   137     Groups _groups;
   138 
   139     struct OtherArg
   140     {
   141       std::string name;
   142       std::string help;
   143       OtherArg(std::string n, std::string h) :name(n), help(h) {}
   144 
   145     };
   146 
   147     std::vector<OtherArg> _others_help;
   148     std::vector<std::string> _file_args;
   149     std::string _command_name;
   150 
   151 
   152   private:
   153     //Bind a function to an option.
   154 
   155     //\param name The name of the option. The leading '-' must be omitted.
   156     //\param help A help string.
   157     //\retval func The function to be called when the option is given. It
   158     //  must be of type "void f(void *)"
   159     //\param data Data to be passed to \c func
   160     ArgParser &funcOption(const std::string &name,
   161                     const std::string &help,
   162                     void (*func)(void *),void *data);
   163 
   164     bool _exit_on_problems;
   165 
   166     void _terminate(ArgParserException::Reason reason) const;
   167 
   168   public:
   169 
   170     ///Constructor
   171     ArgParser(int argc, const char * const *argv);
   172 
   173     ~ArgParser();
   174 
   175     ///\name Options
   176     ///
   177 
   178     ///@{
   179 
   180     ///Add a new integer type option
   181 
   182     ///Add a new integer type option.
   183     ///\param name The name of the option. The leading '-' must be omitted.
   184     ///\param help A help string.
   185     ///\param value A default value for the option.
   186     ///\param obl Indicate if the option is mandatory.
   187     ArgParser &intOption(const std::string &name,
   188                     const std::string &help,
   189                     int value=0, bool obl=false);
   190 
   191     ///Add a new floating point type option
   192 
   193     ///Add a new floating point type option.
   194     ///\param name The name of the option. The leading '-' must be omitted.
   195     ///\param help A help string.
   196     ///\param value A default value for the option.
   197     ///\param obl Indicate if the option is mandatory.
   198     ArgParser &doubleOption(const std::string &name,
   199                       const std::string &help,
   200                       double value=0, bool obl=false);
   201 
   202     ///Add a new bool type option
   203 
   204     ///Add a new bool type option.
   205     ///\param name The name of the option. The leading '-' must be omitted.
   206     ///\param help A help string.
   207     ///\param value A default value for the option.
   208     ///\param obl Indicate if the option is mandatory.
   209     ///\note A mandatory bool obtion is of very little use.
   210     ArgParser &boolOption(const std::string &name,
   211                       const std::string &help,
   212                       bool value=false, bool obl=false);
   213 
   214     ///Add a new string type option
   215 
   216     ///Add a new string type option.
   217     ///\param name The name of the option. The leading '-' must be omitted.
   218     ///\param help A help string.
   219     ///\param value A default value for the option.
   220     ///\param obl Indicate if the option is mandatory.
   221     ArgParser &stringOption(const std::string &name,
   222                       const std::string &help,
   223                       std::string value="", bool obl=false);
   224 
   225     ///Give help string for non-parsed arguments.
   226 
   227     ///With this function you can give help string for non-parsed arguments.
   228     ///The parameter \c name will be printed in the short usage line, while
   229     ///\c help gives a more detailed description.
   230     ArgParser &other(const std::string &name,
   231                      const std::string &help="");
   232 
   233     ///@}
   234 
   235     ///\name Options with External Storage
   236     ///Using this functions, the value of the option will be directly written
   237     ///into a variable once the option appears in the command line.
   238 
   239     ///@{
   240 
   241     ///Add a new integer type option with a storage reference
   242 
   243     ///Add a new integer type option with a storage reference.
   244     ///\param name The name of the option. The leading '-' must be omitted.
   245     ///\param help A help string.
   246     ///\param obl Indicate if the option is mandatory.
   247     ///\retval ref The value of the argument will be written to this variable.
   248     ArgParser &refOption(const std::string &name,
   249                     const std::string &help,
   250                     int &ref, bool obl=false);
   251 
   252     ///Add a new floating type option with a storage reference
   253 
   254     ///Add a new floating type option with a storage reference.
   255     ///\param name The name of the option. The leading '-' must be omitted.
   256     ///\param help A help string.
   257     ///\param obl Indicate if the option is mandatory.
   258     ///\retval ref The value of the argument will be written to this variable.
   259     ArgParser &refOption(const std::string &name,
   260                       const std::string &help,
   261                       double &ref, bool obl=false);
   262 
   263     ///Add a new bool type option with a storage reference
   264 
   265     ///Add a new bool type option with a storage reference.
   266     ///\param name The name of the option. The leading '-' must be omitted.
   267     ///\param help A help string.
   268     ///\param obl Indicate if the option is mandatory.
   269     ///\retval ref The value of the argument will be written to this variable.
   270     ///\note A mandatory bool obtion is of very little use.
   271     ArgParser &refOption(const std::string &name,
   272                       const std::string &help,
   273                       bool &ref, bool obl=false);
   274 
   275     ///Add a new string type option with a storage reference
   276 
   277     ///Add a new string type option with a storage reference.
   278     ///\param name The name of the option. The leading '-' must be omitted.
   279     ///\param help A help string.
   280     ///\param obl Indicate if the option is mandatory.
   281     ///\retval ref The value of the argument will be written to this variable.
   282     ArgParser &refOption(const std::string &name,
   283                       const std::string &help,
   284                       std::string &ref, bool obl=false);
   285 
   286     ///@}
   287 
   288     ///\name Option Groups and Synonyms
   289     ///
   290 
   291     ///@{
   292 
   293     ///Bundle some options into a group
   294 
   295     /// You can group some option by calling this function repeatedly for each
   296     /// option to be grouped with the same groupname.
   297     ///\param group The group name.
   298     ///\param opt The option name.
   299     ArgParser &optionGroup(const std::string &group,
   300                            const std::string &opt);
   301 
   302     ///Make the members of a group exclusive
   303 
   304     ///If you call this function for a group, than at most one of them can be
   305     ///given at the same time.
   306     ArgParser &onlyOneGroup(const std::string &group);
   307 
   308     ///Make a group mandatory
   309 
   310     ///Using this function, at least one of the members of \c group
   311     ///must be given.
   312     ArgParser &mandatoryGroup(const std::string &group);
   313 
   314     ///Create synonym to an option
   315 
   316     ///With this function you can create a synonym \c syn of the
   317     ///option \c opt.
   318     ArgParser &synonym(const std::string &syn,
   319                            const std::string &opt);
   320 
   321     ///@}
   322 
   323   private:
   324     void show(std::ostream &os,Opts::const_iterator i) const;
   325     void show(std::ostream &os,Groups::const_iterator i) const;
   326     void showHelp(Opts::const_iterator i) const;
   327     void showHelp(std::vector<OtherArg>::const_iterator i) const;
   328 
   329     void unknownOpt(std::string arg) const;
   330 
   331     void requiresValue(std::string arg, OptType t) const;
   332     void checkMandatories() const;
   333 
   334     void shortHelp() const;
   335     void showHelp() const;
   336   public:
   337 
   338     ///Start the parsing process
   339     ArgParser &parse();
   340 
   341     /// Synonym for parse()
   342     ArgParser &run()
   343     {
   344       return parse();
   345     }
   346 
   347     ///Give back the command name (the 0th argument)
   348     const std::string &commandName() const { return _command_name; }
   349 
   350     ///Check if an opion has been given to the command.
   351     bool given(std::string op) const
   352     {
   353       Opts::const_iterator i = _opts.find(op);
   354       return i!=_opts.end()?i->second.set:false;
   355     }
   356 
   357 
   358     ///Magic type for operator[]
   359 
   360     ///This is the type of the return value of ArgParser::operator[]().
   361     ///It automatically converts to \c int, \c double, \c bool or
   362     ///\c std::string if the type of the option matches, which is checked
   363     ///with an \ref LEMON_ASSERT "assertion" (i.e. it performs runtime
   364     ///type checking).
   365     class RefType
   366     {
   367       const ArgParser &_parser;
   368       std::string _name;
   369     public:
   370       ///\e
   371       RefType(const ArgParser &p,const std::string &n) :_parser(p),_name(n) {}
   372       ///\e
   373       operator bool()
   374       {
   375         Opts::const_iterator i = _parser._opts.find(_name);
   376         LEMON_ASSERT(i!=_parser._opts.end(),
   377                      std::string()+"Unkown option: '"+_name+"'");
   378         LEMON_ASSERT(i->second.type==ArgParser::BOOL,
   379                      std::string()+"'"+_name+"' is a bool option");
   380         return *(i->second.bool_p);
   381       }
   382       ///\e
   383       operator std::string()
   384       {
   385         Opts::const_iterator i = _parser._opts.find(_name);
   386         LEMON_ASSERT(i!=_parser._opts.end(),
   387                      std::string()+"Unkown option: '"+_name+"'");
   388         LEMON_ASSERT(i->second.type==ArgParser::STRING,
   389                      std::string()+"'"+_name+"' is a string option");
   390         return *(i->second.string_p);
   391       }
   392       ///\e
   393       operator double()
   394       {
   395         Opts::const_iterator i = _parser._opts.find(_name);
   396         LEMON_ASSERT(i!=_parser._opts.end(),
   397                      std::string()+"Unkown option: '"+_name+"'");
   398         LEMON_ASSERT(i->second.type==ArgParser::DOUBLE ||
   399                      i->second.type==ArgParser::INTEGER,
   400                      std::string()+"'"+_name+"' is a floating point option");
   401         return i->second.type==ArgParser::DOUBLE ?
   402           *(i->second.double_p) : *(i->second.int_p);
   403       }
   404       ///\e
   405       operator int()
   406       {
   407         Opts::const_iterator i = _parser._opts.find(_name);
   408         LEMON_ASSERT(i!=_parser._opts.end(),
   409                      std::string()+"Unkown option: '"+_name+"'");
   410         LEMON_ASSERT(i->second.type==ArgParser::INTEGER,
   411                      std::string()+"'"+_name+"' is an integer option");
   412         return *(i->second.int_p);
   413       }
   414 
   415     };
   416 
   417     ///Give back the value of an option
   418 
   419     ///Give back the value of an option.
   420     ///\sa RefType
   421     RefType operator[](const std::string &n) const
   422     {
   423       return RefType(*this, n);
   424     }
   425 
   426     ///Give back the non-option type arguments.
   427 
   428     ///Give back a reference to a vector consisting of the program arguments
   429     ///not starting with a '-' character.
   430     const std::vector<std::string> &files() const { return _file_args; }
   431 
   432     ///Throw instead of exit in case of problems
   433     void throwOnProblems()
   434     {
   435       _exit_on_problems=false;
   436     }
   437   };
   438 }
   439 
   440 #endif // LEMON_ARG_PARSER_H