/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
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 < T >
- 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 <mrw/smartpointer.hpp>
# include <stdlib.h> // exit
# include <string>
# include <map>
# include <vector>
# include <set>
# include <list>
# include <exception>
# include <stdexcept>
# include <sstream>
# include <iostream>
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 , const char * const * const argv ) {
try {
mrw : : Args : : instance ( )
// setup the possible options
< < mrw : : Opt ( ' h ' , " --help " , " Show this help text " )
< < mrw : : Opt ( ' v ' , " --verbose " , " print more information " )
< < mrw : : Opt ( ' q ' , " --quiet " , " be quiet " )
< < mrw : : Opt ( ' n ' , " --name " , mrw : : Param ( ) < < " MRW " , " name of the user " )
< < mrw : : Opt ( ' o ' , " --output-file " , mrw : : Param ( ) < < " " , " file to load " )
< < mrw : : Opt ( ' c ' , " --coordinates " , mrw : : Param ( ) < < 0 < < 0 , " X, Y coordinate " )
// set a description text for help
< < " This is a testprogram for argument evaluation in C++ "
// define the help option
< < ' h '
// shift in the command line arguments
< < argc < < argv ;
. . .
// example usage of simple option
if ( mrw : : Args : : instance ( ) . find ( ' v ' ) ) // be verbose here
. . .
// example usage of option with (one) parameter
ifstream file ( mrw : : Args : : instance ( ) . find ( ' o ' ) . toString ( ) . c_str ( ) ) ;
. . .
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 < mrw / arg . hpp >
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
@ 2 and a boolean that defaults to @ c " true " :
@ code
// if you need the instance as variable:
mrw : : Param p ( ) ;
p < < " noname " < < 4 < < 2 < < true ;
// or in an expression:
mrw : : Opt o ( ' e ' , " --example " , mrw : : Param ( ) < < " noname " < < 4 < < 2 < < true , " " ) ;
@ endcode
To access a value at a given position , simply use @ c
operator [ ] . Then use @ c mrw : : Value : : toString , @ c
mrw : : Value : : toInt or @ c mrw : : Value : : toBool to get the value of
that parameter . Of course yo must know the correct type of a
parameter at a given position , but since you are the programmer
you know it , or you can get it by running your program with the
help option , mostly @ c - h . To retrieve the parameters setup in
the example above ( connected to option @ c - e or @ c - - example ) ,
either the default value , or the value overwritten by the user ,
simply type :
@ code
mrw : : Args & args = mrw : : Args : : instance ( ) ;
std : : string theString = args [ 0 ] - > toString ( ) ;
int firstInteger = args [ 1 ] - > toInt ( ) ;
int secondInteger = args [ 2 ] - > toInt ( ) ;
bool theBoolean = args [ 3 ] - > toBool ( ) ;
@ endcode
< h3 > Setup Command Line from Different Program Parts < / h3 >
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 ( )
< < mrw : : Opt ( ' n ' , " --name " , mrw : : Param ( ) < < " MRW " , " name of the user " )
< < mrw : : Opt ( ' o ' , " --output-file " , mrw : : Param ( ) < < " " , " file to load " )
< < mrw : : Opt ( ' c ' , " --coordinates " , mrw : : Param ( ) < < 0 < < 0 ,
" X, Y coordinate " )
< < " Description text for part Abc, will be added to the \n "
" overall documentation " ;
}
} ;
static AbcArgument abcArgumentInitializer ;
@ endcode
Do the same for all other parts Then the @ c main ( ) function reduces to :
@ code
int main ( int argc , const char * const * const argv ) {
// set the help and evaluate the user given arguments
mrw : : Args : : instance ( )
< < mrw : : Opt ( ' h ' , " --help " , " Show this help text " )
< < ' h ' < < argc < < argv ;
. . .
}
@ endcode
*/
class Param {
public :
/** @brief Abstract base class to represent one single parameter value.
@ pre # include < mrw / arg . hpp >
*/
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 < Value > > 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 < < ( const char * 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 @i
@ throw mrw : : out_of_range if @ c i is too big */
const mrw : : SmartPointer < Value > & 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 < Value > & setable ( unsigned int i ) throw ( std : : exception ) ;
} ;
/** @brief this class represents one command line option
@ pre # include < mrw / arg . hpp >
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 ( )
< < mrw : : Opt ( ' h ' , " --help " , " Show this help text " ) ;
@ endcode
*/
class Opt {
public :
/** @brief create an @c mrw::Opt with additional parameter
@ param shortname short name of the option
@ param longname long name of the option , must start with " -- "
@ param param the additional parameter
@ param helptext the help string for this option
*/
Opt ( const char shortname , const std : : string & longname ,
const Param & param , const std : : string & helptext )
throw ( std : : bad_exception ) :
_set ( false ) , _shortname ( shortname ) , _longname ( longname ) ,
_param ( param ) , _help ( helptext ) {
}
/** @brief create a simple @c mrw::Opt
This option is either set or not set , there are no additional
parameter .
@ param shortname short name of the option
@ param longname long name of the option , must start with " -- "
@ param helptext the help string for this option
*/
Opt ( const char shortname , const std : : string & longname ,
const std : : string & helptext ) throw ( std : : bad_exception ) :
_set ( false ) , _shortname ( shortname ) , _longname ( longname ) ,
_help ( helptext ) {
}
/** @brief get the help text for this option */
const std : : string & help ( ) const throw ( std : : bad_exception ) {
return _help ;
}
/** @brief find out, whether this option was set by the user
Example : Check whether the user has set the @ c - v option for
verbose output :
@ code
if ( mrw : : Args : : instance ( ) . find ( ' v ' ) ) // -v is set
@ endcode
@ return
- @ c true if the user has started the program with this option
- @ c false if the user has not set this option
*/
operator bool ( ) const throw ( std : : bad_exception ) { return _set ; }
/** @brief get one of the additional parameter
If this option has additional parameter , get the @ c i - th of them .
@ throw mrw : : out_of_range if @ c i is too big
@ param i number of the additional parameter to get ( starting with @ c 0 )
@ return a smart pointer to the value ( default or given by the user )
*/
const mrw : : SmartPointer < Param : : Value > & 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 < mrw / arg . hpp >
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 ( )
< < mrw : : Opt ( ' h ' , " --help " , " Show this help text " )
< < mrw : : Opt ( ' v ' , " --verbose " , " print more information " )
< < " This is a testprogram for argument evaluation in C++ "
< < ' h ' < < argc < < argv ;
@ endcode
*/
class Args {
public :
typedef std : : list < std : : string > OtherArgs ;
/// @brief get the one and only instance
static Args & instance ( ) throw ( std : : bad_exception ) { // singleton
static Args _instance ;
return _instance ;
}
/** @brief setup an acceptable option
Setup an acceptable user option .
Example :
@ code
mrw : : Args : : instance ( )
< < mrw : : Opt ( ' v ' , " --verbose " , " print more information " ) ;
@ endcode
*/
Args & operator < < ( const mrw : : Opt & opt ) throw ( std : : invalid_argument ) ;
/** @brief setup the number of arguments
Setup the number of arguments .
This must be done before @ c argv is shifted in .
Example :
@ code
int main ( int argv , const char * const * const argv ) {
mrw : : Args : : instance ( ) < < argc < < argv ;
. . .
}
@ endcode
*/
Args & operator < < ( int argc ) throw ( std : : bad_exception ) {
_argc = argc ;
return * this ;
}
/** @brief setup the C array of command line arguments
Setup the C array of command line arguments . This must be the
very last thing shifted in .
Example :
@ code
int main ( int argv , const char * const * const argv ) {
mrw : : Args : : instance ( ) < < argc < < argv ;
. . .
}
@ endcode
*/
Args & operator < < ( const char * const * const argv ) throw ( std : : exception ) ;
/** @brief add a description text
Add a description text . This description text is shown in the
@ c DESCRIPTION section of the help display . If the description
text is shifted in more then once , the different sections are
appended with new line and an empty line between .
Example :
@ code
mrw : : Args : : instance ( ) < < " this is a description for --help " ;
@ endcode
*/
Args & operator < < ( const std : : string & description ) throw ( std : : exception ) {
if ( _description = = " " )
_description = description ;
else
_description + = " \n \n " + description ;
return * this ;
}
/** @brief set the help option
Define which option prints the help text . There is no code
needed for printing the help text : if the help option has been
shifted in , help is printed automatically at user request ,
then the program is terminated . Only specify the short option
name , the long option name is known .
Example :
@ code
mrw : : Args : : instance ( ) < < ' h ' ;
@ endcode
*/
Args & operator < < ( char helpopt ) throw ( std : : exception ) {
_help = helpopt ;
return * this ;
}
/** @brief get an option, given the short option name
@ throw mrw : : out_of_range if the option does not exist
*/
const Opt & find ( char c ) const throw ( std : : exception ) ;
/** @brief get an option, given the long option name
@ throw mrw : : out_of_range if the option does not exist
*/
const Opt & find ( const std : : string & s ) const throw ( std : : exception ) ;
/** @brief get all non interpreted options
All user options that don ' t fit the defined and interpreted
options . The meaning for this is , that a user may append ,
e . g . a list of file names .
*/
const OtherArgs & otherArgs ( ) {
return _otherargs ;
}
/** @brief get the file name of the executable, that's @c argv[0] */
const std : : string & filename ( ) throw ( std : : bad_exception ) {
return _filename ;
}
/** @brief print the help text, then exit */
void help ( ) {
std : : cout < < " USAGE: " < < std : : endl
< < " " < < _filename < < " [ OPTIONS ] " < < std : : endl
< < " OPTIONS: " < < std : : endl ;
for ( Options : : iterator it ( _options . begin ( ) ) ; it ! = _options . end ( ) ; + + it ) {
std : : cout < < " - " < < it - > _shortname < < " | " < < it - > _longname ;
for ( int i ( 0 ) ; i < it - > _param . size ( ) ; + + i )
std : : cout < < " < " < < ( * it ) [ i ] - > typestr ( ) < < " > " ;
if ( it - > _param . size ( ) > 0 ) std : : cout < < " (default: " ;
for ( int i ( 0 ) ; i < it - > _param . size ( ) - 1 ; + + i )
std : : cout < < ( * it ) [ i ] - > printable ( ) < < " " ;
if ( it - > _param . size ( ) > 0 )
std : : cout < < ( * it ) [ it - > _param . size ( ) - 1 ] - > printable ( ) < < " ) " ;
std : : cout < < std : : endl < < " " < < it - > help ( ) < < std : : endl ;
}
if ( _description . size ( ) > 0 )
std : : cout < < " DESCRIPTION: " < < std : : endl
< < _description < < std : : endl ;
exit ( 0 ) ;
}
private :
Args ( ) : _argc ( - 1 ) , _help ( 0 ) { } // singleton
Args & operator = ( const Args & ) ; // singleton, not implemented
Args & parse ( int argc , const char * const * const argv ) throw ( std : : exception ) ;
typedef std : : list < Opt > Options ;
typedef std : : map < char , Options : : iterator > ShortOpts ;
typedef std : : map < std : : string , Options : : iterator > LongOpts ;
std : : string _filename ;
Options _options ;
ShortOpts _shortopts ;
LongOpts _longopts ;
OtherArgs _otherargs ;
int _argc ;
char _help ;
std : : string _description ;
} ;
//@}
}