mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-30 12:43:52 +00:00
Adding better help print, with default and value info, better spacing
This commit is contained in:
parent
ca16a762db
commit
5af4d9c36d
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
132
include/CLI.hpp
132
include/CLI.hpp
@ -111,15 +111,56 @@ namespace detail {
|
||||
template <typename T>
|
||||
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<typename T,
|
||||
enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
|
||||
constexpr const char* type_name() {
|
||||
return "INT";
|
||||
}
|
||||
|
||||
template<typename T,
|
||||
enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
|
||||
constexpr const char* type_name() {
|
||||
return "UINT";
|
||||
}
|
||||
|
||||
|
||||
template<typename T,
|
||||
enable_if_t<std::is_floating_point<T>::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<typename T,
|
||||
enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
|
||||
constexpr const char* type_name() {
|
||||
return "VECTOR";
|
||||
}
|
||||
|
||||
|
||||
template<typename T,
|
||||
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::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<std::string>,std::vector<std::string>>(short_names, long_names);
|
||||
}
|
||||
|
||||
|
||||
// Integers
|
||||
template<typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = detail::dummy>
|
||||
bool lexical_cast(std::string input, T& output) {
|
||||
try{
|
||||
@ -282,6 +323,7 @@ namespace detail {
|
||||
}
|
||||
}
|
||||
|
||||
// Floats
|
||||
template<typename T, enable_if_t<std::is_floating_point<T>::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<typename T,
|
||||
enable_if_t<is_vector<T>::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<std::vector<std::string>> results_t;
|
||||
typedef std::function<bool(results_t)> callback_t;
|
||||
|
||||
|
||||
class App;
|
||||
|
||||
class Option {
|
||||
public:
|
||||
friend App;
|
||||
protected:
|
||||
// Config
|
||||
std::vector<std::string> 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 <typename T>
|
||||
@ -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<T>();
|
||||
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<T>();
|
||||
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<T>();
|
||||
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<T>();
|
||||
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<T>();
|
||||
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<T>();
|
||||
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<T>();
|
||||
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<App> &j){return std::max(i, (int) j->get_name().length()+1);});
|
||||
[](int i, const std::unique_ptr<App> &j){return std::max(i, (int) j->get_name().length()+3);});
|
||||
for(const std::unique_ptr<App> &com : subcommands) {
|
||||
out << std::setw(max) << std::left << com->get_name() << " " << com->prog_discription << std::endl;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user