CLI11  1.9.1
Error.hpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2020, 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 #include <exception>
10 #include <stdexcept>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 
15 // CLI library includes
16 #include "StringTools.hpp"
17 
18 namespace CLI {
19 
20 // Use one of these on all error classes.
21 // These are temporary and are undef'd at the end of this file.
22 #define CLI11_ERROR_DEF(parent, name) \
23  protected: \
24  name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \
25  name(std::string ename, std::string msg, ExitCodes exit_code) \
26  : parent(std::move(ename), std::move(msg), exit_code) {} \
27  \
28  public: \
29  name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \
30  name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {}
31 
32 // This is added after the one above if a class is used directly and builds its own message
33 #define CLI11_ERROR_SIMPLE(name) \
34  explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}
35 
38 enum class ExitCodes {
39  Success = 0,
43  FileError,
55  BaseClass = 127
56 };
57 
58 // Error definitions
59 
65 
67 class Error : public std::runtime_error {
68  int actual_exit_code;
69  std::string error_name{"Error"};
70 
71  public:
72  int get_exit_code() const { return actual_exit_code; }
73 
74  std::string get_name() const { return error_name; }
75 
76  Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass))
77  : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {}
78 
79  Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {}
80 };
81 
82 // Note: Using Error::Error constructors does not work on GCC 4.7
83 
85 class ConstructionError : public Error {
87 };
88 
93  static IncorrectConstruction PositionalFlag(std::string name) {
94  return IncorrectConstruction(name + ": Flags cannot be positional");
95  }
96  static IncorrectConstruction Set0Opt(std::string name) {
97  return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
98  }
99  static IncorrectConstruction SetFlag(std::string name) {
100  return IncorrectConstruction(name + ": Cannot set an expected number for flags");
101  }
102  static IncorrectConstruction ChangeNotVector(std::string name) {
103  return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
104  }
105  static IncorrectConstruction AfterMultiOpt(std::string name) {
106  return IncorrectConstruction(
107  name + ": You can't change expected arguments after you've changed the multi option policy!");
108  }
109  static IncorrectConstruction MissingOption(std::string name) {
110  return IncorrectConstruction("Option " + name + " is not defined");
111  }
112  static IncorrectConstruction MultiOptionPolicy(std::string name) {
113  return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
114  }
115 };
116 
121  static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); }
122  static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); }
123  static BadNameString DashesOnly(std::string name) {
124  return BadNameString("Must have a name, not just dashes: " + name);
125  }
126  static BadNameString MultiPositionalNames(std::string name) {
127  return BadNameString("Only one positional name allowed, remove: " + name);
128  }
129 };
130 
134  explicit OptionAlreadyAdded(std::string name)
135  : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
136  static OptionAlreadyAdded Requires(std::string name, std::string other) {
137  return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded);
138  }
139  static OptionAlreadyAdded Excludes(std::string name, std::string other) {
140  return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded);
141  }
142 };
143 
144 // Parsing errors
145 
147 class ParseError : public Error {
149 };
150 
151 // Not really "errors"
152 
154 class Success : public ParseError {
156  Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {}
157 };
158 
160 class CallForHelp : public ParseError {
162  CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
163 };
164 
166 class CallForAllHelp : public ParseError {
169  : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
170 };
171 
173 class RuntimeError : public ParseError {
175  explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
176 };
177 
179 class FileError : public ParseError {
182  static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); }
183 };
184 
186 class ConversionError : public ParseError {
189  ConversionError(std::string member, std::string name)
190  : ConversionError("The value " + member + " is not an allowed value for " + name) {}
191  ConversionError(std::string name, std::vector<std::string> results)
192  : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
193  static ConversionError TooManyInputsFlag(std::string name) {
194  return ConversionError(name + ": too many inputs for a flag");
195  }
196  static ConversionError TrueFalse(std::string name) {
197  return ConversionError(name + ": Should be true/false or a number");
198  }
199 };
200 
202 class ValidationError : public ParseError {
205  explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
206 };
207 
209 class RequiredError : public ParseError {
211  explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
212  static RequiredError Subcommand(std::size_t min_subcom) {
213  if(min_subcom == 1) {
214  return RequiredError("A subcommand");
215  }
216  return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands",
218  }
219  static RequiredError
220  Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) {
221  if((min_option == 1) && (max_option == 1) && (used == 0))
222  return RequiredError("Exactly 1 option from [" + option_list + "]");
223  if((min_option == 1) && (max_option == 1) && (used > 1)) {
224  return RequiredError("Exactly 1 option from [" + option_list + "] is required and " + std::to_string(used) +
225  " were given",
227  }
228  if((min_option == 1) && (used == 0))
229  return RequiredError("At least 1 option from [" + option_list + "]");
230  if(used < min_option) {
231  return RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " +
232  std::to_string(used) + "were given from [" + option_list + "]",
234  }
235  if(max_option == 1)
236  return RequiredError("Requires at most 1 options be given from [" + option_list + "]",
238 
239  return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
240  std::to_string(used) + "were given from [" + option_list + "]",
242  }
243 };
244 
246 class ArgumentMismatch : public ParseError {
249  ArgumentMismatch(std::string name, int expected, std::size_t received)
250  : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
251  ", got " + std::to_string(received))
252  : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
253  ", got " + std::to_string(received)),
255 
256  static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) {
257  return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " +
258  std::to_string(received));
259  }
260  static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) {
261  return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " +
262  std::to_string(received));
263  }
264  static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
265  return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");
266  }
267  static ArgumentMismatch FlagOverride(std::string name) {
268  return ArgumentMismatch(name + " was given a disallowed flag override");
269  }
270 };
271 
273 class RequiresError : public ParseError {
275  RequiresError(std::string curname, std::string subname)
276  : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {}
277 };
278 
280 class ExcludesError : public ParseError {
282  ExcludesError(std::string curname, std::string subname)
283  : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {}
284 };
285 
287 class ExtrasError : public ParseError {
289  explicit ExtrasError(std::vector<std::string> args)
290  : ExtrasError((args.size() > 1 ? "The following arguments were not expected: "
291  : "The following argument was not expected: ") +
292  detail::rjoin(args, " "),
294  ExtrasError(const std::string &name, std::vector<std::string> args)
295  : ExtrasError(name,
296  (args.size() > 1 ? "The following arguments were not expected: "
297  : "The following argument was not expected: ") +
298  detail::rjoin(args, " "),
300 };
301 
303 class ConfigError : public ParseError {
306  static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); }
307  static ConfigError NotConfigurable(std::string item) {
308  return ConfigError(item + ": This option is not allowed in a configuration file");
309  }
310 };
311 
313 class InvalidError : public ParseError {
315  explicit InvalidError(std::string name)
316  : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) {
317  }
318 };
319 
322 class HorribleError : public ParseError {
325 };
326 
327 // After parsing
328 
330 class OptionNotFound : public Error {
332  explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
333 };
334 
335 #undef CLI11_ERROR_DEF
336 #undef CLI11_ERROR_SIMPLE
337 
339 
340 } // namespace CLI
CLI::FileError
Thrown when parsing an INI file and it is missing.
Definition: Error.hpp:179
CLI::ExitCodes::ArgumentMismatch
@ ArgumentMismatch
CLI::ExitCodes::ExtrasError
@ ExtrasError
CLI::detail::rjoin
std::string rjoin(const T &v, std::string delim=",")
Join a string in reverse order.
Definition: StringTools.hpp:88
CLI::ExitCodes::ConversionError
@ ConversionError
CLI::ExitCodes::BaseClass
@ BaseClass
CLI::ExcludesError
Thrown when an excludes option is present.
Definition: Error.hpp:280
CLI::Success
This is a successful completion on parsing, supposed to exit.
Definition: Error.hpp:154
CLI::CallForHelp
-h or –help on command line
Definition: Error.hpp:160
CLI::ParseError
Anything that can error in Parse.
Definition: Error.hpp:147
CLI::ExitCodes::Success
@ Success
CLI::Error::Error
Error(std::string name, std::string msg, ExitCodes exit_code)
Definition: Error.hpp:79
CLI::ExtrasError
Thrown when too many positionals or options are found.
Definition: Error.hpp:287
CLI::ExitCodes
ExitCodes
Definition: Error.hpp:38
CLI::ConversionError
Thrown when conversion call back fails, such as when an int fails to coerce to a string.
Definition: Error.hpp:186
CLI::ExitCodes::OptionAlreadyAdded
@ OptionAlreadyAdded
CLI::detail::join
std::string join(const T &v, std::string delim=",")
Simple function to join a string.
Definition: StringTools.hpp:59
CLI::ExitCodes::RequiresError
@ RequiresError
CLI::ExitCodes::FileError
@ FileError
CLI::MultiOptionPolicy
MultiOptionPolicy
Enumeration of the multiOption Policy selection.
Definition: Option.hpp:35
CLI::detail::to_string
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:226
CLI::Error::get_exit_code
int get_exit_code() const
Definition: Error.hpp:72
CLI::RuntimeError
Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error c...
Definition: Error.hpp:173
CLI::ExitCodes::RequiredError
@ RequiredError
CLI
Definition: App.hpp:32
CLI11_ERROR_DEF
#define CLI11_ERROR_DEF(parent, name)
Definition: Error.hpp:22
CLI::ExitCodes::OptionNotFound
@ OptionNotFound
CLI::Error::get_name
std::string get_name() const
Definition: Error.hpp:74
CLI::BadNameString
Thrown on construction of a bad name.
Definition: Error.hpp:118
CLI::ConstructionError
Construction errors (not in parsing)
Definition: Error.hpp:85
CLI::Error
All errors derive from this one.
Definition: Error.hpp:67
CLI::OptionAlreadyAdded
Thrown when an option already exists.
Definition: Error.hpp:132
CLI::Option
Definition: Option.hpp:231
CLI::ExitCodes::InvalidError
@ InvalidError
CLI::ExitCodes::BadNameString
@ BadNameString
CLI::ValidationError
Thrown when validation of results fails.
Definition: Error.hpp:202
StringTools.hpp
CLI::InvalidError
Thrown when validation fails before parsing.
Definition: Error.hpp:313
CLI::Error::Error
Error(std::string name, std::string msg, int exit_code=static_cast< int >(ExitCodes::BaseClass))
Definition: Error.hpp:76
CLI::ExitCodes::ConfigError
@ ConfigError
CLI::ExitCodes::ExcludesError
@ ExcludesError
CLI::CallForAllHelp
Usually something like –help-all on command line.
Definition: Error.hpp:166
CLI::ArgumentMismatch
Thrown when the wrong number of arguments has been received.
Definition: Error.hpp:246
CLI::HorribleError
Definition: Error.hpp:322
CLI::RequiredError
Thrown when a required option is missing.
Definition: Error.hpp:209
CLI11_ERROR_SIMPLE
#define CLI11_ERROR_SIMPLE(name)
Definition: Error.hpp:33
CLI::OptionNotFound
Thrown when counting a non-existent option.
Definition: Error.hpp:330
CLI::ConfigError
Thrown when extra values are found in an INI file.
Definition: Error.hpp:303
CLI::ExitCodes::ValidationError
@ ValidationError
CLI::IncorrectConstruction
Thrown when an option is set to conflicting values (non-vector and multi args, for example)
Definition: Error.hpp:90
CLI::RequiresError
Thrown when a requires option is missing.
Definition: Error.hpp:273
CLI::ExitCodes::HorribleError
@ HorribleError