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