/** * Author: Dmitriy Morozov * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). */ #ifndef OPTS_OPTS_H #define OPTS_OPTS_H #include #include #include #include #include namespace opts { // Converters template struct Converter { Converter() {} static T convert(const std::string& val) { std::istringstream iss(val); T res; iss >> res; return res; } }; // Type template struct Traits { static std::string type_string() { return "UNKNOWN TYPE"; } }; template<> struct Traits { static std::string type_string() { return "INT"; } }; template<> struct Traits { static std::string type_string() { return "SHORT INT"; } }; template<> struct Traits { static std::string type_string() { return "UNSIGNED INT"; } }; template<> struct Traits { static std::string type_string() { return "SHORT UNSIGNED INT"; } }; template<> struct Traits { static std::string type_string() { return "FLOAT"; } }; template<> struct Traits { static std::string type_string() { return "DOUBLE"; } }; template<> struct Traits { static std::string type_string() { return "STRING"; } }; struct BasicOption { BasicOption(char s_, std::string l_, std::string default_, std::string type_, std::string help_): s(s_), l(l_), d(default_), t(type_), help(help_) {} int long_size() const { return l.size() + 1 + t.size(); } void output(std::ostream& out, int max_long) const { out << " "; if (s) out << '-' << s << ", "; else out << " "; out << "--" << l << ' '; if (!t.empty()) out << t; for (int i = long_size(); i < max_long; ++i) out << ' '; out << " " << help; if (!d.empty()) { out << " [default: " << d << "]"; } out << '\n'; } char s; std::string l; std::string d; std::string t; std::string help; }; // Option template struct OptionContainer: public BasicOption { OptionContainer(char s_, const std::string& l_, T& var_, const std::string& help_, const std::string& type_ = Traits::type_string()): BasicOption(s_, l_, default_value(var_), type_, help_), var(&var_) {} static std::string default_value(const T& def) { std::ostringstream oss; oss << def; return oss.str(); } void parse(std::list& args) const { std::string short_opt = "-"; short_opt += s; std::string long_opt = "--" + l; for (std::list::iterator cur = args.begin(); cur != args.end(); ++cur) { if (*cur == short_opt || *cur == long_opt) { cur = args.erase(cur); if (cur != args.end()) { *var = Converter::convert(*cur); cur = args.erase(cur); break; // finds first occurrence } else break; // if the last option's value is missing, it remains default } } } T* var; }; template struct OptionContainer< std::vector >: public BasicOption { OptionContainer(char s_, const std::string& l_, std::vector& var_, const std::string& help_, const std::string& type_ = "SEQUENCE"): BasicOption(s_, l_, default_value(var_), type_, help_), var(&var_) { } static std::string default_value(const std::vector& def) { std::ostringstream oss; oss << "("; if (def.size()) oss << def[0]; for (int i = 1; i < def.size(); ++i) oss << ", " << def[i]; oss << ")"; return oss.str(); } void parse(std::list& args) const { std::string short_opt = "-"; short_opt += s; std::string long_opt = "--" + l; for (std::list::iterator cur = args.begin(); cur != args.end(); ++cur) { if (*cur == short_opt || *cur == long_opt) { cur = args.erase(cur); if (cur != args.end()) { var->push_back(Converter::convert(*cur)); cur = args.erase(cur); } --cur; } } } std::vector* var; }; template OptionContainer Option(char s, const std::string& l, T& var, const std::string& help) { return OptionContainer(s, l, var, help); } template OptionContainer Option(char s, const std::string& l, T& var, const std::string& type, const std::string& help) { return OptionContainer(s, l, var, help, type); } template OptionContainer Option(const std::string& l, T& var, const std::string& help) { return OptionContainer(0, l, var, help); } template OptionContainer Option(const std::string& l, T& var, const std::string& type, const std::string& help) { return OptionContainer(0, l, var, help, type); } // Present struct PresentContainer: public BasicOption { PresentContainer(char s, const std::string& l, const std::string& help): BasicOption(s,l,"","",help) {} }; inline PresentContainer Present(char s, const std::string& l, const std::string& help) { return PresentContainer(s, l, help); } inline PresentContainer Present(const std::string& l, const std::string& help) { return PresentContainer(0, l, help); } // PosOption template struct PosOptionContainer { PosOptionContainer(T& var_): var(&var_) {} bool parse(std::list& args) const { if (args.empty()) return false; *var = Converter::convert(args.front()); args.pop_front(); return true; } T* var; }; template PosOptionContainer PosOption(T& var) { return PosOptionContainer(var); } // Options struct Options { Options(int argc_, char** argv_): args(argv_ + 1, argv_ + argc_), failed(false) {} template Options& operator>>(const OptionContainer& oc); bool operator>>(const PresentContainer& pc); template Options& operator>>(const PosOptionContainer& poc); operator bool() { return !failed; } friend std::ostream& operator<<(std::ostream& out, const Options& ops) { int max_long = 0; for (std::list::const_iterator cur = ops.options.begin(); cur != ops.options.end(); ++cur) { int cur_long = cur->long_size(); if (cur_long > max_long) max_long = cur_long; } out << "Options:\n"; for (std::list::const_iterator cur = ops.options.begin(); cur != ops.options.end(); ++cur) cur->output(out, max_long); return out; } private: std::list args; std::list options; bool failed; }; template Options& Options::operator>>(const OptionContainer& oc) { options.push_back(oc); oc.parse(args); return *this; } inline bool Options::operator>>(const PresentContainer& pc) { options.push_back(pc); for(std::list::iterator cur = args.begin(); cur != args.end(); ++cur) { std::string short_opt = "-"; short_opt += pc.s; std::string long_opt = "--" + pc.l; if (*cur == short_opt || *cur == long_opt) { args.erase(cur); return true; } } return false; } template Options& Options::operator>>(const PosOptionContainer& poc) { failed = !poc.parse(args); return *this; } } #endif