1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-30 20:53:52 +00:00

Massive move to -a,--long,posit syntax

This commit is contained in:
Henry Fredrick Schreiner 2017-02-04 10:10:48 -05:00
parent 955dd950f0
commit 200d0f277f
5 changed files with 290 additions and 186 deletions

View File

@ -6,13 +6,13 @@ int main (int argc, char** argv) {
CLI::App app("K3Pi goofit fitter"); CLI::App app("K3Pi goofit fitter");
std::string file; std::string file;
app.add_option("f,file", file, "File name"); app.add_option("-f,--file", file, "File name");
int count; int count;
app.add_flag("c,count", count, "Counter"); app.add_flag("-c,--count", count, "Counter");
double value = 3.14; double value = 3.14;
app.add_option("-d,--double", value, "Some Value", CLI::DEFAULT); app.add_option("-d,--double", value, "Some Value", CLI::Default);
try { try {
app.run(argc, argv); app.run(argc, argv);
@ -20,8 +20,8 @@ int main (int argc, char** argv) {
return app.exit(e); return app.exit(e);
} }
std::cout << "Working on file: " << file << ", direct count: " << app.count("file") << std::endl; 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 << "Working on count: " << count << ", direct count: " << app.count("--count") << std::endl;
std::cout << "Some value: " << value << std::endl; std::cout << "Some value: " << value << std::endl;
return 0; return 0;

View File

@ -9,10 +9,10 @@ int main (int argc, char** argv) {
std::cout << app.help(); std::cout << app.help();
std::string file; std::string file;
start->add_option("f,file", file, "File name"); start->add_option("-f,--file", file, "File name");
int count; int count;
stop->add_flag("c,count", count, "Counter"); stop->add_flag("-c,--count", count, "Counter");
try { try {
app.run(argc, argv); app.run(argc, argv);
@ -20,8 +20,8 @@ int main (int argc, char** argv) {
return app.exit(e); return app.exit(e);
} }
std::cout << "Working on file: " << file << ", direct count: " << start->count("file") << std::endl; std::cout << "Working on file: " << file << ", direct count: " << start->count("--file") << std::endl;
std::cout << "Working on count: " << count << ", direct count: " << stop->count("count") << std::endl; std::cout << "Working on count: " << count << ", direct count: " << stop->count("--count") << std::endl;
if(app.get_subcommand() != nullptr) if(app.get_subcommand() != nullptr)
std::cout << "Subcommand:" << app.get_subcommand()->get_name() << std::endl; std::cout << "Subcommand:" << app.get_subcommand()->get_name() << std::endl;

View File

@ -178,7 +178,6 @@ namespace detail {
struct Combiner { struct Combiner {
int num; int num;
bool positional;
bool required; bool required;
bool defaulted; bool defaulted;
std::vector<std::function<bool(std::string)>> validators; std::vector<std::function<bool(std::string)>> validators;
@ -187,7 +186,6 @@ namespace detail {
Combiner operator | (Combiner b) const { Combiner operator | (Combiner b) const {
Combiner self; Combiner self;
self.num = std::min(num, b.num) == -1 ? -1 : std::max(num, b.num); self.num = std::min(num, b.num) == -1 ? -1 : std::max(num, b.num);
self.positional = positional || b.positional;
self.required = required || b.required; self.required = required || b.required;
self.defaulted = defaulted || b.defaulted; self.defaulted = defaulted || b.defaulted;
self.validators.reserve(validators.size() + b.validators.size()); self.validators.reserve(validators.size() + b.validators.size());
@ -290,36 +288,39 @@ namespace detail {
} }
inline std::tuple<std::vector<std::string>,std::vector<std::string>> get_names(const std::vector<std::string> &input) { inline std::tuple<std::vector<std::string>,std::vector<std::string>, std::string>
get_names(const std::vector<std::string> &input) {
std::vector<std::string> short_names; std::vector<std::string> short_names;
std::vector<std::string> long_names; std::vector<std::string> long_names;
std::string pos_name;
for(std::string name : input) { for(std::string name : input) {
if(name.length() == 0) if(name.length() == 0)
continue; continue;
else if(name.length() == 1) else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
if(valid_first_char(name[0])) if(name.length()==2 && valid_first_char(name[1]))
short_names.push_back(name);
else
throw BadNameString("Invalid one char name: "+name);
else if(name.length() == 2 && name[0] == '-' && name[1] != '-') {
if(valid_first_char(name[1]))
short_names.push_back(std::string(1,name[1])); short_names.push_back(std::string(1,name[1]));
else else
throw BadNameString("Invalid one char name: "+name); throw BadNameString("Invalid one char name: "+name);
} else { } else if(name.length() > 2 && name.substr(0,2) == "--") {
name = name.substr(2);
if(name.substr(0,2) == "--")
name = name.substr(2);
if(valid_name_string(name)) if(valid_name_string(name))
long_names.push_back(name); long_names.push_back(name);
else else
throw BadNameString("Bad long name"+name); throw BadNameString("Bad long name: "+name);
} else if(name == "-" || name == "--") {
throw BadNameString("Must have a name, not just dashes");
} else {
if(pos_name.length() > 0)
throw BadNameString("Only one positional name allowed, remove: "+name);
pos_name = name;
} }
} }
return std::tuple<std::vector<std::string>,std::vector<std::string>>(short_names, long_names); return std::tuple<std::vector<std::string>,std::vector<std::string>, std::string>
(short_names, long_names, pos_name);
} }
// Integers // Integers
@ -373,19 +374,18 @@ namespace detail {
// Defines for common Combiners (don't use combiners directly) // Defines for common Combiners (don't use combiners directly)
const detail::Combiner NOTHING {0, false,false,false, {}}; const detail::Combiner Nothing {0, false, false, {}};
const detail::Combiner REQUIRED {1, false,true, false, {}}; const detail::Combiner Required {1, true, false, {}};
const detail::Combiner DEFAULT {1, false,false,true, {}}; const detail::Combiner Default {1, false, true, {}};
const detail::Combiner POSITIONAL {1, true, false,false, {}}; const detail::Combiner Args {-1, false, false, {}};
const detail::Combiner ARGS {-1, false,false,false, {}}; const detail::Combiner Validators {1, false, false, {}};
const detail::Combiner VALIDATORS {1, false, false, false, {}};
// Warning about using these validators: // Warning about using these validators:
// The files could be added/deleted after the validation. This is not common, // The files could be added/deleted after the validation. This is not common,
// but if this is a possibility, check the file you open afterwards // but if this is a possibility, check the file you open afterwards
const detail::Combiner ExistingFile {1, false, false, false, {detail::_ExistingFile}}; const detail::Combiner ExistingFile {1, false, false, {detail::_ExistingFile}};
const detail::Combiner ExistingDirectory {1, false, false, false, {detail::_ExistingDirectory}}; const detail::Combiner ExistingDirectory {1, false, false, {detail::_ExistingDirectory}};
const detail::Combiner NonexistentPath {1, false, false, false, {detail::_NonexistentPath}}; const detail::Combiner NonexistentPath {1, false, false, {detail::_NonexistentPath}};
typedef std::vector<std::vector<std::string>> results_t; typedef std::vector<std::vector<std::string>> results_t;
typedef std::function<bool(results_t)> callback_t; typedef std::function<bool(results_t)> callback_t;
@ -399,6 +399,8 @@ protected:
// Config // Config
std::vector<std::string> snames; std::vector<std::string> snames;
std::vector<std::string> lnames; std::vector<std::string> lnames;
std::string pname;
detail::Combiner opts; detail::Combiner opts;
std::string discription; std::string discription;
callback_t callback; callback_t callback;
@ -412,31 +414,62 @@ protected:
public: public:
Option(std::string name, std::string discription = "", detail::Combiner opts=NOTHING, std::function<bool(results_t)> callback=[](results_t){return true;}) : Option(std::string name, std::string discription = "", detail::Combiner opts=Nothing, std::function<bool(results_t)> callback=[](results_t){return true;}) :
opts(opts), discription(discription), callback(callback){ opts(opts), discription(discription), callback(callback){
std::tie(snames, lnames) = detail::get_names(detail::split_names(name)); std::tie(snames, lnames, pname) = detail::get_names(detail::split_names(name));
} }
/// Clear the parsed results (mostly for testing)
void clear() { void clear() {
results.clear(); results.clear();
} }
/// True if option is required
bool required() const { bool required() const {
return opts.required; return opts.required;
} }
/// The number of arguments the option expects
int expected() const { int expected() const {
return opts.num; return opts.num;
} }
/// True if the argument can be given directly
bool positional() const { bool positional() const {
return opts.positional; return pname.length() > 0;
} }
/// True if option has at least one non-positional name
bool nonpositional() const {
return (snames.size() + lnames.size()) > 0;
}
/// True if this should print the default string
bool defaulted() const { bool defaulted() const {
return opts.defaulted; return opts.defaulted;
} }
/// True if option has discription
bool has_discription() const {
return discription.length() > 0;
}
/// Get the discription
const std::string& get_discription() const {
return discription;
}
/// The name and any extras needed for positionals
std::string help_positional() const {
std::string out = pname;
if(expected()<1)
out = out + "x" + std::to_string(expected());
else if(expected()==-1)
out = out + "...";
out = required() ? out : "["+out+"]";
return out;
}
/// Process the callback /// Process the callback
bool run_callback() const { bool run_callback() const {
if(opts.validators.size()>0) { if(opts.validators.size()>0) {
@ -448,7 +481,7 @@ public:
return callback(results); return callback(results);
} }
/// If options share any of the same names, they are equal /// If options share any of the same names, they are equal (not counting positional)
bool operator== (const Option& other) const { bool operator== (const Option& other) const {
for(const std::string &sname : snames) for(const std::string &sname : snames)
for(const std::string &othersname : other.snames) for(const std::string &othersname : other.snames)
@ -461,6 +494,7 @@ public:
return false; return false;
} }
/// Gets a , sep list of names. Does not include the positional name.
std::string get_name() const { std::string get_name() const {
std::vector<std::string> name_list; std::vector<std::string> name_list;
for(const std::string& sname : snames) for(const std::string& sname : snames)
@ -470,30 +504,40 @@ public:
return detail::join(name_list); return detail::join(name_list);
} }
/// Check a name. Requires "-" or "--" for short / long, supports positional name
bool check_name(std::string name) const { bool check_name(std::string name) const {
for(int i=0; i<2; i++)
if(name.length()>2 && name[0] == '-')
name = name.substr(1);
return check_sname(name) || check_lname(name); if(name.length()>2 && name.substr(0,2) == "--")
return check_lname(name.substr(2));
else if (name.length()>1 && name.substr(0,1) == "-")
return check_sname(name.substr(1));
else
return name == pname;
} }
/// Requires "-" to be removed from string
bool check_sname(const std::string& name) const { bool check_sname(const std::string& name) const {
return std::find(std::begin(snames), std::end(snames), name) != std::end(snames); return std::find(std::begin(snames), std::end(snames), name) != std::end(snames);
} }
/// Requires "--" to be removed from string
bool check_lname(const std::string& name) const { bool check_lname(const std::string& name) const {
return std::find(std::begin(lnames), std::end(lnames), name) != std::end(lnames); return std::find(std::begin(lnames), std::end(lnames), name) != std::end(lnames);
} }
/// Puts a result at position r
void add_result(int r, std::string s) { void add_result(int r, std::string s) {
results.at(r).push_back(s); results.at(r).push_back(s);
} }
/// Starts a new results vector (used for r in add_result)
int get_new() { int get_new() {
results.emplace_back(); results.emplace_back();
return results.size() - 1; return results.size() - 1;
} }
/// Count the total number of times an option was passed
int count() const { int count() const {
int out = 0; int out = 0;
for(const std::vector<std::string>& v : results) for(const std::vector<std::string>& v : results)
@ -501,6 +545,7 @@ public:
return out; return out;
} }
/// Diagnostic representation
std::string string() const { std::string string() const {
std::string val = "Option: " + get_name() + "\n" std::string val = "Option: " + get_name() + "\n"
+ " " + discription + "\n" + " " + discription + "\n"
@ -514,6 +559,7 @@ public:
return val; return val;
} }
/// The first half of the help print, name plus default, etc
std::string help_name() const { std::string help_name() const {
std::stringstream out; std::stringstream out;
out << " " << get_name(); out << " " << get_name();
@ -530,10 +576,12 @@ public:
return out.str(); return out.str();
} }
/// The length of the name part of the help, for formatting
int help_len() const { int help_len() const {
return help_name().length(); return help_name().length();
} }
/// Make a help string, adjustable len.
std::string help(int len = 0) const { std::string help(int len = 0) const {
std::stringstream out; std::stringstream out;
if(help_len() > len) { if(help_len() > len) {
@ -547,6 +595,7 @@ public:
return out.str(); return out.str();
} }
/// Produce a flattened vector of results, vs. a vector of vectors.
std::vector<std::string> flatten_results() const { std::vector<std::string> flatten_results() const {
std::vector<std::string> output; std::vector<std::string> output;
for(const std::vector<std::string> result : results) for(const std::vector<std::string> result : results)
@ -602,6 +651,7 @@ protected:
std::vector<std::unique_ptr<App>> subcommands; std::vector<std::unique_ptr<App>> subcommands;
bool parsed{false}; bool parsed{false};
App* subcommand = nullptr; App* subcommand = nullptr;
std::string progname = "program";
std::function<void()> app_callback; std::function<void()> app_callback;
@ -639,7 +689,7 @@ public:
App(std::string prog_discription="") App(std::string prog_discription="")
: prog_discription(prog_discription) { : prog_discription(prog_discription) {
add_flag("h,help", "Print this help message and exit"); add_flag("-h,--help", "Print this help message and exit");
} }
@ -657,7 +707,7 @@ public:
* After start is called, you can use count to see if the value was passed, and * After start is called, you can use count to see if the value was passed, and
* the value will be initialized properly. * the value will be initialized properly.
* *
* Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|` * Program::Required, Program::Default, and the validators are options, and can be `|`
* together. The positional options take an optional number of arguments. * together. The positional options take an optional number of arguments.
* *
* For example, * For example,
@ -666,10 +716,10 @@ public:
* program.add_option("filename", filename, "discription of filename"); * program.add_option("filename", filename, "discription of filename");
*/ */
Option* add_option( Option* add_option(
std::string name, ///< The name, long,short std::string name,
callback_t callback, ///< The callback callback_t callback,
std::string discription="", ///< Discription string std::string discription="",
detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=Validators
) { ) {
Option myopt{name, discription, opts, callback}; Option myopt{name, discription, opts, callback};
if(std::find(std::begin(options), std::end(options), myopt) == std::end(options)) if(std::find(std::begin(options), std::end(options), myopt) == std::end(options))
@ -683,15 +733,15 @@ public:
/// Add option for string /// Add option for string
template<typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy> template<typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Option* add_option( Option* add_option(
std::string name, ///< The name, long,short std::string name,
T &variable, ///< The variable to set T &variable, ///< The variable to set
std::string discription="", ///< Discription string std::string discription="",
detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=Validators
) { ) {
if(opts.num!=1) if(opts.num!=1)
throw IncorrectConstruction("Must have ARGS(1) or be a vector."); throw IncorrectConstruction("Must have Args(1) or be a vector.");
CLI::callback_t fun = [&variable](CLI::results_t res){ CLI::callback_t fun = [&variable](CLI::results_t res){
if(res.size()!=1) { if(res.size()!=1) {
return false; return false;
@ -715,14 +765,14 @@ public:
/// Add option for vector of results /// Add option for vector of results
template<typename T> template<typename T>
Option* add_option( Option* add_option(
std::string name, ///< The name, long,short std::string name,
std::vector<T> &variable, ///< The variable to set std::vector<T> &variable, ///< The variable vector to set
std::string discription="", ///< Discription string std::string discription="",
detail::Combiner opts=ARGS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=Args
) { ) {
if(opts.num==0) if(opts.num==0)
throw IncorrectConstruction("Must have ARGS or be a vector."); throw IncorrectConstruction("Must have Args or be a vector.");
CLI::callback_t fun = [&variable](CLI::results_t res){ CLI::callback_t fun = [&variable](CLI::results_t res){
bool retval = true; bool retval = true;
variable.clear(); variable.clear();
@ -746,10 +796,10 @@ public:
/// Multiple options are supported /// Multiple options are supported
template<typename T, typename... Args> template<typename T, typename... Args>
Option* add_option( Option* add_option(
std::string name, ///< The name, long,short std::string name,
T &variable, ///< The variable to set T &variable, ///< The variable to set
std::string discription, ///< Discription string std::string discription,
detail::Combiner opts, ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts,
detail::Combiner opts2, detail::Combiner opts2,
Args... args ///< More options Args... args ///< More options
) { ) {
@ -757,23 +807,26 @@ public:
} }
/// Add option for flag /// Add option for flag
Option* add_flag( Option* add_flag(
std::string name, ///< The name, short,long std::string name,
std::string discription="" ///< Discription string std::string discription=""
) { ) {
CLI::callback_t fun = [](CLI::results_t){ CLI::callback_t fun = [](CLI::results_t){
return true; return true;
}; };
return add_option(name, fun, discription, NOTHING); Option* opt = add_option(name, fun, discription, Nothing);
if(opt->positional())
throw IncorrectConstruction("Flags cannot be positional");
return opt;
} }
/// Add option for flag /// Add option for flag
template<typename T, template<typename T,
enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy> enable_if_t<std::is_integral<T>::value && !is_bool<T>::value, detail::enabler> = detail::dummy>
Option* add_flag( Option* add_flag(
std::string name, ///< The name, short,long std::string name,
T &count, ///< A varaible holding the count T &count, ///< A varaible holding the count
std::string discription="" ///< Discription string std::string discription=""
) { ) {
count = 0; count = 0;
@ -782,16 +835,19 @@ public:
return true; return true;
}; };
return add_option(name, fun, discription, NOTHING); Option* opt = add_option(name, fun, discription, Nothing);
if(opt->positional())
throw IncorrectConstruction("Flags cannot be positional");
return opt;
} }
/// Bool version only allows the flag once /// Bool version only allows the flag once
template<typename T, template<typename T,
enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy> enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy>
Option* add_flag( Option* add_flag(
std::string name, ///< The name, short,long std::string name,
T &count, ///< A varaible holding true if passed T &count, ///< A varaible holding true if passed
std::string discription="" ///< Discription string std::string discription=""
) { ) {
count = false; count = false;
@ -800,22 +856,25 @@ public:
return res.size() == 1; return res.size() == 1;
}; };
return add_option(name, fun, discription, NOTHING); Option* opt = add_option(name, fun, discription, Nothing);
if(opt->positional())
throw IncorrectConstruction("Flags cannot be positional");
return opt;
} }
/// Add set of options /// Add set of options
template<typename T> template<typename T>
Option* add_set( Option* add_set(
std::string name, ///< The name, short,long std::string name,
T &member, ///< The selected member of the set T &member, ///< The selected member of the set
std::set<T> options, ///< The set of posibilities std::set<T> options, ///< The set of posibilities
std::string discription="", ///< Discription string std::string discription="",
detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=Validators
) { ) {
if(opts.num!=1) if(opts.num!=1)
throw IncorrectConstruction("Must have ARGS(1)."); throw IncorrectConstruction("Must have Args(1).");
CLI::callback_t fun = [&member, options](CLI::results_t res){ CLI::callback_t fun = [&member, options](CLI::results_t res){
if(res.size()!=1) { if(res.size()!=1) {
@ -844,11 +903,11 @@ public:
template<typename T, typename... Args> template<typename T, typename... Args>
Option* add_set( Option* add_set(
std::string name, ///< The name, short,long std::string name,
T &member, ///< The selected member of the set T &member,
std::set<T> options, ///< The set of posibilities std::set<T> options, ///< The set of posibilities
std::string discription, ///< Discription string std::string discription,
detail::Combiner opts, ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts,
detail::Combiner opts2, detail::Combiner opts2,
Args... args Args... args
) { ) {
@ -862,13 +921,13 @@ public:
template<typename T = std::string, template<typename T = std::string,
enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy> enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name,
std::string discription="", std::string discription="",
detail::Combiner opts=VALIDATORS detail::Combiner opts=Validators
) { ) {
if(opts.num!=1) if(opts.num!=1)
throw IncorrectConstruction("Must have ARGS(1)."); throw IncorrectConstruction("Must have Args(1).");
Value<T> out(name); Value<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value; std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
@ -890,7 +949,7 @@ public:
template<typename T = std::string, typename... Args> template<typename T = std::string, typename... Args>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name,
std::string discription, std::string discription,
detail::Combiner opts, detail::Combiner opts,
detail::Combiner opts2, detail::Combiner opts2,
@ -903,14 +962,14 @@ public:
template<typename T, template<typename T,
enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy> enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name,
const T& default_value, const T& default_value,
std::string discription="", std::string discription="",
detail::Combiner opts=VALIDATORS detail::Combiner opts=Validators
) { ) {
if(opts.num!=1) if(opts.num!=1)
throw IncorrectConstruction("Must have ARGS(1)."); throw IncorrectConstruction("Must have Args(1).");
Value<T> out(name); Value<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value; std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
@ -938,13 +997,13 @@ public:
template<typename T, template<typename T,
enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy> enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name,
std::string discription="", std::string discription="",
detail::Combiner opts=VALIDATORS detail::Combiner opts=Args
) { ) {
if(opts.num==0) if(opts.num==0)
throw IncorrectConstruction("Must have ARGS or be a vector."); throw IncorrectConstruction("Must have Args or be a vector.");
Value<T> out(name); Value<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value; std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
@ -967,7 +1026,7 @@ public:
template<typename T, typename... Args> template<typename T, typename... Args>
Value<T> make_option( Value<T> make_option(
std::string name, ///< The name, short,long std::string name,
const T& default_value, const T& default_value,
std::string discription, std::string discription,
detail::Combiner opts, detail::Combiner opts,
@ -979,7 +1038,7 @@ public:
/// Prototype for new output style: flag /// Prototype for new output style: flag
Value<int> make_flag( Value<int> make_flag(
std::string name, ///< The name, short,long std::string name,
std::string discription="" std::string discription=""
) { ) {
@ -992,24 +1051,27 @@ public:
**ptr = (int) res.size(); **ptr = (int) res.size();
return true; return true;
}; };
add_option(name, fun, discription, NOTHING);
Option* opt = add_option(name, fun, discription, Nothing);
if(opt->positional())
throw IncorrectConstruction("Flags cannot be positional");
return out; return out;
} }
/// Add set of options /// Add set of options
template<typename T> template<typename T>
Value<T> make_set( Value<T> make_set(
std::string name, ///< The name, short,long std::string name,
std::set<T> options, ///< The set of posibilities std::set<T> options, ///< The set of posibilities
std::string discription="", ///< Discription string std::string discription="",
detail::Combiner opts=VALIDATORS ///< The options (REQUIRED, DEFAULT, POSITIONAL, ARGS()) detail::Combiner opts=Validators
) { ) {
Value<T> out(name); Value<T> out(name);
std::shared_ptr<std::unique_ptr<T>> ptr = out.value; std::shared_ptr<std::unique_ptr<T>> ptr = out.value;
if(opts.num!=1) if(opts.num!=1)
throw IncorrectConstruction("Must have ARGS(1)."); throw IncorrectConstruction("Must have Args(1).");
CLI::callback_t fun = [ptr, options](CLI::results_t res){ CLI::callback_t fun = [ptr, options](CLI::results_t res){
if(res.size()!=1) { if(res.size()!=1) {
@ -1046,6 +1108,7 @@ public:
/// Parses the command line - throws errors /// Parses the command line - throws errors
void parse(int argc, char **argv) { void parse(int argc, char **argv) {
progname = argv[0];
std::vector<std::string> args; std::vector<std::string> args;
for(int i=argc-1; i>0; i--) for(int i=argc-1; i>0; i--)
args.push_back(argv[i]); args.push_back(argv[i]);
@ -1081,7 +1144,7 @@ public:
} }
} }
if (count("help") > 0) { if (count("--help") > 0) {
throw CallForHelp(); throw CallForHelp();
} }
@ -1259,12 +1322,50 @@ public:
if(name != "") if(name != "")
out << "Subcommand: " << name << " "; out << "Subcommand: " << name << " ";
out << prog_discription << std::endl; out << prog_discription << std::endl;
out << "Usage: " << progname;
// Positionals
bool pos=false;
for(const Option &opt : options)
if(opt.positional()) {
out << " " << opt.help_positional();
if(opt.has_discription())
pos=true;
}
out << std::endl << std::endl;
// Positional discriptions
if(pos) {
out << "Positionals:" << std::endl;
for(const Option &opt : options)
if(opt.positional() && opt.has_discription()) {
out << " " << std::setw(30) << std::right << opt.help_positional();
out << opt.get_discription() << std::endl;
}
out << std::endl;
}
// Options
int len = std::accumulate(std::begin(options), std::end(options), 0, int len = std::accumulate(std::begin(options), std::end(options), 0,
[](int val, const Option &opt){ [](int val, const Option &opt){
return std::max(opt.help_len()+3, val);}); return std::max(opt.help_len()+3, val);});
bool npos = false;
for(const Option &opt : options) { for(const Option &opt : options) {
out << opt.help(len) << std::endl; if(opt.nonpositional()) {
if(!npos) {
out << "Options:" << std::endl;
npos=true;
}
out << opt.help(len) << std::endl;
}
if(npos)
out << std::endl;
} }
// Subcommands
if(subcommands.size()> 0) { if(subcommands.size()> 0) {
out << "Subcommands:" << std::endl; out << "Subcommands:" << std::endl;
int max = std::accumulate(std::begin(subcommands), std::end(subcommands), 0, int max = std::accumulate(std::begin(subcommands), std::end(subcommands), 0,

View File

@ -37,19 +37,19 @@ struct TApp : public ::testing::Test {
}; };
TEST_F(TApp, OneFlagShort) { TEST_F(TApp, OneFlagShort) {
app.add_flag("c,count"); app.add_flag("-c,--count");
args = {"-c"}; args = {"-c"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("c")); EXPECT_EQ(1, app.count("-c"));
EXPECT_EQ(1, app.count("count")); EXPECT_EQ(1, app.count("--count"));
} }
TEST_F(TApp, OneFlagLong) { TEST_F(TApp, OneFlagLong) {
app.add_flag("c,count"); app.add_flag("-c,--count");
args = {"--count"}; args = {"--count"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("c")); EXPECT_EQ(1, app.count("-c"));
EXPECT_EQ(1, app.count("count")); EXPECT_EQ(1, app.count("--count"));
} }
TEST_F(TApp, DashedOptions) { TEST_F(TApp, DashedOptions) {
@ -59,9 +59,9 @@ TEST_F(TApp, DashedOptions) {
args = {"-c", "--q", "--this", "--that"}; args = {"-c", "--q", "--this", "--that"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("c")); EXPECT_EQ(1, app.count("-c"));
EXPECT_EQ(1, app.count("q")); EXPECT_EQ(1, app.count("--q"));
EXPECT_EQ(2, app.count("this")); EXPECT_EQ(2, app.count("--this"));
EXPECT_EQ(2, app.count("--that")); EXPECT_EQ(2, app.count("--that"));
} }
@ -69,76 +69,76 @@ TEST_F(TApp, DashedOptions) {
TEST_F(TApp, OneFlagRef) { TEST_F(TApp, OneFlagRef) {
int ref; int ref;
app.add_flag("c,count", ref); app.add_flag("-c,--count", ref);
args = {"--count"}; args = {"--count"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("c")); EXPECT_EQ(1, app.count("-c"));
EXPECT_EQ(1, app.count("count")); EXPECT_EQ(1, app.count("--count"));
EXPECT_EQ(1, ref); EXPECT_EQ(1, ref);
} }
TEST_F(TApp, OneString) { TEST_F(TApp, OneString) {
std::string str; std::string str;
app.add_option("s,string", str); app.add_option("-s,--string", str);
args = {"--string", "mystring"}; args = {"--string", "mystring"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("s")); EXPECT_EQ(1, app.count("-s"));
EXPECT_EQ(1, app.count("string")); EXPECT_EQ(1, app.count("--string"));
EXPECT_EQ(str, "mystring"); EXPECT_EQ(str, "mystring");
} }
TEST_F(TApp, TogetherInt) { TEST_F(TApp, TogetherInt) {
int i; int i;
app.add_option("i,int", i); app.add_option("-i,--int", i);
args = {"-i4"}; args = {"-i4"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("int")); EXPECT_EQ(1, app.count("--int"));
EXPECT_EQ(1, app.count("i")); EXPECT_EQ(1, app.count("-i"));
EXPECT_EQ(i, 4); EXPECT_EQ(i, 4);
} }
TEST_F(TApp, SepInt) { TEST_F(TApp, SepInt) {
int i; int i;
app.add_option("i,int", i); app.add_option("-i,--int", i);
args = {"-i","4"}; args = {"-i","4"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("int")); EXPECT_EQ(1, app.count("--int"));
EXPECT_EQ(1, app.count("i")); EXPECT_EQ(1, app.count("-i"));
EXPECT_EQ(i, 4); EXPECT_EQ(i, 4);
} }
TEST_F(TApp, OneStringAgain) { TEST_F(TApp, OneStringAgain) {
std::string str; std::string str;
app.add_option("s,string", str); app.add_option("-s,--string", str);
args = {"--string", "mystring"}; args = {"--string", "mystring"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("s")); EXPECT_EQ(1, app.count("-s"));
EXPECT_EQ(1, app.count("string")); EXPECT_EQ(1, app.count("--string"));
EXPECT_EQ(str, "mystring"); EXPECT_EQ(str, "mystring");
} }
TEST_F(TApp, DefaultStringAgain) { TEST_F(TApp, DefaultStringAgain) {
std::string str = "previous"; std::string str = "previous";
app.add_option("s,string", str); app.add_option("-s,--string", str);
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(0, app.count("s")); EXPECT_EQ(0, app.count("-s"));
EXPECT_EQ(0, app.count("string")); EXPECT_EQ(0, app.count("--string"));
EXPECT_EQ(str, "previous"); EXPECT_EQ(str, "previous");
} }
TEST_F(TApp, LotsOfFlags) { TEST_F(TApp, LotsOfFlags) {
app.add_flag("a"); app.add_flag("-a");
app.add_flag("A"); app.add_flag("-A");
app.add_flag("b"); app.add_flag("-b");
args = {"-a","-b","-aA"}; args = {"-a","-b","-aA"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(2, app.count("a")); EXPECT_EQ(2, app.count("-a"));
EXPECT_EQ(1, app.count("b")); EXPECT_EQ(1, app.count("-b"));
EXPECT_EQ(1, app.count("A")); EXPECT_EQ(1, app.count("-A"));
} }
@ -148,9 +148,9 @@ TEST_F(TApp, BoolAndIntFlags) {
int iflag; int iflag;
unsigned int uflag; unsigned int uflag;
app.add_flag("b", bflag); app.add_flag("-b", bflag);
app.add_flag("i", iflag); app.add_flag("-i", iflag);
app.add_flag("u", uflag); app.add_flag("-u", uflag);
args = {"-b", "-i", "-u"}; args = {"-b", "-i", "-u"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
@ -177,15 +177,15 @@ TEST_F(TApp, ShortOpts) {
unsigned long long funnyint; unsigned long long funnyint;
std::string someopt; std::string someopt;
app.add_flag("z", funnyint); app.add_flag("-z", funnyint);
app.add_option("y", someopt); app.add_option("-y", someopt);
args = {"-zzyzyz",}; args = {"-zzyzyz",};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(2, app.count("z")); EXPECT_EQ(2, app.count("-z"));
EXPECT_EQ(1, app.count("y")); EXPECT_EQ(1, app.count("-y"));
EXPECT_EQ((unsigned long long) 2, funnyint); EXPECT_EQ((unsigned long long) 2, funnyint);
EXPECT_EQ("zyz", someopt); EXPECT_EQ("zyz", someopt);
} }
@ -195,15 +195,15 @@ TEST_F(TApp, Flags) {
int i = 3; int i = 3;
std::string s = "HI"; std::string s = "HI";
app.add_option("-i", i, "", CLI::DEFAULT, CLI::POSITIONAL); app.add_option("-i,i", i, "", CLI::Default);
app.add_option("-s", s, "", CLI::DEFAULT, CLI::POSITIONAL); app.add_option("-s,s", s, "", CLI::Default);
args = {"-i2", "9"}; args = {"-i2", "9"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("i")); EXPECT_EQ(1, app.count("i"));
EXPECT_EQ(1, app.count("s")); EXPECT_EQ(1, app.count("-s"));
EXPECT_EQ(2, i); EXPECT_EQ(2, i);
EXPECT_EQ("9", s); EXPECT_EQ("9", s);
} }
@ -212,8 +212,8 @@ TEST_F(TApp, Positionals) {
std::string posit1; std::string posit1;
std::string posit2; std::string posit2;
app.add_option("posit1", posit1, "", CLI::POSITIONAL); app.add_option("posit1", posit1);
app.add_option("posit2", posit2, "", CLI::POSITIONAL); app.add_option("posit2", posit2);
args = {"thing1","thing2"}; args = {"thing1","thing2"};
@ -229,42 +229,42 @@ TEST_F(TApp, MixedPositionals) {
int positional_int; int positional_int;
std::string positional_string; std::string positional_string;
app.add_option("posit1", positional_int, "", CLI::POSITIONAL); app.add_option("posit1,--posit1", positional_int, "");
app.add_option("posit2", positional_string, "", CLI::POSITIONAL); app.add_option("posit2,--posit2", positional_string, "");
args = {"--posit2","thing2","7"}; args = {"--posit2","thing2","7"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("posit2")); EXPECT_EQ(1, app.count("posit2"));
EXPECT_EQ(1, app.count("posit1")); EXPECT_EQ(1, app.count("--posit1"));
EXPECT_EQ(7, positional_int); EXPECT_EQ(7, positional_int);
EXPECT_EQ("thing2", positional_string); EXPECT_EQ("thing2", positional_string);
} }
TEST_F(TApp, Reset) { TEST_F(TApp, Reset) {
app.add_flag("simple"); app.add_flag("--simple");
double doub; double doub;
app.add_option("d,double", doub); app.add_option("-d,--double", doub);
args = {"--simple", "--double", "1.2"}; args = {"--simple", "--double", "1.2"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("simple")); EXPECT_EQ(1, app.count("--simple"));
EXPECT_EQ(1, app.count("d")); EXPECT_EQ(1, app.count("-d"));
EXPECT_FLOAT_EQ(1.2, doub); EXPECT_FLOAT_EQ(1.2, doub);
app.reset(); app.reset();
EXPECT_EQ(0, app.count("simple")); EXPECT_EQ(0, app.count("--simple"));
EXPECT_EQ(0, app.count("d")); EXPECT_EQ(0, app.count("-d"));
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(1, app.count("simple")); EXPECT_EQ(1, app.count("--simple"));
EXPECT_EQ(1, app.count("d")); EXPECT_EQ(1, app.count("-d"));
EXPECT_FLOAT_EQ(1.2, doub); EXPECT_FLOAT_EQ(1.2, doub);
} }
@ -275,7 +275,7 @@ TEST_F(TApp, FileNotExists) {
EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile)); EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile));
std::string filename; std::string filename;
app.add_option("file", filename, "", CLI::NonexistentPath); app.add_option("--file", filename, "", CLI::NonexistentPath);
args = {"--file", myfile}; args = {"--file", myfile};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
@ -297,7 +297,7 @@ TEST_F(TApp, FileExists) {
EXPECT_FALSE(CLI::detail::_ExistingFile(myfile)); EXPECT_FALSE(CLI::detail::_ExistingFile(myfile));
std::string filename = "Failed"; std::string filename = "Failed";
app.add_option("file", filename, "", CLI::ExistingFile); app.add_option("--file", filename, "", CLI::ExistingFile);
args = {"--file", myfile}; args = {"--file", myfile};
EXPECT_THROW(run(), CLI::ParseError); EXPECT_THROW(run(), CLI::ParseError);
@ -317,7 +317,7 @@ TEST_F(TApp, FileExists) {
TEST_F(TApp, InSet) { TEST_F(TApp, InSet) {
std::string choice; std::string choice;
app.add_set("q,quick", choice, {"one", "two", "three"}); app.add_set("-q,--quick", choice, {"one", "two", "three"});
args = {"--quick", "two"}; args = {"--quick", "two"};
@ -334,12 +334,12 @@ TEST_F(TApp, VectorFixedString) {
std::vector<std::string> strvec; std::vector<std::string> strvec;
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
CLI::Option* opt = app.add_option("s,string", strvec, "", CLI::ARGS(3)); CLI::Option* opt = app.add_option("-s,--string", strvec, "", CLI::Args(3));
EXPECT_EQ(3, opt->expected()); EXPECT_EQ(3, opt->expected());
args = {"--string", "mystring", "mystring2", "mystring3"}; args = {"--string", "mystring", "mystring2", "mystring3"};
run(); run();
EXPECT_EQ(3, app.count("string")); EXPECT_EQ(3, app.count("--string"));
EXPECT_EQ(answer, strvec); EXPECT_EQ(answer, strvec);
} }
@ -349,12 +349,12 @@ TEST_F(TApp, VectorUnlimString) {
std::vector<std::string> strvec; std::vector<std::string> strvec;
std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
CLI::Option* opt = app.add_option("s,string", strvec, "", CLI::ARGS); CLI::Option* opt = app.add_option("-s,--string", strvec);
EXPECT_EQ(-1, opt->expected()); EXPECT_EQ(-1, opt->expected());
args = {"--string", "mystring", "mystring2", "mystring3"}; args = {"--string", "mystring", "mystring2", "mystring3"};
EXPECT_NO_THROW(run()); EXPECT_NO_THROW(run());
EXPECT_EQ(3, app.count("string")); EXPECT_EQ(3, app.count("--string"));
EXPECT_EQ(answer, strvec); EXPECT_EQ(answer, strvec);
} }
@ -417,9 +417,9 @@ struct SubcommandProgram : public TApp {
start = app.add_subcommand("start", "Start prog"); start = app.add_subcommand("start", "Start prog");
stop = app.add_subcommand("stop", "Stop prog"); stop = app.add_subcommand("stop", "Stop prog");
app.add_flag("d", dummy, "My dummy var"); app.add_flag("-d", dummy, "My dummy var");
start->add_option("f,file", file, "File name"); start->add_option("-f,--file", file, "File name");
stop->add_flag("c,count", count, "Some flag opt"); stop->add_flag("-c,--count", count, "Some flag opt");
} }
}; };
@ -449,7 +449,7 @@ TEST_F(SubcommandProgram, SpareSub) {
class TAppValue : public TApp {}; class TAppValue : public TApp {};
TEST_F(TAppValue, OneString) { TEST_F(TAppValue, OneString) {
auto str = app.make_option("s,string"); auto str = app.make_option("-s,--string");
std::string v; std::string v;
args = {"--string", "mystring"}; args = {"--string", "mystring"};
EXPECT_FALSE((bool) str); EXPECT_FALSE((bool) str);
@ -461,15 +461,15 @@ TEST_F(TAppValue, OneString) {
EXPECT_NO_THROW(v = *str); EXPECT_NO_THROW(v = *str);
EXPECT_NO_THROW(v = str); EXPECT_NO_THROW(v = str);
EXPECT_EQ(1, app.count("s")); EXPECT_EQ(1, app.count("-s"));
EXPECT_EQ(1, app.count("string")); EXPECT_EQ(1, app.count("--string"));
EXPECT_EQ(*str, "mystring"); EXPECT_EQ(*str, "mystring");
} }
TEST_F(TAppValue, SeveralInts) { TEST_F(TAppValue, SeveralInts) {
auto value = app.make_option<int>("first"); auto value = app.make_option<int>("--first");
CLI::Value<int> value2 = app.make_option<int>("s"); CLI::Value<int> value2 = app.make_option<int>("-s");
int v; int v;
args = {"--first", "12", "-s", "19"}; args = {"--first", "12", "-s", "19"};
EXPECT_FALSE((bool) value); EXPECT_FALSE((bool) value);
@ -482,16 +482,16 @@ TEST_F(TAppValue, SeveralInts) {
EXPECT_NO_THROW(v = *value); EXPECT_NO_THROW(v = *value);
EXPECT_NO_THROW(v = value); EXPECT_NO_THROW(v = value);
EXPECT_EQ(1, app.count("s")); EXPECT_EQ(1, app.count("-s"));
EXPECT_EQ(1, app.count("first")); EXPECT_EQ(1, app.count("--first"));
EXPECT_EQ(*value, 12); EXPECT_EQ(*value, 12);
EXPECT_EQ(*value2, 19); EXPECT_EQ(*value2, 19);
} }
TEST_F(TAppValue, Vector) { TEST_F(TAppValue, Vector) {
auto value = app.make_option<std::vector<int>>("first", "", CLI::ARGS); auto value = app.make_option<std::vector<int>>("--first", "", CLI::Args);
auto value2 = app.make_option<std::vector<std::string>>("second", "", CLI::ARGS); auto value2 = app.make_option<std::vector<std::string>>("--second");
std::vector<int> i; std::vector<int> i;
std::vector<std::string> s; std::vector<std::string> s;
@ -515,8 +515,8 @@ TEST_F(TAppValue, Vector) {
EXPECT_NO_THROW(s = *value2); EXPECT_NO_THROW(s = *value2);
//EXPECT_NO_THROW(s = value2); //EXPECT_NO_THROW(s = value2);
EXPECT_EQ(3, app.count("first")); EXPECT_EQ(3, app.count("--first"));
EXPECT_EQ(2, app.count("second")); EXPECT_EQ(2, app.count("--second"));
EXPECT_EQ(std::vector<int>({12,3,9}), *value); EXPECT_EQ(std::vector<int>({12,3,9}), *value);
EXPECT_EQ(std::vector<std::string>({"thing", "try"}), *value2); EXPECT_EQ(std::vector<std::string>({"thing", "try"}), *value2);
@ -524,7 +524,7 @@ TEST_F(TAppValue, Vector) {
} }
TEST_F(TAppValue, DoubleVector) { TEST_F(TAppValue, DoubleVector) {
auto value = app.make_option<std::vector<double>>("simple", "", CLI::ARGS); auto value = app.make_option<std::vector<double>>("--simple");
std::vector<double> d; std::vector<double> d;
args = {"--simple", "1.2", "3.4", "-1"}; args = {"--simple", "1.2", "3.4", "-1"};
@ -535,7 +535,7 @@ TEST_F(TAppValue, DoubleVector) {
EXPECT_NO_THROW(d = *value); EXPECT_NO_THROW(d = *value);
EXPECT_EQ(3, app.count("simple")); EXPECT_EQ(3, app.count("--simple"));
EXPECT_EQ(std::vector<double>({1.2, 3.4, -1}), *value); EXPECT_EQ(std::vector<double>({1.2, 3.4, -1}), *value);
} }

View File

@ -87,17 +87,20 @@ TEST(Regex, SplittingNew) {
std::vector<std::string> shorts; std::vector<std::string> shorts;
std::vector<std::string> longs; std::vector<std::string> longs;
std::string pname;
EXPECT_NO_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--long", "s", "-q", "also-long"})); EXPECT_NO_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--long", "-s", "-q", "--also-long"}));
EXPECT_EQ(std::vector<std::string>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts);
EXPECT_EQ("", pname);
EXPECT_NO_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--long", "", "-s", "-q", "", "--also-long"}));
EXPECT_EQ(std::vector<std::string>({"long", "also-long"}), longs); EXPECT_EQ(std::vector<std::string>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts); EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts);
EXPECT_NO_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--long", "", "s", "-q", "", "also-long"})); EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"-"}), CLI::BadNameString);
EXPECT_EQ(std::vector<std::string>({"long", "also-long"}), longs); EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--"}), CLI::BadNameString);
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts); EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"-hi"}), CLI::BadNameString);
EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"one","two"}), CLI::BadNameString);
EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"-"}), CLI::BadNameString);
EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"--"}), CLI::BadNameString);
EXPECT_THROW(std::tie(shorts, longs) = CLI::detail::get_names({"-hi"}), CLI::BadNameString);
} }