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
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
|
|
|