CLI11
C++11 Command Line Interface Parser
Error.hpp
1 #pragma once
2 
3 // Distributed under the 3-Clause BSD License. See accompanying
4 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
5 
6 #include <exception>
7 #include <stdexcept>
8 #include <string>
9 #include <utility>
10 
11 // CLI library includes
12 #include "CLI/StringTools.hpp"
13 
14 namespace CLI {
15 
16 // Use one of these on all error classes.
17 // These are temporary and are undef'd at the end of this file.
18 #define CLI11_ERROR_DEF(parent, name) \
19  protected: \
20  name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \
21  name(std::string ename, std::string msg, ExitCodes exit_code) \
22  : parent(std::move(ename), std::move(msg), exit_code) {} \
23  \
24  public: \
25  name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \
26  name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {}
27 
28 // This is added after the one above if a class is used directly and builds its own message
29 #define CLI11_ERROR_SIMPLE(name) \
30  explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}
31 
34 enum class ExitCodes {
35  Success = 0,
36  IncorrectConstruction = 100,
37  BadNameString,
38  OptionAlreadyAdded,
39  FileError,
40  ConversionError,
41  ValidationError,
42  RequiredError,
43  RequiresError,
44  ExcludesError,
45  ExtrasError,
46  ConfigError,
47  InvalidError,
48  HorribleError,
49  OptionNotFound,
50  ArgumentMismatch,
51  BaseClass = 127
52 };
53 
54 // Error definitions
55 
61 
63 class Error : public std::runtime_error {
64  int actual_exit_code;
65  std::string error_name{"Error"};
66 
67  public:
68  int get_exit_code() const { return actual_exit_code; }
69 
70  std::string get_name() const { return error_name; }
71 
72  Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass))
73  : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {}
74 
75  Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {}
76 };
77 
78 // Note: Using Error::Error constructors does not work on GCC 4.7
79 
81 class ConstructionError : public Error {
82  CLI11_ERROR_DEF(Error, ConstructionError)
83 };
84 
88  CLI11_ERROR_SIMPLE(IncorrectConstruction)
89  static IncorrectConstruction PositionalFlag(std::string name) {
90  return IncorrectConstruction(name + ": Flags cannot be positional");
91  }
92  static IncorrectConstruction Set0Opt(std::string name) {
93  return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
94  }
95  static IncorrectConstruction SetFlag(std::string name) {
96  return IncorrectConstruction(name + ": Cannot set an expected number for flags");
97  }
98  static IncorrectConstruction ChangeNotVector(std::string name) {
99  return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
100  }
101  static IncorrectConstruction AfterMultiOpt(std::string name) {
102  return IncorrectConstruction(
103  name + ": You can't change expected arguments after you've changed the multi option policy!");
104  }
105  static IncorrectConstruction MissingOption(std::string name) {
106  return IncorrectConstruction("Option " + name + " is not defined");
107  }
108  static IncorrectConstruction MultiOptionPolicy(std::string name) {
109  return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
110  }
111 };
112 
115  CLI11_ERROR_DEF(ConstructionError, BadNameString)
116  CLI11_ERROR_SIMPLE(BadNameString)
117  static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); }
118  static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); }
119  static BadNameString DashesOnly(std::string name) {
120  return BadNameString("Must have a name, not just dashes: " + name);
121  }
122  static BadNameString MultiPositionalNames(std::string name) {
123  return BadNameString("Only one positional name allowed, remove: " + name);
124  }
125 };
126 
129  CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)
130  explicit OptionAlreadyAdded(std::string name)
131  : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
132  static OptionAlreadyAdded Requires(std::string name, std::string other) {
133  return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded);
134  }
135  static OptionAlreadyAdded Excludes(std::string name, std::string other) {
136  return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded);
137  }
138 };
139 
140 // Parsing errors
141 
143 class ParseError : public Error {
144  CLI11_ERROR_DEF(Error, ParseError)
145 };
146 
147 // Not really "errors"
148 
150 class Success : public ParseError {
151  CLI11_ERROR_DEF(ParseError, Success)
152  Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {}
153 };
154 
156 class CallForHelp : public ParseError {
157  CLI11_ERROR_DEF(ParseError, CallForHelp)
158  CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
159 };
160 
162 class CallForAllHelp : public ParseError {
163  CLI11_ERROR_DEF(ParseError, CallForAllHelp)
165  : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
166 };
167 
169 class RuntimeError : public ParseError {
170  CLI11_ERROR_DEF(ParseError, RuntimeError)
171  explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
172 };
173 
175 class FileError : public ParseError {
176  CLI11_ERROR_DEF(ParseError, FileError)
177  CLI11_ERROR_SIMPLE(FileError)
178  static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); }
179 };
180 
182 class ConversionError : public ParseError {
183  CLI11_ERROR_DEF(ParseError, ConversionError)
184  CLI11_ERROR_SIMPLE(ConversionError)
185  ConversionError(std::string member, std::string name)
186  : ConversionError("The value " + member + " is not an allowed value for " + name) {}
187  ConversionError(std::string name, std::vector<std::string> results)
188  : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
189  static ConversionError TooManyInputsFlag(std::string name) {
190  return ConversionError(name + ": too many inputs for a flag");
191  }
192  static ConversionError TrueFalse(std::string name) {
193  return ConversionError(name + ": Should be true/false or a number");
194  }
195 };
196 
198 class ValidationError : public ParseError {
199  CLI11_ERROR_DEF(ParseError, ValidationError)
200  CLI11_ERROR_SIMPLE(ValidationError)
201  explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
202 };
203 
205 class RequiredError : public ParseError {
206  CLI11_ERROR_DEF(ParseError, RequiredError)
207  explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
208  static RequiredError Subcommand(size_t min_subcom) {
209  if(min_subcom == 1)
210  return RequiredError("A subcommand");
211  else
212  return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands",
213  ExitCodes::RequiredError);
214  }
215  static RequiredError Option(size_t min_option, size_t max_option, size_t used, const std::string &option_list) {
216  if((min_option == 1) && (max_option == 1) && (used == 0))
217  return RequiredError("Exactly 1 option from [" + option_list + "]");
218  else if((min_option == 1) && (max_option == 1) && (used > 1))
219  return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) +
220  " were given",
221  ExitCodes::RequiredError);
222  else if((min_option == 1) && (used == 0))
223  return RequiredError("At least 1 option from [" + option_list + "]");
224  else if(used < min_option)
225  return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " +
226  std::to_string(used) + "were given from [" + option_list + "]",
227  ExitCodes::RequiredError);
228  else if(max_option == 1)
229  return RequiredError("Requires at most 1 options be given from [" + option_list + "]",
230  ExitCodes::RequiredError);
231  else
232  return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
233  std::to_string(used) + "were given from [" + option_list + "]",
234  ExitCodes::RequiredError);
235  }
236 };
237 
239 class ArgumentMismatch : public ParseError {
240  CLI11_ERROR_DEF(ParseError, ArgumentMismatch)
241  CLI11_ERROR_SIMPLE(ArgumentMismatch)
242  ArgumentMismatch(std::string name, int expected, size_t recieved)
243  : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
244  ", got " + std::to_string(recieved))
245  : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
246  ", got " + std::to_string(recieved)),
247  ExitCodes::ArgumentMismatch) {}
248 
249  static ArgumentMismatch AtLeast(std::string name, int num) {
250  return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required");
251  }
252  static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
253  return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");
254  }
255  static ArgumentMismatch FlagOverride(std::string name) {
256  return ArgumentMismatch(name + " was given a disallowed flag override");
257  }
258 };
259 
261 class RequiresError : public ParseError {
262  CLI11_ERROR_DEF(ParseError, RequiresError)
263  RequiresError(std::string curname, std::string subname)
264  : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {}
265 };
266 
268 class ExcludesError : public ParseError {
269  CLI11_ERROR_DEF(ParseError, ExcludesError)
270  ExcludesError(std::string curname, std::string subname)
271  : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {}
272 };
273 
275 class ExtrasError : public ParseError {
276  CLI11_ERROR_DEF(ParseError, ExtrasError)
277  explicit ExtrasError(std::vector<std::string> args)
278  : ExtrasError((args.size() > 1 ? "The following arguments were not expected: "
279  : "The following argument was not expected: ") +
280  detail::rjoin(args, " "),
281  ExitCodes::ExtrasError) {}
282 };
283 
285 class ConfigError : public ParseError {
286  CLI11_ERROR_DEF(ParseError, ConfigError)
287  CLI11_ERROR_SIMPLE(ConfigError)
288  static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); }
289  static ConfigError NotConfigurable(std::string item) {
290  return ConfigError(item + ": This option is not allowed in a configuration file");
291  }
292 };
293 
295 class InvalidError : public ParseError {
296  CLI11_ERROR_DEF(ParseError, InvalidError)
297  explicit InvalidError(std::string name)
298  : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) {
299  }
300 };
301 
304 class HorribleError : public ParseError {
305  CLI11_ERROR_DEF(ParseError, HorribleError)
306  CLI11_ERROR_SIMPLE(HorribleError)
307 };
308 
309 // After parsing
310 
312 class OptionNotFound : public Error {
313  CLI11_ERROR_DEF(Error, OptionNotFound)
314  explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
315 };
316 
317 #undef CLI11_ERROR_DEF
318 #undef CLI11_ERROR_SIMPLE
319 
321 
322 } // namespace CLI
Definition: App.hpp:29
All errors derive from this one.
Definition: Error.hpp:63
-h or –help on command line
Definition: Error.hpp:156
Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error c...
Definition: Error.hpp:169
Definition: Error.hpp:304
Thrown when an excludes option is present.
Definition: Error.hpp:268
Thrown when an option is set to conflicting values (non-vector and multi args, for example) ...
Definition: Error.hpp:86
Thrown when too many positionals or options are found.
Definition: Error.hpp:275
Thrown when a requires option is missing.
Definition: Error.hpp:261
Usually something like –help-all on command line.
Definition: Error.hpp:162
Thrown when extra values are found in an INI file.
Definition: Error.hpp:285
Thrown when validation fails before parsing.
Definition: Error.hpp:295
Thrown when parsing an INI file and it is missing.
Definition: Error.hpp:175
Construction errors (not in parsing)
Definition: Error.hpp:81
Thrown when an option already exists.
Definition: Error.hpp:128
Thrown when counting a non-existent option.
Definition: Error.hpp:312
This is a successful completion on parsing, supposed to exit.
Definition: Error.hpp:150
Thrown on construction of a bad name.
Definition: Error.hpp:114
Anything that can error in Parse.
Definition: Error.hpp:143
Definition: Option.hpp:206
Thrown when the wrong number of arguments has been received.
Definition: Error.hpp:239
Thrown when conversion call back fails, such as when an int fails to coerce to a string.
Definition: Error.hpp:182
Thrown when a required option is missing.
Definition: Error.hpp:205
Thrown when validation of results fails.
Definition: Error.hpp:198