alpar@209: /* -*- mode: C++; indent-tabs-mode: nil; -*- alpar@85: * alpar@209: * This file is a part of LEMON, a generic C++ optimization library. alpar@85: * alpar@463: * Copyright (C) 2003-2009 alpar@85: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport alpar@85: * (Egervary Research Group on Combinatorial Optimization, EGRES). alpar@85: * alpar@85: * Permission to use, modify and distribute this software is granted alpar@85: * provided that this copyright notice appears in all copies. For alpar@85: * precise terms see the accompanying LICENSE file. alpar@85: * alpar@85: * This software is provided "AS IS" with no warranty of any kind, alpar@85: * express or implied, and with no claim as to its suitability for any alpar@85: * purpose. alpar@85: * alpar@85: */ alpar@85: alpar@261: #ifndef LEMON_ARG_PARSER_H alpar@261: #define LEMON_ARG_PARSER_H alpar@85: alpar@85: #include alpar@85: #include alpar@85: #include alpar@85: #include alpar@85: #include alpar@85: #include alpar@85: #include deba@108: #include alpar@85: alpar@85: ///\ingroup misc alpar@85: ///\file kpeter@88: ///\brief A tool to parse command line arguments. alpar@85: alpar@85: namespace lemon { alpar@85: alpar@915: ///Exception used by ArgParser alpar@915: class ArgParserException : public Exception { alpar@915: public: alpar@915: enum Reason { alpar@915: HELP, /// --help option was given alpar@915: UNKNOWN_OPT, /// Unknown option was given alpar@915: INVALID_OPT /// Invalid combination of options alpar@915: }; alpar@915: alpar@915: private: alpar@915: Reason _reason; alpar@915: alpar@915: public: alpar@915: ///Constructor alpar@915: ArgParserException(Reason r) throw() : _reason(r) {} alpar@915: ///Virtual destructor alpar@915: virtual ~ArgParserException() throw() {} alpar@915: ///A short description of the exception alpar@915: virtual const char* what() const throw() { alpar@915: switch(_reason) alpar@915: { alpar@915: case HELP: alpar@915: return "lemon::ArgParseException: ask for help"; alpar@915: break; alpar@915: case UNKNOWN_OPT: alpar@915: return "lemon::ArgParseException: unknown option"; alpar@915: break; alpar@915: case INVALID_OPT: alpar@915: return "lemon::ArgParseException: invalid combination of options"; alpar@915: break; alpar@915: } alpar@915: return ""; alpar@915: } alpar@915: ///Return the reason for the failure alpar@915: Reason reason() const {return _reason; } alpar@915: }; alpar@915: alpar@915: alpar@85: ///Command line arguments parser alpar@85: alpar@85: ///\ingroup misc kpeter@88: ///Command line arguments parser. alpar@85: /// kpeter@88: ///For a complete example see the \ref arg_parser_demo.cc demo file. alpar@85: class ArgParser { alpar@209: alpar@85: static void _showHelp(void *p); alpar@85: protected: alpar@209: alpar@85: int _argc; ladanyi@311: const char * const *_argv; alpar@209: alpar@85: enum OptType { UNKNOWN=0, BOOL=1, STRING=2, DOUBLE=3, INTEGER=4, FUNC=5 }; alpar@209: alpar@85: class ParData { alpar@85: public: alpar@85: union { alpar@209: bool *bool_p; alpar@209: int *int_p; alpar@209: double *double_p; alpar@209: std::string *string_p; alpar@209: struct { alpar@209: void (*p)(void *); alpar@209: void *data; alpar@209: } func_p; alpar@209: alpar@85: }; alpar@85: std::string help; alpar@85: bool mandatory; alpar@85: OptType type; alpar@85: bool set; alpar@85: bool ingroup; alpar@85: bool has_syn; alpar@85: bool syn; alpar@85: bool self_delete; alpar@85: ParData() : mandatory(false), type(UNKNOWN), set(false), ingroup(false), alpar@209: has_syn(false), syn(false), self_delete(false) {} alpar@85: }; alpar@85: alpar@85: typedef std::map Opts; alpar@85: Opts _opts; alpar@85: alpar@209: class GroupData alpar@85: { alpar@85: public: alpar@85: typedef std::list Opts; alpar@85: Opts opts; alpar@85: bool only_one; alpar@85: bool mandatory; alpar@85: GroupData() :only_one(false), mandatory(false) {} alpar@85: }; alpar@209: alpar@85: typedef std::map Groups; alpar@85: Groups _groups; alpar@85: alpar@85: struct OtherArg alpar@85: { alpar@85: std::string name; alpar@85: std::string help; alpar@85: OtherArg(std::string n, std::string h) :name(n), help(h) {} alpar@85: alpar@85: }; alpar@209: alpar@85: std::vector _others_help; alpar@85: std::vector _file_args; alpar@85: std::string _command_name; alpar@87: alpar@915: alpar@87: private: alpar@87: //Bind a function to an option. alpar@87: alpar@87: //\param name The name of the option. The leading '-' must be omitted. alpar@87: //\param help A help string. alpar@87: //\retval func The function to be called when the option is given. It alpar@87: // must be of type "void f(void *)" alpar@87: //\param data Data to be passed to \c func alpar@87: ArgParser &funcOption(const std::string &name, alpar@209: const std::string &help, alpar@209: void (*func)(void *),void *data); alpar@209: alpar@915: bool _exit_on_problems; alpar@915: alpar@915: void _terminate(ArgParserException::Reason reason) const; alpar@915: alpar@85: public: alpar@85: kpeter@204: ///Constructor ladanyi@311: ArgParser(int argc, const char * const *argv); alpar@85: alpar@85: ~ArgParser(); alpar@85: kpeter@204: ///\name Options kpeter@204: /// kpeter@204: kpeter@204: ///@{ kpeter@204: alpar@85: ///Add a new integer type option alpar@85: kpeter@204: ///Add a new integer type option. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. kpeter@95: ///\param value A default value for the option. alpar@85: ///\param obl Indicate if the option is mandatory. alpar@85: ArgParser &intOption(const std::string &name, alpar@209: const std::string &help, alpar@209: int value=0, bool obl=false); alpar@85: alpar@86: ///Add a new floating point type option alpar@85: kpeter@204: ///Add a new floating point type option. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. kpeter@95: ///\param value A default value for the option. alpar@85: ///\param obl Indicate if the option is mandatory. alpar@85: ArgParser &doubleOption(const std::string &name, alpar@209: const std::string &help, alpar@209: double value=0, bool obl=false); alpar@85: alpar@85: ///Add a new bool type option alpar@85: kpeter@204: ///Add a new bool type option. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. kpeter@95: ///\param value A default value for the option. alpar@85: ///\param obl Indicate if the option is mandatory. kpeter@95: ///\note A mandatory bool obtion is of very little use. alpar@85: ArgParser &boolOption(const std::string &name, alpar@209: const std::string &help, alpar@209: bool value=false, bool obl=false); alpar@85: alpar@85: ///Add a new string type option alpar@85: kpeter@204: ///Add a new string type option. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. kpeter@95: ///\param value A default value for the option. alpar@85: ///\param obl Indicate if the option is mandatory. alpar@85: ArgParser &stringOption(const std::string &name, alpar@209: const std::string &help, alpar@209: std::string value="", bool obl=false); alpar@85: kpeter@204: ///Give help string for non-parsed arguments. kpeter@204: kpeter@204: ///With this function you can give help string for non-parsed arguments. kpeter@204: ///The parameter \c name will be printed in the short usage line, while kpeter@204: ///\c help gives a more detailed description. kpeter@204: ArgParser &other(const std::string &name, alpar@209: const std::string &help=""); alpar@209: kpeter@204: ///@} kpeter@204: kpeter@204: ///\name Options with External Storage alpar@85: ///Using this functions, the value of the option will be directly written alpar@85: ///into a variable once the option appears in the command line. alpar@85: alpar@85: ///@{ alpar@85: alpar@85: ///Add a new integer type option with a storage reference alpar@85: kpeter@204: ///Add a new integer type option with a storage reference. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. alpar@90: ///\param obl Indicate if the option is mandatory. alpar@85: ///\retval ref The value of the argument will be written to this variable. alpar@85: ArgParser &refOption(const std::string &name, alpar@209: const std::string &help, alpar@209: int &ref, bool obl=false); alpar@85: alpar@85: ///Add a new floating type option with a storage reference alpar@85: kpeter@204: ///Add a new floating type option with a storage reference. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. alpar@90: ///\param obl Indicate if the option is mandatory. alpar@85: ///\retval ref The value of the argument will be written to this variable. alpar@85: ArgParser &refOption(const std::string &name, alpar@209: const std::string &help, alpar@209: double &ref, bool obl=false); alpar@85: alpar@85: ///Add a new bool type option with a storage reference alpar@85: kpeter@204: ///Add a new bool type option with a storage reference. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. alpar@90: ///\param obl Indicate if the option is mandatory. alpar@85: ///\retval ref The value of the argument will be written to this variable. kpeter@95: ///\note A mandatory bool obtion is of very little use. alpar@85: ArgParser &refOption(const std::string &name, alpar@209: const std::string &help, alpar@209: bool &ref, bool obl=false); alpar@85: alpar@85: ///Add a new string type option with a storage reference alpar@85: kpeter@204: ///Add a new string type option with a storage reference. alpar@85: ///\param name The name of the option. The leading '-' must be omitted. alpar@85: ///\param help A help string. kpeter@95: ///\param obl Indicate if the option is mandatory. alpar@85: ///\retval ref The value of the argument will be written to this variable. alpar@85: ArgParser &refOption(const std::string &name, alpar@209: const std::string &help, alpar@209: std::string &ref, bool obl=false); alpar@209: alpar@85: ///@} alpar@85: alpar@85: ///\name Option Groups and Synonyms alpar@85: /// alpar@209: alpar@85: ///@{ alpar@85: kpeter@204: ///Bundle some options into a group alpar@85: alpar@85: /// You can group some option by calling this function repeatedly for each kpeter@88: /// option to be grouped with the same groupname. kpeter@88: ///\param group The group name. kpeter@88: ///\param opt The option name. alpar@85: ArgParser &optionGroup(const std::string &group, alpar@209: const std::string &opt); alpar@85: alpar@85: ///Make the members of a group exclusive alpar@85: alpar@85: ///If you call this function for a group, than at most one of them can be kpeter@204: ///given at the same time. alpar@85: ArgParser &onlyOneGroup(const std::string &group); alpar@209: alpar@85: ///Make a group mandatory alpar@85: alpar@85: ///Using this function, at least one of the members of \c group alpar@85: ///must be given. alpar@85: ArgParser &mandatoryGroup(const std::string &group); alpar@209: alpar@85: ///Create synonym to an option alpar@85: kpeter@88: ///With this function you can create a synonym \c syn of the alpar@85: ///option \c opt. alpar@85: ArgParser &synonym(const std::string &syn, alpar@209: const std::string &opt); alpar@209: alpar@85: ///@} alpar@85: alpar@214: private: alpar@214: void show(std::ostream &os,Opts::const_iterator i) const; alpar@214: void show(std::ostream &os,Groups::const_iterator i) const; alpar@214: void showHelp(Opts::const_iterator i) const; alpar@214: void showHelp(std::vector::const_iterator i) const; alpar@85: alpar@214: void unknownOpt(std::string arg) const; alpar@85: alpar@214: void requiresValue(std::string arg, OptType t) const; alpar@214: void checkMandatories() const; alpar@214: alpar@214: void shortHelp() const; alpar@214: void showHelp() const; alpar@214: public: alpar@209: alpar@85: ///Start the parsing process alpar@85: ArgParser &parse(); alpar@85: alpar@85: /// Synonym for parse() alpar@209: ArgParser &run() alpar@85: { alpar@85: return parse(); alpar@85: } alpar@209: kpeter@204: ///Give back the command name (the 0th argument) alpar@214: const std::string &commandName() const { return _command_name; } kpeter@204: alpar@85: ///Check if an opion has been given to the command. alpar@214: bool given(std::string op) const alpar@85: { alpar@214: Opts::const_iterator i = _opts.find(op); alpar@85: return i!=_opts.end()?i->second.set:false; alpar@85: } alpar@85: alpar@85: alpar@85: ///Magic type for operator[] alpar@209: alpar@85: ///This is the type of the return value of ArgParser::operator[](). kpeter@88: ///It automatically converts to \c int, \c double, \c bool or deba@290: ///\c std::string if the type of the option matches, which is checked deba@290: ///with an \ref LEMON_ASSERT "assertion" (i.e. it performs runtime deba@290: ///type checking). alpar@209: class RefType alpar@85: { alpar@214: const ArgParser &_parser; alpar@85: std::string _name; alpar@85: public: alpar@85: ///\e alpar@214: RefType(const ArgParser &p,const std::string &n) :_parser(p),_name(n) {} alpar@85: ///\e alpar@209: operator bool() alpar@85: { alpar@214: Opts::const_iterator i = _parser._opts.find(_name); alpar@209: LEMON_ASSERT(i!=_parser._opts.end(), alpar@209: std::string()+"Unkown option: '"+_name+"'"); alpar@209: LEMON_ASSERT(i->second.type==ArgParser::BOOL, alpar@209: std::string()+"'"+_name+"' is a bool option"); alpar@209: return *(i->second.bool_p); alpar@85: } alpar@85: ///\e alpar@85: operator std::string() alpar@85: { alpar@214: Opts::const_iterator i = _parser._opts.find(_name); alpar@209: LEMON_ASSERT(i!=_parser._opts.end(), alpar@209: std::string()+"Unkown option: '"+_name+"'"); alpar@209: LEMON_ASSERT(i->second.type==ArgParser::STRING, alpar@209: std::string()+"'"+_name+"' is a string option"); alpar@209: return *(i->second.string_p); alpar@85: } alpar@85: ///\e alpar@209: operator double() alpar@85: { alpar@214: Opts::const_iterator i = _parser._opts.find(_name); alpar@209: LEMON_ASSERT(i!=_parser._opts.end(), alpar@209: std::string()+"Unkown option: '"+_name+"'"); alpar@209: LEMON_ASSERT(i->second.type==ArgParser::DOUBLE || alpar@209: i->second.type==ArgParser::INTEGER, alpar@209: std::string()+"'"+_name+"' is a floating point option"); alpar@209: return i->second.type==ArgParser::DOUBLE ? alpar@209: *(i->second.double_p) : *(i->second.int_p); alpar@85: } alpar@85: ///\e alpar@209: operator int() alpar@85: { alpar@214: Opts::const_iterator i = _parser._opts.find(_name); alpar@209: LEMON_ASSERT(i!=_parser._opts.end(), alpar@209: std::string()+"Unkown option: '"+_name+"'"); alpar@209: LEMON_ASSERT(i->second.type==ArgParser::INTEGER, alpar@209: std::string()+"'"+_name+"' is an integer option"); alpar@209: return *(i->second.int_p); alpar@85: } alpar@85: alpar@85: }; alpar@85: alpar@85: ///Give back the value of an option alpar@209: kpeter@88: ///Give back the value of an option. alpar@85: ///\sa RefType alpar@214: RefType operator[](const std::string &n) const alpar@85: { alpar@85: return RefType(*this, n); alpar@209: } kpeter@204: kpeter@204: ///Give back the non-option type arguments. kpeter@204: kpeter@204: ///Give back a reference to a vector consisting of the program arguments kpeter@204: ///not starting with a '-' character. alpar@214: const std::vector &files() const { return _file_args; } alpar@209: alpar@915: ///Throw instead of exit in case of problems alpar@915: void throwOnProblems() alpar@915: { alpar@915: _exit_on_problems=false; alpar@915: } alpar@85: }; alpar@85: } alpar@85: alpar@261: #endif // LEMON_ARG_PARSER_H