Barretenberg
The ZK-SNARK library at the core of Aztec
Loading...
Searching...
No Matches
cli11.hpp
Go to the documentation of this file.
1// clang-format off
2
3// CLI11: Version 2.4.2
4// Originally designed by Henry Schreiner
5// https://github.com/CLIUtils/CLI11
6//
7// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
8// from: v2.4.2
9//
10// CLI11 2.4.2 Copyright (c) 2017-2024 University of Cincinnati, developed by Henry
11// Schreiner under NSF AWARD 1414736. All rights reserved.
12//
13// Redistribution and use in source and binary forms of CLI11, with or without
14// modification, are permitted provided that the following conditions are met:
15//
16// 1. Redistributions of source code must retain the above copyright notice, this
17// list of conditions and the following disclaimer.
18// 2. Redistributions in binary form must reproduce the above copyright notice,
19// this list of conditions and the following disclaimer in the documentation
20// and/or other materials provided with the distribution.
21// 3. Neither the name of the copyright holder nor the names of its contributors
22// may be used to endorse or promote products derived from this software without
23// specific prior written permission.
24//
25// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
29// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
36#pragma once
37
38// AZTEC-SPECIFIC HACK: Make this compile with -fno-exceptions.
40
41// Standard combined includes:
42#include <algorithm>
43#include <array>
44#include <cctype>
45#include <clocale>
46#include <cmath>
47#include <cstdint>
48#include <cstdlib>
49#include <cstring>
50#include <cwchar>
51#include <exception>
52#include <fstream>
53#include <functional>
54#include <iomanip>
55#include <iostream>
56#include <iterator>
57#include <limits>
58#include <locale>
59#include <map>
60#include <memory>
61#include <numeric>
62#include <set>
63#include <sstream>
64#include <stdexcept>
65#include <string>
66#include <tuple>
67#include <type_traits>
68#include <utility>
69#include <vector>
70
71
72#define CLI11_VERSION_MAJOR 2
73#define CLI11_VERSION_MINOR 4
74#define CLI11_VERSION_PATCH 2
75#define CLI11_VERSION "2.4.2"
76
77
78
79
80// The following version macro is very similar to the one in pybind11
81#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
82#if __cplusplus >= 201402L
83#define CLI11_CPP14
84#if __cplusplus >= 201703L
85#define CLI11_CPP17
86#if __cplusplus > 201703L
87#define CLI11_CPP20
88#endif
89#endif
90#endif
91#elif defined(_MSC_VER) && __cplusplus == 199711L
92// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is fully implemented)
93// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer
94#if _MSVC_LANG >= 201402L
95#define CLI11_CPP14
96#if _MSVC_LANG > 201402L && _MSC_VER >= 1910
97#define CLI11_CPP17
98#if _MSVC_LANG > 201703L && _MSC_VER >= 1910
99#define CLI11_CPP20
100#endif
101#endif
102#endif
103#endif
104
105#if defined(CLI11_CPP14)
106#define CLI11_DEPRECATED(reason) [[deprecated(reason)]]
107#elif defined(_MSC_VER)
108#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason))
109#else
110#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))
111#endif
112
113// GCC < 10 doesn't ignore this in unevaluated contexts
114#if !defined(CLI11_CPP17) || \
115 (defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 10 && __GNUC__ > 4)
116#define CLI11_NODISCARD
117#else
118#define CLI11_NODISCARD [[nodiscard]]
119#endif
120
122#ifndef CLI11_USE_STATIC_RTTI
123#if(defined(_HAS_STATIC_RTTI) && _HAS_STATIC_RTTI)
124#define CLI11_USE_STATIC_RTTI 1
125#elif defined(__cpp_rtti)
126#if(defined(_CPPRTTI) && _CPPRTTI == 0)
127#define CLI11_USE_STATIC_RTTI 1
128#else
129#define CLI11_USE_STATIC_RTTI 0
130#endif
131#elif(defined(__GCC_RTTI) && __GXX_RTTI)
132#define CLI11_USE_STATIC_RTTI 0
133#else
134#define CLI11_USE_STATIC_RTTI 1
135#endif
136#endif
137
139#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
140#if __has_include(<filesystem>)
141// Filesystem cannot be used if targeting macOS < 10.15
142#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
143#define CLI11_HAS_FILESYSTEM 0
144#elif defined(__wasi__)
145// As of wasi-sdk-14, filesystem is not implemented
146#define CLI11_HAS_FILESYSTEM 0
147#else
148#include <filesystem>
149#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
150#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
151#define CLI11_HAS_FILESYSTEM 1
152#elif defined(__GLIBCXX__)
153// if we are using gcc and Version <9 default to no filesystem
154#define CLI11_HAS_FILESYSTEM 0
155#else
156#define CLI11_HAS_FILESYSTEM 1
157#endif
158#else
159#define CLI11_HAS_FILESYSTEM 0
160#endif
161#endif
162#endif
163#endif
164
166#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 5
167#define CLI11_HAS_CODECVT 0
168#else
169#define CLI11_HAS_CODECVT 1
170#include <codecvt>
171#endif
172
174#if defined(__GNUC__) // GCC or clang
175#define CLI11_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
176#define CLI11_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
177
178#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
179
180#elif defined(_MSC_VER)
181#define CLI11_DIAGNOSTIC_PUSH __pragma(warning(push))
182#define CLI11_DIAGNOSTIC_POP __pragma(warning(pop))
183
184#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED __pragma(warning(disable : 4996))
185
186#else
187#define CLI11_DIAGNOSTIC_PUSH
188#define CLI11_DIAGNOSTIC_POP
189
190#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
191
192#endif
193
195#ifdef CLI11_COMPILE
196#define CLI11_INLINE
197#else
198#define CLI11_INLINE inline
199#endif
200
201
202
203#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
204#include <filesystem> // NOLINT(build/include)
205#else
206#include <sys/stat.h>
207#include <sys/types.h>
208#endif
209
210
211
212
213#ifdef CLI11_CPP17
214#include <string_view>
215#endif // CLI11_CPP17
216
217#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
218#include <filesystem>
219#include <string_view> // NOLINT(build/include)
220#endif // CLI11_HAS_FILESYSTEM
221
222
223
224#if defined(_WIN32)
225#if !(defined(_AMD64_) || defined(_X86_) || defined(_ARM_))
226#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || \
227 defined(_M_AMD64)
228#define _AMD64_
229#elif defined(i386) || defined(__i386) || defined(__i386__) || defined(__i386__) || defined(_M_IX86)
230#define _X86_
231#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARMT)
232#define _ARM_
233#elif defined(__aarch64__) || defined(_M_ARM64)
234#define _ARM64_
235#elif defined(_M_ARM64EC)
236#define _ARM64EC_
237#endif
238#endif
239
240// first
241#ifndef NOMINMAX
242// if NOMINMAX is already defined we don't want to mess with that either way
243#define NOMINMAX
244#include <windef.h>
245#undef NOMINMAX
246#else
247#include <windef.h>
248#endif
249
250// second
251#include <winbase.h>
252// third
253#include <processthreadsapi.h>
254#include <shellapi.h>
255#endif
256
257
258namespace CLI {
259
260
262CLI11_INLINE std::string narrow(const std::wstring &str);
263CLI11_INLINE std::string narrow(const wchar_t *str);
264CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t size);
265
267CLI11_INLINE std::wstring widen(const std::string &str);
268CLI11_INLINE std::wstring widen(const char *str);
269CLI11_INLINE std::wstring widen(const char *str, std::size_t size);
270
271#ifdef CLI11_CPP17
272CLI11_INLINE std::string narrow(std::wstring_view str);
273CLI11_INLINE std::wstring widen(std::string_view str);
274#endif // CLI11_CPP17
275
276#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
278CLI11_INLINE std::filesystem::path to_path(std::string_view str);
279#endif // CLI11_HAS_FILESYSTEM
280
281
282
283
284namespace detail {
285
286#if !CLI11_HAS_CODECVT
288CLI11_INLINE void set_unicode_locale() {
289 static const std::array<const char *, 3> unicode_locales{{"C.UTF-8", "en_US.UTF-8", ".UTF-8"}};
290
291 for(const auto &locale_name : unicode_locales) {
292 if(std::setlocale(LC_ALL, locale_name) != nullptr) {
293 return;
294 }
295 }
296 THROW std::runtime_error("CLI::narrow: could not set locale to C.UTF-8");
297}
298
299template <typename F> struct scope_guard_t {
300 F closure;
301
302 explicit scope_guard_t(F closure_) : closure(closure_) {}
303 ~scope_guard_t() { closure(); }
304};
305
306template <typename F> CLI11_NODISCARD CLI11_INLINE scope_guard_t<F> scope_guard(F &&closure) {
307 return scope_guard_t<F>{std::forward<F>(closure)};
308}
309
310#endif // !CLI11_HAS_CODECVT
311
314
315CLI11_INLINE std::string narrow_impl(const wchar_t *str, std::size_t str_size) {
316#if CLI11_HAS_CODECVT
317#ifdef _WIN32
318 return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(str, str + str_size);
319
320#else
321 return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
322
323#endif // _WIN32
324#else // CLI11_HAS_CODECVT
325 (void)str_size;
327 const wchar_t *it = str;
328
329 std::string old_locale = std::setlocale(LC_ALL, nullptr);
330 auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
331 set_unicode_locale();
332
333 std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state);
334 if(new_size == static_cast<std::size_t>(-1)) {
335 THROW std::runtime_error("CLI::narrow: conversion error in std::wcsrtombs at offset " +
336 std::to_string(it - str));
337 }
338 std::string result(new_size, '\0');
339 std::wcsrtombs(const_cast<char *>(result.data()), &str, new_size, &state);
340
341 return result;
342
343#endif // CLI11_HAS_CODECVT
344}
345
346CLI11_INLINE std::wstring widen_impl(const char *str, std::size_t str_size) {
347#if CLI11_HAS_CODECVT
348#ifdef _WIN32
349 return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(str, str + str_size);
350
351#else
352 return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
353
354#endif // _WIN32
355#else // CLI11_HAS_CODECVT
356 (void)str_size;
358 const char *it = str;
359
360 std::string old_locale = std::setlocale(LC_ALL, nullptr);
361 auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
362 set_unicode_locale();
363
364 std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state);
365 if(new_size == static_cast<std::size_t>(-1)) {
366 THROW std::runtime_error("CLI::widen: conversion error in std::mbsrtowcs at offset " +
367 std::to_string(it - str));
368 }
369 std::wstring result(new_size, L'\0');
370 std::mbsrtowcs(const_cast<wchar_t *>(result.data()), &str, new_size, &state);
371
372 return result;
373
374#endif // CLI11_HAS_CODECVT
375}
376
378
379} // namespace detail
380
381CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { return detail::narrow_impl(str, str_size); }
382CLI11_INLINE std::string narrow(const std::wstring &str) { return detail::narrow_impl(str.data(), str.size()); }
383// Flawfinder: ignore
384CLI11_INLINE std::string narrow(const wchar_t *str) { return detail::narrow_impl(str, std::wcslen(str)); }
385
386CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { return detail::widen_impl(str, str_size); }
387CLI11_INLINE std::wstring widen(const std::string &str) { return detail::widen_impl(str.data(), str.size()); }
388// Flawfinder: ignore
389CLI11_INLINE std::wstring widen(const char *str) { return detail::widen_impl(str, std::strlen(str)); }
390
391#ifdef CLI11_CPP17
392CLI11_INLINE std::string narrow(std::wstring_view str) { return detail::narrow_impl(str.data(), str.size()); }
393CLI11_INLINE std::wstring widen(std::string_view str) { return detail::widen_impl(str.data(), str.size()); }
394#endif // CLI11_CPP17
395
396#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
397CLI11_INLINE std::filesystem::path to_path(std::string_view str) {
398 return std::filesystem::path{
399#ifdef _WIN32
400 widen(str)
401#else
402 str
403#endif // _WIN32
404 };
405}
406#endif // CLI11_HAS_FILESYSTEM
407
408
409
410
411namespace detail {
412#ifdef _WIN32
414CLI11_INLINE std::vector<std::string> compute_win32_argv();
415#endif
416} // namespace detail
417
418
419
420namespace detail {
421
422#ifdef _WIN32
423CLI11_INLINE std::vector<std::string> compute_win32_argv() {
424 std::vector<std::string> result;
425 int argc = 0;
426
427 auto deleter = [](wchar_t **ptr) { LocalFree(ptr); };
428 // NOLINTBEGIN(*-avoid-c-arrays)
429 auto wargv = std::unique_ptr<wchar_t *[], decltype(deleter)>(CommandLineToArgvW(GetCommandLineW(), &argc), deleter);
430 // NOLINTEND(*-avoid-c-arrays)
431
432 if(wargv == nullptr) {
433 THROW std::runtime_error("CommandLineToArgvW failed with code " + std::to_string(GetLastError()));
434 }
435
436 result.reserve(static_cast<size_t>(argc));
437 for(size_t i = 0; i < static_cast<size_t>(argc); ++i) {
438 result.push_back(narrow(wargv[i]));
439 }
440
441 return result;
442}
443#endif
444
445} // namespace detail
446
447
448
449
452namespace enums {
453
455template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
456std::ostream &operator<<(std::ostream &in, const T &item) {
457 // make sure this is out of the detail namespace otherwise it won't be found when needed
458 return in << static_cast<typename std::underlying_type<T>::type>(item);
459}
460
461} // namespace enums
462
464using enums::operator<<;
465
466namespace detail {
469constexpr int expected_max_vector_size{1 << 29};
470// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
472CLI11_INLINE std::vector<std::string> split(const std::string &s, char delim);
473
475template <typename T> std::string join(const T &v, std::string delim = ",") {
476 std::ostringstream s;
477 auto beg = std::begin(v);
478 auto end = std::end(v);
479 if(beg != end)
480 s << *beg++;
481 while(beg != end) {
482 s << delim << *beg++;
483 }
484 return s.str();
485}
486
488template <typename T,
489 typename Callable,
491std::string join(const T &v, Callable func, std::string delim = ",") {
492 std::ostringstream s;
493 auto beg = std::begin(v);
494 auto end = std::end(v);
495 auto loc = s.tellp();
496 while(beg != end) {
497 auto nloc = s.tellp();
498 if(nloc > loc) {
499 s << delim;
500 loc = nloc;
501 }
502 s << func(*beg++);
503 }
504 return s.str();
505}
506
508template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
509 std::ostringstream s;
510 for(std::size_t start = 0; start < v.size(); start++) {
511 if(start > 0)
512 s << delim;
513 s << v[v.size() - start - 1];
514 }
515 return s.str();
516}
517
518// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
519
521CLI11_INLINE std::string &ltrim(std::string &str);
522
524CLI11_INLINE std::string &ltrim(std::string &str, const std::string &filter);
525
527CLI11_INLINE std::string &rtrim(std::string &str);
528
530CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter);
531
533inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }
534
536inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }
537
539inline std::string trim_copy(const std::string &str) {
540 std::string s = str;
541 return trim(s);
542}
543
545CLI11_INLINE std::string &remove_quotes(std::string &str);
546
548CLI11_INLINE void remove_quotes(std::vector<std::string> &args);
549
554CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input);
555
557inline std::string trim_copy(const std::string &str, const std::string &filter) {
558 std::string s = str;
559 return trim(s, filter);
560}
562CLI11_INLINE std::ostream &
563format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid);
564
566CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid);
567
570template <typename T> bool valid_first_char(T c) {
571 return ((c != '-') && (static_cast<unsigned char>(c) > 33)); // space and '!' not allowed
572}
573
575template <typename T> bool valid_later_char(T c) {
576 // = and : are value separators, { has special meaning for option defaults,
577 // and control codes other than tab would just be annoying to deal with in many places allowing space here has too
578 // much potential for inadvertent entry errors and bugs
579 return ((c != '=') && (c != ':') && (c != '{') && ((static_cast<unsigned char>(c) > 32) || c == '\t'));
580}
581
583CLI11_INLINE bool valid_name_string(const std::string &str);
584
586inline bool valid_alias_name_string(const std::string &str) {
587 static const std::string badChars(std::string("\n") + '\0');
588 return (str.find_first_of(badChars) == std::string::npos);
589}
590
592inline bool is_separator(const std::string &str) {
593 static const std::string sep("%%");
594 return (str.empty() || str == sep);
595}
596
598inline bool isalpha(const std::string &str) {
599 return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); });
600}
601
603inline std::string to_lower(std::string str) {
604 std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
605 return std::tolower(x, std::locale());
606 });
607 return str;
608}
609
611inline std::string remove_underscore(std::string str) {
612 str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));
613 return str;
614}
615
617CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to);
618
620inline bool has_default_flag_values(const std::string &flags) {
621 return (flags.find_first_of("{!") != std::string::npos);
622}
623
624CLI11_INLINE void remove_default_flag_values(std::string &flags);
625
627CLI11_INLINE std::ptrdiff_t find_member(std::string name,
628 const std::vector<std::string> names,
629 bool ignore_case = false,
630 bool ignore_underscore = false);
631
634template <typename Callable> inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) {
635 std::size_t start_pos = 0;
636 while((start_pos = str.find(trigger, start_pos)) != std::string::npos) {
637 start_pos = modify(str, start_pos);
638 }
639 return str;
640}
641
644CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char);
645
648CLI11_INLINE std::vector<std::string> split_up(std::string str, char delimiter = '\0');
649
651CLI11_INLINE std::string get_environment_value(const std::string &env_name);
652
657CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset);
658
662CLI11_INLINE bool has_escapable_character(const std::string &str);
663
667CLI11_INLINE std::string add_escaped_characters(const std::string &str);
668
670CLI11_INLINE std::string remove_escaped_characters(const std::string &str);
671
673CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape);
674
675CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string);
676
678CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string);
679
681CLI11_INLINE bool process_quoted_string(std::string &str, char string_char = '\"', char literal_char = '\'');
682
683} // namespace detail
684
685
686
687
688namespace detail {
689CLI11_INLINE std::vector<std::string> split(const std::string &s, char delim) {
690 std::vector<std::string> elems;
691 // Check to see if empty string, give consistent result
692 if(s.empty()) {
693 elems.emplace_back();
694 } else {
695 std::stringstream ss;
696 ss.str(s);
697 std::string item;
698 while(std::getline(ss, item, delim)) {
699 elems.push_back(item);
700 }
701 }
702 return elems;
703}
704
705CLI11_INLINE std::string &ltrim(std::string &str) {
706 auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
707 str.erase(str.begin(), it);
708 return str;
709}
710
711CLI11_INLINE std::string &ltrim(std::string &str, const std::string &filter) {
712 auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
713 str.erase(str.begin(), it);
714 return str;
715}
716
717CLI11_INLINE std::string &rtrim(std::string &str) {
718 auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
719 str.erase(it.base(), str.end());
720 return str;
721}
722
723CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) {
724 auto it =
725 std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
726 str.erase(it.base(), str.end());
727 return str;
728}
729
730CLI11_INLINE std::string &remove_quotes(std::string &str) {
731 if(str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) {
732 if(str.front() == str.back()) {
733 str.pop_back();
734 str.erase(str.begin(), str.begin() + 1);
735 }
736 }
737 return str;
738}
739
740CLI11_INLINE std::string &remove_outer(std::string &str, char key) {
741 if(str.length() > 1 && (str.front() == key)) {
742 if(str.front() == str.back()) {
743 str.pop_back();
744 str.erase(str.begin(), str.begin() + 1);
745 }
746 }
747 return str;
748}
749
750CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input) {
751 std::string::size_type n = 0;
752 while(n != std::string::npos && n < input.size()) {
753 n = input.find('\n', n);
754 if(n != std::string::npos) {
755 input = input.substr(0, n + 1) + leader + input.substr(n + 1);
756 n += leader.size();
757 }
758 }
759 return input;
760}
761
762CLI11_INLINE std::ostream &
763format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid) {
764 name = " " + name;
765 out << std::setw(static_cast<int>(wid)) << std::left << name;
766 if(!description.empty()) {
767 if(name.length() >= wid)
768 out << "\n" << std::setw(static_cast<int>(wid)) << "";
769 for(const char c : description) {
770 out.put(c);
771 if(c == '\n') {
772 out << std::setw(static_cast<int>(wid)) << "";
773 }
774 }
775 }
776 out << "\n";
777 return out;
778}
779
780CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid) {
781 if(!aliases.empty()) {
782 out << std::setw(static_cast<int>(wid)) << " aliases: ";
783 bool front = true;
784 for(const auto &alias : aliases) {
785 if(!front) {
786 out << ", ";
787 } else {
788 front = false;
789 }
790 out << detail::fix_newlines(" ", alias);
791 }
792 out << "\n";
793 }
794 return out;
795}
796
797CLI11_INLINE bool valid_name_string(const std::string &str) {
798 if(str.empty() || !valid_first_char(str[0])) {
799 return false;
800 }
801 auto e = str.end();
802 for(auto c = str.begin() + 1; c != e; ++c)
803 if(!valid_later_char(*c))
804 return false;
805 return true;
806}
807
808CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) {
809
810 std::size_t start_pos = 0;
811
812 while((start_pos = str.find(from, start_pos)) != std::string::npos) {
813 str.replace(start_pos, from.length(), to);
814 start_pos += to.length();
815 }
816
817 return str;
818}
819
820CLI11_INLINE void remove_default_flag_values(std::string &flags) {
821 auto loc = flags.find_first_of('{', 2);
822 while(loc != std::string::npos) {
823 auto finish = flags.find_first_of("},", loc + 1);
824 if((finish != std::string::npos) && (flags[finish] == '}')) {
825 flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
826 flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
827 }
828 loc = flags.find_first_of('{', loc + 1);
829 }
830 flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
831}
832
834find_member(std::string name, const std::vector<std::string> names, bool ignore_case, bool ignore_underscore) {
835 auto it = std::end(names);
836 if(ignore_case) {
837 if(ignore_underscore) {
838 name = detail::to_lower(detail::remove_underscore(name));
839 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
840 return detail::to_lower(detail::remove_underscore(local_name)) == name;
841 });
842 } else {
843 name = detail::to_lower(name);
844 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
845 return detail::to_lower(local_name) == name;
846 });
847 }
848
849 } else if(ignore_underscore) {
850 name = detail::remove_underscore(name);
851 it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
852 return detail::remove_underscore(local_name) == name;
853 });
854 } else {
855 it = std::find(std::begin(names), std::end(names), name);
856 }
857
858 return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
859}
860
861static const std::string escapedChars("\b\t\n\f\r\"\\");
862static const std::string escapedCharsCode("btnfr\"\\");
863static const std::string bracketChars{"\"'`[(<{"};
864static const std::string matchBracketChars("\"'`])>}");
865
866CLI11_INLINE bool has_escapable_character(const std::string &str) {
867 return (str.find_first_of(escapedChars) != std::string::npos);
868}
869
870CLI11_INLINE std::string add_escaped_characters(const std::string &str) {
871 std::string out;
872 out.reserve(str.size() + 4);
873 for(char s : str) {
874 auto sloc = escapedChars.find_first_of(s);
875 if(sloc != std::string::npos) {
876 out.push_back('\\');
877 out.push_back(escapedCharsCode[sloc]);
878 } else {
879 out.push_back(s);
880 }
881 }
882 return out;
883}
884
885CLI11_INLINE std::uint32_t hexConvert(char hc) {
886 int hcode{0};
887 if(hc >= '0' && hc <= '9') {
888 hcode = (hc - '0');
889 } else if(hc >= 'A' && hc <= 'F') {
890 hcode = (hc - 'A' + 10);
891 } else if(hc >= 'a' && hc <= 'f') {
892 hcode = (hc - 'a' + 10);
893 } else {
894 hcode = -1;
895 }
896 return static_cast<uint32_t>(hcode);
897}
898
899CLI11_INLINE char make_char(std::uint32_t code) { return static_cast<char>(static_cast<unsigned char>(code)); }
900
901CLI11_INLINE void append_codepoint(std::string &str, std::uint32_t code) {
902 if(code < 0x80) { // ascii code equivalent
903 str.push_back(static_cast<char>(code));
904 } else if(code < 0x800) { // \u0080 to \u07FF
905 // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111
906 str.push_back(make_char(0xC0 | code >> 6));
907 str.push_back(make_char(0x80 | (code & 0x3F)));
908 } else if(code < 0x10000) { // U+0800...U+FFFF
909 if(0xD800 <= code && code <= 0xDFFF) {
910 THROW std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8.");
911 }
912 // 1110yyyy 10yxxxxx 10xxxxxx
913 str.push_back(make_char(0xE0 | code >> 12));
914 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
915 str.push_back(make_char(0x80 | (code & 0x3F)));
916 } else if(code < 0x110000) { // U+010000 ... U+10FFFF
917 // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
918 str.push_back(make_char(0xF0 | code >> 18));
919 str.push_back(make_char(0x80 | (code >> 12 & 0x3F)));
920 str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
921 str.push_back(make_char(0x80 | (code & 0x3F)));
922 }
923}
924
925CLI11_INLINE std::string remove_escaped_characters(const std::string &str) {
926
927 std::string out;
928 out.reserve(str.size());
929 for(auto loc = str.begin(); loc < str.end(); ++loc) {
930 if(*loc == '\\') {
931 if(str.end() - loc < 2) {
932 THROW std::invalid_argument("invalid escape sequence " + str);
933 }
934 auto ecloc = escapedCharsCode.find_first_of(*(loc + 1));
935 if(ecloc != std::string::npos) {
936 out.push_back(escapedChars[ecloc]);
937 ++loc;
938 } else if(*(loc + 1) == 'u') {
939 // must have 4 hex characters
940 if(str.end() - loc < 6) {
941 THROW std::invalid_argument("unicode sequence must have 4 hex codes " + str);
942 }
943 std::uint32_t code{0};
944 std::uint32_t mplier{16 * 16 * 16};
945 for(int ii = 2; ii < 6; ++ii) {
946 std::uint32_t res = hexConvert(*(loc + ii));
947 if(res > 0x0F) {
948 THROW std::invalid_argument("unicode sequence must have 4 hex codes " + str);
949 }
950 code += res * mplier;
951 mplier = mplier / 16;
952 }
953 append_codepoint(out, code);
954 loc += 5;
955 } else if(*(loc + 1) == 'U') {
956 // must have 8 hex characters
957 if(str.end() - loc < 10) {
958 THROW std::invalid_argument("unicode sequence must have 8 hex codes " + str);
959 }
960 std::uint32_t code{0};
961 std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16};
962 for(int ii = 2; ii < 10; ++ii) {
963 std::uint32_t res = hexConvert(*(loc + ii));
964 if(res > 0x0F) {
965 THROW std::invalid_argument("unicode sequence must have 8 hex codes " + str);
966 }
967 code += res * mplier;
968 mplier = mplier / 16;
969 }
970 append_codepoint(out, code);
971 loc += 9;
972 } else if(*(loc + 1) == '0') {
973 out.push_back('\0');
974 ++loc;
975 } else {
976 THROW std::invalid_argument(std::string("unrecognized escape sequence \\") + *(loc + 1) + " in " + str);
977 }
978 } else {
979 out.push_back(*loc);
980 }
981 }
982 return out;
983}
984
985CLI11_INLINE std::size_t close_string_quote(const std::string &str, std::size_t start, char closure_char) {
986 std::size_t loc{0};
987 for(loc = start + 1; loc < str.size(); ++loc) {
988 if(str[loc] == closure_char) {
989 break;
990 }
991 if(str[loc] == '\\') {
992 // skip the next character for escaped sequences
993 ++loc;
994 }
995 }
996 return loc;
997}
998
999CLI11_INLINE std::size_t close_literal_quote(const std::string &str, std::size_t start, char closure_char) {
1000 auto loc = str.find_first_of(closure_char, start + 1);
1001 return (loc != std::string::npos ? loc : str.size());
1002}
1003
1004CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char) {
1005
1006 auto bracket_loc = matchBracketChars.find(closure_char);
1007 switch(bracket_loc) {
1008 case 0:
1009 return close_string_quote(str, start, closure_char);
1010 case 1:
1011 case 2:
1012 case std::string::npos:
1013 return close_literal_quote(str, start, closure_char);
1014 default:
1015 break;
1016 }
1017
1018 std::string closures(1, closure_char);
1019 auto loc = start + 1;
1020
1021 while(loc < str.size()) {
1022 if(str[loc] == closures.back()) {
1023 closures.pop_back();
1024 if(closures.empty()) {
1025 return loc;
1026 }
1027 }
1028 bracket_loc = bracketChars.find(str[loc]);
1029 if(bracket_loc != std::string::npos) {
1030 switch(bracket_loc) {
1031 case 0:
1032 loc = close_string_quote(str, loc, str[loc]);
1033 break;
1034 case 1:
1035 case 2:
1036 loc = close_literal_quote(str, loc, str[loc]);
1037 break;
1038 default:
1039 closures.push_back(matchBracketChars[bracket_loc]);
1040 break;
1041 }
1042 }
1043 ++loc;
1044 }
1045 if(loc > str.size()) {
1046 loc = str.size();
1047 }
1048 return loc;
1049}
1050
1051CLI11_INLINE std::vector<std::string> split_up(std::string str, char delimiter) {
1052
1053 auto find_ws = [delimiter](char ch) {
1054 return (delimiter == '\0') ? std::isspace<char>(ch, std::locale()) : (ch == delimiter);
1055 };
1056 trim(str);
1057
1058 std::vector<std::string> output;
1059 while(!str.empty()) {
1060 if(bracketChars.find_first_of(str[0]) != std::string::npos) {
1061 auto bracketLoc = bracketChars.find_first_of(str[0]);
1062 auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]);
1063 if(end >= str.size()) {
1064 output.push_back(std::move(str));
1065 str.clear();
1066 } else {
1067 output.push_back(str.substr(0, end + 1));
1068 if(end + 2 < str.size()) {
1069 str = str.substr(end + 2);
1070 } else {
1071 str.clear();
1072 }
1073 }
1074
1075 } else {
1076 auto it = std::find_if(std::begin(str), std::end(str), find_ws);
1077 if(it != std::end(str)) {
1078 std::string value = std::string(str.begin(), it);
1079 output.push_back(value);
1080 str = std::string(it + 1, str.end());
1081 } else {
1082 output.push_back(str);
1083 str.clear();
1084 }
1085 }
1086 trim(str);
1087 }
1088 return output;
1089}
1090
1091CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) {
1092 auto next = str[offset + 1];
1093 if((next == '\"') || (next == '\'') || (next == '`')) {
1094 auto astart = str.find_last_of("-/ \"\'`", offset - 1);
1095 if(astart != std::string::npos) {
1096 if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
1097 str[offset] = ' '; // interpret this as a space so the split_up works properly
1098 }
1099 }
1100 return offset + 1;
1101}
1102
1103CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape) {
1104 // s is our escaped output string
1105 std::string escaped_string{};
1106 // loop through all characters
1107 for(char c : string_to_escape) {
1108 // check if a given character is printable
1109 // the cast is necessary to avoid undefined behaviour
1110 if(isprint(static_cast<unsigned char>(c)) == 0) {
1111 std::stringstream stream;
1112 // if the character is not printable
1113 // we'll convert it to a hex string using a stringstream
1114 // note that since char is signed we have to cast it to unsigned first
1115 stream << std::hex << static_cast<unsigned int>(static_cast<unsigned char>(c));
1116 std::string code = stream.str();
1117 escaped_string += std::string("\\x") + (code.size() < 2 ? "0" : "") + code;
1118
1119 } else {
1120 escaped_string.push_back(c);
1121 }
1122 }
1123 if(escaped_string != string_to_escape) {
1124 auto sqLoc = escaped_string.find('\'');
1125 while(sqLoc != std::string::npos) {
1126 escaped_string.replace(sqLoc, sqLoc + 1, "\\x27");
1127 sqLoc = escaped_string.find('\'');
1128 }
1129 escaped_string.insert(0, "'B\"(");
1130 escaped_string.push_back(')');
1131 escaped_string.push_back('"');
1132 escaped_string.push_back('\'');
1133 }
1134 return escaped_string;
1135}
1136
1137CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string) {
1138 size_t ssize = escaped_string.size();
1139 if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
1140 return true;
1141 }
1142 return (escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0);
1143}
1144
1145CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string) {
1146 std::size_t start{0};
1147 std::size_t tail{0};
1148 size_t ssize = escaped_string.size();
1149 if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
1150 start = 3;
1151 tail = 2;
1152 } else if(escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0) {
1153 start = 4;
1154 tail = 3;
1155 }
1156
1157 if(start == 0) {
1158 return escaped_string;
1159 }
1160 std::string outstring;
1161
1162 outstring.reserve(ssize - start - tail);
1163 std::size_t loc = start;
1164 while(loc < ssize - tail) {
1165 // ssize-2 to skip )" at the end
1166 if(escaped_string[loc] == '\\' && (escaped_string[loc + 1] == 'x' || escaped_string[loc + 1] == 'X')) {
1167 auto c1 = escaped_string[loc + 2];
1168 auto c2 = escaped_string[loc + 3];
1169
1170 std::uint32_t res1 = hexConvert(c1);
1171 std::uint32_t res2 = hexConvert(c2);
1172 if(res1 <= 0x0F && res2 <= 0x0F) {
1173 loc += 4;
1174 outstring.push_back(static_cast<char>(res1 * 16 + res2));
1175 continue;
1176 }
1177 }
1178 outstring.push_back(escaped_string[loc]);
1179 ++loc;
1180 }
1181 return outstring;
1182}
1183
1184CLI11_INLINE void remove_quotes(std::vector<std::string> &args) {
1185 for(auto &arg : args) {
1186 if(arg.front() == '\"' && arg.back() == '\"') {
1187 remove_quotes(arg);
1188 // only remove escaped for string arguments not literal strings
1189 arg = remove_escaped_characters(arg);
1190 } else {
1191 remove_quotes(arg);
1192 }
1193 }
1194}
1195
1196CLI11_INLINE bool process_quoted_string(std::string &str, char string_char, char literal_char) {
1197 if(str.size() <= 1) {
1198 return false;
1199 }
1200 if(detail::is_binary_escaped_string(str)) {
1201 str = detail::extract_binary_string(str);
1202 return true;
1203 }
1204 if(str.front() == string_char && str.back() == string_char) {
1205 detail::remove_outer(str, string_char);
1206 if(str.find_first_of('\\') != std::string::npos) {
1207 str = detail::remove_escaped_characters(str);
1208 }
1209 return true;
1210 }
1211 if((str.front() == literal_char || str.front() == '`') && str.back() == str.front()) {
1212 detail::remove_outer(str, str.front());
1213 return true;
1214 }
1215 return false;
1216}
1217
1218std::string get_environment_value(const std::string &env_name) {
1219 char *buffer = nullptr;
1220 std::string ename_string;
1221
1222#ifdef _MSC_VER
1223 // Windows version
1224 std::size_t sz = 0;
1225 if(_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer != nullptr) {
1226 ename_string = std::string(buffer);
1227 free(buffer);
1228 }
1229#else
1230 // This also works on Windows, but gives a warning
1231 buffer = std::getenv(env_name.c_str());
1232 if(buffer != nullptr) {
1233 ename_string = std::string(buffer);
1234 }
1235#endif
1236 return ename_string;
1237}
1238
1239} // namespace detail
1240
1241
1242
1243// Use one of these on all error classes.
1244// These are temporary and are undef'd at the end of this file.
1245#define CLI11_ERROR_DEF(parent, name) \
1246 protected: \
1247 name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \
1248 name(std::string ename, std::string msg, ExitCodes exit_code) \
1249 : parent(std::move(ename), std::move(msg), exit_code) {} \
1250 \
1251 public: \
1252 name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \
1253 name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {}
1254
1255// This is added after the one above if a class is used directly and builds its own message
1256#define CLI11_ERROR_SIMPLE(name) \
1257 explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}
1258
1261enum class ExitCodes {
1262 Success = 0,
1263 IncorrectConstruction = 100,
1264 BadNameString,
1265 OptionAlreadyAdded,
1266 FileError,
1267 ConversionError,
1268 ValidationError,
1269 RequiredError,
1270 RequiresError,
1271 ExcludesError,
1272 ExtrasError,
1273 ConfigError,
1274 InvalidError,
1275 HorribleError,
1276 OptionNotFound,
1277 ArgumentMismatch,
1278 BaseClass = 127
1279};
1280
1281// Error definitions
1282
1288
1290class Error : public std::runtime_error {
1291 int actual_exit_code;
1292 std::string error_name{"Error"};
1293
1294 public:
1295 CLI11_NODISCARD int get_exit_code() const { return actual_exit_code; }
1296
1297 CLI11_NODISCARD std::string get_name() const { return error_name; }
1298
1299 Error(std::string name, std::string msg, int exit_code = static_cast<int>(ExitCodes::BaseClass))
1300 : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {}
1301
1302 Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast<int>(exit_code)) {}
1303};
1304
1305// Note: Using Error::Error constructors does not work on GCC 4.7
1306
1308class ConstructionError : public Error {
1309 CLI11_ERROR_DEF(Error, ConstructionError)
1310};
1311
1313class IncorrectConstruction : public ConstructionError {
1314 CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction)
1315 CLI11_ERROR_SIMPLE(IncorrectConstruction)
1316 static IncorrectConstruction PositionalFlag(std::string name) {
1317 return IncorrectConstruction(name + ": Flags cannot be positional");
1318 }
1319 static IncorrectConstruction Set0Opt(std::string name) {
1320 return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead");
1321 }
1322 static IncorrectConstruction SetFlag(std::string name) {
1323 return IncorrectConstruction(name + ": Cannot set an expected number for flags");
1324 }
1325 static IncorrectConstruction ChangeNotVector(std::string name) {
1326 return IncorrectConstruction(name + ": You can only change the expected arguments for vectors");
1327 }
1328 static IncorrectConstruction AfterMultiOpt(std::string name) {
1329 return IncorrectConstruction(
1330 name + ": You can't change expected arguments after you've changed the multi option policy!");
1331 }
1332 static IncorrectConstruction MissingOption(std::string name) {
1333 return IncorrectConstruction("Option " + name + " is not defined");
1334 }
1335 static IncorrectConstruction MultiOptionPolicy(std::string name) {
1336 return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options");
1337 }
1338};
1339
1341class BadNameString : public ConstructionError {
1342 CLI11_ERROR_DEF(ConstructionError, BadNameString)
1343 CLI11_ERROR_SIMPLE(BadNameString)
1344 static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); }
1345 static BadNameString MissingDash(std::string name) {
1346 return BadNameString("Long names strings require 2 dashes " + name);
1347 }
1348 static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); }
1349 static BadNameString BadPositionalName(std::string name) {
1350 return BadNameString("Invalid positional Name: " + name);
1351 }
1352 static BadNameString DashesOnly(std::string name) {
1353 return BadNameString("Must have a name, not just dashes: " + name);
1354 }
1355 static BadNameString MultiPositionalNames(std::string name) {
1356 return BadNameString("Only one positional name allowed, remove: " + name);
1357 }
1358};
1359
1361class OptionAlreadyAdded : public ConstructionError {
1362 CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)
1363 explicit OptionAlreadyAdded(std::string name)
1364 : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
1365 static OptionAlreadyAdded Requires(std::string name, std::string other) {
1366 return {name + " requires " + other, ExitCodes::OptionAlreadyAdded};
1367 }
1368 static OptionAlreadyAdded Excludes(std::string name, std::string other) {
1369 return {name + " excludes " + other, ExitCodes::OptionAlreadyAdded};
1370 }
1371};
1372
1373// Parsing errors
1374
1376class ParseError : public Error {
1377 CLI11_ERROR_DEF(Error, ParseError)
1378};
1379
1380// Not really "errors"
1381
1383class Success : public ParseError {
1384 CLI11_ERROR_DEF(ParseError, Success)
1385 Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {}
1386};
1387
1389class CallForHelp : public Success {
1390 CLI11_ERROR_DEF(Success, CallForHelp)
1391 CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
1392};
1393
1395class CallForAllHelp : public Success {
1396 CLI11_ERROR_DEF(Success, CallForAllHelp)
1397 CallForAllHelp()
1398 : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
1399};
1400
1402class CallForVersion : public Success {
1403 CLI11_ERROR_DEF(Success, CallForVersion)
1404 CallForVersion()
1405 : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {}
1406};
1407
1409class RuntimeError : public ParseError {
1410 CLI11_ERROR_DEF(ParseError, RuntimeError)
1411 explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
1412};
1413
1415class FileError : public ParseError {
1416 CLI11_ERROR_DEF(ParseError, FileError)
1417 CLI11_ERROR_SIMPLE(FileError)
1418 static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); }
1419};
1420
1422class ConversionError : public ParseError {
1423 CLI11_ERROR_DEF(ParseError, ConversionError)
1424 CLI11_ERROR_SIMPLE(ConversionError)
1425 ConversionError(std::string member, std::string name)
1426 : ConversionError("The value " + member + " is not an allowed value for " + name) {}
1427 ConversionError(std::string name, std::vector<std::string> results)
1428 : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {}
1429 static ConversionError TooManyInputsFlag(std::string name) {
1430 return ConversionError(name + ": too many inputs for a flag");
1431 }
1432 static ConversionError TrueFalse(std::string name) {
1433 return ConversionError(name + ": Should be true/false or a number");
1434 }
1435};
1436
1438class ValidationError : public ParseError {
1439 CLI11_ERROR_DEF(ParseError, ValidationError)
1440 CLI11_ERROR_SIMPLE(ValidationError)
1441 explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
1442};
1443
1445class RequiredError : public ParseError {
1446 CLI11_ERROR_DEF(ParseError, RequiredError)
1447 explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
1448 static RequiredError Subcommand(std::size_t min_subcom) {
1449 if(min_subcom == 1) {
1450 return RequiredError("A subcommand");
1451 }
1452 return {"Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError};
1453 }
1454 static RequiredError
1455 Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) {
1456 if((min_option == 1) && (max_option == 1) && (used == 0))
1457 return RequiredError("Exactly 1 option from [" + option_list + "]");
1458 if((min_option == 1) && (max_option == 1) && (used > 1)) {
1459 return {"Exactly 1 option from [" + option_list + "] is required but " + std::to_string(used) +
1460 " were given",
1461 ExitCodes::RequiredError};
1462 }
1463 if((min_option == 1) && (used == 0))
1464 return RequiredError("At least 1 option from [" + option_list + "]");
1465 if(used < min_option) {
1466 return {"Requires at least " + std::to_string(min_option) + " options used but only " +
1467 std::to_string(used) + " were given from [" + option_list + "]",
1468 ExitCodes::RequiredError};
1469 }
1470 if(max_option == 1)
1471 return {"Requires at most 1 options be given from [" + option_list + "]", ExitCodes::RequiredError};
1472
1473 return {"Requires at most " + std::to_string(max_option) + " options be used but " + std::to_string(used) +
1474 " were given from [" + option_list + "]",
1475 ExitCodes::RequiredError};
1476 }
1477};
1478
1480class ArgumentMismatch : public ParseError {
1481 CLI11_ERROR_DEF(ParseError, ArgumentMismatch)
1482 CLI11_ERROR_SIMPLE(ArgumentMismatch)
1483 ArgumentMismatch(std::string name, int expected, std::size_t received)
1484 : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name +
1485 ", got " + std::to_string(received))
1486 : ("Expected at least " + std::to_string(-expected) + " arguments to " + name +
1487 ", got " + std::to_string(received)),
1488 ExitCodes::ArgumentMismatch) {}
1489
1490 static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) {
1491 return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " +
1492 std::to_string(received));
1493 }
1494 static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) {
1495 return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " +
1496 std::to_string(received));
1497 }
1498 static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) {
1499 return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing");
1500 }
1501 static ArgumentMismatch FlagOverride(std::string name) {
1502 return ArgumentMismatch(name + " was given a disallowed flag override");
1503 }
1504 static ArgumentMismatch PartialType(std::string name, int num, std::string type) {
1505 return ArgumentMismatch(name + ": " + type + " only partially specified: " + std::to_string(num) +
1506 " required for each element");
1507 }
1508};
1509
1511class RequiresError : public ParseError {
1512 CLI11_ERROR_DEF(ParseError, RequiresError)
1513 RequiresError(std::string curname, std::string subname)
1514 : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {}
1515};
1516
1518class ExcludesError : public ParseError {
1519 CLI11_ERROR_DEF(ParseError, ExcludesError)
1520 ExcludesError(std::string curname, std::string subname)
1521 : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {}
1522};
1523
1525class ExtrasError : public ParseError {
1526 CLI11_ERROR_DEF(ParseError, ExtrasError)
1527 explicit ExtrasError(std::vector<std::string> args)
1528 : ExtrasError((args.size() > 1 ? "The following arguments were not expected: "
1529 : "The following argument was not expected: ") +
1530 detail::rjoin(args, " "),
1531 ExitCodes::ExtrasError) {}
1532 ExtrasError(const std::string &name, std::vector<std::string> args)
1533 : ExtrasError(name,
1534 (args.size() > 1 ? "The following arguments were not expected: "
1535 : "The following argument was not expected: ") +
1536 detail::rjoin(args, " "),
1537 ExitCodes::ExtrasError) {}
1538};
1539
1541class ConfigError : public ParseError {
1542 CLI11_ERROR_DEF(ParseError, ConfigError)
1543 CLI11_ERROR_SIMPLE(ConfigError)
1544 static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); }
1545 static ConfigError NotConfigurable(std::string item) {
1546 return ConfigError(item + ": This option is not allowed in a configuration file");
1547 }
1548};
1549
1551class InvalidError : public ParseError {
1552 CLI11_ERROR_DEF(ParseError, InvalidError)
1553 explicit InvalidError(std::string name)
1554 : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) {
1555 }
1556};
1557
1560class HorribleError : public ParseError {
1561 CLI11_ERROR_DEF(ParseError, HorribleError)
1562 CLI11_ERROR_SIMPLE(HorribleError)
1563};
1564
1565// After parsing
1566
1568class OptionNotFound : public Error {
1569 CLI11_ERROR_DEF(Error, OptionNotFound)
1570 explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
1571};
1572
1573#undef CLI11_ERROR_DEF
1574#undef CLI11_ERROR_SIMPLE
1575
1577
1578
1579
1580
1581// Type tools
1582
1583// Utilities for type enabling
1584namespace detail {
1585// Based generally on https://rmf.io/cxx11/almost-static-if
1587enum class enabler {};
1588
1590constexpr enabler dummy = {};
1591} // namespace detail
1592
1598template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
1599
1601template <typename... Ts> struct make_void {
1602 using type = void;
1603};
1604
1606template <typename... Ts> using void_t = typename make_void<Ts...>::type;
1607
1609template <bool B, class T, class F> using conditional_t = typename std::conditional<B, T, F>::type;
1610
1612template <typename T> struct is_bool : std::false_type {};
1613
1615template <> struct is_bool<bool> : std::true_type {};
1616
1618template <typename T> struct is_shared_ptr : std::false_type {};
1619
1621template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
1622
1624template <typename T> struct is_shared_ptr<const std::shared_ptr<T>> : std::true_type {};
1625
1627template <typename T> struct is_copyable_ptr {
1628 static bool const value = is_shared_ptr<T>::value || std::is_pointer<T>::value;
1629};
1630
1632template <typename T> struct IsMemberType {
1633 using type = T;
1634};
1635
1637template <> struct IsMemberType<const char *> {
1638 using type = std::string;
1639};
1640
1641namespace adl_detail {
1647template <typename T, typename S = std::string> class is_lexical_castable {
1648 template <typename TT, typename SS>
1649 static auto test(int) -> decltype(lexical_cast(std::declval<const SS &>(), std::declval<TT &>()), std::true_type());
1650
1651 template <typename, typename> static auto test(...) -> std::false_type;
1652
1653 public:
1654 static constexpr bool value = decltype(test<T, S>(0))::value;
1655};
1656} // namespace adl_detail
1657
1658namespace detail {
1659
1660// These are utilities for IsMember and other transforming objects
1661
1664
1666template <typename T, typename Enable = void> struct element_type {
1667 using type = T;
1668};
1669
1670template <typename T> struct element_type<T, typename std::enable_if<is_copyable_ptr<T>::value>::type> {
1671 using type = typename std::pointer_traits<T>::element_type;
1672};
1673
1676template <typename T> struct element_value_type {
1677 using type = typename element_type<T>::type::value_type;
1678};
1679
1681template <typename T, typename _ = void> struct pair_adaptor : std::false_type {
1682 using value_type = typename T::value_type;
1683 using first_type = typename std::remove_const<value_type>::type;
1684 using second_type = typename std::remove_const<value_type>::type;
1685
1687 template <typename Q> static auto first(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
1688 return std::forward<Q>(pair_value);
1689 }
1691 template <typename Q> static auto second(Q &&pair_value) -> decltype(std::forward<Q>(pair_value)) {
1692 return std::forward<Q>(pair_value);
1693 }
1694};
1695
1698template <typename T>
1699struct pair_adaptor<
1700 T,
1701 conditional_t<false, void_t<typename T::value_type::first_type, typename T::value_type::second_type>, void>>
1702 : std::true_type {
1703 using value_type = typename T::value_type;
1704 using first_type = typename std::remove_const<typename value_type::first_type>::type;
1705 using second_type = typename std::remove_const<typename value_type::second_type>::type;
1706
1708 template <typename Q> static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward<Q>(pair_value))) {
1709 return std::get<0>(std::forward<Q>(pair_value));
1710 }
1712 template <typename Q> static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward<Q>(pair_value))) {
1713 return std::get<1>(std::forward<Q>(pair_value));
1714 }
1715};
1716
1717// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a Wnarrowing warning
1718// in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in
1719// brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a
1720// little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out.
1721// But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be
1722// suppressed
1723#ifdef __GNUC__
1724#pragma GCC diagnostic push
1725#pragma GCC diagnostic ignored "-Wnarrowing"
1726#endif
1727// check for constructibility from a specific type and copy assignable used in the parse detection
1728template <typename T, typename C> class is_direct_constructible {
1729 template <typename TT, typename CC>
1730 static auto test(int, std::true_type) -> decltype(
1731// NVCC warns about narrowing conversions here
1732#ifdef __CUDACC__
1733#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
1734#pragma nv_diag_suppress 2361
1735#else
1736#pragma diag_suppress 2361
1737#endif
1738#endif
1739 TT{std::declval<CC>()}
1740#ifdef __CUDACC__
1741#ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
1742#pragma nv_diag_default 2361
1743#else
1744#pragma diag_default 2361
1745#endif
1746#endif
1747 ,
1749
1750 template <typename TT, typename CC> static auto test(int, std::false_type) -> std::false_type;
1751
1752 template <typename, typename> static auto test(...) -> std::false_type;
1753
1754 public:
1755 static constexpr bool value = decltype(test<T, C>(0, typename std::is_constructible<T, C>::type()))::value;
1756};
1757#ifdef __GNUC__
1758#pragma GCC diagnostic pop
1759#endif
1760
1761// Check for output streamability
1762// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream
1763
1764template <typename T, typename S = std::ostringstream> class is_ostreamable {
1765 template <typename TT, typename SS>
1766 static auto test(int) -> decltype(std::declval<SS &>() << std::declval<TT>(), std::true_type());
1767
1768 template <typename, typename> static auto test(...) -> std::false_type;
1769
1770 public:
1771 static constexpr bool value = decltype(test<T, S>(0))::value;
1772};
1773
1775template <typename T, typename S = std::istringstream> class is_istreamable {
1776 template <typename TT, typename SS>
1777 static auto test(int) -> decltype(std::declval<SS &>() >> std::declval<TT &>(), std::true_type());
1778
1779 template <typename, typename> static auto test(...) -> std::false_type;
1780
1781 public:
1782 static constexpr bool value = decltype(test<T, S>(0))::value;
1783};
1784
1786template <typename T> class is_complex {
1787 template <typename TT>
1788 static auto test(int) -> decltype(std::declval<TT>().real(), std::declval<TT>().imag(), std::true_type());
1789
1790 template <typename> static auto test(...) -> std::false_type;
1791
1792 public:
1793 static constexpr bool value = decltype(test<T>(0))::value;
1794};
1795
1797template <typename T, enable_if_t<is_istreamable<T>::value, detail::enabler> = detail::dummy>
1798bool from_stream(const std::string &istring, T &obj) {
1799 std::istringstream is;
1800 is.str(istring);
1801 is >> obj;
1802 return !is.fail() && !is.rdbuf()->in_avail();
1803}
1804
1805template <typename T, enable_if_t<!is_istreamable<T>::value, detail::enabler> = detail::dummy>
1806bool from_stream(const std::string & /*istring*/, T & /*obj*/) {
1807 return false;
1808}
1809
1810// check to see if an object is a mutable container (fail by default)
1811template <typename T, typename _ = void> struct is_mutable_container : std::false_type {};
1812
1816template <typename T>
1817struct is_mutable_container<
1818 T,
1819 conditional_t<false,
1820 void_t<typename T::value_type,
1821 decltype(std::declval<T>().end()),
1822 decltype(std::declval<T>().clear()),
1823 decltype(std::declval<T>().insert(std::declval<decltype(std::declval<T>().end())>(),
1824 std::declval<const typename T::value_type &>()))>,
1825 void>> : public conditional_t<std::is_constructible<T, std::string>::value ||
1826 std::is_constructible<T, std::wstring>::value,
1827 std::false_type,
1828 std::true_type> {};
1829
1830// check to see if an object is a mutable container (fail by default)
1831template <typename T, typename _ = void> struct is_readable_container : std::false_type {};
1832
1836template <typename T>
1837struct is_readable_container<
1838 T,
1839 conditional_t<false, void_t<decltype(std::declval<T>().end()), decltype(std::declval<T>().begin())>, void>>
1840 : public std::true_type {};
1841
1842// check to see if an object is a wrapper (fail by default)
1843template <typename T, typename _ = void> struct is_wrapper : std::false_type {};
1844
1845// check if an object is a wrapper (it has a value_type defined)
1846template <typename T>
1847struct is_wrapper<T, conditional_t<false, void_t<typename T::value_type>, void>> : public std::true_type {};
1848
1849// Check for tuple like types, as in classes with a tuple_size type trait
1850template <typename S> class is_tuple_like {
1851 template <typename SS>
1852 // static auto test(int)
1853 // -> decltype(std::conditional<(std::tuple_size<SS>::value > 0), std::true_type, std::false_type>::type());
1854 static auto test(int) -> decltype(std::tuple_size<typename std::decay<SS>::type>::value, std::true_type{});
1855 template <typename> static auto test(...) -> std::false_type;
1856
1857 public:
1858 static constexpr bool value = decltype(test<S>(0))::value;
1859};
1860
1862template <typename T, enable_if_t<std::is_convertible<T, std::string>::value, detail::enabler> = detail::dummy>
1863auto to_string(T &&value) -> decltype(std::forward<T>(value)) {
1864 return std::forward<T>(value);
1865}
1866
1868template <typename T,
1869 enable_if_t<std::is_constructible<std::string, T>::value && !std::is_convertible<T, std::string>::value,
1870 detail::enabler> = detail::dummy>
1871std::string to_string(const T &value) {
1872 return std::string(value); // NOLINT(google-readability-casting)
1873}
1874
1876template <typename T,
1877 enable_if_t<!std::is_convertible<std::string, T>::value && !std::is_constructible<std::string, T>::value &&
1878 is_ostreamable<T>::value,
1879 detail::enabler> = detail::dummy>
1880std::string to_string(T &&value) {
1881 std::stringstream stream;
1882 stream << value;
1883 return stream.str();
1884}
1885
1887template <typename T,
1888 enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
1889 !is_readable_container<typename std::remove_const<T>::type>::value,
1890 detail::enabler> = detail::dummy>
1891std::string to_string(T &&) {
1892 return {};
1893}
1894
1896template <typename T,
1897 enable_if_t<!std::is_constructible<std::string, T>::value && !is_ostreamable<T>::value &&
1898 is_readable_container<T>::value,
1899 detail::enabler> = detail::dummy>
1900std::string to_string(T &&variable) {
1901 auto cval = variable.begin();
1902 auto end = variable.end();
1903 if(cval == end) {
1904 return {"{}"};
1905 }
1906 std::vector<std::string> defaults;
1907 while(cval != end) {
1908 defaults.emplace_back(CLI::detail::to_string(*cval));
1909 ++cval;
1910 }
1911 return {"[" + detail::join(defaults) + "]"};
1912}
1913
1915template <typename T1,
1916 typename T2,
1917 typename T,
1918 enable_if_t<std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
1919auto checked_to_string(T &&value) -> decltype(to_string(std::forward<T>(value))) {
1920 return to_string(std::forward<T>(value));
1921}
1922
1924template <typename T1,
1925 typename T2,
1926 typename T,
1927 enable_if_t<!std::is_same<T1, T2>::value, detail::enabler> = detail::dummy>
1928std::string checked_to_string(T &&) {
1929 return std::string{};
1930}
1932template <typename T, enable_if_t<std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
1933std::string value_string(const T &value) {
1934 return std::to_string(value);
1935}
1937template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy>
1938std::string value_string(const T &value) {
1939 return std::to_string(static_cast<typename std::underlying_type<T>::type>(value));
1940}
1942template <typename T,
1943 enable_if_t<!std::is_enum<T>::value && !std::is_arithmetic<T>::value, detail::enabler> = detail::dummy>
1944auto value_string(const T &value) -> decltype(to_string(value)) {
1945 return to_string(value);
1946}
1947
1949template <typename T, typename def, typename Enable = void> struct wrapped_type {
1950 using type = def;
1951};
1952
1954template <typename T, typename def> struct wrapped_type<T, def, typename std::enable_if<is_wrapper<T>::value>::type> {
1955 using type = typename T::value_type;
1956};
1957
1959template <typename T, typename Enable = void> struct type_count_base {
1960 static const int value{0};
1961};
1962
1964template <typename T>
1965struct type_count_base<T,
1966 typename std::enable_if<!is_tuple_like<T>::value && !is_mutable_container<T>::value &&
1967 !std::is_void<T>::value>::type> {
1968 static constexpr int value{1};
1969};
1970
1972template <typename T>
1973struct type_count_base<T, typename std::enable_if<is_tuple_like<T>::value && !is_mutable_container<T>::value>::type> {
1974 static constexpr int value{std::tuple_size<T>::value};
1975};
1976
1978template <typename T> struct type_count_base<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
1979 static constexpr int value{type_count_base<typename T::value_type>::value};
1980};
1981
1983
1985template <typename T> struct subtype_count;
1986
1988template <typename T> struct subtype_count_min;
1989
1991template <typename T, typename Enable = void> struct type_count {
1992 static const int value{0};
1993};
1994
1996template <typename T>
1997struct type_count<T,
1998 typename std::enable_if<!is_wrapper<T>::value && !is_tuple_like<T>::value && !is_complex<T>::value &&
1999 !std::is_void<T>::value>::type> {
2000 static constexpr int value{1};
2001};
2002
2004template <typename T> struct type_count<T, typename std::enable_if<is_complex<T>::value>::type> {
2005 static constexpr int value{2};
2006};
2007
2009template <typename T> struct type_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
2010 static constexpr int value{subtype_count<typename T::value_type>::value};
2011};
2012
2014template <typename T>
2015struct type_count<T,
2016 typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value &&
2017 !is_mutable_container<T>::value>::type> {
2018 static constexpr int value{type_count<typename T::value_type>::value};
2019};
2020
2022template <typename T, std::size_t I>
2023constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size() {
2024 return 0;
2025}
2026
2028template <typename T, std::size_t I>
2029 constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size() {
2030 return subtype_count<typename std::tuple_element<I, T>::type>::value + tuple_type_size<T, I + 1>();
2031}
2032
2034template <typename T> struct type_count<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
2035 static constexpr int value{tuple_type_size<T, 0>()};
2036};
2037
2039template <typename T> struct subtype_count {
2040 static constexpr int value{is_mutable_container<T>::value ? expected_max_vector_size : type_count<T>::value};
2041};
2042
2044template <typename T, typename Enable = void> struct type_count_min {
2045 static const int value{0};
2046};
2047
2049template <typename T>
2050struct type_count_min<
2051 T,
2052 typename std::enable_if<!is_mutable_container<T>::value && !is_tuple_like<T>::value && !is_wrapper<T>::value &&
2053 !is_complex<T>::value && !std::is_void<T>::value>::type> {
2054 static constexpr int value{type_count<T>::value};
2055};
2056
2058template <typename T> struct type_count_min<T, typename std::enable_if<is_complex<T>::value>::type> {
2059 static constexpr int value{1};
2060};
2061
2063template <typename T>
2064struct type_count_min<
2065 T,
2066 typename std::enable_if<is_wrapper<T>::value && !is_complex<T>::value && !is_tuple_like<T>::value>::type> {
2067 static constexpr int value{subtype_count_min<typename T::value_type>::value};
2068};
2069
2071template <typename T, std::size_t I>
2072constexpr typename std::enable_if<I == type_count_base<T>::value, int>::type tuple_type_size_min() {
2073 return 0;
2074}
2075
2077template <typename T, std::size_t I>
2078 constexpr typename std::enable_if < I<type_count_base<T>::value, int>::type tuple_type_size_min() {
2079 return subtype_count_min<typename std::tuple_element<I, T>::type>::value + tuple_type_size_min<T, I + 1>();
2080}
2081
2083template <typename T> struct type_count_min<T, typename std::enable_if<is_tuple_like<T>::value>::type> {
2084 static constexpr int value{tuple_type_size_min<T, 0>()};
2085};
2086
2088template <typename T> struct subtype_count_min {
2089 static constexpr int value{is_mutable_container<T>::value
2090 ? ((type_count<T>::value < expected_max_vector_size) ? type_count<T>::value : 0)
2091 : type_count_min<T>::value};
2092};
2093
2095template <typename T, typename Enable = void> struct expected_count {
2096 static const int value{0};
2097};
2098
2100template <typename T>
2101struct expected_count<T,
2102 typename std::enable_if<!is_mutable_container<T>::value && !is_wrapper<T>::value &&
2103 !std::is_void<T>::value>::type> {
2104 static constexpr int value{1};
2105};
2107template <typename T> struct expected_count<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
2108 static constexpr int value{expected_max_vector_size};
2109};
2110
2112template <typename T>
2113struct expected_count<T, typename std::enable_if<!is_mutable_container<T>::value && is_wrapper<T>::value>::type> {
2114 static constexpr int value{expected_count<typename T::value_type>::value};
2115};
2116
2117// Enumeration of the different supported categorizations of objects
2118enum class object_category : int {
2119 char_value = 1,
2120 integral_value = 2,
2121 unsigned_integral = 4,
2122 enumeration = 6,
2123 boolean_value = 8,
2124 floating_point = 10,
2125 number_constructible = 12,
2126 double_constructible = 14,
2127 integer_constructible = 16,
2128 // string like types
2129 string_assignable = 23,
2130 string_constructible = 24,
2131 wstring_assignable = 25,
2132 wstring_constructible = 26,
2133 other = 45,
2134 // special wrapper or container types
2135 wrapper_value = 50,
2136 complex_number = 60,
2137 tuple_value = 70,
2138 container_value = 80,
2139
2140};
2141
2143
2145template <typename T, typename Enable = void> struct classify_object {
2146 static constexpr object_category value{object_category::other};
2147};
2148
2150template <typename T>
2151struct classify_object<
2152 T,
2153 typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && std::is_signed<T>::value &&
2154 !is_bool<T>::value && !std::is_enum<T>::value>::type> {
2155 static constexpr object_category value{object_category::integral_value};
2156};
2157
2159template <typename T>
2160struct classify_object<T,
2161 typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value &&
2162 !std::is_same<T, char>::value && !is_bool<T>::value>::type> {
2163 static constexpr object_category value{object_category::unsigned_integral};
2164};
2165
2167template <typename T>
2168struct classify_object<T, typename std::enable_if<std::is_same<T, char>::value && !std::is_enum<T>::value>::type> {
2169 static constexpr object_category value{object_category::char_value};
2170};
2171
2173template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> {
2174 static constexpr object_category value{object_category::boolean_value};
2175};
2176
2178template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
2179 static constexpr object_category value{object_category::floating_point};
2180};
2181#if defined _MSC_VER
2182// in MSVC wstring should take precedence if available this isn't as useful on other compilers due to the broader use of
2183// utf-8 encoding
2184#define WIDE_STRING_CHECK \
2185 !std::is_assignable<T &, std::wstring>::value && !std::is_constructible<T, std::wstring>::value
2186#define STRING_CHECK true
2187#else
2188#define WIDE_STRING_CHECK true
2189#define STRING_CHECK !std::is_assignable<T &, std::string>::value && !std::is_constructible<T, std::string>::value
2190#endif
2191
2193template <typename T>
2194struct classify_object<
2195 T,
2196 typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value && WIDE_STRING_CHECK &&
2197 std::is_assignable<T &, std::string>::value>::type> {
2198 static constexpr object_category value{object_category::string_assignable};
2199};
2200
2202template <typename T>
2203struct classify_object<
2204 T,
2205 typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
2206 !std::is_assignable<T &, std::string>::value && (type_count<T>::value == 1) &&
2207 WIDE_STRING_CHECK && std::is_constructible<T, std::string>::value>::type> {
2208 static constexpr object_category value{object_category::string_constructible};
2209};
2210
2212template <typename T>
2213struct classify_object<T,
2214 typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
2215 STRING_CHECK && std::is_assignable<T &, std::wstring>::value>::type> {
2216 static constexpr object_category value{object_category::wstring_assignable};
2217};
2218
2219template <typename T>
2220struct classify_object<
2221 T,
2222 typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
2223 !std::is_assignable<T &, std::wstring>::value && (type_count<T>::value == 1) &&
2224 STRING_CHECK && std::is_constructible<T, std::wstring>::value>::type> {
2225 static constexpr object_category value{object_category::wstring_constructible};
2226};
2227
2229template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
2230 static constexpr object_category value{object_category::enumeration};
2231};
2232
2233template <typename T> struct classify_object<T, typename std::enable_if<is_complex<T>::value>::type> {
2234 static constexpr object_category value{object_category::complex_number};
2235};
2236
2239template <typename T> struct uncommon_type {
2240 using type = typename std::conditional<
2241 !std::is_floating_point<T>::value && !std::is_integral<T>::value &&
2242 !std::is_assignable<T &, std::string>::value && !std::is_constructible<T, std::string>::value &&
2243 !std::is_assignable<T &, std::wstring>::value && !std::is_constructible<T, std::wstring>::value &&
2244 !is_complex<T>::value && !is_mutable_container<T>::value && !std::is_enum<T>::value,
2245 std::true_type,
2246 std::false_type>::type;
2247 static constexpr bool value = type::value;
2248};
2249
2251template <typename T>
2252struct classify_object<T,
2253 typename std::enable_if<(!is_mutable_container<T>::value && is_wrapper<T>::value &&
2254 !is_tuple_like<T>::value && uncommon_type<T>::value)>::type> {
2255 static constexpr object_category value{object_category::wrapper_value};
2256};
2257
2259template <typename T>
2260struct classify_object<T,
2261 typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
2262 !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
2263 is_direct_constructible<T, int>::value>::type> {
2264 static constexpr object_category value{object_category::number_constructible};
2265};
2266
2268template <typename T>
2269struct classify_object<T,
2270 typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
2271 !is_wrapper<T>::value && !is_direct_constructible<T, double>::value &&
2272 is_direct_constructible<T, int>::value>::type> {
2273 static constexpr object_category value{object_category::integer_constructible};
2274};
2275
2277template <typename T>
2278struct classify_object<T,
2279 typename std::enable_if<uncommon_type<T>::value && type_count<T>::value == 1 &&
2280 !is_wrapper<T>::value && is_direct_constructible<T, double>::value &&
2281 !is_direct_constructible<T, int>::value>::type> {
2282 static constexpr object_category value{object_category::double_constructible};
2283};
2284
2286template <typename T>
2287struct classify_object<
2288 T,
2289 typename std::enable_if<is_tuple_like<T>::value &&
2290 ((type_count<T>::value >= 2 && !is_wrapper<T>::value) ||
2291 (uncommon_type<T>::value && !is_direct_constructible<T, double>::value &&
2292 !is_direct_constructible<T, int>::value) ||
2293 (uncommon_type<T>::value && type_count<T>::value >= 2))>::type> {
2294 static constexpr object_category value{object_category::tuple_value};
2295 // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be
2296 // constructed from just the first element so tuples of <string, int,int> can be constructed from a string, which
2297 // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2
2298 // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out
2299 // those cases that are caught by other object classifications
2300};
2301
2303template <typename T> struct classify_object<T, typename std::enable_if<is_mutable_container<T>::value>::type> {
2304 static constexpr object_category value{object_category::container_value};
2305};
2306
2307// Type name print
2308
2312
2313template <typename T,
2314 enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
2315constexpr const char *type_name() {
2316 return "CHAR";
2317}
2318
2319template <typename T,
2320 enable_if_t<classify_object<T>::value == object_category::integral_value ||
2321 classify_object<T>::value == object_category::integer_constructible,
2322 detail::enabler> = detail::dummy>
2323constexpr const char *type_name() {
2324 return "INT";
2325}
2326
2327template <typename T,
2328 enable_if_t<classify_object<T>::value == object_category::unsigned_integral, detail::enabler> = detail::dummy>
2329constexpr const char *type_name() {
2330 return "UINT";
2331}
2332
2333template <typename T,
2334 enable_if_t<classify_object<T>::value == object_category::floating_point ||
2335 classify_object<T>::value == object_category::number_constructible ||
2336 classify_object<T>::value == object_category::double_constructible,
2337 detail::enabler> = detail::dummy>
2338constexpr const char *type_name() {
2339 return "FLOAT";
2340}
2341
2343template <typename T,
2344 enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
2345constexpr const char *type_name() {
2346 return "ENUM";
2347}
2348
2350template <typename T,
2351 enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
2352constexpr const char *type_name() {
2353 return "BOOLEAN";
2354}
2355
2357template <typename T,
2358 enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
2359constexpr const char *type_name() {
2360 return "COMPLEX";
2361}
2362
2364template <typename T,
2365 enable_if_t<classify_object<T>::value >= object_category::string_assignable &&
2366 classify_object<T>::value <= object_category::other,
2367 detail::enabler> = detail::dummy>
2368constexpr const char *type_name() {
2369 return "TEXT";
2370}
2372template <typename T,
2373 enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
2374 detail::enabler> = detail::dummy>
2375std::string type_name(); // forward declaration
2376
2378template <typename T,
2379 enable_if_t<classify_object<T>::value == object_category::container_value ||
2380 classify_object<T>::value == object_category::wrapper_value,
2381 detail::enabler> = detail::dummy>
2382std::string type_name(); // forward declaration
2383
2385template <typename T,
2386 enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value == 1,
2387 detail::enabler> = detail::dummy>
2388inline std::string type_name() {
2389 return type_name<typename std::decay<typename std::tuple_element<0, T>::type>::type>();
2390}
2391
2393template <typename T, std::size_t I>
2394inline typename std::enable_if<I == type_count_base<T>::value, std::string>::type tuple_name() {
2395 return std::string{};
2396}
2397
2399template <typename T, std::size_t I>
2400inline typename std::enable_if<(I < type_count_base<T>::value), std::string>::type tuple_name() {
2401 auto str = std::string{type_name<typename std::decay<typename std::tuple_element<I, T>::type>::type>()} + ',' +
2402 tuple_name<T, I + 1>();
2403 if(str.back() == ',')
2404 str.pop_back();
2405 return str;
2406}
2407
2409template <typename T,
2410 enable_if_t<classify_object<T>::value == object_category::tuple_value && type_count_base<T>::value >= 2,
2411 detail::enabler>>
2412inline std::string type_name() {
2413 auto tname = std::string(1, '[') + tuple_name<T, 0>();
2414 tname.push_back(']');
2415 return tname;
2416}
2417
2419template <typename T,
2420 enable_if_t<classify_object<T>::value == object_category::container_value ||
2421 classify_object<T>::value == object_category::wrapper_value,
2422 detail::enabler>>
2423inline std::string type_name() {
2424 return type_name<typename T::value_type>();
2425}
2426
2427// Lexical cast
2428
2430template <typename T, enable_if_t<std::is_unsigned<T>::value, detail::enabler> = detail::dummy>
2431bool integral_conversion(const std::string &input, T &output) noexcept {
2432 if(input.empty() || input.front() == '-') {
2433 return false;
2434 }
2435 char *val{nullptr};
2436 errno = 0;
2437 std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0);
2438 if(errno == ERANGE) {
2439 return false;
2440 }
2441 output = static_cast<T>(output_ll);
2442 if(val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll) {
2443 return true;
2444 }
2445 val = nullptr;
2446 std::int64_t output_sll = std::strtoll(input.c_str(), &val, 0);
2447 if(val == (input.c_str() + input.size())) {
2448 output = (output_sll < 0) ? static_cast<T>(0) : static_cast<T>(output_sll);
2449 return (static_cast<std::int64_t>(output) == output_sll);
2450 }
2451 // remove separators
2452 if(input.find_first_of("_'") != std::string::npos) {
2453 std::string nstring = input;
2454 nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end());
2455 nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end());
2456 return integral_conversion(nstring, output);
2457 }
2458 if(input.compare(0, 2, "0o") == 0) {
2459 val = nullptr;
2460 errno = 0;
2461 output_ll = std::strtoull(input.c_str() + 2, &val, 8);
2462 if(errno == ERANGE) {
2463 return false;
2464 }
2465 output = static_cast<T>(output_ll);
2466 return (val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll);
2467 }
2468 if(input.compare(0, 2, "0b") == 0) {
2469 val = nullptr;
2470 errno = 0;
2471 output_ll = std::strtoull(input.c_str() + 2, &val, 2);
2472 if(errno == ERANGE) {
2473 return false;
2474 }
2475 output = static_cast<T>(output_ll);
2476 return (val == (input.c_str() + input.size()) && static_cast<std::uint64_t>(output) == output_ll);
2477 }
2478 return false;
2479}
2480
2482template <typename T, enable_if_t<std::is_signed<T>::value, detail::enabler> = detail::dummy>
2483bool integral_conversion(const std::string &input, T &output) noexcept {
2484 if(input.empty()) {
2485 return false;
2486 }
2487 char *val = nullptr;
2488 errno = 0;
2489 std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0);
2490 if(errno == ERANGE) {
2491 return false;
2492 }
2493 output = static_cast<T>(output_ll);
2494 if(val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll) {
2495 return true;
2496 }
2497 if(input == "true") {
2498 // this is to deal with a few oddities with flags and wrapper int types
2499 output = static_cast<T>(1);
2500 return true;
2501 }
2502 // remove separators
2503 if(input.find_first_of("_'") != std::string::npos) {
2504 std::string nstring = input;
2505 nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end());
2506 nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end());
2507 return integral_conversion(nstring, output);
2508 }
2509 if(input.compare(0, 2, "0o") == 0) {
2510 val = nullptr;
2511 errno = 0;
2512 output_ll = std::strtoll(input.c_str() + 2, &val, 8);
2513 if(errno == ERANGE) {
2514 return false;
2515 }
2516 output = static_cast<T>(output_ll);
2517 return (val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll);
2518 }
2519 if(input.compare(0, 2, "0b") == 0) {
2520 val = nullptr;
2521 errno = 0;
2522 output_ll = std::strtoll(input.c_str() + 2, &val, 2);
2523 if(errno == ERANGE) {
2524 return false;
2525 }
2526 output = static_cast<T>(output_ll);
2527 return (val == (input.c_str() + input.size()) && static_cast<std::int64_t>(output) == output_ll);
2528 }
2529 return false;
2530}
2531
2533inline std::int64_t to_flag_value(std::string val) noexcept {
2534 static const std::string trueString("true");
2535 static const std::string falseString("false");
2536 if(val == trueString) {
2537 return 1;
2538 }
2539 if(val == falseString) {
2540 return -1;
2541 }
2542 val = detail::to_lower(val);
2543 std::int64_t ret = 0;
2544 if(val.size() == 1) {
2545 if(val[0] >= '1' && val[0] <= '9') {
2546 return (static_cast<std::int64_t>(val[0]) - '0');
2547 }
2548 switch(val[0]) {
2549 case '0':
2550 case 'f':
2551 case 'n':
2552 case '-':
2553 ret = -1;
2554 break;
2555 case 't':
2556 case 'y':
2557 case '+':
2558 ret = 1;
2559 break;
2560 default:
2561 errno = EINVAL;
2562 return -1;
2563 }
2564 return ret;
2565 }
2566 if(val == trueString || val == "on" || val == "yes" || val == "enable") {
2567 ret = 1;
2568 } else if(val == falseString || val == "off" || val == "no" || val == "disable") {
2569 ret = -1;
2570 } else {
2571 char *loc_ptr{nullptr};
2572 ret = std::strtoll(val.c_str(), &loc_ptr, 0);
2573 if(loc_ptr != (val.c_str() + val.size()) && errno == 0) {
2574 errno = EINVAL;
2575 }
2576 }
2577 return ret;
2578}
2579
2581template <typename T,
2582 enable_if_t<classify_object<T>::value == object_category::integral_value ||
2583 classify_object<T>::value == object_category::unsigned_integral,
2584 detail::enabler> = detail::dummy>
2585bool lexical_cast(const std::string &input, T &output) {
2586 return integral_conversion(input, output);
2587}
2588
2590template <typename T,
2591 enable_if_t<classify_object<T>::value == object_category::char_value, detail::enabler> = detail::dummy>
2592bool lexical_cast(const std::string &input, T &output) {
2593 if(input.size() == 1) {
2594 output = static_cast<T>(input[0]);
2595 return true;
2596 }
2597 return integral_conversion(input, output);
2598}
2599
2601template <typename T,
2602 enable_if_t<classify_object<T>::value == object_category::boolean_value, detail::enabler> = detail::dummy>
2603bool lexical_cast(const std::string &input, T &output) {
2604 errno = 0;
2605 auto out = to_flag_value(input);
2606 if(errno == 0) {
2607 output = (out > 0);
2608 } else if(errno == ERANGE) {
2609 output = (input[0] != '-');
2610 } else {
2611 return false;
2612 }
2613 return true;
2614}
2615
2617template <typename T,
2618 enable_if_t<classify_object<T>::value == object_category::floating_point, detail::enabler> = detail::dummy>
2619bool lexical_cast(const std::string &input, T &output) {
2620 if(input.empty()) {
2621 return false;
2622 }
2623 char *val = nullptr;
2624 auto output_ld = std::strtold(input.c_str(), &val);
2625 output = static_cast<T>(output_ld);
2626 if(val == (input.c_str() + input.size())) {
2627 return true;
2628 }
2629 // remove separators
2630 if(input.find_first_of("_'") != std::string::npos) {
2631 std::string nstring = input;
2632 nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end());
2633 nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end());
2634 return lexical_cast(nstring, output);
2635 }
2636 return false;
2637}
2638
2640template <typename T,
2641 enable_if_t<classify_object<T>::value == object_category::complex_number, detail::enabler> = detail::dummy>
2642bool lexical_cast(const std::string &input, T &output) {
2643 using XC = typename wrapped_type<T, double>::type;
2644 XC x{0.0}, y{0.0};
2645 auto str1 = input;
2646 bool worked = false;
2647 auto nloc = str1.find_last_of("+-");
2648 if(nloc != std::string::npos && nloc > 0) {
2649 worked = lexical_cast(str1.substr(0, nloc), x);
2650 str1 = str1.substr(nloc);
2651 if(str1.back() == 'i' || str1.back() == 'j')
2652 str1.pop_back();
2653 worked = worked && lexical_cast(str1, y);
2654 } else {
2655 if(str1.back() == 'i' || str1.back() == 'j') {
2656 str1.pop_back();
2657 worked = lexical_cast(str1, y);
2658 x = XC{0};
2659 } else {
2660 worked = lexical_cast(str1, x);
2661 y = XC{0};
2662 }
2663 }
2664 if(worked) {
2665 output = T{x, y};
2666 return worked;
2667 }
2668 return from_stream(input, output);
2669}
2670
2672template <typename T,
2673 enable_if_t<classify_object<T>::value == object_category::string_assignable, detail::enabler> = detail::dummy>
2674bool lexical_cast(const std::string &input, T &output) {
2675 output = input;
2676 return true;
2677}
2678
2680template <
2681 typename T,
2682 enable_if_t<classify_object<T>::value == object_category::string_constructible, detail::enabler> = detail::dummy>
2683bool lexical_cast(const std::string &input, T &output) {
2684 output = T(input);
2685 return true;
2686}
2687
2689template <
2690 typename T,
2691 enable_if_t<classify_object<T>::value == object_category::wstring_assignable, detail::enabler> = detail::dummy>
2692bool lexical_cast(const std::string &input, T &output) {
2693 output = widen(input);
2694 return true;
2695}
2696
2697template <
2698 typename T,
2699 enable_if_t<classify_object<T>::value == object_category::wstring_constructible, detail::enabler> = detail::dummy>
2700bool lexical_cast(const std::string &input, T &output) {
2701 output = T{widen(input)};
2702 return true;
2703}
2704
2706template <typename T,
2707 enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
2708bool lexical_cast(const std::string &input, T &output) {
2709 typename std::underlying_type<T>::type val;
2710 if(!integral_conversion(input, val)) {
2711 return false;
2712 }
2713 output = static_cast<T>(val);
2714 return true;
2715}
2716
2718template <typename T,
2719 enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
2720 std::is_assignable<T &, typename T::value_type>::value,
2721 detail::enabler> = detail::dummy>
2722bool lexical_cast(const std::string &input, T &output) {
2723 typename T::value_type val;
2724 if(lexical_cast(input, val)) {
2725 output = val;
2726 return true;
2727 }
2728 return from_stream(input, output);
2729}
2730
2731template <typename T,
2732 enable_if_t<classify_object<T>::value == object_category::wrapper_value &&
2733 !std::is_assignable<T &, typename T::value_type>::value && std::is_assignable<T &, T>::value,
2734 detail::enabler> = detail::dummy>
2735bool lexical_cast(const std::string &input, T &output) {
2736 typename T::value_type val;
2737 if(lexical_cast(input, val)) {
2738 output = T{val};
2739 return true;
2740 }
2741 return from_stream(input, output);
2742}
2743
2745template <
2746 typename T,
2747 enable_if_t<classify_object<T>::value == object_category::number_constructible, detail::enabler> = detail::dummy>
2748bool lexical_cast(const std::string &input, T &output) {
2749 int val = 0;
2750 if(integral_conversion(input, val)) {
2751 output = T(val);
2752 return true;
2753 }
2754
2755 double dval = 0.0;
2756 if(lexical_cast(input, dval)) {
2757 output = T{dval};
2758 return true;
2759 }
2760
2761 return from_stream(input, output);
2762}
2763
2765template <
2766 typename T,
2767 enable_if_t<classify_object<T>::value == object_category::integer_constructible, detail::enabler> = detail::dummy>
2768bool lexical_cast(const std::string &input, T &output) {
2769 int val = 0;
2770 if(integral_conversion(input, val)) {
2771 output = T(val);
2772 return true;
2773 }
2774 return from_stream(input, output);
2775}
2776
2778template <
2779 typename T,
2780 enable_if_t<classify_object<T>::value == object_category::double_constructible, detail::enabler> = detail::dummy>
2781bool lexical_cast(const std::string &input, T &output) {
2782 double val = 0.0;
2783 if(lexical_cast(input, val)) {
2784 output = T{val};
2785 return true;
2786 }
2787 return from_stream(input, output);
2788}
2789
2791template <typename T,
2792 enable_if_t<classify_object<T>::value == object_category::other && std::is_assignable<T &, int>::value,
2793 detail::enabler> = detail::dummy>
2794bool lexical_cast(const std::string &input, T &output) {
2795 int val = 0;
2796 if(integral_conversion(input, val)) {
2797#ifdef _MSC_VER
2798#pragma warning(push)
2799#pragma warning(disable : 4800)
2800#endif
2801 // with Atomic<XX> this could produce a warning due to the conversion but if atomic gets here it is an old style
2802 // so will most likely still work
2803 output = val;
2804#ifdef _MSC_VER
2805#pragma warning(pop)
2806#endif
2807 return true;
2808 }
2809 // LCOV_EXCL_START
2810 // This version of cast is only used for odd cases in an older compilers the fail over
2811 // from_stream is tested elsewhere an not relevant for coverage here
2812 return from_stream(input, output);
2813 // LCOV_EXCL_STOP
2814}
2815
2817template <typename T,
2818 enable_if_t<classify_object<T>::value == object_category::other && !std::is_assignable<T &, int>::value &&
2819 is_istreamable<T>::value,
2820 detail::enabler> = detail::dummy>
2821bool lexical_cast(const std::string &input, T &output) {
2822 return from_stream(input, output);
2823}
2824
2827template <typename T,
2828 enable_if_t<classify_object<T>::value == object_category::other && !std::is_assignable<T &, int>::value &&
2829 !is_istreamable<T>::value && !adl_detail::is_lexical_castable<T>::value,
2830 detail::enabler> = detail::dummy>
2831bool lexical_cast(const std::string & /*input*/, T & /*output*/) {
2832 static_assert(!std::is_same<T, T>::value, // Can't just write false here.
2833 "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it "
2834 "is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
2835 return false;
2836}
2837
2840template <typename AssignTo,
2841 typename ConvertTo,
2842 enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&
2843 (classify_object<AssignTo>::value == object_category::string_assignable ||
2844 classify_object<AssignTo>::value == object_category::string_constructible ||
2845 classify_object<AssignTo>::value == object_category::wstring_assignable ||
2846 classify_object<AssignTo>::value == object_category::wstring_constructible),
2847 detail::enabler> = detail::dummy>
2848bool lexical_assign(const std::string &input, AssignTo &output) {
2849 return lexical_cast(input, output);
2850}
2851
2853template <typename AssignTo,
2854 typename ConvertTo,
2855 enable_if_t<std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, AssignTo>::value &&
2856 classify_object<AssignTo>::value != object_category::string_assignable &&
2857 classify_object<AssignTo>::value != object_category::string_constructible &&
2858 classify_object<AssignTo>::value != object_category::wstring_assignable &&
2859 classify_object<AssignTo>::value != object_category::wstring_constructible,
2860 detail::enabler> = detail::dummy>
2861bool lexical_assign(const std::string &input, AssignTo &output) {
2862 if(input.empty()) {
2863 output = AssignTo{};
2864 return true;
2865 }
2866
2867 return lexical_cast(input, output);
2868}
2869
2871template <typename AssignTo,
2872 typename ConvertTo,
2873 enable_if_t<std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, AssignTo>::value &&
2874 classify_object<AssignTo>::value == object_category::wrapper_value,
2875 detail::enabler> = detail::dummy>
2876bool lexical_assign(const std::string &input, AssignTo &output) {
2877 if(input.empty()) {
2878 typename AssignTo::value_type emptyVal{};
2879 output = emptyVal;
2880 return true;
2881 }
2882 return lexical_cast(input, output);
2883}
2884
2887template <typename AssignTo,
2888 typename ConvertTo,
2889 enable_if_t<std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, AssignTo>::value &&
2890 classify_object<AssignTo>::value != object_category::wrapper_value &&
2891 std::is_assignable<AssignTo &, int>::value,
2892 detail::enabler> = detail::dummy>
2893bool lexical_assign(const std::string &input, AssignTo &output) {
2894 if(input.empty()) {
2895 output = 0;
2896 return true;
2897 }
2898 int val{0};
2899 if(lexical_cast(input, val)) {
2900#if defined(__clang__)
2901/* on some older clang compilers */
2902#pragma clang diagnostic push
2903#pragma clang diagnostic ignored "-Wsign-conversion"
2904#endif
2905 output = val;
2906#if defined(__clang__)
2907#pragma clang diagnostic pop
2908#endif
2909 return true;
2910 }
2911 return false;
2912}
2913
2915template <typename AssignTo,
2916 typename ConvertTo,
2917 enable_if_t<!std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, ConvertTo &>::value,
2918 detail::enabler> = detail::dummy>
2919bool lexical_assign(const std::string &input, AssignTo &output) {
2920 ConvertTo val{};
2921 bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true;
2922 if(parse_result) {
2923 output = val;
2924 }
2925 return parse_result;
2926}
2927
2929template <
2930 typename AssignTo,
2931 typename ConvertTo,
2932 enable_if_t<!std::is_same<AssignTo, ConvertTo>::value && !std::is_assignable<AssignTo &, ConvertTo &>::value &&
2933 std::is_move_assignable<AssignTo>::value,
2934 detail::enabler> = detail::dummy>
2935bool lexical_assign(const std::string &input, AssignTo &output) {
2936 ConvertTo val{};
2937 bool parse_result = input.empty() ? true : lexical_cast(input, val);
2938 if(parse_result) {
2939 output = AssignTo(val); // use () form of constructor to allow some implicit conversions
2940 }
2941 return parse_result;
2942}
2943
2945template <typename AssignTo,
2946 typename ConvertTo,
2947 enable_if_t<classify_object<ConvertTo>::value <= object_category::other &&
2948 classify_object<AssignTo>::value <= object_category::wrapper_value,
2949 detail::enabler> = detail::dummy>
2950bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
2951 return lexical_assign<AssignTo, ConvertTo>(strings[0], output);
2952}
2953
2956template <typename AssignTo,
2957 typename ConvertTo,
2958 enable_if_t<(type_count<AssignTo>::value <= 2) && expected_count<AssignTo>::value == 1 &&
2959 is_tuple_like<ConvertTo>::value && type_count_base<ConvertTo>::value == 2,
2960 detail::enabler> = detail::dummy>
2961bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
2962 // the remove const is to handle pair types coming from a container
2964 using SecondType = typename std::tuple_element<1, ConvertTo>::type;
2965 FirstType v1;
2966 SecondType v2;
2967 bool retval = lexical_assign<FirstType, FirstType>(strings[0], v1);
2968 retval = retval && lexical_assign<SecondType, SecondType>((strings.size() > 1) ? strings[1] : std::string{}, v2);
2969 if(retval) {
2970 output = AssignTo{v1, v2};
2971 }
2972 return retval;
2973}
2974
2976template <class AssignTo,
2977 class ConvertTo,
2978 enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
2979 type_count<ConvertTo>::value == 1,
2980 detail::enabler> = detail::dummy>
2981bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
2982 output.erase(output.begin(), output.end());
2983 if(strings.empty()) {
2984 return true;
2985 }
2986 if(strings.size() == 1 && strings[0] == "{}") {
2987 return true;
2988 }
2989 bool skip_remaining = false;
2990 if(strings.size() == 2 && strings[0] == "{}" && is_separator(strings[1])) {
2991 skip_remaining = true;
2992 }
2993 for(const auto &elem : strings) {
2994 typename AssignTo::value_type out;
2995 bool retval = lexical_assign<typename AssignTo::value_type, typename ConvertTo::value_type>(elem, out);
2996 if(!retval) {
2997 return false;
2998 }
2999 output.insert(output.end(), std::move(out));
3000 if(skip_remaining) {
3001 break;
3002 }
3003 }
3004 return (!output.empty());
3005}
3006
3008template <class AssignTo, class ConvertTo, enable_if_t<is_complex<ConvertTo>::value, detail::enabler> = detail::dummy>
3009bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
3010
3011 if(strings.size() >= 2 && !strings[1].empty()) {
3012 using XC2 = typename wrapped_type<ConvertTo, double>::type;
3013 XC2 x{0.0}, y{0.0};
3014 auto str1 = strings[1];
3015 if(str1.back() == 'i' || str1.back() == 'j') {
3016 str1.pop_back();
3017 }
3018 auto worked = lexical_cast(strings[0], x) && lexical_cast(str1, y);
3019 if(worked) {
3020 output = ConvertTo{x, y};
3021 }
3022 return worked;
3023 }
3024 return lexical_assign<AssignTo, ConvertTo>(strings[0], output);
3025}
3026
3028template <class AssignTo,
3029 class ConvertTo,
3030 enable_if_t<is_mutable_container<AssignTo>::value && (expected_count<ConvertTo>::value == 1) &&
3031 (type_count<ConvertTo>::value == 1),
3032 detail::enabler> = detail::dummy>
3033bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
3034 bool retval = true;
3035 output.clear();
3036 output.reserve(strings.size());
3037 for(const auto &elem : strings) {
3038
3039 output.emplace_back();
3040 retval = retval && lexical_assign<typename AssignTo::value_type, ConvertTo>(elem, output.back());
3041 }
3042 return (!output.empty()) && retval;
3043}
3044
3045// forward declaration
3046
3048template <class AssignTo,
3049 class ConvertTo,
3050 enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
3051 type_count_base<ConvertTo>::value == 2,
3052 detail::enabler> = detail::dummy>
3053bool lexical_conversion(std::vector<std::string> strings, AssignTo &output);
3054
3056template <class AssignTo,
3057 class ConvertTo,
3058 enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
3059 type_count_base<ConvertTo>::value != 2 &&
3060 ((type_count<ConvertTo>::value > 2) ||
3061 (type_count<ConvertTo>::value > type_count_base<ConvertTo>::value)),
3062 detail::enabler> = detail::dummy>
3063bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output);
3064
3066template <class AssignTo,
3067 class ConvertTo,
3068 enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
3069 (type_count_base<ConvertTo>::value != type_count<ConvertTo>::value ||
3070 type_count<ConvertTo>::value > 2),
3071 detail::enabler> = detail::dummy>
3072bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output); // forward declaration
3073
3076template <typename AssignTo,
3077 typename ConvertTo,
3078 enable_if_t<!is_tuple_like<AssignTo>::value && !is_mutable_container<AssignTo>::value &&
3079 classify_object<ConvertTo>::value != object_category::wrapper_value &&
3080 (is_mutable_container<ConvertTo>::value || type_count<ConvertTo>::value > 2),
3081 detail::enabler> = detail::dummy>
3082bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
3083
3084 if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) {
3085 ConvertTo val;
3086 auto retval = lexical_conversion<ConvertTo, ConvertTo>(strings, val);
3087 output = AssignTo{val};
3088 return retval;
3089 }
3090 output = AssignTo{};
3091 return true;
3092}
3093
3095template <class AssignTo, class ConvertTo, std::size_t I>
3096inline typename std::enable_if<(I >= type_count_base<AssignTo>::value), bool>::type
3097tuple_conversion(const std::vector<std::string> &, AssignTo &) {
3098 return true;
3099}
3100
3102template <class AssignTo, class ConvertTo>
3103inline typename std::enable_if<!is_mutable_container<ConvertTo>::value && type_count<ConvertTo>::value == 1, bool>::type
3104tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
3105 auto retval = lexical_assign<AssignTo, ConvertTo>(strings[0], output);
3106 strings.erase(strings.begin());
3107 return retval;
3108}
3109
3111template <class AssignTo, class ConvertTo>
3112inline typename std::enable_if<!is_mutable_container<ConvertTo>::value && (type_count<ConvertTo>::value > 1) &&
3113 type_count<ConvertTo>::value == type_count_min<ConvertTo>::value,
3114 bool>::type
3115tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
3116 auto retval = lexical_conversion<AssignTo, ConvertTo>(strings, output);
3117 strings.erase(strings.begin(), strings.begin() + type_count<ConvertTo>::value);
3118 return retval;
3119}
3120
3122template <class AssignTo, class ConvertTo>
3124 type_count<ConvertTo>::value != type_count_min<ConvertTo>::value,
3125 bool>::type
3126tuple_type_conversion(std::vector<std::string> &strings, AssignTo &output) {
3127
3128 std::size_t index{subtype_count_min<ConvertTo>::value};
3129 const std::size_t mx_count{subtype_count<ConvertTo>::value};
3130 const std::size_t mx{(std::min)(mx_count, strings.size() - 1)};
3131
3132 while(index < mx) {
3133 if(is_separator(strings[index])) {
3134 break;
3135 }
3136 ++index;
3137 }
3138 bool retval = lexical_conversion<AssignTo, ConvertTo>(
3139 std::vector<std::string>(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index)), output);
3140 if(strings.size() > index) {
3141 strings.erase(strings.begin(), strings.begin() + static_cast<std::ptrdiff_t>(index) + 1);
3142 } else {
3143 strings.clear();
3144 }
3145 return retval;
3146}
3147
3149template <class AssignTo, class ConvertTo, std::size_t I>
3150inline typename std::enable_if<(I < type_count_base<AssignTo>::value), bool>::type
3151tuple_conversion(std::vector<std::string> strings, AssignTo &output) {
3152 bool retval = true;
3153 using ConvertToElement = typename std::
3154 conditional<is_tuple_like<ConvertTo>::value, typename std::tuple_element<I, ConvertTo>::type, ConvertTo>::type;
3155 if(!strings.empty()) {
3156 retval = retval && tuple_type_conversion<typename std::tuple_element<I, AssignTo>::type, ConvertToElement>(
3157 strings, std::get<I>(output));
3158 }
3159 retval = retval && tuple_conversion<AssignTo, ConvertTo, I + 1>(std::move(strings), output);
3160 return retval;
3161}
3162
3164template <class AssignTo,
3165 class ConvertTo,
3166 enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
3167 type_count_base<ConvertTo>::value == 2,
3168 detail::enabler>>
3169bool lexical_conversion(std::vector<std::string> strings, AssignTo &output) {
3170 output.clear();
3171 while(!strings.empty()) {
3172
3174 typename std::tuple_element<1, typename ConvertTo::value_type>::type v2;
3175 bool retval = tuple_type_conversion<decltype(v1), decltype(v1)>(strings, v1);
3176 if(!strings.empty()) {
3177 retval = retval && tuple_type_conversion<decltype(v2), decltype(v2)>(strings, v2);
3178 }
3179 if(retval) {
3180 output.insert(output.end(), typename AssignTo::value_type{v1, v2});
3181 } else {
3182 return false;
3183 }
3184 }
3185 return (!output.empty());
3186}
3187
3189template <class AssignTo,
3190 class ConvertTo,
3191 enable_if_t<is_tuple_like<AssignTo>::value && is_tuple_like<ConvertTo>::value &&
3192 (type_count_base<ConvertTo>::value != type_count<ConvertTo>::value ||
3193 type_count<ConvertTo>::value > 2),
3194 detail::enabler>>
3195bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
3196 static_assert(
3197 !is_tuple_like<ConvertTo>::value || type_count_base<AssignTo>::value == type_count_base<ConvertTo>::value,
3198 "if the conversion type is defined as a tuple it must be the same size as the type you are converting to");
3199 return tuple_conversion<AssignTo, ConvertTo, 0>(strings, output);
3200}
3201
3203template <class AssignTo,
3204 class ConvertTo,
3205 enable_if_t<is_mutable_container<AssignTo>::value && is_mutable_container<ConvertTo>::value &&
3206 type_count_base<ConvertTo>::value != 2 &&
3207 ((type_count<ConvertTo>::value > 2) ||
3208 (type_count<ConvertTo>::value > type_count_base<ConvertTo>::value)),
3209 detail::enabler>>
3210bool lexical_conversion(const std::vector<std ::string> &strings, AssignTo &output) {
3211 bool retval = true;
3212 output.clear();
3213 std::vector<std::string> temp;
3214 std::size_t ii{0};
3215 std::size_t icount{0};
3216 std::size_t xcm{type_count<ConvertTo>::value};
3217 auto ii_max = strings.size();
3218 while(ii < ii_max) {
3219 temp.push_back(strings[ii]);
3220 ++ii;
3221 ++icount;
3222 if(icount == xcm || is_separator(temp.back()) || ii == ii_max) {
3223 if(static_cast<int>(xcm) > type_count_min<ConvertTo>::value && is_separator(temp.back())) {
3224 temp.pop_back();
3225 }
3226 typename AssignTo::value_type temp_out;
3227 retval = retval &&
3228 lexical_conversion<typename AssignTo::value_type, typename ConvertTo::value_type>(temp, temp_out);
3229 temp.clear();
3230 if(!retval) {
3231 return false;
3232 }
3233 output.insert(output.end(), std::move(temp_out));
3234 icount = 0;
3235 }
3236 }
3237 return retval;
3238}
3239
3241template <typename AssignTo,
3242 class ConvertTo,
3243 enable_if_t<classify_object<ConvertTo>::value == object_category::wrapper_value &&
3244 std::is_assignable<ConvertTo &, ConvertTo>::value,
3245 detail::enabler> = detail::dummy>
3246bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
3247 if(strings.empty() || strings.front().empty()) {
3248 output = ConvertTo{};
3249 return true;
3250 }
3251 typename ConvertTo::value_type val;
3252 if(lexical_conversion<typename ConvertTo::value_type, typename ConvertTo::value_type>(strings, val)) {
3253 output = ConvertTo{val};
3254 return true;
3255 }
3256 return false;
3257}
3258
3260template <typename AssignTo,
3261 class ConvertTo,
3262 enable_if_t<classify_object<ConvertTo>::value == object_category::wrapper_value &&
3263 !std::is_assignable<AssignTo &, ConvertTo>::value,
3264 detail::enabler> = detail::dummy>
3265bool lexical_conversion(const std::vector<std::string> &strings, AssignTo &output) {
3266 using ConvertType = typename ConvertTo::value_type;
3267 if(strings.empty() || strings.front().empty()) {
3268 output = ConvertType{};
3269 return true;
3270 }
3271 ConvertType val;
3272 if(lexical_conversion<typename ConvertTo::value_type, typename ConvertTo::value_type>(strings, val)) {
3273 output = val;
3274 return true;
3275 }
3276 return false;
3277}
3278
3280inline std::string sum_string_vector(const std::vector<std::string> &values) {
3281 double val{0.0};
3282 bool fail{false};
3283 std::string output;
3284 for(const auto &arg : values) {
3285 double tv{0.0};
3286 auto comp = lexical_cast(arg, tv);
3287 if(!comp) {
3288 errno = 0;
3289 auto fv = detail::to_flag_value(arg);
3290 fail = (errno != 0);
3291 if(fail) {
3292 break;
3293 }
3294 tv = static_cast<double>(fv);
3295 }
3296 val += tv;
3297 }
3298 if(fail) {
3299 for(const auto &arg : values) {
3300 output.append(arg);
3301 }
3302 } else {
3303 std::ostringstream out;
3304 out.precision(16);
3305 out << val;
3306 output = out.str();
3307 }
3308 return output;
3309}
3310
3311} // namespace detail
3312
3313
3314
3315namespace detail {
3316
3317// Returns false if not a short option. Otherwise, sets opt name and rest and returns true
3318CLI11_INLINE bool split_short(const std::string &current, std::string &name, std::string &rest);
3319
3320// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true
3321CLI11_INLINE bool split_long(const std::string &current, std::string &name, std::string &value);
3322
3323// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true
3324CLI11_INLINE bool split_windows_style(const std::string &current, std::string &name, std::string &value);
3325
3326// Splits a string into multiple long and short names
3327CLI11_INLINE std::vector<std::string> split_names(std::string current);
3328
3330CLI11_INLINE std::vector<std::pair<std::string, std::string>> get_default_flag_values(const std::string &str);
3331
3333CLI11_INLINE std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>
3334get_names(const std::vector<std::string> &input);
3335
3336} // namespace detail
3337
3338
3339
3340namespace detail {
3341
3342CLI11_INLINE bool split_short(const std::string &current, std::string &name, std::string &rest) {
3343 if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) {
3344 name = current.substr(1, 1);
3345 rest = current.substr(2);
3346 return true;
3347 }
3348 return false;
3349}
3350
3351CLI11_INLINE bool split_long(const std::string &current, std::string &name, std::string &value) {
3352 if(current.size() > 2 && current.compare(0, 2, "--") == 0 && valid_first_char(current[2])) {
3353 auto loc = current.find_first_of('=');
3354 if(loc != std::string::npos) {
3355 name = current.substr(2, loc - 2);
3356 value = current.substr(loc + 1);
3357 } else {
3358 name = current.substr(2);
3359 value = "";
3360 }
3361 return true;
3362 }
3363 return false;
3364}
3365
3366CLI11_INLINE bool split_windows_style(const std::string &current, std::string &name, std::string &value) {
3367 if(current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) {
3368 auto loc = current.find_first_of(':');
3369 if(loc != std::string::npos) {
3370 name = current.substr(1, loc - 1);
3371 value = current.substr(loc + 1);
3372 } else {
3373 name = current.substr(1);
3374 value = "";
3375 }
3376 return true;
3377 }
3378 return false;
3379}
3380
3381CLI11_INLINE std::vector<std::string> split_names(std::string current) {
3382 std::vector<std::string> output;
3383 std::size_t val = 0;
3384 while((val = current.find(',')) != std::string::npos) {
3385 output.push_back(trim_copy(current.substr(0, val)));
3386 current = current.substr(val + 1);
3387 }
3388 output.push_back(trim_copy(current));
3389 return output;
3390}
3391
3392CLI11_INLINE std::vector<std::pair<std::string, std::string>> get_default_flag_values(const std::string &str) {
3393 std::vector<std::string> flags = split_names(str);
3394 flags.erase(std::remove_if(flags.begin(),
3395 flags.end(),
3396 [](const std::string &name) {
3397 return ((name.empty()) || (!(((name.find_first_of('{') != std::string::npos) &&
3398 (name.back() == '}')) ||
3399 (name[0] == '!'))));
3400 }),
3401 flags.end());
3403 output.reserve(flags.size());
3404 for(auto &flag : flags) {
3405 auto def_start = flag.find_first_of('{');
3406 std::string defval = "false";
3407 if((def_start != std::string::npos) && (flag.back() == '}')) {
3408 defval = flag.substr(def_start + 1);
3409 defval.pop_back();
3410 flag.erase(def_start, std::string::npos); // NOLINT(readability-suspicious-call-argument)
3411 }
3412 flag.erase(0, flag.find_first_not_of("-!"));
3413 output.emplace_back(flag, defval);
3414 }
3415 return output;
3416}
3417
3418CLI11_INLINE std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>
3419get_names(const std::vector<std::string> &input) {
3420
3421 std::vector<std::string> short_names;
3422 std::vector<std::string> long_names;
3423 std::string pos_name;
3424 for(std::string name : input) {
3425 if(name.length() == 0) {
3426 continue;
3427 }
3428 if(name.length() > 1 && name[0] == '-' && name[1] != '-') {
3429 if(name.length() == 2 && valid_first_char(name[1]))
3430 short_names.emplace_back(1, name[1]);
3431 else if(name.length() > 2)
3432 THROW BadNameString::MissingDash(name);
3433 else
3434 THROW BadNameString::OneCharName(name);
3435 } else if(name.length() > 2 && name.substr(0, 2) == "--") {
3436 name = name.substr(2);
3437 if(valid_name_string(name))
3438 long_names.push_back(name);
3439 else
3440 THROW BadNameString::BadLongName(name);
3441 } else if(name == "-" || name == "--") {
3442 THROW BadNameString::DashesOnly(name);
3443 } else {
3444 if(!pos_name.empty())
3445 THROW BadNameString::MultiPositionalNames(name);
3446 if(valid_name_string(name)) {
3447 pos_name = name;
3448 } else {
3449 THROW BadNameString::BadPositionalName(name);
3450 }
3451 }
3452 }
3453 return std::make_tuple(short_names, long_names, pos_name);
3454}
3455
3456} // namespace detail
3457
3458
3459
3460class App;
3461
3463struct ConfigItem {
3465 std::vector<std::string> parents{};
3466
3468 std::string name{};
3470 std::vector<std::string> inputs{};
3471
3473 CLI11_NODISCARD std::string fullname() const {
3474 std::vector<std::string> tmp = parents;
3475 tmp.emplace_back(name);
3476 return detail::join(tmp, ".");
3477 }
3478};
3479
3481class Config {
3482 protected:
3484
3485 public:
3487 virtual std::string to_config(const App *, bool, bool, std::string) const = 0;
3488
3490 virtual std::vector<ConfigItem> from_config(std::istream &) const = 0;
3491
3493 CLI11_NODISCARD virtual std::string to_flag(const ConfigItem &item) const {
3494 if(item.inputs.size() == 1) {
3495 return item.inputs.at(0);
3496 }
3497 if(item.inputs.empty()) {
3498 return "{}";
3499 }
3500 THROW ConversionError::TooManyInputsFlag(item.fullname()); // LCOV_EXCL_LINE
3501 }
3502
3504 CLI11_NODISCARD std::vector<ConfigItem> from_file(const std::string &name) const {
3505 std::ifstream input{name};
3506 if(!input.good())
3507 THROW FileError::Missing(name);
3508
3509 return from_config(input);
3510 }
3511
3513 virtual ~Config() = default;
3514};
3515
3517class ConfigBase : public Config {
3518 protected:
3520 char commentChar = '#';
3522 char arrayStart = '[';
3524 char arrayEnd = ']';
3526 char arraySeparator = ',';
3528 char valueDelimiter = '=';
3530 char stringQuote = '"';
3532 char literalQuote = '\'';
3534 uint8_t maximumLayers{255};
3536 char parentSeparatorChar{'.'};
3538 int16_t configIndex{-1};
3540 std::string configSection{};
3541
3542 public:
3543 std::string
3544 to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override;
3545
3546 std::vector<ConfigItem> from_config(std::istream &input) const override;
3548 ConfigBase *comment(char cchar) {
3549 commentChar = cchar;
3550 return this;
3551 }
3553 ConfigBase *arrayBounds(char aStart, char aEnd) {
3554 arrayStart = aStart;
3555 arrayEnd = aEnd;
3556 return this;
3557 }
3559 ConfigBase *arrayDelimiter(char aSep) {
3560 arraySeparator = aSep;
3561 return this;
3562 }
3564 ConfigBase *valueSeparator(char vSep) {
3565 valueDelimiter = vSep;
3566 return this;
3567 }
3569 ConfigBase *quoteCharacter(char qString, char literalChar) {
3570 stringQuote = qString;
3571 literalQuote = literalChar;
3572 return this;
3573 }
3575 ConfigBase *maxLayers(uint8_t layers) {
3576 maximumLayers = layers;
3577 return this;
3578 }
3580 ConfigBase *parentSeparator(char sep) {
3581 parentSeparatorChar = sep;
3582 return this;
3583 }
3585 std::string &sectionRef() { return configSection; }
3587 CLI11_NODISCARD const std::string &section() const { return configSection; }
3589 ConfigBase *section(const std::string &sectionName) {
3590 configSection = sectionName;
3591 return this;
3592 }
3593
3595 int16_t &indexRef() { return configIndex; }
3597 CLI11_NODISCARD int16_t index() const { return configIndex; }
3599 ConfigBase *index(int16_t sectionIndex) {
3600 configIndex = sectionIndex;
3601 return this;
3602 }
3603};
3604
3606using ConfigTOML = ConfigBase;
3607
3609class ConfigINI : public ConfigTOML {
3610
3611 public:
3612 ConfigINI() {
3613 commentChar = ';';
3614 arrayStart = '\0';
3615 arrayEnd = '\0';
3616 arraySeparator = ' ';
3617 valueDelimiter = '=';
3618 }
3619};
3620
3621
3622
3623class Option;
3624
3626
3633
3635class Validator {
3636 protected:
3638 std::function<std::string()> desc_function_{[]() { return std::string{}; }};
3639
3642 std::function<std::string(std::string &)> func_{[](std::string &) { return std::string{}; }};
3644 std::string name_{};
3646 int application_index_ = -1;
3648 bool active_{true};
3650 bool non_modifying_{false};
3651
3652 Validator(std::string validator_desc, std::function<std::string(std::string &)> func)
3653 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(func)) {}
3654
3655 public:
3656 Validator() = default;
3658 explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {}
3660 Validator(std::function<std::string(std::string &)> op, std::string validator_desc, std::string validator_name = "")
3661 : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)),
3662 name_(std::move(validator_name)) {}
3664 Validator &operation(std::function<std::string(std::string &)> op) {
3665 func_ = std::move(op);
3666 return *this;
3667 }
3670 std::string operator()(std::string &str) const;
3671
3674 std::string operator()(const std::string &str) const {
3675 std::string value = str;
3676 return (active_) ? func_(value) : std::string{};
3677 }
3678
3680 Validator &description(std::string validator_desc) {
3681 desc_function_ = [validator_desc]() { return validator_desc; };
3682 return *this;
3683 }
3685 CLI11_NODISCARD Validator description(std::string validator_desc) const;
3686
3688 CLI11_NODISCARD std::string get_description() const {
3689 if(active_) {
3690 return desc_function_();
3691 }
3692 return std::string{};
3693 }
3695 Validator &name(std::string validator_name) {
3696 name_ = std::move(validator_name);
3697 return *this;
3698 }
3700 CLI11_NODISCARD Validator name(std::string validator_name) const {
3701 Validator newval(*this);
3702 newval.name_ = std::move(validator_name);
3703 return newval;
3704 }
3706 CLI11_NODISCARD const std::string &get_name() const { return name_; }
3708 Validator &active(bool active_val = true) {
3709 active_ = active_val;
3710 return *this;
3711 }
3713 CLI11_NODISCARD Validator active(bool active_val = true) const {
3714 Validator newval(*this);
3715 newval.active_ = active_val;
3716 return newval;
3717 }
3718
3720 Validator &non_modifying(bool no_modify = true) {
3721 non_modifying_ = no_modify;
3722 return *this;
3723 }
3725 Validator &application_index(int app_index) {
3726 application_index_ = app_index;
3727 return *this;
3728 }
3730 CLI11_NODISCARD Validator application_index(int app_index) const {
3731 Validator newval(*this);
3732 newval.application_index_ = app_index;
3733 return newval;
3734 }
3736 CLI11_NODISCARD int get_application_index() const { return application_index_; }
3738 CLI11_NODISCARD bool get_active() const { return active_; }
3739
3741 CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; }
3742
3745 Validator operator&(const Validator &other) const;
3746
3749 Validator operator|(const Validator &other) const;
3750
3752 Validator operator!() const;
3753
3754 private:
3755 void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger);
3756};
3757
3759class CustomValidator : public Validator {
3760 public:
3761};
3762// The implementation of the built in validators is using the Validator class;
3763// the user is only expected to use the const (static) versions (since there's no setup).
3764// Therefore, this is in detail.
3765namespace detail {
3766
3768enum class path_type { nonexistent, file, directory };
3769
3771CLI11_INLINE path_type check_path(const char *file) noexcept;
3772
3774class ExistingFileValidator : public Validator {
3775 public:
3776 ExistingFileValidator();
3777};
3778
3780class ExistingDirectoryValidator : public Validator {
3781 public:
3782 ExistingDirectoryValidator();
3783};
3784
3786class ExistingPathValidator : public Validator {
3787 public:
3788 ExistingPathValidator();
3789};
3790
3792class NonexistentPathValidator : public Validator {
3793 public:
3794 NonexistentPathValidator();
3795};
3796
3798class IPV4Validator : public Validator {
3799 public:
3800 IPV4Validator();
3801};
3802
3803class EscapedStringTransformer : public Validator {
3804 public:
3805 EscapedStringTransformer();
3806};
3807
3808} // namespace detail
3809
3810// Static is not needed here, because global const implies static.
3811
3813const detail::ExistingFileValidator ExistingFile;
3814
3816const detail::ExistingDirectoryValidator ExistingDirectory;
3817
3819const detail::ExistingPathValidator ExistingPath;
3820
3822const detail::NonexistentPathValidator NonexistentPath;
3823
3825const detail::IPV4Validator ValidIPV4;
3826
3828const detail::EscapedStringTransformer EscapedString;
3829
3831template <typename DesiredType> class TypeValidator : public Validator {
3832 public:
3833 explicit TypeValidator(const std::string &validator_name)
3834 : Validator(validator_name, [](std::string &input_string) {
3835 using CLI::detail::lexical_cast;
3836 auto val = DesiredType();
3837 if(!lexical_cast(input_string, val)) {
3838 return std::string("Failed parsing ") + input_string + " as a " + detail::type_name<DesiredType>();
3839 }
3840 return std::string();
3841 }) {}
3842 TypeValidator() : TypeValidator(detail::type_name<DesiredType>()) {}
3843};
3844
3846const TypeValidator<double> Number("NUMBER");
3847
3850class FileOnDefaultPath : public Validator {
3851 public:
3852 explicit FileOnDefaultPath(std::string default_path, bool enableErrorReturn = true);
3853};
3854
3856class Range : public Validator {
3857 public:
3862 template <typename T>
3863 Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) {
3864 if(validator_name.empty()) {
3865 std::stringstream out;
3866 out << detail::type_name<T>() << " in [" << min_val << " - " << max_val << "]";
3867 description(out.str());
3868 }
3869
3870 func_ = [min_val, max_val](std::string &input) {
3871 using CLI::detail::lexical_cast;
3872 T val;
3873 bool converted = lexical_cast(input, val);
3874 if((!converted) || (val < min_val || val > max_val)) {
3875 std::stringstream out;
3876 out << "Value " << input << " not in range [";
3877 out << min_val << " - " << max_val << "]";
3878 return out.str();
3879 }
3880 return std::string{};
3881 };
3882 }
3883
3885 template <typename T>
3886 explicit Range(T max_val, const std::string &validator_name = std::string{})
3887 : Range(static_cast<T>(0), max_val, validator_name) {}
3888};
3889
3891const Range NonNegativeNumber((std::numeric_limits<double>::max)(), "NONNEGATIVE");
3892
3894const Range PositiveNumber((std::numeric_limits<double>::min)(), (std::numeric_limits<double>::max)(), "POSITIVE");
3895
3897class Bound : public Validator {
3898 public:
3903 template <typename T> Bound(T min_val, T max_val) {
3904 std::stringstream out;
3905 out << detail::type_name<T>() << " bounded to [" << min_val << " - " << max_val << "]";
3906 description(out.str());
3907
3908 func_ = [min_val, max_val](std::string &input) {
3909 using CLI::detail::lexical_cast;
3910 T val;
3911 bool converted = lexical_cast(input, val);
3912 if(!converted) {
3913 return std::string("Value ") + input + " could not be converted";
3914 }
3915 if(val < min_val)
3916 input = detail::to_string(min_val);
3917 else if(val > max_val)
3918 input = detail::to_string(max_val);
3919
3920 return std::string{};
3921 };
3922 }
3923
3925 template <typename T> explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}
3926};
3927
3928namespace detail {
3929template <typename T,
3930 enable_if_t<is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
3931auto smart_deref(T value) -> decltype(*value) {
3932 return *value;
3933}
3934
3935template <
3936 typename T,
3937 enable_if_t<!is_copyable_ptr<typename std::remove_reference<T>::type>::value, detail::enabler> = detail::dummy>
3938typename std::remove_reference<T>::type &smart_deref(T &value) {
3939 return value;
3940}
3942template <typename T> std::string generate_set(const T &set) {
3943 using element_t = typename detail::element_type<T>::type;
3944 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
3945 std::string out(1, '{');
3946 out.append(detail::join(
3947 detail::smart_deref(set),
3948 [](const iteration_type_t &v) { return detail::pair_adaptor<element_t>::first(v); },
3949 ","));
3950 out.push_back('}');
3951 return out;
3952}
3953
3955template <typename T> std::string generate_map(const T &map, bool key_only = false) {
3956 using element_t = typename detail::element_type<T>::type;
3957 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
3958 std::string out(1, '{');
3959 out.append(detail::join(
3960 detail::smart_deref(map),
3961 [key_only](const iteration_type_t &v) {
3962 std::string res{detail::to_string(detail::pair_adaptor<element_t>::first(v))};
3963
3964 if(!key_only) {
3965 res.append("->");
3966 res += detail::to_string(detail::pair_adaptor<element_t>::second(v));
3967 }
3968 return res;
3969 },
3970 ","));
3971 out.push_back('}');
3972 return out;
3973}
3974
3975template <typename C, typename V> struct has_find {
3976 template <typename CC, typename VV>
3977 static auto test(int) -> decltype(std::declval<CC>().find(std::declval<VV>()), std::true_type());
3978 template <typename, typename> static auto test(...) -> decltype(std::false_type());
3979
3980 static const auto value = decltype(test<C, V>(0))::value;
3982};
3983
3985template <typename T, typename V, enable_if_t<!has_find<T, V>::value, detail::enabler> = detail::dummy>
3986auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
3987 using element_t = typename detail::element_type<T>::type;
3988 auto &setref = detail::smart_deref(set);
3989 auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) {
3990 return (detail::pair_adaptor<element_t>::first(v) == val);
3991 });
3992 return {(it != std::end(setref)), it};
3993}
3994
3996template <typename T, typename V, enable_if_t<has_find<T, V>::value, detail::enabler> = detail::dummy>
3997auto search(const T &set, const V &val) -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
3998 auto &setref = detail::smart_deref(set);
3999 auto it = setref.find(val);
4000 return {(it != std::end(setref)), it};
4001}
4002
4004template <typename T, typename V>
4005auto search(const T &set, const V &val, const std::function<V(V)> &filter_function)
4006 -> std::pair<bool, decltype(std::begin(detail::smart_deref(set)))> {
4007 using element_t = typename detail::element_type<T>::type;
4008 // do the potentially faster first search
4009 auto res = search(set, val);
4010 if((res.first) || (!(filter_function))) {
4011 return res;
4012 }
4013 // if we haven't found it do the longer linear search with all the element translations
4014 auto &setref = detail::smart_deref(set);
4015 auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) {
4016 V a{detail::pair_adaptor<element_t>::first(v)};
4017 a = filter_function(a);
4018 return (a == val);
4019 });
4020 return {(it != std::end(setref)), it};
4021}
4022
4023// the following suggestion was made by Nikita Ofitserov(@himikof)
4024// done in templates to prevent compiler warnings on negation of unsigned numbers
4025
4027template <typename T>
4028inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
4029 if((a > 0) == (b > 0)) {
4030 return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b));
4031 }
4032 return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b));
4033}
4035template <typename T>
4036inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) {
4037 return ((std::numeric_limits<T>::max)() / a < b);
4038}
4039
4041template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) {
4042 if(a == 0 || b == 0 || a == 1 || b == 1) {
4043 a *= b;
4044 return true;
4045 }
4046 if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) {
4047 return false;
4048 }
4049 if(overflowCheck(a, b)) {
4050 return false;
4051 }
4052 a *= b;
4053 return true;
4054}
4055
4057template <typename T>
4058typename std::enable_if<std::is_floating_point<T>::value, bool>::type checked_multiply(T &a, T b) {
4059 T c = a * b;
4060 if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) {
4061 return false;
4062 }
4063 a = c;
4064 return true;
4065}
4066
4067} // namespace detail
4069class IsMember : public Validator {
4070 public:
4071 using filter_fn_t = std::function<std::string(std::string)>;
4072
4074 template <typename T, typename... Args>
4075 IsMember(std::initializer_list<T> values, Args &&...args)
4076 : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
4077
4079 template <typename T> explicit IsMember(T &&set) : IsMember(std::forward<T>(set), nullptr) {}
4080
4083 template <typename T, typename F> explicit IsMember(T set, F filter_function) {
4084
4085 // Get the type of the contained item - requires a container have ::value_type
4086 // if the type does not have first_type and second_type, these are both value_type
4087 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
4088 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
4089
4090 using local_item_t = typename IsMemberType<item_t>::type; // This will convert bad types to good ones
4091 // (const char * to std::string)
4092
4093 // Make a local copy of the filter function, using a std::function if not one already
4094 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
4095
4096 // This is the type name for help, it will take the current version of the set contents
4097 desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); };
4098
4099 // This is the function that validates
4100 // It stores a copy of the set pointer-like, so shared_ptr will stay alive
4101 func_ = [set, filter_fn](std::string &input) {
4102 using CLI::detail::lexical_cast;
4103 local_item_t b;
4104 if(!lexical_cast(input, b)) {
4105 THROW ValidationError(input); // name is added later
4106 }
4107 if(filter_fn) {
4108 b = filter_fn(b);
4109 }
4110 auto res = detail::search(set, b, filter_fn);
4111 if(res.first) {
4112 // Make sure the version in the input string is identical to the one in the set
4113 if(filter_fn) {
4114 input = detail::value_string(detail::pair_adaptor<element_t>::first(*(res.second)));
4115 }
4116
4117 // Return empty error string (success)
4118 return std::string{};
4119 }
4120
4121 // If you reach this point, the result was not found
4122 return input + " not in " + detail::generate_set(detail::smart_deref(set));
4123 };
4124 }
4125
4127 template <typename T, typename... Args>
4128 IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
4129 : IsMember(
4130 std::forward<T>(set),
4131 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
4132 other...) {}
4133};
4134
4136template <typename T> using TransformPairs = std::vector<std::pair<std::string, T>>;
4137
4139class Transformer : public Validator {
4140 public:
4141 using filter_fn_t = std::function<std::string(std::string)>;
4142
4144 template <typename... Args>
4145 Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
4146 : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
4147
4149 template <typename T> explicit Transformer(T &&mapping) : Transformer(std::forward<T>(mapping), nullptr) {}
4150
4153 template <typename T, typename F> explicit Transformer(T mapping, F filter_function) {
4154
4155 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
4156 "mapping must produce value pairs");
4157 // Get the type of the contained item - requires a container have ::value_type
4158 // if the type does not have first_type and second_type, these are both value_type
4159 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
4160 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
4161 using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
4162 // (const char * to std::string)
4163
4164 // Make a local copy of the filter function, using a std::function if not one already
4165 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
4166
4167 // This is the type name for help, it will take the current version of the set contents
4168 desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); };
4169
4170 func_ = [mapping, filter_fn](std::string &input) {
4171 using CLI::detail::lexical_cast;
4172 local_item_t b;
4173 if(!lexical_cast(input, b)) {
4174 return std::string();
4175 // there is no possible way we can match anything in the mapping if we can't convert so just return
4176 }
4177 if(filter_fn) {
4178 b = filter_fn(b);
4179 }
4180 auto res = detail::search(mapping, b, filter_fn);
4181 if(res.first) {
4182 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
4183 }
4184 return std::string{};
4185 };
4186 }
4187
4189 template <typename T, typename... Args>
4190 Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
4191 : Transformer(
4192 std::forward<T>(mapping),
4193 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
4194 other...) {}
4195};
4196
4198class CheckedTransformer : public Validator {
4199 public:
4200 using filter_fn_t = std::function<std::string(std::string)>;
4201
4203 template <typename... Args>
4204 CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
4205 : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
4206
4208 template <typename T> explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {}
4209
4212 template <typename T, typename F> explicit CheckedTransformer(T mapping, F filter_function) {
4213
4214 static_assert(detail::pair_adaptor<typename detail::element_type<T>::type>::value,
4215 "mapping must produce value pairs");
4216 // Get the type of the contained item - requires a container have ::value_type
4217 // if the type does not have first_type and second_type, these are both value_type
4218 using element_t = typename detail::element_type<T>::type; // Removes (smart) pointers if needed
4219 using item_t = typename detail::pair_adaptor<element_t>::first_type; // Is value_type if not a map
4220 using local_item_t = typename IsMemberType<item_t>::type; // Will convert bad types to good ones
4221 // (const char * to std::string)
4222 using iteration_type_t = typename detail::pair_adaptor<element_t>::value_type; // the type of the object pair
4223
4224 // Make a local copy of the filter function, using a std::function if not one already
4225 std::function<local_item_t(local_item_t)> filter_fn = filter_function;
4226
4227 auto tfunc = [mapping]() {
4228 std::string out("value in ");
4229 out += detail::generate_map(detail::smart_deref(mapping)) + " OR {";
4230 out += detail::join(
4231 detail::smart_deref(mapping),
4232 [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor<element_t>::second(v)); },
4233 ",");
4234 out.push_back('}');
4235 return out;
4236 };
4237
4238 desc_function_ = tfunc;
4239
4240 func_ = [mapping, tfunc, filter_fn](std::string &input) {
4241 using CLI::detail::lexical_cast;
4242 local_item_t b;
4243 bool converted = lexical_cast(input, b);
4244 if(converted) {
4245 if(filter_fn) {
4246 b = filter_fn(b);
4247 }
4248 auto res = detail::search(mapping, b, filter_fn);
4249 if(res.first) {
4250 input = detail::value_string(detail::pair_adaptor<element_t>::second(*res.second));
4251 return std::string{};
4252 }
4253 }
4254 for(const auto &v : detail::smart_deref(mapping)) {
4255 auto output_string = detail::value_string(detail::pair_adaptor<element_t>::second(v));
4256 if(output_string == input) {
4257 return std::string();
4258 }
4259 }
4260
4261 return "Check " + input + " " + tfunc() + " FAILED";
4262 };
4263 }
4264
4266 template <typename T, typename... Args>
4267 CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
4268 : CheckedTransformer(
4269 std::forward<T>(mapping),
4270 [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
4271 other...) {}
4272};
4273
4275inline std::string ignore_case(std::string item) { return detail::to_lower(item); }
4276
4278inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); }
4279
4281inline std::string ignore_space(std::string item) {
4282 item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item));
4283 item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item));
4284 return item;
4285}
4286
4298class AsNumberWithUnit : public Validator {
4299 public:
4304 enum Options {
4305 CASE_SENSITIVE = 0,
4306 CASE_INSENSITIVE = 1,
4307 UNIT_OPTIONAL = 0,
4308 UNIT_REQUIRED = 2,
4309 DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL
4310 };
4311
4312 template <typename Number>
4313 explicit AsNumberWithUnit(std::map<std::string, Number> mapping,
4314 Options opts = DEFAULT,
4315 const std::string &unit_name = "UNIT") {
4316 description(generate_description<Number>(unit_name, opts));
4317 validate_mapping(mapping, opts);
4318
4319 // transform function
4320 func_ = [mapping, opts](std::string &input) -> std::string {
4321 Number num{};
4322
4323 detail::rtrim(input);
4324 if(input.empty()) {
4325 THROW ValidationError("Input is empty");
4326 }
4327
4328 // Find split position between number and prefix
4329 auto unit_begin = input.end();
4330 while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) {
4331 --unit_begin;
4332 }
4333
4334 std::string unit{unit_begin, input.end()};
4335 input.resize(static_cast<std::size_t>(std::distance(input.begin(), unit_begin)));
4336 detail::trim(input);
4337
4338 if(opts & UNIT_REQUIRED && unit.empty()) {
4339 THROW ValidationError("Missing mandatory unit");
4340 }
4341 if(opts & CASE_INSENSITIVE) {
4342 unit = detail::to_lower(unit);
4343 }
4344 if(unit.empty()) {
4345 using CLI::detail::lexical_cast;
4346 if(!lexical_cast(input, num)) {
4347 THROW ValidationError(std::string("Value ") + input + " could not be converted to " +
4348 detail::type_name<Number>());
4349 }
4350 // No need to modify input if no unit passed
4351 return {};
4352 }
4353
4354 // find corresponding factor
4355 auto it = mapping.find(unit);
4356 if(it == mapping.end()) {
4357 THROW ValidationError(unit +
4358 " unit not recognized. "
4359 "Allowed values: " +
4360 detail::generate_map(mapping, true));
4361 }
4362
4363 if(!input.empty()) {
4364 using CLI::detail::lexical_cast;
4365 bool converted = lexical_cast(input, num);
4366 if(!converted) {
4367 THROW ValidationError(std::string("Value ") + input + " could not be converted to " +
4368 detail::type_name<Number>());
4369 }
4370 // perform safe multiplication
4371 bool ok = detail::checked_multiply(num, it->second);
4372 if(!ok) {
4373 THROW ValidationError(detail::to_string(num) + " multiplied by " + unit +
4374 " factor would cause number overflow. Use smaller value.");
4375 }
4376 } else {
4377 num = static_cast<Number>(it->second);
4378 }
4379
4380 input = detail::to_string(num);
4381
4382 return {};
4383 };
4384 }
4385
4386 private:
4389 template <typename Number> static void validate_mapping(std::map<std::string, Number> &mapping, Options opts) {
4390 for(auto &kv : mapping) {
4391 if(kv.first.empty()) {
4392 THROW ValidationError("Unit must not be empty.");
4393 }
4394 if(!detail::isalpha(kv.first)) {
4395 THROW ValidationError("Unit must contain only letters.");
4396 }
4397 }
4398
4399 // make all units lowercase if CASE_INSENSITIVE
4400 if(opts & CASE_INSENSITIVE) {
4401 std::map<std::string, Number> lower_mapping;
4402 for(auto &kv : mapping) {
4403 auto s = detail::to_lower(kv.first);
4404 if(lower_mapping.count(s)) {
4405 THROW ValidationError(std::string("Several matching lowercase unit representations are found: ") +
4406 s);
4407 }
4408 lower_mapping[detail::to_lower(kv.first)] = kv.second;
4409 }
4410 mapping = std::move(lower_mapping);
4411 }
4412 }
4413
4415 template <typename Number> static std::string generate_description(const std::string &name, Options opts) {
4416 std::stringstream out;
4417 out << detail::type_name<Number>() << ' ';
4418 if(opts & UNIT_REQUIRED) {
4419 out << name;
4420 } else {
4421 out << '[' << name << ']';
4422 }
4423 return out.str();
4424 }
4425};
4426
4427inline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a, const AsNumberWithUnit::Options &b) {
4428 return static_cast<AsNumberWithUnit::Options>(static_cast<int>(a) | static_cast<int>(b));
4429}
4430
4442class AsSizeValue : public AsNumberWithUnit {
4443 public:
4444 using result_t = std::uint64_t;
4445
4453 explicit AsSizeValue(bool kb_is_1000);
4454
4455 private:
4457 static std::map<std::string, result_t> init_mapping(bool kb_is_1000);
4458
4460 static std::map<std::string, result_t> get_mapping(bool kb_is_1000);
4461};
4462
4463namespace detail {
4468CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline);
4469
4470} // namespace detail
4472
4473
4474
4475
4476CLI11_INLINE std::string Validator::operator()(std::string &str) const {
4477 std::string retstring;
4478 if(active_) {
4479 if(non_modifying_) {
4480 std::string value = str;
4481 retstring = func_(value);
4482 } else {
4483 retstring = func_(str);
4484 }
4485 }
4486 return retstring;
4487}
4488
4489CLI11_NODISCARD CLI11_INLINE Validator Validator::description(std::string validator_desc) const {
4490 Validator newval(*this);
4491 newval.desc_function_ = [validator_desc]() { return validator_desc; };
4492 return newval;
4493}
4494
4495CLI11_INLINE Validator Validator::operator&(const Validator &other) const {
4496 Validator newval;
4497
4498 newval._merge_description(*this, other, " AND ");
4499
4500 // Give references (will make a copy in lambda function)
4501 const std::function<std::string(std::string & filename)> &f1 = func_;
4502 const std::function<std::string(std::string & filename)> &f2 = other.func_;
4503
4504 newval.func_ = [f1, f2](std::string &input) {
4505 std::string s1 = f1(input);
4506 std::string s2 = f2(input);
4507 if(!s1.empty() && !s2.empty())
4508 return std::string("(") + s1 + ") AND (" + s2 + ")";
4509 return s1 + s2;
4510 };
4511
4512 newval.active_ = active_ && other.active_;
4513 newval.application_index_ = application_index_;
4514 return newval;
4515}
4516
4517CLI11_INLINE Validator Validator::operator|(const Validator &other) const {
4518 Validator newval;
4519
4520 newval._merge_description(*this, other, " OR ");
4521
4522 // Give references (will make a copy in lambda function)
4523 const std::function<std::string(std::string &)> &f1 = func_;
4524 const std::function<std::string(std::string &)> &f2 = other.func_;
4525
4526 newval.func_ = [f1, f2](std::string &input) {
4527 std::string s1 = f1(input);
4528 std::string s2 = f2(input);
4529 if(s1.empty() || s2.empty())
4530 return std::string();
4531
4532 return std::string("(") + s1 + ") OR (" + s2 + ")";
4533 };
4534 newval.active_ = active_ && other.active_;
4535 newval.application_index_ = application_index_;
4536 return newval;
4537}
4538
4539CLI11_INLINE Validator Validator::operator!() const {
4540 Validator newval;
4541 const std::function<std::string()> &dfunc1 = desc_function_;
4542 newval.desc_function_ = [dfunc1]() {
4543 auto str = dfunc1();
4544 return (!str.empty()) ? std::string("NOT ") + str : std::string{};
4545 };
4546 // Give references (will make a copy in lambda function)
4547 const std::function<std::string(std::string & res)> &f1 = func_;
4548
4549 newval.func_ = [f1, dfunc1](std::string &test) -> std::string {
4550 std::string s1 = f1(test);
4551 if(s1.empty()) {
4552 return std::string("check ") + dfunc1() + " succeeded improperly";
4553 }
4554 return std::string{};
4555 };
4556 newval.active_ = active_;
4557 newval.application_index_ = application_index_;
4558 return newval;
4559}
4560
4561CLI11_INLINE void
4562Validator::_merge_description(const Validator &val1, const Validator &val2, const std::string &merger) {
4563
4564 const std::function<std::string()> &dfunc1 = val1.desc_function_;
4565 const std::function<std::string()> &dfunc2 = val2.desc_function_;
4566
4567 desc_function_ = [=]() {
4568 std::string f1 = dfunc1();
4569 std::string f2 = dfunc2();
4570 if((f1.empty()) || (f2.empty())) {
4571 return f1 + f2;
4572 }
4573 return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')';
4574 };
4575}
4576
4577namespace detail {
4578
4579#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
4580CLI11_INLINE path_type check_path(const char *file) noexcept {
4581 std::error_code ec;
4582 auto stat = std::filesystem::status(to_path(file), ec);
4583 if(ec) {
4584 return path_type::nonexistent;
4585 }
4586 switch(stat.type()) {
4587 case std::filesystem::file_type::none: // LCOV_EXCL_LINE
4588 case std::filesystem::file_type::not_found:
4589 return path_type::nonexistent; // LCOV_EXCL_LINE
4590 case std::filesystem::file_type::directory:
4591 return path_type::directory;
4592 case std::filesystem::file_type::symlink:
4593 case std::filesystem::file_type::block:
4594 case std::filesystem::file_type::character:
4595 case std::filesystem::file_type::fifo:
4596 case std::filesystem::file_type::socket:
4597 case std::filesystem::file_type::regular:
4598 case std::filesystem::file_type::unknown:
4599 default:
4600 return path_type::file;
4601 }
4602}
4603#else
4604CLI11_INLINE path_type check_path(const char *file) noexcept {
4605#if defined(_MSC_VER)
4606 struct __stat64 buffer;
4607 if(_stat64(file, &buffer) == 0) {
4608 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
4609 }
4610#else
4611 struct stat buffer;
4612 if(stat(file, &buffer) == 0) {
4613 return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file;
4614 }
4615#endif
4616 return path_type::nonexistent;
4617}
4618#endif
4619
4620CLI11_INLINE ExistingFileValidator::ExistingFileValidator() : Validator("FILE") {
4621 func_ = [](std::string &filename) {
4622 auto path_result = check_path(filename.c_str());
4623 if(path_result == path_type::nonexistent) {
4624 return "File does not exist: " + filename;
4625 }
4626 if(path_result == path_type::directory) {
4627 return "File is actually a directory: " + filename;
4628 }
4629 return std::string();
4630 };
4631}
4632
4633CLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator() : Validator("DIR") {
4634 func_ = [](std::string &filename) {
4635 auto path_result = check_path(filename.c_str());
4636 if(path_result == path_type::nonexistent) {
4637 return "Directory does not exist: " + filename;
4638 }
4639 if(path_result == path_type::file) {
4640 return "Directory is actually a file: " + filename;
4641 }
4642 return std::string();
4643 };
4644}
4645
4646CLI11_INLINE ExistingPathValidator::ExistingPathValidator() : Validator("PATH(existing)") {
4647 func_ = [](std::string &filename) {
4648 auto path_result = check_path(filename.c_str());
4649 if(path_result == path_type::nonexistent) {
4650 return "Path does not exist: " + filename;
4651 }
4652 return std::string();
4653 };
4654}
4655
4656CLI11_INLINE NonexistentPathValidator::NonexistentPathValidator() : Validator("PATH(non-existing)") {
4657 func_ = [](std::string &filename) {
4658 auto path_result = check_path(filename.c_str());
4659 if(path_result != path_type::nonexistent) {
4660 return "Path already exists: " + filename;
4661 }
4662 return std::string();
4663 };
4664}
4665
4666CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") {
4667 func_ = [](std::string &ip_addr) {
4668 auto result = CLI::detail::split(ip_addr, '.');
4669 if(result.size() != 4) {
4670 return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')';
4671 }
4672 int num = 0;
4673 for(const auto &var : result) {
4674 using CLI::detail::lexical_cast;
4675 bool retval = lexical_cast(var, num);
4676 if(!retval) {
4677 return std::string("Failed parsing number (") + var + ')';
4678 }
4679 if(num < 0 || num > 255) {
4680 return std::string("Each IP number must be between 0 and 255 ") + var;
4681 }
4682 }
4683 return std::string{};
4684 };
4685}
4686
4687CLI11_INLINE EscapedStringTransformer::EscapedStringTransformer() {
4688 func_ = [](std::string &str) {
4689 try {
4690 if(str.size() > 1 && (str.front() == '\"' || str.front() == '\'' || str.front() == '`') &&
4691 str.front() == str.back()) {
4692 process_quoted_string(str);
4693 } else if(str.find_first_of('\\') != std::string::npos) {
4694 if(detail::is_binary_escaped_string(str)) {
4695 str = detail::extract_binary_string(str);
4696 } else {
4697 str = remove_escaped_characters(str);
4698 }
4699 }
4700 return std::string{};
4701 } catch(const std::invalid_argument &ia) {
4702#ifndef BB_NO_EXCEPTIONS
4703 return std::string(ia.what());
4704#endif
4705 }
4706 };
4707}
4708} // namespace detail
4709
4710CLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path, bool enableErrorReturn)
4711 : Validator("FILE") {
4712 func_ = [default_path, enableErrorReturn](std::string &filename) {
4713 auto path_result = detail::check_path(filename.c_str());
4714 if(path_result == detail::path_type::nonexistent) {
4715 std::string test_file_path = default_path;
4716 if(default_path.back() != '/' && default_path.back() != '\\') {
4717 // Add folder separator
4718 test_file_path += '/';
4719 }
4720 test_file_path.append(filename);
4721 path_result = detail::check_path(test_file_path.c_str());
4722 if(path_result == detail::path_type::file) {
4723 filename = test_file_path;
4724 } else {
4725 if(enableErrorReturn) {
4726 return "File does not exist: " + filename;
4727 }
4728 }
4729 }
4730 return std::string{};
4731 };
4732}
4733
4734CLI11_INLINE AsSizeValue::AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) {
4735 if(kb_is_1000) {
4736 description("SIZE [b, kb(=1000b), kib(=1024b), ...]");
4737 } else {
4738 description("SIZE [b, kb(=1024b), ...]");
4739 }
4740}
4741
4742CLI11_INLINE std::map<std::string, AsSizeValue::result_t> AsSizeValue::init_mapping(bool kb_is_1000) {
4744 result_t k_factor = kb_is_1000 ? 1000 : 1024;
4745 result_t ki_factor = 1024;
4746 result_t k = 1;
4747 result_t ki = 1;
4748 m["b"] = 1;
4749 for(std::string p : {"k", "m", "g", "t", "p", "e"}) {
4750 k *= k_factor;
4751 ki *= ki_factor;
4752 m[p] = k;
4753 m[p + "b"] = k;
4754 m[p + "i"] = ki;
4755 m[p + "ib"] = ki;
4756 }
4757 return m;
4758}
4759
4760CLI11_INLINE std::map<std::string, AsSizeValue::result_t> AsSizeValue::get_mapping(bool kb_is_1000) {
4761 if(kb_is_1000) {
4762 static auto m = init_mapping(true);
4763 return m;
4764 }
4765 static auto m = init_mapping(false);
4766 return m;
4767}
4768
4769namespace detail {
4770
4771CLI11_INLINE std::pair<std::string, std::string> split_program_name(std::string commandline) {
4772 // try to determine the programName
4774 trim(commandline);
4775 auto esp = commandline.find_first_of(' ', 1);
4776 while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) {
4777 esp = commandline.find_first_of(' ', esp + 1);
4778 if(esp == std::string::npos) {
4779 // if we have reached the end and haven't found a valid file just assume the first argument is the
4780 // program name
4781 if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') {
4782 bool embeddedQuote = false;
4783 auto keyChar = commandline[0];
4784 auto end = commandline.find_first_of(keyChar, 1);
4785 while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes
4786 end = commandline.find_first_of(keyChar, end + 1);
4787 embeddedQuote = true;
4788 }
4789 if(end != std::string::npos) {
4790 vals.first = commandline.substr(1, end - 1);
4791 esp = end + 1;
4792 if(embeddedQuote) {
4793 vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar));
4794 }
4795 } else {
4796 esp = commandline.find_first_of(' ', 1);
4797 }
4798 } else {
4799 esp = commandline.find_first_of(' ', 1);
4800 }
4801
4802 break;
4803 }
4804 }
4805 if(vals.first.empty()) {
4806 vals.first = commandline.substr(0, esp);
4807 rtrim(vals.first);
4808 }
4809
4810 // strip the program name
4811 vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1) : std::string{};
4812 ltrim(vals.second);
4813 return vals;
4814}
4815
4816} // namespace detail
4818
4819
4820
4821
4822class Option;
4823class App;
4824
4829
4830enum class AppFormatMode {
4831 Normal,
4832 All,
4833 Sub,
4834};
4835
4840class FormatterBase {
4841 protected:
4844
4846 std::size_t column_width_{30};
4847
4850 std::map<std::string, std::string> labels_{};
4851
4855
4856 public:
4857 FormatterBase() = default;
4858 FormatterBase(const FormatterBase &) = default;
4859 FormatterBase(FormatterBase &&) = default;
4860 FormatterBase &operator=(const FormatterBase &) = default;
4861 FormatterBase &operator=(FormatterBase &&) = default;
4862
4864 virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default)
4865
4867 virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0;
4868
4872
4874 void label(std::string key, std::string val) { labels_[key] = val; }
4875
4877 void column_width(std::size_t val) { column_width_ = val; }
4878
4882
4884 CLI11_NODISCARD std::string get_label(std::string key) const {
4885 if(labels_.find(key) == labels_.end())
4886 return key;
4887 return labels_.at(key);
4888 }
4889
4891 CLI11_NODISCARD std::size_t get_column_width() const { return column_width_; }
4892
4894};
4895
4897class FormatterLambda final : public FormatterBase {
4898 using funct_t = std::function<std::string(const App *, std::string, AppFormatMode)>;
4899
4901 funct_t lambda_;
4902
4903 public:
4905 explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {}
4906
4908 ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default)
4909
4911 std::string make_help(const App *app, std::string name, AppFormatMode mode) const override {
4912 return lambda_(app, name, mode);
4913 }
4914};
4915
4918class Formatter : public FormatterBase {
4919 public:
4920 Formatter() = default;
4921 Formatter(const Formatter &) = default;
4922 Formatter(Formatter &&) = default;
4923 Formatter &operator=(const Formatter &) = default;
4924 Formatter &operator=(Formatter &&) = default;
4925
4928
4931 CLI11_NODISCARD virtual std::string
4932 make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const;
4933
4935 virtual std::string make_positionals(const App *app) const;
4936
4938 std::string make_groups(const App *app, AppFormatMode mode) const;
4939
4941 virtual std::string make_subcommands(const App *app, AppFormatMode mode) const;
4942
4944 virtual std::string make_subcommand(const App *sub) const;
4945
4947 virtual std::string make_expanded(const App *sub) const;
4948
4950 virtual std::string make_footer(const App *app) const;
4951
4953 virtual std::string make_description(const App *app) const;
4954
4956 virtual std::string make_usage(const App *app, std::string name) const;
4957
4959 std::string make_help(const App * /*app*/, std::string, AppFormatMode) const override;
4960
4964
4966 virtual std::string make_option(const Option *opt, bool is_positional) const {
4967 std::stringstream out;
4968 detail::format_help(
4969 out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_);
4970 return out.str();
4971 }
4972
4974 virtual std::string make_option_name(const Option *, bool) const;
4975
4977 virtual std::string make_option_opts(const Option *) const;
4978
4980 virtual std::string make_option_desc(const Option *) const;
4981
4983 virtual std::string make_option_usage(const Option *opt) const;
4984
4986};
4987
4988
4989
4990
4991using results_t = std::vector<std::string>;
4993using callback_t = std::function<bool(const results_t &)>;
4994
4995class Option;
4996class App;
4997
4998using Option_p = std::unique_ptr<Option>;
5000enum class MultiOptionPolicy : char {
5001 Throw,
5002 TakeLast,
5003 TakeFirst,
5004 Join,
5005 TakeAll,
5006 Sum,
5007 Reverse,
5008};
5009
5012template <typename CRTP> class OptionBase {
5013 friend App;
5014
5015 protected:
5017 std::string group_ = std::string("Options");
5018
5020 bool required_{false};
5021
5023 bool ignore_case_{false};
5024
5026 bool ignore_underscore_{false};
5027
5029 bool configurable_{true};
5030
5032 bool disable_flag_override_{false};
5033
5035 char delimiter_{'\0'};
5036
5038 bool always_capture_default_{false};
5039
5041 MultiOptionPolicy multi_option_policy_{MultiOptionPolicy::Throw};
5042
5044 template <typename T> void copy_to(T *other) const;
5045
5046 public:
5047 // setters
5048
5050 CRTP *group(const std::string &name) {
5051 if(!detail::valid_alias_name_string(name)) {
5052 THROW IncorrectConstruction("Group names may not contain newlines or null characters");
5053 }
5054 group_ = name;
5055 return static_cast<CRTP *>(this);
5056 }
5057
5059 CRTP *required(bool value = true) {
5060 required_ = value;
5061 return static_cast<CRTP *>(this);
5062 }
5063
5065 CRTP *mandatory(bool value = true) { return required(value); }
5066
5067 CRTP *always_capture_default(bool value = true) {
5068 always_capture_default_ = value;
5069 return static_cast<CRTP *>(this);
5070 }
5071
5072 // Getters
5073
5075 CLI11_NODISCARD const std::string &get_group() const { return group_; }
5076
5078 CLI11_NODISCARD bool get_required() const { return required_; }
5079
5081 CLI11_NODISCARD bool get_ignore_case() const { return ignore_case_; }
5082
5084 CLI11_NODISCARD bool get_ignore_underscore() const { return ignore_underscore_; }
5085
5087 CLI11_NODISCARD bool get_configurable() const { return configurable_; }
5088
5090 CLI11_NODISCARD bool get_disable_flag_override() const { return disable_flag_override_; }
5091
5093 CLI11_NODISCARD char get_delimiter() const { return delimiter_; }
5094
5096 CLI11_NODISCARD bool get_always_capture_default() const { return always_capture_default_; }
5097
5099 CLI11_NODISCARD MultiOptionPolicy get_multi_option_policy() const { return multi_option_policy_; }
5100
5101 // Shortcuts for multi option policy
5102
5104 CRTP *take_last() {
5105 auto *self = static_cast<CRTP *>(this);
5106 self->multi_option_policy(MultiOptionPolicy::TakeLast);
5107 return self;
5108 }
5109
5111 CRTP *take_first() {
5112 auto *self = static_cast<CRTP *>(this);
5113 self->multi_option_policy(MultiOptionPolicy::TakeFirst);
5114 return self;
5115 }
5116
5118 CRTP *take_all() {
5119 auto self = static_cast<CRTP *>(this);
5120 self->multi_option_policy(MultiOptionPolicy::TakeAll);
5121 return self;
5122 }
5123
5125 CRTP *join() {
5126 auto *self = static_cast<CRTP *>(this);
5127 self->multi_option_policy(MultiOptionPolicy::Join);
5128 return self;
5129 }
5130
5132 CRTP *join(char delim) {
5133 auto self = static_cast<CRTP *>(this);
5134 self->delimiter_ = delim;
5135 self->multi_option_policy(MultiOptionPolicy::Join);
5136 return self;
5137 }
5138
5140 CRTP *configurable(bool value = true) {
5141 configurable_ = value;
5142 return static_cast<CRTP *>(this);
5143 }
5144
5146 CRTP *delimiter(char value = '\0') {
5147 delimiter_ = value;
5148 return static_cast<CRTP *>(this);
5149 }
5150};
5151
5154class OptionDefaults : public OptionBase<OptionDefaults> {
5155 public:
5156 OptionDefaults() = default;
5157
5158 // Methods here need a different implementation if they are Option vs. OptionDefault
5159
5161 OptionDefaults *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
5162 multi_option_policy_ = value;
5163 return this;
5164 }
5165
5167 OptionDefaults *ignore_case(bool value = true) {
5168 ignore_case_ = value;
5169 return this;
5170 }
5171
5173 OptionDefaults *ignore_underscore(bool value = true) {
5174 ignore_underscore_ = value;
5175 return this;
5176 }
5177
5179 OptionDefaults *disable_flag_override(bool value = true) {
5180 disable_flag_override_ = value;
5181 return this;
5182 }
5183
5185 OptionDefaults *delimiter(char value = '\0') {
5186 delimiter_ = value;
5187 return this;
5188 }
5189};
5190
5191class Option : public OptionBase<Option> {
5192 friend App;
5193
5194 protected:
5197
5199 std::vector<std::string> snames_{};
5200
5202 std::vector<std::string> lnames_{};
5203
5207
5209 std::vector<std::string> fnames_{};
5210
5212 std::string pname_{};
5213
5215 std::string envname_{};
5216
5220
5222 std::string description_{};
5223
5225 std::string default_str_{};
5226
5228 std::string option_text_{};
5229
5233 std::function<std::string()> type_name_{[]() { return std::string(); }};
5234
5236 std::function<std::string()> default_function_{};
5237
5241
5244 int type_size_max_{1};
5246 int type_size_min_{1};
5247
5249 int expected_min_{1};
5251 int expected_max_{1};
5252
5254 std::vector<Validator> validators_{};
5255
5257 std::set<Option *> needs_{};
5258
5260 std::set<Option *> excludes_{};
5261
5265
5267 App *parent_{nullptr};
5268
5270 callback_t callback_{};
5271
5275
5277 results_t results_{};
5279 results_t proc_results_{};
5281 enum class option_state : char {
5282 parsing = 0,
5283 validated = 2,
5284 reduced = 4,
5285 callback_run = 6,
5286 };
5288 option_state current_option_state_{option_state::parsing};
5290 bool allow_extra_args_{false};
5292 bool flag_like_{false};
5294 bool run_callback_for_default_{false};
5296 bool inject_separator_{false};
5298 bool trigger_on_result_{false};
5300 bool force_callback_{false};
5302
5304 Option(std::string option_name, std::string option_description, callback_t callback, App *parent)
5305 : description_(std::move(option_description)), parent_(parent), callback_(std::move(callback)) {
5306 std::tie(snames_, lnames_, pname_) = detail::get_names(detail::split_names(option_name));
5307 }
5308
5309 public:
5312
5313 Option(const Option &) = delete;
5314 Option &operator=(const Option &) = delete;
5315
5317 CLI11_NODISCARD std::size_t count() const { return results_.size(); }
5318
5320 CLI11_NODISCARD bool empty() const { return results_.empty(); }
5321
5323 explicit operator bool() const { return !empty() || force_callback_; }
5324
5326 void clear() {
5327 results_.clear();
5328 current_option_state_ = option_state::parsing;
5329 }
5330
5334
5336 Option *expected(int value);
5337
5339 Option *expected(int value_min, int value_max);
5340
5343 Option *allow_extra_args(bool value = true) {
5344 allow_extra_args_ = value;
5345 return this;
5346 }
5348 CLI11_NODISCARD bool get_allow_extra_args() const { return allow_extra_args_; }
5350 Option *trigger_on_parse(bool value = true) {
5351 trigger_on_result_ = value;
5352 return this;
5353 }
5355 CLI11_NODISCARD bool get_trigger_on_parse() const { return trigger_on_result_; }
5356
5358 Option *force_callback(bool value = true) {
5359 force_callback_ = value;
5360 return this;
5361 }
5363 CLI11_NODISCARD bool get_force_callback() const { return force_callback_; }
5364
5367 Option *run_callback_for_default(bool value = true) {
5368 run_callback_for_default_ = value;
5369 return this;
5370 }
5372 CLI11_NODISCARD bool get_run_callback_for_default() const { return run_callback_for_default_; }
5373
5375 Option *check(Validator validator, const std::string &validator_name = "");
5376
5378 Option *check(std::function<std::string(const std::string &)> Validator,
5379 std::string Validator_description = "",
5380 std::string Validator_name = "");
5381
5383 Option *transform(Validator Validator, const std::string &Validator_name = "");
5384
5386 Option *transform(const std::function<std::string(std::string)> &func,
5387 std::string transform_description = "",
5388 std::string transform_name = "");
5389
5391 Option *each(const std::function<void(std::string)> &func);
5392
5394 Validator *get_validator(const std::string &Validator_name = "");
5395
5397 Validator *get_validator(int index);
5398
5400 Option *needs(Option *opt) {
5401 if(opt != this) {
5402 needs_.insert(opt);
5403 }
5404 return this;
5405 }
5406
5408 template <typename T = App> Option *needs(std::string opt_name) {
5409 auto opt = static_cast<T *>(parent_)->get_option_no_THROW(opt_name);
5410 if(opt == nullptr) {
5411 THROW IncorrectConstruction::MissingOption(opt_name);
5412 }
5413 return needs(opt);
5414 }
5415
5417 template <typename A, typename B, typename... ARG> Option *needs(A opt, B opt1, ARG... args) {
5418 needs(opt);
5419 return needs(opt1, args...); // NOLINT(readability-suspicious-call-argument)
5420 }
5421
5423 bool remove_needs(Option *opt);
5424
5426 Option *excludes(Option *opt);
5427
5429 template <typename T = App> Option *excludes(std::string opt_name) {
5430 auto opt = static_cast<T *>(parent_)->get_option_no_THROW(opt_name);
5431 if(opt == nullptr) {
5432 THROW IncorrectConstruction::MissingOption(opt_name);
5433 }
5434 return excludes(opt);
5435 }
5436
5438 template <typename A, typename B, typename... ARG> Option *excludes(A opt, B opt1, ARG... args) {
5439 excludes(opt);
5440 return excludes(opt1, args...);
5441 }
5442
5444 bool remove_excludes(Option *opt);
5445
5447 Option *envname(std::string name) {
5448 envname_ = std::move(name);
5449 return this;
5450 }
5451
5456 template <typename T = App> Option *ignore_case(bool value = true);
5457
5462 template <typename T = App> Option *ignore_underscore(bool value = true);
5463
5465 Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw);
5466
5468 Option *disable_flag_override(bool value = true) {
5469 disable_flag_override_ = value;
5470 return this;
5471 }
5475
5477 CLI11_NODISCARD int get_type_size() const { return type_size_min_; }
5478
5480 CLI11_NODISCARD int get_type_size_min() const { return type_size_min_; }
5482 CLI11_NODISCARD int get_type_size_max() const { return type_size_max_; }
5483
5485 CLI11_NODISCARD bool get_inject_separator() const { return inject_separator_; }
5486
5488 CLI11_NODISCARD std::string get_envname() const { return envname_; }
5489
5491 CLI11_NODISCARD std::set<Option *> get_needs() const { return needs_; }
5492
5494 CLI11_NODISCARD std::set<Option *> get_excludes() const { return excludes_; }
5495
5497 CLI11_NODISCARD std::string get_default_str() const { return default_str_; }
5498
5500 CLI11_NODISCARD callback_t get_callback() const { return callback_; }
5501
5503 CLI11_NODISCARD const std::vector<std::string> &get_lnames() const { return lnames_; }
5504
5506 CLI11_NODISCARD const std::vector<std::string> &get_snames() const { return snames_; }
5507
5509 CLI11_NODISCARD const std::vector<std::string> &get_fnames() const { return fnames_; }
5511 CLI11_NODISCARD const std::string &get_single_name() const {
5512 if(!lnames_.empty()) {
5513 return lnames_[0];
5514 }
5515 if(!snames_.empty()) {
5516 return snames_[0];
5517 }
5518 if(!pname_.empty()) {
5519 return pname_;
5520 }
5521 return envname_;
5522 }
5524 CLI11_NODISCARD int get_expected() const { return expected_min_; }
5525
5527 CLI11_NODISCARD int get_expected_min() const { return expected_min_; }
5529 CLI11_NODISCARD int get_expected_max() const { return expected_max_; }
5530
5532 CLI11_NODISCARD int get_items_expected_min() const { return type_size_min_ * expected_min_; }
5533
5535 CLI11_NODISCARD int get_items_expected_max() const {
5536 int t = type_size_max_;
5537 return detail::checked_multiply(t, expected_max_) ? t : detail::expected_max_vector_size;
5538 }
5540 CLI11_NODISCARD int get_items_expected() const { return get_items_expected_min(); }
5541
5543 CLI11_NODISCARD bool get_positional() const { return !pname_.empty(); }
5544
5546 CLI11_NODISCARD bool nonpositional() const { return (!lnames_.empty() || !snames_.empty()); }
5547
5549 CLI11_NODISCARD bool has_description() const { return !description_.empty(); }
5550
5552 CLI11_NODISCARD const std::string &get_description() const { return description_; }
5553
5555 Option *description(std::string option_description) {
5556 description_ = std::move(option_description);
5557 return this;
5558 }
5559
5560 Option *option_text(std::string text) {
5561 option_text_ = std::move(text);
5562 return this;
5563 }
5564
5565 CLI11_NODISCARD const std::string &get_option_text() const { return option_text_; }
5566
5570
5575 CLI11_NODISCARD std::string get_name(bool positional = false,
5576 bool all_options = false
5577 ) const;
5578
5582
5584 void run_callback();
5585
5587 CLI11_NODISCARD const std::string &matching_name(const Option &other) const;
5588
5590 bool operator==(const Option &other) const { return !matching_name(other).empty(); }
5591
5593 CLI11_NODISCARD bool check_name(const std::string &name) const;
5594
5596 CLI11_NODISCARD bool check_sname(std::string name) const {
5597 return (detail::find_member(std::move(name), snames_, ignore_case_) >= 0);
5598 }
5599
5601 CLI11_NODISCARD bool check_lname(std::string name) const {
5602 return (detail::find_member(std::move(name), lnames_, ignore_case_, ignore_underscore_) >= 0);
5603 }
5604
5606 CLI11_NODISCARD bool check_fname(std::string name) const {
5607 if(fnames_.empty()) {
5608 return false;
5609 }
5610 return (detail::find_member(std::move(name), fnames_, ignore_case_, ignore_underscore_) >= 0);
5611 }
5612
5615 CLI11_NODISCARD std::string get_flag_value(const std::string &name, std::string input_value) const;
5616
5618 Option *add_result(std::string s);
5619
5621 Option *add_result(std::string s, int &results_added);
5622
5624 Option *add_result(std::vector<std::string> s);
5625
5627 CLI11_NODISCARD const results_t &results() const { return results_; }
5628
5630 CLI11_NODISCARD results_t reduced_results() const;
5631
5633 template <typename T> void results(T &output) const {
5634 bool retval = false;
5635 if(current_option_state_ >= option_state::reduced || (results_.size() == 1 && validators_.empty())) {
5636 const results_t &res = (proc_results_.empty()) ? results_ : proc_results_;
5637 retval = detail::lexical_conversion<T, T>(res, output);
5638 } else {
5639 results_t res;
5640 if(results_.empty()) {
5641 if(!default_str_.empty()) {
5642 // _add_results takes an rvalue only
5643 _add_result(std::string(default_str_), res);
5644 _validate_results(res);
5645 results_t extra;
5646 _reduce_results(extra, res);
5647 if(!extra.empty()) {
5648 res = std::move(extra);
5649 }
5650 } else {
5651 res.emplace_back();
5652 }
5653 } else {
5654 res = reduced_results();
5655 }
5656 retval = detail::lexical_conversion<T, T>(res, output);
5657 }
5658 if(!retval) {
5659 THROW ConversionError(get_name(), results_);
5660 }
5661 }
5662
5664 template <typename T> CLI11_NODISCARD T as() const {
5665 T output;
5666 results(output);
5667 return output;
5668 }
5669
5671 CLI11_NODISCARD bool get_callback_run() const { return (current_option_state_ == option_state::callback_run); }
5672
5676
5678 Option *type_name_fn(std::function<std::string()> typefun) {
5679 type_name_ = std::move(typefun);
5680 return this;
5681 }
5682
5684 Option *type_name(std::string typeval) {
5685 type_name_fn([typeval]() { return typeval; });
5686 return this;
5687 }
5688
5690 Option *type_size(int option_type_size);
5691
5693 Option *type_size(int option_type_size_min, int option_type_size_max);
5694
5696 void inject_separator(bool value = true) { inject_separator_ = value; }
5697
5699 Option *default_function(const std::function<std::string()> &func) {
5700 default_function_ = func;
5701 return this;
5702 }
5703
5705 Option *capture_default_str() {
5706 if(default_function_) {
5707 default_str_ = default_function_();
5708 }
5709 return this;
5710 }
5711
5713 Option *default_str(std::string val) {
5714 default_str_ = std::move(val);
5715 return this;
5716 }
5717
5720 template <typename X> Option *default_val(const X &val) {
5721 std::string val_str = detail::to_string(val);
5722 auto old_option_state = current_option_state_;
5723 results_t old_results{std::move(results_)};
5724 results_.clear();
5725 try {
5726 add_result(val_str);
5727 // if trigger_on_result_ is set the callback already ran
5728 if(run_callback_for_default_ && !trigger_on_result_) {
5729 run_callback(); // run callback sets the state, we need to reset it again
5730 current_option_state_ = option_state::parsing;
5731 } else {
5732 _validate_results(results_);
5733 current_option_state_ = old_option_state;
5734 }
5735 } catch(const CLI::Error &) {
5736 // this should be done
5737 results_ = std::move(old_results);
5738 current_option_state_ = old_option_state;
5739 RETHROW;
5740 }
5741 results_ = std::move(old_results);
5742 default_str_ = std::move(val_str);
5743 return this;
5744 }
5745
5747 CLI11_NODISCARD std::string get_type_name() const;
5748
5749 private:
5751 void _validate_results(results_t &res) const;
5752
5756 void _reduce_results(results_t &out, const results_t &original) const;
5757
5758 // Run a result through the Validators
5759 std::string _validate(std::string &result, int index) const;
5760
5762 int _add_result(std::string &&result, std::vector<std::string> &res) const;
5763};
5764
5765
5766
5767
5768template <typename CRTP> template <typename T> void OptionBase<CRTP>::copy_to(T *other) const {
5769 other->group(group_);
5770 other->required(required_);
5771 other->ignore_case(ignore_case_);
5772 other->ignore_underscore(ignore_underscore_);
5773 other->configurable(configurable_);
5774 other->disable_flag_override(disable_flag_override_);
5775 other->delimiter(delimiter_);
5776 other->always_capture_default(always_capture_default_);
5777 other->multi_option_policy(multi_option_policy_);
5778}
5779
5780CLI11_INLINE Option *Option::expected(int value) {
5781 if(value < 0) {
5782 expected_min_ = -value;
5783 if(expected_max_ < expected_min_) {
5784 expected_max_ = expected_min_;
5785 }
5786 allow_extra_args_ = true;
5787 flag_like_ = false;
5788 } else if(value == detail::expected_max_vector_size) {
5789 expected_min_ = 1;
5790 expected_max_ = detail::expected_max_vector_size;
5791 allow_extra_args_ = true;
5792 flag_like_ = false;
5793 } else {
5794 expected_min_ = value;
5795 expected_max_ = value;
5796 flag_like_ = (expected_min_ == 0);
5797 }
5798 return this;
5799}
5800
5801CLI11_INLINE Option *Option::expected(int value_min, int value_max) {
5802 if(value_min < 0) {
5803 value_min = -value_min;
5804 }
5805
5806 if(value_max < 0) {
5807 value_max = detail::expected_max_vector_size;
5808 }
5809 if(value_max < value_min) {
5810 expected_min_ = value_max;
5811 expected_max_ = value_min;
5812 } else {
5813 expected_max_ = value_max;
5814 expected_min_ = value_min;
5815 }
5816
5817 return this;
5818}
5819
5820CLI11_INLINE Option *Option::check(Validator validator, const std::string &validator_name) {
5821 validator.non_modifying();
5822 validators_.push_back(std::move(validator));
5823 if(!validator_name.empty())
5824 validators_.back().name(validator_name);
5825 return this;
5826}
5827
5828CLI11_INLINE Option *Option::check(std::function<std::string(const std::string &)> Validator,
5829 std::string Validator_description,
5830 std::string Validator_name) {
5831 validators_.emplace_back(Validator, std::move(Validator_description), std::move(Validator_name));
5832 validators_.back().non_modifying();
5833 return this;
5834}
5835
5836CLI11_INLINE Option *Option::transform(Validator Validator, const std::string &Validator_name) {
5837 validators_.insert(validators_.begin(), std::move(Validator));
5838 if(!Validator_name.empty())
5839 validators_.front().name(Validator_name);
5840 return this;
5841}
5842
5843CLI11_INLINE Option *Option::transform(const std::function<std::string(std::string)> &func,
5844 std::string transform_description,
5845 std::string transform_name) {
5846 validators_.insert(validators_.begin(),
5847 Validator(
5848 [func](std::string &val) {
5849 val = func(val);
5850 return std::string{};
5851 },
5852 std::move(transform_description),
5853 std::move(transform_name)));
5854
5855 return this;
5856}
5857
5858CLI11_INLINE Option *Option::each(const std::function<void(std::string)> &func) {
5859 validators_.emplace_back(
5860 [func](std::string &inout) {
5861 func(inout);
5862 return std::string{};
5863 },
5864 std::string{});
5865 return this;
5866}
5867
5868CLI11_INLINE Validator *Option::get_validator(const std::string &Validator_name) {
5869 for(auto &Validator : validators_) {
5870 if(Validator_name == Validator.get_name()) {
5871 return &Validator;
5872 }
5873 }
5874 if((Validator_name.empty()) && (!validators_.empty())) {
5875 return &(validators_.front());
5876 }
5877 THROW OptionNotFound(std::string{"Validator "} + Validator_name + " Not Found");
5878}
5879
5880CLI11_INLINE Validator *Option::get_validator(int index) {
5881 // This is an signed int so that it is not equivalent to a pointer.
5882 if(index >= 0 && index < static_cast<int>(validators_.size())) {
5883 return &(validators_[static_cast<decltype(validators_)::size_type>(index)]);
5884 }
5885 THROW OptionNotFound("Validator index is not valid");
5886}
5887
5888CLI11_INLINE bool Option::remove_needs(Option *opt) {
5889 auto iterator = std::find(std::begin(needs_), std::end(needs_), opt);
5890
5891 if(iterator == std::end(needs_)) {
5892 return false;
5893 }
5894 needs_.erase(iterator);
5895 return true;
5896}
5897
5898CLI11_INLINE Option *Option::excludes(Option *opt) {
5899 if(opt == this) {
5900 THROW(IncorrectConstruction("and option cannot exclude itself"));
5901 }
5902 excludes_.insert(opt);
5903
5904 // Help text should be symmetric - excluding a should exclude b
5905 opt->excludes_.insert(this);
5906
5907 // Ignoring the insert return value, excluding twice is now allowed.
5908 // (Mostly to allow both directions to be excluded by user, even though the library does it for you.)
5909
5910 return this;
5911}
5912
5913CLI11_INLINE bool Option::remove_excludes(Option *opt) {
5914 auto iterator = std::find(std::begin(excludes_), std::end(excludes_), opt);
5915
5916 if(iterator == std::end(excludes_)) {
5917 return false;
5918 }
5919 excludes_.erase(iterator);
5920 return true;
5921}
5922
5923template <typename T> Option *Option::ignore_case(bool value) {
5924 if(!ignore_case_ && value) {
5925 ignore_case_ = value;
5926 auto *parent = static_cast<T *>(parent_);
5927 for(const Option_p &opt : parent->options_) {
5928 if(opt.get() == this) {
5929 continue;
5930 }
5931 const auto &omatch = opt->matching_name(*this);
5932 if(!omatch.empty()) {
5933 ignore_case_ = false;
5934 THROW OptionAlreadyAdded("adding ignore case caused a name conflict with " + omatch);
5935 }
5936 }
5937 } else {
5938 ignore_case_ = value;
5939 }
5940 return this;
5941}
5942
5943template <typename T> Option *Option::ignore_underscore(bool value) {
5944
5945 if(!ignore_underscore_ && value) {
5946 ignore_underscore_ = value;
5947 auto *parent = static_cast<T *>(parent_);
5948 for(const Option_p &opt : parent->options_) {
5949 if(opt.get() == this) {
5950 continue;
5951 }
5952 const auto &omatch = opt->matching_name(*this);
5953 if(!omatch.empty()) {
5954 ignore_underscore_ = false;
5955 THROW OptionAlreadyAdded("adding ignore underscore caused a name conflict with " + omatch);
5956 }
5957 }
5958 } else {
5959 ignore_underscore_ = value;
5960 }
5961 return this;
5962}
5963
5964CLI11_INLINE Option *Option::multi_option_policy(MultiOptionPolicy value) {
5965 if(value != multi_option_policy_) {
5966 if(multi_option_policy_ == MultiOptionPolicy::Throw && expected_max_ == detail::expected_max_vector_size &&
5967 expected_min_ > 1) { // this bizarre condition is to maintain backwards compatibility
5968 // with the previous behavior of expected_ with vectors
5969 expected_max_ = expected_min_;
5970 }
5971 multi_option_policy_ = value;
5972 current_option_state_ = option_state::parsing;
5973 }
5974 return this;
5975}
5976
5977CLI11_NODISCARD CLI11_INLINE std::string Option::get_name(bool positional, bool all_options) const {
5978 if(get_group().empty())
5979 return {}; // Hidden
5980
5981 if(all_options) {
5982
5983 std::vector<std::string> name_list;
5984
5986 if((positional && (!pname_.empty())) || (snames_.empty() && lnames_.empty())) {
5987 name_list.push_back(pname_);
5988 }
5989 if((get_items_expected() == 0) && (!fnames_.empty())) {
5990 for(const std::string &sname : snames_) {
5991 name_list.push_back("-" + sname);
5992 if(check_fname(sname)) {
5993 name_list.back() += "{" + get_flag_value(sname, "") + "}";
5994 }
5995 }
5996
5997 for(const std::string &lname : lnames_) {
5998 name_list.push_back("--" + lname);
5999 if(check_fname(lname)) {
6000 name_list.back() += "{" + get_flag_value(lname, "") + "}";
6001 }
6002 }
6003 } else {
6004 for(const std::string &sname : snames_)
6005 name_list.push_back("-" + sname);
6006
6007 for(const std::string &lname : lnames_)
6008 name_list.push_back("--" + lname);
6009 }
6010
6011 return detail::join(name_list);
6012 }
6013
6014 // This returns the positional name no matter what
6015 if(positional)
6016 return pname_;
6017
6018 // Prefer long name
6019 if(!lnames_.empty())
6020 return std::string(2, '-') + lnames_[0];
6021
6022 // Or short name if no long name
6023 if(!snames_.empty())
6024 return std::string(1, '-') + snames_[0];
6025
6026 // If positional is the only name, it's okay to use that
6027 return pname_;
6028}
6029
6030CLI11_INLINE void Option::run_callback() {
6031 if(force_callback_ && results_.empty()) {
6032 add_result(default_str_);
6033 }
6034 if(current_option_state_ == option_state::parsing) {
6035 _validate_results(results_);
6036 current_option_state_ = option_state::validated;
6037 }
6038
6039 if(current_option_state_ < option_state::reduced) {
6040 _reduce_results(proc_results_, results_);
6041 current_option_state_ = option_state::reduced;
6042 }
6043 if(current_option_state_ >= option_state::reduced) {
6044 current_option_state_ = option_state::callback_run;
6045 if(!(callback_)) {
6046 return;
6047 }
6048 const results_t &send_results = proc_results_.empty() ? results_ : proc_results_;
6049 bool local_result = callback_(send_results);
6050
6051 if(!local_result)
6052 THROW ConversionError(get_name(), results_);
6053 }
6054}
6055
6056CLI11_NODISCARD CLI11_INLINE const std::string &Option::matching_name(const Option &other) const {
6057 static const std::string estring;
6058 for(const std::string &sname : snames_) {
6059 if(other.check_sname(sname))
6060 return sname;
6061 if(other.check_lname(sname))
6062 return sname;
6063 }
6064 for(const std::string &lname : lnames_) {
6065 if(other.check_lname(lname))
6066 return lname;
6067 if(lname.size() == 1) {
6068 if(other.check_sname(lname)) {
6069 return lname;
6070 }
6071 }
6072 }
6073 if(snames_.empty() && lnames_.empty() && !pname_.empty()) {
6074 if(other.check_sname(pname_) || other.check_lname(pname_) || pname_ == other.pname_)
6075 return pname_;
6076 }
6077 if(other.snames_.empty() && other.fnames_.empty() && !other.pname_.empty()) {
6078 if(check_sname(other.pname_) || check_lname(other.pname_) || (pname_ == other.pname_))
6079 return other.pname_;
6080 }
6081 if(ignore_case_ ||
6082 ignore_underscore_) { // We need to do the inverse, in case we are ignore_case or ignore underscore
6083 for(const std::string &sname : other.snames_)
6084 if(check_sname(sname))
6085 return sname;
6086 for(const std::string &lname : other.lnames_)
6087 if(check_lname(lname))
6088 return lname;
6089 }
6090 return estring;
6091}
6092
6093CLI11_NODISCARD CLI11_INLINE bool Option::check_name(const std::string &name) const {
6094
6095 if(name.length() > 2 && name[0] == '-' && name[1] == '-')
6096 return check_lname(name.substr(2));
6097 if(name.length() > 1 && name.front() == '-')
6098 return check_sname(name.substr(1));
6099 if(!pname_.empty()) {
6100 std::string local_pname = pname_;
6101 std::string local_name = name;
6102 if(ignore_underscore_) {
6103 local_pname = detail::remove_underscore(local_pname);
6104 local_name = detail::remove_underscore(local_name);
6105 }
6106 if(ignore_case_) {
6107 local_pname = detail::to_lower(local_pname);
6108 local_name = detail::to_lower(local_name);
6109 }
6110 if(local_name == local_pname) {
6111 return true;
6112 }
6113 }
6114
6115 if(!envname_.empty()) {
6116 // this needs to be the original since envname_ shouldn't match on case insensitivity
6117 return (name == envname_);
6118 }
6119 return false;
6120}
6121
6122CLI11_NODISCARD CLI11_INLINE std::string Option::get_flag_value(const std::string &name,
6123 std::string input_value) const {
6124 static const std::string trueString{"true"};
6125 static const std::string falseString{"false"};
6126 static const std::string emptyString{"{}"};
6127 // check for disable flag override_
6128 if(disable_flag_override_) {
6129 if(!((input_value.empty()) || (input_value == emptyString))) {
6130 auto default_ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
6131 if(default_ind >= 0) {
6132 // We can static cast this to std::size_t because it is more than 0 in this block
6133 if(default_flag_values_[static_cast<std::size_t>(default_ind)].second != input_value) {
6134 if(input_value == default_str_ && force_callback_) {
6135 return input_value;
6136 }
6137 THROW(ArgumentMismatch::FlagOverride(name));
6138 }
6139 } else {
6140 if(input_value != trueString) {
6141 THROW(ArgumentMismatch::FlagOverride(name));
6142 }
6143 }
6144 }
6145 }
6146 auto ind = detail::find_member(name, fnames_, ignore_case_, ignore_underscore_);
6147 if((input_value.empty()) || (input_value == emptyString)) {
6148 if(flag_like_) {
6149 return (ind < 0) ? trueString : default_flag_values_[static_cast<std::size_t>(ind)].second;
6150 }
6151 return (ind < 0) ? default_str_ : default_flag_values_[static_cast<std::size_t>(ind)].second;
6152 }
6153 if(ind < 0) {
6154 return input_value;
6155 }
6156 if(default_flag_values_[static_cast<std::size_t>(ind)].second == falseString) {
6157 errno = 0;
6158 auto val = detail::to_flag_value(input_value);
6159 if(errno != 0) {
6160 errno = 0;
6161 return input_value;
6162 }
6163 return (val == 1) ? falseString : (val == (-1) ? trueString : std::to_string(-val));
6164 }
6165 return input_value;
6166}
6167
6168CLI11_INLINE Option *Option::add_result(std::string s) {
6169 _add_result(std::move(s), results_);
6170 current_option_state_ = option_state::parsing;
6171 return this;
6172}
6173
6174CLI11_INLINE Option *Option::add_result(std::string s, int &results_added) {
6175 results_added = _add_result(std::move(s), results_);
6176 current_option_state_ = option_state::parsing;
6177 return this;
6178}
6179
6180CLI11_INLINE Option *Option::add_result(std::vector<std::string> s) {
6181 current_option_state_ = option_state::parsing;
6182 for(auto &str : s) {
6183 _add_result(std::move(str), results_);
6184 }
6185 return this;
6186}
6187
6188CLI11_NODISCARD CLI11_INLINE results_t Option::reduced_results() const {
6189 results_t res = proc_results_.empty() ? results_ : proc_results_;
6190 if(current_option_state_ < option_state::reduced) {
6191 if(current_option_state_ == option_state::parsing) {
6192 res = results_;
6193 _validate_results(res);
6194 }
6195 if(!res.empty()) {
6196 results_t extra;
6197 _reduce_results(extra, res);
6198 if(!extra.empty()) {
6199 res = std::move(extra);
6200 }
6201 }
6202 }
6203 return res;
6204}
6205
6206CLI11_INLINE Option *Option::type_size(int option_type_size) {
6207 if(option_type_size < 0) {
6208 // this section is included for backwards compatibility
6209 type_size_max_ = -option_type_size;
6210 type_size_min_ = -option_type_size;
6211 expected_max_ = detail::expected_max_vector_size;
6212 } else {
6213 type_size_max_ = option_type_size;
6214 if(type_size_max_ < detail::expected_max_vector_size) {
6215 type_size_min_ = option_type_size;
6216 } else {
6217 inject_separator_ = true;
6218 }
6219 if(type_size_max_ == 0)
6220 required_ = false;
6221 }
6222 return this;
6223}
6224
6225CLI11_INLINE Option *Option::type_size(int option_type_size_min, int option_type_size_max) {
6226 if(option_type_size_min < 0 || option_type_size_max < 0) {
6227 // this section is included for backwards compatibility
6228 expected_max_ = detail::expected_max_vector_size;
6229 option_type_size_min = (std::abs)(option_type_size_min);
6230 option_type_size_max = (std::abs)(option_type_size_max);
6231 }
6232
6233 if(option_type_size_min > option_type_size_max) {
6234 type_size_max_ = option_type_size_min;
6235 type_size_min_ = option_type_size_max;
6236 } else {
6237 type_size_min_ = option_type_size_min;
6238 type_size_max_ = option_type_size_max;
6239 }
6240 if(type_size_max_ == 0) {
6241 required_ = false;
6242 }
6243 if(type_size_max_ >= detail::expected_max_vector_size) {
6244 inject_separator_ = true;
6245 }
6246 return this;
6247}
6248
6249CLI11_NODISCARD CLI11_INLINE std::string Option::get_type_name() const {
6250 std::string full_type_name = type_name_();
6251 if(!validators_.empty()) {
6252 for(const auto &Validator : validators_) {
6253 std::string vtype = Validator.get_description();
6254 if(!vtype.empty()) {
6255 full_type_name += ":" + vtype;
6256 }
6257 }
6258 }
6259 return full_type_name;
6260}
6261
6262CLI11_INLINE void Option::_validate_results(results_t &res) const {
6263 // Run the Validators (can change the string)
6264 if(!validators_.empty()) {
6265 if(type_size_max_ > 1) { // in this context index refers to the index in the type
6266 int index = 0;
6267 if(get_items_expected_max() < static_cast<int>(res.size()) &&
6268 (multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast ||
6269 multi_option_policy_ == CLI::MultiOptionPolicy::Reverse)) {
6270 // create a negative index for the earliest ones
6271 index = get_items_expected_max() - static_cast<int>(res.size());
6272 }
6273
6274 for(std::string &result : res) {
6275 if(detail::is_separator(result) && type_size_max_ != type_size_min_ && index >= 0) {
6276 index = 0; // reset index for variable size chunks
6277 continue;
6278 }
6279 auto err_msg = _validate(result, (index >= 0) ? (index % type_size_max_) : index);
6280 if(!err_msg.empty())
6281 THROW ValidationError(get_name(), err_msg);
6282 ++index;
6283 }
6284 } else {
6285 int index = 0;
6286 if(expected_max_ < static_cast<int>(res.size()) &&
6287 (multi_option_policy_ == CLI::MultiOptionPolicy::TakeLast ||
6288 multi_option_policy_ == CLI::MultiOptionPolicy::Reverse)) {
6289 // create a negative index for the earliest ones
6290 index = expected_max_ - static_cast<int>(res.size());
6291 }
6292 for(std::string &result : res) {
6293 auto err_msg = _validate(result, index);
6294 ++index;
6295 if(!err_msg.empty())
6296 THROW ValidationError(get_name(), err_msg);
6297 }
6298 }
6299 }
6300}
6301
6302CLI11_INLINE void Option::_reduce_results(results_t &out, const results_t &original) const {
6303
6304 // max num items expected or length of vector, always at least 1
6305 // Only valid for a trimming policy
6306
6307 out.clear();
6308 // Operation depends on the policy setting
6309 switch(multi_option_policy_) {
6310 case MultiOptionPolicy::TakeAll:
6311 break;
6312 case MultiOptionPolicy::TakeLast: {
6313 // Allow multi-option sizes (including 0)
6315 static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
6316 if(original.size() != trim_size) {
6317 out.assign(original.end() - static_cast<results_t::difference_type>(trim_size), original.end());
6318 }
6319 } break;
6320 case MultiOptionPolicy::Reverse: {
6321 // Allow multi-option sizes (including 0)
6323 static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
6324 if(original.size() != trim_size || trim_size > 1) {
6325 out.assign(original.end() - static_cast<results_t::difference_type>(trim_size), original.end());
6326 }
6327 std::reverse(out.begin(), out.end());
6328 } break;
6329 case MultiOptionPolicy::TakeFirst: {
6331 static_cast<std::size_t>(std::max<int>(get_items_expected_max(), 1)), original.size());
6332 if(original.size() != trim_size) {
6333 out.assign(original.begin(), original.begin() + static_cast<results_t::difference_type>(trim_size));
6334 }
6335 } break;
6336 case MultiOptionPolicy::Join:
6337 if(results_.size() > 1) {
6338 out.push_back(detail::join(original, std::string(1, (delimiter_ == '\0') ? '\n' : delimiter_)));
6339 }
6340 break;
6341 case MultiOptionPolicy::Sum:
6342 out.push_back(detail::sum_string_vector(original));
6343 break;
6344 case MultiOptionPolicy::Throw:
6345 default: {
6346 auto num_min = static_cast<std::size_t>(get_items_expected_min());
6347 auto num_max = static_cast<std::size_t>(get_items_expected_max());
6348 if(num_min == 0) {
6349 num_min = 1;
6350 }
6351 if(num_max == 0) {
6352 num_max = 1;
6353 }
6354 if(original.size() < num_min) {
6355 THROW ArgumentMismatch::AtLeast(get_name(), static_cast<int>(num_min), original.size());
6356 }
6357 if(original.size() > num_max) {
6358 if(original.size() == 2 && num_max == 1 && original[1] == "%%" && original[0] == "{}") {
6359 // this condition is a trap for the following empty indicator check on config files
6360 out = original;
6361 } else {
6362 THROW ArgumentMismatch::AtMost(get_name(), static_cast<int>(num_max), original.size());
6363 }
6364 }
6365 break;
6366 }
6367 }
6368 // this check is to allow an empty vector in certain circumstances but not if expected is not zero.
6369 // {} is the indicator for an empty container
6370 if(out.empty()) {
6371 if(original.size() == 1 && original[0] == "{}" && get_items_expected_min() > 0) {
6372 out.emplace_back("{}");
6373 out.emplace_back("%%");
6374 }
6375 } else if(out.size() == 1 && out[0] == "{}" && get_items_expected_min() > 0) {
6376 out.emplace_back("%%");
6377 }
6378}
6379
6380CLI11_INLINE std::string Option::_validate(std::string &result, int index) const {
6381 std::string err_msg;
6382 if(result.empty() && expected_min_ == 0) {
6383 // an empty with nothing expected is allowed
6384 return err_msg;
6385 }
6386 for(const auto &vali : validators_) {
6387 auto v = vali.get_application_index();
6388 if(v == -1 || v == index) {
6389 try {
6390 err_msg = vali(result);
6391 } catch(const ValidationError &err) {
6392#ifndef BB_NO_EXCEPTIONS
6393 err_msg = err.what();
6394#endif
6395 }
6396 if(!err_msg.empty())
6397 break;
6398 }
6399 }
6400
6401 return err_msg;
6402}
6403
6404CLI11_INLINE int Option::_add_result(std::string &&result, std::vector<std::string> &res) const {
6405 int result_count = 0;
6406 if(allow_extra_args_ && !result.empty() && result.front() == '[' &&
6407 result.back() == ']') { // this is now a vector string likely from the default or user entry
6408 result.pop_back();
6409
6410 for(auto &var : CLI::detail::split(result.substr(1), ',')) {
6411 if(!var.empty()) {
6412 result_count += _add_result(std::move(var), res);
6413 }
6414 }
6415 return result_count;
6416 }
6417 if(delimiter_ == '\0') {
6418 res.push_back(std::move(result));
6419 ++result_count;
6420 } else {
6421 if((result.find_first_of(delimiter_) != std::string::npos)) {
6422 for(const auto &var : CLI::detail::split(result, delimiter_)) {
6423 if(!var.empty()) {
6424 res.push_back(var);
6425 ++result_count;
6426 }
6427 }
6428 } else {
6429 res.push_back(std::move(result));
6430 ++result_count;
6431 }
6432 }
6433 return result_count;
6434}
6435
6436
6437
6438#ifndef CLI11_PARSE
6439#ifdef BB_NO_EXCEPTIONS
6440#define CLI11_PARSE(app, ...) (app).parse(__VA_ARGS__)
6441#else
6442#define CLI11_PARSE(app, ...) \
6443 try { \
6444 (app).parse(__VA_ARGS__); \
6445 } catch(const CLI::ParseError &e) { \
6446 return (app).exit(e); \
6447 }
6448#endif
6449#endif
6450
6451namespace detail {
6452enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS_STYLE, SUBCOMMAND, SUBCOMMAND_TERMINATOR };
6453struct AppFriend;
6454} // namespace detail
6455
6456namespace FailureMessage {
6458CLI11_INLINE std::string simple(const App *app, const Error &e);
6459
6461CLI11_INLINE std::string help(const App *app, const Error &e);
6462} // namespace FailureMessage
6463
6465
6466enum class config_extras_mode : char { error = 0, ignore, ignore_all, capture };
6467
6468class App;
6469
6470using App_p = std::shared_ptr<App>;
6471
6472namespace detail {
6474
6475template <typename T, enable_if_t<!std::is_integral<T>::value || (sizeof(T) <= 1U), detail::enabler> = detail::dummy>
6476Option *default_flag_modifiers(Option *opt) {
6477 return opt->always_capture_default();
6478}
6479
6481template <typename T, enable_if_t<std::is_integral<T>::value && (sizeof(T) > 1U), detail::enabler> = detail::dummy>
6482Option *default_flag_modifiers(Option *opt) {
6483 return opt->multi_option_policy(MultiOptionPolicy::Sum)->default_str("0")->force_callback();
6484}
6485
6486} // namespace detail
6487
6488class Option_group;
6490
6493class App {
6494 friend Option;
6495 friend detail::AppFriend;
6496
6497 protected:
6498 // This library follows the Google style guide for member names ending in underscores
6499
6502
6504 std::string name_{};
6505
6507 std::string description_{};
6508
6510 bool allow_extras_{false};
6511
6514 config_extras_mode allow_config_extras_{config_extras_mode::ignore};
6515
6517 bool prefix_command_{false};
6518
6520 bool has_automatic_name_{false};
6521
6523 bool required_{false};
6524
6526 bool disabled_{false};
6527
6529 bool pre_parse_called_{false};
6530
6533 bool immediate_callback_{false};
6534
6536 std::function<void(std::size_t)> pre_parse_callback_{};
6537
6539 std::function<void()> parse_complete_callback_{};
6540
6542 std::function<void()> final_callback_{};
6543
6547
6549 OptionDefaults option_defaults_{};
6550
6552 std::vector<Option_p> options_{};
6553
6557
6559 std::string usage_{};
6560
6562 std::function<std::string()> usage_callback_{};
6563
6565 std::string footer_{};
6566
6568 std::function<std::string()> footer_callback_{};
6569
6571 Option *help_ptr_{nullptr};
6572
6574 Option *help_all_ptr_{nullptr};
6575
6577 Option *version_ptr_{nullptr};
6578
6580 std::shared_ptr<FormatterBase> formatter_{new Formatter()};
6581
6583 std::function<std::string(const App *, const Error &e)> failure_message_{FailureMessage::simple};
6584
6588
6590
6594 missing_t missing_{};
6595
6597 std::vector<Option *> parse_order_{};
6598
6600 std::vector<App *> parsed_subcommands_{};
6601
6603 std::set<App *> exclude_subcommands_{};
6604
6607 std::set<Option *> exclude_options_{};
6608
6611 std::set<App *> need_subcommands_{};
6612
6615 std::set<Option *> need_options_{};
6616
6620
6622 std::vector<App_p> subcommands_{};
6623
6625 bool ignore_case_{false};
6626
6628 bool ignore_underscore_{false};
6629
6631 bool fallthrough_{false};
6632
6634 bool allow_windows_style_options_{
6635#ifdef _WIN32
6636 true
6637#else
6638 false
6639#endif
6640 };
6642 bool positionals_at_end_{false};
6643
6644 enum class startup_mode : char { stable, enabled, disabled };
6647 startup_mode default_startup{startup_mode::stable};
6648
6650 bool configurable_{false};
6651
6653 bool validate_positionals_{false};
6654
6656 bool validate_optional_arguments_{false};
6657
6660 bool silent_{false};
6661
6663 std::uint32_t parsed_{0U};
6664
6666 std::size_t require_subcommand_min_{0};
6667
6669 std::size_t require_subcommand_max_{0};
6670
6672 std::size_t require_option_min_{0};
6673
6675 std::size_t require_option_max_{0};
6676
6678 App *parent_{nullptr};
6679
6681 std::string group_{"Subcommands"};
6682
6684 std::vector<std::string> aliases_{};
6685
6689
6691 Option *config_ptr_{nullptr};
6692
6694 std::shared_ptr<Config> config_formatter_{new ConfigTOML()};
6695
6697
6698#ifdef _WIN32
6700 std::vector<std::string> normalized_argv_{};
6701
6703 std::vector<char *> normalized_argv_view_{};
6704#endif
6705
6707 App(std::string app_description, std::string app_name, App *parent);
6708
6709 public:
6712
6714 explicit App(std::string app_description = "", std::string app_name = "")
6715 : App(app_description, app_name, nullptr) {
6716 set_help_flag("-h,--help", "Print this help message and exit");
6717 }
6718
6719 App(const App &) = delete;
6720 App &operator=(const App &) = delete;
6721
6723 virtual ~App() = default;
6724
6726 CLI11_NODISCARD char **ensure_utf8(char **argv);
6727
6734 App *callback(std::function<void()> app_callback) {
6735 if(immediate_callback_) {
6736 parse_complete_callback_ = std::move(app_callback);
6737 } else {
6738 final_callback_ = std::move(app_callback);
6739 }
6740 return this;
6741 }
6742
6745 App *final_callback(std::function<void()> app_callback) {
6746 final_callback_ = std::move(app_callback);
6747 return this;
6748 }
6749
6752 App *parse_complete_callback(std::function<void()> pc_callback) {
6753 parse_complete_callback_ = std::move(pc_callback);
6754 return this;
6755 }
6756
6759 App *preparse_callback(std::function<void(std::size_t)> pp_callback) {
6760 pre_parse_callback_ = std::move(pp_callback);
6761 return this;
6762 }
6763
6765 App *name(std::string app_name = "");
6766
6768 App *alias(std::string app_name);
6769
6771 App *allow_extras(bool allow = true) {
6772 allow_extras_ = allow;
6773 return this;
6774 }
6775
6777 App *required(bool require = true) {
6778 required_ = require;
6779 return this;
6780 }
6781
6783 App *disabled(bool disable = true) {
6784 disabled_ = disable;
6785 return this;
6786 }
6787
6789 App *silent(bool silence = true) {
6790 silent_ = silence;
6791 return this;
6792 }
6793
6795 App *disabled_by_default(bool disable = true) {
6796 if(disable) {
6797 default_startup = startup_mode::disabled;
6798 } else {
6799 default_startup = (default_startup == startup_mode::enabled) ? startup_mode::enabled : startup_mode::stable;
6800 }
6801 return this;
6802 }
6803
6806 App *enabled_by_default(bool enable = true) {
6807 if(enable) {
6808 default_startup = startup_mode::enabled;
6809 } else {
6810 default_startup =
6811 (default_startup == startup_mode::disabled) ? startup_mode::disabled : startup_mode::stable;
6812 }
6813 return this;
6814 }
6815
6817 App *immediate_callback(bool immediate = true);
6818
6820 App *validate_positionals(bool validate = true) {
6821 validate_positionals_ = validate;
6822 return this;
6823 }
6824
6826 App *validate_optional_arguments(bool validate = true) {
6827 validate_optional_arguments_ = validate;
6828 return this;
6829 }
6830
6832 App *allow_config_extras(bool allow = true) {
6833 if(allow) {
6834 allow_config_extras_ = config_extras_mode::capture;
6835 allow_extras_ = true;
6836 } else {
6837 allow_config_extras_ = config_extras_mode::error;
6838 }
6839 return this;
6840 }
6841
6843 App *allow_config_extras(config_extras_mode mode) {
6844 allow_config_extras_ = mode;
6845 return this;
6846 }
6847
6849 App *prefix_command(bool allow = true) {
6850 prefix_command_ = allow;
6851 return this;
6852 }
6853
6855 App *ignore_case(bool value = true);
6856
6859 App *allow_windows_style_options(bool value = true) {
6860 allow_windows_style_options_ = value;
6861 return this;
6862 }
6863
6865 App *positionals_at_end(bool value = true) {
6866 positionals_at_end_ = value;
6867 return this;
6868 }
6869
6871 App *configurable(bool value = true) {
6872 configurable_ = value;
6873 return this;
6874 }
6875
6877 App *ignore_underscore(bool value = true);
6878
6880 App *formatter(std::shared_ptr<FormatterBase> fmt) {
6881 formatter_ = fmt;
6882 return this;
6883 }
6884
6886 App *formatter_fn(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) {
6887 formatter_ = std::make_shared<FormatterLambda>(fmt);
6888 return this;
6889 }
6890
6892 App *config_formatter(std::shared_ptr<Config> fmt) {
6893 config_formatter_ = fmt;
6894 return this;
6895 }
6896
6898 CLI11_NODISCARD bool parsed() const { return parsed_ > 0; }
6899
6901 OptionDefaults *option_defaults() { return &option_defaults_; }
6902
6906
6921 Option *add_option(std::string option_name,
6922 callback_t option_callback,
6923 std::string option_description = "",
6924 bool defaulted = false,
6925 std::function<std::string()> func = {});
6926
6928 template <typename AssignTo,
6929 typename ConvertTo = AssignTo,
6930 enable_if_t<!std::is_const<ConvertTo>::value, detail::enabler> = detail::dummy>
6931 Option *add_option(std::string option_name,
6932 AssignTo &variable,
6933 std::string option_description = "") {
6934
6935 auto fun = [&variable](const CLI::results_t &res) { // comment for spacing
6936 return detail::lexical_conversion<AssignTo, ConvertTo>(res, variable);
6937 };
6938
6939 Option *opt = add_option(option_name, fun, option_description, false, [&variable]() {
6940 return CLI::detail::checked_to_string<AssignTo, ConvertTo>(variable);
6941 });
6942 opt->type_name(detail::type_name<ConvertTo>());
6943 // these must be actual lvalues since (std::max) sometimes is defined in terms of references and references
6944 // to structs used in the evaluation can be temporary so that would cause issues.
6945 auto Tcount = detail::type_count<AssignTo>::value;
6946 auto XCcount = detail::type_count<ConvertTo>::value;
6947 opt->type_size(detail::type_count_min<ConvertTo>::value, (std::max)(Tcount, XCcount));
6948 opt->expected(detail::expected_count<ConvertTo>::value);
6949 opt->run_callback_for_default();
6950 return opt;
6951 }
6952
6954 template <typename AssignTo, enable_if_t<!std::is_const<AssignTo>::value, detail::enabler> = detail::dummy>
6955 Option *add_option_no_stream(std::string option_name,
6956 AssignTo &variable,
6957 std::string option_description = "") {
6958
6959 auto fun = [&variable](const CLI::results_t &res) { // comment for spacing
6960 return detail::lexical_conversion<AssignTo, AssignTo>(res, variable);
6961 };
6962
6963 Option *opt = add_option(option_name, fun, option_description, false, []() { return std::string{}; });
6964 opt->type_name(detail::type_name<AssignTo>());
6965 opt->type_size(detail::type_count_min<AssignTo>::value, detail::type_count<AssignTo>::value);
6966 opt->expected(detail::expected_count<AssignTo>::value);
6967 opt->run_callback_for_default();
6968 return opt;
6969 }
6970
6972 template <typename ArgType>
6973 Option *add_option_function(std::string option_name,
6974 const std::function<void(const ArgType &)> &func,
6975 std::string option_description = "") {
6976
6977 auto fun = [func](const CLI::results_t &res) {
6978 ArgType variable;
6979 bool result = detail::lexical_conversion<ArgType, ArgType>(res, variable);
6980 if(result) {
6981 func(variable);
6982 }
6983 return result;
6984 };
6985
6986 Option *opt = add_option(option_name, std::move(fun), option_description, false);
6987 opt->type_name(detail::type_name<ArgType>());
6988 opt->type_size(detail::type_count_min<ArgType>::value, detail::type_count<ArgType>::value);
6989 opt->expected(detail::expected_count<ArgType>::value);
6990 return opt;
6991 }
6992
6994 Option *add_option(std::string option_name) {
6995 return add_option(option_name, CLI::callback_t{}, std::string{}, false);
6996 }
6997
6999 template <typename T,
7000 enable_if_t<std::is_const<T>::value && std::is_constructible<std::string, T>::value, detail::enabler> =
7001 detail::dummy>
7002 Option *add_option(std::string option_name, T &option_description) {
7003 return add_option(option_name, CLI::callback_t(), option_description, false);
7004 }
7005
7007 Option *set_help_flag(std::string flag_name = "", const std::string &help_description = "");
7008
7010 Option *set_help_all_flag(std::string help_name = "", const std::string &help_description = "");
7011
7013 Option *set_version_flag(std::string flag_name = "",
7014 const std::string &versionString = "",
7015 const std::string &version_help = "Display program version information and exit");
7016
7018 Option *set_version_flag(std::string flag_name,
7019 std::function<std::string()> vfunc,
7020 const std::string &version_help = "Display program version information and exit");
7021
7022 private:
7024 Option *_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description);
7025
7026 public:
7028 Option *add_flag(std::string flag_name) { return _add_flag_internal(flag_name, CLI::callback_t(), std::string{}); }
7029
7033 template <typename T,
7034 enable_if_t<std::is_const<T>::value && std::is_constructible<std::string, T>::value, detail::enabler> =
7035 detail::dummy>
7036 Option *add_flag(std::string flag_name, T &flag_description) {
7037 return _add_flag_internal(flag_name, CLI::callback_t(), flag_description);
7038 }
7039
7042 template <typename T,
7043 enable_if_t<!detail::is_mutable_container<T>::value && !std::is_const<T>::value &&
7044 !std::is_constructible<std::function<void(int)>, T>::value,
7045 detail::enabler> = detail::dummy>
7046 Option *add_flag(std::string flag_name,
7047 T &flag_result,
7048 std::string flag_description = "") {
7049
7050 CLI::callback_t fun = [&flag_result](const CLI::results_t &res) {
7051 using CLI::detail::lexical_cast;
7052 return lexical_cast(res[0], flag_result);
7053 };
7054 auto *opt = _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
7055 return detail::default_flag_modifiers<T>(opt);
7056 }
7057
7059 template <typename T,
7060 enable_if_t<!std::is_assignable<std::function<void(std::int64_t)> &, T>::value, detail::enabler> =
7061 detail::dummy>
7062 Option *add_flag(std::string flag_name,
7063 std::vector<T> &flag_results,
7064 std::string flag_description = "") {
7065 CLI::callback_t fun = [&flag_results](const CLI::results_t &res) {
7066 bool retval = true;
7067 for(const auto &elem : res) {
7068 using CLI::detail::lexical_cast;
7069 flag_results.emplace_back();
7070 retval &= lexical_cast(elem, flag_results.back());
7071 }
7072 return retval;
7073 };
7074 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
7075 ->multi_option_policy(MultiOptionPolicy::TakeAll)
7076 ->run_callback_for_default();
7077 }
7078
7080 Option *add_flag_callback(std::string flag_name,
7081 std::function<void(void)> function,
7082 std::string flag_description = "");
7083
7085 Option *add_flag_function(std::string flag_name,
7086 std::function<void(std::int64_t)> function,
7087 std::string flag_description = "");
7088
7089#ifdef CLI11_CPP14
7091 Option *add_flag(std::string flag_name,
7092 std::function<void(std::int64_t)> function,
7093 std::string flag_description = "") {
7094 return add_flag_function(std::move(flag_name), std::move(function), std::move(flag_description));
7095 }
7096#endif
7097
7099 Option *set_config(std::string option_name = "",
7100 std::string default_filename = "",
7101 const std::string &help_message = "Read an ini file",
7102 bool config_required = false);
7103
7105 bool remove_option(Option *opt);
7106
7108 template <typename T = Option_group>
7109 T *add_option_group(std::string group_name, std::string group_description = "") {
7110 if(!detail::valid_alias_name_string(group_name)) {
7111 THROW IncorrectConstruction("option group names may not contain newlines or null characters");
7112 }
7113 auto option_group = std::make_shared<T>(std::move(group_description), group_name, this);
7114 auto *ptr = option_group.get();
7115 // move to App_p for overload resolution on older gcc versions
7116 App_p app_ptr = std::dynamic_pointer_cast<App>(option_group);
7117 add_subcommand(std::move(app_ptr));
7118 return ptr;
7119 }
7120
7124
7126 App *add_subcommand(std::string subcommand_name = "", std::string subcommand_description = "");
7127
7129 App *add_subcommand(CLI::App_p subcom);
7130
7132 bool remove_subcommand(App *subcom);
7133
7136 App *get_subcommand(const App *subcom) const;
7137
7139 CLI11_NODISCARD App *get_subcommand(std::string subcom) const;
7140
7143 CLI11_NODISCARD App *get_subcommand_no_THROW(std::string subcom) const noexcept;
7144
7146 CLI11_NODISCARD App *get_subcommand(int index = 0) const;
7147
7149 CLI::App_p get_subcommand_ptr(App *subcom) const;
7150
7152 CLI11_NODISCARD CLI::App_p get_subcommand_ptr(std::string subcom) const;
7153
7155 CLI11_NODISCARD CLI::App_p get_subcommand_ptr(int index = 0) const;
7156
7158 CLI11_NODISCARD App *get_option_group(std::string group_name) const;
7159
7163 CLI11_NODISCARD std::size_t count() const { return parsed_; }
7164
7167 CLI11_NODISCARD std::size_t count_all() const;
7168
7170 App *group(std::string group_name) {
7171 group_ = group_name;
7172 return this;
7173 }
7174
7176 App *require_subcommand() {
7177 require_subcommand_min_ = 1;
7178 require_subcommand_max_ = 0;
7179 return this;
7180 }
7181
7185 App *require_subcommand(int value) {
7186 if(value < 0) {
7187 require_subcommand_min_ = 0;
7188 require_subcommand_max_ = static_cast<std::size_t>(-value);
7189 } else {
7190 require_subcommand_min_ = static_cast<std::size_t>(value);
7191 require_subcommand_max_ = static_cast<std::size_t>(value);
7192 }
7193 return this;
7194 }
7195
7198 App *require_subcommand(std::size_t min, std::size_t max) {
7199 require_subcommand_min_ = min;
7200 require_subcommand_max_ = max;
7201 return this;
7202 }
7203
7205 App *require_option() {
7206 require_option_min_ = 1;
7207 require_option_max_ = 0;
7208 return this;
7209 }
7210
7214 App *require_option(int value) {
7215 if(value < 0) {
7216 require_option_min_ = 0;
7217 require_option_max_ = static_cast<std::size_t>(-value);
7218 } else {
7219 require_option_min_ = static_cast<std::size_t>(value);
7220 require_option_max_ = static_cast<std::size_t>(value);
7221 }
7222 return this;
7223 }
7224
7227 App *require_option(std::size_t min, std::size_t max) {
7228 require_option_min_ = min;
7229 require_option_max_ = max;
7230 return this;
7231 }
7232
7235 App *fallthrough(bool value = true) {
7236 fallthrough_ = value;
7237 return this;
7238 }
7239
7242 explicit operator bool() const { return parsed_ > 0; }
7243
7247
7251 virtual void pre_callback() {}
7252
7256 //
7258 void clear();
7259
7262 void parse(int argc, const char *const *argv);
7263 void parse(int argc, const wchar_t *const *argv);
7264
7265 private:
7266 template <class CharT> void parse_char_t(int argc, const CharT *const *argv);
7267
7268 public:
7273 void parse(std::string commandline, bool program_name_included = false);
7274 void parse(std::wstring commandline, bool program_name_included = false);
7275
7278 void parse(std::vector<std::string> &args);
7279
7281 void parse(std::vector<std::string> &&args);
7282
7283 void parse_from_stream(std::istream &input);
7284
7286 void failure_message(std::function<std::string(const App *, const Error &e)> function) {
7287 failure_message_ = function;
7288 }
7289
7291 int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const;
7292
7296
7298 CLI11_NODISCARD std::size_t count(std::string option_name) const { return get_option(option_name)->count(); }
7299
7302 CLI11_NODISCARD std::vector<App *> get_subcommands() const { return parsed_subcommands_; }
7303
7306 std::vector<const App *> get_subcommands(const std::function<bool(const App *)> &filter) const;
7307
7310 std::vector<App *> get_subcommands(const std::function<bool(App *)> &filter);
7311
7313 bool got_subcommand(const App *subcom) const {
7314 // get subcom needed to verify that this was a real subcommand
7315 return get_subcommand(subcom)->parsed_ > 0;
7316 }
7317
7319 CLI11_NODISCARD bool got_subcommand(std::string subcommand_name) const noexcept {
7320 App *sub = get_subcommand_no_THROW(subcommand_name);
7321 return (sub != nullptr) ? (sub->parsed_ > 0) : false;
7322 }
7323
7325 App *excludes(Option *opt) {
7326 if(opt == nullptr) {
7327 THROW OptionNotFound("nullptr passed");
7328 }
7329 exclude_options_.insert(opt);
7330 return this;
7331 }
7332
7334 App *excludes(App *app) {
7335 if(app == nullptr) {
7336 THROW OptionNotFound("nullptr passed");
7337 }
7338 if(app == this) {
7339 THROW OptionNotFound("cannot self reference in needs");
7340 }
7341 auto res = exclude_subcommands_.insert(app);
7342 // subcommand exclusion should be symmetric
7343 if(res.second) {
7344 app->exclude_subcommands_.insert(this);
7345 }
7346 return this;
7347 }
7348
7349 App *needs(Option *opt) {
7350 if(opt == nullptr) {
7351 THROW OptionNotFound("nullptr passed");
7352 }
7353 need_options_.insert(opt);
7354 return this;
7355 }
7356
7357 App *needs(App *app) {
7358 if(app == nullptr) {
7359 THROW OptionNotFound("nullptr passed");
7360 }
7361 if(app == this) {
7362 THROW OptionNotFound("cannot self reference in needs");
7363 }
7364 need_subcommands_.insert(app);
7365 return this;
7366 }
7367
7369 bool remove_excludes(Option *opt);
7370
7372 bool remove_excludes(App *app);
7373
7375 bool remove_needs(Option *opt);
7376
7378 bool remove_needs(App *app);
7382
7384 App *usage(std::string usage_string) {
7385 usage_ = std::move(usage_string);
7386 return this;
7387 }
7389 App *usage(std::function<std::string()> usage_function) {
7390 usage_callback_ = std::move(usage_function);
7391 return this;
7392 }
7394 App *footer(std::string footer_string) {
7395 footer_ = std::move(footer_string);
7396 return this;
7397 }
7399 App *footer(std::function<std::string()> footer_function) {
7400 footer_callback_ = std::move(footer_function);
7401 return this;
7402 }
7405 CLI11_NODISCARD std::string config_to_str(bool default_also = false, bool write_description = false) const {
7406 return config_formatter_->to_config(this, default_also, write_description, "");
7407 }
7408
7411 CLI11_NODISCARD std::string help(std::string prev = "", AppFormatMode mode = AppFormatMode::Normal) const;
7412
7414 CLI11_NODISCARD std::string version() const;
7418
7420 CLI11_NODISCARD std::shared_ptr<FormatterBase> get_formatter() const { return formatter_; }
7421
7423 CLI11_NODISCARD std::shared_ptr<Config> get_config_formatter() const { return config_formatter_; }
7424
7426 CLI11_NODISCARD std::shared_ptr<ConfigBase> get_config_formatter_base() const {
7427 // This is safer as a dynamic_cast if we have RTTI, as Config -> ConfigBase
7428#if CLI11_USE_STATIC_RTTI == 0
7429 return std::dynamic_pointer_cast<ConfigBase>(config_formatter_);
7430#else
7431 return std::static_pointer_cast<ConfigBase>(config_formatter_);
7432#endif
7433 }
7434
7436 CLI11_NODISCARD std::string get_description() const { return description_; }
7437
7439 App *description(std::string app_description) {
7440 description_ = std::move(app_description);
7441 return this;
7442 }
7443
7445 std::vector<const Option *> get_options(const std::function<bool(const Option *)> filter = {}) const;
7446
7448 std::vector<Option *> get_options(const std::function<bool(Option *)> filter = {});
7449
7451 CLI11_NODISCARD Option *get_option_no_THROW(std::string option_name) noexcept;
7452
7454 CLI11_NODISCARD const Option *get_option_no_THROW(std::string option_name) const noexcept;
7455
7457 CLI11_NODISCARD const Option *get_option(std::string option_name) const {
7458 const auto *opt = get_option_no_THROW(option_name);
7459 if(opt == nullptr) {
7460 THROW OptionNotFound(option_name);
7461 }
7462 return opt;
7463 }
7464
7466 Option *get_option(std::string option_name) {
7467 auto *opt = get_option_no_THROW(option_name);
7468 if(opt == nullptr) {
7469 THROW OptionNotFound(option_name);
7470 }
7471 return opt;
7472 }
7473
7475 const Option *operator[](const std::string &option_name) const { return get_option(option_name); }
7476
7478 const Option *operator[](const char *option_name) const { return get_option(option_name); }
7479
7481 CLI11_NODISCARD bool get_ignore_case() const { return ignore_case_; }
7482
7484 CLI11_NODISCARD bool get_ignore_underscore() const { return ignore_underscore_; }
7485
7487 CLI11_NODISCARD bool get_fallthrough() const { return fallthrough_; }
7488
7490 CLI11_NODISCARD bool get_allow_windows_style_options() const { return allow_windows_style_options_; }
7491
7493 CLI11_NODISCARD bool get_positionals_at_end() const { return positionals_at_end_; }
7494
7496 CLI11_NODISCARD bool get_configurable() const { return configurable_; }
7497
7499 CLI11_NODISCARD const std::string &get_group() const { return group_; }
7500
7502 CLI11_NODISCARD std::string get_usage() const {
7503 return (usage_callback_) ? usage_callback_() + '\n' + usage_ : usage_;
7504 }
7505
7507 CLI11_NODISCARD std::string get_footer() const {
7508 return (footer_callback_) ? footer_callback_() + '\n' + footer_ : footer_;
7509 }
7510
7512 CLI11_NODISCARD std::size_t get_require_subcommand_min() const { return require_subcommand_min_; }
7513
7515 CLI11_NODISCARD std::size_t get_require_subcommand_max() const { return require_subcommand_max_; }
7516
7518 CLI11_NODISCARD std::size_t get_require_option_min() const { return require_option_min_; }
7519
7521 CLI11_NODISCARD std::size_t get_require_option_max() const { return require_option_max_; }
7522
7524 CLI11_NODISCARD bool get_prefix_command() const { return prefix_command_; }
7525
7527 CLI11_NODISCARD bool get_allow_extras() const { return allow_extras_; }
7528
7530 CLI11_NODISCARD bool get_required() const { return required_; }
7531
7533 CLI11_NODISCARD bool get_disabled() const { return disabled_; }
7534
7536 CLI11_NODISCARD bool get_silent() const { return silent_; }
7537
7539 CLI11_NODISCARD bool get_immediate_callback() const { return immediate_callback_; }
7540
7542 CLI11_NODISCARD bool get_disabled_by_default() const { return (default_startup == startup_mode::disabled); }
7543
7545 CLI11_NODISCARD bool get_enabled_by_default() const { return (default_startup == startup_mode::enabled); }
7547 CLI11_NODISCARD bool get_validate_positionals() const { return validate_positionals_; }
7549 CLI11_NODISCARD bool get_validate_optional_arguments() const { return validate_optional_arguments_; }
7550
7552 CLI11_NODISCARD config_extras_mode get_allow_config_extras() const { return allow_config_extras_; }
7553
7555 Option *get_help_ptr() { return help_ptr_; }
7556
7558 CLI11_NODISCARD const Option *get_help_ptr() const { return help_ptr_; }
7559
7561 CLI11_NODISCARD const Option *get_help_all_ptr() const { return help_all_ptr_; }
7562
7564 Option *get_config_ptr() { return config_ptr_; }
7565
7567 CLI11_NODISCARD const Option *get_config_ptr() const { return config_ptr_; }
7568
7570 Option *get_version_ptr() { return version_ptr_; }
7571
7573 CLI11_NODISCARD const Option *get_version_ptr() const { return version_ptr_; }
7574
7576 App *get_parent() { return parent_; }
7577
7579 CLI11_NODISCARD const App *get_parent() const { return parent_; }
7580
7582 CLI11_NODISCARD const std::string &get_name() const { return name_; }
7583
7585 CLI11_NODISCARD const std::vector<std::string> &get_aliases() const { return aliases_; }
7586
7588 App *clear_aliases() {
7589 aliases_.clear();
7590 return this;
7591 }
7592
7594 CLI11_NODISCARD std::string get_display_name(bool with_aliases = false) const;
7595
7597 CLI11_NODISCARD bool check_name(std::string name_to_check) const;
7598
7600 CLI11_NODISCARD std::vector<std::string> get_groups() const;
7601
7603 CLI11_NODISCARD const std::vector<Option *> &parse_order() const { return parse_order_; }
7604
7606 CLI11_NODISCARD std::vector<std::string> remaining(bool recurse = false) const;
7607
7609 CLI11_NODISCARD std::vector<std::string> remaining_for_passthrough(bool recurse = false) const;
7610
7612 CLI11_NODISCARD std::size_t remaining_size(bool recurse = false) const;
7613
7615
7616 protected:
7621 void _validate() const;
7622
7626 void _configure();
7627
7629 void run_callback(bool final_mode = false, bool suppress_final_callback = false);
7630
7632 CLI11_NODISCARD bool _valid_subcommand(const std::string &current, bool ignore_used = true) const;
7633
7635 CLI11_NODISCARD detail::Classifier _recognize(const std::string &current,
7636 bool ignore_used_subcommands = true) const;
7637
7638 // The parse function is now broken into several parts, and part of process
7639
7641 void _process_config_file();
7642
7644 bool _process_config_file(const std::string &config_file, bool throw_error);
7645
7647 void _process_env();
7648
7650 void _process_callbacks();
7651
7655 void _process_help_flags(bool trigger_help = false, bool trigger_all_help = false) const;
7656
7658 void _process_requirements();
7659
7661 void _process();
7662
7664 void _process_extras();
7665
7668 void _process_extras(std::vector<std::string> &args);
7669
7671 void increment_parsed();
7672
7674 void _parse(std::vector<std::string> &args);
7675
7677 void _parse(std::vector<std::string> &&args);
7678
7680 void _parse_stream(std::istream &input);
7681
7686 void _parse_config(const std::vector<ConfigItem> &args);
7687
7689 bool _parse_single_config(const ConfigItem &item, std::size_t level = 0);
7690
7693 bool _parse_single(std::vector<std::string> &args, bool &positional_only);
7694
7696 CLI11_NODISCARD std::size_t _count_remaining_positionals(bool required_only = false) const;
7697
7699 CLI11_NODISCARD bool _has_remaining_positionals() const;
7700
7704 bool _parse_positional(std::vector<std::string> &args, bool haltOnSubcommand);
7705
7708 CLI11_NODISCARD App *
7709 _find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept;
7710
7715 bool _parse_subcommand(std::vector<std::string> &args);
7716
7720 bool _parse_arg(std::vector<std::string> &args, detail::Classifier current_type, bool local_processing_only);
7721
7723 void _trigger_pre_parse(std::size_t remaining_args);
7724
7726 App *_get_fallthrough_parent();
7727
7729 CLI11_NODISCARD const std::string &_compare_subcommand_names(const App &subcom, const App &base) const;
7730
7732 void _move_to_missing(detail::Classifier val_type, const std::string &val);
7733
7734 public:
7736 void _move_option(Option *opt, App *app);
7737}; // namespace CLI
7738
7740class Option_group : public App {
7741 public:
7742 Option_group(std::string group_description, std::string group_name, App *parent)
7743 : App(std::move(group_description), "", parent) {
7744 group(group_name);
7745 // option groups should have automatic fallthrough
7746 }
7747 using App::add_option;
7749 Option *add_option(Option *opt) {
7750 if(get_parent() == nullptr) {
7751 THROW OptionNotFound("Unable to locate the specified option");
7752 }
7753 get_parent()->_move_option(opt, this);
7754 return opt;
7755 }
7757 void add_options(Option *opt) { add_option(opt); }
7759 template <typename... Args> void add_options(Option *opt, Args... args) {
7760 add_option(opt);
7761 add_options(args...);
7762 }
7763 using App::add_subcommand;
7765 App *add_subcommand(App *subcom) {
7766 App_p subc = subcom->get_parent()->get_subcommand_ptr(subcom);
7767 subc->get_parent()->remove_subcommand(subcom);
7768 add_subcommand(std::move(subc));
7769 return subcom;
7770 }
7771};
7772
7774CLI11_INLINE void TriggerOn(App *trigger_app, App *app_to_enable);
7775
7777CLI11_INLINE void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable);
7778
7780CLI11_INLINE void TriggerOff(App *trigger_app, App *app_to_enable);
7781
7783CLI11_INLINE void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable);
7784
7786CLI11_INLINE void deprecate_option(Option *opt, const std::string &replacement = "");
7787
7789inline void deprecate_option(App *app, const std::string &option_name, const std::string &replacement = "") {
7790 auto *opt = app->get_option(option_name);
7791 deprecate_option(opt, replacement);
7792}
7793
7795inline void deprecate_option(App &app, const std::string &option_name, const std::string &replacement = "") {
7796 auto *opt = app.get_option(option_name);
7797 deprecate_option(opt, replacement);
7798}
7799
7801CLI11_INLINE void retire_option(App *app, Option *opt);
7802
7804CLI11_INLINE void retire_option(App &app, Option *opt);
7805
7807CLI11_INLINE void retire_option(App *app, const std::string &option_name);
7808
7810CLI11_INLINE void retire_option(App &app, const std::string &option_name);
7811
7812namespace detail {
7814struct AppFriend {
7815#ifdef CLI11_CPP14
7816
7818 template <typename... Args> static decltype(auto) parse_arg(App *app, Args &&...args) {
7819 return app->_parse_arg(std::forward<Args>(args)...);
7820 }
7821
7823 template <typename... Args> static decltype(auto) parse_subcommand(App *app, Args &&...args) {
7824 return app->_parse_subcommand(std::forward<Args>(args)...);
7825 }
7826#else
7828 template <typename... Args>
7829 static auto parse_arg(App *app, Args &&...args) ->
7830 typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type {
7831 return app->_parse_arg(std::forward<Args>(args)...);
7832 }
7833
7835 template <typename... Args>
7836 static auto parse_subcommand(App *app, Args &&...args) ->
7837 typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type {
7838 return app->_parse_subcommand(std::forward<Args>(args)...);
7839 }
7840#endif
7842 static App *get_fallthrough_parent(App *app) { return app->_get_fallthrough_parent(); }
7843};
7844} // namespace detail
7845
7846
7847
7848
7849CLI11_INLINE App::App(std::string app_description, std::string app_name, App *parent)
7850 : name_(std::move(app_name)), description_(std::move(app_description)), parent_(parent) {
7851 // Inherit if not from a nullptr
7852 if(parent_ != nullptr) {
7853 if(parent_->help_ptr_ != nullptr)
7854 set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description());
7855 if(parent_->help_all_ptr_ != nullptr)
7856 set_help_all_flag(parent_->help_all_ptr_->get_name(false, true), parent_->help_all_ptr_->get_description());
7857
7859 option_defaults_ = parent_->option_defaults_;
7860
7861 // INHERITABLE
7862 failure_message_ = parent_->failure_message_;
7863 allow_extras_ = parent_->allow_extras_;
7864 allow_config_extras_ = parent_->allow_config_extras_;
7865 prefix_command_ = parent_->prefix_command_;
7866 immediate_callback_ = parent_->immediate_callback_;
7867 ignore_case_ = parent_->ignore_case_;
7868 ignore_underscore_ = parent_->ignore_underscore_;
7869 fallthrough_ = parent_->fallthrough_;
7870 validate_positionals_ = parent_->validate_positionals_;
7871 validate_optional_arguments_ = parent_->validate_optional_arguments_;
7872 configurable_ = parent_->configurable_;
7873 allow_windows_style_options_ = parent_->allow_windows_style_options_;
7874 group_ = parent_->group_;
7875 usage_ = parent_->usage_;
7876 footer_ = parent_->footer_;
7877 formatter_ = parent_->formatter_;
7878 config_formatter_ = parent_->config_formatter_;
7879 require_subcommand_max_ = parent_->require_subcommand_max_;
7880 }
7881}
7882
7883CLI11_NODISCARD CLI11_INLINE char **App::ensure_utf8(char **argv) {
7884#ifdef _WIN32
7885 (void)argv;
7886
7887 normalized_argv_ = detail::compute_win32_argv();
7888
7889 if(!normalized_argv_view_.empty()) {
7890 normalized_argv_view_.clear();
7891 }
7892
7893 normalized_argv_view_.reserve(normalized_argv_.size());
7894 for(auto &arg : normalized_argv_) {
7895 // using const_cast is well-defined, string is known to not be const.
7896 normalized_argv_view_.push_back(const_cast<char *>(arg.data()));
7897 }
7898
7899 return normalized_argv_view_.data();
7900#else
7901 return argv;
7902#endif
7903}
7904
7905CLI11_INLINE App *App::name(std::string app_name) {
7906
7907 if(parent_ != nullptr) {
7908 std::string oname = name_;
7909 name_ = app_name;
7910 const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
7911 if(!res.empty()) {
7912 name_ = oname;
7913 THROW(OptionAlreadyAdded(app_name + " conflicts with existing subcommand names"));
7914 }
7915 } else {
7916 name_ = app_name;
7917 }
7918 has_automatic_name_ = false;
7919 return this;
7920}
7921
7922CLI11_INLINE App *App::alias(std::string app_name) {
7923 if(app_name.empty() || !detail::valid_alias_name_string(app_name)) {
7924 THROW IncorrectConstruction("Aliases may not be empty or contain newlines or null characters");
7925 }
7926 if(parent_ != nullptr) {
7927 aliases_.push_back(app_name);
7928 const auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
7929 if(!res.empty()) {
7930 aliases_.pop_back();
7931 THROW(OptionAlreadyAdded("alias already matches an existing subcommand: " + app_name));
7932 }
7933 } else {
7934 aliases_.push_back(app_name);
7935 }
7936
7937 return this;
7938}
7939
7940CLI11_INLINE App *App::immediate_callback(bool immediate) {
7941 immediate_callback_ = immediate;
7942 if(immediate_callback_) {
7943 if(final_callback_ && !(parse_complete_callback_)) {
7944 std::swap(final_callback_, parse_complete_callback_);
7945 }
7946 } else if(!(final_callback_) && parse_complete_callback_) {
7947 std::swap(final_callback_, parse_complete_callback_);
7948 }
7949 return this;
7950}
7951
7952CLI11_INLINE App *App::ignore_case(bool value) {
7953 if(value && !ignore_case_) {
7954 ignore_case_ = true;
7955 auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
7956 const auto &match = _compare_subcommand_names(*this, *p);
7957 if(!match.empty()) {
7958 ignore_case_ = false; // we are throwing so need to be exception invariant
7959 THROW OptionAlreadyAdded("ignore case would cause subcommand name conflicts: " + match);
7960 }
7961 }
7962 ignore_case_ = value;
7963 return this;
7964}
7965
7966CLI11_INLINE App *App::ignore_underscore(bool value) {
7967 if(value && !ignore_underscore_) {
7968 ignore_underscore_ = true;
7969 auto *p = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
7970 const auto &match = _compare_subcommand_names(*this, *p);
7971 if(!match.empty()) {
7972 ignore_underscore_ = false;
7973 THROW OptionAlreadyAdded("ignore underscore would cause subcommand name conflicts: " + match);
7974 }
7975 }
7976 ignore_underscore_ = value;
7977 return this;
7978}
7979
7980CLI11_INLINE Option *App::add_option(std::string option_name,
7981 callback_t option_callback,
7982 std::string option_description,
7983 bool defaulted,
7984 std::function<std::string()> func) {
7985 Option myopt{option_name, option_description, option_callback, this};
7986
7987 if(std::find_if(std::begin(options_), std::end(options_), [&myopt](const Option_p &v) { return *v == myopt; }) ==
7988 std::end(options_)) {
7989 if(myopt.lnames_.empty() && myopt.snames_.empty()) {
7990 // if the option is positional only there is additional potential for ambiguities in config files and needs
7991 // to be checked
7992 std::string test_name = "--" + myopt.get_single_name();
7993 if(test_name.size() == 3) {
7994 test_name.erase(0, 1);
7995 }
7996
7997 auto *op = get_option_no_THROW(test_name);
7998 if(op != nullptr) {
7999 THROW(OptionAlreadyAdded("added option positional name matches existing option: " + test_name));
8000 }
8001 } else if(parent_ != nullptr) {
8002 for(auto &ln : myopt.lnames_) {
8003 auto *op = parent_->get_option_no_THROW(ln);
8004 if(op != nullptr) {
8005 THROW(OptionAlreadyAdded("added option matches existing positional option: " + ln));
8006 }
8007 }
8008 for(auto &sn : myopt.snames_) {
8009 auto *op = parent_->get_option_no_THROW(sn);
8010 if(op != nullptr) {
8011 THROW(OptionAlreadyAdded("added option matches existing positional option: " + sn));
8012 }
8013 }
8014 }
8015 options_.emplace_back();
8016 Option_p &option = options_.back();
8017 option.reset(new Option(option_name, option_description, option_callback, this));
8018
8019 // Set the default string capture function
8020 option->default_function(func);
8021
8022 // For compatibility with CLI11 1.7 and before, capture the default string here
8023 if(defaulted)
8024 option->capture_default_str();
8025
8026 // Transfer defaults to the new option
8027 option_defaults_.copy_to(option.get());
8028
8029 // Don't bother to capture if we already did
8030 if(!defaulted && option->get_always_capture_default())
8031 option->capture_default_str();
8032
8033 return option.get();
8034 }
8035 // we know something matches now find what it is so we can produce more error information
8036 for(auto &opt : options_) {
8037 const auto &matchname = opt->matching_name(myopt);
8038 if(!matchname.empty()) {
8039 THROW(OptionAlreadyAdded("added option matched existing option name: " + matchname));
8040 }
8041 }
8042 // this line should not be reached the above loop should trigger the throw
8043 THROW(OptionAlreadyAdded("added option matched existing option name")); // LCOV_EXCL_LINE
8044}
8045
8046CLI11_INLINE Option *App::set_help_flag(std::string flag_name, const std::string &help_description) {
8047 // take flag_description by const reference otherwise add_flag tries to assign to help_description
8048 if(help_ptr_ != nullptr) {
8049 remove_option(help_ptr_);
8050 help_ptr_ = nullptr;
8051 }
8052
8053 // Empty name will simply remove the help flag
8054 if(!flag_name.empty()) {
8055 help_ptr_ = add_flag(flag_name, help_description);
8056 help_ptr_->configurable(false);
8057 }
8058
8059 return help_ptr_;
8060}
8061
8062CLI11_INLINE Option *App::set_help_all_flag(std::string help_name, const std::string &help_description) {
8063 // take flag_description by const reference otherwise add_flag tries to assign to flag_description
8064 if(help_all_ptr_ != nullptr) {
8065 remove_option(help_all_ptr_);
8066 help_all_ptr_ = nullptr;
8067 }
8068
8069 // Empty name will simply remove the help all flag
8070 if(!help_name.empty()) {
8071 help_all_ptr_ = add_flag(help_name, help_description);
8072 help_all_ptr_->configurable(false);
8073 }
8074
8075 return help_all_ptr_;
8076}
8077
8078CLI11_INLINE Option *
8079App::set_version_flag(std::string flag_name, const std::string &versionString, const std::string &version_help) {
8080 // take flag_description by const reference otherwise add_flag tries to assign to version_description
8081 if(version_ptr_ != nullptr) {
8082 remove_option(version_ptr_);
8083 version_ptr_ = nullptr;
8084 }
8085
8086 // Empty name will simply remove the version flag
8087 if(!flag_name.empty()) {
8088 version_ptr_ = add_flag_callback(
8089 flag_name, [versionString]() { THROW(CLI::CallForVersion(versionString, 0)); }, version_help);
8090 version_ptr_->configurable(false);
8091 }
8092
8093 return version_ptr_;
8094}
8095
8096CLI11_INLINE Option *
8097App::set_version_flag(std::string flag_name, std::function<std::string()> vfunc, const std::string &version_help) {
8098 if(version_ptr_ != nullptr) {
8099 remove_option(version_ptr_);
8100 version_ptr_ = nullptr;
8101 }
8102
8103 // Empty name will simply remove the version flag
8104 if(!flag_name.empty()) {
8105 version_ptr_ =
8106 add_flag_callback(flag_name, [vfunc]() { THROW(CLI::CallForVersion(vfunc(), 0)); }, version_help);
8107 version_ptr_->configurable(false);
8108 }
8109
8110 return version_ptr_;
8111}
8112
8113CLI11_INLINE Option *App::_add_flag_internal(std::string flag_name, CLI::callback_t fun, std::string flag_description) {
8114 Option *opt = nullptr;
8115 if(detail::has_default_flag_values(flag_name)) {
8116 // check for default values and if it has them
8117 auto flag_defaults = detail::get_default_flag_values(flag_name);
8118 detail::remove_default_flag_values(flag_name);
8119 opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
8120 for(const auto &fname : flag_defaults)
8121 opt->fnames_.push_back(fname.first);
8122 opt->default_flag_values_ = std::move(flag_defaults);
8123 } else {
8124 opt = add_option(std::move(flag_name), std::move(fun), std::move(flag_description), false);
8125 }
8126 // flags cannot have positional values
8127 if(opt->get_positional()) {
8128 auto pos_name = opt->get_name(true);
8129 remove_option(opt);
8130 THROW IncorrectConstruction::PositionalFlag(pos_name);
8131 }
8132 opt->multi_option_policy(MultiOptionPolicy::TakeLast);
8133 opt->expected(0);
8134 opt->required(false);
8135 return opt;
8136}
8137
8138CLI11_INLINE Option *App::add_flag_callback(std::string flag_name,
8139 std::function<void(void)> function,
8140 std::string flag_description) {
8141
8142 CLI::callback_t fun = [function](const CLI::results_t &res) {
8143 using CLI::detail::lexical_cast;
8144 bool trigger{false};
8145 auto result = lexical_cast(res[0], trigger);
8146 if(result && trigger) {
8147 function();
8148 }
8149 return result;
8150 };
8151 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description));
8152}
8153
8154CLI11_INLINE Option *
8155App::add_flag_function(std::string flag_name,
8156 std::function<void(std::int64_t)> function,
8157 std::string flag_description) {
8158
8159 CLI::callback_t fun = [function](const CLI::results_t &res) {
8160 using CLI::detail::lexical_cast;
8161 std::int64_t flag_count{0};
8162 lexical_cast(res[0], flag_count);
8163 function(flag_count);
8164 return true;
8165 };
8166 return _add_flag_internal(flag_name, std::move(fun), std::move(flag_description))
8167 ->multi_option_policy(MultiOptionPolicy::Sum);
8168}
8169
8170CLI11_INLINE Option *App::set_config(std::string option_name,
8171 std::string default_filename,
8172 const std::string &help_message,
8173 bool config_required) {
8174
8175 // Remove existing config if present
8176 if(config_ptr_ != nullptr) {
8177 remove_option(config_ptr_);
8178 config_ptr_ = nullptr; // need to remove the config_ptr completely
8179 }
8180
8181 // Only add config if option passed
8182 if(!option_name.empty()) {
8183 config_ptr_ = add_option(option_name, help_message);
8184 if(config_required) {
8185 config_ptr_->required();
8186 }
8187 if(!default_filename.empty()) {
8188 config_ptr_->default_str(std::move(default_filename));
8189 config_ptr_->force_callback_ = true;
8190 }
8191 config_ptr_->configurable(false);
8192 // set the option to take the last value and reverse given by default
8193 config_ptr_->multi_option_policy(MultiOptionPolicy::Reverse);
8194 }
8195
8196 return config_ptr_;
8197}
8198
8199CLI11_INLINE bool App::remove_option(Option *opt) {
8200 // Make sure no links exist
8201 for(Option_p &op : options_) {
8202 op->remove_needs(opt);
8203 op->remove_excludes(opt);
8204 }
8205
8206 if(help_ptr_ == opt)
8207 help_ptr_ = nullptr;
8208 if(help_all_ptr_ == opt)
8209 help_all_ptr_ = nullptr;
8210
8211 auto iterator =
8212 std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
8213 if(iterator != std::end(options_)) {
8214 options_.erase(iterator);
8215 return true;
8216 }
8217 return false;
8218}
8219
8220CLI11_INLINE App *App::add_subcommand(std::string subcommand_name, std::string subcommand_description) {
8221 if(!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {
8222 if(!detail::valid_first_char(subcommand_name[0])) {
8223 THROW IncorrectConstruction(
8224 "Subcommand name starts with invalid character, '!' and '-' and control characters");
8225 }
8226 for(auto c : subcommand_name) {
8227 if(!detail::valid_later_char(c)) {
8228 THROW IncorrectConstruction(std::string("Subcommand name contains invalid character ('") + c +
8229 "'), all characters are allowed except"
8230 "'=',':','{','}', ' ', and control characters");
8231 }
8232 }
8233 }
8234 CLI::App_p subcom = std::shared_ptr<App>(new App(std::move(subcommand_description), subcommand_name, this));
8235 return add_subcommand(std::move(subcom));
8236}
8237
8238CLI11_INLINE App *App::add_subcommand(CLI::App_p subcom) {
8239 if(!subcom)
8240 THROW IncorrectConstruction("passed App is not valid");
8241 auto *ckapp = (name_.empty() && parent_ != nullptr) ? _get_fallthrough_parent() : this;
8242 const auto &mstrg = _compare_subcommand_names(*subcom, *ckapp);
8243 if(!mstrg.empty()) {
8244 THROW(OptionAlreadyAdded("subcommand name or alias matches existing subcommand: " + mstrg));
8245 }
8246 subcom->parent_ = this;
8247 subcommands_.push_back(std::move(subcom));
8248 return subcommands_.back().get();
8249}
8250
8251CLI11_INLINE bool App::remove_subcommand(App *subcom) {
8252 // Make sure no links exist
8253 for(App_p &sub : subcommands_) {
8254 sub->remove_excludes(subcom);
8255 sub->remove_needs(subcom);
8256 }
8257
8258 auto iterator = std::find_if(
8259 std::begin(subcommands_), std::end(subcommands_), [subcom](const App_p &v) { return v.get() == subcom; });
8260 if(iterator != std::end(subcommands_)) {
8261 subcommands_.erase(iterator);
8262 return true;
8263 }
8264 return false;
8265}
8266
8267CLI11_INLINE App *App::get_subcommand(const App *subcom) const {
8268 if(subcom == nullptr)
8269 THROW OptionNotFound("nullptr passed");
8270 for(const App_p &subcomptr : subcommands_)
8271 if(subcomptr.get() == subcom)
8272 return subcomptr.get();
8273 THROW OptionNotFound(subcom->get_name());
8274}
8275
8276CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(std::string subcom) const {
8277 auto *subc = _find_subcommand(subcom, false, false);
8278 if(subc == nullptr)
8279 THROW OptionNotFound(subcom);
8280 return subc;
8281}
8282
8283CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand_no_THROW(std::string subcom) const noexcept {
8284 return _find_subcommand(subcom, false, false);
8285}
8286
8287CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(int index) const {
8288 if(index >= 0) {
8289 auto uindex = static_cast<unsigned>(index);
8290 if(uindex < subcommands_.size())
8291 return subcommands_[uindex].get();
8292 }
8293 THROW OptionNotFound(std::to_string(index));
8294}
8295
8296CLI11_INLINE CLI::App_p App::get_subcommand_ptr(App *subcom) const {
8297 if(subcom == nullptr)
8298 THROW OptionNotFound("nullptr passed");
8299 for(const App_p &subcomptr : subcommands_)
8300 if(subcomptr.get() == subcom)
8301 return subcomptr;
8302 THROW OptionNotFound(subcom->get_name());
8303}
8304
8305CLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(std::string subcom) const {
8306 for(const App_p &subcomptr : subcommands_)
8307 if(subcomptr->check_name(subcom))
8308 return subcomptr;
8309 THROW OptionNotFound(subcom);
8310}
8311
8312CLI11_NODISCARD CLI11_INLINE CLI::App_p App::get_subcommand_ptr(int index) const {
8313 if(index >= 0) {
8314 auto uindex = static_cast<unsigned>(index);
8315 if(uindex < subcommands_.size())
8316 return subcommands_[uindex];
8317 }
8318 THROW OptionNotFound(std::to_string(index));
8319}
8320
8321CLI11_NODISCARD CLI11_INLINE CLI::App *App::get_option_group(std::string group_name) const {
8322 for(const App_p &app : subcommands_) {
8323 if(app->name_.empty() && app->group_ == group_name) {
8324 return app.get();
8325 }
8326 }
8327 THROW OptionNotFound(group_name);
8328}
8329
8330CLI11_NODISCARD CLI11_INLINE std::size_t App::count_all() const {
8331 std::size_t cnt{0};
8332 for(const auto &opt : options_) {
8333 cnt += opt->count();
8334 }
8335 for(const auto &sub : subcommands_) {
8336 cnt += sub->count_all();
8337 }
8338 if(!get_name().empty()) { // for named subcommands add the number of times the subcommand was called
8339 cnt += parsed_;
8340 }
8341 return cnt;
8342}
8343
8344CLI11_INLINE void App::clear() {
8345
8346 parsed_ = 0;
8347 pre_parse_called_ = false;
8348
8349 missing_.clear();
8350 parsed_subcommands_.clear();
8351 for(const Option_p &opt : options_) {
8352 opt->clear();
8353 }
8354 for(const App_p &subc : subcommands_) {
8355 subc->clear();
8356 }
8357}
8358
8359CLI11_INLINE void App::parse(int argc, const char *const *argv) { parse_char_t(argc, argv); }
8360CLI11_INLINE void App::parse(int argc, const wchar_t *const *argv) { parse_char_t(argc, argv); }
8361
8362namespace detail {
8363
8364// Do nothing or perform narrowing
8365CLI11_INLINE const char *maybe_narrow(const char *str) { return str; }
8366CLI11_INLINE std::string maybe_narrow(const wchar_t *str) { return narrow(str); }
8367
8368} // namespace detail
8369
8370template <class CharT> CLI11_INLINE void App::parse_char_t(int argc, const CharT *const *argv) {
8371 // If the name is not set, read from command line
8372 if(name_.empty() || has_automatic_name_) {
8373 has_automatic_name_ = true;
8374 name_ = detail::maybe_narrow(argv[0]);
8375 }
8376
8377 std::vector<std::string> args;
8378 args.reserve(static_cast<std::size_t>(argc) - 1U);
8379 for(auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i)
8380 args.emplace_back(detail::maybe_narrow(argv[i]));
8381
8382 parse(std::move(args));
8383}
8384
8385CLI11_INLINE void App::parse(std::string commandline, bool program_name_included) {
8386
8387 if(program_name_included) {
8388 auto nstr = detail::split_program_name(commandline);
8389 if((name_.empty()) || (has_automatic_name_)) {
8390 has_automatic_name_ = true;
8391 name_ = nstr.first;
8392 }
8393 commandline = std::move(nstr.second);
8394 } else {
8395 detail::trim(commandline);
8396 }
8397 // the next section of code is to deal with quoted arguments after an '=' or ':' for windows like operations
8398 if(!commandline.empty()) {
8399 commandline = detail::find_and_modify(commandline, "=", detail::escape_detect);
8400 if(allow_windows_style_options_)
8401 commandline = detail::find_and_modify(commandline, ":", detail::escape_detect);
8402 }
8403
8404 auto args = detail::split_up(std::move(commandline));
8405 // remove all empty strings
8406 args.erase(std::remove(args.begin(), args.end(), std::string{}), args.end());
8407 try {
8408 detail::remove_quotes(args);
8409 } catch(const std::invalid_argument &arg) {
8410#ifndef BB_NO_EXCEPTIONS
8411 THROW CLI::ParseError(arg.what(), CLI::ExitCodes::InvalidError);
8412#endif
8413 }
8414 std::reverse(args.begin(), args.end());
8415 parse(std::move(args));
8416}
8417
8418CLI11_INLINE void App::parse(std::wstring commandline, bool program_name_included) {
8419 parse(narrow(commandline), program_name_included);
8420}
8421
8422CLI11_INLINE void App::parse(std::vector<std::string> &args) {
8423 // Clear if parsed
8424 if(parsed_ > 0)
8425 clear();
8426
8427 // parsed_ is incremented in commands/subcommands,
8428 // but placed here to make sure this is cleared when
8429 // running parse after an error is thrown, even by _validate or _configure.
8430 parsed_ = 1;
8431 _validate();
8432 _configure();
8433 // set the parent as nullptr as this object should be the top now
8434 parent_ = nullptr;
8435 parsed_ = 0;
8436
8437 _parse(args);
8438 run_callback();
8439}
8440
8441CLI11_INLINE void App::parse(std::vector<std::string> &&args) {
8442 // Clear if parsed
8443 if(parsed_ > 0)
8444 clear();
8445
8446 // parsed_ is incremented in commands/subcommands,
8447 // but placed here to make sure this is cleared when
8448 // running parse after an error is thrown, even by _validate or _configure.
8449 parsed_ = 1;
8450 _validate();
8451 _configure();
8452 // set the parent as nullptr as this object should be the top now
8453 parent_ = nullptr;
8454 parsed_ = 0;
8455
8456 _parse(std::move(args));
8457 run_callback();
8458}
8459
8460CLI11_INLINE void App::parse_from_stream(std::istream &input) {
8461 if(parsed_ == 0) {
8462 _validate();
8463 _configure();
8464 // set the parent as nullptr as this object should be the top now
8465 }
8466
8467 _parse_stream(input);
8468 run_callback();
8469}
8470
8471CLI11_INLINE int App::exit(const Error &e, std::ostream &out, std::ostream &err) const {
8472
8474 if(e.get_name() == "RuntimeError")
8475 return e.get_exit_code();
8476
8477 if(e.get_name() == "CallForHelp") {
8478 out << help();
8479 return e.get_exit_code();
8480 }
8481
8482 if(e.get_name() == "CallForAllHelp") {
8483 out << help("", AppFormatMode::All);
8484 return e.get_exit_code();
8485 }
8486
8487 if(e.get_name() == "CallForVersion") {
8488 out << e.what() << '\n';
8489 return e.get_exit_code();
8490 }
8491
8492 if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {
8493 if(failure_message_)
8494 err << failure_message_(this, e) << std::flush;
8495 }
8496
8497 return e.get_exit_code();
8498}
8499
8500CLI11_INLINE std::vector<const App *> App::get_subcommands(const std::function<bool(const App *)> &filter) const {
8501 std::vector<const App *> subcomms(subcommands_.size());
8502 std::transform(
8503 std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { return v.get(); });
8504
8505 if(filter) {
8506 subcomms.erase(std::remove_if(std::begin(subcomms),
8507 std::end(subcomms),
8508 [&filter](const App *app) { return !filter(app); }),
8509 std::end(subcomms));
8510 }
8511
8512 return subcomms;
8513}
8514
8515CLI11_INLINE std::vector<App *> App::get_subcommands(const std::function<bool(App *)> &filter) {
8516 std::vector<App *> subcomms(subcommands_.size());
8517 std::transform(
8518 std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) { return v.get(); });
8519
8520 if(filter) {
8521 subcomms.erase(
8522 std::remove_if(std::begin(subcomms), std::end(subcomms), [&filter](App *app) { return !filter(app); }),
8523 std::end(subcomms));
8524 }
8525
8526 return subcomms;
8527}
8528
8529CLI11_INLINE bool App::remove_excludes(Option *opt) {
8530 auto iterator = std::find(std::begin(exclude_options_), std::end(exclude_options_), opt);
8531 if(iterator == std::end(exclude_options_)) {
8532 return false;
8533 }
8534 exclude_options_.erase(iterator);
8535 return true;
8536}
8537
8538CLI11_INLINE bool App::remove_excludes(App *app) {
8539 auto iterator = std::find(std::begin(exclude_subcommands_), std::end(exclude_subcommands_), app);
8540 if(iterator == std::end(exclude_subcommands_)) {
8541 return false;
8542 }
8543 auto *other_app = *iterator;
8544 exclude_subcommands_.erase(iterator);
8545 other_app->remove_excludes(this);
8546 return true;
8547}
8548
8549CLI11_INLINE bool App::remove_needs(Option *opt) {
8550 auto iterator = std::find(std::begin(need_options_), std::end(need_options_), opt);
8551 if(iterator == std::end(need_options_)) {
8552 return false;
8553 }
8554 need_options_.erase(iterator);
8555 return true;
8556}
8557
8558CLI11_INLINE bool App::remove_needs(App *app) {
8559 auto iterator = std::find(std::begin(need_subcommands_), std::end(need_subcommands_), app);
8560 if(iterator == std::end(need_subcommands_)) {
8561 return false;
8562 }
8563 need_subcommands_.erase(iterator);
8564 return true;
8565}
8566
8567CLI11_NODISCARD CLI11_INLINE std::string App::help(std::string prev, AppFormatMode mode) const {
8568 if(prev.empty())
8569 prev = get_name();
8570 else
8571 prev += " " + get_name();
8572
8573 // Delegate to subcommand if needed
8574 auto selected_subcommands = get_subcommands();
8575 if(!selected_subcommands.empty()) {
8576 return selected_subcommands.back()->help(prev, mode);
8577 }
8578 return formatter_->make_help(this, prev, mode);
8579}
8580
8581CLI11_NODISCARD CLI11_INLINE std::string App::version() const {
8582 std::string val;
8583 if(version_ptr_ != nullptr) {
8584 // copy the results for reuse later
8585 results_t rv = version_ptr_->results();
8586 version_ptr_->clear();
8587 version_ptr_->add_result("true");
8588 try {
8589 version_ptr_->run_callback();
8590 } catch(const CLI::CallForVersion &cfv) {
8591#ifndef BB_NO_EXCEPTIONS
8592 val = cfv.what();
8593#endif
8594 }
8595 version_ptr_->clear();
8596 version_ptr_->add_result(rv);
8597 }
8598 return val;
8599}
8600
8601CLI11_INLINE std::vector<const Option *> App::get_options(const std::function<bool(const Option *)> filter) const {
8602 std::vector<const Option *> options(options_.size());
8603 std::transform(
8604 std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) { return val.get(); });
8605
8606 if(filter) {
8607 options.erase(std::remove_if(std::begin(options),
8608 std::end(options),
8609 [&filter](const Option *opt) { return !filter(opt); }),
8610 std::end(options));
8611 }
8612
8613 return options;
8614}
8615
8616CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Option *)> filter) {
8617 std::vector<Option *> options(options_.size());
8618 std::transform(
8619 std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) { return val.get(); });
8620
8621 if(filter) {
8622 options.erase(
8623 std::remove_if(std::begin(options), std::end(options), [&filter](Option *opt) { return !filter(opt); }),
8624 std::end(options));
8625 }
8626
8627 return options;
8628}
8629
8630CLI11_NODISCARD CLI11_INLINE Option *App::get_option_no_THROW(std::string option_name) noexcept {
8631 for(Option_p &opt : options_) {
8632 if(opt->check_name(option_name)) {
8633 return opt.get();
8634 }
8635 }
8636 for(auto &subc : subcommands_) {
8637 // also check down into nameless subcommands
8638 if(subc->get_name().empty()) {
8639 auto *opt = subc->get_option_no_THROW(option_name);
8640 if(opt != nullptr) {
8641 return opt;
8642 }
8643 }
8644 }
8645 return nullptr;
8646}
8647
8648CLI11_NODISCARD CLI11_INLINE const Option *App::get_option_no_THROW(std::string option_name) const noexcept {
8649 for(const Option_p &opt : options_) {
8650 if(opt->check_name(option_name)) {
8651 return opt.get();
8652 }
8653 }
8654 for(const auto &subc : subcommands_) {
8655 // also check down into nameless subcommands
8656 if(subc->get_name().empty()) {
8657 auto *opt = subc->get_option_no_THROW(option_name);
8658 if(opt != nullptr) {
8659 return opt;
8660 }
8661 }
8662 }
8663 return nullptr;
8664}
8665
8666CLI11_NODISCARD CLI11_INLINE std::string App::get_display_name(bool with_aliases) const {
8667 if(name_.empty()) {
8668 return std::string("[Option Group: ") + get_group() + "]";
8669 }
8670 if(aliases_.empty() || !with_aliases) {
8671 return name_;
8672 }
8673 std::string dispname = name_;
8674 for(const auto &lalias : aliases_) {
8675 dispname.push_back(',');
8676 dispname.push_back(' ');
8677 dispname.append(lalias);
8678 }
8679 return dispname;
8680}
8681
8682CLI11_NODISCARD CLI11_INLINE bool App::check_name(std::string name_to_check) const {
8683 std::string local_name = name_;
8684 if(ignore_underscore_) {
8685 local_name = detail::remove_underscore(name_);
8686 name_to_check = detail::remove_underscore(name_to_check);
8687 }
8688 if(ignore_case_) {
8689 local_name = detail::to_lower(name_);
8690 name_to_check = detail::to_lower(name_to_check);
8691 }
8692
8693 if(local_name == name_to_check) {
8694 return true;
8695 }
8696 for(std::string les : aliases_) { // NOLINT(performance-for-range-copy)
8697 if(ignore_underscore_) {
8698 les = detail::remove_underscore(les);
8699 }
8700 if(ignore_case_) {
8701 les = detail::to_lower(les);
8702 }
8703 if(les == name_to_check) {
8704 return true;
8705 }
8706 }
8707 return false;
8708}
8709
8710CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::get_groups() const {
8711 std::vector<std::string> groups;
8712
8713 for(const Option_p &opt : options_) {
8714 // Add group if it is not already in there
8715 if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) {
8716 groups.push_back(opt->get_group());
8717 }
8718 }
8719
8720 return groups;
8721}
8722
8723CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining(bool recurse) const {
8724 std::vector<std::string> miss_list;
8725 for(const std::pair<detail::Classifier, std::string> &miss : missing_) {
8726 miss_list.push_back(std::get<1>(miss));
8727 }
8728 // Get from a subcommand that may allow extras
8729 if(recurse) {
8730 if(!allow_extras_) {
8731 for(const auto &sub : subcommands_) {
8732 if(sub->name_.empty() && !sub->missing_.empty()) {
8733 for(const std::pair<detail::Classifier, std::string> &miss : sub->missing_) {
8734 miss_list.push_back(std::get<1>(miss));
8735 }
8736 }
8737 }
8738 }
8739 // Recurse into subcommands
8740
8741 for(const App *sub : parsed_subcommands_) {
8742 std::vector<std::string> output = sub->remaining(recurse);
8743 std::copy(std::begin(output), std::end(output), std::back_inserter(miss_list));
8744 }
8745 }
8746 return miss_list;
8747}
8748
8749CLI11_NODISCARD CLI11_INLINE std::vector<std::string> App::remaining_for_passthrough(bool recurse) const {
8750 std::vector<std::string> miss_list = remaining(recurse);
8751 std::reverse(std::begin(miss_list), std::end(miss_list));
8752 return miss_list;
8753}
8754
8755CLI11_NODISCARD CLI11_INLINE std::size_t App::remaining_size(bool recurse) const {
8756 auto remaining_options = static_cast<std::size_t>(std::count_if(
8757 std::begin(missing_), std::end(missing_), [](const std::pair<detail::Classifier, std::string> &val) {
8758 return val.first != detail::Classifier::POSITIONAL_MARK;
8759 }));
8760
8761 if(recurse) {
8762 for(const App_p &sub : subcommands_) {
8763 remaining_options += sub->remaining_size(recurse);
8764 }
8765 }
8766 return remaining_options;
8767}
8768
8769CLI11_INLINE void App::_validate() const {
8770 // count the number of positional only args
8771 auto pcount = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
8772 return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional();
8773 });
8774 if(pcount > 1) {
8775 auto pcount_req = std::count_if(std::begin(options_), std::end(options_), [](const Option_p &opt) {
8776 return opt->get_items_expected_max() >= detail::expected_max_vector_size && !opt->nonpositional() &&
8777 opt->get_required();
8778 });
8779 if(pcount - pcount_req > 1) {
8780 THROW InvalidError(name_);
8781 }
8782 }
8783
8784 std::size_t nameless_subs{0};
8785 for(const App_p &app : subcommands_) {
8786 app->_validate();
8787 if(app->get_name().empty())
8788 ++nameless_subs;
8789 }
8790
8791 if(require_option_min_ > 0) {
8792 if(require_option_max_ > 0) {
8793 if(require_option_max_ < require_option_min_) {
8794 THROW(InvalidError("Required min options greater than required max options", ExitCodes::InvalidError));
8795 }
8796 }
8797 if(require_option_min_ > (options_.size() + nameless_subs)) {
8798 THROW(
8799 InvalidError("Required min options greater than number of available options", ExitCodes::InvalidError));
8800 }
8801 }
8802}
8803
8804CLI11_INLINE void App::_configure() {
8805 if(default_startup == startup_mode::enabled) {
8806 disabled_ = false;
8807 } else if(default_startup == startup_mode::disabled) {
8808 disabled_ = true;
8809 }
8810 for(const App_p &app : subcommands_) {
8811 if(app->has_automatic_name_) {
8812 app->name_.clear();
8813 }
8814 if(app->name_.empty()) {
8815 app->fallthrough_ = false; // make sure fallthrough_ is false to prevent infinite loop
8816 app->prefix_command_ = false;
8817 }
8818 // make sure the parent is set to be this object in preparation for parse
8819 app->parent_ = this;
8820 app->_configure();
8821 }
8822}
8823
8824CLI11_INLINE void App::run_callback(bool final_mode, bool suppress_final_callback) {
8825 pre_callback();
8826 // in the main app if immediate_callback_ is set it runs the main callback before the used subcommands
8827 if(!final_mode && parse_complete_callback_) {
8828 parse_complete_callback_();
8829 }
8830 // run the callbacks for the received subcommands
8831 for(App *subc : get_subcommands()) {
8832 if(subc->parent_ == this) {
8833 subc->run_callback(true, suppress_final_callback);
8834 }
8835 }
8836 // now run callbacks for option_groups
8837 for(auto &subc : subcommands_) {
8838 if(subc->name_.empty() && subc->count_all() > 0) {
8839 subc->run_callback(true, suppress_final_callback);
8840 }
8841 }
8842
8843 // finally run the main callback
8844 if(final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) {
8845 if(!name_.empty() || count_all() > 0 || parent_ == nullptr) {
8846 final_callback_();
8847 }
8848 }
8849}
8850
8851CLI11_NODISCARD CLI11_INLINE bool App::_valid_subcommand(const std::string &current, bool ignore_used) const {
8852 // Don't match if max has been reached - but still check parents
8853 if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
8854 return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
8855 }
8856 auto *com = _find_subcommand(current, true, ignore_used);
8857 if(com != nullptr) {
8858 return true;
8859 }
8860 // Check parent if exists, else return false
8861 return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
8862}
8863
8864CLI11_NODISCARD CLI11_INLINE detail::Classifier App::_recognize(const std::string &current,
8865 bool ignore_used_subcommands) const {
8866 std::string dummy1, dummy2;
8867
8868 if(current == "--")
8869 return detail::Classifier::POSITIONAL_MARK;
8870 if(_valid_subcommand(current, ignore_used_subcommands))
8871 return detail::Classifier::SUBCOMMAND;
8872 if(detail::split_long(current, dummy1, dummy2))
8873 return detail::Classifier::LONG;
8874 if(detail::split_short(current, dummy1, dummy2)) {
8875 if(dummy1[0] >= '0' && dummy1[0] <= '9') {
8876 if(get_option_no_THROW(std::string{'-', dummy1[0]}) == nullptr) {
8877 return detail::Classifier::NONE;
8878 }
8879 }
8880 return detail::Classifier::SHORT;
8881 }
8882 if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
8883 return detail::Classifier::WINDOWS_STYLE;
8884 if((current == "++") && !name_.empty() && parent_ != nullptr)
8885 return detail::Classifier::SUBCOMMAND_TERMINATOR;
8886 auto dotloc = current.find_first_of('.');
8887 if(dotloc != std::string::npos) {
8888 auto *cm = _find_subcommand(current.substr(0, dotloc), true, ignore_used_subcommands);
8889 if(cm != nullptr) {
8890 auto res = cm->_recognize(current.substr(dotloc + 1), ignore_used_subcommands);
8891 if(res == detail::Classifier::SUBCOMMAND) {
8892 return res;
8893 }
8894 }
8895 }
8896 return detail::Classifier::NONE;
8897}
8898
8899CLI11_INLINE bool App::_process_config_file(const std::string &config_file, bool throw_error) {
8900 auto path_result = detail::check_path(config_file.c_str());
8901 if(path_result == detail::path_type::file) {
8902 try {
8903 std::vector<ConfigItem> values = config_formatter_->from_file(config_file);
8904 _parse_config(values);
8905 return true;
8906 } catch(const FileError &) {
8907 if(throw_error) {
8908 RETHROW;
8909 }
8910 return false;
8911 }
8912 } else if(throw_error) {
8913 THROW FileError::Missing(config_file);
8914 } else {
8915 return false;
8916 }
8917}
8918
8919CLI11_INLINE void App::_process_config_file() {
8920 if(config_ptr_ != nullptr) {
8921 bool config_required = config_ptr_->get_required();
8922 auto file_given = config_ptr_->count() > 0;
8923 if(!(file_given || config_ptr_->envname_.empty())) {
8924 std::string ename_string = detail::get_environment_value(config_ptr_->envname_);
8925 if(!ename_string.empty()) {
8926 config_ptr_->add_result(ename_string);
8927 }
8928 }
8929 config_ptr_->run_callback();
8930
8931 auto config_files = config_ptr_->as<std::vector<std::string>>();
8932 bool files_used{file_given};
8933 if(config_files.empty() || config_files.front().empty()) {
8934 if(config_required) {
8935 THROW FileError("config file is required but none was given");
8936 }
8937 return;
8938 }
8939 for(const auto &config_file : config_files) {
8940 if(_process_config_file(config_file, config_required || file_given)) {
8941 files_used = true;
8942 }
8943 }
8944 if(!files_used) {
8945 // this is done so the count shows as 0 if no callbacks were processed
8946 config_ptr_->clear();
8947 bool force = config_ptr_->force_callback_;
8948 config_ptr_->force_callback_ = false;
8949 config_ptr_->run_callback();
8950 config_ptr_->force_callback_ = force;
8951 }
8952 }
8953}
8954
8955CLI11_INLINE void App::_process_env() {
8956 for(const Option_p &opt : options_) {
8957 if(opt->count() == 0 && !opt->envname_.empty()) {
8958 std::string ename_string = detail::get_environment_value(opt->envname_);
8959 if(!ename_string.empty()) {
8960 std::string result = ename_string;
8961 result = opt->_validate(result, 0);
8962 if(result.empty()) {
8963 opt->add_result(ename_string);
8964 }
8965 }
8966 }
8967 }
8968
8969 for(App_p &sub : subcommands_) {
8970 if(sub->get_name().empty() || (sub->count_all() > 0 && !sub->parse_complete_callback_)) {
8971 // only process environment variables if the callback has actually been triggered already
8972 sub->_process_env();
8973 }
8974 }
8975}
8976
8977CLI11_INLINE void App::_process_callbacks() {
8978
8979 for(App_p &sub : subcommands_) {
8980 // process the priority option_groups first
8981 if(sub->get_name().empty() && sub->parse_complete_callback_) {
8982 if(sub->count_all() > 0) {
8983 sub->_process_callbacks();
8984 sub->run_callback();
8985 }
8986 }
8987 }
8988
8989 for(const Option_p &opt : options_) {
8990 if((*opt) && !opt->get_callback_run()) {
8991 opt->run_callback();
8992 }
8993 }
8994 for(App_p &sub : subcommands_) {
8995 if(!sub->parse_complete_callback_) {
8996 sub->_process_callbacks();
8997 }
8998 }
8999}
9000
9001CLI11_INLINE void App::_process_help_flags(bool trigger_help, bool trigger_all_help) const {
9002 const Option *help_ptr = get_help_ptr();
9003 const Option *help_all_ptr = get_help_all_ptr();
9004
9005 if(help_ptr != nullptr && help_ptr->count() > 0)
9006 trigger_help = true;
9007 if(help_all_ptr != nullptr && help_all_ptr->count() > 0)
9008 trigger_all_help = true;
9009
9010 // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones.
9011 if(!parsed_subcommands_.empty()) {
9012 for(const App *sub : parsed_subcommands_)
9013 sub->_process_help_flags(trigger_help, trigger_all_help);
9014
9015 // Only the final subcommand should call for help. All help wins over help.
9016 } else if(trigger_all_help) {
9017 THROW CallForAllHelp();
9018 } else if(trigger_help) {
9019 THROW CallForHelp();
9020 }
9021}
9022
9023CLI11_INLINE void App::_process_requirements() {
9024 // check excludes
9025 bool excluded{false};
9026 std::string excluder;
9027 for(const auto &opt : exclude_options_) {
9028 if(opt->count() > 0) {
9029 excluded = true;
9030 excluder = opt->get_name();
9031 }
9032 }
9033 for(const auto &subc : exclude_subcommands_) {
9034 if(subc->count_all() > 0) {
9035 excluded = true;
9036 excluder = subc->get_display_name();
9037 }
9038 }
9039 if(excluded) {
9040 if(count_all() > 0) {
9041 THROW ExcludesError(get_display_name(), excluder);
9042 }
9043 // if we are excluded but didn't receive anything, just return
9044 return;
9045 }
9046
9047 // check excludes
9048 bool missing_needed{false};
9049 std::string missing_need;
9050 for(const auto &opt : need_options_) {
9051 if(opt->count() == 0) {
9052 missing_needed = true;
9053 missing_need = opt->get_name();
9054 }
9055 }
9056 for(const auto &subc : need_subcommands_) {
9057 if(subc->count_all() == 0) {
9058 missing_needed = true;
9059 missing_need = subc->get_display_name();
9060 }
9061 }
9062 if(missing_needed) {
9063 if(count_all() > 0) {
9064 THROW RequiresError(get_display_name(), missing_need);
9065 }
9066 // if we missing something but didn't have any options, just return
9067 return;
9068 }
9069
9070 std::size_t used_options = 0;
9071 for(const Option_p &opt : options_) {
9072
9073 if(opt->count() != 0) {
9074 ++used_options;
9075 }
9076 // Required but empty
9077 if(opt->get_required() && opt->count() == 0) {
9078 THROW RequiredError(opt->get_name());
9079 }
9080 // Requires
9081 for(const Option *opt_req : opt->needs_)
9082 if(opt->count() > 0 && opt_req->count() == 0)
9083 THROW RequiresError(opt->get_name(), opt_req->get_name());
9084 // Excludes
9085 for(const Option *opt_ex : opt->excludes_)
9086 if(opt->count() > 0 && opt_ex->count() != 0)
9087 THROW ExcludesError(opt->get_name(), opt_ex->get_name());
9088 }
9089 // check for the required number of subcommands
9090 if(require_subcommand_min_ > 0) {
9091 auto selected_subcommands = get_subcommands();
9092 if(require_subcommand_min_ > selected_subcommands.size())
9093 THROW RequiredError::Subcommand(require_subcommand_min_);
9094 }
9095
9096 // Max error cannot occur, the extra subcommand will parse as an ExtrasError or a remaining item.
9097
9098 // run this loop to check how many unnamed subcommands were actually used since they are considered options
9099 // from the perspective of an App
9100 for(App_p &sub : subcommands_) {
9101 if(sub->disabled_)
9102 continue;
9103 if(sub->name_.empty() && sub->count_all() > 0) {
9104 ++used_options;
9105 }
9106 }
9107
9108 if(require_option_min_ > used_options || (require_option_max_ > 0 && require_option_max_ < used_options)) {
9109 auto option_list = detail::join(options_, [this](const Option_p &ptr) {
9110 if(ptr.get() == help_ptr_ || ptr.get() == help_all_ptr_) {
9111 return std::string{};
9112 }
9113 return ptr->get_name(false, true);
9114 });
9115
9116 auto subc_list = get_subcommands([](App *app) { return ((app->get_name().empty()) && (!app->disabled_)); });
9117 if(!subc_list.empty()) {
9118 option_list += "," + detail::join(subc_list, [](const App *app) { return app->get_display_name(); });
9119 }
9120 THROW RequiredError::Option(require_option_min_, require_option_max_, used_options, option_list);
9121 }
9122
9123 // now process the requirements for subcommands if needed
9124 for(App_p &sub : subcommands_) {
9125 if(sub->disabled_)
9126 continue;
9127 if(sub->name_.empty() && sub->required_ == false) {
9128 if(sub->count_all() == 0) {
9129 if(require_option_min_ > 0 && require_option_min_ <= used_options) {
9130 continue;
9131 // if we have met the requirement and there is nothing in this option group skip checking
9132 // requirements
9133 }
9134 if(require_option_max_ > 0 && used_options >= require_option_min_) {
9135 continue;
9136 // if we have met the requirement and there is nothing in this option group skip checking
9137 // requirements
9138 }
9139 }
9140 }
9141 if(sub->count() > 0 || sub->name_.empty()) {
9142 sub->_process_requirements();
9143 }
9144
9145 if(sub->required_ && sub->count_all() == 0) {
9146 THROW(CLI::RequiredError(sub->get_display_name()));
9147 }
9148 }
9149}
9150
9151CLI11_INLINE void App::_process() {
9152 try {
9153 // the config file might generate a FileError but that should not be processed until later in the process
9154 // to allow for help, version and other errors to generate first.
9155 _process_config_file();
9156
9157 // process env shouldn't THROW but no reason to process it if config generated an error
9158 _process_env();
9159 } catch(const CLI::FileError &) {
9160 // callbacks and help_flags can generate exceptions which should take priority
9161 // over the config file error if one exists.
9162 _process_callbacks();
9163 _process_help_flags();
9164 RETHROW;
9165 }
9166
9167 _process_callbacks();
9168 _process_help_flags();
9169
9170 _process_requirements();
9171}
9172
9173CLI11_INLINE void App::_process_extras() {
9174 if(!(allow_extras_ || prefix_command_)) {
9175 std::size_t num_left_over = remaining_size();
9176 if(num_left_over > 0) {
9177 THROW ExtrasError(name_, remaining(false));
9178 }
9179 }
9180
9181 for(App_p &sub : subcommands_) {
9182 if(sub->count() > 0)
9183 sub->_process_extras();
9184 }
9185}
9186
9187CLI11_INLINE void App::_process_extras(std::vector<std::string> &args) {
9188 if(!(allow_extras_ || prefix_command_)) {
9189 std::size_t num_left_over = remaining_size();
9190 if(num_left_over > 0) {
9191 args = remaining(false);
9192 THROW ExtrasError(name_, args);
9193 }
9194 }
9195
9196 for(App_p &sub : subcommands_) {
9197 if(sub->count() > 0)
9198 sub->_process_extras(args);
9199 }
9200}
9201
9202CLI11_INLINE void App::increment_parsed() {
9203 ++parsed_;
9204 for(App_p &sub : subcommands_) {
9205 if(sub->get_name().empty())
9206 sub->increment_parsed();
9207 }
9208}
9209
9210CLI11_INLINE void App::_parse(std::vector<std::string> &args) {
9211 increment_parsed();
9212 _trigger_pre_parse(args.size());
9213 bool positional_only = false;
9214
9215 while(!args.empty()) {
9216 if(!_parse_single(args, positional_only)) {
9217 break;
9218 }
9219 }
9220
9221 if(parent_ == nullptr) {
9222 _process();
9223
9224 // Throw error if any items are left over (depending on settings)
9225 _process_extras(args);
9226
9227 // Convert missing (pairs) to extras (string only) ready for processing in another app
9228 args = remaining_for_passthrough(false);
9229 } else if(parse_complete_callback_) {
9230 _process_env();
9231 _process_callbacks();
9232 _process_help_flags();
9233 _process_requirements();
9234 run_callback(false, true);
9235 }
9236}
9237
9238CLI11_INLINE void App::_parse(std::vector<std::string> &&args) {
9239 // this can only be called by the top level in which case parent == nullptr by definition
9240 // operation is simplified
9241 increment_parsed();
9242 _trigger_pre_parse(args.size());
9243 bool positional_only = false;
9244
9245 while(!args.empty()) {
9246 _parse_single(args, positional_only);
9247 }
9248 _process();
9249
9250 // Throw error if any items are left over (depending on settings)
9251 _process_extras();
9252}
9253
9254CLI11_INLINE void App::_parse_stream(std::istream &input) {
9255 auto values = config_formatter_->from_config(input);
9256 _parse_config(values);
9257 increment_parsed();
9258 _trigger_pre_parse(values.size());
9259 _process();
9260
9261 // Throw error if any items are left over (depending on settings)
9262 _process_extras();
9263}
9264
9265CLI11_INLINE void App::_parse_config(const std::vector<ConfigItem> &args) {
9266 for(const ConfigItem &item : args) {
9267 if(!_parse_single_config(item) && allow_config_extras_ == config_extras_mode::error)
9268 THROW ConfigError::Extras(item.fullname());
9269 }
9270}
9271
9272CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t level) {
9273
9274 if(level < item.parents.size()) {
9275 auto *subcom = get_subcommand_no_THROW(item.parents.at(level));
9276 return (subcom != nullptr) ? subcom->_parse_single_config(item, level + 1) : false;
9277 }
9278 // check for section open
9279 if(item.name == "++") {
9280 if(configurable_) {
9281 increment_parsed();
9282 _trigger_pre_parse(2);
9283 if(parent_ != nullptr) {
9284 parent_->parsed_subcommands_.push_back(this);
9285 }
9286 }
9287 return true;
9288 }
9289 // check for section close
9290 if(item.name == "--") {
9291 if(configurable_ && parse_complete_callback_) {
9292 _process_callbacks();
9293 _process_requirements();
9294 run_callback();
9295 }
9296 return true;
9297 }
9298 Option *op = get_option_no_THROW("--" + item.name);
9299 if(op == nullptr) {
9300 if(item.name.size() == 1) {
9301 op = get_option_no_THROW("-" + item.name);
9302 }
9303 if(op == nullptr) {
9304 op = get_option_no_THROW(item.name);
9305 }
9306 }
9307
9308 if(op == nullptr) {
9309 // If the option was not present
9310 if(get_allow_config_extras() == config_extras_mode::capture) {
9311 // Should we worry about classifying the extras properly?
9312 missing_.emplace_back(detail::Classifier::NONE, item.fullname());
9313 for(const auto &input : item.inputs) {
9314 missing_.emplace_back(detail::Classifier::NONE, input);
9315 }
9316 }
9317 return false;
9318 }
9319
9320 if(!op->get_configurable()) {
9321 if(get_allow_config_extras() == config_extras_mode::ignore_all) {
9322 return false;
9323 }
9324 THROW ConfigError::NotConfigurable(item.fullname());
9325 }
9326
9327 if(op->empty()) {
9328
9329 if(op->get_expected_min() == 0) {
9330 if(item.inputs.size() <= 1) {
9331 // Flag parsing
9332 auto res = config_formatter_->to_flag(item);
9333 bool converted{false};
9334 if(op->get_disable_flag_override()) {
9335 auto val = detail::to_flag_value(res);
9336 if(val == 1) {
9337 res = op->get_flag_value(item.name, "{}");
9338 converted = true;
9339 }
9340 }
9341
9342 if(!converted) {
9343 errno = 0;
9344 res = op->get_flag_value(item.name, res);
9345 }
9346
9347 op->add_result(res);
9348 return true;
9349 }
9350 if(static_cast<int>(item.inputs.size()) > op->get_items_expected_max() &&
9351 op->get_multi_option_policy() != MultiOptionPolicy::TakeAll) {
9352 if(op->get_items_expected_max() > 1) {
9353 THROW ArgumentMismatch::AtMost(item.fullname(), op->get_items_expected_max(), item.inputs.size());
9354 }
9355
9356 if(!op->get_disable_flag_override()) {
9357 THROW ConversionError::TooManyInputsFlag(item.fullname());
9358 }
9359 // if the disable flag override is set then we must have the flag values match a known flag value
9360 // this is true regardless of the output value, so an array input is possible and must be accounted for
9361 for(const auto &res : item.inputs) {
9362 bool valid_value{false};
9363 if(op->default_flag_values_.empty()) {
9364 if(res == "true" || res == "false" || res == "1" || res == "0") {
9365 valid_value = true;
9366 }
9367 } else {
9368 for(const auto &valid_res : op->default_flag_values_) {
9369 if(valid_res.second == res) {
9370 valid_value = true;
9371 break;
9372 }
9373 }
9374 }
9375
9376 if(valid_value) {
9377 op->add_result(res);
9378 } else {
9379 THROW InvalidError("invalid flag argument given");
9380 }
9381 }
9382 return true;
9383 }
9384 }
9385 op->add_result(item.inputs);
9386 op->run_callback();
9387 }
9388
9389 return true;
9390}
9391
9392CLI11_INLINE bool App::_parse_single(std::vector<std::string> &args, bool &positional_only) {
9393 bool retval = true;
9394 detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back());
9395 switch(classifier) {
9396 case detail::Classifier::POSITIONAL_MARK:
9397 args.pop_back();
9398 positional_only = true;
9399 if((!_has_remaining_positionals()) && (parent_ != nullptr)) {
9400 retval = false;
9401 } else {
9402 _move_to_missing(classifier, "--");
9403 }
9404 break;
9405 case detail::Classifier::SUBCOMMAND_TERMINATOR:
9406 // treat this like a positional mark if in the parent app
9407 args.pop_back();
9408 retval = false;
9409 break;
9410 case detail::Classifier::SUBCOMMAND:
9411 retval = _parse_subcommand(args);
9412 break;
9413 case detail::Classifier::LONG:
9414 case detail::Classifier::SHORT:
9415 case detail::Classifier::WINDOWS_STYLE:
9416 // If already parsed a subcommand, don't accept options_
9417 retval = _parse_arg(args, classifier, false);
9418 break;
9419 case detail::Classifier::NONE:
9420 // Probably a positional or something for a parent (sub)command
9421 retval = _parse_positional(args, false);
9422 if(retval && positionals_at_end_) {
9423 positional_only = true;
9424 }
9425 break;
9426 // LCOV_EXCL_START
9427 default:
9428 THROW HorribleError("unrecognized classifier (you should not see this!)");
9429 // LCOV_EXCL_STOP
9430 }
9431 return retval;
9432}
9433
9434CLI11_NODISCARD CLI11_INLINE std::size_t App::_count_remaining_positionals(bool required_only) const {
9435 std::size_t retval = 0;
9436 for(const Option_p &opt : options_) {
9437 if(opt->get_positional() && (!required_only || opt->get_required())) {
9438 if(opt->get_items_expected_min() > 0 && static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
9439 retval += static_cast<std::size_t>(opt->get_items_expected_min()) - opt->count();
9440 }
9441 }
9442 }
9443 return retval;
9444}
9445
9446CLI11_NODISCARD CLI11_INLINE bool App::_has_remaining_positionals() const {
9447 for(const Option_p &opt : options_) {
9448 if(opt->get_positional() && ((static_cast<int>(opt->count()) < opt->get_items_expected_min()))) {
9449 return true;
9450 }
9451 }
9452
9453 return false;
9454}
9455
9456CLI11_INLINE bool App::_parse_positional(std::vector<std::string> &args, bool haltOnSubcommand) {
9457
9458 const std::string &positional = args.back();
9459 Option *posOpt{nullptr};
9460
9461 if(positionals_at_end_) {
9462 // deal with the case of required arguments at the end which should take precedence over other arguments
9463 auto arg_rem = args.size();
9464 auto remreq = _count_remaining_positionals(true);
9465 if(arg_rem <= remreq) {
9466 for(const Option_p &opt : options_) {
9467 if(opt->get_positional() && opt->required_) {
9468 if(static_cast<int>(opt->count()) < opt->get_items_expected_min()) {
9469 if(validate_positionals_) {
9470 std::string pos = positional;
9471 pos = opt->_validate(pos, 0);
9472 if(!pos.empty()) {
9473 continue;
9474 }
9475 }
9476 posOpt = opt.get();
9477 break;
9478 }
9479 }
9480 }
9481 }
9482 }
9483 if(posOpt == nullptr) {
9484 for(const Option_p &opt : options_) {
9485 // Eat options, one by one, until done
9486 if(opt->get_positional() &&
9487 (static_cast<int>(opt->count()) < opt->get_items_expected_min() || opt->get_allow_extra_args())) {
9488 if(validate_positionals_) {
9489 std::string pos = positional;
9490 pos = opt->_validate(pos, 0);
9491 if(!pos.empty()) {
9492 continue;
9493 }
9494 }
9495 posOpt = opt.get();
9496 break;
9497 }
9498 }
9499 }
9500 if(posOpt != nullptr) {
9501 parse_order_.push_back(posOpt);
9502 if(posOpt->get_inject_separator()) {
9503 if(!posOpt->results().empty() && !posOpt->results().back().empty()) {
9504 posOpt->add_result(std::string{});
9505 }
9506 }
9507 if(posOpt->get_trigger_on_parse() && posOpt->current_option_state_ == Option::option_state::callback_run) {
9508 posOpt->clear();
9509 }
9510 posOpt->add_result(positional);
9511 if(posOpt->get_trigger_on_parse()) {
9512 posOpt->run_callback();
9513 }
9514
9515 args.pop_back();
9516 return true;
9517 }
9518
9519 for(auto &subc : subcommands_) {
9520 if((subc->name_.empty()) && (!subc->disabled_)) {
9521 if(subc->_parse_positional(args, false)) {
9522 if(!subc->pre_parse_called_) {
9523 subc->_trigger_pre_parse(args.size());
9524 }
9525 return true;
9526 }
9527 }
9528 }
9529 // let the parent deal with it if possible
9530 if(parent_ != nullptr && fallthrough_)
9531 return _get_fallthrough_parent()->_parse_positional(args, static_cast<bool>(parse_complete_callback_));
9532
9534 auto *com = _find_subcommand(args.back(), true, false);
9535 if(com != nullptr && (require_subcommand_max_ == 0 || require_subcommand_max_ > parsed_subcommands_.size())) {
9536 if(haltOnSubcommand) {
9537 return false;
9538 }
9539 args.pop_back();
9540 com->_parse(args);
9541 return true;
9542 }
9545 auto *parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
9546 com = parent_app->_find_subcommand(args.back(), true, false);
9547 if(com != nullptr && (com->parent_->require_subcommand_max_ == 0 ||
9548 com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size())) {
9549 return false;
9550 }
9551
9552 if(positionals_at_end_) {
9553 THROW CLI::ExtrasError(name_, args);
9554 }
9556 if(parent_ != nullptr && name_.empty()) {
9557 return false;
9558 }
9560 _move_to_missing(detail::Classifier::NONE, positional);
9561 args.pop_back();
9562 if(prefix_command_) {
9563 while(!args.empty()) {
9564 _move_to_missing(detail::Classifier::NONE, args.back());
9565 args.pop_back();
9566 }
9567 }
9568
9569 return true;
9570}
9571
9573App::_find_subcommand(const std::string &subc_name, bool ignore_disabled, bool ignore_used) const noexcept {
9574 for(const App_p &com : subcommands_) {
9575 if(com->disabled_ && ignore_disabled)
9576 continue;
9577 if(com->get_name().empty()) {
9578 auto *subc = com->_find_subcommand(subc_name, ignore_disabled, ignore_used);
9579 if(subc != nullptr) {
9580 return subc;
9581 }
9582 }
9583 if(com->check_name(subc_name)) {
9584 if((!*com) || !ignore_used)
9585 return com.get();
9586 }
9587 }
9588 return nullptr;
9589}
9590
9591CLI11_INLINE bool App::_parse_subcommand(std::vector<std::string> &args) {
9592 if(_count_remaining_positionals(/* required */ true) > 0) {
9593 _parse_positional(args, false);
9594 return true;
9595 }
9596 auto *com = _find_subcommand(args.back(), true, true);
9597 if(com == nullptr) {
9598 // the main way to get here is using .notation
9599 auto dotloc = args.back().find_first_of('.');
9600 if(dotloc != std::string::npos) {
9601 com = _find_subcommand(args.back().substr(0, dotloc), true, true);
9602 if(com != nullptr) {
9603 args.back() = args.back().substr(dotloc + 1);
9604 args.push_back(com->get_display_name());
9605 }
9606 }
9607 }
9608 if(com != nullptr) {
9609 args.pop_back();
9610 if(!com->silent_) {
9611 parsed_subcommands_.push_back(com);
9612 }
9613 com->_parse(args);
9614 auto *parent_app = com->parent_;
9615 while(parent_app != this) {
9616 parent_app->_trigger_pre_parse(args.size());
9617 if(!com->silent_) {
9618 parent_app->parsed_subcommands_.push_back(com);
9619 }
9620 parent_app = parent_app->parent_;
9621 }
9622 return true;
9623 }
9624
9625 if(parent_ == nullptr)
9626 THROW HorribleError("Subcommand " + args.back() + " missing");
9627 return false;
9628}
9629
9630CLI11_INLINE bool
9631App::_parse_arg(std::vector<std::string> &args, detail::Classifier current_type, bool local_processing_only) {
9632
9633 std::string current = args.back();
9634
9635 std::string arg_name;
9636 std::string value;
9637 std::string rest;
9638
9639 switch(current_type) {
9640 case detail::Classifier::LONG:
9641 if(!detail::split_long(current, arg_name, value))
9642 THROW HorribleError("Long parsed but missing (you should not see this):" + args.back());
9643 break;
9644 case detail::Classifier::SHORT:
9645 if(!detail::split_short(current, arg_name, rest))
9646 THROW HorribleError("Short parsed but missing! You should not see this");
9647 break;
9648 case detail::Classifier::WINDOWS_STYLE:
9649 if(!detail::split_windows_style(current, arg_name, value))
9650 THROW HorribleError("windows option parsed but missing! You should not see this");
9651 break;
9652 case detail::Classifier::SUBCOMMAND:
9653 case detail::Classifier::SUBCOMMAND_TERMINATOR:
9654 case detail::Classifier::POSITIONAL_MARK:
9655 case detail::Classifier::NONE:
9656 default:
9657 THROW HorribleError("parsing got called with invalid option! You should not see this");
9658 }
9659
9660 auto op_ptr = std::find_if(std::begin(options_), std::end(options_), [arg_name, current_type](const Option_p &opt) {
9661 if(current_type == detail::Classifier::LONG)
9662 return opt->check_lname(arg_name);
9663 if(current_type == detail::Classifier::SHORT)
9664 return opt->check_sname(arg_name);
9665 // this will only get called for detail::Classifier::WINDOWS_STYLE
9666 return opt->check_lname(arg_name) || opt->check_sname(arg_name);
9667 });
9668
9669 // Option not found
9670 if(op_ptr == std::end(options_)) {
9671 for(auto &subc : subcommands_) {
9672 if(subc->name_.empty() && !subc->disabled_) {
9673 if(subc->_parse_arg(args, current_type, local_processing_only)) {
9674 if(!subc->pre_parse_called_) {
9675 subc->_trigger_pre_parse(args.size());
9676 }
9677 return true;
9678 }
9679 }
9680 }
9681
9682 // don't capture missing if this is a nameless subcommand and nameless subcommands can't fallthrough
9683 if(parent_ != nullptr && name_.empty()) {
9684 return false;
9685 }
9686
9687 // now check for '.' notation of subcommands
9688 auto dotloc = arg_name.find_first_of('.', 1);
9689 if(dotloc != std::string::npos) {
9690 // using dot notation is equivalent to single argument subcommand
9691 auto *sub = _find_subcommand(arg_name.substr(0, dotloc), true, false);
9692 if(sub != nullptr) {
9693 auto v = args.back();
9694 args.pop_back();
9695 arg_name = arg_name.substr(dotloc + 1);
9696 if(arg_name.size() > 1) {
9697 args.push_back(std::string("--") + v.substr(dotloc + 3));
9698 current_type = detail::Classifier::LONG;
9699 } else {
9700 auto nval = v.substr(dotloc + 2);
9701 nval.front() = '-';
9702 if(nval.size() > 2) {
9703 // '=' not allowed in short form arguments
9704 args.push_back(nval.substr(3));
9705 nval.resize(2);
9706 }
9707 args.push_back(nval);
9708 current_type = detail::Classifier::SHORT;
9709 }
9710 auto val = sub->_parse_arg(args, current_type, true);
9711 if(val) {
9712 if(!sub->silent_) {
9713 parsed_subcommands_.push_back(sub);
9714 }
9715 // deal with preparsing
9716 increment_parsed();
9717 _trigger_pre_parse(args.size());
9718 // run the parse complete callback since the subcommand processing is now complete
9719 if(sub->parse_complete_callback_) {
9720 sub->_process_env();
9721 sub->_process_callbacks();
9722 sub->_process_help_flags();
9723 sub->_process_requirements();
9724 sub->run_callback(false, true);
9725 }
9726 return true;
9727 }
9728 args.pop_back();
9729 args.push_back(v);
9730 }
9731 }
9732 if(local_processing_only) {
9733 return false;
9734 }
9735 // If a subcommand, try the main command
9736 if(parent_ != nullptr && fallthrough_)
9737 return _get_fallthrough_parent()->_parse_arg(args, current_type, false);
9738
9739 // Otherwise, add to missing
9740 args.pop_back();
9741 _move_to_missing(current_type, current);
9742 return true;
9743 }
9744
9745 args.pop_back();
9746
9747 // Get a reference to the pointer to make syntax bearable
9748 Option_p &op = *op_ptr;
9750 if(op->get_inject_separator()) {
9751 if(!op->results().empty() && !op->results().back().empty()) {
9752 op->add_result(std::string{});
9753 }
9754 }
9755 if(op->get_trigger_on_parse() && op->current_option_state_ == Option::option_state::callback_run) {
9756 op->clear();
9757 }
9758 int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min());
9759 int max_num = op->get_items_expected_max();
9760 // check container like options to limit the argument size to a single type if the allow_extra_flags argument is
9761 // set. 16 is somewhat arbitrary (needs to be at least 4)
9762 if(max_num >= detail::expected_max_vector_size / 16 && !op->get_allow_extra_args()) {
9763 auto tmax = op->get_type_size_max();
9764 max_num = detail::checked_multiply(tmax, op->get_expected_min()) ? tmax : detail::expected_max_vector_size;
9765 }
9766 // Make sure we always eat the minimum for unlimited vectors
9767 int collected = 0; // total number of arguments collected
9768 int result_count = 0; // local variable for number of results in a single arg string
9769 // deal with purely flag like things
9770 if(max_num == 0) {
9771 auto res = op->get_flag_value(arg_name, value);
9772 op->add_result(res);
9773 parse_order_.push_back(op.get());
9774 } else if(!value.empty()) { // --this=value
9775 op->add_result(value, result_count);
9776 parse_order_.push_back(op.get());
9777 collected += result_count;
9778 // -Trest
9779 } else if(!rest.empty()) {
9780 op->add_result(rest, result_count);
9781 parse_order_.push_back(op.get());
9782 rest = "";
9783 collected += result_count;
9784 }
9785
9786 // gather the minimum number of arguments
9787 while(min_num > collected && !args.empty()) {
9788 std::string current_ = args.back();
9789 args.pop_back();
9790 op->add_result(current_, result_count);
9791 parse_order_.push_back(op.get());
9792 collected += result_count;
9793 }
9794
9795 if(min_num > collected) { // if we have run out of arguments and the minimum was not met
9796 THROW ArgumentMismatch::TypedAtLeast(op->get_name(), min_num, op->get_type_name());
9797 }
9798
9799 // now check for optional arguments
9800 if(max_num > collected || op->get_allow_extra_args()) { // we allow optional arguments
9801 auto remreqpos = _count_remaining_positionals(true);
9802 // we have met the minimum now optionally check up to the maximum
9803 while((collected < max_num || op->get_allow_extra_args()) && !args.empty() &&
9804 _recognize(args.back(), false) == detail::Classifier::NONE) {
9805 // If any required positionals remain, don't keep eating
9806 if(remreqpos >= args.size()) {
9807 break;
9808 }
9809 if(validate_optional_arguments_) {
9810 std::string arg = args.back();
9811 arg = op->_validate(arg, 0);
9812 if(!arg.empty()) {
9813 break;
9814 }
9815 }
9816 op->add_result(args.back(), result_count);
9817 parse_order_.push_back(op.get());
9818 args.pop_back();
9819 collected += result_count;
9820 }
9821
9822 // Allow -- to end an unlimited list and "eat" it
9823 if(!args.empty() && _recognize(args.back()) == detail::Classifier::POSITIONAL_MARK)
9824 args.pop_back();
9825 // optional flag that didn't receive anything now get the default value
9826 if(min_num == 0 && max_num > 0 && collected == 0) {
9827 auto res = op->get_flag_value(arg_name, std::string{});
9828 op->add_result(res);
9829 parse_order_.push_back(op.get());
9830 }
9831 }
9832 // if we only partially completed a type then add an empty string if allowed for later processing
9833 if(min_num > 0 && (collected % op->get_type_size_max()) != 0) {
9834 if(op->get_type_size_max() != op->get_type_size_min()) {
9835 op->add_result(std::string{});
9836 } else {
9837 THROW ArgumentMismatch::PartialType(op->get_name(), op->get_type_size_min(), op->get_type_name());
9838 }
9839 }
9840 if(op->get_trigger_on_parse()) {
9841 op->run_callback();
9842 }
9843 if(!rest.empty()) {
9844 rest = "-" + rest;
9845 args.push_back(rest);
9846 }
9847 return true;
9848}
9849
9850CLI11_INLINE void App::_trigger_pre_parse(std::size_t remaining_args) {
9851 if(!pre_parse_called_) {
9852 pre_parse_called_ = true;
9853 if(pre_parse_callback_) {
9854 pre_parse_callback_(remaining_args);
9855 }
9856 } else if(immediate_callback_) {
9857 if(!name_.empty()) {
9858 auto pcnt = parsed_;
9859 missing_t extras = std::move(missing_);
9860 clear();
9861 parsed_ = pcnt;
9862 pre_parse_called_ = true;
9863 missing_ = std::move(extras);
9864 }
9865 }
9866}
9867
9868CLI11_INLINE App *App::_get_fallthrough_parent() {
9869 if(parent_ == nullptr) {
9870 THROW(HorribleError("No Valid parent"));
9871 }
9872 auto *fallthrough_parent = parent_;
9873 while((fallthrough_parent->parent_ != nullptr) && (fallthrough_parent->get_name().empty())) {
9874 fallthrough_parent = fallthrough_parent->parent_;
9875 }
9876 return fallthrough_parent;
9877}
9878
9879CLI11_NODISCARD CLI11_INLINE const std::string &App::_compare_subcommand_names(const App &subcom,
9880 const App &base) const {
9881 static const std::string estring;
9882 if(subcom.disabled_) {
9883 return estring;
9884 }
9885 for(const auto &subc : base.subcommands_) {
9886 if(subc.get() != &subcom) {
9887 if(subc->disabled_) {
9888 continue;
9889 }
9890 if(!subcom.get_name().empty()) {
9891 if(subc->check_name(subcom.get_name())) {
9892 return subcom.get_name();
9893 }
9894 }
9895 if(!subc->get_name().empty()) {
9896 if(subcom.check_name(subc->get_name())) {
9897 return subc->get_name();
9898 }
9899 }
9900 for(const auto &les : subcom.aliases_) {
9901 if(subc->check_name(les)) {
9902 return les;
9903 }
9904 }
9905 // this loop is needed in case of ignore_underscore or ignore_case on one but not the other
9906 for(const auto &les : subc->aliases_) {
9907 if(subcom.check_name(les)) {
9908 return les;
9909 }
9910 }
9911 // if the subcommand is an option group we need to check deeper
9912 if(subc->get_name().empty()) {
9913 const auto &cmpres = _compare_subcommand_names(subcom, *subc);
9914 if(!cmpres.empty()) {
9915 return cmpres;
9916 }
9917 }
9918 // if the test subcommand is an option group we need to check deeper
9919 if(subcom.get_name().empty()) {
9920 const auto &cmpres = _compare_subcommand_names(*subc, subcom);
9921 if(!cmpres.empty()) {
9922 return cmpres;
9923 }
9924 }
9925 }
9926 }
9927 return estring;
9928}
9929
9930CLI11_INLINE void App::_move_to_missing(detail::Classifier val_type, const std::string &val) {
9931 if(allow_extras_ || subcommands_.empty()) {
9932 missing_.emplace_back(val_type, val);
9933 return;
9934 }
9935 // allow extra arguments to be places in an option group if it is allowed there
9936 for(auto &subc : subcommands_) {
9937 if(subc->name_.empty() && subc->allow_extras_) {
9938 subc->missing_.emplace_back(val_type, val);
9939 return;
9940 }
9941 }
9942 // if we haven't found any place to put them yet put them in missing
9943 missing_.emplace_back(val_type, val);
9944}
9945
9946CLI11_INLINE void App::_move_option(Option *opt, App *app) {
9947 if(opt == nullptr) {
9948 THROW OptionNotFound("the option is NULL");
9949 }
9950 // verify that the give app is actually a subcommand
9951 bool found = false;
9952 for(auto &subc : subcommands_) {
9953 if(app == subc.get()) {
9954 found = true;
9955 }
9956 }
9957 if(!found) {
9958 THROW OptionNotFound("The Given app is not a subcommand");
9959 }
9960
9961 if((help_ptr_ == opt) || (help_all_ptr_ == opt))
9962 THROW OptionAlreadyAdded("cannot move help options");
9963
9964 if(config_ptr_ == opt)
9965 THROW OptionAlreadyAdded("cannot move config file options");
9966
9967 auto iterator =
9968 std::find_if(std::begin(options_), std::end(options_), [opt](const Option_p &v) { return v.get() == opt; });
9969 if(iterator != std::end(options_)) {
9970 const auto &opt_p = *iterator;
9971 if(std::find_if(std::begin(app->options_), std::end(app->options_), [&opt_p](const Option_p &v) {
9972 return (*v == *opt_p);
9973 }) == std::end(app->options_)) {
9974 // only erase after the insertion was successful
9975 app->options_.push_back(std::move(*iterator));
9976 options_.erase(iterator);
9977 } else {
9978 THROW OptionAlreadyAdded("option was not located: " + opt->get_name());
9979 }
9980 } else {
9981 THROW OptionNotFound("could not locate the given Option");
9982 }
9983}
9984
9985CLI11_INLINE void TriggerOn(App *trigger_app, App *app_to_enable) {
9986 app_to_enable->enabled_by_default(false);
9987 app_to_enable->disabled_by_default();
9988 trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(false); });
9989}
9990
9991CLI11_INLINE void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
9992 for(auto &app : apps_to_enable) {
9993 app->enabled_by_default(false);
9994 app->disabled_by_default();
9995 }
9996
9997 trigger_app->preparse_callback([apps_to_enable](std::size_t) {
9998 for(const auto &app : apps_to_enable) {
9999 app->disabled(false);
10000 }
10001 });
10002}
10003
10004CLI11_INLINE void TriggerOff(App *trigger_app, App *app_to_enable) {
10005 app_to_enable->disabled_by_default(false);
10006 app_to_enable->enabled_by_default();
10007 trigger_app->preparse_callback([app_to_enable](std::size_t) { app_to_enable->disabled(); });
10008}
10009
10010CLI11_INLINE void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable) {
10011 for(auto &app : apps_to_enable) {
10012 app->disabled_by_default(false);
10013 app->enabled_by_default();
10014 }
10015
10016 trigger_app->preparse_callback([apps_to_enable](std::size_t) {
10017 for(const auto &app : apps_to_enable) {
10018 app->disabled();
10019 }
10020 });
10021}
10022
10023CLI11_INLINE void deprecate_option(Option *opt, const std::string &replacement) {
10024 Validator deprecate_warning{[opt, replacement](std::string &) {
10025 std::cout << opt->get_name() << " is deprecated please use '" << replacement
10026 << "' instead\n";
10027 return std::string();
10028 },
10029 "DEPRECATED"};
10030 deprecate_warning.application_index(0);
10031 opt->check(deprecate_warning);
10032 if(!replacement.empty()) {
10033 opt->description(opt->get_description() + " DEPRECATED: please use '" + replacement + "' instead");
10034 }
10035}
10036
10037CLI11_INLINE void retire_option(App *app, Option *opt) {
10038 App temp;
10039 auto *option_copy = temp.add_option(opt->get_name(false, true))
10040 ->type_size(opt->get_type_size_min(), opt->get_type_size_max())
10041 ->expected(opt->get_expected_min(), opt->get_expected_max())
10042 ->allow_extra_args(opt->get_allow_extra_args());
10043
10044 app->remove_option(opt);
10045 auto *opt2 = app->add_option(option_copy->get_name(false, true), "option has been retired and has no effect");
10046 opt2->type_name("RETIRED")
10047 ->default_str("RETIRED")
10048 ->type_size(option_copy->get_type_size_min(), option_copy->get_type_size_max())
10049 ->expected(option_copy->get_expected_min(), option_copy->get_expected_max())
10050 ->allow_extra_args(option_copy->get_allow_extra_args());
10051
10052 Validator retired_warning{[opt2](std::string &) {
10053 std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
10054 return std::string();
10055 },
10056 ""};
10057 retired_warning.application_index(0);
10058 opt2->check(retired_warning);
10059}
10060
10061CLI11_INLINE void retire_option(App &app, Option *opt) { retire_option(&app, opt); }
10062
10063CLI11_INLINE void retire_option(App *app, const std::string &option_name) {
10064
10065 auto *opt = app->get_option_no_THROW(option_name);
10066 if(opt != nullptr) {
10067 retire_option(app, opt);
10068 return;
10069 }
10070 auto *opt2 = app->add_option(option_name, "option has been retired and has no effect")
10071 ->type_name("RETIRED")
10072 ->expected(0, 1)
10073 ->default_str("RETIRED");
10074 Validator retired_warning{[opt2](std::string &) {
10075 std::cout << "WARNING " << opt2->get_name() << " is retired and has no effect\n";
10076 return std::string();
10077 },
10078 ""};
10079 retired_warning.application_index(0);
10080 opt2->check(retired_warning);
10081}
10082
10083CLI11_INLINE void retire_option(App &app, const std::string &option_name) { retire_option(&app, option_name); }
10084
10085namespace FailureMessage {
10086
10087CLI11_INLINE std::string simple(const App *app, const Error &e) {
10088 std::string header = std::string(e.what()) + "\n";
10089 std::vector<std::string> names;
10090
10091 // Collect names
10092 if(app->get_help_ptr() != nullptr)
10093 names.push_back(app->get_help_ptr()->get_name());
10094
10095 if(app->get_help_all_ptr() != nullptr)
10096 names.push_back(app->get_help_all_ptr()->get_name());
10097
10098 // If any names found, suggest those
10099 if(!names.empty())
10100 header += "Run with " + detail::join(names, " or ") + " for more information.\n";
10101
10102 return header;
10103}
10104
10105CLI11_INLINE std::string help(const App *app, const Error &e) {
10106 std::string header = std::string("ERROR: ") + e.get_name() + ": " + e.what() + "\n";
10107 header += app->help();
10108 return header;
10109}
10110
10111} // namespace FailureMessage
10112
10113
10114
10115
10116namespace detail {
10117
10118std::string convert_arg_for_ini(const std::string &arg,
10119 char stringQuote = '"',
10120 char literalQuote = '\'',
10121 bool disable_multi_line = false);
10122
10124std::string ini_join(const std::vector<std::string> &args,
10125 char sepChar = ',',
10126 char arrayStart = '[',
10127 char arrayEnd = ']',
10128 char stringQuote = '"',
10129 char literalQuote = '\'');
10130
10131void clean_name_string(std::string &name, const std::string &keyChars);
10132
10133std::vector<std::string> generate_parents(const std::string &section, std::string &name, char parentSeparator);
10134
10136void checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator);
10137} // namespace detail
10138
10139
10140
10141
10142static constexpr auto multiline_literal_quote = R"(''')";
10143static constexpr auto multiline_string_quote = R"(""")";
10144
10145namespace detail {
10146
10147CLI11_INLINE bool is_printable(const std::string &test_string) {
10148 return std::all_of(test_string.begin(), test_string.end(), [](char x) {
10149 return (isprint(static_cast<unsigned char>(x)) != 0 || x == '\n' || x == '\t');
10150 });
10151}
10152
10153CLI11_INLINE std::string
10154convert_arg_for_ini(const std::string &arg, char stringQuote, char literalQuote, bool disable_multi_line) {
10155 if(arg.empty()) {
10156 return std::string(2, stringQuote);
10157 }
10158 // some specifically supported strings
10159 if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
10160 return arg;
10161 }
10162 // floating point conversion can convert some hex codes, but don't try that here
10163 if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
10164 using CLI::detail::lexical_cast;
10165 double val = 0.0;
10166 if(lexical_cast(arg, val)) {
10167 if(arg.find_first_not_of("0123456789.-+eE") == std::string::npos) {
10168 return arg;
10169 }
10170 }
10171 }
10172 // just quote a single non numeric character
10173 if(arg.size() == 1) {
10174 if(isprint(static_cast<unsigned char>(arg.front())) == 0) {
10175 return binary_escape_string(arg);
10176 }
10177 if(arg == "'") {
10178 return std::string(1, stringQuote) + "'" + stringQuote;
10179 }
10180 return std::string(1, literalQuote) + arg + literalQuote;
10181 }
10182 // handle hex, binary or octal arguments
10183 if(arg.front() == '0') {
10184 if(arg[1] == 'x') {
10185 if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
10186 return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
10187 })) {
10188 return arg;
10189 }
10190 } else if(arg[1] == 'o') {
10191 if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
10192 return arg;
10193 }
10194 } else if(arg[1] == 'b') {
10195 if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
10196 return arg;
10197 }
10198 }
10199 }
10200 if(!is_printable(arg)) {
10201 return binary_escape_string(arg);
10202 }
10203 if(detail::has_escapable_character(arg)) {
10204 if(arg.size() > 100 && !disable_multi_line) {
10205 return std::string(multiline_literal_quote) + arg + multiline_literal_quote;
10206 }
10207 return std::string(1, stringQuote) + detail::add_escaped_characters(arg) + stringQuote;
10208 }
10209 return std::string(1, stringQuote) + arg + stringQuote;
10210}
10211
10212CLI11_INLINE std::string ini_join(const std::vector<std::string> &args,
10213 char sepChar,
10214 char arrayStart,
10215 char arrayEnd,
10216 char stringQuote,
10217 char literalQuote) {
10218 bool disable_multi_line{false};
10219 std::string joined;
10220 if(args.size() > 1 && arrayStart != '\0') {
10221 joined.push_back(arrayStart);
10222 disable_multi_line = true;
10223 }
10224 std::size_t start = 0;
10225 for(const auto &arg : args) {
10226 if(start++ > 0) {
10227 joined.push_back(sepChar);
10228 if(!std::isspace<char>(sepChar, std::locale())) {
10229 joined.push_back(' ');
10230 }
10231 }
10232 joined.append(convert_arg_for_ini(arg, stringQuote, literalQuote, disable_multi_line));
10233 }
10234 if(args.size() > 1 && arrayEnd != '\0') {
10235 joined.push_back(arrayEnd);
10236 }
10237 return joined;
10238}
10239
10240CLI11_INLINE std::vector<std::string>
10241generate_parents(const std::string &section, std::string &name, char parentSeparator) {
10242 std::vector<std::string> parents;
10243 if(detail::to_lower(section) != "default") {
10244 if(section.find(parentSeparator) != std::string::npos) {
10245 parents = detail::split_up(section, parentSeparator);
10246 } else {
10247 parents = {section};
10248 }
10249 }
10250 if(name.find(parentSeparator) != std::string::npos) {
10251 std::vector<std::string> plist = detail::split_up(name, parentSeparator);
10252 name = plist.back();
10253 plist.pop_back();
10254 parents.insert(parents.end(), plist.begin(), plist.end());
10255 }
10256 // clean up quotes on the parents
10257 try {
10258 detail::remove_quotes(parents);
10259 } catch(const std::invalid_argument &iarg) {
10260#ifndef BB_NO_EXCEPTIONS
10261 THROW CLI::ParseError(iarg.what(), CLI::ExitCodes::InvalidError);
10262#endif
10263 }
10264 return parents;
10265}
10266
10267CLI11_INLINE void
10268checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
10269
10270 std::string estring;
10271 auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
10272 if(!output.empty() && output.back().name == "--") {
10273 std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
10274 while(output.back().parents.size() >= msize) {
10275 output.push_back(output.back());
10276 output.back().parents.pop_back();
10277 }
10278
10279 if(parents.size() > 1) {
10280 std::size_t common = 0;
10281 std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
10282 for(std::size_t ii = 0; ii < mpair; ++ii) {
10283 if(output.back().parents[ii] != parents[ii]) {
10284 break;
10285 }
10286 ++common;
10287 }
10288 if(common == mpair) {
10289 output.pop_back();
10290 } else {
10291 while(output.back().parents.size() > common + 1) {
10292 output.push_back(output.back());
10293 output.back().parents.pop_back();
10294 }
10295 }
10296 for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
10297 output.emplace_back();
10298 output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
10299 output.back().name = "++";
10300 }
10301 }
10302 } else if(parents.size() > 1) {
10303 for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
10304 output.emplace_back();
10305 output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
10306 output.back().name = "++";
10307 }
10308 }
10309
10310 // insert a section end which is just an empty items_buffer
10311 output.emplace_back();
10312 output.back().parents = std::move(parents);
10313 output.back().name = "++";
10314}
10315
10317CLI11_INLINE bool hasMLString(std::string const &fullString, char check) {
10318 if(fullString.length() < 3) {
10319 return false;
10320 }
10321 auto it = fullString.rbegin();
10322 return (*it == check) && (*(it + 1) == check) && (*(it + 2) == check);
10323}
10324} // namespace detail
10325
10326inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
10327 std::string line;
10328 std::string buffer;
10329 std::string currentSection = "default";
10330 std::string previousSection = "default";
10332 bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
10333 bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
10334 bool inSection{false};
10335 bool inMLineComment{false};
10336 bool inMLineValue{false};
10337
10338 char aStart = (isINIArray) ? '[' : arrayStart;
10339 char aEnd = (isINIArray) ? ']' : arrayEnd;
10340 char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
10341 int currentSectionIndex{0};
10342
10343 std::string line_sep_chars{parentSeparatorChar, commentChar, valueDelimiter};
10344 while(getline(input, buffer)) {
10345 std::vector<std::string> items_buffer;
10346 std::string name;
10347 line = detail::trim_copy(buffer);
10348 std::size_t len = line.length();
10349 // lines have to be at least 3 characters to have any meaning to CLI just skip the rest
10350 if(len < 3) {
10351 continue;
10352 }
10353 if(line.compare(0, 3, multiline_string_quote) == 0 || line.compare(0, 3, multiline_literal_quote) == 0) {
10354 inMLineComment = true;
10355 auto cchar = line.front();
10356 while(inMLineComment) {
10357 if(getline(input, line)) {
10358 detail::trim(line);
10359 } else {
10360 break;
10361 }
10362 if(detail::hasMLString(line, cchar)) {
10363 inMLineComment = false;
10364 }
10365 }
10366 continue;
10367 }
10368 if(line.front() == '[' && line.back() == ']') {
10369 if(currentSection != "default") {
10370 // insert a section end which is just an empty items_buffer
10371 output.emplace_back();
10372 output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
10373 output.back().name = "--";
10374 }
10375 currentSection = line.substr(1, len - 2);
10376 // deal with double brackets for TOML
10377 if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
10378 currentSection = currentSection.substr(1, currentSection.size() - 2);
10379 }
10380 if(detail::to_lower(currentSection) == "default") {
10381 currentSection = "default";
10382 } else {
10383 detail::checkParentSegments(output, currentSection, parentSeparatorChar);
10384 }
10385 inSection = false;
10386 if(currentSection == previousSection) {
10387 ++currentSectionIndex;
10388 } else {
10389 currentSectionIndex = 0;
10390 previousSection = currentSection;
10391 }
10392 continue;
10393 }
10394
10395 // comment lines
10396 if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
10397 continue;
10398 }
10399 std::size_t search_start = 0;
10400 if(line.find_first_of("\"'`") != std::string::npos) {
10401 while(search_start < line.size()) {
10402 auto test_char = line[search_start];
10403 if(test_char == '\"' || test_char == '\'' || test_char == '`') {
10404 search_start = detail::close_sequence(line, search_start, line[search_start]);
10405 ++search_start;
10406 } else if(test_char == valueDelimiter || test_char == commentChar) {
10407 --search_start;
10408 break;
10409 } else if(test_char == ' ' || test_char == '\t' || test_char == parentSeparatorChar) {
10410 ++search_start;
10411 } else {
10412 search_start = line.find_first_of(line_sep_chars, search_start);
10413 }
10414 }
10415 }
10416 // Find = in string, split and recombine
10417 auto delimiter_pos = line.find_first_of(valueDelimiter, search_start + 1);
10418 auto comment_pos = line.find_first_of(commentChar, search_start);
10419 if(comment_pos < delimiter_pos) {
10420 delimiter_pos = std::string::npos;
10421 }
10422 if(delimiter_pos != std::string::npos) {
10423
10424 name = detail::trim_copy(line.substr(0, delimiter_pos));
10425 std::string item = detail::trim_copy(line.substr(delimiter_pos + 1, std::string::npos));
10426 bool mlquote =
10427 (item.compare(0, 3, multiline_literal_quote) == 0 || item.compare(0, 3, multiline_string_quote) == 0);
10428 if(!mlquote && comment_pos != std::string::npos) {
10429 auto citems = detail::split_up(item, commentChar);
10430 item = detail::trim_copy(citems.front());
10431 }
10432 if(mlquote) {
10433 // mutliline string
10434 auto keyChar = item.front();
10435 item = buffer.substr(delimiter_pos + 1, std::string::npos);
10436 detail::ltrim(item);
10437 item.erase(0, 3);
10438 inMLineValue = true;
10439 bool lineExtension{false};
10440 bool firstLine = true;
10441 if(!item.empty() && item.back() == '\\') {
10442 item.pop_back();
10443 lineExtension = true;
10444 }
10445 while(inMLineValue) {
10446 std::string l2;
10447 if(!std::getline(input, l2)) {
10448 break;
10449 }
10450 line = l2;
10451 detail::rtrim(line);
10452 if(detail::hasMLString(line, keyChar)) {
10453 line.pop_back();
10454 line.pop_back();
10455 line.pop_back();
10456 if(lineExtension) {
10457 detail::ltrim(line);
10458 } else if(!(firstLine && item.empty())) {
10459 item.push_back('\n');
10460 }
10461 firstLine = false;
10462 item += line;
10463 inMLineValue = false;
10464 if(!item.empty() && item.back() == '\n') {
10465 item.pop_back();
10466 }
10467 if(keyChar == '\"') {
10468 try {
10469 item = detail::remove_escaped_characters(item);
10470 } catch(const std::invalid_argument &iarg) {
10471#ifndef BB_NO_EXCEPTIONS
10472 THROW CLI::ParseError(iarg.what(), CLI::ExitCodes::InvalidError);
10473#endif
10474 }
10475 }
10476 } else {
10477 if(lineExtension) {
10478 detail::trim(l2);
10479 } else if(!(firstLine && item.empty())) {
10480 item.push_back('\n');
10481 }
10482 lineExtension = false;
10483 firstLine = false;
10484 if(!l2.empty() && l2.back() == '\\') {
10485 lineExtension = true;
10486 l2.pop_back();
10487 }
10488 item += l2;
10489 }
10490 }
10491 items_buffer = {item};
10492 } else if(item.size() > 1 && item.front() == aStart) {
10493 for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
10494 detail::trim(multiline);
10495 item += multiline;
10496 }
10497 if(item.back() == aEnd) {
10498 items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
10499 } else {
10500 items_buffer = detail::split_up(item.substr(1, std::string::npos), aSep);
10501 }
10502 } else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
10503 items_buffer = detail::split_up(item, aSep);
10504 } else if((isDefaultArray || isINIArray) && item.find_first_of(' ') != std::string::npos) {
10505 items_buffer = detail::split_up(item, '\0');
10506 } else {
10507 items_buffer = {item};
10508 }
10509 } else {
10510 name = detail::trim_copy(line.substr(0, comment_pos));
10511 items_buffer = {"true"};
10512 }
10513 std::vector<std::string> parents;
10514 try {
10515 parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
10516 detail::process_quoted_string(name);
10517 // clean up quotes on the items and check for escaped strings
10518 for(auto &it : items_buffer) {
10519 detail::process_quoted_string(it, stringQuote, literalQuote);
10520 }
10521 } catch(const std::invalid_argument &ia) {
10522#ifndef BB_NO_EXCEPTIONS
10523 THROW CLI::ParseError(ia.what(), CLI::ExitCodes::InvalidError);
10524#endif
10525 }
10526
10527 if(parents.size() > maximumLayers) {
10528 continue;
10529 }
10530 if(!configSection.empty() && !inSection) {
10531 if(parents.empty() || parents.front() != configSection) {
10532 continue;
10533 }
10534 if(configIndex >= 0 && currentSectionIndex != configIndex) {
10535 continue;
10536 }
10537 parents.erase(parents.begin());
10538 inSection = true;
10539 }
10540 if(!output.empty() && name == output.back().name && parents == output.back().parents) {
10541 output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
10542 } else {
10543 output.emplace_back();
10544 output.back().parents = std::move(parents);
10545 output.back().name = std::move(name);
10546 output.back().inputs = std::move(items_buffer);
10547 }
10548 }
10549 if(currentSection != "default") {
10550 // insert a section end which is just an empty items_buffer
10551 std::string ename;
10552 output.emplace_back();
10553 output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
10554 output.back().name = "--";
10555 while(output.back().parents.size() > 1) {
10556 output.push_back(output.back());
10557 output.back().parents.pop_back();
10558 }
10559 }
10560 return output;
10561}
10562
10563CLI11_INLINE std::string &clean_name_string(std::string &name, const std::string &keyChars) {
10564 if(name.find_first_of(keyChars) != std::string::npos || (name.front() == '[' && name.back() == ']') ||
10565 (name.find_first_of("'`\"\\") != std::string::npos)) {
10566 if(name.find_first_of('\'') == std::string::npos) {
10567 name.insert(0, 1, '\'');
10568 name.push_back('\'');
10569 } else {
10570 if(detail::has_escapable_character(name)) {
10571 name = detail::add_escaped_characters(name);
10572 }
10573 name.insert(0, 1, '\"');
10574 name.push_back('\"');
10575 }
10576 }
10577 return name;
10578}
10579
10580CLI11_INLINE std::string
10581ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
10582 std::stringstream out;
10583 std::string commentLead;
10584 commentLead.push_back(commentChar);
10585 commentLead.push_back(' ');
10586
10587 std::string commentTest = "#;";
10588 commentTest.push_back(commentChar);
10589 commentTest.push_back(parentSeparatorChar);
10590
10591 std::string keyChars = commentTest;
10592 keyChars.push_back(literalQuote);
10593 keyChars.push_back(stringQuote);
10594 keyChars.push_back(arrayStart);
10595 keyChars.push_back(arrayEnd);
10596 keyChars.push_back(valueDelimiter);
10597 keyChars.push_back(arraySeparator);
10598
10599 std::vector<std::string> groups = app->get_groups();
10600 bool defaultUsed = false;
10601 groups.insert(groups.begin(), std::string("Options"));
10602 if(write_description && (app->get_configurable() || app->get_parent() == nullptr || app->get_name().empty())) {
10603 out << commentLead << detail::fix_newlines(commentLead, app->get_description()) << '\n';
10604 }
10605 for(auto &group : groups) {
10606 if(group == "Options" || group.empty()) {
10607 if(defaultUsed) {
10608 continue;
10609 }
10610 defaultUsed = true;
10611 }
10612 if(write_description && group != "Options" && !group.empty()) {
10613 out << '\n' << commentLead << group << " Options\n";
10614 }
10615 for(const Option *opt : app->get_options({})) {
10616
10617 // Only process options that are configurable
10618 if(opt->get_configurable()) {
10619 if(opt->get_group() != group) {
10620 if(!(group == "Options" && opt->get_group().empty())) {
10621 continue;
10622 }
10623 }
10624 std::string single_name = opt->get_single_name();
10625 if(single_name.empty()) {
10626 continue;
10627 }
10628
10629 std::string value = detail::ini_join(
10630 opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
10631
10632 if(value.empty() && default_also) {
10633 if(!opt->get_default_str().empty()) {
10634 value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, literalQuote, false);
10635 } else if(opt->get_expected_min() == 0) {
10636 value = "false";
10637 } else if(opt->get_run_callback_for_default()) {
10638 value = "\"\""; // empty string default value
10639 }
10640 }
10641
10642 if(!value.empty()) {
10643
10644 if(!opt->get_fnames().empty()) {
10645 try {
10646 value = opt->get_flag_value(single_name, value);
10647 } catch(const CLI::ArgumentMismatch &) {
10648 bool valid{false};
10649 for(const auto &test_name : opt->get_fnames()) {
10650 try {
10651 value = opt->get_flag_value(test_name, value);
10652 single_name = test_name;
10653 valid = true;
10654 } catch(const CLI::ArgumentMismatch &) {
10655 continue;
10656 }
10657 }
10658 if(!valid) {
10659 value = detail::ini_join(
10660 opt->results(), arraySeparator, arrayStart, arrayEnd, stringQuote, literalQuote);
10661 }
10662 }
10663 }
10664 if(write_description && opt->has_description()) {
10665 out << '\n';
10666 out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
10667 }
10668 clean_name_string(single_name, keyChars);
10669
10670 std::string name = prefix + single_name;
10671
10672 out << name << valueDelimiter << value << '\n';
10673 }
10674 }
10675 }
10676 }
10677 auto subcommands = app->get_subcommands({});
10678 for(const App *subcom : subcommands) {
10679 if(subcom->get_name().empty()) {
10680 if(!default_also && (subcom->count_all() == 0)) {
10681 continue;
10682 }
10683 if(write_description && !subcom->get_group().empty()) {
10684 out << '\n' << commentLead << subcom->get_group() << " Options\n";
10685 }
10686 /*if (!prefix.empty() || app->get_parent() == nullptr) {
10687 out << '[' << prefix << "___"<< subcom->get_group() << "]\n";
10688 } else {
10689 std::string subname = app->get_name() + parentSeparatorChar + "___"+subcom->get_group();
10690 const auto *p = app->get_parent();
10691 while(p->get_parent() != nullptr) {
10692 subname = p->get_name() + parentSeparatorChar +subname;
10693 p = p->get_parent();
10694 }
10695 out << '[' << subname << "]\n";
10696 }
10697 */
10698 out << to_config(subcom, default_also, write_description, prefix);
10699 }
10700 }
10701
10702 for(const App *subcom : subcommands) {
10703 if(!subcom->get_name().empty()) {
10704 if(!default_also && (subcom->count_all() == 0)) {
10705 continue;
10706 }
10707 std::string subname = subcom->get_name();
10708 clean_name_string(subname, keyChars);
10709
10710 if(subcom->get_configurable() && app->got_subcommand(subcom)) {
10711 if(!prefix.empty() || app->get_parent() == nullptr) {
10712
10713 out << '[' << prefix << subname << "]\n";
10714 } else {
10715 std::string appname = app->get_name();
10716 clean_name_string(appname, keyChars);
10717 subname = appname + parentSeparatorChar + subname;
10718 const auto *p = app->get_parent();
10719 while(p->get_parent() != nullptr) {
10720 std::string pname = p->get_name();
10721 clean_name_string(pname, keyChars);
10722 subname = pname + parentSeparatorChar + subname;
10723 p = p->get_parent();
10724 }
10725 out << '[' << subname << "]\n";
10726 }
10727 out << to_config(subcom, default_also, write_description, "");
10728 } else {
10729 out << to_config(subcom, default_also, write_description, prefix + subname + parentSeparatorChar);
10730 }
10731 }
10732 }
10733
10734 return out.str();
10735}
10736
10737
10738
10739
10740
10741
10742CLI11_INLINE std::string
10743Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
10744 std::stringstream out;
10745
10746 out << "\n" << group << ":\n";
10747 for(const Option *opt : opts) {
10748 out << make_option(opt, is_positional);
10749 }
10750
10751 return out.str();
10752}
10753
10754CLI11_INLINE std::string Formatter::make_positionals(const App *app) const {
10756 app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
10757
10758 if(opts.empty())
10759 return {};
10760
10761 return make_group(get_label("Positionals"), true, opts);
10762}
10763
10764CLI11_INLINE std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
10765 std::stringstream out;
10766 std::vector<std::string> groups = app->get_groups();
10767
10768 // Options
10769 for(const std::string &group : groups) {
10770 std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) {
10771 return opt->get_group() == group // Must be in the right group
10772 && opt->nonpositional() // Must not be a positional
10773 && (mode != AppFormatMode::Sub // If mode is Sub, then
10774 || (app->get_help_ptr() != opt // Ignore help pointer
10775 && app->get_help_all_ptr() != opt)); // Ignore help all pointer
10776 });
10777 if(!group.empty() && !opts.empty()) {
10778 out << make_group(group, false, opts);
10779
10780 if(group != groups.back())
10781 out << "\n";
10782 }
10783 }
10784
10785 return out.str();
10786}
10787
10788CLI11_INLINE std::string Formatter::make_description(const App *app) const {
10789 std::string desc = app->get_description();
10790 auto min_options = app->get_require_option_min();
10791 auto max_options = app->get_require_option_max();
10792 if(app->get_required()) {
10793 desc += " " + get_label("REQUIRED") + " ";
10794 }
10795 if((max_options == min_options) && (min_options > 0)) {
10796 if(min_options == 1) {
10797 desc += " \n[Exactly 1 of the following options is required]";
10798 } else {
10799 desc += " \n[Exactly " + std::to_string(min_options) + " options from the following list are required]";
10800 }
10801 } else if(max_options > 0) {
10802 if(min_options > 0) {
10803 desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
10804 " of the follow options are required]";
10805 } else {
10806 desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
10807 }
10808 } else if(min_options > 0) {
10809 desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
10810 }
10811 return (!desc.empty()) ? desc + "\n" : std::string{};
10812}
10813
10814CLI11_INLINE std::string Formatter::make_usage(const App *app, std::string name) const {
10815 std::string usage = app->get_usage();
10816 if(!usage.empty()) {
10817 return usage + "\n";
10818 }
10819
10820 std::stringstream out;
10821
10822 out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
10823
10824 std::vector<std::string> groups = app->get_groups();
10825
10826 // Print an Options badge if any options exist
10827 std::vector<const Option *> non_pos_options =
10828 app->get_options([](const Option *opt) { return opt->nonpositional(); });
10829 if(!non_pos_options.empty())
10830 out << " [" << get_label("OPTIONS") << "]";
10831
10832 // Positionals need to be listed here
10833 std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
10834
10835 // Print out positionals if any are left
10836 if(!positionals.empty()) {
10837 // Convert to help names
10838 std::vector<std::string> positional_names(positionals.size());
10839 std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
10840 return make_option_usage(opt);
10841 });
10842
10843 out << " " << detail::join(positional_names, " ");
10844 }
10845
10846 // Add a marker if subcommands are expected or optional
10847 if(!app->get_subcommands(
10848 [](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); })
10849 .empty()) {
10850 out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
10851 << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
10852 : "SUBCOMMANDS")
10853 << (app->get_require_subcommand_min() == 0 ? "]" : "");
10854 }
10855
10856 out << '\n';
10857
10858 return out.str();
10859}
10860
10861CLI11_INLINE std::string Formatter::make_footer(const App *app) const {
10862 std::string footer = app->get_footer();
10863 if(footer.empty()) {
10864 return std::string{};
10865 }
10866 return "\n" + footer + "\n";
10867}
10868
10869CLI11_INLINE std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
10870
10871 // This immediately forwards to the make_expanded method. This is done this way so that subcommands can
10872 // have overridden formatters
10873 if(mode == AppFormatMode::Sub)
10874 return make_expanded(app);
10875
10876 std::stringstream out;
10877 if((app->get_name().empty()) && (app->get_parent() != nullptr)) {
10878 if(app->get_group() != "Subcommands") {
10879 out << app->get_group() << ':';
10880 }
10881 }
10882
10883 out << make_description(app);
10884 out << make_usage(app, name);
10885 out << make_positionals(app);
10886 out << make_groups(app, mode);
10887 out << make_subcommands(app, mode);
10888 out << make_footer(app);
10889
10890 return out.str();
10891}
10892
10893CLI11_INLINE std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
10894 std::stringstream out;
10895
10896 std::vector<const App *> subcommands = app->get_subcommands({});
10897
10898 // Make a list in definition order of the groups seen
10899 std::vector<std::string> subcmd_groups_seen;
10900 for(const App *com : subcommands) {
10901 if(com->get_name().empty()) {
10902 if(!com->get_group().empty()) {
10903 out << make_expanded(com);
10904 }
10905 continue;
10906 }
10907 std::string group_key = com->get_group();
10908 if(!group_key.empty() &&
10909 std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
10910 return detail::to_lower(a) == detail::to_lower(group_key);
10911 }) == subcmd_groups_seen.end())
10912 subcmd_groups_seen.push_back(group_key);
10913 }
10914
10915 // For each group, filter out and print subcommands
10916 for(const std::string &group : subcmd_groups_seen) {
10917 out << "\n" << group << ":\n";
10918 std::vector<const App *> subcommands_group = app->get_subcommands(
10919 [&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); });
10920 for(const App *new_com : subcommands_group) {
10921 if(new_com->get_name().empty())
10922 continue;
10923 if(mode != AppFormatMode::All) {
10924 out << make_subcommand(new_com);
10925 } else {
10926 out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
10927 out << "\n";
10928 }
10929 }
10930 }
10931
10932 return out.str();
10933}
10934
10935CLI11_INLINE std::string Formatter::make_subcommand(const App *sub) const {
10936 std::stringstream out;
10937 detail::format_help(out,
10938 sub->get_display_name(true) + (sub->get_required() ? " " + get_label("REQUIRED") : ""),
10939 sub->get_description(),
10940 column_width_);
10941 return out.str();
10942}
10943
10944CLI11_INLINE std::string Formatter::make_expanded(const App *sub) const {
10945 std::stringstream out;
10946 out << sub->get_display_name(true) << "\n";
10947
10948 out << make_description(sub);
10949 if(sub->get_name().empty() && !sub->get_aliases().empty()) {
10950 detail::format_aliases(out, sub->get_aliases(), column_width_ + 2);
10951 }
10952 out << make_positionals(sub);
10953 out << make_groups(sub, AppFormatMode::Sub);
10954 out << make_subcommands(sub, AppFormatMode::Sub);
10955
10956 // Drop blank spaces
10957 std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n");
10958 tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n'
10959
10960 // Indent all but the first line (the name)
10961 return detail::find_and_replace(tmp, "\n", "\n ") + "\n";
10962}
10963
10964CLI11_INLINE std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
10965 if(is_positional)
10966 return opt->get_name(true, false);
10967
10968 return opt->get_name(false, true);
10969}
10970
10971CLI11_INLINE std::string Formatter::make_option_opts(const Option *opt) const {
10972 std::stringstream out;
10973
10974 if(!opt->get_option_text().empty()) {
10975 out << " " << opt->get_option_text();
10976 } else {
10977 if(opt->get_type_size() != 0) {
10978 if(!opt->get_type_name().empty())
10979 out << " " << get_label(opt->get_type_name());
10980 if(!opt->get_default_str().empty())
10981 out << " [" << opt->get_default_str() << "] ";
10982 if(opt->get_expected_max() == detail::expected_max_vector_size)
10983 out << " ...";
10984 else if(opt->get_expected_min() > 1)
10985 out << " x " << opt->get_expected();
10986
10987 if(opt->get_required())
10988 out << " " << get_label("REQUIRED");
10989 }
10990 if(!opt->get_envname().empty())
10991 out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
10992 if(!opt->get_needs().empty()) {
10993 out << " " << get_label("Needs") << ":";
10994 for(const Option *op : opt->get_needs())
10995 out << " " << op->get_name();
10996 }
10997 if(!opt->get_excludes().empty()) {
10998 out << " " << get_label("Excludes") << ":";
10999 for(const Option *op : opt->get_excludes())
11000 out << " " << op->get_name();
11001 }
11002 }
11003 return out.str();
11004}
11005
11006CLI11_INLINE std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
11007
11008CLI11_INLINE std::string Formatter::make_option_usage(const Option *opt) const {
11009 // Note that these are positionals usages
11010 std::stringstream out;
11011 out << make_option_name(opt, true);
11012 if(opt->get_expected_max() >= detail::expected_max_vector_size)
11013 out << "...";
11014 else if(opt->get_expected_max() > 1)
11015 out << "(" << opt->get_expected() << "x)";
11016
11017 return opt->get_required() ? out.str() : "[" + out.str() + "]";
11018}
11019
11020
11021} // namespace CLI
#define CLI11_ERROR_SIMPLE(name)
#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
Definition cli11.hpp:190
#define CLI11_DIAGNOSTIC_PUSH
Definition cli11.hpp:187
#define CLI11_DIAGNOSTIC_POP
Definition cli11.hpp:188
#define CLI11_ERROR_DEF(parent, name)
#define CLI11_INLINE
Definition cli11.hpp:198
#define CLI11_NODISCARD
Definition cli11.hpp:116
FF a
FF b
ssize_t offset
Definition engine.cpp:36
uint8_t buffer[RANDOM_BUFFER_SIZE]
Definition engine.cpp:34
FF const & operator[](size_t idx) const
Retruns the element in beta_products at place #idx.
STL namespace.
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Definition tuple.hpp:13
std::string to_string(bb::avm2::ValueTag tag)
uint8_t len
#define RETHROW
#define THROW