1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 20:23:55 +00:00

Reformat with clang-format

This commit is contained in:
Henry Fredrick Schreiner 2017-05-31 12:03:05 -04:00
parent f24561f460
commit 27f718125d
27 changed files with 902 additions and 1097 deletions

86
.clang-format Normal file
View File

@ -0,0 +1,86 @@
Language: Cpp
BasedOnStyle: LLVM
# AccessModifierOffset: -2
# AlignAfterOpenBracket: Align
# AlignConsecutiveAssignments: false
# AlignConsecutiveDeclarations: false
# AlignEscapedNewlinesLeft: false
# AlignOperands: true
# AlignTrailingComments: true
# AllowAllParametersOfDeclarationOnNextLine: true
# AllowShortBlocksOnASingleLine: false
# AllowShortCaseLabelsOnASingleLine: false
# AllowShortFunctionsOnASingleLine: All
# AllowShortIfStatementsOnASingleLine: false
# AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterDefinitionReturnType: None
# AlwaysBreakAfterReturnType: None
# AlwaysBreakBeforeMultilineStrings: false
# AlwaysBreakTemplateDeclarations: false
BinPackArguments: false
BinPackParameters: false
# BraceWrapping:
# AfterClass: false
# AfterControlStatement: false
# AfterEnum: false
# AfterFunction: false
# AfterNamespace: false
# AfterObjCDeclaration: false
# AfterStruct: false
# AfterUnion: false
# BeforeCatch: false
# BeforeElse: false
# IndentBraces: false
# BreakBeforeBinaryOperators: None
# BreakBeforeBraces: Attach
# BreakBeforeTernaryOperators: true
# BreakConstructorInitializersBeforeComma: false
# BreakAfterJavaFieldAnnotations: false
# BreakStringLiterals: true
ColumnLimit: 120
# CommentPragmas: '^ IWYU pragma:'
# ConstructorInitializerAllOnOneLineOrOnePerLine: false
# ConstructorInitializerIndentWidth: 4
# ContinuationIndentWidth: 4
# Cpp11BracedListStyle: true
# DerivePointerAlignment: false
# DisableFormat: false
# ExperimentalAutoDetectBinPacking: false
# ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
# IncludeIsMainRegex: '$'
# IndentCaseLabels: false
IndentWidth: 4
# IndentWrappedFunctionNames: false
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
# KeepEmptyLinesAtTheStartOfBlocks: true
# MacroBlockBegin: ''
# MacroBlockEnd: ''
# MaxEmptyLinesToKeep: 1
# NamespaceIndentation: None
# ObjCBlockIndentWidth: 2
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
# PenaltyBreakBeforeFirstCallParameter: 19
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakString: 1000
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 60
# PointerAlignment: Right
# ReflowComments: true
SortIncludes: false
# SpaceAfterCStyleCast: false
# SpaceAfterTemplateKeyword: true
# SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: Never
# SpaceInEmptyParentheses: false
# SpacesBeforeTrailingComments: 1
# SpacesInAngles: false
# SpacesInContainerLiterals: true
# SpacesInCStyleCastParentheses: false
# SpacesInParentheses: false
# SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never

View File

