Commit a958ffece27fd4cef59ff4809bb672810d65f0b0
Committed by
Henry Schreiner
1 parent
17353068
Dropping name from simple print, better seperation for Errors
Some error codes are renamed
Showing
3 changed files
with
102 additions
and
73 deletions
include/CLI/App.hpp
| @@ -746,7 +746,7 @@ class App { | @@ -746,7 +746,7 @@ class App { | ||
| 746 | return e.get_exit_code(); | 746 | return e.get_exit_code(); |
| 747 | } | 747 | } |
| 748 | 748 | ||
| 749 | - if(e.exit_code != static_cast<int>(ExitCodes::Success)) { | 749 | + if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) { |
| 750 | if(failure_message_) | 750 | if(failure_message_) |
| 751 | err << failure_message_(this, e) << std::flush; | 751 | err << failure_message_(this, e) << std::flush; |
| 752 | } | 752 | } |
| @@ -1514,7 +1514,7 @@ inline std::string simple(const App *app, const Error &e) { | @@ -1514,7 +1514,7 @@ inline std::string simple(const App *app, const Error &e) { | ||
| 1514 | }; | 1514 | }; |
| 1515 | 1515 | ||
| 1516 | inline std::string help(const App *app, const Error &e) { | 1516 | inline std::string help(const App *app, const Error &e) { |
| 1517 | - std::string header = std::string("ERROR: ") + e.what() + "\n"; | 1517 | + std::string header = std::string("ERROR: ") + e.get_name() + ": " + e.what() + "\n"; |
| 1518 | header += app->help(); | 1518 | header += app->help(); |
| 1519 | return header; | 1519 | return header; |
| 1520 | }; | 1520 | }; |
include/CLI/Error.hpp
| @@ -7,6 +7,20 @@ | @@ -7,6 +7,20 @@ | ||
| 7 | #include <stdexcept> | 7 | #include <stdexcept> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | 9 | ||
| 10 | +// Use one of these on all error classes | ||
| 11 | +#define CLI11_ERROR_DEF(parent, name) \ | ||
| 12 | + protected: \ | ||
| 13 | + name(std::string name, std::string msg, int exit_code) : parent(name, msg, exit_code) {} \ | ||
| 14 | + name(std::string name, std::string msg, ExitCodes exit_code) : parent(name, msg, exit_code) {} \ | ||
| 15 | + \ | ||
| 16 | + public: \ | ||
| 17 | + name(std::string msg, ExitCodes exit_code) : parent(#name, msg, exit_code) {} \ | ||
| 18 | + name(std::string msg, int exit_code) : parent(#name, msg, exit_code) {} | ||
| 19 | + | ||
| 20 | +// This is added after the one above if a class is used directly and builds its own message | ||
| 21 | +#define CLI11_ERROR_SIMPLE(name) \ | ||
| 22 | + name(std::string msg) : name(#name, msg, ExitCodes::name) {} | ||
| 23 | + | ||
| 10 | namespace CLI { | 24 | namespace CLI { |
| 11 | 25 | ||
| 12 | /// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, | 26 | /// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, |
| @@ -17,15 +31,15 @@ enum class ExitCodes { | @@ -17,15 +31,15 @@ enum class ExitCodes { | ||
| 17 | BadNameString, | 31 | BadNameString, |
| 18 | OptionAlreadyAdded, | 32 | OptionAlreadyAdded, |
| 19 | File, | 33 | File, |
| 20 | - Conversion, | ||
| 21 | - Validation, | ||
| 22 | - Required, | ||
| 23 | - Requires, | ||
| 24 | - Excludes, | ||
| 25 | - Extras, | ||
| 26 | - ExtrasINI, | ||
| 27 | - Invalid, | ||
| 28 | - Horrible, | 34 | + ConversionError, |
| 35 | + ValidationError, | ||
| 36 | + RequiredError, | ||
| 37 | + RequiresError, | ||
| 38 | + ExcludesError, | ||
| 39 | + ExtrasError, | ||
| 40 | + ExtrasINIError, | ||
| 41 | + InvalidError, | ||
| 42 | + HorribleError, | ||
| 29 | OptionNotFound, | 43 | OptionNotFound, |
| 30 | BaseClass = 127 | 44 | BaseClass = 127 |
| 31 | }; | 45 | }; |
| @@ -39,126 +53,141 @@ enum class ExitCodes { | @@ -39,126 +53,141 @@ enum class ExitCodes { | ||
| 39 | /// @{ | 53 | /// @{ |
| 40 | 54 | ||
| 41 | /// All errors derive from this one | 55 | /// All errors derive from this one |
| 42 | -struct Error : public std::runtime_error { | 56 | +class Error : public std::runtime_error { |
| 43 | int exit_code; | 57 | int exit_code; |
| 58 | + std::string name{"Error"}; | ||
| 59 | + | ||
| 60 | + public: | ||
| 44 | int get_exit_code() const { return exit_code; } | 61 | int get_exit_code() const { return exit_code; } |
| 45 | 62 | ||
| 46 | - Error(std::string parent, std::string name, ExitCodes exit_code = ExitCodes::BaseClass) | ||
| 47 | - : runtime_error(parent + ": " + name), exit_code(static_cast<int>(exit_code)) {} | ||
| 48 | - Error(std::string parent, std::string name, int exit_code = static_cast<int>(ExitCodes::BaseClass)) | ||
| 49 | - : runtime_error(parent + ": " + name), exit_code(exit_code) {} | 63 | + std::string get_name() const { return name; } |
| 64 | + | ||
| 65 | + Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass)) | ||
| 66 | + : runtime_error(msg), exit_code(exit_code), name(name) {} | ||
| 67 | + | ||
| 68 | + Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {} | ||
| 50 | }; | 69 | }; |
| 51 | 70 | ||
| 71 | +// Note: Using Error::Error constructors does not work on GCC 4.7 | ||
| 72 | + | ||
| 52 | /// Construction errors (not in parsing) | 73 | /// Construction errors (not in parsing) |
| 53 | -struct ConstructionError : public Error { | ||
| 54 | - // Using Error::Error constructors seem to not work on GCC 4.7 | ||
| 55 | - ConstructionError(std::string parent, std::string name, ExitCodes exit_code = ExitCodes::BaseClass) | ||
| 56 | - : Error(parent, name, exit_code) {} | 74 | +class ConstructionError : public Error { |
| 75 | + CLI11_ERROR_DEF(Error, ConstructionError) | ||
| 57 | }; | 76 | }; |
| 58 | 77 | ||
| 59 | /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) | 78 | /// Thrown when an option is set to conflicting values (non-vector and multi args, for example) |
| 60 | -struct IncorrectConstruction : public ConstructionError { | ||
| 61 | - IncorrectConstruction(std::string name) | ||
| 62 | - : ConstructionError("IncorrectConstruction", name, ExitCodes::IncorrectConstruction) {} | 79 | +class IncorrectConstruction : public ConstructionError { |
| 80 | + CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) | ||
| 81 | + CLI11_ERROR_SIMPLE(IncorrectConstruction) | ||
| 63 | }; | 82 | }; |
| 64 | 83 | ||
| 65 | /// Thrown on construction of a bad name | 84 | /// Thrown on construction of a bad name |
| 66 | -struct BadNameString : public ConstructionError { | ||
| 67 | - BadNameString(std::string name) : ConstructionError("BadNameString", name, ExitCodes::BadNameString) {} | 85 | +class BadNameString : public ConstructionError { |
| 86 | + CLI11_ERROR_DEF(ConstructionError, BadNameString) | ||
| 87 | + CLI11_ERROR_SIMPLE(BadNameString) | ||
| 68 | }; | 88 | }; |
| 69 | 89 | ||
| 70 | /// Thrown when an option already exists | 90 | /// Thrown when an option already exists |
| 71 | -struct OptionAlreadyAdded : public ConstructionError { | ||
| 72 | - OptionAlreadyAdded(std::string name) | ||
| 73 | - : ConstructionError("OptionAlreadyAdded", name, ExitCodes::OptionAlreadyAdded) {} | 91 | +class OptionAlreadyAdded : public ConstructionError { |
| 92 | + CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) | ||
| 93 | + CLI11_ERROR_SIMPLE(OptionAlreadyAdded) | ||
| 74 | }; | 94 | }; |
| 75 | 95 | ||
| 76 | // Parsing errors | 96 | // Parsing errors |
| 77 | 97 | ||
| 78 | /// Anything that can error in Parse | 98 | /// Anything that can error in Parse |
| 79 | -struct ParseError : public Error { | ||
| 80 | - ParseError(std::string parent, std::string name, ExitCodes exit_code = ExitCodes::BaseClass) | ||
| 81 | - : Error(parent, name, exit_code) {} | ||
| 82 | - ParseError(std::string parent, std::string name, int exit_code = static_cast<int>(ExitCodes::BaseClass)) | ||
| 83 | - : Error(parent, name, exit_code) {} | 99 | +class ParseError : public Error { |
| 100 | + CLI11_ERROR_DEF(Error, ParseError) | ||
| 84 | }; | 101 | }; |
| 85 | 102 | ||
| 86 | // Not really "errors" | 103 | // Not really "errors" |
| 87 | 104 | ||
| 88 | /// This is a successful completion on parsing, supposed to exit | 105 | /// This is a successful completion on parsing, supposed to exit |
| 89 | -struct Success : public ParseError { | ||
| 90 | - Success() : ParseError("Success", "Successfully completed, should be caught and quit", ExitCodes::Success) {} | 106 | +class Success : public ParseError { |
| 107 | + CLI11_ERROR_DEF(ParseError, Success) | ||
| 108 | + Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} | ||
| 91 | }; | 109 | }; |
| 92 | 110 | ||
| 93 | /// -h or --help on command line | 111 | /// -h or --help on command line |
| 94 | -struct CallForHelp : public ParseError { | ||
| 95 | - CallForHelp() | ||
| 96 | - : ParseError("CallForHelp", "This should be caught in your main function, see examples", ExitCodes::Success) {} | 112 | +class CallForHelp : public ParseError { |
| 113 | + CLI11_ERROR_DEF(ParseError, CallForHelp) | ||
| 114 | + CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} | ||
| 97 | }; | 115 | }; |
| 98 | 116 | ||
| 99 | /// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code. | 117 | /// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code. |
| 100 | -struct RuntimeError : public ParseError { | ||
| 101 | - RuntimeError(int exit_code = 1) : ParseError("RuntimeError", "runtime error", exit_code) {} | 118 | +class RuntimeError : public ParseError { |
| 119 | + CLI11_ERROR_DEF(ParseError, RuntimeError) | ||
| 120 | + RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {} | ||
| 102 | }; | 121 | }; |
| 103 | 122 | ||
| 104 | /// Thrown when parsing an INI file and it is missing | 123 | /// Thrown when parsing an INI file and it is missing |
| 105 | -struct FileError : public ParseError { | ||
| 106 | - FileError(std::string name) : ParseError("FileError", name, ExitCodes::File) {} | 124 | +class FileError : public ParseError { |
| 125 | + CLI11_ERROR_DEF(ParseError, FileError) | ||
| 126 | + FileError(std::string msg) : FileError(msg, ExitCodes::File) {} | ||
| 107 | }; | 127 | }; |
| 108 | 128 | ||
| 109 | /// Thrown when conversion call back fails, such as when an int fails to coerce to a string | 129 | /// Thrown when conversion call back fails, such as when an int fails to coerce to a string |
| 110 | -struct ConversionError : public ParseError { | ||
| 111 | - ConversionError(std::string name) : ParseError("ConversionError", name, ExitCodes::Conversion) {} | 130 | +class ConversionError : public ParseError { |
| 131 | + CLI11_ERROR_DEF(ParseError, ConversionError) | ||
| 132 | + CLI11_ERROR_SIMPLE(ConversionError) | ||
| 112 | }; | 133 | }; |
| 113 | 134 | ||
| 114 | /// Thrown when validation of results fails | 135 | /// Thrown when validation of results fails |
| 115 | -struct ValidationError : public ParseError { | ||
| 116 | - ValidationError(std::string name) : ParseError("ValidationError", name, ExitCodes::Validation) {} | 136 | +class ValidationError : public ParseError { |
| 137 | + CLI11_ERROR_DEF(ParseError, ValidationError) | ||
| 138 | + CLI11_ERROR_SIMPLE(ValidationError) | ||
| 117 | }; | 139 | }; |
| 118 | 140 | ||
| 119 | /// Thrown when a required option is missing | 141 | /// Thrown when a required option is missing |
| 120 | -struct RequiredError : public ParseError { | ||
| 121 | - RequiredError(std::string name) : ParseError("RequiredError", name, ExitCodes::Required) {} | 142 | +class RequiredError : public ParseError { |
| 143 | + CLI11_ERROR_DEF(ParseError, RequiredError) | ||
| 144 | + CLI11_ERROR_SIMPLE(RequiredError) | ||
| 122 | }; | 145 | }; |
| 123 | 146 | ||
| 124 | /// Thrown when a requires option is missing | 147 | /// Thrown when a requires option is missing |
| 125 | -struct RequiresError : public ParseError { | ||
| 126 | - RequiresError(std::string name, std::string subname) | ||
| 127 | - : ParseError("RequiresError", name + " requires " + subname, ExitCodes::Requires) {} | 148 | +class RequiresError : public ParseError { |
| 149 | + CLI11_ERROR_DEF(ParseError, RequiresError) | ||
| 150 | + RequiresError(std::string curname, std::string subname) | ||
| 151 | + : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} | ||
| 128 | }; | 152 | }; |
| 129 | 153 | ||
| 130 | /// Thrown when a exludes option is present | 154 | /// Thrown when a exludes option is present |
| 131 | -struct ExcludesError : public ParseError { | ||
| 132 | - ExcludesError(std::string name, std::string subname) | ||
| 133 | - : ParseError("ExcludesError", name + " excludes " + subname, ExitCodes::Excludes) {} | 155 | +class ExcludesError : public ParseError { |
| 156 | + CLI11_ERROR_DEF(ParseError, ExcludesError) | ||
| 157 | + ExcludesError(std::string curname, std::string subname) | ||
| 158 | + : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} | ||
| 134 | }; | 159 | }; |
| 135 | 160 | ||
| 136 | /// Thrown when too many positionals or options are found | 161 | /// Thrown when too many positionals or options are found |
| 137 | -struct ExtrasError : public ParseError { | ||
| 138 | - ExtrasError(std::string name) : ParseError("ExtrasError", name, ExitCodes::Extras) {} | 162 | +class ExtrasError : public ParseError { |
| 163 | + CLI11_ERROR_DEF(ParseError, ExtrasError) | ||
| 164 | + CLI11_ERROR_SIMPLE(ExtrasError) | ||
| 139 | }; | 165 | }; |
| 140 | 166 | ||
| 141 | /// Thrown when extra values are found in an INI file | 167 | /// Thrown when extra values are found in an INI file |
| 142 | -struct ExtrasINIError : public ParseError { | ||
| 143 | - ExtrasINIError(std::string name) : ParseError("ExtrasINIError", name, ExitCodes::ExtrasINI) {} | 168 | +class ExtrasINIError : public ParseError { |
| 169 | + CLI11_ERROR_DEF(ParseError, ExtrasINIError) | ||
| 170 | + CLI11_ERROR_SIMPLE(ExtrasINIError) | ||
| 144 | }; | 171 | }; |
| 145 | 172 | ||
| 146 | /// Thrown when validation fails before parsing | 173 | /// Thrown when validation fails before parsing |
| 147 | -struct InvalidError : public ParseError { | ||
| 148 | - InvalidError(std::string name) : ParseError("InvalidError", name, ExitCodes::Invalid) {} | 174 | +class InvalidError : public ParseError { |
| 175 | + CLI11_ERROR_DEF(ParseError, InvalidError) | ||
| 176 | + CLI11_ERROR_SIMPLE(InvalidError) | ||
| 149 | }; | 177 | }; |
| 150 | 178 | ||
| 151 | -/// This is just a safety check to verify selection and parsing match | ||
| 152 | -struct HorribleError : public ParseError { | ||
| 153 | - HorribleError(std::string name) | ||
| 154 | - : ParseError("HorribleError", "(You should never see this error) " + name, ExitCodes::Horrible) {} | 179 | +/// This is just a safety check to verify selection and parsing match - you should not ever see it |
| 180 | +class HorribleError : public ParseError { | ||
| 181 | + CLI11_ERROR_DEF(ParseError, HorribleError) | ||
| 182 | + CLI11_ERROR_SIMPLE(HorribleError) | ||
| 155 | }; | 183 | }; |
| 156 | 184 | ||
| 157 | // After parsing | 185 | // After parsing |
| 158 | 186 | ||
| 159 | /// Thrown when counting a non-existent option | 187 | /// Thrown when counting a non-existent option |
| 160 | -struct OptionNotFound : public Error { | ||
| 161 | - OptionNotFound(std::string name) : Error("OptionNotFound", name, ExitCodes::OptionNotFound) {} | 188 | +class OptionNotFound : public Error { |
| 189 | + CLI11_ERROR_DEF(Error, OptionNotFound) | ||
| 190 | + CLI11_ERROR_SIMPLE(OptionNotFound) | ||
| 162 | }; | 191 | }; |
| 163 | 192 | ||
| 164 | /// @} | 193 | /// @} |
tests/HelpTest.cpp
| @@ -309,7 +309,7 @@ TEST(THelp, RemoveHelp) { | @@ -309,7 +309,7 @@ TEST(THelp, RemoveHelp) { | ||
| 309 | try { | 309 | try { |
| 310 | app.parse(input); | 310 | app.parse(input); |
| 311 | } catch(const CLI::ParseError &e) { | 311 | } catch(const CLI::ParseError &e) { |
| 312 | - EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Extras), e.get_exit_code()); | 312 | + EXPECT_EQ(static_cast<int>(CLI::ExitCodes::ExtrasError), e.get_exit_code()); |
| 313 | } | 313 | } |
| 314 | } | 314 | } |
| 315 | 315 | ||
| @@ -328,7 +328,7 @@ TEST(THelp, NoHelp) { | @@ -328,7 +328,7 @@ TEST(THelp, NoHelp) { | ||
| 328 | try { | 328 | try { |
| 329 | app.parse(input); | 329 | app.parse(input); |
| 330 | } catch(const CLI::ParseError &e) { | 330 | } catch(const CLI::ParseError &e) { |
| 331 | - EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Extras), e.get_exit_code()); | 331 | + EXPECT_EQ(static_cast<int>(CLI::ExitCodes::ExtrasError), e.get_exit_code()); |
| 332 | } | 332 | } |
| 333 | } | 333 | } |
| 334 | 334 | ||
| @@ -385,14 +385,14 @@ TEST(Exit, ErrorWithoutHelp) { | @@ -385,14 +385,14 @@ TEST(Exit, ErrorWithoutHelp) { | ||
| 385 | try { | 385 | try { |
| 386 | app.parse(input); | 386 | app.parse(input); |
| 387 | } catch(const CLI::ParseError &e) { | 387 | } catch(const CLI::ParseError &e) { |
| 388 | - EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Extras), e.get_exit_code()); | 388 | + EXPECT_EQ(static_cast<int>(CLI::ExitCodes::ExtrasError), e.get_exit_code()); |
| 389 | } | 389 | } |
| 390 | } | 390 | } |
| 391 | 391 | ||
| 392 | TEST(Exit, ExitCodes) { | 392 | TEST(Exit, ExitCodes) { |
| 393 | CLI::App app; | 393 | CLI::App app; |
| 394 | 394 | ||
| 395 | - auto i = static_cast<int>(CLI::ExitCodes::Extras); | 395 | + auto i = static_cast<int>(CLI::ExitCodes::ExtrasError); |
| 396 | EXPECT_EQ(0, app.exit(CLI::Success())); | 396 | EXPECT_EQ(0, app.exit(CLI::Success())); |
| 397 | EXPECT_EQ(0, app.exit(CLI::CallForHelp())); | 397 | EXPECT_EQ(0, app.exit(CLI::CallForHelp())); |
| 398 | EXPECT_EQ(i, app.exit(CLI::ExtrasError("Thing"))); | 398 | EXPECT_EQ(i, app.exit(CLI::ExtrasError("Thing"))); |
| @@ -432,10 +432,10 @@ TEST_F(CapturedHelp, CallForHelp) { | @@ -432,10 +432,10 @@ TEST_F(CapturedHelp, CallForHelp) { | ||
| 432 | } | 432 | } |
| 433 | 433 | ||
| 434 | TEST_F(CapturedHelp, NormalError) { | 434 | TEST_F(CapturedHelp, NormalError) { |
| 435 | - EXPECT_EQ(run(CLI::ExtrasError("Thing")), static_cast<int>(CLI::ExitCodes::Extras)); | 435 | + EXPECT_EQ(run(CLI::ExtrasError("Thing")), static_cast<int>(CLI::ExitCodes::ExtrasError)); |
| 436 | EXPECT_EQ(out.str(), ""); | 436 | EXPECT_EQ(out.str(), ""); |
| 437 | EXPECT_THAT(err.str(), HasSubstr("for more information")); | 437 | EXPECT_THAT(err.str(), HasSubstr("for more information")); |
| 438 | - EXPECT_THAT(err.str(), HasSubstr("ExtrasError")); | 438 | + EXPECT_THAT(err.str(), Not(HasSubstr("ExtrasError"))); |
| 439 | EXPECT_THAT(err.str(), HasSubstr("Thing")); | 439 | EXPECT_THAT(err.str(), HasSubstr("Thing")); |
| 440 | EXPECT_THAT(err.str(), Not(HasSubstr("Usage"))); | 440 | EXPECT_THAT(err.str(), Not(HasSubstr("Usage"))); |
| 441 | } | 441 | } |
| @@ -443,7 +443,7 @@ TEST_F(CapturedHelp, NormalError) { | @@ -443,7 +443,7 @@ TEST_F(CapturedHelp, NormalError) { | ||
| 443 | TEST_F(CapturedHelp, RepacedError) { | 443 | TEST_F(CapturedHelp, RepacedError) { |
| 444 | app.set_failure_message(CLI::FailureMessage::help); | 444 | app.set_failure_message(CLI::FailureMessage::help); |
| 445 | 445 | ||
| 446 | - EXPECT_EQ(run(CLI::ExtrasError("Thing")), static_cast<int>(CLI::ExitCodes::Extras)); | 446 | + EXPECT_EQ(run(CLI::ExtrasError("Thing")), static_cast<int>(CLI::ExitCodes::ExtrasError)); |
| 447 | EXPECT_EQ(out.str(), ""); | 447 | EXPECT_EQ(out.str(), ""); |
| 448 | EXPECT_THAT(err.str(), Not(HasSubstr("for more information"))); | 448 | EXPECT_THAT(err.str(), Not(HasSubstr("for more information"))); |
| 449 | EXPECT_THAT(err.str(), HasSubstr("ERROR: ExtrasError")); | 449 | EXPECT_THAT(err.str(), HasSubstr("ERROR: ExtrasError")); |