From 36e3bc290d58ad29692a4d594f37a6cb761912c7 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 28 Nov 2009 11:41:15 +0200 Subject: Use a scanner interface instead of argc/argv This will allow supporting other sources of options, for example, an option file. --- cli/header.cxx | 6 +- cli/options.cxx | 193 ++++++++++++++++++++++++++++++++----------------- cli/options.hxx | 61 +++++++++++++++- cli/options.ixx | 20 +++++ cli/runtime-header.cxx | 60 +++++++++++++++ cli/runtime-inline.cxx | 21 ++++++ cli/runtime-source.cxx | 152 ++++++++++++++++++++++++++------------ cli/source.cxx | 51 +++++++------ 8 files changed, 419 insertions(+), 145 deletions(-) diff --git a/cli/header.cxx b/cli/header.cxx index 2749b6d..0cc9ede 100644 --- a/cli/header.cxx +++ b/cli/header.cxx @@ -123,10 +123,8 @@ namespace // _parse() // os << "private:" << endl - << "int" << endl - << "_parse (int start," << endl - << "int argc," << endl - << "char** argv," << endl + << "void" << endl + << "_parse (::cli::scanner&," << endl << um << " option," << endl << um << " argument);" << endl; diff --git a/cli/options.cxx b/cli/options.cxx index 78b3f5f..399069e 100644 --- a/cli/options.cxx +++ b/cli/options.cxx @@ -90,98 +90,155 @@ namespace cli return "invalid option value"; } + // eos_reached + // + void eos_reached:: + print (std::ostream& os) const + { + os << what (); + } + + const char* eos_reached:: + what () const throw () + { + return "end of argument stream reached"; + } + + // scanner + // + scanner:: + ~scanner () + { + } + + // argv_scanner + // + bool argv_scanner:: + more () + { + return i_ < argc_; + } + + const char* argv_scanner:: + peek () + { + if (i_ < argc_) + return argv_[i_]; + else + throw eos_reached (); + } + + const char* argv_scanner:: + next () + { + if (i_ < argc_) + return argv_[i_++]; + else + throw eos_reached (); + } + + void argv_scanner:: + skip () + { + if (i_ < argc_) + i_++; + else + throw eos_reached (); + } + template struct parser { - static int - parse (X& x, char** argv, int n) + static void + parse (X& x, scanner& s) { - if (n > 1) + const char* o (s.next ()); + + if (s.more ()) { - std::istringstream is (argv[1]); + const char* v (s.next ()); + std::istringstream is (v); if (!(is >> x && is.eof ())) - throw invalid_value (argv[0], argv[1]); - return 2; + throw invalid_value (o, v); } else - throw missing_value (argv[0]); + throw missing_value (o); } }; template <> struct parser { - static int - parse (bool& x, char**, int) + static void + parse (bool& x, scanner& s) { + s.next (); x = true; - return 1; } }; template <> struct parser { - static int - parse (std::string& x, char** argv, int n) + static void + parse (std::string& x, scanner& s) { - if (n > 1) - { - x = argv[1]; - return 2; - } + const char* o (s.next ()); + + if (s.more ()) + x = s.next (); else - throw missing_value (argv[0]); + throw missing_value (o); } }; template struct parser > { - static int - parse (std::vector& v, char** argv, int n) + static void + parse (std::vector& c, scanner& s) { X x; - int i = parser::parse (x, argv, n); - v.push_back (x); - return i; + parser::parse (x, s); + c.push_back (x); } }; template struct parser > { - static int - parse (std::set& s, char** argv, int n) + static void + parse (std::set& c, scanner& s) { X x; - int i = parser::parse (x, argv, n); - s.insert (x); - return i; + parser::parse (x, s); + c.insert (x); } }; template struct parser > { - static int - parse (std::map& m, char** argv, int n) + static void + parse (std::map& m, scanner& s) { - if (n > 1) + const char* o (s.next ()); + + if (s.more ()) { - std::string s (argv[1]); - std::string::size_type p = s.find ('='); + std::string ov (s.next ()); + std::string::size_type p = ov.find ('='); if (p == std::string::npos) { K k = K (); - if (!s.empty ()) + if (!ov.empty ()) { - std::istringstream ks (s); + std::istringstream ks (ov); if (!(ks >> k && ks.eof ())) - throw invalid_value (argv[0], argv[1]); + throw invalid_value (o, ov); } m[k] = V (); @@ -190,15 +247,15 @@ namespace cli { K k = K (); V v = V (); - std::string kstr (s, 0, p); - std::string vstr (s, p + 1); + std::string kstr (ov, 0, p); + std::string vstr (ov, p + 1); if (!kstr.empty ()) { std::istringstream ks (kstr); if (!(ks >> k && ks.eof ())) - throw invalid_value (argv[0], argv[1]); + throw invalid_value (o, ov); } if (!vstr.empty ()) @@ -206,24 +263,22 @@ namespace cli std::istringstream vs (vstr); if (!(vs >> v && vs.eof ())) - throw invalid_value (argv[0], argv[1]); + throw invalid_value (o, ov); } m[k] = v; } - - return 2; } else - throw missing_value (argv[0]); + throw missing_value (o); } }; template - int - thunk (X& x, char** argv, int n) + void + thunk (X& x, scanner& s) { - return parser::parse (x.*P, argv, n); + parser::parse (x.*P, s); } } @@ -267,7 +322,8 @@ options (int argc, guard_prefix_ (), reserved_name_ () { - _parse (1, argc, argv, opt, arg); + ::cli::argv_scanner s (argc, argv); + _parse (s, opt, arg); } options:: @@ -305,7 +361,8 @@ options (int start, guard_prefix_ (), reserved_name_ () { - _parse (start, argc, argv, opt, arg); + ::cli::argv_scanner s (start, argc, argv); + _parse (s, opt, arg); } options:: @@ -343,7 +400,9 @@ options (int argc, guard_prefix_ (), reserved_name_ () { - end = _parse (1, argc, argv, opt, arg); + ::cli::argv_scanner s (argc, argv); + _parse (s, opt, arg); + end = s.end (); } options:: @@ -382,7 +441,9 @@ options (int start, guard_prefix_ (), reserved_name_ () { - end = _parse (start, argc, argv, opt, arg); + ::cli::argv_scanner s (start, argc, argv); + _parse (s, opt, arg); + end = s.end (); } void options:: @@ -467,7 +528,7 @@ print_usage (::std::ostream& os) } typedef -std::map +std::map _cli_options_map; static _cli_options_map _cli_options_map_; @@ -537,40 +598,38 @@ struct _cli_options_map_init } } _cli_options_map_init_; -int options:: -_parse (int start, - int argc, - char** argv, +void options:: +_parse (::cli::scanner& s, ::cli::unknown_mode opt_mode, ::cli::unknown_mode arg_mode) { bool opt = true; - for (; start < argc;) + while (s.more ()) { - const char* s = argv[start]; + const char* o = s.peek (); - if (std::strcmp (s, "--") == 0) + if (std::strcmp (o, "--") == 0) { - start++; + s.skip (); opt = false; continue; } _cli_options_map::const_iterator i ( - opt ? _cli_options_map_.find (s) : _cli_options_map_.end ()); + opt ? _cli_options_map_.find (o) : _cli_options_map_.end ()); if (i != _cli_options_map_.end ()) { - start += (*(i->second)) (*this, argv + start, argc - start); + (*(i->second)) (*this, s); } - else if (opt && std::strncmp (s, "-", 1) == 0 && s[1] != '\0') + else if (opt && std::strncmp (o, "-", 1) == 0 && o[1] != '\0') { switch (opt_mode) { case ::cli::unknown_mode::skip: { - start++; + s.skip (); continue; } case ::cli::unknown_mode::stop: @@ -579,7 +638,7 @@ _parse (int start, } case ::cli::unknown_mode::fail: { - throw ::cli::unknown_option (s); + throw ::cli::unknown_option (o); } } @@ -591,7 +650,7 @@ _parse (int start, { case ::cli::unknown_mode::skip: { - start++; + s.skip (); continue; } case ::cli::unknown_mode::stop: @@ -600,14 +659,12 @@ _parse (int start, } case ::cli::unknown_mode::fail: { - throw ::cli::unknown_argument (s); + throw ::cli::unknown_argument (o); } } break; } } - - return start; } diff --git a/cli/options.hxx b/cli/options.hxx index d5712d1..2be8e91 100644 --- a/cli/options.hxx +++ b/cli/options.hxx @@ -133,6 +133,61 @@ namespace cli std::string option_; std::string value_; }; + + class eos_reached: public exception + { + public: + virtual void + print (std::ostream&) const; + + virtual const char* + what () const throw (); + }; + + class scanner + { + public: + virtual + ~scanner (); + + virtual bool + more () = 0; + + virtual const char* + peek () = 0; + + virtual const char* + next () = 0; + + virtual void + skip () = 0; + }; + + class argv_scanner: public scanner + { + public: + argv_scanner (int argc, char** argv); + argv_scanner (int start, int argc, char** argv); + + int + end () const; + + virtual bool + more (); + + virtual const char* + peek (); + + virtual const char* + next (); + + virtual void + skip (); + + private:int i_; + int argc_; + char** argv_; + }; } #include @@ -264,10 +319,8 @@ class options print_usage (::std::ostream&); private: - int - _parse (int start, - int argc, - char** argv, + void + _parse (::cli::scanner&, ::cli::unknown_mode option, ::cli::unknown_mode argument); diff --git a/cli/options.ixx b/cli/options.ixx index 60afbda..6fcc80f 100644 --- a/cli/options.ixx +++ b/cli/options.ixx @@ -83,6 +83,26 @@ namespace cli { return value_; } + + // argv_scanner + // + inline argv_scanner:: + argv_scanner (int argc, char** argv) + : i_ (1), argc_ (argc), argv_ (argv) + { + } + + inline argv_scanner:: + argv_scanner (int start, int argc, char** argv) + : i_ (start), argc_ (argc), argv_ (argv) + { + } + + inline int argv_scanner:: + end () const + { + return i_; + } } // options diff --git a/cli/runtime-header.cxx b/cli/runtime-header.cxx index 055754c..cf3ebd3 100644 --- a/cli/runtime-header.cxx +++ b/cli/runtime-header.cxx @@ -145,5 +145,65 @@ generate_runtime_header (context& ctx) << "std::string value_;" << "};"; + os << "class eos_reached: public exception" + << "{" + << "public:" << endl + << "virtual void" << endl + << "print (std::ostream&) const;" + << endl + << "virtual const char*" << endl + << "what () const throw ();" + << "};"; + + // scanner + // + os << "class scanner" + << "{" + << "public:" << endl + << "virtual" << endl + << "~scanner ();" + << endl + << "virtual bool" << endl + << "more () = 0;" + << endl + << "virtual const char*" << endl + << "peek () = 0;" + << endl + << "virtual const char*" << endl + << "next () = 0;" + << endl + << "virtual void" << endl + << "skip () = 0;" + << "};"; + + // argv_scanner + // + os << "class argv_scanner: public scanner" + << "{" + << "public:" << endl + << "argv_scanner (int argc, char** argv);" + << "argv_scanner (int start, int argc, char** argv);" + << endl + << "int" << endl + << "end () const;" + << endl + << "virtual bool" << endl + << "more ();" + << endl + << "virtual const char*" << endl + << "peek ();" + << endl + << "virtual const char*" << endl + << "next ();" + << endl + << "virtual void" << endl + << "skip ();" + << endl + << "private:" + << "int i_;" + << "int argc_;" + << "char** argv_;" + << "};"; + os << "}"; // namespace cli } diff --git a/cli/runtime-inline.cxx b/cli/runtime-inline.cxx index 9c33c2e..d4ce3ec 100644 --- a/cli/runtime-inline.cxx +++ b/cli/runtime-inline.cxx @@ -103,5 +103,26 @@ generate_runtime_inline (context& ctx) << "return value_;" << "}"; + os << "// argv_scanner" << endl + << "//" << endl; + + os << inl << "argv_scanner::" << endl + << "argv_scanner (int argc, char** argv)" << endl + << ": i_ (1), argc_ (argc), argv_ (argv)" + << "{" + << "}"; + + os << inl << "argv_scanner::" << endl + << "argv_scanner (int start, int argc, char** argv)" << endl + << ": i_ (start), argc_ (argc), argv_ (argv)" + << "{" + << "}"; + + os << inl << "int argv_scanner::" << endl + << "end () const" + << "{" + << "return i_;" + << "}"; + os << "}"; // namespace cli } diff --git a/cli/runtime-source.cxx b/cli/runtime-source.cxx index 9f96416..3003b6b 100644 --- a/cli/runtime-source.cxx +++ b/cli/runtime-source.cxx @@ -100,23 +100,87 @@ generate_runtime_source (context& ctx) << "return \"invalid option value\";" << "}"; + // eos_reached + // + os << "// eos_reached" << endl + << "//" << endl + << "void eos_reached::" << endl + << "print (std::ostream& os) const" + << "{" + << "os << what ();" + << "}" + << "const char* eos_reached::" << endl + << "what () const throw ()" + << "{" + << "return \"end of argument stream reached\";" + << "}"; + + // scanner + // + os << "// scanner" << endl + << "//" << endl + << "scanner::" << endl + << "~scanner ()" + << "{" + << "}"; + + // argv_scanner + // + os << "// argv_scanner" << endl + << "//" << endl + + << "bool argv_scanner::" << endl + << "more ()" + << "{" + << "return i_ < argc_;" + << "}" + + << "const char* argv_scanner::" << endl + << "peek ()" + << "{" + << "if (i_ < argc_)" << endl + << "return argv_[i_];" + << "else" << endl + << "throw eos_reached ();" + << "}" + + << "const char* argv_scanner::" << endl + << "next ()" + << "{" + << "if (i_ < argc_)" << endl + << "return argv_[i_++];" + << "else" << endl + << "throw eos_reached ();" + << "}" + + << "void argv_scanner::" << endl + << "skip ()" + << "{" + << "if (i_ < argc_)" << endl + << "i_++;" + << "else" << endl + << "throw eos_reached ();" + << "}"; + // parser class template & its specializations // os << "template " << endl << "struct parser" << "{" - << "static int" << endl - << "parse (X& x, char** argv, int n)" + << "static void" << endl + << "parse (X& x, scanner& s)" << "{" - << "if (n > 1)" + << "const char* o (s.next ());" + << endl + << "if (s.more ())" << "{" - << "std::istringstream is (argv[1]);" + << "const char* v (s.next ());" + << "std::istringstream is (v);" << "if (!(is >> x && is.eof ()))" << endl - << "throw invalid_value (argv[0], argv[1]);" - << "return 2;" + << "throw invalid_value (o, v);" << "}" << "else" << endl - << "throw missing_value (argv[0]);" + << "throw missing_value (o);" << "}" << "};"; @@ -125,11 +189,11 @@ generate_runtime_source (context& ctx) os << "template <>" << endl << "struct parser" << "{" - << "static int" << endl - << "parse (bool& x, char**, int)" + << "static void" << endl + << "parse (bool& x, scanner& s)" << "{" + << "s.next ();" << "x = true;" - << "return 1;" << "}" << "};"; @@ -138,16 +202,15 @@ generate_runtime_source (context& ctx) os << "template <>" << endl << "struct parser" << "{" - << "static int" << endl - << "parse (std::string& x, char** argv, int n)" + << "static void" << endl + << "parse (std::string& x, scanner& s)" << "{" - << "if (n > 1)" - << "{" - << "x = argv[1];" - << "return 2;" - << "}" + << "const char* o (s.next ());" + << endl + << "if (s.more ())" << endl + << "x = s.next ();" << "else" << endl - << "throw missing_value (argv[0]);" + << "throw missing_value (o);" << "}" << "};"; @@ -156,13 +219,12 @@ generate_runtime_source (context& ctx) os << "template " << endl << "struct parser >" << "{" - << "static int" << endl - << "parse (std::vector& v, char** argv, int n)" + << "static void" << endl + << "parse (std::vector& c, scanner& s)" << "{" << "X x;" - << "int i = parser::parse (x, argv, n);" - << "v.push_back (x);" - << "return i;" + << "parser::parse (x, s);" + << "c.push_back (x);" << "}" << "};"; @@ -171,13 +233,12 @@ generate_runtime_source (context& ctx) os << "template " << endl << "struct parser >" << "{" - << "static int" << endl - << "parse (std::set& s, char** argv, int n)" + << "static void" << endl + << "parse (std::set& c, scanner& s)" << "{" << "X x;" - << "int i = parser::parse (x, argv, n);" - << "s.insert (x);" - << "return i;" + << "parser::parse (x, s);" + << "c.insert (x);" << "}" << "};"; @@ -186,24 +247,26 @@ generate_runtime_source (context& ctx) os << "template " << endl << "struct parser >" << "{" - << "static int" << endl - << "parse (std::map& m, char** argv, int n)" + << "static void" << endl + << "parse (std::map& m, scanner& s)" << "{" - << "if (n > 1)" + << "const char* o (s.next ());" + << endl + << "if (s.more ())" << "{" - << "std::string s (argv[1]);" - << "std::string::size_type p = s.find ('=');" + << "std::string ov (s.next ());" + << "std::string::size_type p = ov.find ('=');" << endl << "if (p == std::string::npos)" << "{" << "K k = K ();" << endl - << "if (!s.empty ())" + << "if (!ov.empty ())" << "{" - << "std::istringstream ks (s);" + << "std::istringstream ks (ov);" << endl << "if (!(ks >> k && ks.eof ()))" << endl - << "throw invalid_value (argv[0], argv[1]);" + << "throw invalid_value (o, ov);" << "}" << "m[k] = V ();" << "}" @@ -211,39 +274,38 @@ generate_runtime_source (context& ctx) << "{" << "K k = K ();" << "V v = V ();" - << "std::string kstr (s, 0, p);" - << "std::string vstr (s, p + 1);" + << "std::string kstr (ov, 0, p);" + << "std::string vstr (ov, p + 1);" << endl << "if (!kstr.empty ())" << "{" << "std::istringstream ks (kstr);" << endl << "if (!(ks >> k && ks.eof ()))" << endl - << "throw invalid_value (argv[0], argv[1]);" + << "throw invalid_value (o, ov);" << "}" << "if (!vstr.empty ())" << "{" << "std::istringstream vs (vstr);" << endl << "if (!(vs >> v && vs.eof ()))" << endl - << "throw invalid_value (argv[0], argv[1]);" + << "throw invalid_value (o, ov);" << "}" << "m[k] = v;" << "}" - << "return 2;" << "}" << "else" << endl - << "throw missing_value (argv[0]);" + << "throw missing_value (o);" << "}" << "};"; // Parser thunk. // os << "template " << endl - << "int" << endl - << "thunk (X& x, char** argv, int n)" + << "void" << endl + << "thunk (X& x, scanner& s)" << "{" - << "return parser::parse (x.*P, argv, n);" + << "parser::parse (x.*P, s);" << "}"; os << "}"; // namespace cli diff --git a/cli/source.cxx b/cli/source.cxx index f473665..5f0b199 100644 --- a/cli/source.cxx +++ b/cli/source.cxx @@ -367,7 +367,8 @@ namespace names (c, names_init); } os << "{" - << "_parse (1, argc, argv, opt, arg);" + << "::cli::argv_scanner s (argc, argv);" + << "_parse (s, opt, arg);" << "}"; os << name << "::" << endl @@ -383,7 +384,8 @@ namespace names (c, names_init); } os << "{" - << "_parse (start, argc, argv, opt, arg);" + << "::cli::argv_scanner s (start, argc, argv);" + << "_parse (s, opt, arg);" << "}"; os << name << "::" << endl @@ -399,7 +401,9 @@ namespace names (c, names_init); } os << "{" - << "end = _parse (1, argc, argv, opt, arg);" + << "::cli::argv_scanner s (argc, argv);" + << "_parse (s, opt, arg);" + << "end = s.end ();" << "}"; os << name << "::" << endl @@ -416,7 +420,9 @@ namespace names (c, names_init); } os << "{" - << "end = _parse (start, argc, argv, opt, arg);" + << "::cli::argv_scanner s (start, argc, argv);" + << "_parse (s, opt, arg);" + << "end = s.end ();" << "}"; // usage @@ -468,8 +474,8 @@ namespace string map ("_cli_" + name + "_map"); os << "typedef" << endl - << "std::map" << endl + << "std::map" << endl << map << ";" << endl << "static " << map << " " << map << "_;" @@ -489,10 +495,8 @@ namespace bool pfx (!opt_prefix.empty ()); bool sep (!opt_sep.empty ()); - os << "int " << name << "::" << endl - << "_parse (int start," << endl - << "int argc," << endl - << "char** argv," << endl + os << "void " << name << "::" << endl + << "_parse (::cli::scanner& s," << endl << um << " opt_mode," << endl << um << " arg_mode)" << "{"; @@ -501,27 +505,27 @@ namespace os << "bool opt = true;" // Still recognizing options. << endl; - os << "for (; start < argc;)" + os << "while (s.more ())" << "{" - << "const char* s = argv[start];"; + << "const char* o = s.peek ();"; if (sep) os << endl - << "if (std::strcmp (s, \"" << opt_sep << "\") == 0)" + << "if (std::strcmp (o, \"" << opt_sep << "\") == 0)" << "{" - << "start++;" + << "s.skip ();" // We don't want to remove the separator. << "opt = false;" << "continue;" << "}" << map << "::const_iterator i (" << endl - << "opt ? " << map << "_.find (s) : " << map << "_.end ());"; + << "opt ? " << map << "_.find (o) : " << map << "_.end ());"; else - os << map << "::const_iterator i (" << map << "_.find (s));"; + os << map << "::const_iterator i (" << map << "_.find (o));"; os << endl << "if (i != " << map << "_.end ())" << "{" - << "start += (*(i->second)) (*this, argv + start, argc - start);" + << "(*(i->second)) (*this, s);" << "}"; // Unknown option. @@ -535,14 +539,14 @@ namespace if (sep) os << "opt && "; - os << "std::strncmp (s, \"" << opt_prefix << "\", " << - n << ") == 0 && s[" << n << "] != '\\0')" + os << "std::strncmp (o, \"" << opt_prefix << "\", " << + n << ") == 0 && o[" << n << "] != '\\0')" << "{" << "switch (opt_mode)" << "{" << "case ::cli::unknown_mode::skip:" << endl << "{" - << "start++;" + << "s.skip ();" << "continue;" << "}" << "case ::cli::unknown_mode::stop:" << endl @@ -551,7 +555,7 @@ namespace << "}" << "case ::cli::unknown_mode::fail:" << endl << "{" - << "throw ::cli::unknown_option (s);" + << "throw ::cli::unknown_option (o);" << "}" << "}" // switch << "break;" // The stop case. @@ -566,7 +570,7 @@ namespace << "{" << "case ::cli::unknown_mode::skip:" << endl << "{" - << "start++;" + << "s.skip ();" << "continue;" << "}" << "case ::cli::unknown_mode::stop:" << endl @@ -575,14 +579,13 @@ namespace << "}" << "case ::cli::unknown_mode::fail:" << endl << "{" - << "throw ::cli::unknown_argument (s);" + << "throw ::cli::unknown_argument (o);" << "}" << "}" // switch << "break;" // The stop case. << "}" << "}" // for - << "return start;" << "}"; } -- cgit v1.1