/*! @file
@ id $ Id $
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
# ifndef __MRW_ARGS__
# define __MRW_ARGS__
# include <mrw/checkcxx11.hxx>
# include <mrw/iomanip.hxx>
# include <vector>
# include <map>
# include <memory>
# include <iostream>
# include <iomanip>
# include <sstream>
# include <stdexcept>
# include <numeric>
# include <cstdlib> // exit()
/** @page oldcompiler Workaround for old non C++11 compilers
. . . to be documented
@ note Old compilers are automatically detected and the flag
@ ref MRW__OLD_PRE11_COMPILER is set .
*/
// Add version information for @c what and @c ident
const std : : string MRW_IDENT ( " $Id: " PACKAGE_NAME " - " PACKAGEPACKAGE_VERSION " $ " ) ;
const std : : string MRW_WHAT ( " #(@) " PACKAGE_NAME " - " PACKAGEPACKAGE_VERSION ) ;
namespace mrw {
/// Cool and easy evaluation of commandline arguments in C++11.
/** Evaluating command line arguments is extremely easy with this library:
@ begincode
void test_func ( ) ; // bound to option --test
int main ( int argc , char * * argv ) try {
// option variables
bool flag ; // bound to option --flag
int r ( 5 ) ; // bound to option --repeat
std : : string txt ( " Hello World " ) ; // bound to option --text
int x ( 0 ) , y ( 0 ) ; // bound to option --coord
// bind and evaluate options
args : : parse ( argc , argv , " This is an example for argument processing. " ,
{
{ " h " , " help " , " show this help " , { args : : help ( ) , args : : exit ( ) } } ,
{ " v " , " version " , " show version " , { args : : version ( ) , args : : exit ( ) } } ,
{ " r " , " repeat " , " number of repetitions " ,
{ args : : param ( r , " number " ) } } ,
{ " f " , " flag " , " check a flag " , { args : : param ( flag ) } } ,
{ " t " , " text " , " pass a text " , { args : : param ( txt , " text " ) } } ,
{ " c " , " coord " , " add some 2D coordinates " , { args : : param ( x , " x " ) ,
args : : param ( y , " y " ) } } ,
{ " " , " test " , " call test_func, no shortcut " ,
{ args : : func ( test_func ) } } ,
} ) ;
// [...] now the variables are setup according to the user's choice
return 0 ;
} catch ( const std : : exception & x ) { // error in commandline options
std : : cerr < < " **** ERROR: " < < x . what ( ) < < std : : endl ;
args : : show_help ( ) ; // display help
return 1 ;
}
@ endcode
@ note This library requires C + + 11 , but there is also a fallback
for old compilers . But the calling syntax is much worse on
non C + + 11 compliant compilers , so the use of a modern
compiler is strongly recommended . On GNU GCC , you may have
to add the commandline option @ c - std = c + + 11
@ note I suggest to take this library into the next C + + standard . */
namespace args {
/// Parent for holding a reference to a parameter variable.
/** This class ist used only as parent for instances of template
parameters . It represents one parameter that is evaluated when
a commandline option is given . The derived classes hold a
reference to a variable that is set in method @ ref evaluate when
the specific commandline parameter is given . */
class abstract_parameter {
public :
/// Called when the user specifies the related commandline option.
/** Method must be implemented according to the specific type in
the child classes . */
virtual void evaluate ( char * * & a , char * * max ) = 0 ;
/// Convert the actual value to a string.
/** Defaults to empty string, can be overwritten in child
classes . Used by the help function to show the default
value . */
virtual operator std : : string ( ) {
return std : : string ( ) ;
}
virtual std : : string type ( ) {
return std : : string ( ) ;
}
} ;
// default implementation for all types
template < typename T > class parameter : public abstract_parameter {
public :
parameter ( T & ref , const std : : string & s = std : : string ( ) ) :
_ref ( ref ) , _type ( s ) { }
virtual void evaluate ( char * * & a , char * * max ) {
if ( max < a + 1 )
throw std : : runtime_error ( " missing parameter for: " + std : : string ( * a ) ) ;
std : : stringstream ss ;
ss < < * + + a ;
ss > > _ref ;
}
virtual operator std : : string ( ) {
std : : stringstream ss ;
ss < < _ref ;
return ss . str ( ) ;
}
virtual std : : string type ( ) {
return _type . size ( ) ? " < " + _type + " > " : " " ;
}
private :
T & _ref ;
std : : string _type ;
} ;
// special case: boolean has no parameter
template < > class parameter < bool > : public abstract_parameter {
public :
parameter ( bool & ref , const std : : string & = std : : string ( ) ) :
_ref ( ref = false ) {
}
virtual void evaluate ( char * * & , char * * ) {
_ref = true ;
}
private :
bool & _ref ;
} ;
// special case: string cannot easily be shifted
template < typename T > class parameter < std : : basic_string < T > > :
public abstract_parameter {
public :
parameter ( std : : basic_string < T > & ref ,
const std : : string & s = std : : string ( ) ) :
_ref ( ref ) , _type ( s ) { }
virtual void evaluate ( char * * & a , char * * max ) {
if ( max < a + 1 )
throw std : : runtime_error ( " missing parameter for: " + std : : string ( * a ) ) ;
std : : stringstream ss ;
ss < < * + + a ;
_ref = std : : basic_string < T > ( std : : istreambuf_iterator < T > ( ss ) ,
std : : istreambuf_iterator < T > ( ) ) ;
}
virtual operator std : : string ( ) {
std : : stringstream ss ;
ss < < _ref ;
return ss . str ( ) ;
}
virtual std : : string type ( ) {
return _type . size ( ) ? " < " + _type + " > " : " " ;
}
private :
std : : basic_string < T > & _ref ;
std : : string _type ;
} ;
// special case: if a function is called
template < > class parameter < void ( * ) ( ) > : public abstract_parameter {
public :
parameter ( void ( * ref ) ( ) , const std : : string & = std : : string ( ) ) :
_ref ( ref ) {
}
virtual void evaluate ( char * * & , char * * ) {
_ref ( ) ;
}
private :
void ( * _ref ) ( ) ;
} ;
typedef std : : shared_ptr < abstract_parameter > param_ptr ;
template < typename T >
param_ptr param ( T & t , const std : : string & s = std : : string ( ) ) {
return param_ptr ( new args : : parameter < T > ( t , s ) ) ;
}
struct decl {
typedef std : : vector < param_ptr > param_list ;
decl ( std : : string p1 , std : : string p2 , std : : string p3 , param_list p4 ) :
short_arg ( p1 ) , long_arg ( p2 ) , desc ( p3 ) , params ( p4 ) {
}
std : : string short_arg ;
std : : string long_arg ;
std : : string desc ;
param_list params ;
} ;
typedef std : : map < std : : string , decl : : param_list > arg_map ;
static arg_map & args ( ) {
static arg_map a ;
return a ;
}
typedef std : : vector < decl > list ;
static list & arg_list ( ) {
static list a ;
return a ;
}
void match ( const std : : string & arg , char * * & a , char * * max ) {
# ifdef MRW__OLD_PRE11_COMPILER
arg_map : : iterator it ( args ( ) . find ( arg ) ) ;
# else
auto it ( args ( ) . find ( arg ) ) ;
# endif
if ( it = = args ( ) . end ( ) )
throw std : : runtime_error ( " unknown argument: " + std : : string ( * a ) ) ;
# ifdef MRW__OLD_PRE11_COMPILER
for ( decl : : param_list : : iterator it2 ( it - > second . begin ( ) ) ;
it2 ! = it - > second . end ( ) ; + + it2 )
# else
for ( auto it2 ( it - > second . begin ( ) ) ; it2 ! = it - > second . end ( ) ; + + it2 )
# endif
( * it2 ) - > evaluate ( a , max ) ;
}
/// Filename as passed in argv[0].
/** Used in the help display.
@ note This function emulates a global variable using parts of a
singleton pattern . */
static std : : string filename ( const std : : string & arg0 = std : : string ( ) ) {
static std : : string file ( arg0 ) ;
return file ;
}
/// Description of the program.
/** Used in the help display.
@ note This function emulates a global variable using parts of a
singleton pattern . */
static std : : string description ( const std : : string & desc = std : : string ( ) ) {
static std : : string d ( desc ) ;
return d ;
}
/// Return values of the program.
/** Used in the help display.
@ note This function emulates a global variable using parts of a
singleton pattern . */
static std : : string returns ( const std : : string & ret = std : : string ( ) ) {
static std : : string r ( ret ) ;
return r ;
}
/// Initialize all parameters according to the commandline options.
/** Sets up the parser from the @ref list of parameters
@ param argc the argument count as given in C + + @ c main function
@ param argv the array of arguments as given in C + + @ c main function
@ param desc a string describing what the program does ,
used in @ ref show_help
@ param l list of options and parameters to be parsed
@ param ret documentation of the return values of the program */
static void parse ( int argc , char * * argv , const std : : string & desc , list l ,
const std : : string & ret = std : : string ( ) ) {
filename ( argv [ 0 ] ) ; // store filename for later use in help
description ( desc ) ; // store program description for later use in help
returns ( ret ) ; // store return documentation for later use in help
arg_list ( ) = l ; // store options and parameters for later use in help
// setup the argument mapping table
# ifdef MRW__OLD_PRE11_COMPILER
for ( list : : iterator it ( l . begin ( ) ) ; it ! = l . end ( ) ; + + it )
# else
for ( auto it ( l . begin ( ) ) ; it ! = l . end ( ) ; + + it )
# endif
{
if ( it - > short_arg . size ( ) = = 1 ) args ( ) [ it - > short_arg ] = it - > params ;
if ( it - > long_arg . size ( ) ) args ( ) [ " -- " + it - > long_arg ] = it - > params ;
}
// parse commandline and evaluate the arguments
# ifdef MRW__OLD_PRE11_COMPILER
for ( char * * a ( argv + 1 ) ; a < argv + argc ; + + a )
# else
for ( auto a ( argv + 1 ) ; a < argv + argc ; + + a )
# endif
{
std : : string arg ( * a ) ;
if ( arg . size ( ) > 1 & & arg [ 0 ] = = ' - ' & & arg [ 1 ] ! = ' - ' ) { // short argument
# ifdef MRW__OLD_PRE11_COMPILER
for ( std : : string : : iterator it ( arg . begin ( ) + 1 ) ; it ! = arg . end ( ) ; + + it )
# else
for ( auto it ( arg . begin ( ) + 1 ) ; it ! = arg . end ( ) ; + + it )
# endif
match ( std : : string ( 1 , * it ) , a , argv + argc ) ;
} else { // long argument or wrong argument
match ( arg , a , argv + argc ) ;
}
}
}
void show_help ( const std : : string & synopsis_txt = " SYNOPSIS " ,
const std : : string & description_txt = " DESCRIPTION " ,
const std : : string & options_txt = " OPTIONS " ,
const std : : string & returns_txt = " RETURNS " ,
unsigned int max_line = 80 , unsigned int indent = 2 ,
unsigned int long_indent = 4 ,
unsigned int option_len = 16 , unsigned int param_len = 21 ) {
std : : cout < < synopsis_txt < < std : : endl < < std : : endl
< < " " < < filename ( ) < < " [ " < < options_txt < < " ] " < < std : : endl < < std : : endl
< < description_txt < < std : : endl < < std : : endl
< < ssplit ( max_line , indent ) < < description ( ) ; //! @bug
std : : cout < < std : : endl < < std : : endl
< < options_txt < < std : : endl ;
# ifdef MRW__OLD_PRE11_COMPILER
for ( list : : iterator arg ( arg_list ( ) . begin ( ) ) ; arg ! = arg_list ( ) . end ( ) ; + + arg )
# else
for ( auto arg ( arg_list ( ) . begin ( ) ) ; arg ! = arg_list ( ) . end ( ) ; + + arg )
# endif
{
std : : string o ( ( arg - > short_arg . size ( ) ? " - " + arg - > short_arg : " " ) +
( arg - > short_arg . size ( ) & & arg - > long_arg . size ( ) ? " , " : " " ) +
( arg - > long_arg . size ( ) ? " -- " + arg - > long_arg : " " ) ) ;
# ifdef MRW__OLD_PRE11_COMPILER
std : : string a ;
for ( decl : : param_list : : iterator p ( arg - > params . begin ( ) ) ;
p ! = arg - > params . end ( ) ; + + p ) {
std : : string def ( * * p ) ;
a + = ( * p ) - > type ( ) + ( def . size ( ) ? " = " + def : " " ) ;
}
# else
std : : string a ( std : : accumulate ( arg - > params . begin ( ) , arg - > params . end ( ) ,
std : : string ( ) ,
[ ] ( const std : : string & s ,
param_ptr p )
- > std : : string {
std : : string def ( * p ) ;
return
s + p - > type ( ) + ( def . size ( ) ? " = " + def : " " ) ;
} ) ) ;
# endif
std : : cout < < std : : endl < < " "
< < o < < std : : setw ( int ( option_len - o . size ( ) ) ) < < ' '
< < a ;
if ( arg - > desc . size ( ) > max_line - indent - option_len - param_len )
std : : cout < < std : : endl < < std : : endl < < ssplit ( max_line , long_indent )
< < arg - > desc ;
else
std : : cout < < std : : setw ( int ( param_len - a . size ( ) ) ) < < ' ' < < arg - > desc ;
std : : cout < < std : : endl ;
}
if ( returns ( ) . size ( ) ) {
std : : cout < < std : : endl < < returns_txt < < std : : endl < < std : : endl
< < ssplit ( max_line , indent ) < < returns ( ) ;
std : : cout < < std : : endl ;
}
}
/// @return version information
std : : string version_text ( ) {
# ifdef PACKAGEPACKAGE_VERSION
# ifdef PACKAGE_NAME
std : : string v ( " : " PACKAGE_NAME " - " PACKAGEPACKAGE_VERSION ) ;
# else
std : : string v ( " - " PACKAGEPACKAGE_VERSION ) ;
# endif
# else
# ifdef PACKAGE_NAME
std : : string v ( " : " PACKAGE_NAME ) ;
# else
std : : string v ;
# endif
# endif
return filename ( ) + v ;
}
void show_version ( ) {
std : : cout < < version_text ( ) < < std : : endl ;
}
void help_no_arg ( ) {
show_help ( ) ;
}
void do_exit ( ) {
exit ( 1 ) ;
}
param_ptr func ( void ( * fn ) ( ) ) {
return param_ptr ( new args : : parameter < void ( * ) ( ) > ( fn ) ) ;
}
param_ptr help ( ) {
return func ( args : : help_no_arg ) ;
}
param_ptr version ( ) {
return func ( args : : show_version ) ;
}
param_ptr exit ( ) {
return func ( args : : do_exit ) ;
}
/// Sets up an argument list containing help and version.
list defaults ( ) {
# ifdef MRW__OLD_PRE11_COMPILER
list res ;
decl : : param_list h ;
h . push_back ( help ( ) ) ;
h . push_back ( exit ( ) ) ;
decl : : param_list v ;
v . push_back ( version ( ) ) ;
v . push_back ( exit ( ) ) ;
res . push_back ( decl ( " h " , " help " , " show help " , h ) ) ;
res . push_back ( decl ( " v " , " version " , " show version " , v ) ) ;
return res ;
# else // New C++ standard C++11 is great:
return list ( {
{ " h " , " help " , " show help " , { help ( ) , exit ( ) } } ,
{ " v " , " version " , " show version " , { version ( ) , exit ( ) } }
} ) ;
# endif
}
}
}
# endif