/** @file $Id$ $Date$ $Author$ @copy © Marc Wäckerlin @license LGPL, see file COPYING $Log$ Revision 1.6 2005/02/08 12:31:36 marc new static methods to simplify access to options Revision 1.5 2004/11/25 18:26:04 marc Constness corrected Revision 1.4 2004/10/07 09:23:39 marc bugs in documentation Revision 1.3 2004/08/31 16:23:29 marc no include of stacktrace.hpp Revision 1.2 2004/08/28 16:21:25 marc mrw-c++-0.92 (mrw) - new file: version.cpp - new file header for all sources - work around warning in mrw::auto - possibility to compile without log4cxx - work around bugs in demangle.h and libiberty.h - corrections in documentation - added simple tracing mechanism - more warnings - small corrections in Auto<>::Free and a new test for it - possibility to compile without stack trace */ #include #include // exit #include #include #include #include #include #include #include #include #include namespace mrw { /** @defgroup arguments C++ Evaluation of Command Line Arguments @brief These classes do simple and easy command line argment evaluation in C++. Features: - every argument has a long and a short option - all arguments are optional and provide a default value - the order of options is not important - every option can take any (fixed) number of additional parameter of type - string - integer - boolean (@c "yes", @c "on", @c "true" evaluates to @c true) - short options can be combined, instead of @c -a @c -b @c -c @c 15 you can simply write @c -abc @c 15 - automated help display (support for option @c -h) @c mrw::Args is the main user interface class that represents all command line options with their arguments. It is implemented as singleton, so the same instance can be accessed from everywhere in the code. It mst be setup just in the beginning of the @c main() function. The other important class for the end user is @c mrw::Opt, one possible option with additional parameter. The end user needs @c mrw::Opt to setup all allowed command line options in the beginning, bevore evaluation of the user given command line is done (before @c argc and @c argv is shifted into @c mrw::Args. The third class a user should know is @c mrw::Param. It represents the arguments to one option. Every instance of @c mrw::Opt owns one instance of @c mrw::Param that is either empty or list of (mandatory) arguments of type @c std::string, @c int or @c bool. The classes are normally used this way: @code // this program may be called e.g. with the following arguments: // ./a.out --coordinates 13 1 -vo out.txt -n MyName int main(int argv, char const*const*const argv) { try { mrw::Args::instance() // setup the possible options <toInt(); // first integer int y = mrw::Args::toInt('c', 1); // second; alternative, simpler access ... return 0 } } catch (mrw::exception& x) { // trace error, print help or mention option -h } } @endcode */ //@{ /** @brief List of additional (mandatory) parameter to one command line argument. @pre #include A new mandatory parameter is added to the list of parameter, by shifting the default value into the instance of @c mrw::Param. E.g. add a string, that defaults to @c "noname", an integer, that defaults to @c 4, another integer that defaults to @c 2 and a boolean that defaults to @c "true": @code // if you need the instance as variable: mrw::Param p(); p<<"noname"<<4<<2<toString(); int firstInteger = args[1]->toInt(); int secondInteger = args[2]->toInt(); bool theBoolean = args[3]->toBool(); @endcode

Setup Command Line from Different Program Parts

