CLI11  2.5.0
Error.hpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2025, University of Cincinnati, developed by Henry Schreiner
2 // under NSF AWARD 1414736 and by the respective contributors.
3 // All rights reserved.
4 //
5 // SPDX-License-Identifier: BSD-3-Clause
6 
7 #pragma once
8 
9 // IWYU pragma: private, include "CLI/CLI.hpp"
10 
11 // [CLI11:public_includes:set]
12 #include <exception>
13 #include <stdexcept>
14 #include <string>
15 #include <utility>
16 #include <vector>
17 // [CLI11:public_includes:end]
18 
19 // CLI library includes
20 #include "Macros.hpp"
21 #include "StringTools.hpp"
22 
23 namespace CLI {
24 // [CLI11:error_hpp:verbatim]
25 
26 // Use one of these on all error classes.
27 // These are temporary and are undef'd at the end of this file.
28 #define CLI11_ERROR_DEF(parent, name) \
29  protected: \
30  name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \
31  name(std::string ename, std::string msg, ExitCodes exit_code) \
32  : parent(std::move(ename), std::move(msg), exit_code) {} \
33  \
34  public: \
35  name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \
36  name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {}
37 
38 // This is added after the one above if a class is used directly and builds its own message
39 #define CLI11_ERROR_SIMPLE(name) \
40  explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}
41 
44 enum class ExitCodes {
45  Success = 0,
49  FileError,
61  BaseClass = 127
62 };
63 
64 // Error definitions
65 
71 
73 class Error : public std::runtime_error {
74  int actual_exit_code;
75  std::string error_name{"Error"};
76 
77  public:
78  CLI11_NODISCARD int get_exit_code() const { return actual_exit_code; }
79 
80  CLI11_NODISCARD std::string get_name() const { return error_name; }
81 
82  Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass))
83  : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {}
84 
85  Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {}
86 };
87 
88 // Note: Using Error::Error constructors does not work on GCC 4.7
89 
91 class ConstructionError : public Error {
93 };
94 
99  static IncorrectConstruction PositionalFlag(std::string name) {
100  return IncorrectConstruction(name + ": Flags cannot be positional");
101  }
102  static IncorrectConstruction Set0Opt(std::string name) {
103  return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
104  }
105  static IncorrectConstruction SetFlag(std::string name) {
106  return IncorrectConstruction(name + ": Cannot set an expected number for flags");
107  }
108  static IncorrectConstruction ChangeNotVector(std::string name) {
109  return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
110  }
111  static IncorrectConstruction AfterMultiOpt(std::string name) {
112  return IncorrectConstruction(
113  name + ": You can't change expected arguments after you've changed the multi option policy!");
114  }
115  static IncorrectConstruction MissingOption(std::string name) {
116  return IncorrectConstruction("Option " + name + " is not defined");
117  }
118  static IncorrectConstruction MultiOptionPolicy(std::string name) {
119  return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
120  }
121 };
122 
127  static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); }
128  static BadNameString MissingDash(std::string name) {
129  return BadNameString("Long names strings require 2 dashes " + name);
130  }
131  static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); }
132  static BadNameString BadPositionalName(std::string name) {
133  return BadNameString("Invalid positional Name: " + name);
134  }
135  static BadNameString ReservedName(std::string name) {
136  return BadNameString("Names '-','--','++' are reserved and not allowed as option names " + name);
137  }
138  static BadNameString MultiPositionalNames(std::string name) {
139  return BadNameString("Only one positional name allowed, remove: " + name);
140  }
141 };
142 
146  explicit OptionAlreadyAdded(std::string name)
147  : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
148  static OptionAlreadyAdded Requires(std::string name, std::string other) {
149  return {name + " requires " + other, ExitCodes::OptionAlreadyAdded};
150  }
151  static OptionAlreadyAdded Excludes(std::string name, std::string other) {
152  return {name + " excludes " + other, ExitCodes::OptionAlreadyAdded};
153  }
154 };
155 
156 // Parsing errors
157 
159 class ParseError : public Error {
161 };
162 
163 // Not really "errors"
164 
166 class Success : public ParseError {
168  Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {}
169 };
170 
172 class CallForHelp : public Success {
174  CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
175 };
176 
178 class CallForAllHelp : public Success {
181  : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
182 };
183 
185 class CallForVersion : public Success {
188  : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {}
189 };
190 
192 class RuntimeError : public ParseError {
194  explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
195 };
196 
198 class FileError : public ParseError {
201  static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); }
202 };
203 
205 class ConversionError : public ParseError {
208  ConversionError(std::string member, std::string name)
209  : ConversionError("The value " + member + " is not an allowed value for " + name) {}
210  ConversionError(std::string name, std::vector<std::string> results)
211  : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
212  static ConversionError TooManyInputsFlag(std::string name) {
213  return ConversionError(name + ": too many inputs for a flag");
214  }
215  static ConversionError TrueFalse(std::string name) {
216  return ConversionError(name + ": Should be true/false or a number");
217  }
218 };
219 
221 class ValidationError : public ParseError {
224  explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
225 };
226 
228 class RequiredError : public ParseError {
230  explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
231  static RequiredError Subcommand(std::size_t min_subcom) {
232  if(min_subcom == 1) {
233  return RequiredError("A subcommand");
234  }
235  return {"Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError};
236  }
237  static RequiredError
238  Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) {
239  if((min_option == 1) && (max_option == 1) && (used == 0))
240  return RequiredError("Exactly 1 option from [" + option_list + "]");
241  if((min_option == 1) && (max_option == 1) && (used > 1)) {
242  return {"Exactly 1 option from [" + option_list + "] is required but " + std::to_string(used) +
243  " were given",
245  }
246  if((min_option == 1) && (used == 0))
247  return RequiredError("At least 1 option from [" + option_list + "]");
248  if(used < min_option) {
249  return {"Requires at least " + std::to_string(min_option) + " options used but only " +
250  std::to_string(used) + " were given from [" + option_list + "]",
252  }
253  if(max_option == 1)
254  return {"Requires at most 1 options be given from [" + option_list + "]", ExitCodes::RequiredError};
255 
256  return {"Requires at most " + std::to_string(max_option) + " options be used but " + std::to_string(used) +
257  " were given from [" + option_list + "]",
259  }
260 };
261 
263 class ArgumentMismatch : public ParseError {
266  ArgumentMismatch(std::string name, int expected, std::size_t received)
267  : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
268  ", got " + std::to_string(received))
269  : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
270  ", got " + std::to_string(received)),
272 
273  static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) {
274  return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " +
275  std::to_string(received));
276  }
277  static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) {
278  return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " +
279  std::to_string(received));
280  }
281  static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
282  return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");
283  }
284  static ArgumentMismatch FlagOverride(std::string name) {
285  return ArgumentMismatch(name + " was given a disallowed flag override");
286  }
287  static ArgumentMismatch PartialType(std::string name, int num, std::string type) {
288  return ArgumentMismatch(name + ": " + type + " only partially specified: " + std::to_string(num) +
289  " required for each element");
290  }
291 };
292 
294 class RequiresError : public ParseError {
296  RequiresError(std::string curname, std::string subname)
297  : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {}
298 };
299 
301 class ExcludesError : public ParseError {
303  ExcludesError(std::string curname, std::string subname)
304  : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {}
305 };
306 
308 class ExtrasError : public ParseError {
310  explicit ExtrasError(std::vector<std::string> args)
311  : ExtrasError((args.size() > 1 ? "The following arguments were not expected: "
312  : "The following argument was not expected: ") +
313  detail::rjoin(args, " "),
315  ExtrasError(const std::string &name, std::vector<std::string> args)
316  : ExtrasError(name,
317  (args.size() > 1 ? "The following arguments were not expected: "
318  : "The following argument was not expected: ") +
319  detail::rjoin(args, " "),
321 };
322 
324 class ConfigError : public ParseError {
327  static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); }
328  static ConfigError NotConfigurable(std::string item) {
329  return ConfigError(item + ": This option is not allowed in a configuration file");
330  }
331 };
332 
334 class InvalidError : public ParseError {
336  explicit InvalidError(std::string name)
337  : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) {
338  }
339 };
340 
343 class HorribleError : public ParseError {
346 };
347 
348 // After parsing
349 
351 class OptionNotFound : public Error {
353  explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
354 };
355 
356 #undef CLI11_ERROR_DEF
357 #undef CLI11_ERROR_SIMPLE
358 
360 
361 // [CLI11:error_hpp:end]
362 } // namespace CLI
Error(std::string name, std::string msg, ExitCodes exit_code)
Definition: Error.hpp:85
Thrown when an option already exists.
Definition: Error.hpp:144
Definition: App.hpp:36
Definition: Error.hpp:343
Anything that can error in Parse.
Definition: Error.hpp:159
Thrown when too many positionals or options are found.
Definition: Error.hpp:308
Does not output a diagnostic in CLI11_PARSE, but allows main() to return with a specific error code...
Definition: Error.hpp:192
ExitCodes
Definition: Error.hpp:44
Error(std::string name, std::string msg, int exit_code=static_cast< int >(ExitCodes::BaseClass))
Definition: Error.hpp:82
Definition: Option.hpp:231
STL namespace.
#define CLI11_ERROR_DEF(parent, name)
Definition: Error.hpp:28
std::string rjoin(const T &v, std::string delim=",")
Join a string in reverse order.
Definition: StringTools.hpp:91
MultiOptionPolicy
Enumeration of the multiOption Policy selection.
Definition: Option.hpp:40
CLI11_NODISCARD int get_exit_code() const
Definition: Error.hpp:78
auto to_string(T &&value) -> decltype(std::forward< T >(value))
Convert an object to a string (directly forward if this can become a string)
Definition: TypeTools.hpp:337
Thrown when a required option is missing.
Definition: Error.hpp:228
Thrown when parsing an INI file and it is missing.
Definition: Error.hpp:198
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:53
Thrown when conversion call back fails, such as when an int fails to coerce to a string.
Definition: Error.hpp:205
Usually something like –help-all on command line.
Definition: Error.hpp:178
Thrown when a requires option is missing.
Definition: Error.hpp:294
Construction errors (not in parsing)
Definition: Error.hpp:91
All errors derive from this one.
Definition: Error.hpp:73
Thrown when validation of results fails.
Definition: Error.hpp:221
Thrown when the wrong number of arguments has been received.
Definition: Error.hpp:263
CLI11_NODISCARD std::string get_name() const
Definition: Error.hpp:80
#define CLI11_NODISCARD
Definition: Macros.hpp:58
This is a successful completion on parsing, supposed to exit.
Definition: Error.hpp:166
#define CLI11_ERROR_SIMPLE(name)
Definition: Error.hpp:39
Thrown when an option is set to conflicting values (non-vector and multi args, for example) ...
Definition: Error.hpp:96
Thrown when an excludes option is present.
Definition: Error.hpp:301
-v or –version on command line
Definition: Error.hpp:185
Thrown when validation fails before parsing.
Definition: Error.hpp:334
Thrown on construction of a bad name.
Definition: Error.hpp:124
Thrown when extra values are found in an INI file.
Definition: Error.hpp:324
-h or –help on command line
Definition: Error.hpp:172
Thrown when counting a nonexistent option.
Definition: Error.hpp:351