diff --git a/examples/try.cpp b/examples/try.cpp index 1a1944cb..38a40de4 100644 --- a/examples/try.cpp +++ b/examples/try.cpp @@ -9,7 +9,10 @@ int main (int argc, char** argv) { app.add_option("f,file", file, "File name"); int count; - app.add_flag("c,count", count, "File name"); + app.add_flag("c,count", count, "Counter"); + + double value = 3.14; + app.add_option("-d,--double", value, "Some Value", CLI::DEFAULT); try { app.run(argc, argv); @@ -19,6 +22,7 @@ int main (int argc, char** argv) { std::cout << "Working on file: " << file << ", direct count: " << app.count("file") << std::endl; std::cout << "Working on count: " << count << ", direct count: " << app.count("count") << std::endl; + std::cout << "Some value: " << value << std::endl; return 0; } diff --git a/examples/try1.cpp b/examples/try1.cpp index 50b186be..76804445 100644 --- a/examples/try1.cpp +++ b/examples/try1.cpp @@ -12,7 +12,7 @@ int main (int argc, char** argv) { start->add_option("f,file", file, "File name"); int count; - stop->add_flag("c,count", count, "File name"); + stop->add_flag("c,count", count, "Counter"); try { app.run(argc, argv); diff --git a/include/CLI.hpp b/include/CLI.hpp index 4ad737cd..c876d712 100644 --- a/include/CLI.hpp +++ b/include/CLI.hpp @@ -111,15 +111,56 @@ namespace detail { template std::string join(const T& v, std::string delim = ",") { std::ostringstream s; + size_t start = 0; for (const auto& i : v) { - if (&i != &v[0]) { + if(start++ > 0) s << delim; - } s << i; } return s.str(); } + /// Was going to be based on + /// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template + /// But this is cleaner and works better in this case + + template::value && std::is_signed::value, detail::enabler> = detail::dummy> + constexpr const char* type_name() { + return "INT"; + } + + template::value && std::is_unsigned::value, detail::enabler> = detail::dummy> + constexpr const char* type_name() { + return "UINT"; + } + + + template::value, detail::enabler> = detail::dummy> + constexpr const char* type_name() { + return "FLOAT"; + } + + + /// This one should not be used, since vector types print the internal type + template::value, detail::enabler> = detail::dummy> + constexpr const char* type_name() { + return "VECTOR"; + } + + + template::value && !std::is_integral::value && !is_vector::value + , detail::enabler> = detail::dummy> + constexpr const char* type_name() { + return "STRING"; + } + + + struct Combiner { int num; bool positional; @@ -269,7 +310,7 @@ namespace detail { return std::tuple,std::vector>(short_names, long_names); } - + // Integers template::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T& output) { try{ @@ -282,6 +323,7 @@ namespace detail { } } + // Floats template::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T& output) { try{ @@ -294,7 +336,7 @@ namespace detail { } } - // String and similar + // Vector template::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T& output) { @@ -319,7 +361,6 @@ namespace detail { // Defines for common Combiners (don't use combiners directly) - const detail::Combiner NOTHING {0, false,false,false, {}}; const detail::Combiner REQUIRED {1, false,true, false, {}}; const detail::Combiner DEFAULT {1, false,false,true, {}}; @@ -338,9 +379,10 @@ typedef std::vector> results_t; typedef std::function callback_t; +class App; class Option { -public: + friend App; protected: // Config std::vector snames; @@ -349,6 +391,10 @@ protected: std::string discription; callback_t callback; + // These are for help strings + std::string defaultval; + std::string typeval; + // Results results_t results {}; @@ -456,13 +502,36 @@ public: return val; } + std::string help_name() const { + std::stringstream out; + out << " " << get_name(); + if(expected() != 0) { + if(typeval != "") + out << " " << typeval; + if(defaultval != "") + out << "=" << defaultval; + if(expected() > 1) + out << " x " << expected(); + if(expected() == -1) + out << " ..."; + } + return out.str(); + } + int help_len() const { - return std::min((int) get_name().length(), 40); + return help_name().length(); } std::string help(int len = 0) const { std::stringstream out; - out << std::setw(len) << std::left << get_name() << discription; + if(help_len() > len) { + out << help_name() << "\n"; + out << std::setw(len) << " "; + + } else { + out << std::setw(len) << std::left << help_name(); + } + out << discription; return out.str(); } @@ -479,7 +548,6 @@ public: enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND}; -class App; // Prototype return value test template @@ -604,7 +672,14 @@ public: return detail::lexical_cast(res[0][0], variable); }; - return add_option(name, fun, discription, opts); + Option* retval = add_option(name, fun, discription, opts); + retval->typeval = detail::type_name(); + if(opts.defaulted) { + std::stringstream out; + out << variable; + retval->defaultval = out.str(); + } + return retval; } /// Add option for vector of results @@ -629,7 +704,12 @@ public: return variable.size() > 0 && retval; }; - return add_option(name, fun, discription, opts); + Option* retval = add_option(name, fun, discription, opts); + retval->typeval = detail::type_name(); + if(opts.defaulted) { + retval->defaultval = "[" + detail::join(variable) + "]"; + } + return retval; } @@ -688,7 +768,15 @@ public: return std::find(std::begin(options), std::end(options), member) != std::end(options); }; - return add_option(name, fun, discription, opts); + Option* retval = add_option(name, fun, discription, opts); + retval->typeval = detail::type_name(); + retval->typeval += " in {" + detail::join(options) + "}"; + if(opts.defaulted) { + std::stringstream out; + out << member; + retval->defaultval = out.str(); + } + return retval; } @@ -721,7 +809,8 @@ public: ptr->reset(new T()); // resets the internal ptr return detail::lexical_cast(res[0][0], **ptr); }; - add_option(name, fun, discription, opts); + Option* retval = add_option(name, fun, discription, opts); + retval->typeval = detail::type_name(); return out; } @@ -752,7 +841,11 @@ public: ptr->reset(new T()); // resets the internal ptr return detail::lexical_cast(res[0][0], **ptr); }; - add_option(name, fun, discription, opts); + Option* retval = add_option(name, fun, discription, opts); + retval->typeval = detail::type_name(); + std::stringstream ot; + ot << default_value; + retval->defaultval = ot.str(); return out; } @@ -781,7 +874,8 @@ public: } return (*ptr)->size() > 0 && retval; }; - add_option(name, fun, discription, opts); + Option* retval = add_option(name, fun, discription, opts); + retval->typeval = detail::type_name(); return out; } @@ -834,7 +928,9 @@ public: return std::find(std::begin(options), std::end(options), **ptr) != std::end(options); }; - add_option(name, fun, discription, opts); + Option* retval = add_option(name, fun, discription, opts); + retval->typeval = detail::type_name(); + retval->typeval += " in {" + detail::join(options) + "}"; return out; } @@ -1052,14 +1148,14 @@ public: out << prog_discription << std::endl; int len = std::accumulate(std::begin(options), std::end(options), 0, [](int val, const Option &opt){ - return std::max(opt.help_len()+1, val);}); + return std::max(opt.help_len()+3, val);}); for(const Option &opt : options) { out << opt.help(len) << std::endl; } if(subcommands.size()> 0) { out << "Subcommands:" << std::endl; int max = std::accumulate(std::begin(subcommands), std::end(subcommands), 0, - [](int i, const std::unique_ptr &j){return std::max(i, (int) j->get_name().length()+1);}); + [](int i, const std::unique_ptr &j){return std::max(i, (int) j->get_name().length()+3);}); for(const std::unique_ptr &com : subcommands) { out << std::setw(max) << std::left << com->get_name() << " " << com->prog_discription << std::endl; }