/*! @file @id $Id$ */ // 1 2 3 4 5 6 7 8 // 45678901234567890123456789012345678901234567890123456789012345678901234567890 #ifndef __MRW_ARGS__ #define __MRW_ARGS__ #include #include #include #include #include #include #include #include #include #include #include // 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: "< 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)(); }; typedef std::shared_ptr param_ptr; template param_ptr param(T& t, const std::string& s = std::string()) { return param_ptr(new args::parameter(t, s)); } struct decl { typedef std::vector 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 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) { #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); a1&&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<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<desc.size()>max_line-indent-option_len-param_len) std::cout<desc; else std::cout<desc; std::cout<(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