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 384 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
///\ingroup demos
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 384 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
  {
79 88
    ParData p;
80 89
    p.double_p=new double(value);
81 90
    p.self_delete=true;
82 91
    p.help=help;
83 92
    p.type=DOUBLE;
84 93
    p.mandatory=obl;
85 94
    _opts[name]=p;
86 95
    return *this;
87 96
  }
88 97

	
89 98
  ArgParser &ArgParser::boolOption(const std::string &name,
90 99
                               const std::string &help,
91 100
                               bool value, bool obl)
92 101
  {
93 102
    ParData p;
94 103
    p.bool_p=new bool(value);
95 104
    p.self_delete=true;
96 105
    p.help=help;
97 106
    p.type=BOOL;
98 107
    p.mandatory=obl;
99 108
    _opts[name]=p;
100 109
    return *this;
101 110
  }
102 111

	
103 112
  ArgParser &ArgParser::stringOption(const std::string &name,
104 113
                               const std::string &help,
105 114
                               std::string value, bool obl)
106 115
  {
107 116
    ParData p;
108 117
    p.string_p=new std::string(value);
109 118
    p.self_delete=true;
110 119
    p.help=help;
111 120
    p.type=STRING;
112 121
    p.mandatory=obl;
113 122
    _opts[name]=p;
114 123
    return *this;
115 124
  }
116 125

	
117 126
  ArgParser &ArgParser::refOption(const std::string &name,
118 127
                               const std::string &help,
119 128
                               int &ref, bool obl)
120 129
  {
121 130
    ParData p;
122 131
    p.int_p=&ref;
123 132
    p.self_delete=false;
124 133
    p.help=help;
125 134
    p.type=INTEGER;
126 135
    p.mandatory=obl;
127 136
    _opts[name]=p;
128 137
    return *this;
129 138
  }
130 139

	
131 140
  ArgParser &ArgParser::refOption(const std::string &name,
132 141
                                  const std::string &help,
133 142
                                  double &ref, bool obl)
134 143
  {
135 144
    ParData p;
136 145
    p.double_p=&ref;
137 146
    p.self_delete=false;
138 147
    p.help=help;
139 148
    p.type=DOUBLE;
140 149
    p.mandatory=obl;
141 150
    _opts[name]=p;
142 151
    return *this;
143 152
  }
144 153

	
145 154
  ArgParser &ArgParser::refOption(const std::string &name,
146 155
                                  const std::string &help,
147 156
                                  bool &ref, bool obl)
148 157
  {
149 158
    ParData p;
150 159
    p.bool_p=&ref;
151 160
    p.self_delete=false;
152 161
    p.help=help;
153 162
    p.type=BOOL;
154 163
    p.mandatory=obl;
155 164
    _opts[name]=p;
156 165

	
157 166
    ref = false;
158 167

	
159 168
    return *this;
160 169
  }
161 170

	
162 171
  ArgParser &ArgParser::refOption(const std::string &name,
163 172
                               const std::string &help,
164 173
                               std::string &ref, bool obl)
165 174
  {
166 175
    ParData p;
167 176
    p.string_p=&ref;
168 177
    p.self_delete=false;
169 178
    p.help=help;
170 179
    p.type=STRING;
171 180
    p.mandatory=obl;
172 181
    _opts[name]=p;
173 182
    return *this;
174 183
  }
175 184

	
176 185
  ArgParser &ArgParser::funcOption(const std::string &name,
177 186
                               const std::string &help,
178 187
                               void (*func)(void *),void *data)
179 188
  {
180 189
    ParData p;
181 190
    p.func_p.p=func;
182 191
    p.func_p.data=data;
183 192
    p.self_delete=false;
184 193
    p.help=help;
185 194
    p.type=FUNC;
186 195
    p.mandatory=false;
187 196
    _opts[name]=p;
188 197
    return *this;
189 198
  }
190 199

	
191 200
  ArgParser &ArgParser::optionGroup(const std::string &group,
192 201
                                    const std::string &opt)
193 202
  {
194 203
    Opts::iterator i = _opts.find(opt);
195 204
    LEMON_ASSERT(i!=_opts.end(), "Unknown option: '"+opt+"'");
196 205
    LEMON_ASSERT(!(i->second.ingroup),
197 206
                 "Option already in option group: '"+opt+"'");
198 207
    GroupData &g=_groups[group];
199 208
    g.opts.push_back(opt);
200 209
    i->second.ingroup=true;
201 210
    return *this;
202 211
  }
203 212

	
204 213
  ArgParser &ArgParser::onlyOneGroup(const std::string &group)
205 214
  {
206 215
    GroupData &g=_groups[group];
207 216
    g.only_one=true;
208 217
    return *this;
209 218
  }
210 219

	
211 220
  ArgParser &ArgParser::synonym(const std::string &syn,
212 221
                                const std::string &opt)
