gravatar
alpar (Alpar Juttner)
alpar@cs.elte.hu
ArgParser can throw exception instead of exit(1) (#332)
0 3 0
default
3 files changed with 70 insertions and 5 deletions:
↑ Collapse diff ↑
Show white space 96 line context
... ...
@@ -20,84 +20,93 @@
20 20
///\file
21 21
///\brief Argument parser demo
22 22
///
23 23
/// This example shows how the argument parser can be used.
24 24
///
25 25
/// \include arg_parser_demo.cc
26 26

	
27 27
#include <lemon/arg_parser.h>
28 28

	
29 29
using namespace lemon;
30 30
int main(int argc, char **argv)
31 31
{
32 32
  // Initialize the argument parser
33 33
  ArgParser ap(argc, argv);
34 34
  int i;
35 35
  std::string s;
36 36
  double d = 1.0;
37 37
  bool b, nh;
38 38
  bool g1, g2, g3;
39 39

	
40 40
  // Add a mandatory integer option with storage reference
41 41
  ap.refOption("n", "An integer input.", i, true);
42 42
  // Add a double option with storage reference (the default value is 1.0)
43 43
  ap.refOption("val", "A double input.", d);
44 44
  // Add a double option without storage reference (the default value is 3.14)
45 45
  ap.doubleOption("val2", "A double input.", 3.14);
46 46
  // Set synonym for -val option
47 47
  ap.synonym("vals", "val");
48 48
  // Add a string option
49 49
  ap.refOption("name", "A string input.", s);
50 50
  // Add bool options
51 51
  ap.refOption("f", "A switch.", b)
52 52
    .refOption("nohelp", "", nh)
53 53
    .refOption("gra", "Choice A", g1)
54 54
    .refOption("grb", "Choice B", g2)
55 55
    .refOption("grc", "Choice C", g3);
56 56
  // Bundle -gr* options into a group
57 57
  ap.optionGroup("gr", "gra")
58 58
    .optionGroup("gr", "grb")
59 59
    .optionGroup("gr", "grc");
60 60
  // Set the group mandatory
61 61
  ap.mandatoryGroup("gr");
62 62
  // Set the options of the group exclusive (only one option can be given)
63 63
  ap.onlyOneGroup("gr");
64 64
  // Add non-parsed arguments (e.g. input files)
65 65
  ap.other("infile", "The input file.")
66 66
    .other("...");
67 67

	
68
  // Throw an exception when problems occurs. The default behavior is to
69
  // exit(1) on these cases, but this makes Valgrind falsely warn
70
  // about memory leaks.
71
  ap.throwOnProblems();
72
  
68 73
  // Perform the parsing process
69 74
  // (in case of any error it terminates the program)
75
  // The try {} construct is necessary only if the ap.trowOnProblems()
76
  // setting is in use.
77
  try {
70 78
  ap.parse();
79
  } catch (ArgParserException &) { return 1; }
71 80

	
72 81
  // Check each option if it has been given and print its value
73 82
  std::cout << "Parameters of '" << ap.commandName() << "':\n";
74 83

	
75 84
  std::cout << "  Value of -n: " << i << std::endl;
76 85
  if(ap.given("val")) std::cout << "  Value of -val: " << d << std::endl;
77 86
  if(ap.given("val2")) {
78 87
    d = ap["val2"];
79 88
    std::cout << "  Value of -val2: " << d << std::endl;
80 89
  }
81 90
  if(ap.given("name")) std::cout << "  Value of -name: " << s << std::endl;
82 91
  if(ap.given("f")) std::cout << "  -f is given\n";
83 92
  if(ap.given("nohelp")) std::cout << "  Value of -nohelp: " << nh << std::endl;
84 93
  if(ap.given("gra")) std::cout << "  -gra is given\n";
85 94
  if(ap.given("grb")) std::cout << "  -grb is given\n";
86 95
  if(ap.given("grc")) std::cout << "  -grc is given\n";
87 96

	
88 97
  switch(ap.files().size()) {
89 98
  case 0:
90 99
    std::cout << "  No file argument was given.\n";
91 100
    break;
92 101
  case 1:
93 102
    std::cout << "  1 file argument was given. It is:\n";
94 103
    break;
95 104
  default:
96 105
    std::cout << "  "
97 106
              << ap.files().size() << " file arguments were given. They are:\n";
98 107
  }
99 108
  for(unsigned int i=0;i<ap.files().size();++i)
100 109
    std::cout << "    '" << ap.files()[i] << "'\n";
101 110

	
102 111
  return 0;
103 112
}
Show white space 96 line context
1 1
/* -*- mode: C++; indent-tabs-mode: nil; -*-
2 2
 *
3 3
 * This file is a part of LEMON, a generic C++ optimization library.
4 4
 *
5 5
 * Copyright (C) 2003-2009
6 6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
8 8
 *
9 9
 * Permission to use, modify and distribute this software is granted
10 10
 * provided that this copyright notice appears in all copies. For
11 11
 * precise terms see the accompanying LICENSE file.
12 12
 *
13 13
 * This software is provided "AS IS" with no warranty of any kind,
14 14
 * express or implied, and with no claim as to its suitability for any
15 15
 * purpose.
16 16
 *
17 17
 */
18 18

	
19 19
#include <lemon/arg_parser.h>
20 20

	
21 21
namespace lemon {
22 22

	
23
  void ArgParser::_terminate(ArgParserException::Reason reason) const
24
  {
25
    if(_exit_on_problems)
26
      exit(1);
27
    else throw(ArgParserException(reason));
28
  }
29
  
30
  
23 31
  void ArgParser::_showHelp(void *p)
24 32
  {
25 33
    (static_cast<ArgParser*>(p))->showHelp();
26
    exit(1);
34
    (static_cast<ArgParser*>(p))->_terminate(ArgParserException::HELP);
27 35
  }
28 36

	
29 37
  ArgParser::ArgParser(int argc, const char * const *argv)
30
    :_argc(argc), _argv(argv), _command_name(argv[0]) {
38
    :_argc(argc), _argv(argv), _command_name(argv[0]),
39
    _exit_on_problems(true) {
31 40
    funcOption("-help","Print a short help message",_showHelp,this);
32 41
    synonym("help","-help");
33 42
    synonym("h","-help");
34 43
  }
35 44

	
36 45
  ArgParser::~ArgParser()
37 46
  {
38 47
    for(Opts::iterator i=_opts.begin();i!=_opts.end();++i)
39 48
      if(i->second.self_delete)
40 49
        switch(i->second.type) {
41 50
        case BOOL:
42 51
          delete i->second.bool_p;
43 52
          break;
44 53
        case STRING:
45 54
          delete i->second.string_p;
46 55
          break;
47 56
        case DOUBLE:
48 57
          delete i->second.double_p;
49 58
          break;
50 59
        case INTEGER:
51 60
          delete i->second.int_p;
52 61
          break;
53 62
        case UNKNOWN:
54 63
          break;
55 64
        case FUNC:
56 65
          break;
57 66
        }
58 67
  }
59 68

	
60 69

	
61 70
  ArgParser &ArgParser::intOption(const std::string &name,
62 71
                               const std::string &help,
63 72
                               int value, bool obl)
64 73
  {
65 74
    ParData p;
66 75
    p.int_p=new int(value);
67 76
    p.self_delete=true;
68 77
    p.help=help;
69 78
    p.type=INTEGER;
70 79
    p.mandatory=obl;
71 80
    _opts[name]=p;
72 81
    return *this;
73 82
  }
74 83

	
75 84
  ArgParser &ArgParser::doubleOption(const std::string &name,
76 85
                               const std::string &help,
77 86
                               double value, bool obl)
78 87
  {
... ...
@@ -297,169 +306,169 @@
297 306
      cstr << ' ';
298 307
      if(!g->second.mandatory) cstr << '[';
299 308
      show(cstr,g);
300 309
      if(!g->second.mandatory) cstr << ']';
301 310
      if(pos+cstr.str().size()>LINE_LEN) {
302 311
        std::cerr << std::endl << indent;
303 312
        pos=indent.size();
304 313
      }
305 314
      std::cerr << cstr.str();
306 315
      pos+=cstr.str().size();
307 316
    }
308 317
    for(Opts::const_iterator i=_opts.begin();i!=_opts.end();++i)
309 318
      if(!i->second.ingroup&&!i->second.syn) {
310 319
        std::ostringstream cstr;
311 320
        cstr << ' ';
312 321
        if(!i->second.mandatory) cstr << '[';
313 322
        show(cstr,i);
314 323
        if(!i->second.mandatory) cstr << ']';
315 324
        if(pos+cstr.str().size()>LINE_LEN) {
316 325
          std::cerr << std::endl << indent;
317 326
          pos=indent.size();
318 327
        }
319 328
        std::cerr << cstr.str();
320 329
        pos+=cstr.str().size();
321 330
      }
322 331
    for(std::vector<OtherArg>::const_iterator i=_others_help.begin();
323 332
        i!=_others_help.end();++i)
324 333
      {
325 334
        std::ostringstream cstr;
326 335
        cstr << ' ' << i->name;
327 336

	
328 337
        if(pos+cstr.str().size()>LINE_LEN) {
329 338
          std::cerr << std::endl << indent;
330 339
          pos=indent.size();
331 340
        }
332 341
        std::cerr << cstr.str();
333 342
        pos+=cstr.str().size();
334 343
      }
335 344
    std::cerr << std::endl;
336 345
  }
337 346

	
338 347
  void ArgParser::showHelp() const
339 348
  {
340 349
    shortHelp();
341 350
    std::cerr << "Where:\n";
342 351
    for(std::vector<OtherArg>::const_iterator i=_others_help.begin();
343 352
        i!=_others_help.end();++i) showHelp(i);
344 353
    for(Opts::const_iterator i=_opts.begin();i!=_opts.end();++i) showHelp(i);
345
    exit(1);
354
    _terminate(ArgParserException::HELP);
346 355
  }
347 356

	
348 357

	
349 358
  void ArgParser::unknownOpt(std::string arg) const
350 359
  {
351 360
    std::cerr << "\nUnknown option: " << arg << "\n";
352 361
    std::cerr << "\nType '" << _command_name <<
353 362
      " --help' to obtain a short summary on the usage.\n\n";
354
    exit(1);
363
    _terminate(ArgParserException::UNKNOWN_OPT);
355 364
  }
356 365

	
357 366
  void ArgParser::requiresValue(std::string arg, OptType t) const
358 367
  {
359 368
    std::cerr << "Argument '" << arg << "' requires a";
360 369
    switch(t) {
361 370
    case STRING:
362 371
      std::cerr << " string";
363 372
      break;
364 373
    case INTEGER:
365 374
      std::cerr << "n integer";
366 375
      break;
367 376
    case DOUBLE:
368 377
      std::cerr << " floating point";
369 378
      break;
370 379
    default:
371 380
      break;
372 381
    }
373 382
    std::cerr << " value\n\n";
374 383
    showHelp();
375 384
  }
376 385

	
377 386

	
378 387
  void ArgParser::checkMandatories() const
379 388
  {
380 389
    bool ok=true;
381 390
    for(Opts::const_iterator i=_opts.begin();i!=_opts.end();++i)
382 391
      if(i->second.mandatory&&!i->second.set)
383 392
        {
384 393
          if(ok)
385 394
            std::cerr << _command_name
386 395
                      << ": The following mandatory arguments are missing.\n";
387 396
          ok=false;
388 397
          showHelp(i);
389 398
        }
390 399
    for(Groups::const_iterator i=_groups.begin();i!=_groups.end();++i)
391 400
      if(i->second.mandatory||i->second.only_one)
392 401
        {
393 402
          int set=0;
394 403
          for(GroupData::Opts::const_iterator o=i->second.opts.begin();
395 404
              o!=i->second.opts.end();++o)
396 405
            if(_opts.find(*o)->second.set) ++set;
397 406
          if(i->second.mandatory&&!set) {
398 407
            std::cerr << _command_name <<
399 408
              ": At least one of the following arguments is mandatory.\n";
400 409
            ok=false;
401 410
            for(GroupData::Opts::const_iterator o=i->second.opts.begin();
402 411
                o!=i->second.opts.end();++o)
403 412
              showHelp(_opts.find(*o));
404 413
          }
405 414
          if(i->second.only_one&&set>1) {
406 415
            std::cerr << _command_name <<
407 416
              ": At most one of the following arguments can be given.\n";
408 417
            ok=false;
409 418
            for(GroupData::Opts::const_iterator o=i->second.opts.begin();
410 419
                o!=i->second.opts.end();++o)
411 420
              showHelp(_opts.find(*o));
412 421
          }
413 422
        }
414 423
    if(!ok) {
415 424
      std::cerr << "\nType '" << _command_name <<
416 425
        " --help' to obtain a short summary on the usage.\n\n";
417
      exit(1);
426
      _terminate(ArgParserException::INVALID_OPT);
418 427
    }
419 428
  }
420 429

	
421 430
  ArgParser &ArgParser::parse()
422 431
  {
423 432
    for(int ar=1; ar<_argc; ++ar) {
424 433
      std::string arg(_argv[ar]);
425 434
      if (arg[0] != '-' || arg.size() == 1) {
426 435
        _file_args.push_back(arg);
427 436
      }
428 437
      else {
429 438
        Opts::iterator i = _opts.find(arg.substr(1));
430 439
        if(i==_opts.end()) unknownOpt(arg);
431 440
        else {
432 441
          if(i->second.syn) i=_opts.find(i->second.help);
433 442
          ParData &p(i->second);
434 443
          if (p.type==BOOL) *p.bool_p=true;
435 444
          else if (p.type==FUNC) p.func_p.p(p.func_p.data);
436 445
          else if(++ar==_argc) requiresValue(arg, p.type);
437 446
          else {
438 447
            std::string val(_argv[ar]);
439 448
            std::istringstream vals(val);
440 449
            switch(p.type) {
441 450
            case STRING:
442 451
              *p.string_p=val;
443 452
              break;
444 453
            case INTEGER:
445 454
              vals >> *p.int_p;
446 455
              break;
447 456
            case DOUBLE:
448 457
              vals >> *p.double_p;
449 458
              break;
450 459
            default:
451 460
              break;
452 461
            }
453 462
            if(p.type!=STRING&&(!vals||!vals.eof()))
454 463
              requiresValue(arg, p.type);
455 464
          }
456 465
          p.set = true;
457 466
        }
458 467
      }
459 468
    }
460 469
    checkMandatories();
461 470

	
462 471
    return *this;
463 472
  }
464 473

	
465 474
}
Show white space 96 line context
1 1
/* -*- mode: C++; indent-tabs-mode: nil; -*-
2 2
 *
3 3
 * This file is a part of LEMON, a generic C++ optimization library.
4 4
 *
5 5
 * Copyright (C) 2003-2009
6 6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
7 7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
8 8
 *
9 9
 * Permission to use, modify and distribute this software is granted
10 10
 * provided that this copyright notice appears in all copies. For
11 11
 * precise terms see the accompanying LICENSE file.
12 12
 *
13 13
 * This software is provided "AS IS" with no warranty of any kind,
14 14
 * express or implied, and with no claim as to its suitability for any
15 15
 * purpose.
16 16
 *
17 17
 */
18 18

	
19 19
#ifndef LEMON_ARG_PARSER_H
20 20
#define LEMON_ARG_PARSER_H
21 21

	
22 22
#include <vector>
23 23
#include <map>
24 24
#include <list>
25 25
#include <string>
26 26
#include <iostream>
27 27
#include <sstream>
28 28
#include <algorithm>
29 29
#include <lemon/assert.h>
30 30

	
31 31
///\ingroup misc
32 32
///\file
33 33
///\brief A tool to parse command line arguments.
34 34

	
35 35
namespace lemon {
36 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

	
37 75
  ///Command line arguments parser
38 76

	
39 77
  ///\ingroup misc
40 78
  ///Command line arguments parser.
41 79
  ///
42 80
  ///For a complete example see the \ref arg_parser_demo.cc demo file.
43 81
  class ArgParser {
44 82

	
45 83
    static void _showHelp(void *p);
46 84
  protected:
47 85

	
48 86
    int _argc;
49 87
    const char * const *_argv;
50 88

	
51 89
    enum OptType { UNKNOWN=0, BOOL=1, STRING=2, DOUBLE=3, INTEGER=4, FUNC=5 };
52 90

	
53 91
    class ParData {
54 92
    public:
55 93
      union {
56 94
        bool *bool_p;
57 95
        int *int_p;
58 96
        double *double_p;
59 97
        std::string *string_p;
60 98
        struct {
61 99
          void (*p)(void *);
62 100
          void *data;
63 101
        } func_p;
64 102

	
65 103
      };
66 104
      std::string help;
67 105
      bool mandatory;
68 106
      OptType type;
69 107
      bool set;
70 108
      bool ingroup;
71 109
      bool has_syn;
72 110
      bool syn;
73 111
      bool self_delete;
74 112
      ParData() : mandatory(false), type(UNKNOWN), set(false), ingroup(false),
75 113
                  has_syn(false), syn(false), self_delete(false) {}
76 114
    };
77 115

	
78 116
    typedef std::map<std::string,ParData> Opts;
79 117
    Opts _opts;
80 118

	
81 119
    class GroupData
82 120
    {
83 121
    public:
84 122
      typedef std::list<std::string> Opts;
85 123
      Opts opts;
86 124
      bool only_one;
87 125
      bool mandatory;
88 126
      GroupData() :only_one(false), mandatory(false) {}
89 127
    };
90 128

	
91 129
    typedef std::map<std::string,GroupData> Groups;
92 130
    Groups _groups;
93 131

	
94 132
    struct OtherArg
95 133
    {
96 134
      std::string name;
97 135
      std::string help;
98 136
      OtherArg(std::string n, std::string h) :name(n), help(h) {}
99 137

	
100 138
    };
101 139

	
102 140
    std::vector<OtherArg> _others_help;
103 141
    std::vector<std::string> _file_args;
104 142
    std::string _command_name;
105 143

	
106 144

	
107 145
  private:
108 146
    //Bind a function to an option.
109 147

	
110 148
    //\param name The name of the option. The leading '-' must be omitted.
111 149
    //\param help A help string.
112 150
    //\retval func The function to be called when the option is given. It
113 151
    //  must be of type "void f(void *)"
114 152
    //\param data Data to be passed to \c func
115 153
    ArgParser &funcOption(const std::string &name,
116 154
                    const std::string &help,
117 155
                    void (*func)(void *),void *data);
118 156

	
157
    bool _exit_on_problems;
158
    
159
    void _terminate(ArgParserException::Reason reason) const;
160

	
119 161
  public:
120 162

	
121 163
    ///Constructor
122 164
    ArgParser(int argc, const char * const *argv);
123 165

	
124 166
    ~ArgParser();
125 167

	
126 168
    ///\name Options
127 169
    ///
128 170

	
129 171
    ///@{
130 172

	
131 173
    ///Add a new integer type option
132 174

	
133 175
    ///Add a new integer type option.
134 176
    ///\param name The name of the option. The leading '-' must be omitted.
135 177
    ///\param help A help string.
136 178
    ///\param value A default value for the option.
137 179
    ///\param obl Indicate if the option is mandatory.
138 180
    ArgParser &intOption(const std::string &name,
139 181
                    const std::string &help,
140 182
                    int value=0, bool obl=false);
141 183

	
142 184
    ///Add a new floating point type option
143 185

	
144 186
    ///Add a new floating point type option.
145 187
    ///\param name The name of the option. The leading '-' must be omitted.
146 188
    ///\param help A help string.
147 189
    ///\param value A default value for the option.
148 190
    ///\param obl Indicate if the option is mandatory.
149 191
    ArgParser &doubleOption(const std::string &name,
150 192
                      const std::string &help,
151 193
                      double value=0, bool obl=false);
152 194

	
153 195
    ///Add a new bool type option
154 196

	
155 197
    ///Add a new bool type option.
156 198
    ///\param name The name of the option. The leading '-' must be omitted.
157 199
    ///\param help A help string.
158 200
    ///\param value A default value for the option.
159 201
    ///\param obl Indicate if the option is mandatory.
160 202
    ///\note A mandatory bool obtion is of very little use.
161 203
    ArgParser &boolOption(const std::string &name,
162 204
                      const std::string &help,
163 205
                      bool value=false, bool obl=false);
164 206

	
165 207
    ///Add a new string type option
166 208

	
... ...
@@ -335,52 +377,57 @@
335 377
      {
336 378
        Opts::const_iterator i = _parser._opts.find(_name);
337 379
        LEMON_ASSERT(i!=_parser._opts.end(),
338 380
                     std::string()+"Unkown option: '"+_name+"'");
339 381
        LEMON_ASSERT(i->second.type==ArgParser::STRING,
340 382
                     std::string()+"'"+_name+"' is a string option");
341 383
        return *(i->second.string_p);
342 384
      }
343 385
      ///\e
344 386
      operator double()
345 387
      {
346 388
        Opts::const_iterator i = _parser._opts.find(_name);
347 389
        LEMON_ASSERT(i!=_parser._opts.end(),
348 390
                     std::string()+"Unkown option: '"+_name+"'");
349 391
        LEMON_ASSERT(i->second.type==ArgParser::DOUBLE ||
350 392
                     i->second.type==ArgParser::INTEGER,
351 393
                     std::string()+"'"+_name+"' is a floating point option");
352 394
        return i->second.type==ArgParser::DOUBLE ?
353 395
          *(i->second.double_p) : *(i->second.int_p);
354 396
      }
355 397
      ///\e
356 398
      operator int()
357 399
      {
358 400
        Opts::const_iterator i = _parser._opts.find(_name);
359 401
        LEMON_ASSERT(i!=_parser._opts.end(),
360 402
                     std::string()+"Unkown option: '"+_name+"'");
361 403
        LEMON_ASSERT(i->second.type==ArgParser::INTEGER,
362 404
                     std::string()+"'"+_name+"' is an integer option");
363 405
        return *(i->second.int_p);
364 406
      }
365 407

	
366 408
    };
367 409

	
368 410
    ///Give back the value of an option
369 411

	
370 412
    ///Give back the value of an option.
371 413
    ///\sa RefType
372 414
    RefType operator[](const std::string &n) const
373 415
    {
374 416
      return RefType(*this, n);
375 417
    }
376 418

	
377 419
    ///Give back the non-option type arguments.
378 420

	
379 421
    ///Give back a reference to a vector consisting of the program arguments
380 422
    ///not starting with a '-' character.
381 423
    const std::vector<std::string> &files() const { return _file_args; }
382 424

	
425
    ///Throw instead of exit in case of problems
426
    void throwOnProblems() 
427
    {
428
      _exit_on_problems=false;
429
    }
383 430
  };
384 431
}
385 432

	
386 433
#endif // LEMON_ARG_PARSER_H
0 comments (0 inline)