alpar@85: /* -*- C++ -*-
alpar@85:  *
alpar@85:  * This file is a part of LEMON, a generic C++ optimization library
alpar@85:  *
alpar@85:  * Copyright (C) 2003-2008
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@85: #ifndef LEMON_ARG_PARSER
alpar@85: #define LEMON_ARG_PARSER
alpar@85: 
alpar@85: #include <vector>
alpar@85: #include <map>
alpar@85: #include <list>
alpar@85: #include <string>
alpar@85: #include <iostream>
alpar@85: #include <sstream>
alpar@85: #include <algorithm>
deba@108: #include <lemon/assert.h>
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@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@85:     
alpar@85:     static void _showHelp(void *p);
alpar@85:   protected:
alpar@85:     
alpar@85:     int _argc;
alpar@85:     const char **_argv;
alpar@85:     
alpar@85:     enum OptType { UNKNOWN=0, BOOL=1, STRING=2, DOUBLE=3, INTEGER=4, FUNC=5 };
alpar@85:     
alpar@85:     class ParData {
alpar@85:     public:
alpar@85:       union {
alpar@85: 	bool *bool_p;
alpar@85: 	int *int_p;
alpar@85: 	double *double_p;
alpar@85: 	std::string *string_p;
alpar@85: 	struct {
alpar@85: 	  void (*p)(void *);
alpar@85: 	  void *data;
alpar@85: 	} func_p;
alpar@85: 	  
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@85: 		  has_syn(false), syn(false), self_delete(false) {}
alpar@85:     };
alpar@85: 
alpar@85:     typedef std::map<std::string,ParData> Opts;
alpar@85:     Opts _opts;
alpar@85: 
alpar@85:     class GroupData 
alpar@85:     {
alpar@85:     public:
alpar@85:       typedef std::list<std::string> 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@85:       
alpar@85:     typedef std::map<std::string,GroupData> 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@85:       
alpar@85:     std::vector<OtherArg> _others_help;
alpar@85:     std::vector<std::string> _file_args;
alpar@85:     std::string _command_name;
alpar@87: 
alpar@87:     
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@87: 		    const std::string &help,
alpar@87: 		    void (*func)(void *),void *data);
alpar@85:     
alpar@85:   public:
alpar@85: 
alpar@85:     ///\e
alpar@85:     ArgParser(int argc, const char **argv);
alpar@85: 
alpar@85:     ~ArgParser();
alpar@85: 
alpar@85:     ///Add a new integer type option
alpar@85: 
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@85: 		    const std::string &help,
alpar@85: 		    int value=0, bool obl=false);
alpar@85: 
alpar@86:     ///Add a new floating point type option
alpar@85: 
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@85: 		      const std::string &help,
alpar@85: 		      double value=0, bool obl=false);
alpar@85: 
alpar@85:     ///Add a new bool type option
alpar@85: 
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@85: 		      const std::string &help,
alpar@85: 		      bool value=false, bool obl=false);
alpar@85: 
alpar@85:     ///Add a new string type option
alpar@85: 
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@85: 		      const std::string &help,
alpar@85: 		      std::string value="", bool obl=false);
alpar@85: 
kpeter@88:     ///\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: 
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@85: 		    const std::string &help,
alpar@85: 		    int &ref, bool obl=false);
alpar@85: 
alpar@85:     ///Add a new floating type option with a storage reference
alpar@85: 
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@85: 		      const std::string &help,
alpar@85: 		      double &ref, bool obl=false);
alpar@85: 
alpar@85:     ///Add a new bool type option with a storage reference
alpar@85: 
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@85: 		      const std::string &help,
alpar@85: 		      bool &ref, bool obl=false);
alpar@85: 
alpar@85:     ///Add a new string type option with a storage reference
alpar@85: 
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@85: 		      const std::string &help,
alpar@85: 		      std::string &ref, bool obl=false);
alpar@85:     
alpar@85:     ///@}
alpar@85: 
alpar@85:     ///\name Option Groups and Synonyms
alpar@85:     ///
alpar@85:     
alpar@85:     ///@{
alpar@85: 
alpar@85:     ///Boundle 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@85: 			   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
alpar@85:     ///given at the same time
alpar@85:     ArgParser &onlyOneGroup(const std::string &group);
alpar@85:   
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@85:     
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@85: 			   const std::string &opt);
alpar@85:     
alpar@85:     ///@}
alpar@85: 
alpar@85:     ///Give help string for non-parsed arguments.
alpar@85: 
alpar@85:     ///With this function you can give help string for non-parsed arguments.
kpeter@88:     ///The parameter \c name will be printed in the short usage line, while
alpar@85:     ///\c help gives a more detailed description.
alpar@85:     ArgParser &other(const std::string &name,
alpar@85: 		     const std::string &help="");
alpar@85:     
kpeter@88:     ///Give back the non-option type arguments.
alpar@85: 
kpeter@88:     ///Give back a reference to a vector consisting of the program arguments
alpar@85:     ///not starting with a '-' character.
alpar@85:     std::vector<std::string> &files() { return _file_args; }
alpar@85: 
alpar@85:     ///Give back the command name (the 0th argument)
alpar@85:     const std::string &commandName() { return _command_name; }
alpar@85: 
alpar@85:     void show(std::ostream &os,Opts::iterator i);
alpar@85:     void show(std::ostream &os,Groups::iterator i);
alpar@85:     void showHelp(Opts::iterator i);
alpar@85:     void showHelp(std::vector<OtherArg>::iterator i);
alpar@85:     void shortHelp();
alpar@85:     void showHelp();
alpar@85: 
alpar@85:     void unknownOpt(std::string arg);
alpar@85: 
alpar@85:     void requiresValue(std::string arg, OptType t);
alpar@85:     void checkMandatories();
alpar@85:     
alpar@85:     ///Start the parsing process
alpar@85:     ArgParser &parse();
alpar@85: 
alpar@85:     /// Synonym for parse()
alpar@85:     ArgParser &run() 
alpar@85:     {
alpar@85:       return parse();
alpar@85:     }
alpar@85:     
alpar@85:     ///Check if an opion has been given to the command.
alpar@85:     bool given(std::string op) 
alpar@85:     {
alpar@85:       Opts::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@85:     
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
kpeter@88:     ///\c std::string if the type of the option matches, otherwise it
kpeter@88:     ///throws an exception (i.e. it performs runtime type checking).
alpar@85:     class RefType 
alpar@85:     {
alpar@85:       ArgParser &_parser;
alpar@85:       std::string _name;
alpar@85:     public:
alpar@85:       ///\e
alpar@85:       RefType(ArgParser &p,const std::string &n) :_parser(p),_name(n) {}
alpar@85:       ///\e
alpar@85:       operator bool() 
alpar@85:       {
alpar@85: 	Opts::iterator i = _parser._opts.find(_name);
kpeter@95: 	LEMON_ASSERT(i!=_parser._opts.end(),
alpar@85: 		     std::string()+"Unkown option: '"+_name+"'");
kpeter@95: 	LEMON_ASSERT(i->second.type==ArgParser::BOOL,
alpar@85: 		     std::string()+"'"+_name+"' is a bool option");
alpar@85: 	return *(i->second.bool_p);
alpar@85:       }
alpar@85:       ///\e
alpar@85:       operator std::string()
alpar@85:       {
alpar@85: 	Opts::iterator i = _parser._opts.find(_name);
kpeter@95: 	LEMON_ASSERT(i!=_parser._opts.end(),
alpar@85: 		     std::string()+"Unkown option: '"+_name+"'");
kpeter@95: 	LEMON_ASSERT(i->second.type==ArgParser::STRING,
alpar@85: 		     std::string()+"'"+_name+"' is a string option");
alpar@85: 	return *(i->second.string_p);
alpar@85:       }
alpar@85:       ///\e
alpar@85:       operator double() 
alpar@85:       {
alpar@85: 	Opts::iterator i = _parser._opts.find(_name);
kpeter@95: 	LEMON_ASSERT(i!=_parser._opts.end(),
alpar@85: 		     std::string()+"Unkown option: '"+_name+"'");
kpeter@95: 	LEMON_ASSERT(i->second.type==ArgParser::DOUBLE ||
kpeter@95: 		     i->second.type==ArgParser::INTEGER,
alpar@85: 		     std::string()+"'"+_name+"' is a floating point option");
alpar@85: 	return i->second.type==ArgParser::DOUBLE ?
alpar@85: 	  *(i->second.double_p) : *(i->second.int_p);
alpar@85:       }
alpar@85:       ///\e
alpar@85:       operator int() 
alpar@85:       {
alpar@85: 	Opts::iterator i = _parser._opts.find(_name);
kpeter@95: 	LEMON_ASSERT(i!=_parser._opts.end(),
alpar@85: 		     std::string()+"Unkown option: '"+_name+"'");
kpeter@95: 	LEMON_ASSERT(i->second.type==ArgParser::INTEGER,
alpar@85: 		     std::string()+"'"+_name+"' is an integer option");
alpar@85: 	return *(i->second.int_p);
alpar@85:       }
alpar@85: 
alpar@85:     };
alpar@85: 
alpar@85:     ///Give back the value of an option
alpar@85:     
kpeter@88:     ///Give back the value of an option.
alpar@85:     ///\sa RefType
alpar@85:     RefType operator[](const std::string &n)
alpar@85:     {
alpar@85:       return RefType(*this, n);
alpar@85:     }    
alpar@85:  
alpar@85:   };
alpar@85: }
alpar@85: 
alpar@85:     
alpar@85: 
alpar@85: #endif // LEMON_MAIN_PARAMS