213 222
  {
214 223
    Opts::iterator o = _opts.find(opt);
215 224
    Opts::iterator s = _opts.find(syn);
216 225
    LEMON_ASSERT(o!=_opts.end(), "Unknown option: '"+opt+"'");
217 226
    LEMON_ASSERT(s==_opts.end(), "Option already used: '"+syn+"'");
218 227
    ParData p;
219 228
    p.help=opt;
220 229
    p.mandatory=false;
221 230
    p.syn=true;
222 231
    _opts[syn]=p;
223 232
    o->second.has_syn=true;
224 233
    return *this;
225 234
  }
226 235

	
227 236
  ArgParser &ArgParser::mandatoryGroup(const std::string &group)
228 237
  {
229 238
    GroupData &g=_groups[group];
230 239
    g.mandatory=true;
231 240
    return *this;
232 241
  }
233 242

	
234 243
  ArgParser &ArgParser::other(const std::string &name,
235 244
                              const std::string &help)
236 245
  {
237 246
    _others_help.push_back(OtherArg(name,help));
238 247
    return *this;
239 248
  }
240 249

	
241 250
  void ArgParser::show(std::ostream &os,Opts::const_iterator i) const
242 251
  {
243 252
    os << "-" << i->first;
244 253
    if(i->second.has_syn)
245 254
      for(Opts::const_iterator j=_opts.begin();j!=_opts.end();++j)
246 255
        if(j->second.syn&&j->second.help==i->first)
247 256
          os << "|-" << j->first;
248 257
    switch(i->second.type) {
249 258
    case STRING:
250 259
      os << " str";
251 260
      break;
252 261
    case INTEGER:
253 262
      os << " int";
254 263
      break;
255 264
    case DOUBLE:
256 265
      os << " num";
257 266
      break;
258 267
    default:
259 268
      break;
260 269
    }
261 270
  }
262 271

	
263 272
  void ArgParser::show(std::ostream &os,Groups::const_iterator i) const
264 273
  {
265 274
    GroupData::Opts::const_iterator o=i->second.opts.begin();
266 275
    while(o!=i->second.opts.end()) {
267 276
      show(os,_opts.find(*o));
268 277
      ++o;
269 278
      if(o!=i->second.opts.end()) os<<'|';
270 279
    }
271 280
  }
272 281

	
273 282
  void ArgParser::showHelp(Opts::const_iterator i) const
274 283
  {
275 284
    if(i->second.help.size()==0||i->second.syn) return;
276 285
    std::cerr << "  ";
277 286
    show(std::cerr,i);
278 287
    std::cerr << std::endl;
279 288
    std::cerr << "     " << i->second.help << std::endl;
280 289
  }
281 290
  void ArgParser::showHelp(std::vector<ArgParser::OtherArg>::const_iterator i)
282 291
    const
283 292
  {
284 293
    if(i->help.size()==0) return;
285 294
    std::cerr << "  " << i->name << std::endl
286 295
              << "     " << i->help << std::endl;
287 296
  }
288 297

	
289 298
  void ArgParser::shortHelp() const
