From 94b3355b9a51eff1c8093e42ce1c002d168a4361 Mon Sep 17 00:00:00 2001 From: Henry Fredrick Schreiner Date: Sat, 18 Nov 2017 16:39:53 -0500 Subject: [PATCH] Adding more detail to error messages --- include/CLI/App.hpp | 44 ++++++++++++++++++++++--------------- include/CLI/Error.hpp | 4 ++-- include/CLI/StringTools.hpp | 2 +- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 32d2ab4a2..5fc02f43d 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -241,9 +241,10 @@ class App { T &variable, ///< The variable to set std::string description = "") { - CLI::callback_t fun = [&variable](CLI::results_t res) { + std::string simple_name = CLI::detail::split(name, ',').at(0); + CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) { if(res.size() != 1) - return false; + throw ConversionError("Only one " + simple_name + " allowed"); return detail::lexical_cast(res[0], variable); }; @@ -259,9 +260,10 @@ class App { std::string description, bool defaulted) { - CLI::callback_t fun = [&variable](CLI::results_t res) { + std::string simple_name = CLI::detail::split(name, ',').at(0); + CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) { if(res.size() != 1) - return false; + throw ConversionError("Only one " + simple_name + " allowed"); return detail::lexical_cast(res[0], variable); }; @@ -405,13 +407,14 @@ class App { std::set options, ///< The set of posibilities std::string description = "") { - CLI::callback_t fun = [&member, options](CLI::results_t res) { + std::string simple_name = CLI::detail::split(name, ',').at(0); + CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { if(res.size() != 1) { - return false; + throw ConversionError("Only one " + simple_name + " allowed"); } bool retval = detail::lexical_cast(res[0], member); if(!retval) - return false; + throw ConversionError("The value " + res[0] + "is not an allowed value for " + simple_name); return std::find(std::begin(options), std::end(options), member) != std::end(options); }; @@ -430,13 +433,14 @@ class App { std::string description, bool defaulted) { - CLI::callback_t fun = [&member, options](CLI::results_t res) { + std::string simple_name = CLI::detail::split(name, ',').at(0); + CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { if(res.size() != 1) { - return false; + throw ConversionError("Only one " + simple_name + " allowed"); } bool retval = detail::lexical_cast(res[0], member); if(!retval) - return false; + throw ConversionError("The value " + res[0] + "is not an allowed value for " + simple_name); return std::find(std::begin(options), std::end(options), member) != std::end(options); }; @@ -458,16 +462,17 @@ class App { std::set options, ///< The set of posibilities std::string description = "") { - CLI::callback_t fun = [&member, options](CLI::results_t res) { + std::string simple_name = CLI::detail::split(name, ',').at(0); + CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { if(res.size() != 1) { - return false; + throw ConversionError("Only one " + simple_name + " allowed"); } member = detail::to_lower(res[0]); auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { return detail::to_lower(val) == member; }); if(iter == std::end(options)) - return false; + throw ConversionError("The value " + member + "is not an allowed value for " + simple_name); else { member = *iter; return true; @@ -489,16 +494,17 @@ class App { std::string description, bool defaulted) { - CLI::callback_t fun = [&member, options](CLI::results_t res) { + std::string simple_name = CLI::detail::split(name, ',').at(0); + CLI::callback_t fun = [&member, options, simple_name](CLI::results_t res) { if(res.size() != 1) { - return false; + throw ConversionError("Only one " + simple_name + " allowed"); } member = detail::to_lower(res[0]); auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) { return detail::to_lower(val) == member; }); if(iter == std::end(options)) - return false; + throw ConversionError("The value " + member + "is not an allowed value for " + simple_name); else { member = *iter; return true; @@ -522,9 +528,11 @@ class App { std::string description = "", bool defaulted = false, std::string label = "COMPLEX") { - CLI::callback_t fun = [&variable](results_t res) { + + std::string simple_name = CLI::detail::split(name, ',').at(0); + CLI::callback_t fun = [&variable, simple_name, label](results_t res) { if(res.size() != 2) - return false; + throw ConversionError(simple_name + " is " + label + " which must have two values"); double x, y; bool worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(res[1], y); if(worked) diff --git a/include/CLI/Error.hpp b/include/CLI/Error.hpp index 0502d6cae..e820f2653 100644 --- a/include/CLI/Error.hpp +++ b/include/CLI/Error.hpp @@ -27,7 +27,7 @@ enum class ExitCodes { Invalid, Horrible, OptionNotFound, - BaseClass = 255 + BaseClass = 127 }; // Error definitions @@ -105,7 +105,7 @@ struct FileError : public ParseError { FileError(std::string name) : 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 coerce to a string struct ConversionError : public ParseError { ConversionError(std::string name) : ParseError("ConversionError", name, ExitCodes::Conversion) {} }; diff --git a/include/CLI/StringTools.hpp b/include/CLI/StringTools.hpp index cae0c11c7..3fff1449c 100644 --- a/include/CLI/StringTools.hpp +++ b/include/CLI/StringTools.hpp @@ -17,7 +17,7 @@ namespace detail { /// Split a string by a delim inline std::vector split(const std::string &s, char delim) { std::vector elems; - // Check to see if emtpy string, give consistent result + // Check to see if empty string, give consistent result if(s.empty()) elems.emplace_back(""); else {