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