290 299
  {
291 300
    const unsigned int LINE_LEN=77;
292 301
    const std::string indent("    ");
293 302
    std::cerr << "Usage:\n  " << _command_name;
294 303
    int pos=_command_name.size()+2;
295 304
    for(Groups::const_iterator g=_groups.begin();g!=_groups.end();++g) {
296 305
      std::ostringstream cstr;
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 384 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

	
167 209
    ///Add a new string type option.
168 210
    ///\param name The name of the option. The leading '-' must be omitted.
169 211
    ///\param help A help string.
170 212
    ///\param value A default value for the option.
171 213
    ///\param obl Indicate if the option is mandatory.
172 214
    ArgParser &stringOption(const std::string &name,
173 215
                      const std::string &help,
174 216
                      std::string value="", bool obl=false);
175 217

	
176 218
    ///Give help string for non-parsed arguments.
177 219

	
178 220
    ///With this function you can give help string for non-parsed arguments.
179 221
    ///The parameter \c name will be printed in the short usage line, while
180 222
    ///\c help gives a more detailed description.
181 223
    ArgParser &other(const std::string &name,
182 224
                     const std::string &help="");
183 225

	
184 226
    ///@}
185 227

	
186 228
    ///\name Options with External Storage
187 229
    ///Using this functions, the value of the option will be directly written
188 230
    ///into a variable once the option appears in the command line.
189 231

	
190 232
    ///@{
191 233

	
192 234
    ///Add a new integer type option with a storage reference
193 235

	
194 236
    ///Add a new integer type option with a storage reference.
195 237
    ///\param name The name of the option. The leading '-' must be omitted.
196 238
    ///\param help A help string.
197 239
    ///\param obl Indicate if the option is mandatory.
198 240
    ///\retval ref The value of the argument will be written to this variable.
199 241
    ArgParser &refOption(const std::string &name,
200 242
                    const std::string &help,
201 243
                    int &ref, bool obl=false);
202 244

	
203 245
    ///Add a new floating type option with a storage reference
204 246

	
205 247
    ///Add a new floating type option with a storage reference.
206 248
    ///\param name The name of the option. The leading '-' must be omitted.
207 249
    ///\param help A help string.
208 250
    ///\param obl Indicate if the option is mandatory.
209 251
    ///\retval ref The value of the argument will be written to this variable.
210 252
    ArgParser &refOption(const std::string &name,
211 253
                      const std::string &help,
212 254
                      double &ref, bool obl=false);
213 255

	
214 256
    ///Add a new bool type option with a storage reference
215 257

	
216 258
    ///Add a new bool type option with a storage reference.
217 259
    ///\param name The name of the option. The leading '-' must be omitted.
218 260
    ///\param help A help string.
219 261
    ///\param obl Indicate if the option is mandatory.
220 262
    ///\retval ref The value of the argument will be written to this variable.
221 263
    ///\note A mandatory bool obtion is of very little use.
222 264
    ArgParser &refOption(const std::string &name,
223 265
                      const std::string &help,
224 266
                      bool &ref, bool obl=false);
225 267

	
226 268
    ///Add a new string type option with a storage reference
227 269

	
228 270
    ///Add a new string type option with a storage reference.
229 271
    ///\param name The name of the option. The leading '-' must be omitted.
230 272
    ///\param help A help string.
231 273
    ///\param obl Indicate if the option is mandatory.
232 274
    ///\retval ref The value of the argument will be written to this variable.
233 275
    ArgParser &refOption(const std::string &name,
234 276
                      const std::string &help,
235 277
                      std::string &ref, bool obl=false);
236 278

	
237 279
    ///@}
238 280

	
239 281
    ///\name Option Groups and Synonyms
240 282
    ///
241 283

	
242 284
    ///@{
243 285

	
244 286
    ///Bundle some options into a group
245 287

	
246 288
    /// You can group some option by calling this function repeatedly for each
247 289
    /// option to be grouped with the same groupname.
248 290
    ///\param group The group name.
249 291
    ///\param opt The option name.
250 292
    ArgParser &optionGroup(const std::string &group,
251 293
                           const std::string &opt);
252 294

	
253 295
    ///Make the members of a group exclusive
254 296

	
255 297
    ///If you call this function for a group, than at most one of them can be
256 298
    ///given at the same time.
257 299
    ArgParser &onlyOneGroup(const std::string &group);
258 300

	
259 301
    ///Make a group mandatory
260 302

	
261 303
    ///Using this function, at least one of the members of \c group
262 304
    ///must be given.
263 305
    ArgParser &mandatoryGroup(const std::string &group);
264 306

	
265 307
    ///Create synonym to an option
266 308

	
267 309
    ///With this function you can create a synonym \c syn of the
268 310
    ///option \c opt.
269 311
    ArgParser &synonym(const std::string &syn,
270 312
                           const std::string &opt);
271 313

	
272 314
    ///@}
273 315

	
274 316
  private:
275 317
    void show(std::ostream &os,Opts::const_iterator i) const;
276 318
    void show(std::ostream &os,Groups::const_iterator i) const;
277 319
    void showHelp(Opts::const_iterator i) const;
278 320
    void showHelp(std::vector<OtherArg>::const_iterator i) const;
279 321

	
280 322
    void unknownOpt(std::string arg) const;
281 323

	
282 324
    void requiresValue(std::string arg, OptType t) const;
283 325
    void checkMandatories() const;
284 326

	
285 327
    void shortHelp() const;
286 328
    void showHelp() const;
287 329
  public:
288 330

	
289 331
    ///Start the parsing process
290 332
    ArgParser &parse();
291 333

	
292 334
    /// Synonym for parse()
293 335
    ArgParser &run()
294 336
    {
295 337
      return parse();
296 338
    }
297 339

	
298 340
    ///Give back the command name (the 0th argument)
299 341
    const std::string &commandName() const { return _command_name; }
300 342

	
301 343
    ///Check if an opion has been given to the command.
302 344
    bool given(std::string op) const
303 345
    {
304 346
      Opts::const_iterator i = _opts.find(op);
305 347
      return i!=_opts.end()?i->second.set:false;
306 348
    }
307 349

	
308 350

	
309 351
    ///Magic type for operator[]
310 352

	
311 353
    ///This is the type of the return value of ArgParser::operator[]().
312 354
    ///It automatically converts to \c int, \c double, \c bool or
313 355
    ///\c std::string if the type of the option matches, which is checked
314 356
    ///with an \ref LEMON_ASSERT "assertion" (i.e. it performs runtime
315 357
    ///type checking).
316 358
    class RefType
317 359
    {
318 360
      const ArgParser &_parser;
319 361
      std::string _name;
320 362
    public:
321 363
      ///\e
322 364
      RefType(const ArgParser &p,const std::string &n) :_parser(p),_name(n) {}
323 365
      ///\e
324 366
      operator bool()
325 367
      {
326 368
        Opts::const_iterator i = _parser._opts.find(_name);
327 369
        LEMON_ASSERT(i!=_parser._opts.end(),
328 370
                     std::string()+"Unkown option: '"+_name+"'");
329 371
        LEMON_ASSERT(i->second.type==ArgParser::BOOL,
330 372
                     std::string()+"'"+_name+"' is a bool option");
331 373
        return *(i->second.bool_p);
332 374
      }
333 375
      ///\e
334 376
      operator std::string()
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)