#ifndef OPTIONS_HPP #define OPTIONS_HPP /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #include #include #include #include #include namespace example { /** bad_option is thrown for option parsing errors */ struct bad_option : public std::runtime_error { bad_option(const std::string& s) : std::runtime_error(s) {} }; /** Simple command-line option parser for example programs */ class options { public: options(int argc, char const * const * argv) : argc_(argc), argv_(argv), prog_(argv[0]), help_() { size_t slash = prog_.find_last_of("/\\"); if (slash != std::string::npos) prog_ = prog_.substr(slash+1); // Extract prog name from path add_flag(help_, 'h', "help", "Print the help message"); } ~options() { for (opts::iterator i = opts_.begin(); i != opts_.end(); ++i) delete *i; } /** Updates value when parse() is called if option is present with a value. */ template void add_value(T& value, char short_name, const std::string& long_name, const std::string& description, const std::string var) { opts_.push_back(new option_value(value, short_name, long_name, description, var)); } /** Sets flag when parse() is called if option is present. */ void add_flag(bool& flag, char short_name, const std::string& long_name, const std::string& description) { opts_.push_back(new option_flag(flag, short_name, long_name, description)); } /** Parse the command line, return the index of the first non-option argument. *@throws bad_option if there is a parsing error or unknown option. */ int parse() { int arg = 1; for (; arg < argc_ && argv_[arg][0] == '-'; ++arg) { opts::iterator i = opts_.begin(); while (i != opts_.end() && !(*i)->parse(argc_, argv_, arg)) ++i; if (i == opts_.end()) throw bad_option(std::string("unknown option ") + argv_[arg]); } if (help_) throw bad_option(""); return arg; } /** Print a usage message */ friend std::ostream& operator<<(std::ostream& os, const options& op) { os << std::endl << "usage: " << op.prog_ << " [options]" << std::endl; os << std::endl << "options:" << std::endl; for (opts::const_iterator i = op.opts_.begin(); i < op.opts_.end(); ++i) os << **i << std::endl; return os; } private: class option { public: option(char s, const std::string& l, const std::string& d, const std::string v) : short_(std::string("-") + s), long_("--" + l), desc_(d), var_(v) {} virtual ~option() {} virtual bool parse(int argc, char const * const * argv, int &i) = 0; virtual void print_default(std::ostream&) const {} friend std::ostream& operator<<(std::ostream& os, const option& op) { os << " " << op.short_; if (!op.var_.empty()) os << " " << op.var_; os << ", " << op.long_; if (!op.var_.empty()) os << "=" << op.var_; os << std::endl << " " << op.desc_; op.print_default(os); return os; } protected: std::string short_, long_, desc_, var_; }; template class option_value : public option { public: option_value(T& value, char s, const std::string& l, const std::string& d, const std::string& v) : option(s, l, d, v), value_(value) {} bool parse(int argc, char const * const * argv, int &i) { std::string arg(argv[i]); if (arg == short_ || arg == long_) { if (i < argc-1) { set_value(arg, argv[++i]); return true; } else { throw bad_option("missing value for " + arg); } } if (arg.compare(0, long_.size(), long_) == 0 && arg[long_.size()] == '=' ) { set_value(long_, arg.substr(long_.size()+1)); return true; } return false; } virtual void print_default(std::ostream& os) const { os << " (default " << value_ << ")"; } void set_value(const std::string& opt, const std::string& s) { std::istringstream is(s); is >> value_; if (is.fail() || is.bad()) throw bad_option("bad value for " + opt + ": " + s); } private: T& value_; }; class option_flag: public option { public: option_flag(bool& flag, const char s, const std::string& l, const std::string& d) : option(s, l, d, ""), flag_(flag) { flag_ = false; } bool parse(int /*argc*/, char const * const * argv, int &i) { if (argv[i] == short_ || argv[i] == long_) { flag_ = true; return true; } else { return false; } } private: bool &flag_; }; typedef std::vector opts; int argc_; char const * const * argv_; std::string prog_; opts opts_; bool help_; }; } #endif // OPTIONS_HPP