diff --git a/doc/examples/makefile.am b/doc/examples/makefile.am index a05446e..99f3829 100644 --- a/doc/examples/makefile.am +++ b/doc/examples/makefile.am @@ -4,17 +4,19 @@ ## 45678901234567890123456789012345678901234567890123456789012345678901234567890 exampledir = ${docdir}/examples -example_PROGRAMS = exceptionhandling smartpointer -example_DATA = ${smartpointer_SOURCES} ${exceptionhandling_SOURCES} +example_PROGRAMS = exceptionhandling smartpointer arguments +example_DATA = ${smartpointer_SOURCES} ${exceptionhandling_SOURCES} \ + ${arguments_SOURCES} + +CPPFLAGS = -I${top_srcdir}/src +LDFLAGS = -L${top_builddir}/src exceptionhandling_SOURCES = exceptionhandling.cxx -exceptionhandling_CPPFLAGS = -I${top_srcdir}/src -exceptionhandling_LDFLAGS = -L${top_builddir}/src exceptionhandling_LDADD = ${top_builddir}/src/.libs/libmrw.la smartpointer_SOURCES = smartpointer.cxx -smartpointer_CPPFLAGS = -I${top_srcdir}/src -smartpointer_LDFLAGS = -L${top_builddir}/src smartpointer_LDADD = ${top_builddir}/src/.libs/libmrw.la +arguments_SOURCES = arguments.cxx + MAINTAINERCLEANFILES = makefile.in \ No newline at end of file diff --git a/mrw-c++.spec.in b/mrw-c++.spec.in index 082ff47..780fbc4 100644 --- a/mrw-c++.spec.in +++ b/mrw-c++.spec.in @@ -12,7 +12,7 @@ # rpmbuild -bb --clean @PACKAGENAME@.spec BuildRequires: boost-devel subversion gcc-c++ doxygen graphviz texlive automake autoconf libtool make %if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version} -BuildRequires: cppunit-devel +BuildRequires: cppunit-devel libtool-ltdl-devel %else%if 0%{?suse_version} || 0%{?sles_version} BuildRequires: libcppunit-devel %endif%endif diff --git a/src/makefile.am b/src/makefile.am index 4f40f4e..2af95d1 100644 --- a/src/makefile.am +++ b/src/makefile.am @@ -14,7 +14,7 @@ nobase_include_HEADERS = mrw/arg.hxx mrw/auto.hxx mrw/configfile.hxx \ mrw/smartpointer.hxx mrw/stacktrace.hxx \ mrw/stdext.hxx mrw/string.hxx \ mrw/tokenizer.hxx mrw/unistd.hxx \ - mrw/vector.hxx + mrw/vector.hxx mrw/args if HAVE_STACKTRACE AM_CPPFLAGS += -DHAVE_STACKTRACE diff --git a/src/mrw/args b/src/mrw/args new file mode 100644 index 0000000..b71c618 --- /dev/null +++ b/src/mrw/args @@ -0,0 +1,438 @@ +/*! @file + + @id $Id$ +*/ +// 1 2 3 4 5 6 7 8 +// 45678901234567890123456789012345678901234567890123456789012345678901234567890 +#ifndef __ARGS__ +#define __ARGS__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// check if code is compiled with a new C++11 compiler +// otherwise there is a fallback wich makes everything much more compliacted +#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 + +#ifdef ARGS__OLD_PRE11_COMPILER +#include +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 class shared_ptr: public boost::shared_ptr { + public: + explicit shared_ptr(T* p): boost::shared_ptr(p) {} + }; +}; +#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. + */ + +/// 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::init(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: "< 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>_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: 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 class parameter >: public abstract_parameter { + public: + parameter(std::basic_string& ref, const std::string& s = std::string()): + _ref(ref), _type(s) {} + virtual void evaluate(char**& a, char** max) { + if (max(std::istreambuf_iterator(ss), + std::istreambuf_iterator()); + } + virtual operator std::string() { + std::stringstream ss; + ss<<_ref; + return ss.str(); + } + virtual std::string type() { + return _type.size()?" <"+_type+">":""; + } + private: + std::basic_string& _ref; + std::string _type; + }; + // special case: if a function is called + template<> class parameter: public abstract_parameter { + public: + parameter(void(*ref)(), const std::string& = std::string()): _ref(ref) {} + virtual void evaluate(char**&, char**) { + _ref(); + } + private: + void(*_ref)(); + }; + template + std::shared_ptr param + (T& t, const std::string& s = std::string()) { + return std::shared_ptr(new args::parameter(t, s)); + } + struct declaration { +#ifndef ARGS__CPLUSPLUS11 + declaration(std::string p1, std::string p2, std::string p3, + std::vector > 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 > params; + }; + typedef std::map > > + arg_map; + static arg_map& args() { + static arg_map a; + return a; + } + typedef std::vector list; + static list& arg_list() { + static list a; + return a; + } + void match(const std::string& arg, char**& a, char** max) { +#ifndef ARGS__CPLUSPLUS11 + 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)); +#ifndef ARGS__CPLUSPLUS11 + for (std::vector >::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 init(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 values description for later use in help + arg_list() = l; // store options and parameters for later use in help + // setup the argument mapping table +#ifndef ARGS__CPLUSPLUS11 + 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 +#ifndef ARGS__CPLUSPLUS11 + for (char** a(argv+1); a1&&arg[0]=='-'&&arg[1]!='-') { // short argument +#ifndef ARGS__CPLUSPLUS11 + 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); + } + } + } + //! IO-Manipulator to split long lines into several shorter lines. + template > + 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(s).flush(); + const_cast(s)._os = &os; + } + return const_cast(s); + } + template 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<=_width-_indent) { + if (_indent) *_os<* _os; + }; + typedef basic_split split; + 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<short_arg.size()?"-"+arg->short_arg:"")+ + (arg->short_arg.size()&&arg->long_arg.size()?", ":"")+ + (arg->long_arg.size()?"--"+arg->long_arg:"")); +#ifndef ARGS__CPLUSPLUS11 + std::string a; + for (std::vector >::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 p) + -> std::string { + std::string def(**p); + return s+p->type()+(def.size()?"="+def:""); + })); +#endif + std::cout<desc.size()>max_line-indent-option_len-param_len) + std::cout<desc; + else + std::cout<desc; + std::cout< func(void(*fn)()) { + return + std::shared_ptr + (new args::parameter(fn)); + } + std::shared_ptr help() { + return func(args::help_no_arg); + } + std::shared_ptr exit() { + return func(args::do_exit); + } +} +#endif