lemon/arg_parser.h
author Alpar Juttner <alpar@cs.elte.hu>
Mon, 24 Oct 2011 21:40:31 +0200
branch1.2
changeset 931 c1a959df3b2c
parent 877 141f9c0db4a3
permissions -rw-r--r--
Merge #428 and #429 to branch 1.2
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@880
    38
kpeter@880
    39
  ///Exception used by ArgParser.
kpeter@880
    40
  ///
alpar@842
    41
  class ArgParserException : public Exception {
alpar@842
    42
  public:
kpeter@880
    43
    /// Reasons for failure
kpeter@880
    44
kpeter@880
    45
    /// Reasons for failure.
kpeter@880
    46
    ///
alpar@842
    47
    enum Reason {
kpeter@880
    48
      HELP,         ///< <tt>--help</tt> option was given.
kpeter@880
    49
      UNKNOWN_OPT,  ///< Unknown option was given.
kpeter@880
    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