lemon/arg_parser.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Tue, 15 Mar 2011 19:32:21 +0100
changeset 936 ddd3c0d3d9bf
parent 842 c2ff0a365245
child 1179 d7e25df22e88
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@85
    19
#include <lemon/arg_parser.h>
alpar@85
    20
alpar@85
    21
namespace lemon {
alpar@85
    22
alpar@842
    23
  void ArgParser::_terminate(ArgParserException::Reason reason) const
alpar@842
    24
  {
alpar@842
    25
    if(_exit_on_problems)
alpar@842
    26
      exit(1);
alpar@842
    27
    else throw(ArgParserException(reason));
alpar@842
    28
  }
alpar@877
    29
alpar@877
    30
alpar@85
    31
  void ArgParser::_showHelp(void *p)
alpar@85
    32
  {
alpar@85
    33
    (static_cast<ArgParser*>(p))->showHelp();
alpar@842
    34
    (static_cast<ArgParser*>(p))->_terminate(ArgParserException::HELP);
alpar@85
    35
  }
alpar@85
    36
ladanyi@311
    37
  ArgParser::ArgParser(int argc, const char * const *argv)
alpar@842
    38
    :_argc(argc), _argv(argv), _command_name(argv[0]),
alpar@842
    39
    _exit_on_problems(true) {
alpar@85
    40
    funcOption("-help","Print a short help message",_showHelp,this);
alpar@85
    41
    synonym("help","-help");
alpar@85
    42
    synonym("h","-help");
alpar@85
    43
  }
alpar@85
    44
alpar@85
    45
  ArgParser::~ArgParser()
alpar@85
    46
  {
alpar@85
    47
    for(Opts::iterator i=_opts.begin();i!=_opts.end();++i)
alpar@85
    48
      if(i->second.self_delete)
alpar@209
    49
        switch(i->second.type) {
alpar@209
    50
        case BOOL:
alpar@209
    51
          delete i->second.bool_p;
alpar@209
    52
          break;
alpar@209
    53
        case STRING:
alpar@209
    54
          delete i->second.string_p;
alpar@209
    55
          break;
alpar@209
    56
        case DOUBLE:
alpar@209
    57
          delete i->second.double_p;
alpar@209
    58
          break;
alpar@209
    59
        case INTEGER:
alpar@209
    60
          delete i->second.int_p;
alpar@209
    61
          break;
alpar@209
    62
        case UNKNOWN:
alpar@209
    63
          break;
alpar@209
    64
        case FUNC:
alpar@209
    65
          break;
alpar@209
    66
        }
alpar@85
    67
  }
alpar@209
    68
alpar@85
    69
alpar@85
    70
  ArgParser &ArgParser::intOption(const std::string &name,
alpar@209
    71
                               const std::string &help,
alpar@209
    72
                               int value, bool obl)
alpar@85
    73
  {
alpar@85
    74
    ParData p;
alpar@85
    75
    p.int_p=new int(value);
alpar@85
    76
    p.self_delete=true;
alpar@85
    77
    p.help=help;
alpar@85
    78
    p.type=INTEGER;
alpar@85
    79
    p.mandatory=obl;
alpar@85
    80
    _opts[name]=p;
alpar@85
    81
    return *this;
alpar@85
    82
  }
alpar@85
    83
alpar@85
    84
  ArgParser &ArgParser::doubleOption(const std::string &name,
alpar@209
    85
                               const std::string &help,
alpar@209
    86
                               double value, bool obl)
alpar@85
    87
  {
alpar@85
    88
    ParData p;
alpar@85
    89
    p.double_p=new double(value);
alpar@85
    90
    p.self_delete=true;
alpar@85
    91
    p.help=help;
alpar@85
    92
    p.type=DOUBLE;
alpar@85
    93
    p.mandatory=obl;
alpar@85
    94
    _opts[name]=p;
alpar@85
    95
    return *this;
alpar@85
    96
  }
alpar@85
    97
alpar@85
    98
  ArgParser &ArgParser::boolOption(const std::string &name,
alpar@209
    99
                               const std::string &help,
alpar@209
   100
                               bool value, bool obl)
alpar@85
   101
  {
alpar@85
   102
    ParData p;
alpar@85
   103
    p.bool_p=new bool(value);
alpar@85
   104
    p.self_delete=true;
alpar@85
   105
    p.help=help;
alpar@85
   106
    p.type=BOOL;
alpar@85
   107
    p.mandatory=obl;
alpar@85
   108
    _opts[name]=p;
alpar@85
   109
    return *this;
alpar@85
   110
  }
alpar@85
   111
alpar@85
   112
  ArgParser &ArgParser::stringOption(const std::string &name,
alpar@209
   113
                               const std::string &help,
alpar@209
   114
                               std::string value, bool obl)
alpar@85
   115
  {
alpar@85
   116
    ParData p;
alpar@85
   117
    p.string_p=new std::string(value);
alpar@85
   118
    p.self_delete=true;
alpar@85
   119
    p.help=help;
alpar@85
   120
    p.type=STRING;
alpar@85
   121
    p.mandatory=obl;
alpar@85
   122
    _opts[name]=p;
alpar@85
   123
    return *this;
alpar@85
   124
  }
alpar@85
   125
alpar@85
   126
  ArgParser &ArgParser::refOption(const std::string &name,
alpar@209
   127
                               const std::string &help,
alpar@209
   128
                               int &ref, bool obl)
alpar@85
   129
  {
alpar@85
   130
    ParData p;
alpar@85
   131
    p.int_p=&ref;
alpar@85
   132
    p.self_delete=false;
alpar@85
   133
    p.help=help;
alpar@85
   134
    p.type=INTEGER;
alpar@85
   135
    p.mandatory=obl;
alpar@85
   136
    _opts[name]=p;
alpar@85
   137
    return *this;
alpar@85
   138
  }
alpar@85
   139
alpar@85
   140
  ArgParser &ArgParser::refOption(const std::string &name,
alpar@85
   141
                                  const std::string &help,
alpar@85
   142
                                  double &ref, bool obl)
alpar@85
   143
  {
alpar@85
   144
    ParData p;
alpar@85
   145
    p.double_p=&ref;
alpar@85
   146
    p.self_delete=false;
alpar@85
   147
    p.help=help;
alpar@85
   148
    p.type=DOUBLE;
alpar@85
   149
    p.mandatory=obl;
alpar@85
   150
    _opts[name]=p;
alpar@85
   151
    return *this;
alpar@85
   152
  }
alpar@85
   153
alpar@85
   154
  ArgParser &ArgParser::refOption(const std::string &name,
alpar@85
   155
                                  const std::string &help,
alpar@85
   156
                                  bool &ref, bool obl)
alpar@85
   157
  {
alpar@85
   158
    ParData p;
alpar@85
   159
    p.bool_p=&ref;
alpar@85
   160
    p.self_delete=false;
alpar@85
   161
    p.help=help;
alpar@85
   162
    p.type=BOOL;
alpar@85
   163
    p.mandatory=obl;
alpar@85
   164
    _opts[name]=p;
alpar@85
   165
alpar@85
   166
    ref = false;
alpar@85
   167
alpar@85
   168
    return *this;
alpar@85
   169
  }
alpar@85
   170
alpar@85
   171
  ArgParser &ArgParser::refOption(const std::string &name,
alpar@209
   172
                               const std::string &help,
alpar@209
   173
                               std::string &ref, bool obl)
alpar@85
   174
  {
alpar@85
   175
    ParData p;
alpar@85
   176
    p.string_p=&ref;
alpar@85
   177
    p.self_delete=false;
alpar@85
   178
    p.help=help;
alpar@85
   179
    p.type=STRING;
alpar@85
   180
    p.mandatory=obl;
alpar@85
   181
    _opts[name]=p;
alpar@85
   182
    return *this;
alpar@85
   183
  }
alpar@85
   184
alpar@85
   185
  ArgParser &ArgParser::funcOption(const std::string &name,
alpar@209
   186
                               const std::string &help,
alpar@209
   187
                               void (*func)(void *),void *data)
alpar@85
   188
  {
alpar@85
   189
    ParData p;
alpar@85
   190
    p.func_p.p=func;
alpar@85
   191
    p.func_p.data=data;
alpar@85
   192
    p.self_delete=false;
alpar@85
   193
    p.help=help;
alpar@85
   194
    p.type=FUNC;
alpar@85
   195
    p.mandatory=false;
alpar@85
   196
    _opts[name]=p;
alpar@85
   197
    return *this;
alpar@85
   198
  }
alpar@85
   199
alpar@85
   200
  ArgParser &ArgParser::optionGroup(const std::string &group,
alpar@209
   201
                                    const std::string &opt)
alpar@85
   202
  {
alpar@85
   203
    Opts::iterator i = _opts.find(opt);
alpar@85
   204
    LEMON_ASSERT(i!=_opts.end(), "Unknown option: '"+opt+"'");
alpar@209
   205
    LEMON_ASSERT(!(i->second.ingroup),
alpar@85
   206
                 "Option already in option group: '"+opt+"'");
alpar@85
   207
    GroupData &g=_groups[group];
alpar@85
   208
    g.opts.push_back(opt);
alpar@85
   209
    i->second.ingroup=true;
alpar@85
   210
    return *this;
alpar@85
   211
  }
alpar@85
   212
alpar@85
   213
  ArgParser &ArgParser::onlyOneGroup(const std::string &group)
alpar@85
   214
  {
alpar@85
   215
    GroupData &g=_groups[group];
alpar@85
   216
    g.only_one=true;
alpar@85
   217
    return *this;
alpar@85
   218
  }
alpar@85
   219
alpar@85
   220
  ArgParser &ArgParser::synonym(const std::string &syn,
alpar@209
   221
                                const std::string &opt)
alpar@85
   222
  {
alpar@85
   223
    Opts::iterator o = _opts.find(opt);
alpar@85
   224
    Opts::iterator s = _opts.find(syn);
alpar@85
   225
    LEMON_ASSERT(o!=_opts.end(), "Unknown option: '"+opt+"'");
alpar@85
   226
    LEMON_ASSERT(s==_opts.end(), "Option already used: '"+syn+"'");
alpar@85
   227
    ParData p;
alpar@85
   228
    p.help=opt;
alpar@85
   229
    p.mandatory=false;
alpar@85
   230
    p.syn=true;
alpar@85
   231
    _opts[syn]=p;
alpar@85
   232
    o->second.has_syn=true;
alpar@85
   233
    return *this;
alpar@85
   234
  }
alpar@85
   235
alpar@85
   236
  ArgParser &ArgParser::mandatoryGroup(const std::string &group)
alpar@85
   237
  {
alpar@85
   238
    GroupData &g=_groups[group];
alpar@85
   239
    g.mandatory=true;
alpar@85
   240
    return *this;
alpar@85
   241
  }
alpar@85
   242
alpar@85
   243
  ArgParser &ArgParser::other(const std::string &name,
alpar@209
   244
                              const std::string &help)
alpar@85
   245
  {
alpar@85
   246
    _others_help.push_back(OtherArg(name,help));
alpar@85
   247
    return *this;
alpar@85
   248
  }
alpar@85
   249
alpar@214
   250
  void ArgParser::show(std::ostream &os,Opts::const_iterator i) const
alpar@85
   251
  {
alpar@85
   252
    os << "-" << i->first;
alpar@85
   253
    if(i->second.has_syn)
alpar@214
   254
      for(Opts::const_iterator j=_opts.begin();j!=_opts.end();++j)
alpar@209
   255
        if(j->second.syn&&j->second.help==i->first)
alpar@209
   256
          os << "|-" << j->first;
alpar@85
   257
    switch(i->second.type) {
alpar@85
   258
    case STRING:
alpar@85
   259
      os << " str";
alpar@85
   260
      break;
alpar@85
   261
    case INTEGER:
alpar@85
   262
      os << " int";
alpar@85
   263
      break;
alpar@85
   264
    case DOUBLE:
alpar@85
   265
      os << " num";
alpar@85
   266
      break;
alpar@85
   267
    default:
alpar@85
   268
      break;
alpar@85
   269
    }
alpar@85
   270
  }
alpar@85
   271
alpar@214
   272
  void ArgParser::show(std::ostream &os,Groups::const_iterator i) const
alpar@85
   273
  {
alpar@214
   274
    GroupData::Opts::const_iterator o=i->second.opts.begin();
alpar@85
   275
    while(o!=i->second.opts.end()) {
alpar@85
   276
      show(os,_opts.find(*o));
alpar@85
   277
      ++o;
alpar@85
   278
      if(o!=i->second.opts.end()) os<<'|';
alpar@85
   279
    }
alpar@85
   280
  }
alpar@209
   281
alpar@214
   282
  void ArgParser::showHelp(Opts::const_iterator i) const
alpar@85
   283
  {
alpar@85
   284
    if(i->second.help.size()==0||i->second.syn) return;
alpar@85
   285
    std::cerr << "  ";
alpar@85
   286
    show(std::cerr,i);
alpar@85
   287
    std::cerr << std::endl;
alpar@85
   288
    std::cerr << "     " << i->second.help << std::endl;
alpar@85
   289
  }
alpar@214
   290
  void ArgParser::showHelp(std::vector<ArgParser::OtherArg>::const_iterator i)
alpar@214
   291
    const
alpar@85
   292
  {
alpar@85
   293
    if(i->help.size()==0) return;
alpar@85
   294
    std::cerr << "  " << i->name << std::endl
alpar@209
   295
              << "     " << i->help << std::endl;
alpar@85
   296
  }
alpar@209
   297
alpar@214
   298
  void ArgParser::shortHelp() const
alpar@85
   299
  {
alpar@85
   300
    const unsigned int LINE_LEN=77;
alpar@85
   301
    const std::string indent("    ");
alpar@85
   302
    std::cerr << "Usage:\n  " << _command_name;
alpar@85
   303
    int pos=_command_name.size()+2;
alpar@214
   304
    for(Groups::const_iterator g=_groups.begin();g!=_groups.end();++g) {
alpar@85
   305
      std::ostringstream cstr;
alpar@85
   306
      cstr << ' ';
alpar@85
   307
      if(!g->second.mandatory) cstr << '[';
alpar@85
   308
      show(cstr,g);
alpar@85
   309
      if(!g->second.mandatory) cstr << ']';
alpar@85
   310
      if(pos+cstr.str().size()>LINE_LEN) {
alpar@209
   311
        std::cerr << std::endl << indent;
alpar@209
   312
        pos=indent.size();
alpar@85
   313
      }
alpar@85
   314
      std::cerr << cstr.str();
alpar@85
   315
      pos+=cstr.str().size();
alpar@85
   316
    }
alpar@214
   317
    for(Opts::const_iterator i=_opts.begin();i!=_opts.end();++i)
alpar@85
   318
      if(!i->second.ingroup&&!i->second.syn) {
alpar@209
   319
        std::ostringstream cstr;
alpar@209
   320
        cstr << ' ';
alpar@209
   321
        if(!i->second.mandatory) cstr << '[';
alpar@209
   322
        show(cstr,i);
alpar@209
   323
        if(!i->second.mandatory) cstr << ']';
alpar@209
   324
        if(pos+cstr.str().size()>LINE_LEN) {
alpar@209
   325
          std::cerr << std::endl << indent;
alpar@209
   326
          pos=indent.size();
alpar@209
   327
        }
alpar@209
   328
        std::cerr << cstr.str();
alpar@209
   329
        pos+=cstr.str().size();
alpar@85
   330
      }
alpar@214
   331
    for(std::vector<OtherArg>::const_iterator i=_others_help.begin();
alpar@209
   332
        i!=_others_help.end();++i)
alpar@85
   333
      {
alpar@209
   334
        std::ostringstream cstr;
alpar@209
   335
        cstr << ' ' << i->name;
alpar@209
   336
alpar@209
   337
        if(pos+cstr.str().size()>LINE_LEN) {
alpar@209
   338
          std::cerr << std::endl << indent;
alpar@209
   339
          pos=indent.size();
alpar@209
   340
        }
alpar@209
   341
        std::cerr << cstr.str();
alpar@209
   342
        pos+=cstr.str().size();
alpar@85
   343
      }
alpar@85
   344
    std::cerr << std::endl;
alpar@85
   345
  }
alpar@209
   346
alpar@214
   347
  void ArgParser::showHelp() const
alpar@85
   348
  {
alpar@85
   349
    shortHelp();
alpar@85
   350
    std::cerr << "Where:\n";
alpar@214
   351
    for(std::vector<OtherArg>::const_iterator i=_others_help.begin();
alpar@209
   352
        i!=_others_help.end();++i) showHelp(i);
alpar@214
   353
    for(Opts::const_iterator i=_opts.begin();i!=_opts.end();++i) showHelp(i);
alpar@842
   354
    _terminate(ArgParserException::HELP);
alpar@85
   355
  }
alpar@209
   356
alpar@209
   357
alpar@214
   358
  void ArgParser::unknownOpt(std::string arg) const
alpar@85
   359
  {
alpar@85
   360
    std::cerr << "\nUnknown option: " << arg << "\n";
alpar@85
   361
    std::cerr << "\nType '" << _command_name <<
alpar@85
   362
      " --help' to obtain a short summary on the usage.\n\n";
alpar@842
   363
    _terminate(ArgParserException::UNKNOWN_OPT);
alpar@85
   364
  }
alpar@209
   365
alpar@214
   366
  void ArgParser::requiresValue(std::string arg, OptType t) const
alpar@85
   367
  {
alpar@85
   368
    std::cerr << "Argument '" << arg << "' requires a";
alpar@85
   369
    switch(t) {
alpar@85
   370
    case STRING:
alpar@85
   371
      std::cerr << " string";
alpar@85
   372
      break;
alpar@85
   373
    case INTEGER:
alpar@85
   374
      std::cerr << "n integer";
alpar@85
   375
      break;
alpar@85
   376
    case DOUBLE:
alpar@85
   377
      std::cerr << " floating point";
alpar@85
   378
      break;
alpar@85
   379
    default:
alpar@85
   380
      break;
alpar@85
   381
    }
alpar@85
   382
    std::cerr << " value\n\n";
alpar@85
   383
    showHelp();
alpar@85
   384
  }
alpar@209
   385
alpar@85
   386
alpar@214
   387
  void ArgParser::checkMandatories() const
alpar@85
   388
  {
alpar@85
   389
    bool ok=true;
alpar@214
   390
    for(Opts::const_iterator i=_opts.begin();i!=_opts.end();++i)
alpar@209
   391
      if(i->second.mandatory&&!i->second.set)
alpar@209
   392
        {
alpar@209
   393
          if(ok)
alpar@209
   394
            std::cerr << _command_name
alpar@209
   395
                      << ": The following mandatory arguments are missing.\n";
alpar@209
   396
          ok=false;
alpar@209
   397
          showHelp(i);
alpar@209
   398
        }
alpar@214
   399
    for(Groups::const_iterator i=_groups.begin();i!=_groups.end();++i)
alpar@85
   400
      if(i->second.mandatory||i->second.only_one)
alpar@209
   401
        {
alpar@209
   402
          int set=0;
alpar@214
   403
          for(GroupData::Opts::const_iterator o=i->second.opts.begin();
alpar@209
   404
              o!=i->second.opts.end();++o)
alpar@209
   405
            if(_opts.find(*o)->second.set) ++set;
alpar@209
   406
          if(i->second.mandatory&&!set) {
alpar@210
   407
            std::cerr << _command_name <<
alpar@210
   408
              ": At least one of the following arguments is mandatory.\n";
alpar@209
   409
            ok=false;
alpar@214
   410
            for(GroupData::Opts::const_iterator o=i->second.opts.begin();
alpar@209
   411
                o!=i->second.opts.end();++o)
alpar@209
   412
              showHelp(_opts.find(*o));
alpar@209
   413
          }
alpar@209
   414
          if(i->second.only_one&&set>1) {
alpar@210
   415
            std::cerr << _command_name <<
alpar@210
   416
              ": At most one of the following arguments can be given.\n";
alpar@209
   417
            ok=false;
alpar@214
   418
            for(GroupData::Opts::const_iterator o=i->second.opts.begin();
alpar@209
   419
                o!=i->second.opts.end();++o)
alpar@209
   420
              showHelp(_opts.find(*o));
alpar@209
   421
          }
alpar@209
   422
        }
alpar@85
   423
    if(!ok) {
alpar@85
   424
      std::cerr << "\nType '" << _command_name <<
alpar@209
   425
        " --help' to obtain a short summary on the usage.\n\n";
alpar@842
   426
      _terminate(ArgParserException::INVALID_OPT);
alpar@85
   427
    }
alpar@85
   428
  }
alpar@85
   429
alpar@85
   430
  ArgParser &ArgParser::parse()
alpar@85
   431
  {
alpar@85
   432
    for(int ar=1; ar<_argc; ++ar) {
alpar@85
   433
      std::string arg(_argv[ar]);
alpar@85
   434
      if (arg[0] != '-' || arg.size() == 1) {
alpar@209
   435
        _file_args.push_back(arg);
alpar@85
   436
      }
alpar@85
   437
      else {
alpar@209
   438
        Opts::iterator i = _opts.find(arg.substr(1));
alpar@209
   439
        if(i==_opts.end()) unknownOpt(arg);
alpar@209
   440
        else {
alpar@209
   441
          if(i->second.syn) i=_opts.find(i->second.help);
alpar@209
   442
          ParData &p(i->second);
alpar@209
   443
          if (p.type==BOOL) *p.bool_p=true;
alpar@209
   444
          else if (p.type==FUNC) p.func_p.p(p.func_p.data);
alpar@209
   445
          else if(++ar==_argc) requiresValue(arg, p.type);
alpar@209
   446
          else {
alpar@209
   447
            std::string val(_argv[ar]);
alpar@209
   448
            std::istringstream vals(val);
alpar@209
   449
            switch(p.type) {
alpar@209
   450
            case STRING:
alpar@209
   451
              *p.string_p=val;
alpar@209
   452
              break;
alpar@209
   453
            case INTEGER:
alpar@209
   454
              vals >> *p.int_p;
alpar@209
   455
              break;
alpar@209
   456
            case DOUBLE:
alpar@209
   457
              vals >> *p.double_p;
alpar@209
   458
              break;
alpar@209
   459
            default:
alpar@209
   460
              break;
alpar@209
   461
            }
alpar@209
   462
            if(p.type!=STRING&&(!vals||!vals.eof()))
alpar@209
   463
              requiresValue(arg, p.type);
alpar@209
   464
          }
alpar@209
   465
          p.set = true;
alpar@209
   466
        }
alpar@85
   467
      }
alpar@85
   468
    }
alpar@85
   469
    checkMandatories();
alpar@85
   470
alpar@85
   471
    return *this;
alpar@209
   472
  }
alpar@85
   473
alpar@85
   474
}