CLI11
C++11 Command Line Interface Parser
Loading...
Searching...
No Matches
Error.hpp
1// Copyright (c) 2017-2026, 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
23namespace 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, std::move(msg), ExitCodes::name) {}
41
44enum class ExitCodes : int {
45 Success = 0,
46 IncorrectConstruction = 100,
47 BadNameString,
48 OptionAlreadyAdded,
49 FileError,
50 ConversionError,
51 ValidationError,
52 RequiredError,
53 RequiresError,
54 ExcludesError,
55 ExtrasError,
56 ConfigError,
57 InvalidError,
58 HorribleError,
59 OptionNotFound,
60 ArgumentMismatch,
61 BaseClass = 127
62};
63
64// Error definitions
65
71
73class 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)
86 : Error(std::move(name), std::move(msg), static_cast<int>(exit_code)) {}
87};
88
89// Note: Using Error::Error constructors does not work on GCC 4.7
90
92class ConstructionError : public Error {
93 CLI11_ERROR_DEF(Error, ConstructionError)
94};
95
99 CLI11_ERROR_SIMPLE(IncorrectConstruction)
100 static IncorrectConstruction PositionalFlag(std::string name) {
101 return IncorrectConstruction(name + ": Flags cannot be positional");
102 }
103 static IncorrectConstruction Set0Opt(std::string name) {
104 return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
105 }
106 static IncorrectConstruction SetFlag(std::string name) {
107 return IncorrectConstruction(name + ": Cannot set an expected number for flags");
108 }
109 static IncorrectConstruction ChangeNotVector(std::string name) {
110 return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
111 }
112 static IncorrectConstruction AfterMultiOpt(std::string name) {
114 name + ": You can't change expected arguments after you've changed the multi option policy!");
115 }
116 static IncorrectConstruction MissingOption(std::string name) {
117 return IncorrectConstruction("Option " + name + " is not defined");
118 }
119 static IncorrectConstruction MultiOptionPolicy(std::string name) {
120 return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
121 }
122};
123
126 CLI11_ERROR_DEF(ConstructionError, BadNameString)
127 CLI11_ERROR_SIMPLE(BadNameString)
128 static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); }
129 static BadNameString MissingDash(std::string name) {
130 return BadNameString("Long names strings require 2 dashes " + name);
131 }
132 static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); }
133 static BadNameString BadPositionalName(std::string name) {
134 return BadNameString("Invalid positional Name: " + name);
135 }
136 static BadNameString ReservedName(std::string name) {
137 return BadNameString("Names '-','--','++' are reserved and not allowed as option names " + name);
138 }
139 static BadNameString MultiPositionalNames(std::string name) {
140 return BadNameString("Only one positional name allowed, remove: " + name);
141 }
142};
143
146 CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)
147 explicit OptionAlreadyAdded(std::string name)
148 : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
149 static OptionAlreadyAdded Requires(std::string name, std::string other) {
150 return {name + " requires " + other, ExitCodes::OptionAlreadyAdded};
151 }
152 static OptionAlreadyAdded Excludes(std::string name, std::string other) {
153 return {name + " excludes " + other, ExitCodes::OptionAlreadyAdded};
154 }
155};
156
157// Parsing errors
158
160class ParseError : public Error {
161 CLI11_ERROR_DEF(Error, ParseError)
162};
163
164// Not really "errors"
165
167class Success : public ParseError {
168 CLI11_ERROR_DEF(ParseError, Success)
169 Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {}
170};
171
173class CallForHelp : public Success {
174 CLI11_ERROR_DEF(Success, CallForHelp)
175 CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
176};
177
179class CallForAllHelp : public Success {
180 CLI11_ERROR_DEF(Success, CallForAllHelp)
182 : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
183};
184
186class CallForVersion : public Success {
187 CLI11_ERROR_DEF(Success, CallForVersion)
189 : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {}
190};
191
193class RuntimeError : public ParseError {
194 CLI11_ERROR_DEF(ParseError, RuntimeError)
195 explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
196};
197
199class FileError : public ParseError {
200 CLI11_ERROR_DEF(ParseError, FileError)
201 CLI11_ERROR_SIMPLE(FileError)
202 static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); }
203};
204
207 CLI11_ERROR_DEF(ParseError, ConversionError)
208 CLI11_ERROR_SIMPLE(ConversionError)
209 ConversionError(std::string member, std::string name)
210 : ConversionError("The value " + member + " is not an allowed value for " + name) {}
211 ConversionError(std::string name, std::vector<std::string> results)
212 : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
213 static ConversionError TooManyInputsFlag(std::string name) {
214 return ConversionError(name + ": too many inputs for a flag");
215 }
216 static ConversionError TrueFalse(std::string name) {
217 return ConversionError(name + ": Should be true/false or a number");
218 }
219};
220
223 CLI11_ERROR_DEF(ParseError, ValidationError)
224 CLI11_ERROR_SIMPLE(ValidationError)
225 explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
226};
227
229class RequiredError : public ParseError {
230 CLI11_ERROR_DEF(ParseError, RequiredError)
231 explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
232 static RequiredError Subcommand(std::size_t min_subcom) {
233 if(min_subcom == 1) {
234 return RequiredError("A subcommand");
235 }
236 return {"Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError};
237 }
238 static RequiredError
239 Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) {
240 if((min_option == 1) && (max_option == 1) && (used == 0))
241 return RequiredError("Exactly 1 option from [" + option_list + "]");
242 if((min_option == 1) && (max_option == 1) && (used > 1)) {
243 return {"Exactly 1 option from [" + option_list + "] is required but " + std::to_string(used) +
244 " were given",
245 ExitCodes::RequiredError};
246 }
247 if((min_option == 1) && (used == 0))
248 return RequiredError("At least 1 option from [" + option_list + "]");
249 if(used < min_option) {
250 return {"Requires at least " + std::to_string(min_option) + " options used but only " +
251 std::to_string(used) + " were given from [" + option_list + "]",
252 ExitCodes::RequiredError};
253 }
254 if(max_option == 1)
255 return {"Requires at most 1 options be given from [" + option_list + "]", ExitCodes::RequiredError};
256
257 return {"Requires at most " + std::to_string(max_option) + " options be used but " + std::to_string(used) +
258 " were given from [" + option_list + "]",
259 ExitCodes::RequiredError};
260 }
261};
262
265 CLI11_ERROR_DEF(ParseError, ArgumentMismatch)
266 CLI11_ERROR_SIMPLE(ArgumentMismatch)
267 ArgumentMismatch(std::string name, int expected, std::size_t received)
268 : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
269 ", got " + std::to_string(received))
270 : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
271 ", got " + std::to_string(received)),
272 ExitCodes::ArgumentMismatch) {}
273
274 static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) {
275 return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " +
276 std::to_string(received));
277 }
278 static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) {
279 return ArgumentMismatch(name + ": At most " + std::to_string(num) + " required but received " +
280 std::to_string(received));
281 }
282 static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
283 return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");
284 }
285 static ArgumentMismatch FlagOverride(std::string name) {
286 return ArgumentMismatch(name + " was given a disallowed flag override");
287 }
288 static ArgumentMismatch PartialType(std::string name, int num, std::string type) {
289 return ArgumentMismatch(name + ": " + type + " only partially specified: " + std::to_string(num) +
290 " required for each element");
291 }
292};
293
295class RequiresError : public ParseError {
296 CLI11_ERROR_DEF(ParseError, RequiresError)
297 RequiresError(std::string curname, std::string subname)
298 : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {}
299};
300
302class ExcludesError : public ParseError {
303 CLI11_ERROR_DEF(ParseError, ExcludesError)
304 ExcludesError(std::string curname, std::string subname)
305 : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {}
306};
307
309class ExtrasError : public ParseError {
310 CLI11_ERROR_DEF(ParseError, ExtrasError)
311 explicit ExtrasError(std::vector<std::string> args)
312 : ExtrasError((args.size() > 1 ? "The following arguments were not expected: "
313 : "The following argument was not expected: ") +
314 detail::join(args, " "),
315 ExitCodes::ExtrasError) {}
316 ExtrasError(const std::string &name, std::vector<std::string> args)
317 : ExtrasError((name.empty() ? std::string{} : name + ": ") +
318 (args.size() > 1 ? "The following arguments were not expected: "
319 : "The following argument was not expected: ") +
320 detail::join(args, " "),
321 ExitCodes::ExtrasError) {}
322};
323
325class ConfigError : public ParseError {
326 CLI11_ERROR_DEF(ParseError, ConfigError)
327 CLI11_ERROR_SIMPLE(ConfigError)
328 static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); }
329 static ConfigError NotConfigurable(std::string item) {
330 return ConfigError(item + ": This option is not allowed in a configuration file");
331 }
332};
333
335class InvalidError : public ParseError {
336 CLI11_ERROR_DEF(ParseError, InvalidError)
337 explicit InvalidError(std::string name)
338 : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) {
339 }
340};
341
344class HorribleError : public ParseError {
345 CLI11_ERROR_DEF(ParseError, HorribleError)
346 CLI11_ERROR_SIMPLE(HorribleError)
347};
348
349// After parsing
350
352class OptionNotFound : public Error {
353 CLI11_ERROR_DEF(Error, OptionNotFound)
354 explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
355};
356
357#undef CLI11_ERROR_DEF
358#undef CLI11_ERROR_SIMPLE
359
361
362// [CLI11:error_hpp:end]
363} // namespace CLI
Thrown when the wrong number of arguments has been received.
Definition Error.hpp:264
Thrown on construction of a bad name.
Definition Error.hpp:125
Usually something like –help-all on command line.
Definition Error.hpp:179
-h or –help on command line
Definition Error.hpp:173
-v or –version on command line
Definition Error.hpp:186
Thrown when extra values are found in an INI file.
Definition Error.hpp:325
Construction errors (not in parsing)
Definition Error.hpp:92
Thrown when conversion call back fails, such as when an int fails to coerce to a string.
Definition Error.hpp:206
All errors derive from this one.
Definition Error.hpp:73
Thrown when an excludes option is present.
Definition Error.hpp:302
Thrown when too many positionals or options are found.
Definition Error.hpp:309
Thrown when parsing an INI file and it is missing.
Definition Error.hpp:199
Definition Error.hpp:344
Thrown when an option is set to conflicting values (non-vector and multi args, for example)
Definition Error.hpp:97
Thrown when validation fails before parsing.
Definition Error.hpp:335
Thrown when an option already exists.
Definition Error.hpp:145
Definition Option.hpp:259
Thrown when counting a nonexistent option.
Definition Error.hpp:352
Anything that can error in Parse.
Definition Error.hpp:160
Thrown when a required option is missing.
Definition Error.hpp:229
Thrown when a requires option is missing.
Definition Error.hpp:295
Does not output a diagnostic in CLI11_PARSE, but allows main() to return with a specific error code.
Definition Error.hpp:193
This is a successful completion on parsing, supposed to exit.
Definition Error.hpp:167
Thrown when validation of results fails.
Definition Error.hpp:222