@ -1,33 +1,28 @@
#include "CLI/CLI.hpp" #include "CLI/CLI.hpp"
int main(int argc, char **argv) {
int main (int argc, char** argv) {
CLI::App app("K3Pi goofit fitter"); CLI::App app("K3Pi goofit fitter");
std::string file; std::string file;
CLI::Option* opt = app.add_option("-f,--file,file", file, "File name"); CLI::Option *opt = app.add_option("-f,--file,file", file, "File name");
int count; int count;
CLI::Option* copt = app.add_flag("-c,--count", count, "Counter"); CLI::Option *copt = app.add_flag("-c,--count", count, "Counter");
double value;// = 3.14; double value; // = 3.14;
app.add_option("-d,--double", value, "Some Value"); app.add_option("-d,--double", value, "Some Value");
try { try {
app.parse(argc, argv); app.parse(argc, argv);
} catch (const CLI::Error &e) { } catch(const CLI::Error &e) {
return app.exit(e); return app.exit(e);
} }
std::cout << "Working on file: " << file std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
<< ", direct count: " << app.count("--file") << ", opt count: " << opt->count() << std::endl;
<< ", opt count: " << opt->count() std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
<< std::endl; << ", opt count: " << copt->count() << std::endl;
std::cout << "Working on count: " << count
<< ", direct count: " << app.count("--count")
<< ", opt count: " << copt->count()
<< std::endl;
std::cout << "Some value: " << value << std::endl; std::cout << "Some value: " << value << std::endl;
return 0; return 0;

View File

@ -1,21 +1,20 @@
#include "CLI/CLI.hpp" #include "CLI/CLI.hpp"
int main(int argc, char **argv) {
int main (int argc, char** argv) {
CLI::App app("K3Pi goofit fitter"); CLI::App app("K3Pi goofit fitter");
app.add_flag("--random", "Some random flag"); app.add_flag("--random", "Some random flag");
CLI::App* start = app.add_subcommand("start", "A great subcommand"); CLI::App *start = app.add_subcommand("start", "A great subcommand");
CLI::App* stop = app.add_subcommand("stop", "Do you really want to stop?"); CLI::App *stop = app.add_subcommand("stop", "Do you really want to stop?");
std::string file; std::string file;
start->add_option("-f,--file", file, "File name"); start->add_option("-f,--file", file, "File name");
CLI::Option* s = stop->add_flag("-c,--count", "Counter"); CLI::Option *s = stop->add_flag("-c,--count", "Counter");
try { try {
app.parse(argc, argv); app.parse(argc, argv);
} catch (const CLI::Error &e) { } catch(const CLI::Error &e) {
return app.exit(e); return app.exit(e);
} }

View File

@ -1,38 +1,30 @@
#include "CLI/CLI.hpp" #include "CLI/CLI.hpp"
#include "CLI/Timer.hpp" #include "CLI/Timer.hpp"
int main(int argc, char **argv) {
int main (int argc, char** argv) {
CLI::AutoTimer("This is a timer"); CLI::AutoTimer("This is a timer");
CLI::App app("K3Pi goofit fitter"); CLI::App app("K3Pi goofit fitter");
std::string file; std::string file;
CLI::Option* opt = app.add_option("-f,--file,file", file, "File name") CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")->required()->group("Important");
->required()->group("Important");
int count; int count;
CLI::Option* copt = app.add_flag("-c,--count", count, "Counter") CLI::Option *copt = app.add_flag("-c,--count", count, "Counter")->required()->group("Important");
->required()->group("Important");
double value;// = 3.14; double value; // = 3.14;
app.add_option("-d,--double", value, "Some Value") app.add_option("-d,--double", value, "Some Value")->group("Other");
->group("Other");
try { try {
app.parse(argc, argv); app.parse(argc, argv);
} catch (const CLI::Error &e) { } catch(const CLI::Error &e) {
return app.exit(e); return app.exit(e);
} }
std::cout << "Working on file: " << file std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
<< ", direct count: " << app.count("--file") << ", opt count: " << opt->count() << std::endl;
<< ", opt count: " << opt->count() std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
<< std::endl; << ", opt count: " << copt->count() << std::endl;
std::cout << "Working on count: " << count
<< ", direct count: " << app.count("--count")
<< ", opt count: " << copt->count()
<< std::endl;
std::cout << "Some value: " << value << std::endl; std::cout << "Some value: " << value << std::endl;
return 0; return 0;

View File

@ -15,7 +15,6 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
// CLI Library includes // CLI Library includes
#include "CLI/Error.hpp" #include "CLI/Error.hpp"
#include "CLI/Ini.hpp" #include "CLI/Ini.hpp"
@ -27,13 +26,12 @@
namespace CLI { namespace CLI {
namespace detail { namespace detail {
enum class Classifer {NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND}; enum class Classifer { NONE, POSITIONAL_MARK, SHORT, LONG, SUBCOMMAND };
struct AppFriend; struct AppFriend;
} // namespace detail } // namespace detail
class App; class App;
using App_p = std::unique_ptr<App>; using App_p = std::unique_ptr<App>;
/// Creates a command line program, with very few defaults. /// Creates a command line program, with very few defaults.
@ -43,21 +41,21 @@ using App_p = std::unique_ptr<App>;
class App { class App {
friend Option; friend Option;
friend detail::AppFriend; friend detail::AppFriend;
protected:
protected:
// This library follows the Google style guide for member names ending in underscores // This library follows the Google style guide for member names ending in underscores
/// @name Basics /// @name Basics
///@{ ///@{
/// Subcommand name or program name (from parser) /// Subcommand name or program name (from parser)
std::string name_ {"program"}; std::string name_{"program"};
/// Description of the current program/subcommand /// Description of the current program/subcommand
std::string description_; std::string description_;
/// If true, allow extra arguments (ie, don't throw an error). /// If true, allow extra arguments (ie, don't throw an error).
bool allow_extras_ {false}; bool allow_extras_{false};
/// This is a function that runs when complete. Great for subcommands. Can throw. /// This is a function that runs when complete. Great for subcommands. Can throw.
std::function<void()> callback_; std::function<void()> callback_;
@ -70,7 +68,7 @@ protected:
std::vector<Option_p> options_; std::vector<Option_p> options_;
/// A pointer to the help flag if there is one /// A pointer to the help flag if there is one
Option* help_ptr_ {nullptr}; Option *help_ptr_{nullptr};
///@} ///@}
/// @name Parsing /// @name Parsing
@ -91,16 +89,16 @@ protected:
std::vector<App_p> subcommands_; std::vector<App_p> subcommands_;
/// If true, the program name is not case sensitive /// If true, the program name is not case sensitive
bool ignore_case_ {false}; bool ignore_case_{false};
/// Allow subcommand fallthrough, so that parent commands can collect commands after subcommand. /// Allow subcommand fallthrough, so that parent commands can collect commands after subcommand.
bool fallthrough_ {false}; bool fallthrough_{false};
/// A pointer to the parent if this is a subcommand /// A pointer to the parent if this is a subcommand
App* parent_ {nullptr}; App *parent_{nullptr};
/// True if this command/subcommand was parsed /// True if this command/subcommand was parsed
bool parsed_ {false}; bool parsed_{false};
/// -1 for 1 or more, 0 for not required, # for exact number required /// -1 for 1 or more, 0 for not required, # for exact number required
int require_subcommand_ = 0; int require_subcommand_ = 0;
@ -113,32 +111,26 @@ protected:
std::string config_name_; std::string config_name_;
/// True if ini is required (throws if not present), if false simply keep going. /// True if ini is required (throws if not present), if false simply keep going.
bool config_required_ {false}; bool config_required_{false};
/// Pointer to the config option /// Pointer to the config option
Option* config_ptr_ {nullptr}; Option *config_ptr_{nullptr};
///@} ///@}
/// Special private constructor for subcommand /// Special private constructor for subcommand
App(std::string description_, bool help, detail::enabler) App(std::string description_, bool help, detail::enabler) : description_(std::move(description_)) {
: description_(std::move(description_)) {
if(help) if(help)
help_ptr_ = add_flag("-h,--help", "Print this help message and exit"); help_ptr_ = add_flag("-h,--help", "Print this help message and exit");
} }
public: public:
/// @name Basic /// @name Basic
///@{ ///@{
/// Create a new program. Pass in the same arguments as main(), along with a help string. /// Create a new program. Pass in the same arguments as main(), along with a help string.
App(std::string description_="", bool help=true) App(std::string description_ = "", bool help = true) : App(description_, help, detail::dummy) {}
: App(description_, help, detail::dummy) {
}
/// Set a callback for the end of parsing. /// Set a callback for the end of parsing.
/// ///
@ -146,19 +138,19 @@ public:
/// it is not possible to overload on std::function (fixed in c++14 /// it is not possible to overload on std::function (fixed in c++14
/// and backported to c++11 on newer compilers). Use capture by reference /// and backported to c++11 on newer compilers). Use capture by reference
/// to get a pointer to App if needed. /// to get a pointer to App if needed.
App* set_callback(std::function<void()> callback) { App *set_callback(std::function<void()> callback) {
callback_ = callback; callback_ = callback;
return this; return this;
} }
/// Remove the error when extras are left over on the command line. /// Remove the error when extras are left over on the command line.
App* allow_extras (bool allow=true) { App *allow_extras(bool allow = true) {
allow_extras_ = allow; allow_extras_ = allow;
return this; return this;
} }
/// Ignore case. Subcommand inherit value. /// Ignore case. Subcommand inherit value.
App* ignore_case(bool value = true) { App *ignore_case(bool value = true) {
ignore_case_ = value; ignore_case_ = value;
if(parent_ != nullptr) { if(parent_ != nullptr) {
for(const auto &subc : parent_->subcommands_) { for(const auto &subc : parent_->subcommands_) {
@ -170,27 +162,26 @@ public:
} }
/// Check to see if this subcommand was parsed, true only if received on command line. /// Check to see if this subcommand was parsed, true only if received on command line.
bool parsed() const {return parsed_;} bool parsed() const { return parsed_; }
/// Check to see if this subcommand was parsed, true only if received on command line. /// Check to see if this subcommand was parsed, true only if received on command line.
/// This allows the subcommand to be directly checked. /// This allows the subcommand to be directly checked.
operator bool () const { return parsed_;} operator bool() const { return parsed_; }
/// Require a subcommand to be given (does not affect help call) /// Require a subcommand to be given (does not affect help call)
/// Does not return a pointer since it is supposed to be called on the main App. /// Does not return a pointer since it is supposed to be called on the main App.
App* require_subcommand(int value = -1) { App *require_subcommand(int value = -1) {
require_subcommand_ = value; require_subcommand_ = value;
return this; return this;
} }
/// Stop subcommand fallthrough, so that parent commands cannot collect commands after subcommand. /// Stop subcommand fallthrough, so that parent commands cannot collect commands after subcommand.
/// Default from parent, usually set on parent. /// Default from parent, usually set on parent.
App* fallthrough(bool value=true) { App *fallthrough(bool value = true) {
fallthrough_ = value; fallthrough_ = value;
return this; return this;
} }
///@} ///@}
/// @name Adding options /// @name Adding options
///@{ ///@{
@ -209,42 +200,34 @@ public:
/// std::string filename; /// std::string filename;
/// program.add_option("filename", filename, "description of filename"); /// program.add_option("filename", filename, "description of filename");
/// ///
Option* add_option( Option *add_option(std::string name, callback_t callback, std::string description = "", bool defaulted = false) {
std::string name,
callback_t callback,
std::string description="",
bool defaulted=false
) {
Option myopt{name, description, callback, defaulted, this}; Option myopt{name, description, callback, defaulted, this};
if(std::find_if(std::begin(options_), std::end(options_), if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) {
[&myopt](const Option_p &v){return *v == myopt;}) == std::end(options_)) { return *v == myopt;
}) == std::end(options_)) {
options_.emplace_back(); options_.emplace_back();
Option_p& option = options_.back(); Option_p &option = options_.back();
option.reset(new Option(name, description, callback, defaulted, this)); option.reset(new Option(name, description, callback, defaulted, this));
return option.get(); return option.get();
} else } else
throw OptionAlreadyAdded(myopt.get_name()); throw OptionAlreadyAdded(myopt.get_name());
} }
/// Add option for non-vectors /// Add option for non-vectors
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,
std::string name,
T &variable, ///< The variable to set T &variable, ///< The variable to set
std::string description="", std::string description = "",
bool defaulted=false bool defaulted = false) {
) {
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;
return detail::lexical_cast(res[0], variable); return detail::lexical_cast(res[0], variable);
}; };
Option* opt = add_option(name, fun, description, defaulted); Option *opt = add_option(name, fun, description, defaulted);
opt->set_custom_option(detail::type_name<T>()); opt->set_custom_option(detail::type_name<T>());
if(defaulted) { if(defaulted) {
std::stringstream out; std::stringstream out;
@ -255,15 +238,13 @@ public:
} }
/// Add option for vectors /// Add option for vectors
template<typename T> template <typename T>
Option* add_option( Option *add_option(std::string name,
std::string name,
std::vector<T> &variable, ///< The variable vector to set std::vector<T> &variable, ///< The variable vector to set
std::string description="", std::string description = "",
bool defaulted=false bool defaulted = false) {
) {
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();
for(const auto &a : res) { for(const auto &a : res) {
@ -273,7 +254,7 @@ public:
return variable.size() > 0 && retval; return variable.size() > 0 && retval;
}; };
Option* opt = add_option(name, fun, description, defaulted); Option *opt = add_option(name, fun, description, defaulted);
opt->set_custom_option(detail::type_name<T>(), -1, true); opt->set_custom_option(detail::type_name<T>(), -1, true);
if(defaulted) if(defaulted)
opt->set_default_val("[" + detail::join(variable) + "]"); opt->set_default_val("[" + detail::join(variable) + "]");
@ -281,15 +262,10 @@ public:
} }
/// Add option for flag /// Add option for flag
Option* add_flag( Option *add_flag(std::string name, std::string description = "") {
std::string name, CLI::callback_t fun = [](CLI::results_t) { return true; };
std::string description=""
) {
CLI::callback_t fun = [](CLI::results_t){
return true;
};
Option* opt = add_option(name, fun, description, false); Option *opt = add_option(name, fun, description, false);
if(opt->get_positional()) if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional"); throw IncorrectConstruction("Flags cannot be positional");
opt->set_custom_option("", 0); opt->set_custom_option("", 0);
@ -297,21 +273,19 @@ public:
} }
/// Add option for flag integer /// Add option for flag integer
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,
std::string name,
T &count, ///< A varaible holding the count T &count, ///< A varaible holding the count
std::string description="" std::string description = "") {
) {
count = 0; count = 0;
CLI::callback_t fun = [&count](CLI::results_t res){ CLI::callback_t fun = [&count](CLI::results_t res) {
count = static_cast<T>(res.size()); count = static_cast<T>(res.size());
return true; return true;
}; };
Option* opt = add_option(name, fun, description, false); Option *opt = add_option(name, fun, description, false);
if(opt->get_positional()) if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional"); throw IncorrectConstruction("Flags cannot be positional");
opt->set_custom_option("", 0); opt->set_custom_option("", 0);
@ -319,40 +293,34 @@ public:
} }
/// 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(std::string name,
Option* add_flag(
std::string name,
T &count, ///< A varaible holding true if passed T &count, ///< A varaible holding true if passed
std::string description="" std::string description = "") {
) {
count = false; count = false;
CLI::callback_t fun = [&count](CLI::results_t res){ CLI::callback_t fun = [&count](CLI::results_t res) {
count = true; count = true;
return res.size() == 1; return res.size() == 1;
}; };
Option* opt = add_option(name, fun, description, false); Option *opt = add_option(name, fun, description, false);
if(opt->get_positional()) if(opt->get_positional())
throw IncorrectConstruction("Flags cannot be positional"); throw IncorrectConstruction("Flags cannot be positional");
opt->set_custom_option("", 0); opt->set_custom_option("", 0);
return opt; 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,
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 description="", std::string description = "",
bool defaulted=false bool defaulted = false) {
) {
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) {
return false; return false;
} }
bool retval = detail::lexical_cast(res[0], member); bool retval = detail::lexical_cast(res[0], member);
@ -361,7 +329,7 @@ public:
return std::find(std::begin(options), std::end(options), member) != std::end(options); return std::find(std::begin(options), std::end(options), member) != std::end(options);
}; };
Option* opt = add_option(name, fun, description, defaulted); Option *opt = add_option(name, fun, description, defaulted);
std::string typeval = detail::type_name<T>(); std::string typeval = detail::type_name<T>();
typeval += " in {" + detail::join(options) + "}"; typeval += " in {" + detail::join(options) + "}";
opt->set_custom_option(typeval); opt->set_custom_option(typeval);
@ -374,21 +342,20 @@ public:
} }
/// Add set of options, string only, ignore case /// Add set of options, string only, ignore case
Option* add_set_ignore_case( Option *add_set_ignore_case(std::string name,
std::string name,
std::string &member, ///< The selected member of the set std::string &member, ///< The selected member of the set
std::set<std::string> options, ///< The set of posibilities std::set<std::string> options, ///< The set of posibilities
std::string description="", std::string description = "",
bool defaulted=false bool defaulted = false) {
) {
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) {
return false; return false;
} }
member = detail::to_lower(res[0]); member = detail::to_lower(res[0]);
auto iter = std::find_if(std::begin(options), std::end(options), auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
[&member](std::string val){return detail::to_lower(val) == member;}); return detail::to_lower(val) == member;
});
if(iter == std::end(options)) if(iter == std::end(options))
return false; return false;
else { else {
@ -397,7 +364,7 @@ public:
} }
}; };
Option* opt = add_option(name, fun, description, defaulted); Option *opt = add_option(name, fun, description, defaulted);
std::string typeval = detail::type_name<std::string>(); std::string typeval = detail::type_name<std::string>();
typeval += " in {" + detail::join(options) + "}"; typeval += " in {" + detail::join(options) + "}";
opt->set_custom_option(typeval); opt->set_custom_option(typeval);
@ -408,23 +375,23 @@ public:
} }
/// Add a complex number /// Add a complex number
template<typename T> template <typename T>
Option* add_complex( Option *add_complex(std::string name,
std::string name, T& variable, T &variable,
std::string description="", bool defaulted=false, std::string description = "",
std::string label="COMPLEX") { bool defaulted = false,
CLI::callback_t fun = [&variable](results_t res){ std::string label = "COMPLEX") {
if(res.size()!=2) CLI::callback_t fun = [&variable](results_t res) {
if(res.size() != 2)
return false; return false;
double x,y; double x, y;
bool worked = detail::lexical_cast(res[0], x) bool worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(res[1], y);
&& detail::lexical_cast(res[1], y);
if(worked) if(worked)
variable = T(x,y); variable = T(x, y);
return worked; return worked;
}; };
CLI::Option* opt = add_option(name, fun, description, defaulted); CLI::Option *opt = add_option(name, fun, description, defaulted);
opt->set_custom_option(label, 2); opt->set_custom_option(label, 2);
if(defaulted) { if(defaulted) {
std::stringstream out; std::stringstream out;
@ -434,27 +401,26 @@ public:
return opt; return opt;
} }
/// Add a configuration ini file option /// Add a configuration ini file option
Option* add_config(std::string name="--config", Option *add_config(std::string name = "--config",
std::string default_filename="", std::string default_filename = "",
std::string help="Read an ini file", std::string help = "Read an ini file",
bool required=false) { bool required = false) {
// Remove existing config if present // Remove existing config if present
if(config_ptr_ != nullptr) if(config_ptr_ != nullptr)
remove_option(config_ptr_); remove_option(config_ptr_);
config_name_ = default_filename; config_name_ = default_filename;
config_required_ = required; config_required_ = required;
config_ptr_ = add_option(name, config_name_, help, default_filename!=""); config_ptr_ = add_option(name, config_name_, help, default_filename != "");
return config_ptr_; return config_ptr_;
} }
/// Removes an option from the App. Takes an option pointer. Returns true if found and removed. /// Removes an option from the App. Takes an option pointer. Returns true if found and removed.
bool remove_option(Option* opt) { bool remove_option(Option *opt) {
auto iterator = std::find_if(std::begin(options_), std::end(options_), auto iterator =
[opt](const Option_p &v){return v.get() == opt;}); std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
if (iterator != std::end(options_)) { if(iterator != std::end(options_)) {
options_.erase(iterator); options_.erase(iterator);
return true; return true;
} }
@ -466,14 +432,14 @@ public:
///@{ ///@{
/// Add a subcommand. Like the constructor, you can override the help message addition by setting help=false /// Add a subcommand. Like the constructor, you can override the help message addition by setting help=false
App* add_subcommand(std::string name, std::string description="", bool help=true) { App *add_subcommand(std::string name, std::string description = "", bool help = true) {
subcommands_.emplace_back(new App(description, help, detail::dummy)); subcommands_.emplace_back(new App(description, help, detail::dummy));
subcommands_.back()->name_ = name; subcommands_.back()->name_ = name;
subcommands_.back()->allow_extras(); subcommands_.back()->allow_extras();
subcommands_.back()->parent_ = this; subcommands_.back()->parent_ = this;
subcommands_.back()->ignore_case_ = ignore_case_; subcommands_.back()->ignore_case_ = ignore_case_;
subcommands_.back()->fallthrough_ = fallthrough_; subcommands_.back()->fallthrough_ = fallthrough_;
for(const auto& subc : subcommands_) for(const auto &subc : subcommands_)
if(subc.get() != subcommands_.back().get()) if(subc.get() != subcommands_.back().get())
if(subc->check_name(subcommands_.back()->name_) || subcommands_.back()->check_name(subc->name_)) if(subc->check_name(subcommands_.back()->name_) || subcommands_.back()->check_name(subc->name_))
throw OptionAlreadyAdded(subc->name_); throw OptionAlreadyAdded(subc->name_);
@ -481,7 +447,7 @@ public:
} }
/// Check to see if a subcommand is part of this command (doesn't have to be in command line) /// Check to see if a subcommand is part of this command (doesn't have to be in command line)
App* get_subcommand(App* subcom) const { App *get_subcommand(App *subcom) const {
for(const App_p &subcomptr : subcommands_) for(const App_p &subcomptr : subcommands_)
if(subcomptr.get() == subcom) if(subcomptr.get() == subcom)
return subcom; return subcom;
@ -489,7 +455,7 @@ public:
} }
/// Check to see if a subcommand is part of this command (text version) /// Check to see if a subcommand is part of this command (text version)
App* get_subcommand(std::string subcom) const { App *get_subcommand(std::string subcom) const {
for(const App_p &subcomptr : subcommands_) for(const App_p &subcomptr : subcommands_)
if(subcomptr->check_name(subcom)) if(subcomptr->check_name(subcom))
return subcomptr.get(); return subcomptr.get();
@ -514,15 +480,14 @@ public:
std::vector<std::string> parse(int argc, char **argv) { std::vector<std::string> parse(int argc, char **argv) {
name_ = argv[0]; name_ = 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.emplace_back(argv[i]); args.emplace_back(argv[i]);
return parse(args); return parse(args);
} }
/// The real work is done here. Expects a reversed vector. /// The real work is done here. Expects a reversed vector.
/// Changes the vector to the remaining options. /// Changes the vector to the remaining options.
std::vector<std::string>& parse(std::vector<std::string> &args) { std::vector<std::string> &parse(std::vector<std::string> &args) {
_validate(); _validate();
_parse(args); _parse(args);
run_callback(); run_callback();
@ -530,7 +495,7 @@ public:
} }
/// Print a nice error message and return the exit code /// Print a nice error message and return the exit code
int exit(const Error& e) const { int exit(const Error &e) const {
if(e.exit_code != static_cast<int>(ExitCodes::Success)) { if(e.exit_code != static_cast<int>(ExitCodes::Success)) {
std::cerr << "ERROR: "; std::cerr << "ERROR: ";
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
@ -572,32 +537,30 @@ public:
} }
/// Get a subcommand pointer list to the currently selected subcommands (after parsing) /// Get a subcommand pointer list to the currently selected subcommands (after parsing)
std::vector<App*> get_subcommands() const { std::vector<App *> get_subcommands() const {
std::vector<App*> subcomms; std::vector<App *> subcomms;
for(const App_p &subcomptr : subcommands_) for(const App_p &subcomptr : subcommands_)
if(subcomptr->parsed_) if(subcomptr->parsed_)
subcomms.push_back(subcomptr.get()); subcomms.push_back(subcomptr.get());
return subcomms; return subcomms;
} }
/// Check to see if given subcommand was selected /// Check to see if given subcommand was selected
bool got_subcommand(App* subcom) const { bool got_subcommand(App *subcom) const {
// get subcom needed to verify that this was a real subcommand // get subcom needed to verify that this was a real subcommand
return get_subcommand(subcom)->parsed_; return get_subcommand(subcom)->parsed_;
} }
/// Check with name instead of pointer to see if subcommand was selected /// Check with name instead of pointer to see if subcommand was selected
bool got_subcommand(std::string name) const { bool got_subcommand(std::string name) const { return get_subcommand(name)->parsed_; }
return get_subcommand(name)->parsed_;
}
///@} ///@}
/// @name Help /// @name Help
///@{ ///@{
/// Produce a string that could be read in as a config of the current values of the App. Set default_also to include default arguments. Prefix will add a string to the beginning of each option. /// Produce a string that could be read in as a config of the current values of the App. Set default_also to include
std::string config_to_str(bool default_also=false, std::string prefix="") const { /// default arguments. Prefix will add a string to the beginning of each option.
std::string config_to_str(bool default_also = false, std::string prefix = "") const {
std::stringstream out; std::stringstream out;
for(const Option_p &opt : options_) { for(const Option_p &opt : options_) {
@ -627,7 +590,6 @@ public:
} else if(opt->count() == 0 && default_also && opt.get() != get_help_ptr()) { } else if(opt->count() == 0 && default_also && opt.get() != get_help_ptr()) {
out << name << "=false" << std::endl; out << name << "=false" << std::endl;
} }
} }
} }
for(const App_p &subcom : subcommands_) for(const App_p &subcom : subcommands_)
@ -636,7 +598,7 @@ public:
} }
/// Makes a help message, with a column wid for column 1 /// Makes a help message, with a column wid for column 1
std::string help(size_t wid=30, std::string prev="") const { std::string help(size_t wid = 30, std::string prev = "") const {
// Delegate to subcommand if needed // Delegate to subcommand if needed
if(prev == "") if(prev == "")
prev = name_; prev = name_;
@ -665,15 +627,15 @@ public:
out << " [OPTIONS]"; out << " [OPTIONS]";
// Positionals // Positionals
bool pos=false; bool pos = false;
for(const Option_p &opt : options_) for(const Option_p &opt : options_)
if(opt->get_positional()) { if(opt->get_positional()) {
// A hidden positional should still show up in the usage statement // A hidden positional should still show up in the usage statement
//if(detail::to_lower(opt->get_group()) == "hidden") // if(detail::to_lower(opt->get_group()) == "hidden")
// continue; // continue;
out << " " << opt->help_positional(); out << " " << opt->help_positional();
if(opt->_has_help_positional()) if(opt->_has_help_positional())
pos=true; pos = true;
} }
if(!subcommands_.empty()) { if(!subcommands_.empty()) {
@ -695,20 +657,17 @@ public:
detail::format_help(out, opt->help_pname(), opt->get_description(), wid); detail::format_help(out, opt->help_pname(), opt->get_description(), wid);
} }
out << std::endl; out << std::endl;
} }
// Options // Options
if(npos) { if(npos) {
for (const std::string& group : groups) { for(const std::string &group : groups) {
if(detail::to_lower(group) == "hidden") if(detail::to_lower(group) == "hidden")
continue; continue;
out << group << ":" << std::endl; out << group << ":" << std::endl;
for(const Option_p &opt : options_) { for(const Option_p &opt : options_) {
if(opt->nonpositional() && opt->get_group() == group) if(opt->nonpositional() && opt->get_group() == group)
detail::format_help(out, opt->help_name(), opt->get_description(), wid); detail::format_help(out, opt->help_name(), opt->get_description(), wid);
} }
out << std::endl; out << std::endl;
} }
@ -728,28 +687,18 @@ public:
///@{ ///@{
/// Get a pointer to the help flag. /// Get a pointer to the help flag.
Option* get_help_ptr() { Option *get_help_ptr() { return help_ptr_; }
return help_ptr_;
}
/// Get a pointer to the help flag. (const) /// Get a pointer to the help flag. (const)
const Option* get_help_ptr() const { const Option *get_help_ptr() const { return help_ptr_; }
return help_ptr_;
}
/// Get a pointer to the config option. /// Get a pointer to the config option.
Option* get_config_ptr() { Option *get_config_ptr() { return config_ptr_; }
return config_ptr_;
}
/// Get a pointer to the config option. (const) /// Get a pointer to the config option. (const)
const Option* get_config_ptr() const { const Option *get_config_ptr() const { return config_ptr_; }
return config_ptr_;
}
/// Get the name of the current app /// Get the name of the current app
std::string get_name() const { std::string get_name() const { return name_; }
return name_;
}
/// Check the name, case insensitive if set /// Check the name, case insensitive if set
bool check_name(std::string name_to_check) const { bool check_name(std::string name_to_check) const {
@ -764,24 +713,22 @@ public:
///@} ///@}
protected:
protected:
/// Check the options to make sure there are no conficts. /// Check the options to make sure there are no conficts.
/// ///
/// Currenly checks to see if mutiple positionals exist with -1 args /// Currenly checks to see if mutiple positionals exist with -1 args
void _validate() const { void _validate() const {
auto count = std::count_if(std::begin(options_), std::end(options_), auto count = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
[](const Option_p& opt){return opt->get_expected() == -1 && opt->get_positional();}); return opt->get_expected() == -1 && opt->get_positional();
});
if(count > 1) if(count > 1)
throw InvalidError(name_ + ": Too many positional arguments with unlimited expected args"); throw InvalidError(name_ + ": Too many positional arguments with unlimited expected args");
for(const App_p& app : subcommands_) for(const App_p &app : subcommands_)
app->_validate(); app->_validate();
} }
/// Return missing from the master /// Return missing from the master
missing_t* missing() { missing_t *missing() {
if(parent_ != nullptr) if(parent_ != nullptr)
return parent_->missing(); return parent_->missing();
return &missing_; return &missing_;
@ -792,7 +739,7 @@ protected:
pre_callback(); pre_callback();
if(callback_) if(callback_)
callback_(); callback_();
for(App* subc : get_subcommands()) { for(App *subc : get_subcommands()) {
subc->run_callback(); subc->run_callback();
} }
} }
@ -807,7 +754,6 @@ protected:
return false; return false;
} }
/// Selects a Classifer enum based on the type of the current argument /// Selects a Classifer enum based on the type of the current argument
detail::Classifer _recognize(const std::string &current) const { detail::Classifer _recognize(const std::string &current) const {
std::string dummy1, dummy2; std::string dummy1, dummy2;
@ -823,7 +769,6 @@ protected:
return detail::Classifer::NONE; return detail::Classifer::NONE;
} }
/// Internal parse function /// Internal parse function
void _parse(std::vector<std::string> &args) { void _parse(std::vector<std::string> &args) {
parsed_ = true; parsed_ = true;
@ -833,13 +778,12 @@ protected:
_parse_single(args, positional_only); _parse_single(args, positional_only);
} }
if (help_ptr_ != nullptr && help_ptr_->count() > 0) { if(help_ptr_ != nullptr && help_ptr_->count() > 0) {
throw CallForHelp(); throw CallForHelp();
} }
// Process an INI file // Process an INI file
if (config_ptr_ != nullptr && config_name_ != "") { if(config_ptr_ != nullptr && config_name_ != "") {
try { try {
std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_); std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_);
while(!values.empty()) { while(!values.empty()) {
@ -847,16 +791,15 @@ protected:
throw ExtrasINIError(values.back().fullname); throw ExtrasINIError(values.back().fullname);
} }
} }
} catch (const FileError &) { } catch(const FileError &) {
if(config_required_) if(config_required_)
throw; throw;
} }
} }
// Get envname options if not yet passed // Get envname options if not yet passed
for(const Option_p& opt : options_) { for(const Option_p &opt : options_) {
if (opt->count() == 0 && opt->envname_ != "") { if(opt->count() == 0 && opt->envname_ != "") {
char *ename = std::getenv(opt->envname_.c_str()); char *ename = std::getenv(opt->envname_.c_str());
if(ename != nullptr) { if(ename != nullptr) {
opt->add_result(std::string(ename)); opt->add_result(std::string(ename));
@ -865,45 +808,48 @@ protected:
} }
// Process callbacks // Process callbacks
for(const Option_p& opt : options_) { for(const Option_p &opt : options_) {
if (opt->count() > 0 && !opt->get_callback_run()) { if(opt->count() > 0 && !opt->get_callback_run()) {
opt->run_callback(); opt->run_callback();
} }
} }
// Verify required options // Verify required options
for(const Option_p& opt : options_) { for(const Option_p &opt : options_) {
// Required // Required
if (opt->get_required() if(opt->get_required() && (static_cast<int>(opt->count()) < opt->get_expected() || opt->count() == 0))
&& (static_cast<int>( opt->count()) < opt->get_expected() || opt->count() == 0))
throw RequiredError(opt->get_name()); throw RequiredError(opt->get_name());
// Requires // Requires
for (const Option* opt_req : opt->requires_) for(const Option *opt_req : opt->requires_)
if (opt->count() > 0 && opt_req->count() == 0) if(opt->count() > 0 && opt_req->count() == 0)
throw RequiresError(opt->get_name(), opt_req->get_name()); throw RequiresError(opt->get_name(), opt_req->get_name());
// Excludes // Excludes
for (const Option* opt_ex : opt->excludes_) for(const Option *opt_ex : opt->excludes_)
if (opt->count() > 0 && opt_ex->count() != 0) if(opt->count() > 0 && opt_ex->count() != 0)
throw ExcludesError(opt->get_name(), opt_ex->get_name()); throw ExcludesError(opt->get_name(), opt_ex->get_name());
} }
auto selected_subcommands =get_subcommands(); auto selected_subcommands = get_subcommands();
if(require_subcommand_ < 0 && selected_subcommands.empty()) if(require_subcommand_ < 0 && selected_subcommands.empty())
throw RequiredError("Subcommand required"); throw RequiredError("Subcommand required");
else if(require_subcommand_ > 0 && static_cast<int>( selected_subcommands.size()) != require_subcommand_) else if(require_subcommand_ > 0 && static_cast<int>(selected_subcommands.size()) != require_subcommand_)
throw RequiredError(std::to_string(require_subcommand_) + " subcommand(s) required"); throw RequiredError(std::to_string(require_subcommand_) + " subcommand(s) required");
// Convert missing (pairs) to extras (string only) // Convert missing (pairs) to extras (string only)
if(parent_ == nullptr) { if(parent_ == nullptr) {
args.resize(missing()->size()); args.resize(missing()->size());
std::transform(std::begin(*missing()), std::end(*missing()), std::begin(args), std::transform(std::begin(*missing()),
[](const std::pair<detail::Classifer, std::string>& val){return val.second;}); std::end(*missing()),
std::begin(args),
[](const std::pair<detail::Classifer, std::string> &val) { return val.second; });
std::reverse(std::begin(args), std::end(args)); std::reverse(std::begin(args), std::end(args));
size_t num_left_over = std::count_if(std::begin(*missing()), std::end(*missing()), size_t num_left_over = std::count_if(
[](std::pair<detail::Classifer, std::string>& val){return val.first != detail::Classifer::POSITIONAL_MARK;}); std::begin(*missing()), std::end(*missing()), [](std::pair<detail::Classifer, std::string> &val) {
return val.first != detail::Classifer::POSITIONAL_MARK;
});
if(num_left_over>0 && !allow_extras_) if(num_left_over > 0 && !allow_extras_)
throw ExtrasError("[" + detail::rjoin(args, " ") + "]"); throw ExtrasError("[" + detail::rjoin(args, " ") + "]");
} }
} }
@ -913,7 +859,7 @@ protected:
/// If this has more than one dot.separated.name, go into the subcommand matching it /// If this has more than one dot.separated.name, go into the subcommand matching it
/// Returns true if it managed to find the option, if false you'll need to remove the arg manully. /// Returns true if it managed to find the option, if false you'll need to remove the arg manully.
bool _parse_ini(std::vector<detail::ini_ret_t> &args) { bool _parse_ini(std::vector<detail::ini_ret_t> &args) {
detail::ini_ret_t& current = args.back(); detail::ini_ret_t &current = args.back();
std::string parent = current.parent(); // respects curent.level std::string parent = current.parent(); // respects curent.level
std::string name = current.name(); std::string name = current.name();
@ -926,15 +872,14 @@ protected:
return false; return false;
} }
auto op_ptr = std::find_if(std::begin(options_), std::end(options_), auto op_ptr = std::find_if(
[name](const Option_p &v){return v->check_lname(name);}); std::begin(options_), std::end(options_), [name](const Option_p &v) { return v->check_lname(name); });
if(op_ptr == std::end(options_)) if(op_ptr == std::end(options_))
return false; return false;
// Let's not go crazy with pointer syntax // Let's not go crazy with pointer syntax
Option_p& op = *op_ptr; Option_p &op = *op_ptr;
if(op->results_.empty()) { if(op->results_.empty()) {
// Flag parsing // Flag parsing
@ -949,9 +894,9 @@ protected:
else else
try { try {
size_t ui = std::stoul(val); size_t ui = std::stoul(val);
for (size_t i=0; i<ui; i++) for(size_t i = 0; i < ui; i++)
op->results_.emplace_back(""); op->results_.emplace_back("");
} catch (const std::invalid_argument &) { } catch(const std::invalid_argument &) {
throw ConversionError(current.fullname + ": Should be true/false or a number"); throw ConversionError(current.fullname + ": Should be true/false or a number");
} }
} else } else
@ -966,7 +911,8 @@ protected:
return true; return true;
} }
/// Parse "one" argument (some may eat more than one), delegate to parent if fails, add to missing if missing from master /// Parse "one" argument (some may eat more than one), delegate to parent if fails, add to missing if missing from
/// master
void _parse_single(std::vector<std::string> &args, bool &positional_only) { void _parse_single(std::vector<std::string> &args, bool &positional_only) {
detail::Classifer classifer = positional_only ? detail::Classifer::NONE : _recognize(args.back()); detail::Classifer classifer = positional_only ? detail::Classifer::NONE : _recognize(args.back());
@ -991,20 +937,16 @@ protected:
// Probably a positional or something for a parent (sub)command // Probably a positional or something for a parent (sub)command
_parse_positional(args); _parse_positional(args);
} }
} }
/// Parse a positional, go up the tree to check /// Parse a positional, go up the tree to check
void _parse_positional(std::vector<std::string> &args) { void _parse_positional(std::vector<std::string> &args) {
std::string positional = args.back(); std::string positional = args.back();
for(const Option_p& opt : options_) { for(const Option_p &opt : options_) {
// Eat options, one by one, until done // Eat options, one by one, until done
if ( opt->get_positional() if(opt->get_positional() &&
&& (static_cast<int>( opt->count()) < opt->get_expected() (static_cast<int>(opt->count()) < opt->get_expected() || opt->get_expected() < 0)) {
|| opt->get_expected() < 0)
) {
opt->add_result(positional); opt->add_result(positional);
args.pop_back(); args.pop_back();
@ -1025,7 +967,7 @@ protected:
/// Unlike the others, this one will always allow fallthrough /// Unlike the others, this one will always allow fallthrough
void _parse_subcommand(std::vector<std::string> &args) { void _parse_subcommand(std::vector<std::string> &args) {
for(const App_p &com : subcommands_) { for(const App_p &com : subcommands_) {
if(com->check_name(args.back())){ if(com->check_name(args.back())) {
args.pop_back(); args.pop_back();
com->_parse(args); com->_parse(args);
return; return;
@ -1046,8 +988,8 @@ protected:
if(!detail::split_short(current, name, rest)) if(!detail::split_short(current, name, rest))
throw HorribleError("Short"); throw HorribleError("Short");
auto op_ptr = std::find_if(std::begin(options_), std::end(options_), [name](const Option_p &opt){return opt->check_sname(name);}); auto op_ptr = std::find_if(
std::begin(options_), std::end(options_), [name](const Option_p &opt) { return opt->check_sname(name); });
// Option not found // Option not found
if(op_ptr == std::end(options_)) { if(op_ptr == std::end(options_)) {
@ -1064,28 +1006,27 @@ protected:
args.pop_back(); args.pop_back();
// Get a reference to the pointer to make syntax bearable // Get a reference to the pointer to make syntax bearable
Option_p& op = *op_ptr; Option_p &op = *op_ptr;
int num = op->get_expected(); int num = op->get_expected();
if(num == 0) if(num == 0)
op->add_result(""); op->add_result("");
else if(rest!="") { else if(rest != "") {
if (num > 0) if(num > 0)
num--; num--;
op->add_result(rest); op->add_result(rest);
rest = ""; rest = "";
} }
if(num == -1) { if(num == -1) {
while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) {
op->add_result(args.back()); op->add_result(args.back());
args.pop_back(); args.pop_back();
} }
} else while(num>0 && !args.empty()) { } else
while(num > 0 && !args.empty()) {
num--; num--;
std::string current_ = args.back(); std::string current_ = args.back();
args.pop_back(); args.pop_back();
@ -1107,7 +1048,8 @@ protected:
if(!detail::split_long(current, name, value)) if(!detail::split_long(current, name, value))
throw HorribleError("Long:" + args.back()); throw HorribleError("Long:" + args.back());
auto op_ptr = std::find_if(std::begin(options_), std::end(options_), [name](const Option_p &v){return v->check_lname(name);}); auto op_ptr = std::find_if(
std::begin(options_), std::end(options_), [name](const Option_p &v) { return v->check_lname(name); });
// Option not found // Option not found
if(op_ptr == std::end(options_)) { if(op_ptr == std::end(options_)) {
@ -1125,14 +1067,15 @@ protected:
args.pop_back(); args.pop_back();
// Get a reference to the pointer to make syntax bearable // Get a reference to the pointer to make syntax bearable
Option_p& op = *op_ptr; Option_p &op = *op_ptr;
int num = op->get_expected(); int num = op->get_expected();
if(value != "") { if(value != "") {
if(num!=-1) num--; if(num != -1)
num--;
op->add_result(value); op->add_result(value);
} else if (num == 0) { } else if(num == 0) {
op->add_result(""); op->add_result("");
} }
@ -1141,14 +1084,14 @@ protected:
op->add_result(args.back()); op->add_result(args.back());
args.pop_back(); args.pop_back();
} }
} else while(num>0 && !args.empty()) { } else
while(num > 0 && !args.empty()) {
num--; num--;
op->add_result(args.back()); op->add_result(args.back());
args.pop_back(); args.pop_back();
} }
return; return;
} }
}; };
namespace detail { namespace detail {
@ -1156,29 +1099,26 @@ namespace detail {
struct AppFriend { struct AppFriend {
/// Wrap _parse_short, perfectly forward arguments and return /// Wrap _parse_short, perfectly forward arguments and return
template<typename ...Args> template <typename... Args>
static auto parse_short(App* app, Args && ...args) static auto parse_short(App *app, Args &&... args) ->
-> typename std::result_of<decltype(&App::_parse_short)(App, Args...)>::type { typename std::result_of<decltype (&App::_parse_short)(App, Args...)>::type {
return app->_parse_short(std::forward<Args>(args)...); return app->_parse_short(std::forward<Args>(args)...);
} }
/// Wrap _parse_long, perfectly forward arguments and return /// Wrap _parse_long, perfectly forward arguments and return
template<typename ...Args> template <typename... Args>
static auto parse_long(App* app, Args && ...args) static auto parse_long(App *app, Args &&... args) ->
-> typename std::result_of<decltype(&App::_parse_long)(App, Args...)>::type { typename std::result_of<decltype (&App::_parse_long)(App, Args...)>::type {
return app->_parse_long(std::forward<Args>(args)...); return app->_parse_long(std::forward<Args>(args)...);
} }
/// Wrap _parse_subcommand, perfectly forward arguments and return /// Wrap _parse_subcommand, perfectly forward arguments and return
template<typename ...Args> template <typename... Args>
static auto parse_subcommand(App* app, Args && ...args) static auto parse_subcommand(App *app, Args &&... args) ->
-> typename std::result_of<decltype(&App::_parse_subcommand)(App, Args...)>::type { typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type {
return app->_parse_subcommand(std::forward<Args>(args)...); return app->_parse_subcommand(std::forward<Args>(args)...);
} }
}; };
} // namespace detail } // namespace detail
} // namespace CLI } // namespace CLI

View File

@ -12,4 +12,3 @@
#include "CLI/StringTools.hpp" #include "CLI/StringTools.hpp"
#include "CLI/TypeTools.hpp" #include "CLI/TypeTools.hpp"
#include "CLI/Validators.hpp" #include "CLI/Validators.hpp"

View File

@ -9,10 +9,11 @@
namespace CLI { namespace CLI {
/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, int values from e.get_error_code(). /// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut,
/// int values from e.get_error_code().
enum class ExitCodes { enum class ExitCodes {
Success = 0, Success = 0,
IncorrectConstruction=100, IncorrectConstruction = 100,
BadNameString, BadNameString,
OptionAlreadyAdded, OptionAlreadyAdded,
File, File,
@ -41,17 +42,23 @@ enum class ExitCodes {
struct Error : public std::runtime_error { struct Error : public std::runtime_error {
int exit_code; int exit_code;
bool print_help; bool print_help;
int get_exit_code() const {return exit_code;} int get_exit_code() const { return exit_code; }
Error(std::string parent, std::string name, ExitCodes exit_code=ExitCodes::BaseClass, bool print_help=true) Error(std::string parent, std::string name, ExitCodes exit_code = ExitCodes::BaseClass, bool print_help = true)
: runtime_error(parent + ": " + name), exit_code(static_cast<int>(exit_code)), print_help(print_help) {} : runtime_error(parent + ": " + name), exit_code(static_cast<int>(exit_code)), print_help(print_help) {}
Error(std::string parent, std::string name, int exit_code=static_cast<int>(ExitCodes::BaseClass), bool print_help=true) Error(std::string parent,
std::string name,
int exit_code = static_cast<int>(ExitCodes::BaseClass),
bool print_help = true)
: runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {} : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {}
}; };
/// Construction errors (not in parsing) /// Construction errors (not in parsing)
struct ConstructionError : public Error { struct ConstructionError : public Error {
// Using Error::Error constructors seem to not work on GCC 4.7 // Using Error::Error constructors seem to not work on GCC 4.7
ConstructionError(std::string parent, std::string name, ExitCodes exit_code=ExitCodes::BaseClass, bool print_help=true) ConstructionError(std::string parent,
std::string name,
ExitCodes exit_code = ExitCodes::BaseClass,
bool print_help = true)
: Error(parent, name, exit_code, print_help) {} : Error(parent, name, exit_code, print_help) {}
}; };
@ -63,8 +70,7 @@ struct IncorrectConstruction : public ConstructionError {
/// Thrown on construction of a bad name /// Thrown on construction of a bad name
struct BadNameString : public ConstructionError { struct BadNameString : public ConstructionError {
BadNameString(std::string name) BadNameString(std::string name) : ConstructionError("BadNameString", name, ExitCodes::BadNameString) {}
: ConstructionError("BadNameString", name, ExitCodes::BadNameString) {}
}; };
/// Thrown when an option already exists /// Thrown when an option already exists
@ -77,7 +83,7 @@ struct OptionAlreadyAdded : public ConstructionError {
/// Anything that can error in Parse /// Anything that can error in Parse
struct ParseError : public Error { struct ParseError : public Error {
ParseError(std::string parent, std::string name, ExitCodes exit_code=ExitCodes::BaseClass, bool print_help=true) ParseError(std::string parent, std::string name, ExitCodes exit_code = ExitCodes::BaseClass, bool print_help = true)
: Error(parent, name, exit_code, print_help) {} : Error(parent, name, exit_code, print_help) {}
}; };
@ -85,8 +91,7 @@ struct ParseError : public Error {
/// This is a successful completion on parsing, supposed to exit /// This is a successful completion on parsing, supposed to exit
struct Success : public ParseError { struct Success : public ParseError {
Success() Success() : ParseError("Success", "Successfully completed, should be caught and quit", ExitCodes::Success, false) {}
: ParseError("Success", "Successfully completed, should be caught and quit", ExitCodes::Success, false) {}
}; };
/// -h or --help on command line /// -h or --help on command line
@ -95,29 +100,24 @@ struct CallForHelp : public ParseError {
: ParseError("CallForHelp", "This should be caught in your main function, see examples", ExitCodes::Success) {} : ParseError("CallForHelp", "This should be caught in your main function, see examples", ExitCodes::Success) {}
}; };
/// Thrown when parsing an INI file and it is missing /// Thrown when parsing an INI file and it is missing
struct FileError : public ParseError { struct FileError : public ParseError {
FileError (std::string name) FileError(std::string name) : ParseError("FileError", name, ExitCodes::File) {}
: ParseError("FileError", name, ExitCodes::File) {}
}; };
/// Thrown when conversion call back fails, such as when an int fails to coerse to a string /// Thrown when conversion call back fails, such as when an int fails to coerse to a string
struct ConversionError : public ParseError { struct ConversionError : public ParseError {
ConversionError(std::string name) ConversionError(std::string name) : ParseError("ConversionError", name, ExitCodes::Conversion) {}
: ParseError("ConversionError", name, ExitCodes::Conversion) {}
}; };
/// Thrown when validation of results fails /// Thrown when validation of results fails
struct ValidationError : public ParseError { struct ValidationError : public ParseError {
ValidationError(std::string name) ValidationError(std::string name) : ParseError("ValidationError", name, ExitCodes::Validation) {}
: ParseError("ValidationError", name, ExitCodes::Validation) {}
}; };
/// Thrown when a required option is missing /// Thrown when a required option is missing
struct RequiredError : public ParseError { struct RequiredError : public ParseError {
RequiredError(std::string name) RequiredError(std::string name) : ParseError("RequiredError", name, ExitCodes::Required) {}
: ParseError("RequiredError", name, ExitCodes::Required) {}
}; };
/// Thrown when a requires option is missing /// Thrown when a requires option is missing
@ -134,20 +134,17 @@ struct ExcludesError : public ParseError {
/// Thrown when too many positionals or options are found /// Thrown when too many positionals or options are found
struct ExtrasError : public ParseError { struct ExtrasError : public ParseError {
ExtrasError(std::string name) ExtrasError(std::string name) : ParseError("ExtrasError", name, ExitCodes::Extras) {}
: ParseError("ExtrasError", name, ExitCodes::Extras) {}
}; };
/// Thrown when extra values are found in an INI file /// Thrown when extra values are found in an INI file
struct ExtrasINIError : public ParseError { struct ExtrasINIError : public ParseError {
ExtrasINIError(std::string name) ExtrasINIError(std::string name) : ParseError("ExtrasINIError", name, ExitCodes::ExtrasINI) {}
: ParseError("ExtrasINIError", name, ExitCodes::ExtrasINI) {}
}; };
/// Thrown when validation fails before parsing /// Thrown when validation fails before parsing
struct InvalidError : public ParseError { struct InvalidError : public ParseError {
InvalidError(std::string name) InvalidError(std::string name) : ParseError("InvalidError", name, ExitCodes::Invalid) {}
: ParseError("InvalidError", name, ExitCodes::Invalid) {}
}; };
/// This is just a safety check to verify selection and parsing match /// This is just a safety check to verify selection and parsing match
@ -160,8 +157,7 @@ struct HorribleError : public ParseError {
/// Thrown when counting a non-existent option /// Thrown when counting a non-existent option
struct OptionNotFound : public Error { struct OptionNotFound : public Error {
OptionNotFound(std::string name) OptionNotFound(std::string name) : Error("OptionNotFound", name, ExitCodes::OptionNotFound) {}
: Error("OptionNotFound", name, ExitCodes::OptionNotFound) {}
}; };
/// @} /// @}

View File

@ -10,18 +10,17 @@
#include "CLI/StringTools.hpp" #include "CLI/StringTools.hpp"
namespace CLI { namespace CLI {
namespace detail { namespace detail {
inline std::string inijoin(std::vector<std::string> args) { inline std::string inijoin(std::vector<std::string> args) {
std::ostringstream s; std::ostringstream s;
size_t start = 0; size_t start = 0;
for (const auto& arg : args) { for(const auto &arg : args) {
if(start++ > 0) if(start++ > 0)
s << " "; s << " ";
auto it = std::find_if(arg.begin(), arg.end(), [](char ch){ return std::isspace<char>(ch , std::locale());}); auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });
if(it == arg.end()) if(it == arg.end())
s << arg; s << arg;
else if(arg.find(R"(")") == std::string::npos) else if(arg.find(R"(")") == std::string::npos)
@ -47,18 +46,18 @@ struct ini_ret_t {
/// ///
/// Level 0, a.b.c would return a /// Level 0, a.b.c would return a
/// Level 1, a.b.c could return b /// Level 1, a.b.c could return b
std::string parent () const { std::string parent() const {
std::vector<std::string> plist = detail::split(fullname, '.'); std::vector<std::string> plist = detail::split(fullname, '.');
if(plist.size() > (level+1)) if(plist.size() > (level + 1))
return plist[level]; return plist[level];
else else
return ""; return "";
} }
/// Return name /// Return name
std::string name () const { std::string name() const {
std::vector<std::string> plist = detail::split(fullname, '.'); std::vector<std::string> plist = detail::split(fullname, '.');
return plist.at(plist.size()-1); return plist.at(plist.size() - 1);
} }
}; };
@ -74,17 +73,17 @@ inline std::vector<ini_ret_t> parse_ini(std::istream &input) {
detail::trim(line); detail::trim(line);
size_t len = line.length(); size_t len = line.length();
if(len > 1 && line[0] == '[' && line[len-1] == ']') { if(len > 1 && line[0] == '[' && line[len - 1] == ']') {
section = line.substr(1,len-2); section = line.substr(1, len - 2);
} else if (len > 0 && line[0] != ';') { } else if(len > 0 && line[0] != ';') {
output.emplace_back(); output.emplace_back();
ini_ret_t& out = output.back(); ini_ret_t &out = output.back();
// Find = in string, split and recombine // Find = in string, split and recombine
auto pos = line.find("="); auto pos = line.find("=");
if(pos != std::string::npos) { if(pos != std::string::npos) {
name = detail::trim_copy(line.substr(0,pos)); name = detail::trim_copy(line.substr(0, pos));
std::string item = detail::trim_copy(line.substr(pos+1)); std::string item = detail::trim_copy(line.substr(pos + 1));
items = detail::split_up(item); items = detail::split_up(item);
} else { } else {
name = detail::trim_copy(line); name = detail::trim_copy(line);
@ -112,6 +111,5 @@ inline std::vector<ini_ret_t> parse_ini(const std::string &name) {
return parse_ini(input); return parse_ini(input);
} }
} // namespace detail } // namespace detail
} // namespace CLI } // namespace CLI

View File

@ -19,17 +19,17 @@
namespace CLI { namespace CLI {
using results_t = std::vector<std::string>; using results_t = std::vector<std::string>;
using callback_t = std::function<bool (results_t)>; using callback_t = std::function<bool(results_t)>;
class Option; class Option;
class App; class App;
using Option_p = std::unique_ptr<Option>; using Option_p = std::unique_ptr<Option>;
class Option { class Option {
friend App; friend App;
protected:
protected:
/// @name Names /// @name Names
///@{ ///@{
@ -59,47 +59,46 @@ protected:
std::string typeval_; std::string typeval_;
/// The group membership /// The group membership
std::string group_ {"Options"}; std::string group_{"Options"};
/// True if this option has a default /// True if this option has a default
bool default_ {false}; bool default_{false};
///@} ///@}
/// @name Configuration /// @name Configuration
///@{ ///@{
/// True if this is a required option /// True if this is a required option
bool required_ {false}; bool required_{false};
/// The number of expected values, 0 for flag, -1 for unlimited vector /// The number of expected values, 0 for flag, -1 for unlimited vector
int expected_ {1}; int expected_{1};
/// A private setting to allow args to not be able to accept incorrect expected values /// A private setting to allow args to not be able to accept incorrect expected values
bool changeable_ {false}; bool changeable_{false};
/// Ignore the case when matching (option, not value) /// Ignore the case when matching (option, not value)
bool ignore_case_ {false}; bool ignore_case_{false};
/// A list of validators to run on each value parsed /// A list of validators to run on each value parsed
std::vector<std::function<bool(std::string)>> validators_; std::vector<std::function<bool(std::string)>> validators_;
/// A list of options that are required with this option /// A list of options that are required with this option
std::set<Option*> requires_; std::set<Option *> requires_;
/// A list of options that are excluded with this option /// A list of options that are excluded with this option
std::set<Option*> excludes_; std::set<Option *> excludes_;
///@} ///@}
/// @name Other /// @name Other
///@{ ///@{
/// Remember the parent app /// Remember the parent app
App* parent_; App *parent_;
/// Options store a callback to do all the work /// Options store a callback to do all the work
callback_t callback_; callback_t callback_;
///@} ///@}
/// @name Parsing results /// @name Parsing results
///@{ ///@{
@ -108,54 +107,48 @@ protected:
results_t results_; results_t results_;
/// Whether the callback has run (needed for INI parsing) /// Whether the callback has run (needed for INI parsing)
bool callback_run_ {false}; bool callback_run_{false};
///@} ///@}
/// Making an option by hand is not defined, it must be made by the App class /// Making an option by hand is not defined, it must be made by the App class
Option(std::string name, std::string description = "", std::function<bool(results_t)> callback=[](results_t){return true;}, bool default_=true, App* parent = nullptr) : Option(std::string name,
description_(std::move(description)), default_(default_), parent_(parent), callback_(std::move(callback)) { std::string description = "",
std::function<bool(results_t)> callback = [](results_t) { return true; },
bool default_ = true,
App *parent = nullptr)
: description_(std::move(description)), default_(default_), parent_(parent), callback_(std::move(callback)) {
std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(name)); std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(name));
} }
public: public:
/// @name Basic /// @name Basic
///@{ ///@{
/// Count the total number of times an option was passed /// Count the total number of times an option was passed
size_t count() const { size_t count() const { return results_.size(); }
return results_.size();
}
/// This class is true if option is passed. /// This class is true if option is passed.
operator bool() const { operator bool() const { return count() > 0; }
return count() > 0;
}
/// Clear the parsed results (mostly for testing) /// Clear the parsed results (mostly for testing)
void clear() { void clear() { results_.clear(); }
results_.clear();
}
///@} ///@}
/// @name Setting options /// @name Setting options
///@{ ///@{
/// Set the option as required /// Set the option as required
Option* required(bool value = true) { Option *required(bool value = true) {
required_ = value; required_ = value;
return this; return this;
} }
/// Support Plubmum term /// Support Plubmum term
Option* mandatory(bool value = true) { Option *mandatory(bool value = true) { return required(value); }
return required(value);
}
/// Set the number of expected arguments (Flags bypass this) /// Set the number of expected arguments (Flags bypass this)
Option* expected(int value) { Option *expected(int value) {
if(value == 0) if(value == 0)
throw IncorrectConstruction("Cannot set 0 expected, use a flag instead"); throw IncorrectConstruction("Cannot set 0 expected, use a flag instead");
else if(expected_ == 0) else if(expected_ == 0)
@ -167,21 +160,20 @@ public:
} }
/// Adds a validator /// Adds a validator
Option* check(std::function<bool(std::string)> validator) { Option *check(std::function<bool(std::string)> validator) {
validators_.push_back(validator); validators_.push_back(validator);
return this; return this;
} }
/// Changes the group membership /// Changes the group membership
Option* group(std::string name) { Option *group(std::string name) {
group_ = name; group_ = name;
return this; return this;
} }
/// Sets required options /// Sets required options
Option* requires(Option* opt) { Option *requires(Option *opt) {
auto tup = requires_.insert(opt); auto tup = requires_.insert(opt);
if(!tup.second) if(!tup.second)
throw OptionAlreadyAdded(get_name() + " requires " + opt->get_name()); throw OptionAlreadyAdded(get_name() + " requires " + opt->get_name());
@ -189,24 +181,21 @@ public:
} }
/// Can find a string if needed /// Can find a string if needed
template<typename T=App> template <typename T = App> Option *requires(std::string opt_name) {
Option* requires(std::string opt_name) { for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
for(const Option_p& opt : dynamic_cast<T*>(parent_)->options_)
if(opt.get() != this && opt->check_name(opt_name)) if(opt.get() != this && opt->check_name(opt_name))
return requires(opt.get()); return requires(opt.get());
throw IncorrectConstruction("Option " + opt_name + " is not defined"); throw IncorrectConstruction("Option " + opt_name + " is not defined");
} }
/// Any number supported, any mix of string and Opt /// Any number supported, any mix of string and Opt
template<typename A, typename B, typename... ARG> template <typename A, typename B, typename... ARG> Option *requires(A opt, B opt1, ARG... args) {
Option* requires(A opt, B opt1, ARG... args) {
requires(opt); requires(opt);
return requires(opt1, args...); return requires(opt1, args...);
} }
/// Sets excluded options /// Sets excluded options
Option* excludes(Option* opt) { Option *excludes(Option *opt) {
auto tup = excludes_.insert(opt); auto tup = excludes_.insert(opt);
if(!tup.second) if(!tup.second)
throw OptionAlreadyAdded(get_name() + " excludes " + opt->get_name()); throw OptionAlreadyAdded(get_name() + " excludes " + opt->get_name());
@ -214,23 +203,20 @@ public:
} }
/// Can find a string if needed /// Can find a string if needed
template<typename T=App> template <typename T = App> Option *excludes(std::string opt_name) {
Option* excludes(std::string opt_name) { for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
for(const Option_p& opt : dynamic_cast<T*>(parent_)->options_)
if(opt.get() != this && opt->check_name(opt_name)) if(opt.get() != this && opt->check_name(opt_name))
return excludes(opt.get()); return excludes(opt.get());
throw IncorrectConstruction("Option " + opt_name + " is not defined"); throw IncorrectConstruction("Option " + opt_name + " is not defined");
} }
/// Any number supported, any mix of string and Opt /// Any number supported, any mix of string and Opt
template<typename A, typename B, typename... ARG> template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {
Option* excludes(A opt, B opt1, ARG... args) {
excludes(opt); excludes(opt);
return excludes(opt1, args...); return excludes(opt1, args...);
} }
/// Sets environment variable to read if no option given /// Sets environment variable to read if no option given
Option* envname(std::string name) { Option *envname(std::string name) {
envname_ = name; envname_ = name;
return this; return this;
} }
@ -239,10 +225,9 @@ public:
/// ///
/// The template hides the fact that we don't have the definition of App yet. /// The template hides the fact that we don't have the definition of App yet.
/// You are never expected to add an argument to the template here. /// You are never expected to add an argument to the template here.
template<typename T=App> template <typename T = App> Option *ignore_case(bool value = true) {
Option* ignore_case(bool value = true) {
ignore_case_ = value; ignore_case_ = value;
for(const Option_p& opt : dynamic_cast<T*>(parent_)->options_) for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
if(opt.get() != this && *opt == *this) if(opt.get() != this && *opt == *this)
throw OptionAlreadyAdded(opt->get_name()); throw OptionAlreadyAdded(opt->get_name());
return this; return this;
@ -253,74 +238,56 @@ public:
///@{ ///@{
/// True if this is a required option /// True if this is a required option
bool get_required() const { bool get_required() const { return required_; }
return required_;
}
/// The number of arguments the option expects /// The number of arguments the option expects
int get_expected() const { int get_expected() const { return expected_; }
return expected_;
}
/// True if this has a default value /// True if this has a default value
int get_default() const { int get_default() const { return default_; }
return default_;
}
/// True if the argument can be given directly /// True if the argument can be given directly
bool get_positional() const { bool get_positional() const { return pname_.length() > 0; }
return pname_.length() > 0;
}
/// True if option has at least one non-positional name /// True if option has at least one non-positional name
bool nonpositional() const { bool nonpositional() const { return (snames_.size() + lnames_.size()) > 0; }
return (snames_.size() + lnames_.size()) > 0;
}
/// True if option has description /// True if option has description
bool has_description() const { bool has_description() const { return description_.length() > 0; }
return description_.length() > 0;
}
/// Get the group of this option /// Get the group of this option
const std::string& get_group() const { const std::string &get_group() const { return group_; }
return group_;
}
/// Get the description /// Get the description
const std::string& get_description() const { const std::string &get_description() const { return description_; }
return description_;
}
// Just the pname // Just the pname
std::string get_pname() const { std::string get_pname() const { return pname_; }
return pname_;
}
///@} ///@}
/// @name Help tools /// @name Help tools
///@{ ///@{
/// Gets a , sep list of names. Does not include the positional name if opt_only=true. /// Gets a , sep list of names. Does not include the positional name if opt_only=true.
std::string get_name(bool opt_only=false) const { std::string get_name(bool opt_only = false) const {
std::vector<std::string> name_list; std::vector<std::string> name_list;
if(!opt_only && pname_.length() > 0) if(!opt_only && pname_.length() > 0)
name_list.push_back(pname_); name_list.push_back(pname_);
for(const std::string& sname : snames_) for(const std::string &sname : snames_)
name_list.push_back("-"+sname); name_list.push_back("-" + sname);
for(const std::string& lname : lnames_) for(const std::string &lname : lnames_)
name_list.push_back("--"+lname); name_list.push_back("--" + lname);
return detail::join(name_list); return detail::join(name_list);
} }
/// The name and any extras needed for positionals /// The name and any extras needed for positionals
std::string help_positional() const { std::string help_positional() const {
std::string out = pname_; std::string out = pname_;
if(get_expected()>1) if(get_expected() > 1)
out = out + "(" + std::to_string(get_expected()) + "x)"; out = out + "(" + std::to_string(get_expected()) + "x)";
else if(get_expected()==-1) else if(get_expected() == -1)
out = out + "..."; out = out + "...";
out = get_required() ? out : "["+out+"]"; out = get_required() ? out : "[" + out + "]";
return out; return out;
} }
@ -356,19 +323,17 @@ public:
out << " (env:" << envname_ << ")"; out << " (env:" << envname_ << ")";
if(!requires_.empty()) { if(!requires_.empty()) {
out << " Requires:"; out << " Requires:";
for(const Option* opt : requires_) for(const Option *opt : requires_)
out << " " << opt->get_name(); out << " " << opt->get_name();
} }
if(!excludes_.empty()) { if(!excludes_.empty()) {
out << " Excludes:"; out << " Excludes:";
for(const Option* opt : excludes_) for(const Option *opt : excludes_)
out << " " << opt->get_name(); out << " " << opt->get_name();
} }
return out.str(); return out.str();
} }
///@} ///@}
/// @name Parser tools /// @name Parser tools
///@{ ///@{
@ -378,7 +343,7 @@ public:
if(!callback_(results_)) if(!callback_(results_))
throw ConversionError(get_name() + "=" + detail::join(results_)); throw ConversionError(get_name() + "=" + detail::join(results_));
if(!validators_.empty()) { if(!validators_.empty()) {
for(const std::string & result : results_) for(const std::string &result : results_)
for(const std::function<bool(std::string)> &vali : validators_) for(const std::function<bool(std::string)> &vali : validators_)
if(!vali(result)) if(!vali(result))
throw ValidationError(get_name() + "=" + result); throw ValidationError(get_name() + "=" + result);
@ -386,7 +351,7 @@ public:
} }
/// If options share any of the same names, they are equal (not counting positional) /// 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_)
if(other.check_sname(sname)) if(other.check_sname(sname))
return true; return true;
@ -406,9 +371,9 @@ public:
/// Check a name. Requires "-" or "--" for short / long, supports positional name /// 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 {
if(name.length()>2 && name.substr(0,2) == "--") if(name.length() > 2 && name.substr(0, 2) == "--")
return check_lname(name.substr(2)); return check_lname(name.substr(2));
else if (name.length()>1 && name.substr(0,1) == "-") else if(name.length() > 1 && name.substr(0, 1) == "-")
return check_sname(name.substr(1)); return check_sname(name.substr(1));
else { else {
std::string local_pname = pname_; std::string local_pname = pname_;
@ -424,9 +389,9 @@ public:
bool check_sname(std::string name) const { bool check_sname(std::string name) const {
if(ignore_case_) { if(ignore_case_) {
name = detail::to_lower(name); name = detail::to_lower(name);
return std::find_if(std::begin(snames_), std::end(snames_), return std::find_if(std::begin(snames_), std::end(snames_), [&name](std::string local_sname) {
[&name](std::string local_sname){return detail::to_lower(local_sname) == name;}) return detail::to_lower(local_sname) == name;
!= std::end(snames_); }) != std::end(snames_);
} else } else
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_);
} }
@ -435,62 +400,49 @@ public:
bool check_lname(std::string name) const { bool check_lname(std::string name) const {
if(ignore_case_) { if(ignore_case_) {
name = detail::to_lower(name); name = detail::to_lower(name);
return std::find_if(std::begin(lnames_), std::end(lnames_), return std::find_if(std::begin(lnames_), std::end(lnames_), [&name](std::string local_sname) {
[&name](std::string local_sname){return detail::to_lower(local_sname) == name;}) return detail::to_lower(local_sname) == name;
!= std::end(lnames_); }) != std::end(lnames_);
} else } else
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 /// Puts a result at position r
void add_result(std::string s) { void add_result(std::string s) {
results_.push_back(s); results_.push_back(s);
callback_run_ = false; callback_run_ = false;
} }
/// Get a copy of the results /// Get a copy of the results
std::vector<std::string> results() const { std::vector<std::string> results() const { return results_; }
return results_;
}
/// See if the callback has been run already /// See if the callback has been run already
bool get_callback_run() const { bool get_callback_run() const { return callback_run_; }
return callback_run_;
}
///@} ///@}
/// @name Custom options /// @name Custom options
///@{ ///@{
/// Set a custom option, typestring, expected, and changeable /// Set a custom option, typestring, expected, and changeable
void set_custom_option(std::string typeval, int expected=1, bool changeable = false) { void set_custom_option(std::string typeval, int expected = 1, bool changeable = false) {
typeval_ = typeval; typeval_ = typeval;
expected_ = expected; expected_ = expected;
changeable_ = changeable; changeable_ = changeable;
} }
/// Set the default value string representation /// Set the default value string representation
void set_default_val(std::string val) { void set_default_val(std::string val) { defaultval_ = val; }
defaultval_ = val;
}
///@} ///@}
protected: protected:
/// @name App Helpers /// @name App Helpers
///@{ ///@{
/// Can print positional name detailed option if true /// Can print positional name detailed option if true
bool _has_help_positional() const { bool _has_help_positional() const {
return get_positional() && (has_description() || !requires_.empty() || !excludes_.empty() ); return get_positional() && (has_description() || !requires_.empty() || !excludes_.empty());
} }
///@} ///@}
}; };
} // namespace CLI } // namespace CLI

View File

@ -15,8 +15,8 @@ namespace detail {
// Returns false if not a short option. Otherwise, sets opt name and rest and returns true // Returns false if not a short option. Otherwise, sets opt name and rest and returns true
inline bool split_short(const std::string &current, std::string &name, std::string &rest) { inline bool split_short(const std::string &current, std::string &name, std::string &rest) {
if(current.size()>1 && current[0] == '-' && valid_first_char(current[1])) { if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) {
name = current.substr(1,1); name = current.substr(1, 1);
rest = current.substr(2); rest = current.substr(2);
return true; return true;
} else } else
@ -25,11 +25,11 @@ inline bool split_short(const std::string &current, std::string &name, std::stri
// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true // Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true
inline bool split_long(const std::string &current, std::string &name, std::string &value) { inline bool split_long(const std::string &current, std::string &name, std::string &value) {
if(current.size()>2 && current.substr(0,2) == "--" && valid_first_char(current[2])) { if(current.size() > 2 && current.substr(0, 2) == "--" && valid_first_char(current[2])) {
auto loc = current.find("="); auto loc = current.find("=");
if(loc != std::string::npos) { if(loc != std::string::npos) {
name = current.substr(2,loc-2); name = current.substr(2, loc - 2);
value = current.substr(loc+1); value = current.substr(loc + 1);
} else { } else {
name = current.substr(2); name = current.substr(2);
value = ""; value = "";
@ -44,17 +44,16 @@ inline std::vector<std::string> split_names(std::string current) {
std::vector<std::string> output; std::vector<std::string> output;
size_t val; size_t val;
while((val = current.find(",")) != std::string::npos) { while((val = current.find(",")) != std::string::npos) {
output.push_back(current.substr(0,val)); output.push_back(current.substr(0, val));
current = current.substr(val+1); current = current.substr(val + 1);
} }
output.push_back(current); output.push_back(current);
return output; return output;
} }
/// Get a vector of short names, one of long names, and a single name /// Get a vector of short names, one of long names, and a single name
inline std::tuple<std::vector<std::string>,std::vector<std::string>, std::string> inline std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>
get_names(const std::vector<std::string> &input) { 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;
@ -64,30 +63,28 @@ inline std::tuple<std::vector<std::string>,std::vector<std::string>, std::string
if(name.length() == 0) if(name.length() == 0)
continue; continue;
else if(name.length() > 1 && name[0] == '-' && name[1] != '-') { else if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
if(name.length()==2 && valid_first_char(name[1])) if(name.length() == 2 && valid_first_char(name[1]))
short_names.emplace_back(1,name[1]); short_names.emplace_back(1, name[1]);
else else
throw BadNameString("Invalid one char name: "+name); throw BadNameString("Invalid one char name: " + name);
} else if(name.length() > 2 && name.substr(0,2) == "--") { } else if(name.length() > 2 && name.substr(0, 2) == "--") {
name = name.substr(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 == "--") { } else if(name == "-" || name == "--") {
throw BadNameString("Must have a name, not just dashes"); throw BadNameString("Must have a name, not just dashes");
} else { } else {
if(pos_name.length() > 0) if(pos_name.length() > 0)
throw BadNameString("Only one positional name allowed, remove: "+name); throw BadNameString("Only one positional name allowed, remove: " + name);
pos_name = name; pos_name = name;
} }
} }
return std::tuple<std::vector<std::string>,std::vector<std::string>, std::string> return std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>(
(short_names, long_names, pos_name); short_names, long_names, pos_name);
} }
} // namespace detail } // namespace detail
} // namespace CLI } // namespace CLI

View File

@ -13,32 +13,29 @@
namespace CLI { namespace CLI {
namespace detail { namespace detail {
// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
///Split a string by a delim /// Split a string by a delim
inline std::vector<std::string> split(const std::string &s, char delim) { inline std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> elems; std::vector<std::string> elems;
// Check to see if emtpy string, give consistent result // Check to see if emtpy string, give consistent result
if(s=="") if(s == "")
elems.emplace_back(""); elems.emplace_back("");
else { else {
std::stringstream ss; std::stringstream ss;
ss.str(s); ss.str(s);
std::string item; std::string item;
while (std::getline(ss, item, delim)) { while(std::getline(ss, item, delim)) {
elems.push_back(item); elems.push_back(item);
} }
} }
return elems; return elems;
} }
/// Simple function to join a string /// Simple function to join a string
template <typename T> template <typename T> std::string join(const T &v, std::string delim = ",") {
std::string join(const T& v, std::string delim = ",") {
std::ostringstream s; std::ostringstream s;
size_t start = 0; size_t start = 0;
for (const auto& i : v) { for(const auto &i : v) {
if(start++ > 0) if(start++ > 0)
s << delim; s << delim;
s << i; s << i;
@ -47,10 +44,9 @@ std::string join(const T& v, std::string delim = ",") {
} }
/// Join a string in reverse order /// Join a string in reverse order
template<typename T> template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
std::string rjoin(const T& v, std::string delim = ",") {
std::ostringstream s; std::ostringstream s;
for(size_t start=0; start<v.size(); start++) { for(size_t start = 0; start < v.size(); start++) {
if(start > 0) if(start > 0)
s << delim; s << delim;
s << v[v.size() - start - 1]; s << v[v.size() - start - 1];
@ -61,43 +57,39 @@ std::string rjoin(const T& v, std::string delim = ",") {
// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string // Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
/// Trim whitespace from left of string /// Trim whitespace from left of string
inline std::string& ltrim(std::string &str) { inline std::string &ltrim(std::string &str) {
auto it = std::find_if(str.begin(), str.end(), [](char ch){ return !std::isspace<char>(ch , std::locale());}); auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
str.erase(str.begin(), it); str.erase(str.begin(), it);
return str; return str;
} }
/// Trim anything from left of string /// Trim anything from left of string
inline std::string& ltrim(std::string &str, const std::string &filter) { inline std::string &ltrim(std::string &str, const std::string &filter) {
auto it = std::find_if(str.begin(), str.end(), [&filter](char ch){return filter.find(ch) == std::string::npos;}); auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
str.erase(str.begin(), it); str.erase(str.begin(), it);
return str; return str;
} }
/// Trim whitespace from right of string /// Trim whitespace from right of string
inline std::string& rtrim(std::string &str) { inline std::string &rtrim(std::string &str) {
auto it = std::find_if(str.rbegin(), str.rend(), [](char ch){ return !std::isspace<char>(ch, std::locale());}); auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
str.erase(it.base() , str.end() ); str.erase(it.base(), str.end());
return str; return str;
} }
/// Trim anything from right of string /// Trim anything from right of string
inline std::string& rtrim(std::string &str, const std::string &filter) { inline std::string &rtrim(std::string &str, const std::string &filter) {
auto it = std::find_if(str.rbegin(), str.rend(), [&filter](char ch){return filter.find(ch) == std::string::npos;}); auto it =
std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
str.erase(it.base(), str.end()); str.erase(it.base(), str.end());
return str; return str;
} }
/// Trim whitespace from string /// Trim whitespace from string
inline std::string& trim(std::string &str) { inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }
return ltrim(rtrim(str));
}
/// Trim anything from string /// Trim anything from string
inline std::string& trim(std::string &str, const std::string filter) { inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }
return ltrim(rtrim(str, filter), filter);
}
/// Make a copy of the string and then trim it /// Make a copy of the string and then trim it
inline std::string trim_copy(const std::string &str) { inline std::string trim_copy(const std::string &str) {
@ -113,25 +105,21 @@ inline std::string trim_copy(const std::string &str, const std::string &filter)
/// Print a two part "help" string /// Print a two part "help" string
inline void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) { inline void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) {
name = " " + name; name = " " + name;
out << std::setw(static_cast<int>( wid)) << std::left << name; out << std::setw(static_cast<int>(wid)) << std::left << name;
if(description != "") { if(description != "") {
if(name.length()>=wid) if(name.length() >= wid)
out << std::endl << std::setw(static_cast<int>( wid)) << ""; out << std::endl << std::setw(static_cast<int>(wid)) << "";
out << description; out << description;
} }
out << std::endl; out << std::endl;
} }
/// Verify the first character of an option /// Verify the first character of an option
template<typename T> template <typename T> bool valid_first_char(T c) { return std::isalpha(c, std::locale()) || c == '_'; }
bool valid_first_char(T c) {
return std::isalpha(c, std::locale()) || c=='_';
}
/// Verify following characters of an option /// Verify following characters of an option
template<typename T> template <typename T> bool valid_later_char(T c) {
bool valid_later_char(T c) { return std::isalnum(c, std::locale()) || c == '_' || c == '.' || c == '-';
return std::isalnum(c, std::locale()) || c=='_' || c=='.' || c=='-';
} }
/// Verify an option name /// Verify an option name
@ -146,8 +134,9 @@ inline bool valid_name_string(const std::string &str) {
/// Return a lower case version of a string /// Return a lower case version of a string
inline std::string to_lower(std::string str) { inline std::string to_lower(std::string str) {
std::transform(std::begin(str), std::end(str), std::begin(str), std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
[](const std::string::value_type &x){return std::tolower(x,std::locale());}); return std::tolower(x, std::locale());
});
return str; return str;
} }
@ -155,7 +144,7 @@ inline std::string to_lower(std::string str) {
inline std::vector<std::string> split_up(std::string str) { inline std::vector<std::string> split_up(std::string str) {
std::vector<char> delims = {'\'', '\"'}; std::vector<char> delims = {'\'', '\"'};
auto find_ws = [](char ch){ return std::isspace<char>(ch , std::locale());}; auto find_ws = [](char ch) { return std::isspace<char>(ch, std::locale()); };
trim(str); trim(str);
std::vector<std::string> output; std::vector<std::string> output;
@ -164,8 +153,8 @@ inline std::vector<std::string> split_up(std::string str) {
if(str[0] == '\'') { if(str[0] == '\'') {
auto end = str.find('\'', 1); auto end = str.find('\'', 1);
if(end != std::string::npos) { if(end != std::string::npos) {
output.push_back(str.substr(1,end-1)); output.push_back(str.substr(1, end - 1));
str = str.substr(end+1); str = str.substr(end + 1);
} else { } else {
output.push_back(str.substr(1)); output.push_back(str.substr(1));
str = ""; str = "";
@ -173,8 +162,8 @@ inline std::vector<std::string> split_up(std::string str) {
} else if(str[0] == '\"') { } else if(str[0] == '\"') {
auto end = str.find('\"', 1); auto end = str.find('\"', 1);
if(end != std::string::npos) { if(end != std::string::npos) {
output.push_back(str.substr(1,end-1)); output.push_back(str.substr(1, end - 1));
str = str.substr(end+1); str = str.substr(end + 1);
} else { } else {
output.push_back(str.substr(1)); output.push_back(str.substr(1));
str = ""; str = "";
@ -183,7 +172,7 @@ inline std::vector<std::string> split_up(std::string str) {
} else { } else {
auto it = std::find_if(std::begin(str), std::end(str), find_ws); auto it = std::find_if(std::begin(str), std::end(str), find_ws);
if(it != std::end(str)) { if(it != std::end(str)) {
std::string value = std::string(str.begin(),it); std::string value = std::string(str.begin(), it);
output.push_back(value); output.push_back(value);
str = std::string(it, str.end()); str = std::string(it, str.end());
} else { } else {

View File

@ -12,7 +12,7 @@
namespace CLI { namespace CLI {
class Timer { class Timer {
protected: protected:
/// This is a typedef to make clocks easier to use /// This is a typedef to make clocks easier to use
using clock = std::chrono::steady_clock; using clock = std::chrono::steady_clock;
@ -20,7 +20,7 @@ protected:
using time_point = std::chrono::time_point<clock>; using time_point = std::chrono::time_point<clock>;
/// This is the type of a printing function, you can make your own /// This is the type of a printing function, you can make your own
using time_print_t = std::function<std::string (std::string, std::string)>; using time_print_t = std::function<std::string(std::string, std::string)>;
/// This is the title of the timer /// This is the title of the timer
std::string title_; std::string title_;
@ -32,29 +32,25 @@ protected:
time_point start_; time_point start_;
/// This is the number of times cycles (print divides by this number) /// This is the number of times cycles (print divides by this number)
size_t cycles {1}; size_t cycles{1};
public:
public:
/// Standard print function, this one is set by default /// Standard print function, this one is set by default
static std::string Simple(std::string title, std::string time) { static std::string Simple(std::string title, std::string time) { return title + ": " + time; }
return title + ": " + time;
}
/// This is a fancy print function with --- headers /// This is a fancy print function with --- headers
static std::string Big(std::string title, std::string time) { static std::string Big(std::string title, std::string time) {
return std::string("-----------------------------------------\n") return std::string("-----------------------------------------\n") + "| " + title + " | Time = " + time + "\n" +
+ "| " + title + " | Time = " + time + "\n" "-----------------------------------------";
+ "-----------------------------------------";
} }
public: public:
/// Standard constructor, can set title and print function /// Standard constructor, can set title and print function
Timer(std::string title="Timer", time_print_t time_print = Simple) Timer(std::string title = "Timer", time_print_t time_print = Simple)
: title_(std::move(title)), time_print_(std::move(time_print)), start_(clock::now()) {} : title_(std::move(title)), time_print_(std::move(time_print)), start_(clock::now()) {}
/// Time a function by running it multiple times. Target time is the len to target. /// Time a function by running it multiple times. Target time is the len to target.
std::string time_it(std::function<void()> f, double target_time=1) { std::string time_it(std::function<void()> f, double target_time = 1) {
time_point start = start_; time_point start = start_;
double total_time; double total_time;
@ -64,9 +60,9 @@ public:
f(); f();
std::chrono::duration<double> elapsed = clock::now() - start_; std::chrono::duration<double> elapsed = clock::now() - start_;
total_time = elapsed.count(); total_time = elapsed.count();
} while (n++ < 100 && total_time < target_time); } while(n++ < 100 && total_time < target_time);
std::string out = make_time_str(total_time/n) + " for " + std::to_string(n) + " tries"; std::string out = make_time_str(total_time / n) + " for " + std::to_string(n) + " tries";
start_ = start; start_ = start;
return out; return out;
} }
@ -81,51 +77,45 @@ public:
// LCOV_EXCL_START // LCOV_EXCL_START
std::string make_time_str(double time) const { std::string make_time_str(double time) const {
auto print_it = [](double x, std::string unit){ auto print_it = [](double x, std::string unit) {
char buffer[50]; char buffer[50];
std::snprintf(buffer, 50, "%.5g", x); std::snprintf(buffer, 50, "%.5g", x);
return buffer + std::string(" ") + unit; return buffer + std::string(" ") + unit;
}; };
if(time < .000001) if(time < .000001)
return print_it(time*1000000000, "ns"); return print_it(time * 1000000000, "ns");
else if(time < .001) else if(time < .001)
return print_it(time*1000000, "us"); return print_it(time * 1000000, "us");
else if(time < 1) else if(time < 1)
return print_it(time*1000, "ms"); return print_it(time * 1000, "ms");
else else
return print_it(time, "s"); return print_it(time, "s");
} }
// LCOV_EXCL_END // LCOV_EXCL_END
/// This is the main function, it creates a string /// This is the main function, it creates a string
std::string to_string() const { std::string to_string() const { return time_print_(title_, make_time_str()); }
return time_print_(title_, make_time_str());
}
/// Division sets the number of cycles to divide by (no graphical change) /// Division sets the number of cycles to divide by (no graphical change)
Timer& operator / (size_t val) {cycles = val; return *this;} Timer &operator/(size_t val) {
cycles = val;
return *this;
}
}; };
/// This class prints out the time upon destruction /// This class prints out the time upon destruction
class AutoTimer : public Timer { class AutoTimer : public Timer {
public: public:
/// Reimplementing the constructor is required in GCC 4.7 /// Reimplementing the constructor is required in GCC 4.7
AutoTimer(std::string title="Timer", time_print_t time_print = Simple) AutoTimer(std::string title = "Timer", time_print_t time_print = Simple) : Timer(title, time_print) {}
: Timer(title, time_print) {}
// GCC 4.7 does not support using inheriting constructors. // GCC 4.7 does not support using inheriting constructors.
/// This desctructor prints the string /// This desctructor prints the string
~AutoTimer () { ~AutoTimer() { std::cout << to_string() << std::endl; }
std::cout << to_string() << std::endl;
}
}; };
} // namespace CLI } // namespace CLI
/// This prints out the time if shifted into a std::cout like stream. /// This prints out the time if shifted into a std::cout like stream.
inline std::ostream & operator<< (std::ostream& in, const CLI::Timer& timer) { inline std::ostream &operator<<(std::ostream &in, const CLI::Timer &timer) { return in << timer.to_string(); }
return in << timer.to_string();
}

View File

@ -14,126 +14,100 @@ namespace CLI {
// Copied from C++14 // Copied from C++14
#if __cplusplus < 201402L #if __cplusplus < 201402L
template< bool B, class T = void > template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
using enable_if_t = typename std::enable_if<B,T>::type;
#else #else
// If your compiler supports C++14, you can use that definition instead // If your compiler supports C++14, you can use that definition instead
using std::enable_if_t; using std::enable_if_t;
#endif #endif
template <typename T> template <typename T> struct is_vector { static const bool value = false; };
struct is_vector {
static const bool value = false;
};
template <class T, class A> struct is_vector<std::vector<T, A>> { static bool const value = true; };
template<class T, class A> template <typename T> struct is_bool { static const bool value = false; };
struct is_vector<std::vector<T, A> > {
static bool const value = true;
};
template <typename T>
struct is_bool {
static const bool value = false;
};
template<>
struct is_bool<bool> {
static bool const value = true;
};
template <> struct is_bool<bool> { static bool const value = true; };
namespace detail { namespace detail {
// Based generally on https://rmf.io/cxx11/almost-static-if // Based generally on https://rmf.io/cxx11/almost-static-if
/// Simple empty scoped class /// Simple empty scoped class
enum class enabler {}; enum class enabler {};
/// An instance to use in EnableIf /// An instance to use in EnableIf
constexpr enabler dummy = {}; constexpr enabler dummy = {};
// Type name print
// Type name print /// 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
/// Was going to be based on template <typename T,
/// 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> enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy>
constexpr const char* type_name() { constexpr const char *type_name() {
return "INT"; return "INT";
} }
template<typename T, template <typename T,
enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy> enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
constexpr const char* type_name() { constexpr const char *type_name() {
return "UINT"; return "UINT";
} }
template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
template<typename T, constexpr const char *type_name() {
enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
constexpr const char* type_name() {
return "FLOAT"; return "FLOAT";
} }
/// This one should not be used, since vector types print the internal type
/// 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>
template<typename T, constexpr const char *type_name() {
enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>
constexpr const char* type_name() {
return "VECTOR"; return "VECTOR";
} }
template <typename T,
template<typename T, enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value detail::enabler> = detail::dummy>
, detail::enabler> = detail::dummy> constexpr const char *type_name() {
constexpr const char* type_name() {
return "TEXT"; return "TEXT";
} }
// Lexical cast
/// Integers
// Lexical cast template <typename T, enable_if_t<std::is_integral<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T &output) {
try {
/// 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{
output = static_cast<T>(std::stoll(input)); output = static_cast<T>(std::stoll(input));
return true; return true;
} catch (const std::invalid_argument&) { } catch(const std::invalid_argument &) {
return false; return false;
} catch (const std::out_of_range&) { } catch(const std::out_of_range &) {
return false; return false;
} }
} }
/// Floats /// Floats
template<typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy> template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy>
bool lexical_cast(std::string input, T& output) { bool lexical_cast(std::string input, T &output) {
try{ try {
output =static_cast<T>(std::stold(input)); output = static_cast<T>(std::stold(input));
return true; return true;
} catch (const std::invalid_argument&) { } catch(const std::invalid_argument &) {
return false; return false;
} catch (const std::out_of_range&) { } catch(const std::out_of_range &) {
return false; return false;
} }
} }
/// String and similar
/// String and similar template <
template<typename T, typename T,
enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value, detail::enabler> = detail::dummy>
, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) {
bool lexical_cast(std::string input, T& output) {
output = input; output = input;
return true; return true;
} }
} // namespace detail } // namespace detail
} // namespace CLI } // namespace CLI

View File

@ -16,7 +16,6 @@
namespace CLI { namespace CLI {
/// @defgroup validator_group Validators /// @defgroup validator_group Validators
/// @brief Some validators that are provided /// @brief Some validators that are provided
/// ///
@ -31,7 +30,7 @@ inline bool ExistingFile(std::string filename) {
if(!exist) { if(!exist) {
std::cerr << "File does not exist: " << filename << std::endl; std::cerr << "File does not exist: " << filename << std::endl;
return false; return false;
} else if (is_dir) { } else if(is_dir) {
std::cerr << "File is actually a directory: " << filename << std::endl; std::cerr << "File is actually a directory: " << filename << std::endl;
return false; return false;
} else { } else {
@ -47,7 +46,7 @@ inline bool ExistingDirectory(std::string filename) {
if(!exist) { if(!exist) {
std::cerr << "Directory does not exist: " << filename << std::endl; std::cerr << "Directory does not exist: " << filename << std::endl;
return false; return false;
} else if (is_dir) { } else if(is_dir) {
return true; return true;
} else { } else {
std::cerr << "Directory is actually a file: " << filename << std::endl; std::cerr << "Directory is actually a file: " << filename << std::endl;
@ -55,7 +54,6 @@ inline bool ExistingDirectory(std::string filename) {
} }
} }
/// Check for a non-existing path /// Check for a non-existing path
inline bool NonexistentPath(std::string filename) { inline bool NonexistentPath(std::string filename) {
struct stat buffer; struct stat buffer;
@ -69,9 +67,8 @@ inline bool NonexistentPath(std::string filename) {
} }
/// Produce a range validator function /// Produce a range validator function
template<typename T> template <typename T> std::function<bool(std::string)> Range(T min, T max) {
std::function<bool(std::string)> Range(T min, T max) { return [min, max](std::string input) {
return [min, max](std::string input){
T val; T val;
detail::lexical_cast(input, val); detail::lexical_cast(input, val);
return val >= min && val <= max; return val >= min && val <= max;
@ -79,10 +76,7 @@ std::function<bool(std::string)> Range(T min, T max) {
} }
/// Range of one value is 0 to value /// Range of one value is 0 to value
template<typename T> template <typename T> std::function<bool(std::string)> Range(T max) { return Range(static_cast<T>(0), max); }
std::function<bool(std::string)> Range(T max) {
return Range(static_cast<T>(0), max);
}
/// @} /// @}

View File

@ -1,2 +1 @@
-I../build/googletest-src/googletest/include/ - I../ build / googletest - src / googletest / include / -I../ build / googletest - src / googlemock / include /
-I../build/googletest-src/googlemock/include/

View File

@ -1,13 +1,12 @@
#include "app_helper.hpp" #include "app_helper.hpp"
#include <cstdlib> #include <cstdlib>
TEST_F(TApp, OneFlagShort) { TEST_F(TApp, OneFlagShort) {
app.add_flag("-c,--count"); app.add_flag("-c,--count");
args = {"-c"}; args = {"-c"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("-c")); EXPECT_EQ((size_t)1, app.count("-c"));
EXPECT_EQ((size_t) 1, app.count("--count")); EXPECT_EQ((size_t)1, app.count("--count"));
} }
TEST_F(TApp, CountNonExist) { TEST_F(TApp, CountNonExist) {
@ -21,8 +20,8 @@ TEST_F(TApp, OneFlagLong) {
app.add_flag("-c,--count"); app.add_flag("-c,--count");
args = {"--count"}; args = {"--count"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("-c")); EXPECT_EQ((size_t)1, app.count("-c"));
EXPECT_EQ((size_t) 1, app.count("--count")); EXPECT_EQ((size_t)1, app.count("--count"));
} }
TEST_F(TApp, DashedOptions) { TEST_F(TApp, DashedOptions) {
@ -32,11 +31,10 @@ TEST_F(TApp, DashedOptions) {
args = {"-c", "--q", "--this", "--that"}; args = {"-c", "--q", "--this", "--that"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("-c")); EXPECT_EQ((size_t)1, app.count("-c"));
EXPECT_EQ((size_t) 1, app.count("--q")); EXPECT_EQ((size_t)1, app.count("--q"));
EXPECT_EQ((size_t) 2, app.count("--this")); EXPECT_EQ((size_t)2, app.count("--this"));
EXPECT_EQ((size_t) 2, app.count("--that")); EXPECT_EQ((size_t)2, app.count("--that"));
} }
TEST_F(TApp, OneFlagRef) { TEST_F(TApp, OneFlagRef) {
@ -44,8 +42,8 @@ TEST_F(TApp, OneFlagRef) {
app.add_flag("-c,--count", ref); app.add_flag("-c,--count", ref);
args = {"--count"}; args = {"--count"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("-c")); EXPECT_EQ((size_t)1, app.count("-c"));
EXPECT_EQ((size_t) 1, app.count("--count")); EXPECT_EQ((size_t)1, app.count("--count"));
EXPECT_EQ(1, ref); EXPECT_EQ(1, ref);
} }
@ -54,8 +52,8 @@ TEST_F(TApp, OneString) {
app.add_option("-s,--string", str); app.add_option("-s,--string", str);
args = {"--string", "mystring"}; args = {"--string", "mystring"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("-s")); EXPECT_EQ((size_t)1, app.count("-s"));
EXPECT_EQ((size_t) 1, app.count("--string")); EXPECT_EQ((size_t)1, app.count("--string"));
EXPECT_EQ(str, "mystring"); EXPECT_EQ(str, "mystring");
} }
@ -64,29 +62,28 @@ TEST_F(TApp, OneStringEqualVersion) {
app.add_option("-s,--string", str); app.add_option("-s,--string", str);
args = {"--string=mystring"}; args = {"--string=mystring"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("-s")); EXPECT_EQ((size_t)1, app.count("-s"));
EXPECT_EQ((size_t) 1, app.count("--string")); EXPECT_EQ((size_t)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"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("--int")); EXPECT_EQ((size_t)1, app.count("--int"));
EXPECT_EQ((size_t) 1, app.count("-i")); EXPECT_EQ((size_t)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"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("--int")); EXPECT_EQ((size_t)1, app.count("--int"));
EXPECT_EQ((size_t) 1, app.count("-i")); EXPECT_EQ((size_t)1, app.count("-i"));
EXPECT_EQ(i, 4); EXPECT_EQ(i, 4);
} }
@ -95,18 +92,17 @@ TEST_F(TApp, OneStringAgain) {
app.add_option("-s,--string", str); app.add_option("-s,--string", str);
args = {"--string", "mystring"}; args = {"--string", "mystring"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("-s")); EXPECT_EQ((size_t)1, app.count("-s"));
EXPECT_EQ((size_t) 1, app.count("--string")); EXPECT_EQ((size_t)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);
run(); run();
EXPECT_EQ((size_t) 0, app.count("-s")); EXPECT_EQ((size_t)0, app.count("-s"));
EXPECT_EQ((size_t) 0, app.count("--string")); EXPECT_EQ((size_t)0, app.count("--string"));
EXPECT_EQ(str, "previous"); EXPECT_EQ(str, "previous");
} }
@ -132,14 +128,13 @@ TEST_F(TApp, LotsOfFlags) {
app.add_flag("-A"); app.add_flag("-A");
app.add_flag("-b"); app.add_flag("-b");
args = {"-a","-b","-aA"}; args = {"-a", "-b", "-aA"};
run(); run();
EXPECT_EQ((size_t) 2, app.count("-a")); EXPECT_EQ((size_t)2, app.count("-a"));
EXPECT_EQ((size_t) 1, app.count("-b")); EXPECT_EQ((size_t)1, app.count("-b"));
EXPECT_EQ((size_t) 1, app.count("-A")); EXPECT_EQ((size_t)1, app.count("-A"));
} }
TEST_F(TApp, BoolAndIntFlags) { TEST_F(TApp, BoolAndIntFlags) {
bool bflag; bool bflag;
@ -154,7 +149,7 @@ TEST_F(TApp, BoolAndIntFlags) {
run(); run();
EXPECT_TRUE(bflag); EXPECT_TRUE(bflag);
EXPECT_EQ(1, iflag); EXPECT_EQ(1, iflag);
EXPECT_EQ((unsigned int) 1, uflag); EXPECT_EQ((unsigned int)1, uflag);
app.reset(); app.reset();
@ -168,7 +163,7 @@ TEST_F(TApp, BoolAndIntFlags) {
run(); run();
EXPECT_FALSE(bflag); EXPECT_FALSE(bflag);
EXPECT_EQ(3, iflag); EXPECT_EQ(3, iflag);
EXPECT_EQ((unsigned int) 2, uflag); EXPECT_EQ((unsigned int)2, uflag);
} }
TEST_F(TApp, ShortOpts) { TEST_F(TApp, ShortOpts) {
@ -178,13 +173,15 @@ TEST_F(TApp, ShortOpts) {
app.add_flag("-z", funnyint); app.add_flag("-z", funnyint);
app.add_option("-y", someopt); app.add_option("-y", someopt);
args = {"-zzyzyz",}; args = {
"-zzyzyz",
};
run(); run();
EXPECT_EQ((size_t) 2, app.count("-z")); EXPECT_EQ((size_t)2, app.count("-z"));
EXPECT_EQ((size_t) 1, app.count("-y")); EXPECT_EQ((size_t)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);
} }
@ -200,8 +197,8 @@ TEST_F(TApp, DefaultOpts) {
run(); run();
EXPECT_EQ((size_t) 1, app.count("i")); EXPECT_EQ((size_t)1, app.count("i"));
EXPECT_EQ((size_t) 1, app.count("-s")); EXPECT_EQ((size_t)1, app.count("-s"));
EXPECT_EQ(2, i); EXPECT_EQ(2, i);
EXPECT_EQ("9", s); EXPECT_EQ("9", s);
} }
@ -224,7 +221,6 @@ TEST_F(TApp, RequiredFlags) {
app.reset(); app.reset();
args = {"-a", "-b"}; args = {"-a", "-b"};
run(); run();
} }
TEST_F(TApp, Positionals) { TEST_F(TApp, Positionals) {
@ -234,17 +230,16 @@ TEST_F(TApp, Positionals) {
app.add_option("posit1", posit1); app.add_option("posit1", posit1);
app.add_option("posit2", posit2); app.add_option("posit2", posit2);
args = {"thing1","thing2"}; args = {"thing1", "thing2"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("posit1")); EXPECT_EQ((size_t)1, app.count("posit1"));
EXPECT_EQ((size_t) 1, app.count("posit2")); EXPECT_EQ((size_t)1, app.count("posit2"));
EXPECT_EQ("thing1", posit1); EXPECT_EQ("thing1", posit1);
EXPECT_EQ("thing2", posit2); EXPECT_EQ("thing2", posit2);
} }
TEST_F(TApp, ForcedPositional) { TEST_F(TApp, ForcedPositional) {
std::vector<std::string> posit; std::vector<std::string> posit;
auto one = app.add_flag("--one"); auto one = app.add_flag("--one");
@ -266,7 +261,6 @@ TEST_F(TApp, ForcedPositional) {
EXPECT_EQ(answers2, posit); EXPECT_EQ(answers2, posit);
} }
TEST_F(TApp, MixedPositionals) { TEST_F(TApp, MixedPositionals) {
int positional_int; int positional_int;
@ -274,12 +268,12 @@ TEST_F(TApp, MixedPositionals) {
app.add_option("posit1,--posit1", positional_int, ""); app.add_option("posit1,--posit1", positional_int, "");
app.add_option("posit2,--posit2", positional_string, ""); app.add_option("posit2,--posit2", positional_string, "");
args = {"--posit2","thing2","7"}; args = {"--posit2", "thing2", "7"};
run(); run();
EXPECT_EQ((size_t) 1, app.count("posit2")); EXPECT_EQ((size_t)1, app.count("posit2"));
EXPECT_EQ((size_t) 1, app.count("--posit1")); EXPECT_EQ((size_t)1, app.count("--posit1"));
EXPECT_EQ(7, positional_int); EXPECT_EQ(7, positional_int);
EXPECT_EQ("thing2", positional_string); EXPECT_EQ("thing2", positional_string);
} }
@ -311,24 +305,22 @@ TEST_F(TApp, Reset) {
run(); run();
EXPECT_EQ((size_t) 1, app.count("--simple")); EXPECT_EQ((size_t)1, app.count("--simple"));
EXPECT_EQ((size_t) 1, app.count("-d")); EXPECT_EQ((size_t)1, app.count("-d"));
EXPECT_DOUBLE_EQ(1.2, doub); EXPECT_DOUBLE_EQ(1.2, doub);
app.reset(); app.reset();
EXPECT_EQ((size_t) 0, app.count("--simple")); EXPECT_EQ((size_t)0, app.count("--simple"));
EXPECT_EQ((size_t) 0, app.count("-d")); EXPECT_EQ((size_t)0, app.count("-d"));
run(); run();
EXPECT_EQ((size_t) 1, app.count("--simple")); EXPECT_EQ((size_t)1, app.count("--simple"));
EXPECT_EQ((size_t) 1, app.count("-d")); EXPECT_EQ((size_t)1, app.count("-d"));
EXPECT_DOUBLE_EQ(1.2, doub); EXPECT_DOUBLE_EQ(1.2, doub);
} }
TEST_F(TApp, RemoveOption) { TEST_F(TApp, RemoveOption) {
app.add_flag("--one"); app.add_flag("--one");
auto opt = app.add_flag("--two"); auto opt = app.add_flag("--two");
@ -354,7 +346,6 @@ TEST_F(TApp, FileNotExists) {
app.reset(); app.reset();
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_THROW(run(), CLI::ValidationError); EXPECT_THROW(run(), CLI::ValidationError);
@ -430,7 +421,6 @@ TEST_F(TApp, FailSet) {
EXPECT_THROW(run(), CLI::ConversionError); EXPECT_THROW(run(), CLI::ConversionError);
} }
TEST_F(TApp, InSetIgnoreCase) { TEST_F(TApp, InSetIgnoreCase) {
std::string choice; std::string choice;
@ -450,7 +440,6 @@ TEST_F(TApp, InSetIgnoreCase) {
run(); run();
EXPECT_EQ("THREE", choice); // Keeps caps from set EXPECT_EQ("THREE", choice); // Keeps caps from set
app.reset(); app.reset();
args = {"--quick", "four"}; args = {"--quick", "four"};
EXPECT_THROW(run(), CLI::ConversionError); EXPECT_THROW(run(), CLI::ConversionError);
@ -458,54 +447,50 @@ TEST_F(TApp, InSetIgnoreCase) {
app.reset(); app.reset();
args = {"--quick=one", "--quick=two"}; args = {"--quick=one", "--quick=two"};
EXPECT_THROW(run(), CLI::ConversionError); EXPECT_THROW(run(), CLI::ConversionError);
} }
TEST_F(TApp, VectorFixedString) { 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)->expected(3); CLI::Option *opt = app.add_option("-s,--string", strvec)->expected(3);
EXPECT_EQ(3, opt->get_expected()); EXPECT_EQ(3, opt->get_expected());
args = {"--string", "mystring", "mystring2", "mystring3"}; args = {"--string", "mystring", "mystring2", "mystring3"};
run(); run();
EXPECT_EQ((size_t) 3, app.count("--string")); EXPECT_EQ((size_t)3, app.count("--string"));
EXPECT_EQ(answer, strvec); EXPECT_EQ(answer, strvec);
} }
TEST_F(TApp, VectorUnlimString) { 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::Option *opt = app.add_option("-s,--string", strvec);
EXPECT_EQ(-1, opt->get_expected()); EXPECT_EQ(-1, opt->get_expected());
args = {"--string", "mystring", "mystring2", "mystring3"}; args = {"--string", "mystring", "mystring2", "mystring3"};
run(); run();
EXPECT_EQ((size_t) 3, app.count("--string")); EXPECT_EQ((size_t)3, app.count("--string"));
EXPECT_EQ(answer, strvec); EXPECT_EQ(answer, strvec);
app.reset(); app.reset();
args = {"-s", "mystring", "mystring2", "mystring3"}; args = {"-s", "mystring", "mystring2", "mystring3"};
run(); run();
EXPECT_EQ((size_t) 3, app.count("--string")); EXPECT_EQ((size_t)3, app.count("--string"));
EXPECT_EQ(answer, strvec); EXPECT_EQ(answer, strvec);
} }
TEST_F(TApp, VectorFancyOpts) { TEST_F(TApp, VectorFancyOpts) {
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)->required()->expected(3); CLI::Option *opt = app.add_option("-s,--string", strvec)->required()->expected(3);
EXPECT_EQ(3, opt->get_expected()); EXPECT_EQ(3, opt->get_expected());
args = {"--string", "mystring", "mystring2", "mystring3"}; args = {"--string", "mystring", "mystring2", "mystring3"};
run(); run();
EXPECT_EQ((size_t) 3, app.count("--string")); EXPECT_EQ((size_t)3, app.count("--string"));
EXPECT_EQ(answer, strvec); EXPECT_EQ(answer, strvec);
app.reset(); app.reset();
@ -517,7 +502,7 @@ TEST_F(TApp, VectorFancyOpts) {
} }
TEST_F(TApp, RequiresFlags) { TEST_F(TApp, RequiresFlags) {
CLI::Option* opt = app.add_flag("-s,--string"); CLI::Option *opt = app.add_flag("-s,--string");
app.add_flag("--both")->requires(opt); app.add_flag("--both")->requires(opt);
run(); run();
@ -535,9 +520,8 @@ TEST_F(TApp, RequiresFlags) {
EXPECT_THROW(run(), CLI::RequiresError); EXPECT_THROW(run(), CLI::RequiresError);
} }
TEST_F(TApp, ExcludesFlags) { TEST_F(TApp, ExcludesFlags) {
CLI::Option* opt = app.add_flag("-s,--string"); CLI::Option *opt = app.add_flag("-s,--string");
app.add_flag("--nostr")->excludes(opt); app.add_flag("--nostr")->excludes(opt);
run(); run();
@ -560,9 +544,9 @@ TEST_F(TApp, ExcludesFlags) {
} }
TEST_F(TApp, ExcludesMixedFlags) { TEST_F(TApp, ExcludesMixedFlags) {
CLI::Option* opt1 = app.add_flag("--opt1"); CLI::Option *opt1 = app.add_flag("--opt1");
app.add_flag("--opt2"); app.add_flag("--opt2");
CLI::Option* opt3 = app.add_flag("--opt3"); CLI::Option *opt3 = app.add_flag("--opt3");
app.add_flag("--no")->excludes(opt1, "--opt2", opt3); app.add_flag("--no")->excludes(opt1, "--opt2", opt3);
run(); run();
@ -585,9 +569,9 @@ TEST_F(TApp, ExcludesMixedFlags) {
} }
TEST_F(TApp, RequiresMultiFlags) { TEST_F(TApp, RequiresMultiFlags) {
CLI::Option* opt1 = app.add_flag("--opt1"); CLI::Option *opt1 = app.add_flag("--opt1");
CLI::Option* opt2 = app.add_flag("--opt2"); CLI::Option *opt2 = app.add_flag("--opt2");
CLI::Option* opt3 = app.add_flag("--opt3"); CLI::Option *opt3 = app.add_flag("--opt3");
app.add_flag("--optall")->requires(opt1, opt2, opt3); app.add_flag("--optall")->requires(opt1, opt2, opt3);
run(); run();
@ -618,7 +602,7 @@ TEST_F(TApp, RequiresMultiFlags) {
} }
TEST_F(TApp, RequiresMixedFlags) { TEST_F(TApp, RequiresMixedFlags) {
CLI::Option* opt1 = app.add_flag("--opt1"); CLI::Option *opt1 = app.add_flag("--opt1");
app.add_flag("--opt2"); app.add_flag("--opt2");
app.add_flag("--opt3"); app.add_flag("--opt3");
app.add_flag("--optall")->requires(opt1, "--opt2", "--opt3"); app.add_flag("--optall")->requires(opt1, "--opt2", "--opt3");
@ -651,8 +635,8 @@ TEST_F(TApp, RequiresMixedFlags) {
} }
TEST_F(TApp, RequiresChainedFlags) { TEST_F(TApp, RequiresChainedFlags) {
CLI::Option* opt1 = app.add_flag("--opt1"); CLI::Option *opt1 = app.add_flag("--opt1");
CLI::Option* opt2 = app.add_flag("--opt2")->requires(opt1); CLI::Option *opt2 = app.add_flag("--opt2")->requires(opt1);
app.add_flag("--opt3")->requires(opt2); app.add_flag("--opt3")->requires(opt2);
run(); run();
@ -690,13 +674,13 @@ TEST_F(TApp, Env) {
put_env("CLI11_TEST_ENV_TMP", "2"); put_env("CLI11_TEST_ENV_TMP", "2");
int val=1; int val = 1;
CLI::Option* vopt = app.add_option("--tmp", val)->envname("CLI11_TEST_ENV_TMP"); CLI::Option *vopt = app.add_option("--tmp", val)->envname("CLI11_TEST_ENV_TMP");
run(); run();
EXPECT_EQ(2, val); EXPECT_EQ(2, val);
EXPECT_EQ((size_t) 1, vopt->count()); EXPECT_EQ((size_t)1, vopt->count());
app.reset(); app.reset();
vopt->required(); vopt->required();
@ -708,8 +692,8 @@ TEST_F(TApp, Env) {
} }
TEST_F(TApp, RangeInt) { TEST_F(TApp, RangeInt) {
int x=0; int x = 0;
app.add_option("--one", x)->check(CLI::Range(3,6)); app.add_option("--one", x)->check(CLI::Range(3, 6));
args = {"--one=1"}; args = {"--one=1"};
EXPECT_THROW(run(), CLI::ValidationError); EXPECT_THROW(run(), CLI::ValidationError);
@ -733,9 +717,9 @@ TEST_F(TApp, RangeInt) {
TEST_F(TApp, RangeDouble) { TEST_F(TApp, RangeDouble) {
double x=0; double x = 0;
/// Note that this must be a double in Range, too /// Note that this must be a double in Range, too
app.add_option("--one", x)->check(CLI::Range(3.0,6.0)); app.add_option("--one", x)->check(CLI::Range(3.0, 6.0));
args = {"--one=1"}; args = {"--one=1"};
EXPECT_THROW(run(), CLI::ValidationError); EXPECT_THROW(run(), CLI::ValidationError);
@ -768,10 +752,9 @@ TEST_F(TApp, AllowExtras) {
args = {"-x", "-f"}; args = {"-x", "-f"};
std::vector<std::string> left_over; std::vector<std::string> left_over;
EXPECT_NO_THROW({left_over = run();}); EXPECT_NO_THROW({ left_over = run(); });
EXPECT_TRUE(val); EXPECT_TRUE(val);
EXPECT_EQ(std::vector<std::string>({"-x"}), left_over); EXPECT_EQ(std::vector<std::string>({"-x"}), left_over);
} }
TEST_F(TApp, AllowExtrasOrder) { TEST_F(TApp, AllowExtrasOrder) {
@ -780,14 +763,13 @@ TEST_F(TApp, AllowExtrasOrder) {
args = {"-x", "-f"}; args = {"-x", "-f"};
std::vector<std::string> left_over; std::vector<std::string> left_over;
EXPECT_NO_THROW({left_over = run();}); EXPECT_NO_THROW({ left_over = run(); });
EXPECT_EQ(std::vector<std::string>({"-f", "-x"}), left_over); EXPECT_EQ(std::vector<std::string>({"-f", "-x"}), left_over);
app.reset(); app.reset();
std::vector<std::string> left_over_2; std::vector<std::string> left_over_2;
left_over_2 = app.parse(left_over); left_over_2 = app.parse(left_over);
EXPECT_EQ(left_over, left_over_2); EXPECT_EQ(left_over, left_over_2);
} }
// Test horrible error // Test horrible error

View File

@ -53,10 +53,9 @@ TEST_F(TApp, AddingMultipleInfPositionals) {
EXPECT_THROW(run(), CLI::InvalidError); EXPECT_THROW(run(), CLI::InvalidError);
} }
TEST_F(TApp, AddingMultipleInfPositionalsSubcom) { TEST_F(TApp, AddingMultipleInfPositionalsSubcom) {
std::vector<std::string> one, two; std::vector<std::string> one, two;
CLI::App* below = app.add_subcommand("below"); CLI::App *below = app.add_subcommand("below");
below->add_option("one", one); below->add_option("one", one);
below->add_option("two", two); below->add_option("two", two);
@ -103,9 +102,7 @@ TEST_F(TApp, MultipleSubcomNoMatchingInplace2) {
EXPECT_NO_THROW(second->ignore_case()); EXPECT_NO_THROW(second->ignore_case());
} }
TEST_F(TApp, IncorrectConstructionFlagPositional1) { TEST_F(TApp, IncorrectConstructionFlagPositional1) { EXPECT_THROW(app.add_flag("cat"), CLI::IncorrectConstruction); }
EXPECT_THROW(app.add_flag("cat"), CLI::IncorrectConstruction);
}
TEST_F(TApp, IncorrectConstructionFlagPositional2) { TEST_F(TApp, IncorrectConstructionFlagPositional2) {
int x; int x;
@ -142,40 +139,40 @@ TEST_F(TApp, IncorrectConstructionVectorAsFlag) {
TEST_F(TApp, IncorrectConstructionRequiresCannotFind) { TEST_F(TApp, IncorrectConstructionRequiresCannotFind) {
auto cat = app.add_flag("--cat"); auto cat = app.add_flag("--cat");
EXPECT_THROW(cat->requires("--nothing"),CLI::IncorrectConstruction); EXPECT_THROW(cat->requires("--nothing"), CLI::IncorrectConstruction);
} }
TEST_F(TApp, IncorrectConstructionExcludesCannotFind) { TEST_F(TApp, IncorrectConstructionExcludesCannotFind) {
auto cat = app.add_flag("--cat"); auto cat = app.add_flag("--cat");
EXPECT_THROW(cat->excludes("--nothing"),CLI::IncorrectConstruction); EXPECT_THROW(cat->excludes("--nothing"), CLI::IncorrectConstruction);
} }
TEST_F(TApp, IncorrectConstructionDuplicateRequires) { TEST_F(TApp, IncorrectConstructionDuplicateRequires) {
auto cat = app.add_flag("--cat"); auto cat = app.add_flag("--cat");
auto other = app.add_flag("--other"); auto other = app.add_flag("--other");
ASSERT_NO_THROW(cat->requires(other)); ASSERT_NO_THROW(cat->requires(other));
EXPECT_THROW(cat->requires(other),CLI::OptionAlreadyAdded); EXPECT_THROW(cat->requires(other), CLI::OptionAlreadyAdded);
} }
TEST_F(TApp, IncorrectConstructionDuplicateRequiresTxt) { TEST_F(TApp, IncorrectConstructionDuplicateRequiresTxt) {
auto cat = app.add_flag("--cat"); auto cat = app.add_flag("--cat");
app.add_flag("--other"); app.add_flag("--other");
ASSERT_NO_THROW(cat->requires("--other")); ASSERT_NO_THROW(cat->requires("--other"));
EXPECT_THROW(cat->requires("--other"),CLI::OptionAlreadyAdded); EXPECT_THROW(cat->requires("--other"), CLI::OptionAlreadyAdded);
} }
TEST_F(TApp, IncorrectConstructionDuplicateExcludes) { TEST_F(TApp, IncorrectConstructionDuplicateExcludes) {
auto cat = app.add_flag("--cat"); auto cat = app.add_flag("--cat");
auto other = app.add_flag("--other"); auto other = app.add_flag("--other");
ASSERT_NO_THROW(cat->excludes(other)); ASSERT_NO_THROW(cat->excludes(other));
EXPECT_THROW(cat->excludes(other),CLI::OptionAlreadyAdded); EXPECT_THROW(cat->excludes(other), CLI::OptionAlreadyAdded);
} }
TEST_F(TApp, IncorrectConstructionDuplicateExcludesTxt) { TEST_F(TApp, IncorrectConstructionDuplicateExcludesTxt) {
auto cat = app.add_flag("--cat"); auto cat = app.add_flag("--cat");
app.add_flag("--other"); app.add_flag("--other");
ASSERT_NO_THROW(cat->excludes("--other")); ASSERT_NO_THROW(cat->excludes("--other"));
EXPECT_THROW(cat->excludes("--other"),CLI::OptionAlreadyAdded); EXPECT_THROW(cat->excludes("--other"), CLI::OptionAlreadyAdded);
} }
TEST_F(TApp, CheckName) { TEST_F(TApp, CheckName) {

View File

@ -20,7 +20,6 @@ TEST(THelp, Basic) {
EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("-h,--help"));
EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Options:"));
EXPECT_THAT(help, HasSubstr("Usage:")); EXPECT_THAT(help, HasSubstr("Usage:"));
} }
TEST(THelp, OptionalPositional) { TEST(THelp, OptionalPositional) {
@ -44,11 +43,9 @@ TEST(THelp, Hidden) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
std::string x; std::string x;
app.add_option("something", x, "My option here") app.add_option("something", x, "My option here")->group("Hidden");
->group("Hidden");
std::string y; std::string y;
app.add_option("--another", y) app.add_option("--another", y)->group("Hidden");
->group("Hidden");
std::string help = app.help(); std::string help = app.help();
@ -73,7 +70,6 @@ TEST(THelp, OptionalPositionalAndOptions) {
EXPECT_THAT(help, HasSubstr("-h,--help")); EXPECT_THAT(help, HasSubstr("-h,--help"));
EXPECT_THAT(help, HasSubstr("Options:")); EXPECT_THAT(help, HasSubstr("Options:"));
EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] [something]")); EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] [something]"));
} }
TEST(THelp, RequiredPositionalAndOptions) { TEST(THelp, RequiredPositionalAndOptions) {
@ -81,8 +77,7 @@ TEST(THelp, RequiredPositionalAndOptions) {
app.add_flag("-q,--quick"); app.add_flag("-q,--quick");
std::string x; std::string x;
app.add_option("something", x, "My option here") app.add_option("something", x, "My option here")->required();
->required();
std::string help = app.help(); std::string help = app.help();
@ -93,7 +88,6 @@ TEST(THelp, RequiredPositionalAndOptions) {
EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] something")); EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] something"));
} }
TEST(THelp, MultiOpts) { TEST(THelp, MultiOpts) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
std::vector<int> x, y; std::vector<int> x, y;
@ -111,7 +105,7 @@ TEST(THelp, MultiOpts) {
TEST(THelp, VectorOpts) { TEST(THelp, VectorOpts) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
std::vector<int> x = {1,2}; std::vector<int> x = {1, 2};
app.add_option("-q,--quick", x, "", true); app.add_option("-q,--quick", x, "", true);
std::string help = app.help(); std::string help = app.help();
@ -136,8 +130,6 @@ TEST(THelp, MultiPosOpts) {
EXPECT_THAT(help, HasSubstr("[vals...]")); EXPECT_THAT(help, HasSubstr("[vals...]"));
} }
TEST(THelp, EnvName) { TEST(THelp, EnvName) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
std::string input; std::string input;
@ -151,7 +143,7 @@ TEST(THelp, EnvName) {
TEST(THelp, Requires) { TEST(THelp, Requires) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
CLI::Option* op1 = app.add_flag("--op1"); CLI::Option *op1 = app.add_flag("--op1");
app.add_flag("--op2")->requires(op1); app.add_flag("--op2")->requires(op1);
std::string help = app.help(); std::string help = app.help();
@ -162,9 +154,9 @@ TEST(THelp, Requires) {
TEST(THelp, RequiresPositional) { TEST(THelp, RequiresPositional) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
int x,y; int x, y;
CLI::Option* op1 = app.add_option("op1", x, "one"); CLI::Option *op1 = app.add_option("op1", x, "one");
app.add_option("op2", y, "two")->requires(op1); app.add_option("op2", y, "two")->requires(op1);
std::string help = app.help(); std::string help = app.help();
@ -176,7 +168,7 @@ TEST(THelp, RequiresPositional) {
TEST(THelp, Excludes) { TEST(THelp, Excludes) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
CLI::Option* op1 = app.add_flag("--op1"); CLI::Option *op1 = app.add_flag("--op1");
app.add_flag("--op2")->excludes(op1); app.add_flag("--op2")->excludes(op1);
std::string help = app.help(); std::string help = app.help();
@ -187,9 +179,9 @@ TEST(THelp, Excludes) {
TEST(THelp, ExcludesPositional) { TEST(THelp, ExcludesPositional) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
int x,y; int x, y;
CLI::Option* op1 = app.add_option("op1", x); CLI::Option *op1 = app.add_option("op1", x);
app.add_option("op2", y)->excludes(op1); app.add_option("op2", y)->excludes(op1);
std::string help = app.help(); std::string help = app.help();
@ -218,8 +210,8 @@ TEST(THelp, Subcom) {
char x[] = "./myprogram"; char x[] = "./myprogram";
char y[] = "sub2"; char y[] = "sub2";
std::vector<char*> args = {x,y}; std::vector<char *> args = {x, y};
app.parse((int) args.size(), args.data()); app.parse((int)args.size(), args.data());
help = app.help(); help = app.help();
EXPECT_THAT(help, HasSubstr("Usage: ./myprogram sub2")); EXPECT_THAT(help, HasSubstr("Usage: ./myprogram sub2"));
@ -230,7 +222,7 @@ TEST(THelp, IntDefaults) {
int one{1}, two{2}; int one{1}, two{2};
app.add_option("--one", one, "Help for one", true); app.add_option("--one", one, "Help for one", true);
app.add_set("--set", two, {2,3,4}, "Help for set", true); app.add_set("--set", two, {2, 3, 4}, "Help for set", true);
std::string help = app.help(); std::string help = app.help();
@ -239,14 +231,13 @@ TEST(THelp, IntDefaults) {
EXPECT_THAT(help, HasSubstr("1")); EXPECT_THAT(help, HasSubstr("1"));
EXPECT_THAT(help, HasSubstr("=2")); EXPECT_THAT(help, HasSubstr("=2"));
EXPECT_THAT(help, HasSubstr("2,3,4")); EXPECT_THAT(help, HasSubstr("2,3,4"));
} }
TEST(THelp, SetLower) { TEST(THelp, SetLower) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
std::string def{"One"}; std::string def{"One"};
app.add_set_ignore_case("--set",def, {"oNe", "twO", "THREE"}, "Help for set", true); app.add_set_ignore_case("--set", def, {"oNe", "twO", "THREE"}, "Help for set", true);
std::string help = app.help(); std::string help = app.help();
@ -255,16 +246,15 @@ TEST(THelp, SetLower) {
EXPECT_THAT(help, HasSubstr("oNe")); EXPECT_THAT(help, HasSubstr("oNe"));
EXPECT_THAT(help, HasSubstr("twO")); EXPECT_THAT(help, HasSubstr("twO"));
EXPECT_THAT(help, HasSubstr("THREE")); EXPECT_THAT(help, HasSubstr("THREE"));
} }
TEST(Exit, ErrorWithHelp) { TEST(Exit, ErrorWithHelp) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
std::vector<std::string> input {"-h"}; std::vector<std::string> input{"-h"};
try { try {
app.parse(input); app.parse(input);
} catch (const CLI::CallForHelp &e) { } catch(const CLI::CallForHelp &e) {
EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Success), e.get_exit_code()); EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Success), e.get_exit_code());
} }
} }
@ -272,10 +262,10 @@ TEST(Exit, ErrorWithHelp) {
TEST(Exit, ErrorWithoutHelp) { TEST(Exit, ErrorWithoutHelp) {
CLI::App app{"My prog"}; CLI::App app{"My prog"};
std::vector<std::string> input {"--none"}; std::vector<std::string> input{"--none"};
try { try {
app.parse(input); app.parse(input);
} catch (const CLI::ParseError &e) { } catch(const CLI::ParseError &e) {
EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Extras), e.get_exit_code()); EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Extras), e.get_exit_code());
} }
} }
@ -287,5 +277,4 @@ TEST(Exit, ExitCodes) {
EXPECT_EQ(0, app.exit(CLI::Success())); EXPECT_EQ(0, app.exit(CLI::Success()));
EXPECT_EQ(0, app.exit(CLI::CallForHelp())); EXPECT_EQ(0, app.exit(CLI::CallForHelp()));
EXPECT_EQ(i, app.exit(CLI::ExtrasError("Thing"))); EXPECT_EQ(i, app.exit(CLI::ExtrasError("Thing")));
} }

View File

@ -7,7 +7,7 @@
TEST(Split, SimpleByToken) { TEST(Split, SimpleByToken) {
auto out = CLI::detail::split("one.two.three", '.'); auto out = CLI::detail::split("one.two.three", '.');
ASSERT_EQ((size_t) 3, out.size()); ASSERT_EQ((size_t)3, out.size());
EXPECT_EQ("one", out.at(0)); EXPECT_EQ("one", out.at(0));
EXPECT_EQ("two", out.at(1)); EXPECT_EQ("two", out.at(1));
EXPECT_EQ("three", out.at(2)); EXPECT_EQ("three", out.at(2));
@ -15,13 +15,13 @@ TEST(Split, SimpleByToken) {
TEST(Split, Single) { TEST(Split, Single) {
auto out = CLI::detail::split("one", '.'); auto out = CLI::detail::split("one", '.');
ASSERT_EQ((size_t) 1, out.size()); ASSERT_EQ((size_t)1, out.size());
EXPECT_EQ("one", out.at(0)); EXPECT_EQ("one", out.at(0));
} }
TEST(Split, Empty) { TEST(Split, Empty) {
auto out = CLI::detail::split("", '.'); auto out = CLI::detail::split("", '.');
ASSERT_EQ((size_t) 1, out.size()); ASSERT_EQ((size_t)1, out.size());
EXPECT_EQ("", out.at(0)); EXPECT_EQ("", out.at(0));
} }
@ -51,7 +51,6 @@ TEST(Trim, Various) {
EXPECT_EQ("a b", CLI::detail::trim(s4)); EXPECT_EQ("a b", CLI::detail::trim(s4));
} }
TEST(Trim, VariousFilters) { TEST(Trim, VariousFilters) {
std::string s1{" sdlfkj sdflk sd s "}; std::string s1{" sdlfkj sdflk sd s "};
std::string a1{"sdlfkj sdflk sd s"}; std::string a1{"sdlfkj sdflk sd s"};
@ -84,10 +83,8 @@ TEST(Trim, TrimCopy) {
EXPECT_NE(orig, trimmed); EXPECT_NE(orig, trimmed);
CLI::detail::trim(orig, "ab"); CLI::detail::trim(orig, "ab");
EXPECT_EQ(trimmed, orig); EXPECT_EQ(trimmed, orig);
} }
TEST(Validators, FileExists) { TEST(Validators, FileExists) {
std::string myfile{"TestFileNotUsed.txt"}; std::string myfile{"TestFileNotUsed.txt"};
EXPECT_FALSE(CLI::ExistingFile(myfile)); EXPECT_FALSE(CLI::ExistingFile(myfile));
@ -147,7 +144,7 @@ TEST(AppHelper, TempfileCreated) {
bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_TRUE(CLI::ExistingFile(name)); EXPECT_TRUE(CLI::ExistingFile(name));
EXPECT_THROW({TempFile otherfile(name);}, std::runtime_error); EXPECT_THROW({ TempFile otherfile(name); }, std::runtime_error);
} }
EXPECT_FALSE(CLI::ExistingFile(name)); EXPECT_FALSE(CLI::ExistingFile(name));
} }
@ -176,12 +173,11 @@ TEST(AppHelper, Ofstream) {
EXPECT_TRUE(CLI::ExistingFile(myfile)); EXPECT_TRUE(CLI::ExistingFile(myfile));
} }
EXPECT_FALSE(CLI::ExistingFile(name)); EXPECT_FALSE(CLI::ExistingFile(name));
} }
TEST(Split, StringList) { TEST(Split, StringList) {
std::vector<std::string> results {"a", "long", "--lone", "-q"}; std::vector<std::string> results{"a", "long", "--lone", "-q"};
EXPECT_EQ(results, CLI::detail::split_names("a,long,--lone,-q")); EXPECT_EQ(results, CLI::detail::split_names("a,long,--lone,-q"));
EXPECT_EQ(std::vector<std::string>({"one"}), CLI::detail::split_names("one")); EXPECT_EQ(std::vector<std::string>({"one"}), CLI::detail::split_names("one"));
@ -232,7 +228,6 @@ TEST(RegEx, Longs) {
EXPECT_FALSE(CLI::detail::split_long("-things", name, value)); EXPECT_FALSE(CLI::detail::split_long("-things", name, value));
EXPECT_FALSE(CLI::detail::split_long("Q", name, value)); EXPECT_FALSE(CLI::detail::split_long("Q", name, value));
EXPECT_FALSE(CLI::detail::split_long("--", name, value)); EXPECT_FALSE(CLI::detail::split_long("--", name, value));
} }
TEST(RegEx, SplittingNew) { TEST(RegEx, SplittingNew) {
@ -246,7 +241,8 @@ TEST(RegEx, SplittingNew) {
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts); EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts);
EXPECT_EQ("", pname); EXPECT_EQ("", pname);
EXPECT_NO_THROW(std::tie(shorts, longs, pname) = 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>({"long", "also-long"}), longs);
EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts); EXPECT_EQ(std::vector<std::string>({"s", "q"}), shorts);
@ -254,52 +250,47 @@ TEST(RegEx, SplittingNew) {
EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--"}), CLI::BadNameString); EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"--"}), CLI::BadNameString);
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({"-hi"}), CLI::BadNameString);
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({"---hi"}), CLI::BadNameString);
EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"one","two"}), CLI::BadNameString); EXPECT_THROW(std::tie(shorts, longs, pname) = CLI::detail::get_names({"one", "two"}), CLI::BadNameString);
} }
TEST(String, ToLower) { TEST(String, ToLower) { EXPECT_EQ("one and two", CLI::detail::to_lower("one And TWO")); }
EXPECT_EQ("one and two", CLI::detail::to_lower("one And TWO"));
}
TEST(Join, Forward) { TEST(Join, Forward) {
std::vector<std::string> val {{"one", "two", "three"}}; std::vector<std::string> val{{"one", "two", "three"}};
EXPECT_EQ("one,two,three", CLI::detail::join(val)); EXPECT_EQ("one,two,three", CLI::detail::join(val));
EXPECT_EQ("one;two;three", CLI::detail::join(val, ";")); EXPECT_EQ("one;two;three", CLI::detail::join(val, ";"));
} }
TEST(Join, Backward) { TEST(Join, Backward) {
std::vector<std::string> val {{"three", "two", "one"}}; std::vector<std::string> val{{"three", "two", "one"}};
EXPECT_EQ("one,two,three", CLI::detail::rjoin(val)); EXPECT_EQ("one,two,three", CLI::detail::rjoin(val));
EXPECT_EQ("one;two;three", CLI::detail::rjoin(val, ";")); EXPECT_EQ("one;two;three", CLI::detail::rjoin(val, ";"));
} }
TEST(SplitUp, Simple) { TEST(SplitUp, Simple) {
std::vector<std::string> oput = {"one", "two three"}; std::vector<std::string> oput = {"one", "two three"};
std::string orig {R"(one "two three")"}; std::string orig{R"(one "two three")"};
std::vector<std::string> result = CLI::detail::split_up(orig); std::vector<std::string> result = CLI::detail::split_up(orig);
EXPECT_EQ(oput, result); EXPECT_EQ(oput, result);
} }
TEST(SplitUp, Layered) { TEST(SplitUp, Layered) {
std::vector<std::string> output = {R"(one 'two three')"}; std::vector<std::string> output = {R"(one 'two three')"};
std::string orig {R"("one 'two three'")"}; std::string orig{R"("one 'two three'")"};
std::vector<std::string> result = CLI::detail::split_up(orig); std::vector<std::string> result = CLI::detail::split_up(orig);
EXPECT_EQ(output, result); EXPECT_EQ(output, result);
} }
TEST(SplitUp, Spaces) { TEST(SplitUp, Spaces) {
std::vector<std::string> oput = {"one", " two three"}; std::vector<std::string> oput = {"one", " two three"};
std::string orig {R"( one " two three" )"}; std::string orig{R"( one " two three" )"};
std::vector<std::string> result = CLI::detail::split_up(orig); std::vector<std::string> result = CLI::detail::split_up(orig);
EXPECT_EQ(oput, result); EXPECT_EQ(oput, result);
} }
TEST(SplitUp, BadStrings) { TEST(SplitUp, BadStrings) {
std::vector<std::string> oput = {"one", " two three"}; std::vector<std::string> oput = {"one", " two three"};
std::string orig {R"( one " two three )"}; std::string orig{R"( one " two three )"};
std::vector<std::string> result = CLI::detail::split_up(orig); std::vector<std::string> result = CLI::detail::split_up(orig);
EXPECT_EQ(oput, result); EXPECT_EQ(oput, result);
@ -328,7 +319,7 @@ TEST(Types, TypeName) {
std::string text_name = CLI::detail::type_name<std::string>(); std::string text_name = CLI::detail::type_name<std::string>();
EXPECT_EQ("TEXT", text_name); EXPECT_EQ("TEXT", text_name);
std::string text2_name = CLI::detail::type_name<char*>(); std::string text2_name = CLI::detail::type_name<char *>();
EXPECT_EQ("TEXT", text2_name); EXPECT_EQ("TEXT", text2_name);
} }
@ -350,7 +341,7 @@ TEST(Types, LexicalCastDouble) {
std::string input = "9.12"; std::string input = "9.12";
long double x; long double x;
EXPECT_TRUE(CLI::detail::lexical_cast(input, x)); EXPECT_TRUE(CLI::detail::lexical_cast(input, x));
EXPECT_FLOAT_EQ((float) 9.12, (float) x); EXPECT_FLOAT_EQ((float)9.12, (float)x);
std::string bad_input = "hello"; std::string bad_input = "hello";
EXPECT_FALSE(CLI::detail::lexical_cast(bad_input, x)); EXPECT_FALSE(CLI::detail::lexical_cast(bad_input, x));
@ -365,4 +356,3 @@ TEST(Types, LexicalCastString) {
CLI::detail::lexical_cast(input, output); CLI::detail::lexical_cast(input, output);
EXPECT_EQ(input, output); EXPECT_EQ(input, output);
} }

View File

@ -17,14 +17,13 @@ TEST(StringBased, First) {
std::vector<CLI::detail::ini_ret_t> output = CLI::detail::parse_ini(ofile); std::vector<CLI::detail::ini_ret_t> output = CLI::detail::parse_ini(ofile);
EXPECT_EQ((size_t) 2, output.size()); EXPECT_EQ((size_t)2, output.size());
EXPECT_EQ("one", output.at(0).name()); EXPECT_EQ("one", output.at(0).name());
EXPECT_EQ((size_t) 1, output.at(0).inputs.size()); EXPECT_EQ((size_t)1, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0)); EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name()); EXPECT_EQ("two", output.at(1).name());
EXPECT_EQ((size_t) 1, output.at(1).inputs.size()); EXPECT_EQ((size_t)1, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0)); EXPECT_EQ("four", output.at(1).inputs.at(0));
} }
TEST(StringBased, FirstWithComments) { TEST(StringBased, FirstWithComments) {
@ -39,12 +38,12 @@ TEST(StringBased, FirstWithComments) {
auto output = CLI::detail::parse_ini(ofile); auto output = CLI::detail::parse_ini(ofile);
EXPECT_EQ((size_t) 2, output.size()); EXPECT_EQ((size_t)2, output.size());
EXPECT_EQ("one", output.at(0).name()); EXPECT_EQ("one", output.at(0).name());
EXPECT_EQ((size_t) 1, output.at(0).inputs.size()); EXPECT_EQ((size_t)1, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0)); EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name()); EXPECT_EQ("two", output.at(1).name());
EXPECT_EQ((size_t) 1, output.at(1).inputs.size()); EXPECT_EQ((size_t)1, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0)); EXPECT_EQ("four", output.at(1).inputs.at(0));
} }
@ -59,15 +58,15 @@ TEST(StringBased, Quotes) {
auto output = CLI::detail::parse_ini(ofile); auto output = CLI::detail::parse_ini(ofile);
EXPECT_EQ((size_t) 3, output.size()); EXPECT_EQ((size_t)3, output.size());
EXPECT_EQ("one", output.at(0).name()); EXPECT_EQ("one", output.at(0).name());
EXPECT_EQ((size_t) 1, output.at(0).inputs.size()); EXPECT_EQ((size_t)1, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0)); EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name()); EXPECT_EQ("two", output.at(1).name());
EXPECT_EQ((size_t) 1, output.at(1).inputs.size()); EXPECT_EQ((size_t)1, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0)); EXPECT_EQ("four", output.at(1).inputs.at(0));
EXPECT_EQ("five", output.at(2).name()); EXPECT_EQ("five", output.at(2).name());
EXPECT_EQ((size_t) 1, output.at(2).inputs.size()); EXPECT_EQ((size_t)1, output.at(2).inputs.size());
EXPECT_EQ("six and seven", output.at(2).inputs.at(0)); EXPECT_EQ("six and seven", output.at(2).inputs.at(0));
} }
@ -82,21 +81,20 @@ TEST(StringBased, Vector) {
auto output = CLI::detail::parse_ini(ofile); auto output = CLI::detail::parse_ini(ofile);
EXPECT_EQ((size_t) 3, output.size()); EXPECT_EQ((size_t)3, output.size());
EXPECT_EQ("one", output.at(0).name()); EXPECT_EQ("one", output.at(0).name());
EXPECT_EQ((size_t) 1, output.at(0).inputs.size()); EXPECT_EQ((size_t)1, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0)); EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name()); EXPECT_EQ("two", output.at(1).name());
EXPECT_EQ((size_t) 1, output.at(1).inputs.size()); EXPECT_EQ((size_t)1, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0)); EXPECT_EQ("four", output.at(1).inputs.at(0));
EXPECT_EQ("five", output.at(2).name()); EXPECT_EQ("five", output.at(2).name());
EXPECT_EQ((size_t) 3, output.at(2).inputs.size()); EXPECT_EQ((size_t)3, output.at(2).inputs.size());
EXPECT_EQ("six", output.at(2).inputs.at(0)); EXPECT_EQ("six", output.at(2).inputs.at(0));
EXPECT_EQ("and", output.at(2).inputs.at(1)); EXPECT_EQ("and", output.at(2).inputs.at(1));
EXPECT_EQ("seven", output.at(2).inputs.at(2)); EXPECT_EQ("seven", output.at(2).inputs.at(2));
} }
TEST(StringBased, Spaces) { TEST(StringBased, Spaces) {
std::stringstream ofile; std::stringstream ofile;
@ -107,12 +105,12 @@ TEST(StringBased, Spaces) {
auto output = CLI::detail::parse_ini(ofile); auto output = CLI::detail::parse_ini(ofile);
EXPECT_EQ((size_t) 2, output.size()); EXPECT_EQ((size_t)2, output.size());
EXPECT_EQ("one", output.at(0).name()); EXPECT_EQ("one", output.at(0).name());
EXPECT_EQ((size_t) 1, output.at(0).inputs.size()); EXPECT_EQ((size_t)1, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0)); EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name()); EXPECT_EQ("two", output.at(1).name());
EXPECT_EQ((size_t) 1, output.at(1).inputs.size()); EXPECT_EQ((size_t)1, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0)); EXPECT_EQ("four", output.at(1).inputs.at(0));
} }
@ -127,17 +125,16 @@ TEST(StringBased, Sections) {
auto output = CLI::detail::parse_ini(ofile); auto output = CLI::detail::parse_ini(ofile);
EXPECT_EQ((size_t) 2, output.size()); EXPECT_EQ((size_t)2, output.size());
EXPECT_EQ("one", output.at(0).name()); EXPECT_EQ("one", output.at(0).name());
EXPECT_EQ((size_t) 1, output.at(0).inputs.size()); EXPECT_EQ((size_t)1, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0)); EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name()); EXPECT_EQ("two", output.at(1).name());
EXPECT_EQ("second", output.at(1).parent()); EXPECT_EQ("second", output.at(1).parent());
EXPECT_EQ((size_t) 1, output.at(1).inputs.size()); EXPECT_EQ((size_t)1, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0)); EXPECT_EQ("four", output.at(1).inputs.at(0));
} }
TEST(StringBased, SpacesSections) { TEST(StringBased, SpacesSections) {
std::stringstream ofile; std::stringstream ofile;
@ -151,13 +148,13 @@ TEST(StringBased, SpacesSections) {
auto output = CLI::detail::parse_ini(ofile); auto output = CLI::detail::parse_ini(ofile);
EXPECT_EQ((size_t) 2, output.size()); EXPECT_EQ((size_t)2, output.size());
EXPECT_EQ("one", output.at(0).name()); EXPECT_EQ("one", output.at(0).name());
EXPECT_EQ((size_t) 1, output.at(0).inputs.size()); EXPECT_EQ((size_t)1, output.at(0).inputs.size());
EXPECT_EQ("three", output.at(0).inputs.at(0)); EXPECT_EQ("three", output.at(0).inputs.at(0));
EXPECT_EQ("two", output.at(1).name()); EXPECT_EQ("two", output.at(1).name());
EXPECT_EQ("second", output.at(1).parent()); EXPECT_EQ("second", output.at(1).parent());
EXPECT_EQ((size_t) 1, output.at(1).inputs.size()); EXPECT_EQ((size_t)1, output.at(1).inputs.size());
EXPECT_EQ("four", output.at(1).inputs.at(0)); EXPECT_EQ("four", output.at(1).inputs.at(0));
} }
@ -174,7 +171,7 @@ TEST_F(TApp, IniNotRequired) {
out << "three=3" << std::endl; out << "three=3" << std::endl;
} }
int one=0, two=0, three=0; int one = 0, two = 0, three = 0;
app.add_option("--one", one); app.add_option("--one", one);
app.add_option("--two", two); app.add_option("--two", two);
app.add_option("--three", three); app.add_option("--three", three);
@ -188,7 +185,7 @@ TEST_F(TApp, IniNotRequired) {
EXPECT_EQ(3, three); EXPECT_EQ(3, three);
app.reset(); app.reset();
one=two=three=0; one = two = three = 0;
args = {"--one=1", "--two=2"}; args = {"--one=1", "--two=2"};
run(); run();
@ -196,7 +193,6 @@ TEST_F(TApp, IniNotRequired) {
EXPECT_EQ(1, one); EXPECT_EQ(1, one);
EXPECT_EQ(2, two); EXPECT_EQ(2, two);
EXPECT_EQ(3, three); EXPECT_EQ(3, three);
} }
TEST_F(TApp, IniRequiredNotFound) { TEST_F(TApp, IniRequiredNotFound) {
@ -205,7 +201,6 @@ TEST_F(TApp, IniRequiredNotFound) {
app.add_config("--config", noini, "", true); app.add_config("--config", noini, "", true);
EXPECT_THROW(run(), CLI::FileError); EXPECT_THROW(run(), CLI::FileError);
} }
TEST_F(TApp, IniOverwrite) { TEST_F(TApp, IniOverwrite) {
@ -243,7 +238,7 @@ TEST_F(TApp, IniRequired) {
out << "three=3" << std::endl; out << "three=3" << std::endl;
} }
int one=0, two=0, three=0; int one = 0, two = 0, three = 0;
app.add_option("--one", one)->required(); app.add_option("--one", one)->required();
app.add_option("--two", two)->required(); app.add_option("--two", two)->required();
app.add_option("--three", three)->required(); app.add_option("--three", three)->required();
@ -253,7 +248,7 @@ TEST_F(TApp, IniRequired) {
run(); run();
app.reset(); app.reset();
one=two=three=0; one = two = three = 0;
args = {"--one=1", "--two=2"}; args = {"--one=1", "--two=2"};
run(); run();
@ -267,7 +262,6 @@ TEST_F(TApp, IniRequired) {
args = {"--two=2"}; args = {"--two=2"};
EXPECT_THROW(run(), CLI::RequiredError); EXPECT_THROW(run(), CLI::RequiredError);
} }
TEST_F(TApp, IniVector) { TEST_F(TApp, IniVector) {
@ -289,12 +283,10 @@ TEST_F(TApp, IniVector) {
run(); run();
EXPECT_EQ(std::vector<int>({2,3}), two); EXPECT_EQ(std::vector<int>({2, 3}), two);
EXPECT_EQ(std::vector<int>({1,2,3}), three); EXPECT_EQ(std::vector<int>({1, 2, 3}), three);
} }
TEST_F(TApp, IniLayered) { TEST_F(TApp, IniLayered) {
TempFile tmpini{"TestIniTmp.ini"}; TempFile tmpini{"TestIniTmp.ini"};
@ -310,7 +302,7 @@ TEST_F(TApp, IniLayered) {
out << "subsubcom.val=3" << std::endl; out << "subsubcom.val=3" << std::endl;
} }
int one=0, two=0, three=0; int one = 0, two = 0, three = 0;
app.add_option("--val", one); app.add_option("--val", one);
auto subcom = app.add_subcommand("subcom"); auto subcom = app.add_subcommand("subcom");
subcom->add_option("--val", two); subcom->add_option("--val", two);
@ -322,7 +314,6 @@ TEST_F(TApp, IniLayered) {
EXPECT_EQ(1, one); EXPECT_EQ(1, one);
EXPECT_EQ(2, two); EXPECT_EQ(2, two);
EXPECT_EQ(3, three); EXPECT_EQ(3, three);
} }
TEST_F(TApp, IniFailure) { TEST_F(TApp, IniFailure) {
@ -340,7 +331,6 @@ TEST_F(TApp, IniFailure) {
EXPECT_THROW(run(), CLI::ExtrasINIError); EXPECT_THROW(run(), CLI::ExtrasINIError);
} }
TEST_F(TApp, IniSubFailure) { TEST_F(TApp, IniSubFailure) {
TempFile tmpini{"TestIniTmp.ini"}; TempFile tmpini{"TestIniTmp.ini"};
@ -357,7 +347,6 @@ TEST_F(TApp, IniSubFailure) {
EXPECT_THROW(run(), CLI::ExtrasINIError); EXPECT_THROW(run(), CLI::ExtrasINIError);
} }
TEST_F(TApp, IniNoSubFailure) { TEST_F(TApp, IniNoSubFailure) {
TempFile tmpini{"TestIniTmp.ini"}; TempFile tmpini{"TestIniTmp.ini"};
@ -388,7 +377,6 @@ TEST_F(TApp, IniFlagConvertFailure) {
EXPECT_THROW(run(), CLI::ConversionError); EXPECT_THROW(run(), CLI::ConversionError);
} }
TEST_F(TApp, IniFlagNumbers) { TEST_F(TApp, IniFlagNumbers) {
TempFile tmpini{"TestIniTmp.ini"}; TempFile tmpini{"TestIniTmp.ini"};
@ -474,7 +462,6 @@ TEST_F(TApp, IniFlags) {
EXPECT_EQ(true, three); EXPECT_EQ(true, three);
EXPECT_EQ(true, four); EXPECT_EQ(true, four);
EXPECT_EQ(true, five); EXPECT_EQ(true, five);
} }
TEST_F(TApp, IniOutputSimple) { TEST_F(TApp, IniOutputSimple) {
@ -528,7 +515,7 @@ TEST_F(TApp, IniOutputFlag) {
TEST_F(TApp, IniOutputSet) { TEST_F(TApp, IniOutputSet) {
int v; int v;
app.add_set("--simple", v, {1,2,3}); app.add_set("--simple", v, {1, 2, 3});
args = {"--simple=2"}; args = {"--simple=2"};
@ -538,10 +525,9 @@ TEST_F(TApp, IniOutputSet) {
EXPECT_THAT(str, HasSubstr("simple=2")); EXPECT_THAT(str, HasSubstr("simple=2"));
} }
TEST_F(TApp, IniOutputDefault) { TEST_F(TApp, IniOutputDefault) {
int v=7; int v = 7;
app.add_option("--simple", v, "", true); app.add_option("--simple", v, "", true);
run(); run();
@ -585,5 +571,4 @@ TEST_F(TApp, IniQuotedOutput) {
std::string str = app.config_to_str(); std::string str = app.config_to_str();
EXPECT_THAT(str, HasSubstr("val1=\"I am a string\"")); EXPECT_THAT(str, HasSubstr("val1=\"I am a string\""));
EXPECT_THAT(str, HasSubstr("val2='I am a \"confusing\" string'")); EXPECT_THAT(str, HasSubstr("val2='I am a \"confusing\" string'"));
} }

View File

@ -6,21 +6,19 @@ using ::testing::HasSubstr;
using cx = std::complex<double>; using cx = std::complex<double>;
CLI::Option* add_option(CLI::App& app, CLI::Option *
std::string name, cx& variable, add_option(CLI::App &app, std::string name, cx &variable, std::string description = "", bool defaulted = false) {
std::string description="", bool defaulted=false) { CLI::callback_t fun = [&variable](CLI::results_t res) {
CLI::callback_t fun = [&variable](CLI::results_t res){ if(res.size() != 2)
if(res.size()!=2)
return false; return false;
double x,y; double x, y;
bool worked = CLI::detail::lexical_cast(res[0], x) bool worked = CLI::detail::lexical_cast(res[0], x) && CLI::detail::lexical_cast(res[1], y);
&& CLI::detail::lexical_cast(res[1], y);
if(worked) if(worked)
variable = cx(x,y); variable = cx(x, y);
return worked; return worked;
}; };
CLI::Option* opt = app.add_option(name, fun, description, defaulted); CLI::Option *opt = app.add_option(name, fun, description, defaulted);
opt->set_custom_option("COMPLEX", 2); opt->set_custom_option("COMPLEX", 2);
if(defaulted) { if(defaulted) {
std::stringstream out; std::stringstream out;
@ -32,19 +30,18 @@ CLI::Option* add_option(CLI::App& app,
TEST_F(TApp, AddingComplexParser) { TEST_F(TApp, AddingComplexParser) {
cx comp{0, 0};
cx comp {0, 0};
add_option(app, "-c,--complex", comp); add_option(app, "-c,--complex", comp);
args = {"-c", "1.5", "2.5"}; args = {"-c", "1.5", "2.5"};
run(); run();
EXPECT_EQ(cx(1.5,2.5), comp); EXPECT_EQ(cx(1.5, 2.5), comp);
} }
TEST_F(TApp, DefaultComplex) { TEST_F(TApp, DefaultComplex) {
cx comp {1, 2}; cx comp{1, 2};
add_option(app, "-c,--complex", comp, "", true); add_option(app, "-c,--complex", comp, "", true);
args = {"-c", "4", "3"}; args = {"-c", "4", "3"};
@ -52,15 +49,15 @@ TEST_F(TApp, DefaultComplex) {
EXPECT_THAT(help, HasSubstr("1")); EXPECT_THAT(help, HasSubstr("1"));
EXPECT_THAT(help, HasSubstr("2")); EXPECT_THAT(help, HasSubstr("2"));
EXPECT_EQ(cx(1,2), comp); EXPECT_EQ(cx(1, 2), comp);
run(); run();
EXPECT_EQ(cx(4,3), comp); EXPECT_EQ(cx(4, 3), comp);
} }
TEST_F(TApp, BuiltinComplex) { TEST_F(TApp, BuiltinComplex) {
cx comp {1, 2}; cx comp{1, 2};
app.add_complex("-c,--complex", comp, "", true); app.add_complex("-c,--complex", comp, "", true);
args = {"-c", "4", "3"}; args = {"-c", "4", "3"};
@ -70,28 +67,26 @@ TEST_F(TApp, BuiltinComplex) {
EXPECT_THAT(help, HasSubstr("2")); EXPECT_THAT(help, HasSubstr("2"));
EXPECT_THAT(help, HasSubstr("COMPLEX")); EXPECT_THAT(help, HasSubstr("COMPLEX"));
EXPECT_EQ(cx(1,2), comp); EXPECT_EQ(cx(1, 2), comp);
run(); run();
EXPECT_EQ(cx(4,3), comp); EXPECT_EQ(cx(4, 3), comp);
} }
TEST_F(TApp, BuiltinComplexIgnoreI) { TEST_F(TApp, BuiltinComplexIgnoreI) {
cx comp {1, 2}; cx comp{1, 2};
app.add_complex("-c,--complex", comp); app.add_complex("-c,--complex", comp);
args = {"-c", "4", "3i"}; args = {"-c", "4", "3i"};
run(); run();
EXPECT_EQ(cx(4,3), comp); EXPECT_EQ(cx(4, 3), comp);
} }
TEST_F(TApp, BuiltinComplexFail) { TEST_F(TApp, BuiltinComplexFail) {
cx comp {1, 2}; cx comp{1, 2};
app.add_complex("-c,--complex", comp); app.add_complex("-c,--complex", comp);
args = {"-c", "4"}; args = {"-c", "4"};

View File

@ -26,5 +26,3 @@ TEST(Basic, Empty) {
app.parse(simpleput); app.parse(simpleput);
} }
} }

View File

@ -9,7 +9,7 @@ TEST_F(TApp, BasicSubcommands) {
EXPECT_THROW(app.get_subcommand("sub3"), CLI::OptionNotFound); EXPECT_THROW(app.get_subcommand("sub3"), CLI::OptionNotFound);
run(); run();
EXPECT_EQ((size_t) 0, app.get_subcommands().size()); EXPECT_EQ((size_t)0, app.get_subcommands().size());
app.reset(); app.reset();
args = {"sub1"}; args = {"sub1"};
@ -17,7 +17,7 @@ TEST_F(TApp, BasicSubcommands) {
EXPECT_EQ(sub1, app.get_subcommands().at(0)); EXPECT_EQ(sub1, app.get_subcommands().at(0));
app.reset(); app.reset();
EXPECT_EQ((size_t) 0, app.get_subcommands().size()); EXPECT_EQ((size_t)0, app.get_subcommands().size());
args = {"sub2"}; args = {"sub2"};
run(); run();
@ -34,7 +34,6 @@ TEST_F(TApp, MultiSubFallthrough) {
auto sub1 = app.add_subcommand("sub1"); auto sub1 = app.add_subcommand("sub1");
auto sub2 = app.add_subcommand("sub2"); auto sub2 = app.add_subcommand("sub2");
args = {"sub1", "sub2"}; args = {"sub1", "sub2"};
run(); run();
EXPECT_TRUE(app.got_subcommand("sub1")); EXPECT_TRUE(app.got_subcommand("sub1"));
@ -75,23 +74,17 @@ TEST_F(TApp, MultiSubFallthrough) {
EXPECT_THROW(app.got_subcommand("sub3"), CLI::OptionNotFound); EXPECT_THROW(app.got_subcommand("sub3"), CLI::OptionNotFound);
} }
TEST_F(TApp, Callbacks) { TEST_F(TApp, Callbacks) {
auto sub1 = app.add_subcommand("sub1"); auto sub1 = app.add_subcommand("sub1");
sub1->set_callback([](){ sub1->set_callback([]() { throw CLI::Success(); });
throw CLI::Success();
});
auto sub2 = app.add_subcommand("sub2"); auto sub2 = app.add_subcommand("sub2");
bool val = false; bool val = false;
sub2->set_callback([&val](){ sub2->set_callback([&val]() { val = true; });
val = true;
});
args = {"sub2"}; args = {"sub2"};
EXPECT_FALSE(val); EXPECT_FALSE(val);
run(); run();
EXPECT_TRUE(val); EXPECT_TRUE(val);
} }
TEST_F(TApp, NoFallThroughOpts) { TEST_F(TApp, NoFallThroughOpts) {
@ -104,7 +97,6 @@ TEST_F(TApp, NoFallThroughOpts) {
EXPECT_THROW(run(), CLI::ExtrasError); EXPECT_THROW(run(), CLI::ExtrasError);
} }
TEST_F(TApp, NoFallThroughPositionals) { TEST_F(TApp, NoFallThroughPositionals) {
int val = 1; int val = 1;
app.add_option("val", val); app.add_option("val", val);
@ -127,7 +119,6 @@ TEST_F(TApp, FallThroughRegular) {
run(); run();
} }
TEST_F(TApp, FallThroughShort) { TEST_F(TApp, FallThroughShort) {
app.fallthrough(); app.fallthrough();
int val = 1; int val = 1;
@ -140,7 +131,6 @@ TEST_F(TApp, FallThroughShort) {
run(); run();
} }
TEST_F(TApp, FallThroughPositional) { TEST_F(TApp, FallThroughPositional) {
app.fallthrough(); app.fallthrough();
int val = 1; int val = 1;
@ -165,7 +155,6 @@ TEST_F(TApp, FallThroughEquals) {
run(); run();
} }
TEST_F(TApp, EvilParseFallthrough) { TEST_F(TApp, EvilParseFallthrough) {
app.fallthrough(); app.fallthrough();
int val1 = 0, val2 = 0; int val1 = 0, val2 = 0;
@ -188,9 +177,7 @@ TEST_F(TApp, CallbackOrdering) {
app.add_option("--val", val); app.add_option("--val", val);
auto sub = app.add_subcommand("sub"); auto sub = app.add_subcommand("sub");
sub->set_callback([&val, &sub_val](){ sub->set_callback([&val, &sub_val]() { sub_val = val; });
sub_val = val;
});
args = {"sub", "--val=2"}; args = {"sub", "--val=2"};
run(); run();
@ -202,7 +189,6 @@ TEST_F(TApp, CallbackOrdering) {
run(); run();
EXPECT_EQ(2, val); EXPECT_EQ(2, val);
EXPECT_EQ(2, sub_val); EXPECT_EQ(2, sub_val);
} }
TEST_F(TApp, RequiredSubCom) { TEST_F(TApp, RequiredSubCom) {
@ -218,7 +204,6 @@ TEST_F(TApp, RequiredSubCom) {
args = {"sub1"}; args = {"sub1"};
run(); run();
} }
TEST_F(TApp, Required1SubCom) { TEST_F(TApp, Required1SubCom) {
@ -248,8 +233,8 @@ TEST_F(TApp, BadSubcomSearch) {
struct SubcommandProgram : public TApp { struct SubcommandProgram : public TApp {
CLI::App* start; CLI::App *start;
CLI::App* stop; CLI::App *stop;
int dummy; int dummy;
std::string file; std::string file;
@ -275,7 +260,6 @@ TEST_F(SubcommandProgram, Working) {
EXPECT_EQ("filename", file); EXPECT_EQ("filename", file);
} }
TEST_F(SubcommandProgram, Spare) { TEST_F(SubcommandProgram, Spare) {
args = {"extra", "-d", "start", "-ffilename"}; args = {"extra", "-d", "start", "-ffilename"};
@ -292,7 +276,7 @@ TEST_F(SubcommandProgram, Multiple) {
args = {"-d", "start", "-ffilename", "stop"}; args = {"-d", "start", "-ffilename", "stop"};
run(); run();
EXPECT_EQ((size_t) 2, app.get_subcommands().size()); EXPECT_EQ((size_t)2, app.get_subcommands().size());
EXPECT_EQ(1, dummy); EXPECT_EQ(1, dummy);
EXPECT_EQ("filename", file); EXPECT_EQ("filename", file);
} }
@ -308,20 +292,17 @@ TEST_F(SubcommandProgram, MultipleArgs) {
run(); run();
EXPECT_EQ((size_t) 2, app.get_subcommands().size()); EXPECT_EQ((size_t)2, app.get_subcommands().size());
} }
TEST_F(SubcommandProgram, CaseCheck) { TEST_F(SubcommandProgram, CaseCheck) {
args = {"Start"}; args = {"Start"};
EXPECT_THROW(run(), CLI::ExtrasError); EXPECT_THROW(run(), CLI::ExtrasError);
app.reset(); app.reset();
args = {"start"}; args = {"start"};
run(); run();
app.reset(); app.reset();
start->ignore_case(); start->ignore_case();
run(); run();
@ -337,7 +318,7 @@ TEST_F(TApp, SubcomInheritCaseCheck) {
auto sub2 = app.add_subcommand("sub2"); auto sub2 = app.add_subcommand("sub2");
run(); run();
EXPECT_EQ((size_t) 0, app.get_subcommands().size()); EXPECT_EQ((size_t)0, app.get_subcommands().size());
app.reset(); app.reset();
args = {"SuB1"}; args = {"SuB1"};
@ -345,7 +326,7 @@ TEST_F(TApp, SubcomInheritCaseCheck) {
EXPECT_EQ(sub1, app.get_subcommands().at(0)); EXPECT_EQ(sub1, app.get_subcommands().at(0));
app.reset(); app.reset();
EXPECT_EQ((size_t) 0, app.get_subcommands().size()); EXPECT_EQ((size_t)0, app.get_subcommands().size());
args = {"sUb2"}; args = {"sUb2"};
run(); run();
@ -366,9 +347,7 @@ TEST_F(SubcommandProgram, HelpOrder) {
TEST_F(SubcommandProgram, Callbacks) { TEST_F(SubcommandProgram, Callbacks) {
start->set_callback([](){ start->set_callback([]() { throw CLI::Success(); });
throw CLI::Success();
});
run(); run();
@ -377,5 +356,4 @@ TEST_F(SubcommandProgram, Callbacks) {
args = {"start"}; args = {"start"};
EXPECT_THROW(run(), CLI::Success); EXPECT_THROW(run(), CLI::Success);
} }

View File

@ -28,7 +28,7 @@ TEST(Timer, STimes) {
*/ */
// Fails on Windows // Fails on Windows
//TEST(Timer, UStimes) { // TEST(Timer, UStimes) {
// CLI::Timer timer; // CLI::Timer timer;
// std::this_thread::sleep_for(std::chrono::microseconds(2)); // std::this_thread::sleep_for(std::chrono::microseconds(2));
// std::string output = timer.to_string(); // std::string output = timer.to_string();
@ -58,8 +58,7 @@ TEST(Timer, PrintTimer) {
TEST(Timer, TimeItTimer) { TEST(Timer, TimeItTimer) {
CLI::Timer timer; CLI::Timer timer;
std::string output = timer.time_it([](){ std::string output = timer.time_it([]() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }, .1);
std::this_thread::sleep_for(std::chrono::milliseconds(10));}, .1);
std::cout << output << std::endl; std::cout << output << std::endl;
EXPECT_THAT(output, HasSubstr("ms")); EXPECT_THAT(output, HasSubstr("ms"));
} }

View File

@ -20,27 +20,23 @@ struct TApp : public ::testing::Test {
std::reverse(std::begin(newargs), std::end(newargs)); std::reverse(std::begin(newargs), std::end(newargs));
return app.parse(newargs); return app.parse(newargs);
} }
}; };
class TempFile { class TempFile {
std::string _name; std::string _name;
public: public:
TempFile(std::string name) : _name(name) { TempFile(std::string name) : _name(name) {
if(!CLI::NonexistentPath(_name)) if(!CLI::NonexistentPath(_name))
throw std::runtime_error(_name); throw std::runtime_error(_name);
} }
~TempFile() { ~TempFile() {
std::remove(_name.c_str()); // Doesn't matter if returns 0 or not std::remove(_name.c_str()); // Doesn't matter if returns 0 or not
} }
operator const std::string& () const {return _name;} operator const std::string &() const { return _name; }
const char* c_str() const {return _name.c_str();} const char *c_str() const { return _name.c_str(); }
}; };
inline void put_env(std::string name, std::string value) { inline void put_env(std::string name, std::string value) {
@ -58,4 +54,3 @@ inline void unset_env(std::string name) {
unsetenv(name.c_str()); unsetenv(name.c_str());
#endif #endif
} }

View File

@ -1,6 +1,4 @@
#include "CLI/CLI.hpp" #include "CLI/CLI.hpp"
#include "CLI/Timer.hpp" #include "CLI/Timer.hpp"
int do_nothing() { int do_nothing() { return 7; }
return 7;
}

View File

@ -9,4 +9,3 @@ TEST(Link, DoNothing) {
int a = do_nothing(); int a = do_nothing();
EXPECT_EQ(7, a); EXPECT_EQ(7, a);
} }