lemon/arg_parser.h
author Peter Kovacs <kpeter@inf.elte.hu>
Tue, 15 Mar 2011 19:32:21 +0100
changeset 936 ddd3c0d3d9bf
parent 877 141f9c0db4a3
child 1123 18c89646185e
permissions -rw-r--r--
Implement the scaling Price Refinement heuristic in CostScaling (#417)
instead of Early Termination.

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