| #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 <string> |
| #include <sstream> |
| #include <ostream> |
| #include <vector> |
| #include <stdexcept> |
| |
| /** 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<class T> |
| 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<T>(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 T> |
| 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<option*> opts; |
| |
| int argc_; |
| char const * const * argv_; |
| std::string prog_; |
| opts opts_; |
| bool help_; |
| }; |
| |
| #endif // OPTIONS_HPP |