// file : cult/cli/options-parser.hxx // author : Boris Kolpackov // copyright : Copyright (c) 2005-2010 Boris Kolpackov // license : GNU GPL v2 + exceptions; see accompanying LICENSE file #ifndef CULT_CLI_OPTIONS_PARSER_HXX #define CULT_CLI_OPTIONS_PARSER_HXX #include #include #include #include #include #include #include #include //@! which types should be NonCopyable namespace Cult { namespace CLI { struct UnknownMode { enum Value { skip, stop, fail }; }; template struct OptionParserBase { T parse (Char const* o, Scanner& s); }; template struct OptionParser: OptionParserBase { OptionParser (Spec const&) { } }; template class OptionParser > > { public: OptionParser (OptionSpec > const&) : impl_ (OptionSpec ()) { } T parse (Char const* k, Scanner& s) { return impl_.parse (k, s); } private: OptionParser > impl_; }; struct OptionParserBooleanBase { Boolean parse (Char const*, Scanner& s); }; template struct OptionParser >: OptionParserBooleanBase { OptionParser (OptionSpec const&) { } }; struct OptionParserStringBase { String parse (Char const*, Scanner& s); }; template struct OptionParser >: OptionParserStringBase { OptionParser (OptionSpec const&) { } }; // // // class OptionsParserBase { protected: struct GlueBase { // I am using Void* here to (significantly) reduce the length // on the object file symbols. // virtual Void operator() (Char const*, Scanner&, Void* options) = 0; }; static Trace::Stream tout; }; template class OptionsParser : OptionsParserBase { typedef Containers::Map > Map; template struct Glue; template struct Glue > : GlueBase { Glue (OptionSpec const& spec) : parser_ (spec) { } virtual Void operator() (Char const* o, Scanner& scan, Void* options) { typedef typename Spec::Options Options; Options& ops (*reinterpret_cast (options)); ops.template value () = parser_.parse (o, scan); } private: OptionParser > parser_; }; //@@ It's bad that I also have to specialize Glue. Need to redesign // this. // template struct Glue > > : GlueBase { Glue (OptionSpec > const& spec) : parser_ (spec) { } virtual Void operator() (Char const* o, Scanner& scan, Void* options) { typedef typename Spec::Options Options; Options& ops (*reinterpret_cast (options)); ops.template value ().push_back (parser_.parse (o, scan)); } private: OptionParser > > parser_; }; // Option-specific specializations of some functions. // template struct S_ { // This is a "specialization" for when type is Bits::Null. // static Void add_parser (Spec const&, Map&) { } static Void set_default (typename Spec::Options&, Spec const&) { } }; template struct S_ > { static Void add_parser (Spec const& spec, Map& map) { if (k[0] != '\0') { OptionSpec const& os (spec.template option ()); map[os.name ()] = new Glue > (os); } } static Void set_default (typename Spec::Options& o, Spec const& s) { o.template value () = s.template option ().default_value (); } }; public: OptionsParser (Spec const& spec) : spec_ (spec) { S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); S_::add_parser (spec_, map_); } typename Spec::Options parse (Scanner& scan, UnknownMode::Value option_mode = UnknownMode::fail, UnknownMode::Value argument_mode = UnknownMode::stop) { typename Spec::Options ops; S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); S_::set_default (ops, spec_); for (Char const* s (scan.peek ()); s != Scanner::eos; s = scan.peek ()) { tout << "looking at \"" << s << "\""; //@@ Std:: // if (strcmp (s, "--") == 0) { // We don't want to remove '--' if our option mode is skip. // if (option_mode == UnknownMode::skip) scan.skip (); else scan.next (); break; } typename Map::ConstIterator it (map_.find (s)); if (it != map_.end ()) { tout << "found parser for \"" << s << "\""; s = scan.next (); (*(it->second)) (s, scan, &ops); } else if (s[0] == '-') { // Unknown option. // switch (option_mode) { case UnknownMode::skip: { scan.skip (); continue; } case UnknownMode::stop: { break; } case UnknownMode::fail: { throw UnexpectedOption (s); } } break; // stop case } else { // Something else. // switch (argument_mode) { case UnknownMode::skip: { scan.skip (); continue; } case UnknownMode::stop: { break; } case UnknownMode::fail: { throw UnexpectedArgument (s); } } break; // stop case } } return ops; } private: Map map_; Spec spec_; }; // // // template typename Spec::Options parse (Spec const& s, Arguments& args, UnknownMode::Value option_mode = UnknownMode::fail, UnknownMode::Value argument_mode = UnknownMode::stop) { Scanner scan (args, Scanner::Action::erase); OptionsParser parser (s); return parser.parse (scan, option_mode, argument_mode); } template Options parse (Arguments& args, UnknownMode::Value option_mode = UnknownMode::fail, UnknownMode::Value argument_mode = UnknownMode::stop) { OptionsSpec spec; return parse (spec, args, option_mode, argument_mode); } template typename Spec::Options parse (Spec const& s, Int& argc, Char** argv, UnknownMode::Value option_mode = UnknownMode::fail, UnknownMode::Value argument_mode = UnknownMode::stop) { BasicArguments args (argc, argv); return parse (s, args, option_mode, argument_mode); } template Options parse (Int& argc, Char** argv, UnknownMode::Value option_mode = UnknownMode::fail, UnknownMode::Value argument_mode = UnknownMode::stop) { OptionsSpec spec; return parse (spec, argc, argv, option_mode, argument_mode); } } } #include #include #endif // CULT_CLI_OPTIONS_PARSER_HXX