| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #pragma once |
| |
|
| | #include <cxxabi.h> |
| |
|
| | #include <algorithm> |
| | #include <cstdlib> |
| | #include <cstring> |
| | #include <iostream> |
| | #include <map> |
| | #include <sstream> |
| | #include <stdexcept> |
| | #include <string> |
| | #include <typeinfo> |
| | #include <vector> |
| |
|
| | namespace cmdline { |
| |
|
| | namespace detail { |
| |
|
| | template <typename Target, typename Source, bool Same> |
| | class lexical_cast_t { |
| | public: |
| | static Target cast(const Source &arg) { |
| | Target ret; |
| | std::stringstream ss; |
| | if (!(ss << arg && ss >> ret && ss.eof())) throw std::bad_cast(); |
| |
|
| | return ret; |
| | } |
| | }; |
| |
|
| | template <typename Target, typename Source> |
| | class lexical_cast_t<Target, Source, true> { |
| | public: |
| | static Target cast(const Source &arg) { return arg; } |
| | }; |
| |
|
| | template <typename Source> |
| | class lexical_cast_t<std::string, Source, false> { |
| | public: |
| | static std::string cast(const Source &arg) { |
| | std::ostringstream ss; |
| | ss << arg; |
| | return ss.str(); |
| | } |
| | }; |
| |
|
| | template <typename Target> |
| | class lexical_cast_t<Target, std::string, false> { |
| | public: |
| | static Target cast(const std::string &arg) { |
| | Target ret; |
| | std::istringstream ss(arg); |
| | if (!(ss >> ret && ss.eof())) throw std::bad_cast(); |
| | return ret; |
| | } |
| | }; |
| |
|
| | template <typename T1, typename T2> |
| | struct is_same { |
| | static const bool value = false; |
| | }; |
| |
|
| | template <typename T> |
| | struct is_same<T, T> { |
| | static const bool value = true; |
| | }; |
| |
|
| | template <typename Target, typename Source> |
| | Target lexical_cast(const Source &arg) { |
| | return lexical_cast_t<Target, Source, |
| | detail::is_same<Target, Source>::value>::cast(arg); |
| | } |
| |
|
| | static inline std::string demangle(const std::string &name) { |
| | int status = 0; |
| | char *p = abi::__cxa_demangle(name.c_str(), 0, 0, &status); |
| | std::string ret(p); |
| | free(p); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | std::string readable_typename() { |
| | return demangle(typeid(T).name()); |
| | } |
| |
|
| | template <class T> |
| | std::string default_value(T def) { |
| | return detail::lexical_cast<std::string>(def); |
| | } |
| |
|
| | template <> |
| | inline std::string readable_typename<std::string>() { |
| | return "string"; |
| | } |
| |
|
| | } |
| |
|
| | |
| |
|
| | class cmdline_error : public std::exception { |
| | public: |
| | cmdline_error(const std::string &msg) : msg(msg) {} |
| | ~cmdline_error() throw() {} |
| | const char *what() const throw() { return msg.c_str(); } |
| |
|
| | private: |
| | std::string msg; |
| | }; |
| |
|
| | template <class T> |
| | struct default_reader { |
| | T operator()(const std::string &str) { return detail::lexical_cast<T>(str); } |
| | }; |
| |
|
| | template <class T> |
| | struct range_reader { |
| | range_reader(const T &low, const T &high) : low(low), high(high) {} |
| | T operator()(const std::string &s) const { |
| | T ret = default_reader<T>()(s); |
| | if (!(ret >= low && ret <= high)) |
| | throw cmdline::cmdline_error("range_error"); |
| | return ret; |
| | } |
| |
|
| | private: |
| | T low, high; |
| | }; |
| |
|
| | template <class T> |
| | range_reader<T> range(const T &low, const T &high) { |
| | return range_reader<T>(low, high); |
| | } |
| |
|
| | template <class T> |
| | struct oneof_reader { |
| | T operator()(const std::string &s) { |
| | T ret = default_reader<T>()(s); |
| | if (std::find(alt.begin(), alt.end(), ret) == alt.end()) |
| | throw cmdline_error(""); |
| | return ret; |
| | } |
| | void add(const T &v) { alt.push_back(v); } |
| |
|
| | private: |
| | std::vector<T> alt; |
| | }; |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3, T a4) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | ret.add(a4); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | ret.add(a4); |
| | ret.add(a5); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | ret.add(a4); |
| | ret.add(a5); |
| | ret.add(a6); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | ret.add(a4); |
| | ret.add(a5); |
| | ret.add(a6); |
| | ret.add(a7); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | ret.add(a4); |
| | ret.add(a5); |
| | ret.add(a6); |
| | ret.add(a7); |
| | ret.add(a8); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | ret.add(a4); |
| | ret.add(a5); |
| | ret.add(a6); |
| | ret.add(a7); |
| | ret.add(a8); |
| | ret.add(a9); |
| | return ret; |
| | } |
| |
|
| | template <class T> |
| | oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, |
| | T a10) { |
| | oneof_reader<T> ret; |
| | ret.add(a1); |
| | ret.add(a2); |
| | ret.add(a3); |
| | ret.add(a4); |
| | ret.add(a5); |
| | ret.add(a6); |
| | ret.add(a7); |
| | ret.add(a8); |
| | ret.add(a9); |
| | ret.add(a10); |
| | return ret; |
| | } |
| |
|
| | |
| |
|
| | class parser { |
| | public: |
| | parser() {} |
| | ~parser() { |
| | for (std::map<std::string, option_base *>::iterator p = options.begin(); |
| | p != options.end(); p++) |
| | delete p->second; |
| | } |
| |
|
| | void add(const std::string &name, char short_name = 0, |
| | const std::string &desc = "") { |
| | if (options.count(name)) |
| | throw cmdline_error("multiple definition: " + name); |
| | options[name] = new option_without_value(name, short_name, desc); |
| | ordered.push_back(options[name]); |
| | } |
| |
|
| | template <class T> |
| | void add(const std::string &name, char short_name = 0, |
| | const std::string &desc = "", bool need = true, const T def = T()) { |
| | add(name, short_name, desc, need, def, default_reader<T>()); |
| | } |
| |
|
| | template <class T, class F> |
| | void add(const std::string &name, char short_name = 0, |
| | const std::string &desc = "", bool need = true, const T def = T(), |
| | F reader = F()) { |
| | if (options.count(name)) |
| | throw cmdline_error("multiple definition: " + name); |
| | options[name] = new option_with_value_with_reader<T, F>( |
| | name, short_name, need, def, desc, reader); |
| | ordered.push_back(options[name]); |
| | } |
| |
|
| | void footer(const std::string &f) { ftr = f; } |
| |
|
| | void set_program_name(const std::string &name) { prog_name = name; } |
| |
|
| | bool exist(const std::string &name) const { |
| | if (options.count(name) == 0) |
| | throw cmdline_error("there is no flag: --" + name); |
| | return options.find(name)->second->has_set(); |
| | } |
| |
|
| | template <class T> |
| | const T &get(const std::string &name) const { |
| | if (options.count(name) == 0) |
| | throw cmdline_error("there is no flag: --" + name); |
| | const option_with_value<T> *p = |
| | dynamic_cast<const option_with_value<T> *>(options.find(name)->second); |
| | if (p == NULL) throw cmdline_error("type mismatch flag '" + name + "'"); |
| | return p->get(); |
| | } |
| |
|
| | const std::vector<std::string> &rest() const { return others; } |
| |
|
| | bool parse(const std::string &arg) { |
| | std::vector<std::string> args; |
| |
|
| | std::string buf; |
| | bool in_quote = false; |
| | for (std::string::size_type i = 0; i < arg.length(); i++) { |
| | if (arg[i] == '\"') { |
| | in_quote = !in_quote; |
| | continue; |
| | } |
| |
|
| | if (arg[i] == ' ' && !in_quote) { |
| | args.push_back(buf); |
| | buf = ""; |
| | continue; |
| | } |
| |
|
| | if (arg[i] == '\\') { |
| | i++; |
| | if (i >= arg.length()) { |
| | errors.push_back("unexpected occurrence of '\\' at end of string"); |
| | return false; |
| | } |
| | } |
| |
|
| | buf += arg[i]; |
| | } |
| |
|
| | if (in_quote) { |
| | errors.push_back("quote is not closed"); |
| | return false; |
| | } |
| |
|
| | if (buf.length() > 0) args.push_back(buf); |
| |
|
| | for (size_t i = 0; i < args.size(); i++) |
| | std::cout << "\"" << args[i] << "\"" << std::endl; |
| |
|
| | return parse(args); |
| | } |
| |
|
| | bool parse(const std::vector<std::string> &args) { |
| | int argc = static_cast<int>(args.size()); |
| | std::vector<const char *> argv(argc); |
| |
|
| | for (int i = 0; i < argc; i++) argv[i] = args[i].c_str(); |
| |
|
| | return parse(argc, &argv[0]); |
| | } |
| |
|
| | bool parse(int argc, const char *const argv[]) { |
| | errors.clear(); |
| | others.clear(); |
| |
|
| | if (argc < 1) { |
| | errors.push_back("argument number must be longer than 0"); |
| | return false; |
| | } |
| | if (prog_name == "") prog_name = argv[0]; |
| |
|
| | std::map<char, std::string> lookup; |
| | for (std::map<std::string, option_base *>::iterator p = options.begin(); |
| | p != options.end(); p++) { |
| | if (p->first.length() == 0) continue; |
| | char initial = p->second->short_name(); |
| | if (initial) { |
| | if (lookup.count(initial) > 0) { |
| | lookup[initial] = ""; |
| | errors.push_back(std::string("short option '") + initial + |
| | "' is ambiguous"); |
| | return false; |
| | } else |
| | lookup[initial] = p->first; |
| | } |
| | } |
| |
|
| | for (int i = 1; i < argc; i++) { |
| | if (strncmp(argv[i], "--", 2) == 0) { |
| | const char *p = strchr(argv[i] + 2, '='); |
| | if (p) { |
| | std::string name(argv[i] + 2, p); |
| | std::string val(p + 1); |
| | set_option(name, val); |
| | } else { |
| | std::string name(argv[i] + 2); |
| | if (options.count(name) == 0) { |
| | errors.push_back("undefined option: --" + name); |
| | continue; |
| | } |
| | if (options[name]->has_value()) { |
| | if (i + 1 >= argc) { |
| | errors.push_back("option needs value: --" + name); |
| | continue; |
| | } else { |
| | i++; |
| | set_option(name, argv[i]); |
| | } |
| | } else { |
| | set_option(name); |
| | } |
| | } |
| | } else if (strncmp(argv[i], "-", 1) == 0) { |
| | if (!argv[i][1]) continue; |
| | char last = argv[i][1]; |
| | for (int j = 2; argv[i][j]; j++) { |
| | last = argv[i][j]; |
| | if (lookup.count(argv[i][j - 1]) == 0) { |
| | errors.push_back(std::string("undefined short option: -") + |
| | argv[i][j - 1]); |
| | continue; |
| | } |
| | if (lookup[argv[i][j - 1]] == "") { |
| | errors.push_back(std::string("ambiguous short option: -") + |
| | argv[i][j - 1]); |
| | continue; |
| | } |
| | set_option(lookup[argv[i][j - 1]]); |
| | } |
| |
|
| | if (lookup.count(last) == 0) { |
| | errors.push_back(std::string("undefined short option: -") + last); |
| | continue; |
| | } |
| | if (lookup[last] == "") { |
| | errors.push_back(std::string("ambiguous short option: -") + last); |
| | continue; |
| | } |
| |
|
| | if (i + 1 < argc && options[lookup[last]]->has_value()) { |
| | set_option(lookup[last], argv[i + 1]); |
| | i++; |
| | } else { |
| | set_option(lookup[last]); |
| | } |
| | } else { |
| | others.push_back(argv[i]); |
| | } |
| | } |
| |
|
| | for (std::map<std::string, option_base *>::iterator p = options.begin(); |
| | p != options.end(); p++) |
| | if (!p->second->valid()) |
| | errors.push_back("need option: --" + std::string(p->first)); |
| |
|
| | return errors.size() == 0; |
| | } |
| |
|
| | void parse_check(const std::string &arg) { |
| | if (!options.count("help")) add("help", '?', "print this message"); |
| | check(0, parse(arg)); |
| | } |
| |
|
| | void parse_check(const std::vector<std::string> &args) { |
| | if (!options.count("help")) add("help", '?', "print this message"); |
| | check(args.size(), parse(args)); |
| | } |
| |
|
| | void parse_check(int argc, char *argv[]) { |
| | if (!options.count("help")) add("help", '?', "print this message"); |
| | check(argc, parse(argc, argv)); |
| | } |
| |
|
| | std::string error() const { return errors.size() > 0 ? errors[0] : ""; } |
| |
|
| | std::string error_full() const { |
| | std::ostringstream oss; |
| | for (size_t i = 0; i < errors.size(); i++) oss << errors[i] << std::endl; |
| | return oss.str(); |
| | } |
| |
|
| | std::string usage() const { |
| | std::ostringstream oss; |
| | oss << "usage: " << prog_name << " "; |
| | for (size_t i = 0; i < ordered.size(); i++) { |
| | if (ordered[i]->must()) oss << ordered[i]->short_description() << " "; |
| | } |
| |
|
| | oss << "[options] ... " << ftr << std::endl; |
| | oss << "options:" << std::endl; |
| |
|
| | size_t max_width = 0; |
| | for (size_t i = 0; i < ordered.size(); i++) { |
| | max_width = std::max(max_width, ordered[i]->name().length()); |
| | } |
| | for (size_t i = 0; i < ordered.size(); i++) { |
| | if (ordered[i]->short_name()) { |
| | oss << " -" << ordered[i]->short_name() << ", "; |
| | } else { |
| | oss << " "; |
| | } |
| |
|
| | oss << "--" << ordered[i]->name(); |
| | for (size_t j = ordered[i]->name().length(); j < max_width + 4; j++) |
| | oss << ' '; |
| | oss << ordered[i]->description() << std::endl; |
| | } |
| | return oss.str(); |
| | } |
| |
|
| | private: |
| | void check(int argc, bool ok) { |
| | if ((argc == 1 && !ok) || exist("help")) { |
| | std::cerr << usage(); |
| | exit(0); |
| | } |
| |
|
| | if (!ok) { |
| | std::cerr << error() << std::endl << usage(); |
| | exit(1); |
| | } |
| | } |
| |
|
| | void set_option(const std::string &name) { |
| | if (options.count(name) == 0) { |
| | errors.push_back("undefined option: --" + name); |
| | return; |
| | } |
| | if (!options[name]->set()) { |
| | errors.push_back("option needs value: --" + name); |
| | return; |
| | } |
| | } |
| |
|
| | void set_option(const std::string &name, const std::string &value) { |
| | if (options.count(name) == 0) { |
| | errors.push_back("undefined option: --" + name); |
| | return; |
| | } |
| | if (!options[name]->set(value)) { |
| | errors.push_back("option value is invalid: --" + name + "=" + value); |
| | return; |
| | } |
| | } |
| |
|
| | class option_base { |
| | public: |
| | virtual ~option_base() {} |
| |
|
| | virtual bool has_value() const = 0; |
| | virtual bool set() = 0; |
| | virtual bool set(const std::string &value) = 0; |
| | virtual bool has_set() const = 0; |
| | virtual bool valid() const = 0; |
| | virtual bool must() const = 0; |
| |
|
| | virtual const std::string &name() const = 0; |
| | virtual char short_name() const = 0; |
| | virtual const std::string &description() const = 0; |
| | virtual std::string short_description() const = 0; |
| | }; |
| |
|
| | class option_without_value : public option_base { |
| | public: |
| | option_without_value(const std::string &name, char short_name, |
| | const std::string &desc) |
| | : nam(name), snam(short_name), desc(desc), has(false) {} |
| | ~option_without_value() {} |
| |
|
| | bool has_value() const { return false; } |
| |
|
| | bool set() { |
| | has = true; |
| | return true; |
| | } |
| |
|
| | bool set(const std::string &) { return false; } |
| |
|
| | bool has_set() const { return has; } |
| |
|
| | bool valid() const { return true; } |
| |
|
| | bool must() const { return false; } |
| |
|
| | const std::string &name() const { return nam; } |
| |
|
| | char short_name() const { return snam; } |
| |
|
| | const std::string &description() const { return desc; } |
| |
|
| | std::string short_description() const { return "--" + nam; } |
| |
|
| | private: |
| | std::string nam; |
| | char snam; |
| | std::string desc; |
| | bool has; |
| | }; |
| |
|
| | template <class T> |
| | class option_with_value : public option_base { |
| | public: |
| | option_with_value(const std::string &name, char short_name, bool need, |
| | const T &def, const std::string &desc) |
| | : nam(name), |
| | snam(short_name), |
| | need(need), |
| | has(false), |
| | def(def), |
| | actual(def) { |
| | this->desc = full_description(desc); |
| | } |
| | ~option_with_value() {} |
| |
|
| | const T &get() const { return actual; } |
| |
|
| | bool has_value() const { return true; } |
| |
|
| | bool set() { return false; } |
| |
|
| | bool set(const std::string &value) { |
| | try { |
| | actual = read(value); |
| | has = true; |
| | } catch (const std::exception &e) { |
| | return false; |
| | } |
| | return true; |
| | } |
| |
|
| | bool has_set() const { return has; } |
| |
|
| | bool valid() const { |
| | if (need && !has) return false; |
| | return true; |
| | } |
| |
|
| | bool must() const { return need; } |
| |
|
| | const std::string &name() const { return nam; } |
| |
|
| | char short_name() const { return snam; } |
| |
|
| | const std::string &description() const { return desc; } |
| |
|
| | std::string short_description() const { |
| | return "--" + nam + "=" + detail::readable_typename<T>(); |
| | } |
| |
|
| | protected: |
| | std::string full_description(const std::string &desc) { |
| | return desc + " (" + detail::readable_typename<T>() + |
| | (need ? "" : " [=" + detail::default_value<T>(def) + "]") + ")"; |
| | } |
| |
|
| | virtual T read(const std::string &s) = 0; |
| |
|
| | std::string nam; |
| | char snam; |
| | bool need; |
| | std::string desc; |
| |
|
| | bool has; |
| | T def; |
| | T actual; |
| | }; |
| |
|
| | template <class T, class F> |
| | class option_with_value_with_reader : public option_with_value<T> { |
| | public: |
| | option_with_value_with_reader(const std::string &name, char short_name, |
| | bool need, const T def, |
| | const std::string &desc, F reader) |
| | : option_with_value<T>(name, short_name, need, def, desc), |
| | reader(reader) {} |
| |
|
| | private: |
| | T read(const std::string &s) { return reader(s); } |
| |
|
| | F reader; |
| | }; |
| |
|
| | std::map<std::string, option_base *> options; |
| | std::vector<option_base *> ordered; |
| | std::string ftr; |
| |
|
| | std::string prog_name; |
| | std::vector<std::string> others; |
| |
|
| | std::vector<std::string> errors; |
| | }; |
| |
|
| | } |