C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

401 lines
15 KiB

/*! @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