389 lines
15 KiB
Plaintext
389 lines
15 KiB
Plaintext
/*! @file
|
|
|
|
@id $Id$
|
|
*/
|
|
// 1 2 3 4 5 6 7 8
|
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
#ifndef __ARGS__
|
|
#define __ARGS__
|
|
|
|
#include <mrw/iomanip>
|
|
|
|
#include <vector>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <numeric>
|
|
#include <cstdlib> // exit()
|
|
|
|
// check if code is compiled with a new C++11 compiler
|
|
// otherwise there is a fallback wich makes everything much more compliacted
|
|
#ifndef ARGS__OLD_PRE11_COMPILER
|
|
#if __cplusplus < 201103L
|
|
/// Code is compiled with an old non C++11 standard compliant compiler
|
|
/** There are workarounds for old non C++11 compatible
|
|
compilers. These workarounds are deprecated, but will remain until
|
|
most compilers fully support C++11. So this workaround will be
|
|
removed in future releases, when support for C++11 is more
|
|
common. Only rely on this workaround, if you really have to.
|
|
@see oldcompiler for details on usage */
|
|
#define ARGS__OLD_PRE11_COMPILER
|
|
#warning You need a C++11 compliant compiler, on gcc use option -std=c++11
|
|
#warning emulating C++11 - this changes the way you use the library
|
|
#warning this is deprecated and will be removed in future releases
|
|
#warning refere to the library documentation for more details
|
|
#endif
|
|
#endif
|
|
|
|
/** @page oldcompiler Workaround for old non C++11 compilers
|
|
... to be documented
|
|
@note Old compilers are automatically detected and the flag
|
|
@refs ARGS__OLD_PRE11_COMPILER is set.
|
|
*/
|
|
|
|
#ifdef ARGS__OLD_PRE11_COMPILER
|
|
#include <boost/shared_ptr.hpp>
|
|
namespace std {
|
|
// there is no std::shared_ptr in pre C++11 compilers, so we use the
|
|
// one from the boost library as a 1:1 replacement
|
|
template <typename T> class shared_ptr: public boost::shared_ptr<T> {
|
|
public:
|
|
explicit shared_ptr(T* p): boost::shared_ptr<T>(p) {}
|
|
};
|
|
};
|
|
#endif
|
|
|
|
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()}},
|
|
{"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)();
|
|
};
|
|
template<typename T>
|
|
std::shared_ptr<abstract_parameter> param
|
|
(T& t, const std::string& s = std::string()) {
|
|
return std::shared_ptr<abstract_parameter>(new args::parameter<T>(t, s));
|
|
}
|
|
struct declaration {
|
|
#ifdef ARGS__OLD_PRE11_COMPILER
|
|
declaration(std::string p1, std::string p2, std::string p3,
|
|
std::vector<std::shared_ptr<abstract_parameter> > p4):
|
|
short_arg(p1), long_arg(p2), desc(p3), params(p4) {
|
|
}
|
|
#endif
|
|
std::string short_arg;
|
|
std::string long_arg;
|
|
std::string desc;
|
|
std::vector<std::shared_ptr<abstract_parameter> > params;
|
|
};
|
|
typedef std::map<std::string,
|
|
std::vector<std::shared_ptr<abstract_parameter> > >
|
|
arg_map;
|
|
static arg_map& args() {
|
|
static arg_map a;
|
|
return a;
|
|
}
|
|
typedef std::vector<declaration> list;
|
|
static list& arg_list() {
|
|
static list a;
|
|
return a;
|
|
}
|
|
void match(const std::string& arg, char**& a, char** max) {
|
|
#ifdef ARGS__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 ARGS__OLD_PRE11_COMPILER
|
|
for (std::vector<std::shared_ptr<abstract_parameter> >::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 descr a string describing what the program does,
|
|
used in @ref show_help
|
|
@param l list of options and parameters to be parsed
|
|
@raram 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 ARGS__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 ARGS__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 ARGS__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",
|
|
int max_line=80, int indent=2, int long_indent=4,
|
|
int option_len=16, 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
|
|
<<split(max_line, indent)<<description(); //! @bug
|
|
std::cout<<std::endl<<std::endl
|
|
<<options_txt<<std::endl;
|
|
#ifdef ARGS__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 ARGS__OLD_PRE11_COMPILER
|
|
std::string a;
|
|
for (std::vector<std::shared_ptr<abstract_parameter> >::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,
|
|
std::shared_ptr<abstract_parameter> p)
|
|
-> std::string {
|
|
std::string def(**p);
|
|
return s+p->type()+(def.size()?"="+def:"");
|
|
}));
|
|
#endif
|
|
std::cout<<std::endl<<" "
|
|
<<o<<std::setw(option_len-o.size())<<' '
|
|
<<a;
|
|
if (arg->desc.size()>max_line-indent-option_len-param_len)
|
|
std::cout<<std::endl<<std::endl<<split(max_line, long_indent)
|
|
<<arg->desc;
|
|
else
|
|
std::cout<<std::setw(param_len-a.size())<<' '<<arg->desc;
|
|
std::cout<<std::endl;
|
|
}
|
|
if (returns().size()) {
|
|
std::cout<<std::endl<<returns_txt<<std::endl<<std::endl
|
|
<<split(max_line, indent)<<returns();
|
|
std::cout<<std::endl;
|
|
}
|
|
}
|
|
void help_no_arg() {
|
|
show_help();
|
|
}
|
|
void do_exit() {
|
|
exit(1);
|
|
}
|
|
std::shared_ptr<args::abstract_parameter> func(void(*fn)()) {
|
|
return
|
|
std::shared_ptr<args::abstract_parameter>
|
|
(new args::parameter<void(*)()>(fn));
|
|
}
|
|
std::shared_ptr<args::abstract_parameter> help() {
|
|
return func(args::help_no_arg);
|
|
}
|
|
std::shared_ptr<args::abstract_parameter> exit() {
|
|
return func(args::do_exit);
|
|
}
|
|
}
|
|
}
|
|
#endif
|