new library for arguments - namespace mrw added; refs #6

master
Marc Wäckerlin 11 years ago
parent a6b48ec7c4
commit fbe6874579
  1. 71
      doc/examples/arguments.cxx
  2. 158
      src/mrw/args
  3. 105
      src/mrw/iomanip

@ -21,55 +21,64 @@ int main(int argc, char** argv) try {
// bind and evaluate options // bind and evaluate options
#ifdef ARGS__OLD_PRE11_COMPILER #ifdef ARGS__OLD_PRE11_COMPILER
args::list options; mrw::args::list options;
std::vector<std::shared_ptr<args::abstract_parameter> > params; std::vector<std::shared_ptr<mrw::args::abstract_parameter> > params;
params.push_back(args::help()); params.push_back(mrw::args::help());
params.push_back(args::exit()); params.push_back(mrw::args::exit());
options.push_back(args::declaration("h", "help", "show this help", params)); options.push_back(mrw::args::declaration("h", "help", "show this help",
params));
params.clear(); params.clear();
params.push_back(args::param(r, "number")); params.push_back(mrw::args::param(r, "number"));
options.push_back(args::declaration("r", "repeat", "number of repetitions", options.push_back(mrw::args::declaration("r", "repeat",
"number of repetitions",
params)); params));
params.clear(); params.clear();
params.push_back(args::param(flag)); params.push_back(mrw::args::param(flag));
options.push_back(args::declaration("f", "flag", "check a flag", params)); options.push_back(mrw::args::declaration("f", "flag", "check a flag",
params));
params.clear(); params.clear();
params.push_back(args::param(txt, "text")); params.push_back(mrw::args::param(txt, "text"));
options.push_back(args::declaration("t", "text", "pass a text", params)); options.push_back(mrw::args::declaration("t", "text", "pass a text", params));
params.clear(); params.clear();
params.push_back(args::param(x, "x")); params.push_back(mrw::args::param(x, "x"));
params.push_back(args::param(y, "y")); params.push_back(mrw::args::param(y, "y"));
options.push_back(args::declaration("c", "coord", "add some 2D coordinates", options.push_back(mrw::args::declaration("c", "coord",
"add some 2D coordinates",
params)); params));
params.clear(); params.clear();
params.push_back(args::func(test_func)); params.push_back(mrw::args::func(test_func));
options.push_back(args::declaration("", "test", options.push_back(mrw::args::declaration("", "test",
"call test_func, no shortcut", params)); "call test_func, no shortcut",
args::init(argc, argv, "This is an example for argument processing.", params));
mrw::args::parse(argc, argv, "This is an example for argument processing.",
options); options);
#else #else
args::init(argc, argv, mrw::args::parse(argc, argv,
"This is an example for argument processing. If the description " "This is an example for argument processing. If the"
"is very long, it is automatically splitted over several lines " " description is very long, it is automatically splitted"
"and indented on each new line. Arguments may have several " " over several lines and indented on each new line."
"parameters. Help is generated automatically from the argument " " Arguments may have several parameters. Help is"
"declaration. The argument declaration contains long and short " " generated automatically from the argument declaration."
"options and a list of parameters.", " The argument declaration contains long and short options"
" and a list of parameters.",
{ {
{"h", "help", "show this help", {args::help(), args::exit()}}, {"h", "help", "show this help",
{mrw::args::help(), mrw::args::exit()}},
{"r", "repeat", "number of repetitions", {"r", "repeat", "number of repetitions",
{args::param(r, "number")}}, {mrw::args::param(r, "number")}},
{"f", "flag", {"f", "flag",
"check a flag; this is a boolean, so it is false by " "check a flag; this is a boolean, so it is false by "
"default and true if the user specifies the flag on " "default and true if the user specifies the flag on "
"the command line", {args::param(flag)}}, "the command line", {mrw::args::param(flag)}},
{"t", "text", "pass a text", {args::param(txt, "text")}}, {"t", "text", "pass a text",
{mrw::args::param(txt, "text")}},
{"c", "coord", {"c", "coord",
"add some 2D coordinates; this is an example on how " "add some 2D coordinates; this is an example on how "
"you can add more than one parameter", "you can add more than one parameter",
{args::param(x, "x"), args::param(y, "y")}}, {mrw::args::param(x, "x"),
mrw::args::param(y, "y")}},
{"", "test", "call test_func, no shortcut", {"", "test", "call test_func, no shortcut",
{args::func(test_func)}}, {mrw::args::func(test_func)}},
}, },
"Returns a status of 0 on success and 1 on any error."); "Returns a status of 0 on success and 1 on any error.");
#endif #endif

@ -7,6 +7,8 @@
#ifndef __ARGS__ #ifndef __ARGS__
#define __ARGS__ #define __ARGS__
#include <mrw/iomanip>
#include <vector> #include <vector>
#include <map> #include <map>
#include <memory> #include <memory>
@ -15,10 +17,11 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <numeric> #include <numeric>
#include <cstdlib> #include <cstdlib> // exit()
// check if code is compiled with a new C++11 compiler // check if code is compiled with a new C++11 compiler
// otherwise there is a fallback wich makes everything much more compliacted // otherwise there is a fallback wich makes everything much more compliacted
#ifndef ARGS__OLD_PRE11_COMPILER
#if __cplusplus < 201103L #if __cplusplus < 201103L
/// Code is compiled with an old non C++11 standard compliant compiler /// Code is compiled with an old non C++11 standard compliant compiler
/** There are workarounds for old non C++11 compatible /** There are workarounds for old non C++11 compatible
@ -33,6 +36,13 @@
#warning this is deprecated and will be removed in future releases #warning this is deprecated and will be removed in future releases
#warning refere to the library documentation for more details #warning refere to the library documentation for more details
#endif #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 #ifdef ARGS__OLD_PRE11_COMPILER
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
@ -46,14 +56,9 @@ namespace std {
}; };
#endif #endif
/** @page oldcompiler Workaround for old non C++11 compilers namespace mrw {
... to be documented /// Cool and easy evaluation of commandline arguments in C++11.
@note Old compilers are automatically detected and the flag /** Evaluating command line arguments is extremely easy with this library:
@refs ARGS__OLD_PRE11_COMPILER is set.
*/
/// Cool and easy evaluation of commandline arguments in C++11.
/** Evaluating command line arguments is extremely easy with this library:
@begincode @begincode
void test_func(); // bound to option --test void test_func(); // bound to option --test
@ -66,14 +71,17 @@ namespace std {
int x(0), y(0); // bound to option --coord int x(0), y(0); // bound to option --coord
// bind and evaluate options // bind and evaluate options
args::init(argc, argv, "This is an example for argument processing.", { args::parse(argc, argv, "This is an example for argument processing.",
{
{"h", "help", "show this help", {args::help(), args::exit()}}, {"h", "help", "show this help", {args::help(), args::exit()}},
{"r", "repeat", "number of repetitions", {args::param(r, "number")}}, {"r", "repeat", "number of repetitions",
{args::param(r, "number")}},
{"f", "flag", "check a flag", {args::param(flag)}}, {"f", "flag", "check a flag", {args::param(flag)}},
{"t", "text", "pass a text", {args::param(txt, "text")}}, {"t", "text", "pass a text", {args::param(txt, "text")}},
{"c", "coord", "add some 2D coordinates", {args::param(x, "x"), {"c", "coord", "add some 2D coordinates", {args::param(x, "x"),
args::param(y, "y")}}, args::param(y, "y")}},
{"", "test", "call test_func, no shortcut", {args::func(test_func)}}, {"", "test", "call test_func, no shortcut",
{args::func(test_func)}},
}); });
// [...] now the variables are setup according to the user's choice // [...] now the variables are setup according to the user's choice
@ -90,9 +98,8 @@ namespace std {
non C++11 compliant compilers, so the use of a modern non C++11 compliant compilers, so the use of a modern
compiler is strongly recommended. On GNU GCC, you may have compiler is strongly recommended. On GNU GCC, you may have
to add the commandline option @c -std=c++11 to add the commandline option @c -std=c++11
@note I suggest to take this library into the next C++ standard. @note I suggest to take this library into the next C++ standard. */
*/ namespace args {
namespace args {
/// Parent for holding a reference to a parameter variable. /// Parent for holding a reference to a parameter variable.
/** This class ist used only as parent for instances of template /** This class ist used only as parent for instances of template
parameters. It represents one parameter that is evaluated when parameters. It represents one parameter that is evaluated when
@ -143,7 +150,9 @@ namespace args {
// special case: boolean has no parameter // special case: boolean has no parameter
template<> class parameter<bool>: public abstract_parameter { template<> class parameter<bool>: public abstract_parameter {
public: public:
parameter(bool& ref, const std::string& = std::string()): _ref(ref=false) {} parameter(bool& ref, const std::string& = std::string()):
_ref(ref=false) {
}
virtual void evaluate(char**&, char**) { virtual void evaluate(char**&, char**) {
_ref = true; _ref = true;
} }
@ -151,9 +160,11 @@ namespace args {
bool& _ref; bool& _ref;
}; };
// special case: string cannot easily be shifted // special case: string cannot easily be shifted
template<typename T> class parameter<std::basic_string<T> >: public abstract_parameter { template<typename T> class parameter<std::basic_string<T> >:
public abstract_parameter {
public: public:
parameter(std::basic_string<T>& ref, const std::string& s = std::string()): parameter(std::basic_string<T>& ref,
const std::string& s = std::string()):
_ref(ref), _type(s) {} _ref(ref), _type(s) {}
virtual void evaluate(char**& a, char** max) { virtual void evaluate(char**& a, char** max) {
if (max<a+1) if (max<a+1)
@ -178,7 +189,9 @@ namespace args {
// special case: if a function is called // special case: if a function is called
template<> class parameter<void(*)()>: public abstract_parameter { template<> class parameter<void(*)()>: public abstract_parameter {
public: public:
parameter(void(*ref)(), const std::string& = std::string()): _ref(ref) {} parameter(void(*ref)(), const std::string& = std::string()):
_ref(ref) {
}
virtual void evaluate(char**&, char**) { virtual void evaluate(char**&, char**) {
_ref(); _ref();
} }
@ -191,7 +204,7 @@ namespace args {
return std::shared_ptr<abstract_parameter>(new args::parameter<T>(t, s)); return std::shared_ptr<abstract_parameter>(new args::parameter<T>(t, s));
} }
struct declaration { struct declaration {
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
declaration(std::string p1, std::string p2, std::string p3, declaration(std::string p1, std::string p2, std::string p3,
std::vector<std::shared_ptr<abstract_parameter> > p4): std::vector<std::shared_ptr<abstract_parameter> > p4):
short_arg(p1), long_arg(p2), desc(p3), params(p4) { short_arg(p1), long_arg(p2), desc(p3), params(p4) {
@ -215,22 +228,21 @@ namespace args {
return a; return a;
} }
void match(const std::string& arg, char**& a, char** max) { void match(const std::string& arg, char**& a, char** max) {
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
arg_map::iterator it(args().find(arg)); arg_map::iterator it(args().find(arg));
#else #else
auto it(args().find(arg)); auto it(args().find(arg));
#endif #endif
if (it==args().end()) if (it==args().end())
throw std::runtime_error("unknown argument: "+std::string(*a)); throw std::runtime_error("unknown argument: "+std::string(*a));
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
for (std::vector<std::shared_ptr<abstract_parameter> >::iterator for (std::vector<std::shared_ptr<abstract_parameter> >::iterator
it2(it->second.begin()); it2!=it->second.end(); ++it2) { it2(it->second.begin()); it2!=it->second.end(); ++it2)
#else #else
for (auto it2(it->second.begin()); it2!=it->second.end(); ++it2) { for (auto it2(it->second.begin()); it2!=it->second.end(); ++it2)
#endif #endif
(*it2)->evaluate(a, max); (*it2)->evaluate(a, max);
} }
}
/// Filename as passed in argv[0]. /// Filename as passed in argv[0].
/** Used in the help display. /** Used in the help display.
@note This function emulates a global variable using parts of a @note This function emulates a global variable using parts of a
@ -263,30 +275,32 @@ namespace args {
used in @ref show_help used in @ref show_help
@param l list of options and parameters to be parsed @param l list of options and parameters to be parsed
@raram ret documentation of the return values of the program */ @raram ret documentation of the return values of the program */
static void init(int argc, char** argv, const std::string& desc, list l, static void parse(int argc, char** argv, const std::string& desc, list l,
const std::string& ret = std::string()) { const std::string& ret = std::string()) {
filename(argv[0]); // store filename for later use in help filename(argv[0]); // store filename for later use in help
description(desc); // store program description for later use in help description(desc); // store program description for later use in help
returns(ret); // store return values 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 arg_list() = l; // store options and parameters for later use in help
// setup the argument mapping table // setup the argument mapping table
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
for (list::iterator it(l.begin()); it!=l.end(); ++it) { for (list::iterator it(l.begin()); it!=l.end(); ++it)
#else #else
for (auto it(l.begin()); it!=l.end(); ++it) { for (auto it(l.begin()); it!=l.end(); ++it)
#endif #endif
{
if (it->short_arg.size()==1) args()[it->short_arg] = it->params; if (it->short_arg.size()==1) args()[it->short_arg] = it->params;
if (it->long_arg.size()) args()["--"+it->long_arg] = it->params; if (it->long_arg.size()) args()["--"+it->long_arg] = it->params;
} }
// parse commandline and evaluate the arguments // parse commandline and evaluate the arguments
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
for (char** a(argv+1); a<argv+argc; ++a) { for (char** a(argv+1); a<argv+argc; ++a)
#else #else
for (auto a(argv+1); a<argv+argc; ++a) { for (auto a(argv+1); a<argv+argc; ++a)
#endif #endif
{
std::string arg(*a); std::string arg(*a);
if (arg.size()>1&&arg[0]=='-'&&arg[1]!='-') { // short argument if (arg.size()>1&&arg[0]=='-'&&arg[1]!='-') { // short argument
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
for (std::string::iterator it(arg.begin()+1); it!=arg.end(); ++it) for (std::string::iterator it(arg.begin()+1); it!=arg.end(); ++it)
#else #else
for (auto it(arg.begin()+1); it!=arg.end(); ++it) for (auto it(arg.begin()+1); it!=arg.end(); ++it)
@ -297,72 +311,6 @@ namespace args {
} }
} }
} }
//! IO-Manipulator to split long lines into several shorter lines.
template <class _CharT = char, class _Traits = std::char_traits<_CharT> >
class basic_split /*: public std::basic_ostream<_CharT, _Traits>*/ {
public:
basic_split(int width=80, int indent=0, char fill=' '):
_width(width), _indent(indent), _fill(fill), _os(0) {
if (_width<_indent)
#ifndef ARGS__CPLUSPLUS11
throw std::runtime_error(((std::stringstream&)
(std::stringstream()
<<"wrong use of split: width is "<<_width
<<" but should be larger than indent,"
<<" which is "<<_indent)).str());
#else
throw std::runtime_error("wrong use of split: width is "+
std::to_string(_width)+
" but should be larger than indent,"
" which is "+std::to_string(_indent));
#endif
}
virtual ~basic_split() {
flush();
}
friend basic_split& operator<<(std::basic_ostream<_CharT, _Traits>& os,
const basic_split& s) {
if (s._os!=&os) {
const_cast<basic_split&>(s).flush();
const_cast<basic_split&>(s)._os = &os;
}
return const_cast<basic_split&>(s);
}
template <typename T> basic_split& operator<<(T t) {
if (!_os)
throw std::runtime_error("wrong use of split, it is an io manipulator"
" and must be used within a stream");
_buffer<<t;
while (_buffer.str().size()>=_width-_indent) {
if (_indent) *_os<<std::setw(_indent)<<std::setfill(_fill)<<_fill;
std::string::size_type pos
(_buffer.str().find_last_of(' ', _width-_indent));
std::string::size_type next(pos+1);
if (pos==std::string::npos) {
pos=_width-_indent;
next=pos;
}
*_os<<_buffer.str().substr(0, pos)<<std::endl;
_buffer.str(_buffer.str().substr(next));
}
return *this;
}
private:
void flush() {
if (_os) {
if (_indent) *_os<<std::setw(_indent)<<std::setfill(_fill)<<_fill;
*_os<<_buffer.str();
_buffer.clear();
}
}
private:
int _width;
int _indent;
char _fill;
std::stringstream _buffer;
std::basic_ostream<_CharT, _Traits>* _os;
};
typedef basic_split<char> split;
void show_help(const std::string& synopsis_txt="SYNOPSIS", void show_help(const std::string& synopsis_txt="SYNOPSIS",
const std::string& description_txt="DESCRIPTION", const std::string& description_txt="DESCRIPTION",
const std::string& options_txt="OPTIONS", const std::string& options_txt="OPTIONS",
@ -375,15 +323,16 @@ namespace args {
<<split(max_line, indent)<<description(); //! @bug <<split(max_line, indent)<<description(); //! @bug
std::cout<<std::endl<<std::endl std::cout<<std::endl<<std::endl
<<options_txt<<std::endl; <<options_txt<<std::endl;
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
for (list::iterator arg(arg_list().begin()); arg!=arg_list().end(); ++arg) { for (list::iterator arg(arg_list().begin()); arg!=arg_list().end(); ++arg)
#else #else
for (auto arg(arg_list().begin()); arg!=arg_list().end(); ++arg) { for (auto arg(arg_list().begin()); arg!=arg_list().end(); ++arg)
#endif #endif
{
std::string o((arg->short_arg.size()?"-"+arg->short_arg:"")+ std::string o((arg->short_arg.size()?"-"+arg->short_arg:"")+
(arg->short_arg.size()&&arg->long_arg.size()?", ":"")+ (arg->short_arg.size()&&arg->long_arg.size()?", ":"")+
(arg->long_arg.size()?"--"+arg->long_arg:"")); (arg->long_arg.size()?"--"+arg->long_arg:""));
#ifndef ARGS__CPLUSPLUS11 #ifdef ARGS__OLD_PRE11_COMPILER
std::string a; std::string a;
for (std::vector<std::shared_ptr<abstract_parameter> >::iterator for (std::vector<std::shared_ptr<abstract_parameter> >::iterator
p(arg->params.begin()); p(arg->params.begin());
@ -434,5 +383,6 @@ namespace args {
std::shared_ptr<args::abstract_parameter> exit() { std::shared_ptr<args::abstract_parameter> exit() {
return func(args::do_exit); return func(args::do_exit);
} }
}
} }
#endif #endif

@ -0,0 +1,105 @@
/*! @file
@id $Id$
*/
// 1 2 3 4 5 6 7 8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890
#ifndef __MRW__IOMANIP__
#define __MRW__IOMANIP__
#include <stdexcept>
#include <sstream>
#include <string>
#include <iomanip>
// 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
namespace mrw {
//! IO-Manipulator to split long lines into several shorter lines.
template <class _CharT = char, class _Traits = std::char_traits<_CharT> >
class basic_split /*: public std::basic_ostream<_CharT, _Traits>*/ {
public:
basic_split(int width=80, int indent=0, char fill=' '):
_width(width), _indent(indent), _fill(fill), _os(0) {
if (_width<_indent)
#ifdef ARGS__OLD_PRE11_COMPILER
throw std::runtime_error(((std::stringstream&)
(std::stringstream()
<<"wrong use of split: width is "<<_width
<<" but should be larger than indent,"
<<" which is "<<_indent)).str());
#else
throw std::runtime_error("wrong use of split: width is "+
std::to_string(_width)+
" but should be larger than indent,"
" which is "+std::to_string(_indent));
#endif
}
virtual ~basic_split() {
flush();
}
friend basic_split& operator<<(std::basic_ostream<_CharT, _Traits>& os,
const basic_split& s) {
if (s._os!=&os) {
const_cast<basic_split&>(s).flush();
const_cast<basic_split&>(s)._os = &os;
}
return const_cast<basic_split&>(s);
}
template <typename T> basic_split& operator<<(T t) {
if (!_os)
throw std::runtime_error("wrong use of split, it is an io"
" manipulator and must be used within"
" a stream");
_buffer<<t;
while (_buffer.str().size()>=_width-_indent) {
if (_indent) *_os<<std::setw(_indent)<<std::setfill(_fill)<<_fill;
std::string::size_type pos
(_buffer.str().find_last_of(' ', _width-_indent));
std::string::size_type next(pos+1);
if (pos==std::string::npos) {
pos=_width-_indent;
next=pos;
}
*_os<<_buffer.str().substr(0, pos)<<std::endl;
_buffer.str(_buffer.str().substr(next));
}
return *this;
}
private:
void flush() {
if (_os) {
if (_indent) *_os<<std::setw(_indent)<<std::setfill(_fill)<<_fill;
*_os<<_buffer.str();
_buffer.clear();
}
}
private:
int _width;
int _indent;
char _fill;
std::stringstream _buffer;
std::basic_ostream<_CharT, _Traits>* _os;
};
typedef basic_split<char> split;
}
#endif
Loading…
Cancel
Save