If your software is large and splitted into different parts (or sub projects or modules, ...), all with their own parameter, you can use the following trick: Statical variables are initialized before the @c main() function is called. In part Abc write in a code file (not in a header file): @code class AbcArguments { public: AbcArguments() { mrw::Args::instance() < */ class Value { public: virtual ~Value() {} /** @brief If the instance is a @c std::string, return that string, otherwise throw an exception. @throw mrw::bad_cast if the instance is not a string @return the string, if the instance is a string */ virtual const std::string& toString() const throw(std::exception); /** @brief If the instance is an @c int, return that integer, otherwise throw an exception. @throw mrw::bad_cast if the instance is not a integer @return the integer, if the instance is a integer */ virtual int toInt() const throw(std::exception); /** @brief If the instance is an @c bool, return that boolean, otherwise throw an exception. @note the following typings are converted to @c true: - true - yes - on Everything else is converted to @c false. @throw mrw::bad_cast if the instance is not a boolean @return the boolean, if the instance is a boolean */ virtual bool toBool() const throw(std::exception); /// @brief returns a printable representation of the value virtual std::string printable() const throw(std::bad_exception) = 0; /// @brief returns a printable typename of the value virtual const std::string& typestr() const throw(std::bad_exception)=0; protected: /// Allow assign for Args, make it a friend. friend class Args; /// Only the class itself and friends (Args) are allowed to assign. virtual void operator=(const std::string&) throw(std::exception) = 0; }; private: class StringValue: public Value { public: virtual ~StringValue() {} StringValue(const std::string& s) throw(std::bad_exception): _s(s) { } virtual const std::string& toString() const throw(std::exception) { return _s; } virtual const std::string& typestr() const throw(std::bad_exception) { static std::string name("string"); return name; } virtual std::string printable() const throw(std::bad_exception) { return _s; } protected: virtual void operator=(const std::string& s) throw(std::exception) { _s = s; } private: std::string _s; }; class IntValue: public Value { public: virtual ~IntValue() {} IntValue(int i) throw(std::bad_exception): _i(i) { } virtual int toInt() const throw(std::exception) { return _i; } virtual const std::string& typestr() const throw(std::bad_exception) { static std::string name("integer"); return name; } virtual std::string printable() const throw(std::bad_exception) { return ((std::stringstream&)(std::stringstream()<<_i)).str(); } protected: virtual void operator=(const std::string& s) throw(std::exception); private: int _i; }; class BoolValue: public Value { public: virtual ~BoolValue() {} BoolValue(bool b) throw(std::bad_exception): _b(b) { } virtual bool toBool() const throw(std::exception) { return _b; } virtual const std::string& typestr() const throw(std::bad_exception) { static std::string name("boolean (\"yes\" or \"no\")"); return name; } virtual std::string printable() const throw(std::bad_exception) { return _b?"yes":"no"; } protected: virtual void operator=(const std::string& s) throw(std::exception) { _b = s=="true" || s=="yes" || s=="on"; } private: bool _b; }; typedef std::vector< mrw::SmartPointer > Params; Params _params; public: /// @brief returns the number of (mandatory) parameter int size() const throw(std::bad_exception) { return _params.size(); } /// @brief add one more mandatory string parameter Param& operator<<(char const*const s) throw(std::bad_exception) { _params.push_back(new StringValue(s)); return *this; } /// @brief add one more mandatory string parameter Param& operator<<(const std::string& s) throw(std::bad_exception) { _params.push_back(new StringValue(s)); return *this; } /// @brief add one more mandatory integer parameter Param& operator<<(int i) throw(std::bad_exception) { _params.push_back(new IntValue(i)); return *this; } // @brief add one more mandatory boolean parameter Param& operator<<(bool b) throw(std::bad_exception) { _params.push_back(new BoolValue(b)); return *this; } /** @brief get parameter number @c i @throw mrw::out_of_range if @c i is too big */ const mrw::SmartPointer& operator[](unsigned int i) const throw(std::exception); private: /// Allow set for Args, make it a friend. friend class Args; // allow set /// Get a parameter with acces right for setting. /// This is allowed for the class itself and friends (Args) only. mrw::SmartPointer& setable(unsigned int i) throw(std::exception); }; /** @brief this class represents one command line option @pre #include The library user needs this class when setting up the list of supported command line ooptions: Simply shift one instance of @c mrw::Opt per supported command line option into @c mrw::Args::instance(), e.g.: @code mrw::Args::instance() <& operator[](unsigned int i) const throw(std::exception) { return _param[i]; } private: /// Allow set values, make Args a friend. friend class Args; /// Set @c _set to true, available only for friends (Args). void set() const throw(std::bad_exception) { _set = true; } Param& args() const throw(std::bad_exception) { return _param; } mutable bool _set; char _shortname; std::string _longname; mutable Param _param; std::string _help; }; /** @brief handle command line arguments @pre #include This class handles command line arguments. It is a singleton. Get the one and only instance of this class with @c mrw::Args::instance(). It is setup by shifting values into this class. The order is important, be sure that you shift in @c argc and @c argv as last parameters. Example setup: @code mrw::Args::instance() < OtherArgs; /// @brief get the one and only instance static Args& instance() throw(std::bad_exception) { // singleton static Args _instance; return _instance; } /** @brief check a simple option This is a shortcut. Instead of the expression: @code if (mrw::Args::instance().find('x')) ... @endcode you can simply write the expression: @code if (mrw::Args::have('x')) ... @endcode It is exactly the same. @param c the short name of the parameter @throw mrw::out_of_range if the parameter is not available (this would be a coding error) */ static bool have(char c) throw(std::exception) { return mrw::Args::instance().find(c); } /** @brief get a simple string parameter This is a shortcut. Instead of the expression: @code mrw::Args::instance().find('x')[0].toString() @endcode you can simply write the expression: @code mrw::Args::toString('x') @endcode It is exactly the same. @param c the short name of the parameter @throw mrw::bad_cast if the parameter is not a string @throw mrw::out_of_range if the parameter is empty or not available */ static const std::string& toString(char c) throw(std::exception) { return mrw::Args::instance().find(c)[0]->toString(); } /** @brief get a simple integer parameter This is a shortcut. Instead of the expression: @code mrw::Args::instance().find('x')[0].toInt() @endcode you can simply write the expression: @code mrw::Args::toInt('x') @endcode It is exactly the same. @param c the short name of the parameter @throw mrw::bad_cast if the parameter is not an int @throw mrw::out_of_range if the parameter is empty or not available */ static int toInt(char c) throw(std::exception) { return mrw::Args::instance().find(c)[0]->toInt(); } /** @brief get a simple boolean parameter This is a shortcut. Instead of the expression: @code mrw::Args::instance().find('x')[0].toBool() @endcode you can simply write the expression: @code mrw::Args::toBool('x') @endcode It is exactly the same. @param c the short name of the parameter @throw mrw::bad_cast if the parameter is not an bool @throw mrw::out_of_range if the parameter is empty or not available */ static bool toBool(char c) throw(std::exception) { return mrw::Args::instance().find(c)[0]->toBool(); } /** @brief get the n-th string parameter This is a shortcut. Instead of the expression: @code mrw::Args::instance().find('x')[n].toString() @endcode you can simply write the expression: @code mrw::Args::toString('x', n) @endcode It is exactly the same. @param c the short name of the parameter @param n the number of the parameter (starting at 0) @throw mrw::bad_cast if the parameter is not a string @throw mrw::out_of_range if the parameter is not available */ static const std::string& toString(char c, int n) throw(std::exception) { return mrw::Args::instance().find(c)[n]->toString(); } /** @brief get the n-th integer parameter This is a shortcut. Instead of the expression: @code mrw::Args::instance().find('x')[n].toInt() @endcode you can simply write the expression: @code mrw::Args::toInt('x', n) @endcode It is exactly the same. @param c the short name of the parameter @param n the number of the parameter (starting at 0) @throw mrw::bad_cast if the parameter is not an int @throw mrw::out_of_range if the parameter is not available */ static int toInt(char c, int n) throw(std::exception) { return mrw::Args::instance().find(c)[n]->toInt(); } /** @brief get the n-th boolean parameter This is a shortcut. Instead of the expression: @code mrw::Args::instance().find('x')[n].toBool() @endcode you can simply write the expression: @code mrw::Args::toBool('x', n) @endcode It is exactly the same. @param c the short name of the parameter @param n the number of the parameter (starting at 0) @throw mrw::bad_cast if the parameter is not an bool @throw mrw::out_of_range if the parameter not available */ static bool toBool(char c, int n) throw(std::exception) { return mrw::Args::instance().find(c)[n]->toBool(); } /** @brief setup an acceptable option Setup an acceptable user option. Example: @code mrw::Args::instance() <_shortname<<" | "<_longname; for (int i(0); i_param.size(); ++i) std::cout<<" <"<<(*it)[i]->typestr()<<">"; if (it->_param.size()>0) std::cout<<" (default: "; for (int i(0); i_param.size()-1; ++i) std::cout<<(*it)[i]->printable()<<" "; if (it->_param.size()>0) std::cout<<(*it)[it->_param.size()-1]->printable()<<")"; std::cout<help()<0) std::cout<<"DESCRIPTION:"< Options; typedef std::map ShortOpts; typedef std::map LongOpts; std::string _filename; Options _options; ShortOpts _shortopts; LongOpts _longopts; OtherArgs _otherargs; int _argc; char _help; std::string _description; }; //@} }