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