From f5e465b995d1d6822d8d0189026d7a8ed6c9277d Mon Sep 17 00:00:00 2001 From: YZBruh Date: Sun, 10 Aug 2025 18:44:58 +0300 Subject: [PATCH] pmt: reformat code --- .clang-format | 288 + include/CLI/CLI11.hpp | 18344 ++++++++-------- include/PartitionManager/PartitionManager.hpp | 134 +- src/FunctionManager.cpp | 103 +- src/Main.cpp | 9 +- src/PartitionManager.cpp | 223 +- src/functions/BackupFunction.cpp | 194 +- src/functions/EraseFunction.cpp | 149 +- src/functions/FlashFunction.cpp | 175 +- src/functions/InfoFunction.cpp | 116 +- src/functions/PartitionSizeFunction.cpp | 100 +- src/functions/RealLinkPathFunction.cpp | 58 +- src/functions/RealPathFunction.cpp | 57 +- src/functions/RebootFunction.cpp | 35 +- src/functions/TypeFunction.cpp | 82 +- src/functions/functions.hpp | 222 +- srclib/libhelper/include/libhelper/lib.hpp | 312 +- srclib/libhelper/src/Checkers.cpp | 78 +- srclib/libhelper/src/Classes.cpp | 143 +- srclib/libhelper/src/FileUtil.cpp | 286 +- srclib/libhelper/src/Sha256.cpp | 51 +- srclib/libhelper/src/Utilities.cpp | 308 +- srclib/libhelper/tests/test.cpp | 163 +- .../include/libpartition_map/lib.hpp | 500 +- srclib/libpartition_map/src/Getters.cpp | 110 +- srclib/libpartition_map/src/Magic.cpp | 174 +- srclib/libpartition_map/src/PartitionMap.cpp | 368 +- srclib/libpartition_map/src/Type.cpp | 336 +- srclib/libpartition_map/tests/test.cpp | 100 +- 29 files changed, 12503 insertions(+), 10715 deletions(-) create mode 100644 .clang-format mode change 100755 => 100644 include/CLI/CLI11.hpp mode change 100755 => 100644 include/PartitionManager/PartitionManager.hpp mode change 100755 => 100644 src/FunctionManager.cpp mode change 100755 => 100644 src/Main.cpp mode change 100755 => 100644 src/PartitionManager.cpp mode change 100755 => 100644 src/functions/functions.hpp mode change 100755 => 100644 srclib/libhelper/include/libhelper/lib.hpp mode change 100755 => 100644 srclib/libhelper/src/Checkers.cpp mode change 100755 => 100644 srclib/libhelper/src/Classes.cpp mode change 100755 => 100644 srclib/libhelper/src/FileUtil.cpp mode change 100755 => 100644 srclib/libhelper/src/Sha256.cpp mode change 100755 => 100644 srclib/libhelper/src/Utilities.cpp mode change 100755 => 100644 srclib/libhelper/tests/test.cpp mode change 100755 => 100644 srclib/libpartition_map/include/libpartition_map/lib.hpp mode change 100755 => 100644 srclib/libpartition_map/src/Getters.cpp mode change 100755 => 100644 srclib/libpartition_map/src/PartitionMap.cpp mode change 100755 => 100644 srclib/libpartition_map/src/Type.cpp mode change 100755 => 100644 srclib/libpartition_map/tests/test.cpp diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ba7174e --- /dev/null +++ b/.clang-format @@ -0,0 +1,288 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: true + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseArrows: false + AlignCaseColons: false +AlignConsecutiveTableGenBreakingDAGArgColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenCondOperatorColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignConsecutiveTableGenDefinitionColons: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionDeclarations: false + AlignFunctionPointers: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Never +AllowShortCaseExpressionOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AllowShortNamespacesOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: BinPack +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAdjacentStringLiterals: true +BreakAfterAttributes: Leave +BreakAfterJavaFieldAnnotations: false +BreakAfterReturnType: None +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakBinaryOperations: Never +BreakConstructorInitializers: BeforeColon +BreakFunctionDefinitionParameters: false +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +BreakTemplateDeclarations: MultiLine +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExportBlock: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLines: + AtEndOfFile: false + AtStartOfBlock: true + AtStartOfFile: true +KeepFormFeed: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MainIncludeChar: Quote +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: BinPack +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakBeforeMemberAccess: 150 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +PPIndentWidth: -1 +QualifierAlignment: Leave +ReferenceAlignment: Pointer +ReflowComments: Always +RemoveBracesLLVM: false +RemoveEmptyLinesInUnwrappedLines: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterPlacementOperator: true + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + ExceptDoubleParentheses: false + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TableGenBreakInsideDAGArg: DontBreak +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +WrapNamespaceBodyWithEmptyLines: Leave +... + diff --git a/include/CLI/CLI11.hpp b/include/CLI/CLI11.hpp old mode 100755 new mode 100644 index 9fa9cc0..bb54136 --- a/include/CLI/CLI11.hpp +++ b/include/CLI/CLI11.hpp @@ -2,34 +2,36 @@ // Originally designed by Henry Schreiner // https://github.com/CLIUtils/CLI11 // -// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts -// from: v2.5.0 +// This is a standalone header file generated by MakeSingleHeader.py in +// CLI11/scripts from: v2.5.0 // -// CLI11 2.5.0 Copyright (c) 2017-2025 University of Cincinnati, developed by Henry -// Schreiner under NSF AWARD 1414736. All rights reserved. +// CLI11 2.5.0 Copyright (c) 2017-2025 University of Cincinnati, developed by +// Henry Schreiner under NSF AWARD 1414736. All rights reserved. // // Redistribution and use in source and binary forms of CLI11, with or without // modification, are permitted provided that the following conditions are met: // -// 1. Redistributions of source code must retain the above copyright notice, this +// 1. Redistributions of source code must retain the above copyright notice, +// this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // 3. Neither the name of the copyright holder nor the names of its contributors -// may be used to endorse or promote products derived from this software without -// specific prior written permission. +// may be used to endorse or promote products derived from this software +// without specific prior written permission. // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. #pragma once @@ -63,15 +65,11 @@ #include #include - #define CLI11_VERSION_MAJOR 2 #define CLI11_VERSION_MINOR 5 #define CLI11_VERSION_PATCH 0 #define CLI11_VERSION "2.5.0" - - - // The following version macro is very similar to the one in pybind11 #if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER) #if __cplusplus >= 201402L @@ -90,8 +88,9 @@ #endif #endif #elif defined(_MSC_VER) && __cplusplus == 199711L -// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard was fully implemented) -// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer +// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard +// was fully implemented) Unless you use the /Zc:__cplusplus flag on Visual +// Studio 2017 15.7 Preview 3 or newer #if _MSVC_LANG >= 201402L #define CLI11_CPP14 #if _MSVC_LANG > 201402L && _MSC_VER >= 1910 @@ -115,8 +114,9 @@ #endif // GCC < 10 doesn't ignore this in unevaluated contexts -#if !defined(CLI11_CPP17) || \ - (defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 10 && __GNUC__ > 4) +#if !defined(CLI11_CPP17) || \ + (defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && \ + __GNUC__ < 10 && __GNUC__ > 4) #define CLI11_NODISCARD #else #define CLI11_NODISCARD [[nodiscard]] @@ -140,10 +140,12 @@ #endif /** availability */ -#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM +#if defined CLI11_CPP17 && defined __has_include && \ + !defined CLI11_HAS_FILESYSTEM #if __has_include() // Filesystem cannot be used if targeting macOS < 10.15 -#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 +#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && \ + __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 #define CLI11_HAS_FILESYSTEM 0 #elif defined(__wasi__) // As of wasi-sdk-14, filesystem is not implemented @@ -168,7 +170,8 @@ /** availability */ #if !defined(CLI11_CPP26) && !defined(CLI11_HAS_CODECVT) -#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 5 +#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && \ + __GNUC__ < 5 #define CLI11_HAS_CODECVT 0 #else #define CLI11_HAS_CODECVT 1 @@ -185,11 +188,12 @@ #endif /** disable deprecations */ -#if defined(__GNUC__) // GCC or clang +#if defined(__GNUC__) // GCC or clang #define CLI11_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") #define CLI11_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #elif defined(_MSC_VER) #define CLI11_DIAGNOSTIC_PUSH __pragma(warning(push)) @@ -212,35 +216,29 @@ #define CLI11_INLINE inline #endif - - #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 -#include // NOLINT(build/include) +#include // NOLINT(build/include) #else #include #include #endif - - - #ifdef CLI11_CPP17 #include -#endif // CLI11_CPP17 +#endif // CLI11_CPP17 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 #include -#include // NOLINT(build/include) -#endif // CLI11_HAS_FILESYSTEM - - +#include // NOLINT(build/include) +#endif // CLI11_HAS_FILESYSTEM #if defined(_WIN32) #if !(defined(_AMD64_) || defined(_X86_) || defined(_ARM_)) -#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || \ - defined(_M_AMD64) +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || \ + defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) #define _AMD64_ -#elif defined(i386) || defined(__i386) || defined(__i386__) || defined(__i386__) || defined(_M_IX86) +#elif defined(i386) || defined(__i386) || defined(__i386__) || \ + defined(__i386__) || defined(_M_IX86) #define _X86_ #elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARMT) #define _ARM_ @@ -268,10 +266,8 @@ #include #endif - namespace CLI { - /// Convert a wide string to a narrow string. CLI11_INLINE std::string narrow(const std::wstring &str); CLI11_INLINE std::string narrow(const wchar_t *str); @@ -285,43 +281,42 @@ CLI11_INLINE std::wstring widen(const char *str, std::size_t size); #ifdef CLI11_CPP17 CLI11_INLINE std::string narrow(std::wstring_view str); CLI11_INLINE std::wstring widen(std::string_view str); -#endif // CLI11_CPP17 +#endif // CLI11_CPP17 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 /// Convert a char-string to a native path correctly. CLI11_INLINE std::filesystem::path to_path(std::string_view str); -#endif // CLI11_HAS_FILESYSTEM - - - +#endif // CLI11_HAS_FILESYSTEM namespace detail { #if !CLI11_HAS_CODECVT /// Attempt to set one of the acceptable unicode locales for conversion CLI11_INLINE void set_unicode_locale() { - static const std::array unicode_locales{{"C.UTF-8", "en_US.UTF-8", ".UTF-8"}}; + static const std::array unicode_locales{ + {"C.UTF-8", "en_US.UTF-8", ".UTF-8"}}; - for(const auto &locale_name : unicode_locales) { - if(std::setlocale(LC_ALL, locale_name) != nullptr) { - return; - } + for (const auto &locale_name : unicode_locales) { + if (std::setlocale(LC_ALL, locale_name) != nullptr) { + return; } - throw std::runtime_error("CLI::narrow: could not set locale to C.UTF-8"); + } + throw std::runtime_error("CLI::narrow: could not set locale to C.UTF-8"); } template struct scope_guard_t { - F closure; + F closure; - explicit scope_guard_t(F closure_) : closure(closure_) {} - ~scope_guard_t() { closure(); } + explicit scope_guard_t(F closure_) : closure(closure_) {} + ~scope_guard_t() { closure(); } }; -template CLI11_NODISCARD CLI11_INLINE scope_guard_t scope_guard(F &&closure) { - return scope_guard_t{std::forward(closure)}; +template +CLI11_NODISCARD CLI11_INLINE scope_guard_t scope_guard(F &&closure) { + return scope_guard_t{std::forward(closure)}; } -#endif // !CLI11_HAS_CODECVT +#endif // !CLI11_HAS_CODECVT CLI11_DIAGNOSTIC_PUSH CLI11_DIAGNOSTIC_IGNORE_DEPRECATED @@ -329,157 +324,177 @@ CLI11_DIAGNOSTIC_IGNORE_DEPRECATED CLI11_INLINE std::string narrow_impl(const wchar_t *str, std::size_t str_size) { #if CLI11_HAS_CODECVT #ifdef _WIN32 - return std::wstring_convert>().to_bytes(str, str + str_size); + return std::wstring_convert>().to_bytes( + str, str + str_size); #else - return std::wstring_convert>().to_bytes(str, str + str_size); + return std::wstring_convert>().to_bytes( + str, str + str_size); -#endif // _WIN32 -#else // CLI11_HAS_CODECVT - (void)str_size; - std::mbstate_t state = std::mbstate_t(); - const wchar_t *it = str; +#endif // _WIN32 +#else // CLI11_HAS_CODECVT + (void)str_size; + std::mbstate_t state = std::mbstate_t(); + const wchar_t *it = str; - std::string old_locale = std::setlocale(LC_ALL, nullptr); - auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); }); - set_unicode_locale(); + std::string old_locale = std::setlocale(LC_ALL, nullptr); + auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); }); + set_unicode_locale(); - std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state); - if(new_size == static_cast(-1)) { - throw std::runtime_error("CLI::narrow: conversion error in std::wcsrtombs at offset " + - std::to_string(it - str)); - } - std::string result(new_size, '\0'); - std::wcsrtombs(const_cast(result.data()), &str, new_size, &state); + std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state); + if (new_size == static_cast(-1)) { + throw std::runtime_error( + "CLI::narrow: conversion error in std::wcsrtombs at offset " + + std::to_string(it - str)); + } + std::string result(new_size, '\0'); + std::wcsrtombs(const_cast(result.data()), &str, new_size, &state); - return result; + return result; -#endif // CLI11_HAS_CODECVT +#endif // CLI11_HAS_CODECVT } CLI11_INLINE std::wstring widen_impl(const char *str, std::size_t str_size) { #if CLI11_HAS_CODECVT #ifdef _WIN32 - return std::wstring_convert>().from_bytes(str, str + str_size); + return std::wstring_convert>().from_bytes( + str, str + str_size); #else - return std::wstring_convert>().from_bytes(str, str + str_size); + return std::wstring_convert>().from_bytes( + str, str + str_size); -#endif // _WIN32 -#else // CLI11_HAS_CODECVT - (void)str_size; - std::mbstate_t state = std::mbstate_t(); - const char *it = str; +#endif // _WIN32 +#else // CLI11_HAS_CODECVT + (void)str_size; + std::mbstate_t state = std::mbstate_t(); + const char *it = str; - std::string old_locale = std::setlocale(LC_ALL, nullptr); - auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); }); - set_unicode_locale(); + std::string old_locale = std::setlocale(LC_ALL, nullptr); + auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); }); + set_unicode_locale(); - std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state); - if(new_size == static_cast(-1)) { - throw std::runtime_error("CLI::widen: conversion error in std::mbsrtowcs at offset " + - std::to_string(it - str)); - } - std::wstring result(new_size, L'\0'); - std::mbsrtowcs(const_cast(result.data()), &str, new_size, &state); + std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state); + if (new_size == static_cast(-1)) { + throw std::runtime_error( + "CLI::widen: conversion error in std::mbsrtowcs at offset " + + std::to_string(it - str)); + } + std::wstring result(new_size, L'\0'); + std::mbsrtowcs(const_cast(result.data()), &str, new_size, &state); - return result; + return result; -#endif // CLI11_HAS_CODECVT +#endif // CLI11_HAS_CODECVT } CLI11_DIAGNOSTIC_POP -} // namespace detail +} // namespace detail -CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { return detail::narrow_impl(str, str_size); } -CLI11_INLINE std::string narrow(const std::wstring &str) { return detail::narrow_impl(str.data(), str.size()); } +CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { + return detail::narrow_impl(str, str_size); +} +CLI11_INLINE std::string narrow(const std::wstring &str) { + return detail::narrow_impl(str.data(), str.size()); +} // Flawfinder: ignore -CLI11_INLINE std::string narrow(const wchar_t *str) { return detail::narrow_impl(str, std::wcslen(str)); } +CLI11_INLINE std::string narrow(const wchar_t *str) { + return detail::narrow_impl(str, std::wcslen(str)); +} -CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { return detail::widen_impl(str, str_size); } -CLI11_INLINE std::wstring widen(const std::string &str) { return detail::widen_impl(str.data(), str.size()); } +CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { + return detail::widen_impl(str, str_size); +} +CLI11_INLINE std::wstring widen(const std::string &str) { + return detail::widen_impl(str.data(), str.size()); +} // Flawfinder: ignore -CLI11_INLINE std::wstring widen(const char *str) { return detail::widen_impl(str, std::strlen(str)); } +CLI11_INLINE std::wstring widen(const char *str) { + return detail::widen_impl(str, std::strlen(str)); +} #ifdef CLI11_CPP17 -CLI11_INLINE std::string narrow(std::wstring_view str) { return detail::narrow_impl(str.data(), str.size()); } -CLI11_INLINE std::wstring widen(std::string_view str) { return detail::widen_impl(str.data(), str.size()); } -#endif // CLI11_CPP17 +CLI11_INLINE std::string narrow(std::wstring_view str) { + return detail::narrow_impl(str.data(), str.size()); +} +CLI11_INLINE std::wstring widen(std::string_view str) { + return detail::widen_impl(str.data(), str.size()); +} +#endif // CLI11_CPP17 #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 CLI11_INLINE std::filesystem::path to_path(std::string_view str) { - return std::filesystem::path{ + return std::filesystem::path{ #ifdef _WIN32 - widen(str) + widen(str) #else - str -#endif // _WIN32 - }; + str +#endif // _WIN32 + }; } -#endif // CLI11_HAS_FILESYSTEM - - - +#endif // CLI11_HAS_FILESYSTEM namespace detail { #ifdef _WIN32 /// Decode and return UTF-8 argv from GetCommandLineW. CLI11_INLINE std::vector compute_win32_argv(); #endif -} // namespace detail - - +} // namespace detail namespace detail { #ifdef _WIN32 CLI11_INLINE std::vector compute_win32_argv() { - std::vector result; - int argc = 0; + std::vector result; + int argc = 0; - auto deleter = [](wchar_t **ptr) { LocalFree(ptr); }; - // NOLINTBEGIN(*-avoid-c-arrays) - auto wargv = std::unique_ptr(CommandLineToArgvW(GetCommandLineW(), &argc), deleter); - // NOLINTEND(*-avoid-c-arrays) + auto deleter = [](wchar_t **ptr) { LocalFree(ptr); }; + // NOLINTBEGIN(*-avoid-c-arrays) + auto wargv = std::unique_ptr( + CommandLineToArgvW(GetCommandLineW(), &argc), deleter); + // NOLINTEND(*-avoid-c-arrays) - if(wargv == nullptr) { - throw std::runtime_error("CommandLineToArgvW failed with code " + std::to_string(GetLastError())); - } + if (wargv == nullptr) { + throw std::runtime_error("CommandLineToArgvW failed with code " + + std::to_string(GetLastError())); + } - result.reserve(static_cast(argc)); - for(size_t i = 0; i < static_cast(argc); ++i) { - result.push_back(narrow(wargv[i])); - } + result.reserve(static_cast(argc)); + for (size_t i = 0; i < static_cast(argc); ++i) { + result.push_back(narrow(wargv[i])); + } - return result; + return result; } #endif -} // namespace detail +} // namespace detail - - - -/// Include the items in this namespace to get free conversion of enums to/from streams. -/// (This is available inside CLI as well, so CLI11 will use this without a using statement). +/// Include the items in this namespace to get free conversion of enums to/from +/// streams. (This is available inside CLI as well, so CLI11 will use this +/// without a using statement). namespace enums { /// output streaming for enumerations -template ::value>::type> +template ::value>::type> std::ostream &operator<<(std::ostream &in, const T &item) { - // make sure this is out of the detail namespace otherwise it won't be found when needed - return in << static_cast::type>(item); + // make sure this is out of the detail namespace otherwise it won't be found + // when needed + return in << static_cast::type>(item); } -} // namespace enums +} // namespace enums /// Export to CLI namespace using enums::operator<<; namespace detail { -/// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not -/// produce overflow for some expected uses +/// a constant defining an expected max vector size defined to be a big number +/// that could be multiplied by 4 and not produce overflow for some expected +/// uses constexpr int expected_max_vector_size{1 << 29}; // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c /// Split a string by a delim @@ -487,54 +502,53 @@ CLI11_INLINE std::vector split(const std::string &s, char delim); /// Simple function to join a string template std::string join(const T &v, std::string delim = ",") { - std::ostringstream s; - auto beg = std::begin(v); - auto end = std::end(v); - if(beg != end) - s << *beg++; - while(beg != end) { - s << delim << *beg++; - } - auto rval = s.str(); - if(!rval.empty() && delim.size() == 1 && rval.back() == delim[0]) { - // remove trailing delimiter if the last entry was empty - rval.pop_back(); - } - return rval; + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + if (beg != end) s << *beg++; + while (beg != end) { + s << delim << *beg++; + } + auto rval = s.str(); + if (!rval.empty() && delim.size() == 1 && rval.back() == delim[0]) { + // remove trailing delimiter if the last entry was empty + rval.pop_back(); + } + return rval; } /// Simple function to join a string from processed elements -template ::value>::type> +template ::value>::type> std::string join(const T &v, Callable func, std::string delim = ",") { - std::ostringstream s; - auto beg = std::begin(v); - auto end = std::end(v); - auto loc = s.tellp(); - while(beg != end) { - auto nloc = s.tellp(); - if(nloc > loc) { - s << delim; - loc = nloc; - } - s << func(*beg++); + std::ostringstream s; + auto beg = std::begin(v); + auto end = std::end(v); + auto loc = s.tellp(); + while (beg != end) { + auto nloc = s.tellp(); + if (nloc > loc) { + s << delim; + loc = nloc; } - return s.str(); + s << func(*beg++); + } + return s.str(); } /// Join a string in reverse order template std::string rjoin(const T &v, std::string delim = ",") { - std::ostringstream s; - for(std::size_t start = 0; start < v.size(); start++) { - if(start > 0) - s << delim; - s << v[v.size() - start - 1]; - } - return s.str(); + std::ostringstream s; + for (std::size_t start = 0; start < v.size(); start++) { + if (start > 0) s << delim; + s << v[v.size() - start - 1]; + } + return s.str(); } -// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string +// Based roughly on +// http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string /// Trim whitespace from left of string CLI11_INLINE std::string <rim(std::string &str); @@ -552,47 +566,59 @@ CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter); inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); } /// Trim anything from string -inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); } +inline std::string &trim(std::string &str, const std::string filter) { + return ltrim(rtrim(str, filter), filter); +} /// Make a copy of the string and then trim it inline std::string trim_copy(const std::string &str) { - std::string s = str; - return trim(s); + std::string s = str; + return trim(s); } /// remove quotes at the front and back of a string either '"' or '\'' CLI11_INLINE std::string &remove_quotes(std::string &str); -/// remove quotes from all elements of a string vector and process escaped components +/// remove quotes from all elements of a string vector and process escaped +/// components CLI11_INLINE void remove_quotes(std::vector &args); /// Add a leader to the beginning of all new lines (nothing is added /// at the start of the first line). `"; "` would be for ini files /// /// Can't use Regex, or this would be a subs. -CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input); +CLI11_INLINE std::string fix_newlines(const std::string &leader, + std::string input); -/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered) -inline std::string trim_copy(const std::string &str, const std::string &filter) { - std::string s = str; - return trim(s, filter); +/// Make a copy of the string and then trim it, any filter string can be used +/// (any char in string is filtered) +inline std::string trim_copy(const std::string &str, + const std::string &filter) { + std::string s = str; + return trim(s, filter); } /// Print subcommand aliases -CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid); +CLI11_INLINE std::ostream & +format_aliases(std::ostream &out, const std::vector &aliases, + std::size_t wid); /// Verify the first character of an option -/// - is a trigger character, ! has special meaning and new lines would just be annoying to deal with +/// - is a trigger character, ! has special meaning and new lines would just be +/// annoying to deal with template bool valid_first_char(T c) { - return ((c != '-') && (static_cast(c) > 33)); // space and '!' not allowed + return ((c != '-') && + (static_cast(c) > 33)); // space and '!' not allowed } /// Verify following characters of an option template bool valid_later_char(T c) { - // = and : are value separators, { has special meaning for option defaults, - // and control codes other than tab would just be annoying to deal with in many places allowing space here has too - // much potential for inadvertent entry errors and bugs - return ((c != '=') && (c != ':') && (c != '{') && ((static_cast(c) > 32) || c == '\t')); + // = and : are value separators, { has special meaning for option defaults, + // and control codes other than tab would just be annoying to deal with in + // many places allowing space here has too much potential for inadvertent + // entry errors and bugs + return ((c != '=') && (c != ':') && (c != '{') && + ((static_cast(c) > 32) || c == '\t')); } /// Verify an option/subcommand name @@ -600,76 +626,88 @@ CLI11_INLINE bool valid_name_string(const std::string &str); /// Verify an app name inline bool valid_alias_name_string(const std::string &str) { - static const std::string badChars(std::string("\n") + '\0'); - return (str.find_first_of(badChars) == std::string::npos); + static const std::string badChars(std::string("\n") + '\0'); + return (str.find_first_of(badChars) == std::string::npos); } /// check if a string is a container segment separator (empty or "%%") inline bool is_separator(const std::string &str) { - static const std::string sep("%%"); - return (str.empty() || str == sep); + static const std::string sep("%%"); + return (str.empty() || str == sep); } /// Verify that str consists of letters only inline bool isalpha(const std::string &str) { - return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); }); + return std::all_of(str.begin(), str.end(), + [](char c) { return std::isalpha(c, std::locale()); }); } /// Return a lower case version of a string inline std::string to_lower(std::string str) { - std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) { - return std::tolower(x, std::locale()); - }); - return str; + std::transform(std::begin(str), std::end(str), std::begin(str), + [](const std::string::value_type &x) { + return std::tolower(x, std::locale()); + }); + return str; } /// remove underscores from a string inline std::string remove_underscore(std::string str) { - str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str)); - return str; + str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str)); + return str; } /// Find and replace a substring with another substring -CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to); +CLI11_INLINE std::string find_and_replace(std::string str, std::string from, + std::string to); /// check if the flag definitions has possible false flags inline bool has_default_flag_values(const std::string &flags) { - return (flags.find_first_of("{!") != std::string::npos); + return (flags.find_first_of("{!") != std::string::npos); } CLI11_INLINE void remove_default_flag_values(std::string &flags); -/// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores +/// Check if a string is a member of a list of strings and optionally ignore +/// case or ignore underscores CLI11_INLINE std::ptrdiff_t find_member(std::string name, const std::vector names, bool ignore_case = false, bool ignore_underscore = false); -/// Find a trigger string and call a modify callable function that takes the current string and starting position of the -/// trigger and returns the position in the string to search for the next trigger string -template inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) { - std::size_t start_pos = 0; - while((start_pos = str.find(trigger, start_pos)) != std::string::npos) { - start_pos = modify(str, start_pos); - } - return str; +/// Find a trigger string and call a modify callable function that takes the +/// current string and starting position of the trigger and returns the position +/// in the string to search for the next trigger string +template +inline std::string find_and_modify(std::string str, std::string trigger, + Callable modify) { + std::size_t start_pos = 0; + while ((start_pos = str.find(trigger, start_pos)) != std::string::npos) { + start_pos = modify(str, start_pos); + } + return str; } -/// close a sequence of characters indicated by a closure character. Brackets allows sub sequences -/// recognized bracket sequences include "'`[(<{ other closure characters are assumed to be literal strings -CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char); +/// close a sequence of characters indicated by a closure character. Brackets +/// allows sub sequences recognized bracket sequences include "'`[(<{ other +/// closure characters are assumed to be literal strings +CLI11_INLINE std::size_t close_sequence(const std::string &str, + std::size_t start, char closure_char); /// Split a string '"one two" "three"' into 'one two', 'three' -/// Quote characters can be ` ' or " or bracket characters [{(< with matching to the matching bracket -CLI11_INLINE std::vector split_up(std::string str, char delimiter = '\0'); +/// Quote characters can be ` ' or " or bracket characters [{(< with matching to +/// the matching bracket +CLI11_INLINE std::vector split_up(std::string str, + char delimiter = '\0'); /// get the value of an environmental variable or empty string if empty CLI11_INLINE std::string get_environment_value(const std::string &env_name); -/// This function detects an equal or colon followed by an escaped quote after an argument -/// then modifies the string to replace the equality with a space. This is needed -/// to allow the split up function to work properly and is intended to be used with the find_and_modify function -/// the return value is the offset+1 which is required by the find_and_modify function. +/// This function detects an equal or colon followed by an escaped quote after +/// an argument then modifies the string to replace the equality with a space. +/// This is needed to allow the split up function to work properly and is +/// intended to be used with the find_and_modify function the return value is +/// the offset+1 which is required by the find_and_modify function. CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset); /// @brief detect if a string has escapable characters @@ -686,182 +724,200 @@ CLI11_INLINE std::string add_escaped_characters(const std::string &str); CLI11_INLINE std::string remove_escaped_characters(const std::string &str); /// generate a string with all non printable characters escaped to hex codes -CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape); +CLI11_INLINE std::string +binary_escape_string(const std::string &string_to_escape); CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string); /// extract an escaped binary_string -CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string); - -/// process a quoted string, remove the quotes and if appropriate handle escaped characters -CLI11_INLINE bool process_quoted_string(std::string &str, char string_char = '\"', char literal_char = '\''); - -/// This function formats the given text as a paragraph with fixed width and applies correct line wrapping -/// with a custom line prefix. The paragraph will get streamed to the given ostream. -CLI11_INLINE std::ostream &streamOutAsParagraph(std::ostream &out, - const std::string &text, - std::size_t paragraphWidth, - const std::string &linePrefix = "", - bool skipPrefixOnFirstLine = false); - -} // namespace detail +CLI11_INLINE std::string +extract_binary_string(const std::string &escaped_string); +/// process a quoted string, remove the quotes and if appropriate handle escaped +/// characters +CLI11_INLINE bool process_quoted_string(std::string &str, + char string_char = '\"', + char literal_char = '\''); +/// This function formats the given text as a paragraph with fixed width and +/// applies correct line wrapping with a custom line prefix. The paragraph will +/// get streamed to the given ostream. +CLI11_INLINE std::ostream &streamOutAsParagraph( + std::ostream &out, const std::string &text, std::size_t paragraphWidth, + const std::string &linePrefix = "", bool skipPrefixOnFirstLine = false); +} // namespace detail namespace detail { CLI11_INLINE std::vector split(const std::string &s, char delim) { - std::vector elems; - // Check to see if empty string, give consistent result - if(s.empty()) { - elems.emplace_back(); - } else { - std::stringstream ss; - ss.str(s); - std::string item; - while(std::getline(ss, item, delim)) { - elems.push_back(item); - } + std::vector elems; + // Check to see if empty string, give consistent result + if (s.empty()) { + elems.emplace_back(); + } else { + std::stringstream ss; + ss.str(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); } - return elems; + } + return elems; } CLI11_INLINE std::string <rim(std::string &str) { - auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(str.begin(), it); - return str; + auto it = std::find_if(str.begin(), str.end(), [](char ch) { + return !std::isspace(ch, std::locale()); + }); + str.erase(str.begin(), it); + return str; } CLI11_INLINE std::string <rim(std::string &str, const std::string &filter) { - auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(str.begin(), it); - return str; + auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { + return filter.find(ch) == std::string::npos; + }); + str.erase(str.begin(), it); + return str; } CLI11_INLINE std::string &rtrim(std::string &str) { - auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(it.base(), str.end()); - return str; + auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { + return !std::isspace(ch, std::locale()); + }); + str.erase(it.base(), str.end()); + return str; } CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) { - auto it = - std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(it.base(), str.end()); - return str; + auto it = std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { + return filter.find(ch) == std::string::npos; + }); + str.erase(it.base(), str.end()); + return str; } CLI11_INLINE std::string &remove_quotes(std::string &str) { - if(str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) { - if(str.front() == str.back()) { - str.pop_back(); - str.erase(str.begin(), str.begin() + 1); - } + if (str.length() > 1 && + (str.front() == '"' || str.front() == '\'' || str.front() == '`')) { + if (str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); } - return str; + } + return str; } CLI11_INLINE std::string &remove_outer(std::string &str, char key) { - if(str.length() > 1 && (str.front() == key)) { - if(str.front() == str.back()) { - str.pop_back(); - str.erase(str.begin(), str.begin() + 1); - } + if (str.length() > 1 && (str.front() == key)) { + if (str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); } - return str; + } + return str; } -CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input) { - std::string::size_type n = 0; - while(n != std::string::npos && n < input.size()) { - n = input.find('\n', n); - if(n != std::string::npos) { - input = input.substr(0, n + 1) + leader + input.substr(n + 1); - n += leader.size(); - } +CLI11_INLINE std::string fix_newlines(const std::string &leader, + std::string input) { + std::string::size_type n = 0; + while (n != std::string::npos && n < input.size()) { + n = input.find('\n', n); + if (n != std::string::npos) { + input = input.substr(0, n + 1) + leader + input.substr(n + 1); + n += leader.size(); } - return input; + } + return input; } -CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid) { - if(!aliases.empty()) { - out << std::setw(static_cast(wid)) << " aliases: "; - bool front = true; - for(const auto &alias : aliases) { - if(!front) { - out << ", "; - } else { - front = false; - } - out << detail::fix_newlines(" ", alias); - } - out << "\n"; +CLI11_INLINE std::ostream & +format_aliases(std::ostream &out, const std::vector &aliases, + std::size_t wid) { + if (!aliases.empty()) { + out << std::setw(static_cast(wid)) << " aliases: "; + bool front = true; + for (const auto &alias : aliases) { + if (!front) { + out << ", "; + } else { + front = false; + } + out << detail::fix_newlines(" ", alias); } - return out; + out << "\n"; + } + return out; } CLI11_INLINE bool valid_name_string(const std::string &str) { - if(str.empty() || !valid_first_char(str[0])) { - return false; - } - auto e = str.end(); - for(auto c = str.begin() + 1; c != e; ++c) - if(!valid_later_char(*c)) - return false; - return true; + if (str.empty() || !valid_first_char(str[0])) { + return false; + } + auto e = str.end(); + for (auto c = str.begin() + 1; c != e; ++c) + if (!valid_later_char(*c)) return false; + return true; } -CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) { +CLI11_INLINE std::string find_and_replace(std::string str, std::string from, + std::string to) { - std::size_t start_pos = 0; + std::size_t start_pos = 0; - while((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); - } + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } - return str; + return str; } CLI11_INLINE void remove_default_flag_values(std::string &flags) { - auto loc = flags.find_first_of('{', 2); - while(loc != std::string::npos) { - auto finish = flags.find_first_of("},", loc + 1); - if((finish != std::string::npos) && (flags[finish] == '}')) { - flags.erase(flags.begin() + static_cast(loc), - flags.begin() + static_cast(finish) + 1); - } - loc = flags.find_first_of('{', loc + 1); + auto loc = flags.find_first_of('{', 2); + while (loc != std::string::npos) { + auto finish = flags.find_first_of("},", loc + 1); + if ((finish != std::string::npos) && (flags[finish] == '}')) { + flags.erase(flags.begin() + static_cast(loc), + flags.begin() + static_cast(finish) + 1); } - flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); + loc = flags.find_first_of('{', loc + 1); + } + flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); } -CLI11_INLINE std::ptrdiff_t -find_member(std::string name, const std::vector names, bool ignore_case, bool ignore_underscore) { - auto it = std::end(names); - if(ignore_case) { - if(ignore_underscore) { - name = detail::to_lower(detail::remove_underscore(name)); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(detail::remove_underscore(local_name)) == name; - }); - } else { - name = detail::to_lower(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(local_name) == name; - }); - } - - } else if(ignore_underscore) { - name = detail::remove_underscore(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::remove_underscore(local_name) == name; - }); +CLI11_INLINE std::ptrdiff_t find_member(std::string name, + const std::vector names, + bool ignore_case, + bool ignore_underscore) { + auto it = std::end(names); + if (ignore_case) { + if (ignore_underscore) { + name = detail::to_lower(detail::remove_underscore(name)); + it = std::find_if( + std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(detail::remove_underscore(local_name)) == + name; + }); } else { - it = std::find(std::begin(names), std::end(names), name); + name = detail::to_lower(name); + it = std::find_if(std::begin(names), std::end(names), + [&name](std::string local_name) { + return detail::to_lower(local_name) == name; + }); } - return (it != std::end(names)) ? (it - std::begin(names)) : (-1); + } else if (ignore_underscore) { + name = detail::remove_underscore(name); + it = std::find_if(std::begin(names), std::end(names), + [&name](std::string local_name) { + return detail::remove_underscore(local_name) == name; + }); + } else { + it = std::find(std::begin(names), std::end(names), name); + } + + return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } static const std::string escapedChars("\b\t\n\f\r\"\\"); @@ -870,399 +926,427 @@ static const std::string bracketChars{"\"'`[(<{"}; static const std::string matchBracketChars("\"'`])>}"); CLI11_INLINE bool has_escapable_character(const std::string &str) { - return (str.find_first_of(escapedChars) != std::string::npos); + return (str.find_first_of(escapedChars) != std::string::npos); } CLI11_INLINE std::string add_escaped_characters(const std::string &str) { - std::string out; - out.reserve(str.size() + 4); - for(char s : str) { - auto sloc = escapedChars.find_first_of(s); - if(sloc != std::string::npos) { - out.push_back('\\'); - out.push_back(escapedCharsCode[sloc]); - } else { - out.push_back(s); - } + std::string out; + out.reserve(str.size() + 4); + for (char s : str) { + auto sloc = escapedChars.find_first_of(s); + if (sloc != std::string::npos) { + out.push_back('\\'); + out.push_back(escapedCharsCode[sloc]); + } else { + out.push_back(s); } - return out; + } + return out; } CLI11_INLINE std::uint32_t hexConvert(char hc) { - int hcode{0}; - if(hc >= '0' && hc <= '9') { - hcode = (hc - '0'); - } else if(hc >= 'A' && hc <= 'F') { - hcode = (hc - 'A' + 10); - } else if(hc >= 'a' && hc <= 'f') { - hcode = (hc - 'a' + 10); - } else { - hcode = -1; - } - return static_cast(hcode); + int hcode{0}; + if (hc >= '0' && hc <= '9') { + hcode = (hc - '0'); + } else if (hc >= 'A' && hc <= 'F') { + hcode = (hc - 'A' + 10); + } else if (hc >= 'a' && hc <= 'f') { + hcode = (hc - 'a' + 10); + } else { + hcode = -1; + } + return static_cast(hcode); } -CLI11_INLINE char make_char(std::uint32_t code) { return static_cast(static_cast(code)); } +CLI11_INLINE char make_char(std::uint32_t code) { + return static_cast(static_cast(code)); +} CLI11_INLINE void append_codepoint(std::string &str, std::uint32_t code) { - if(code < 0x80) { // ascii code equivalent - str.push_back(static_cast(code)); - } else if(code < 0x800) { // \u0080 to \u07FF - // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 - str.push_back(make_char(0xC0 | code >> 6)); - str.push_back(make_char(0x80 | (code & 0x3F))); - } else if(code < 0x10000) { // U+0800...U+FFFF - if(0xD800 <= code && code <= 0xDFFF) { - throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8."); - } - // 1110yyyy 10yxxxxx 10xxxxxx - str.push_back(make_char(0xE0 | code >> 12)); - str.push_back(make_char(0x80 | (code >> 6 & 0x3F))); - str.push_back(make_char(0x80 | (code & 0x3F))); - } else if(code < 0x110000) { // U+010000 ... U+10FFFF - // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx - str.push_back(make_char(0xF0 | code >> 18)); - str.push_back(make_char(0x80 | (code >> 12 & 0x3F))); - str.push_back(make_char(0x80 | (code >> 6 & 0x3F))); - str.push_back(make_char(0x80 | (code & 0x3F))); + if (code < 0x80) { // ascii code equivalent + str.push_back(static_cast(code)); + } else if (code < 0x800) { // \u0080 to \u07FF + // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 + str.push_back(make_char(0xC0 | code >> 6)); + str.push_back(make_char(0x80 | (code & 0x3F))); + } else if (code < 0x10000) { // U+0800...U+FFFF + if (0xD800 <= code && code <= 0xDFFF) { + throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8."); } + // 1110yyyy 10yxxxxx 10xxxxxx + str.push_back(make_char(0xE0 | code >> 12)); + str.push_back(make_char(0x80 | (code >> 6 & 0x3F))); + str.push_back(make_char(0x80 | (code & 0x3F))); + } else if (code < 0x110000) { // U+010000 ... U+10FFFF + // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx + str.push_back(make_char(0xF0 | code >> 18)); + str.push_back(make_char(0x80 | (code >> 12 & 0x3F))); + str.push_back(make_char(0x80 | (code >> 6 & 0x3F))); + str.push_back(make_char(0x80 | (code & 0x3F))); + } } CLI11_INLINE std::string remove_escaped_characters(const std::string &str) { - std::string out; - out.reserve(str.size()); - for(auto loc = str.begin(); loc < str.end(); ++loc) { - if(*loc == '\\') { - if(str.end() - loc < 2) { - throw std::invalid_argument("invalid escape sequence " + str); - } - auto ecloc = escapedCharsCode.find_first_of(*(loc + 1)); - if(ecloc != std::string::npos) { - out.push_back(escapedChars[ecloc]); - ++loc; - } else if(*(loc + 1) == 'u') { - // must have 4 hex characters - if(str.end() - loc < 6) { - throw std::invalid_argument("unicode sequence must have 4 hex codes " + str); - } - std::uint32_t code{0}; - std::uint32_t mplier{16 * 16 * 16}; - for(int ii = 2; ii < 6; ++ii) { - std::uint32_t res = hexConvert(*(loc + ii)); - if(res > 0x0F) { - throw std::invalid_argument("unicode sequence must have 4 hex codes " + str); - } - code += res * mplier; - mplier = mplier / 16; - } - append_codepoint(out, code); - loc += 5; - } else if(*(loc + 1) == 'U') { - // must have 8 hex characters - if(str.end() - loc < 10) { - throw std::invalid_argument("unicode sequence must have 8 hex codes " + str); - } - std::uint32_t code{0}; - std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16}; - for(int ii = 2; ii < 10; ++ii) { - std::uint32_t res = hexConvert(*(loc + ii)); - if(res > 0x0F) { - throw std::invalid_argument("unicode sequence must have 8 hex codes " + str); - } - code += res * mplier; - mplier = mplier / 16; - } - append_codepoint(out, code); - loc += 9; - } else if(*(loc + 1) == '0') { - out.push_back('\0'); - ++loc; - } else { - throw std::invalid_argument(std::string("unrecognized escape sequence \\") + *(loc + 1) + " in " + str); - } - } else { - out.push_back(*loc); - } - } - return out; -} - -CLI11_INLINE std::size_t close_string_quote(const std::string &str, std::size_t start, char closure_char) { - std::size_t loc{0}; - for(loc = start + 1; loc < str.size(); ++loc) { - if(str[loc] == closure_char) { - break; - } - if(str[loc] == '\\') { - // skip the next character for escaped sequences - ++loc; - } - } - return loc; -} - -CLI11_INLINE std::size_t close_literal_quote(const std::string &str, std::size_t start, char closure_char) { - auto loc = str.find_first_of(closure_char, start + 1); - return (loc != std::string::npos ? loc : str.size()); -} - -CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char) { - - auto bracket_loc = matchBracketChars.find(closure_char); - switch(bracket_loc) { - case 0: - return close_string_quote(str, start, closure_char); - case 1: - case 2: - case std::string::npos: - return close_literal_quote(str, start, closure_char); - default: - break; - } - - std::string closures(1, closure_char); - auto loc = start + 1; - - while(loc < str.size()) { - if(str[loc] == closures.back()) { - closures.pop_back(); - if(closures.empty()) { - return loc; - } - } - bracket_loc = bracketChars.find(str[loc]); - if(bracket_loc != std::string::npos) { - switch(bracket_loc) { - case 0: - loc = close_string_quote(str, loc, str[loc]); - break; - case 1: - case 2: - loc = close_literal_quote(str, loc, str[loc]); - break; - default: - closures.push_back(matchBracketChars[bracket_loc]); - break; - } - } + std::string out; + out.reserve(str.size()); + for (auto loc = str.begin(); loc < str.end(); ++loc) { + if (*loc == '\\') { + if (str.end() - loc < 2) { + throw std::invalid_argument("invalid escape sequence " + str); + } + auto ecloc = escapedCharsCode.find_first_of(*(loc + 1)); + if (ecloc != std::string::npos) { + out.push_back(escapedChars[ecloc]); ++loc; + } else if (*(loc + 1) == 'u') { + // must have 4 hex characters + if (str.end() - loc < 6) { + throw std::invalid_argument( + "unicode sequence must have 4 hex codes " + str); + } + std::uint32_t code{0}; + std::uint32_t mplier{16 * 16 * 16}; + for (int ii = 2; ii < 6; ++ii) { + std::uint32_t res = hexConvert(*(loc + ii)); + if (res > 0x0F) { + throw std::invalid_argument( + "unicode sequence must have 4 hex codes " + str); + } + code += res * mplier; + mplier = mplier / 16; + } + append_codepoint(out, code); + loc += 5; + } else if (*(loc + 1) == 'U') { + // must have 8 hex characters + if (str.end() - loc < 10) { + throw std::invalid_argument( + "unicode sequence must have 8 hex codes " + str); + } + std::uint32_t code{0}; + std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16}; + for (int ii = 2; ii < 10; ++ii) { + std::uint32_t res = hexConvert(*(loc + ii)); + if (res > 0x0F) { + throw std::invalid_argument( + "unicode sequence must have 8 hex codes " + str); + } + code += res * mplier; + mplier = mplier / 16; + } + append_codepoint(out, code); + loc += 9; + } else if (*(loc + 1) == '0') { + out.push_back('\0'); + ++loc; + } else { + throw std::invalid_argument( + std::string("unrecognized escape sequence \\") + *(loc + 1) + + " in " + str); + } + } else { + out.push_back(*loc); } - if(loc > str.size()) { - loc = str.size(); - } - return loc; + } + return out; } -CLI11_INLINE std::vector split_up(std::string str, char delimiter) { - - auto find_ws = [delimiter](char ch) { - return (delimiter == '\0') ? std::isspace(ch, std::locale()) : (ch == delimiter); - }; - trim(str); - - std::vector output; - while(!str.empty()) { - if(bracketChars.find_first_of(str[0]) != std::string::npos) { - auto bracketLoc = bracketChars.find_first_of(str[0]); - auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]); - if(end >= str.size()) { - output.push_back(std::move(str)); - str.clear(); - } else { - output.push_back(str.substr(0, end + 1)); - if(end + 2 < str.size()) { - str = str.substr(end + 2); - } else { - str.clear(); - } - } - - } else { - auto it = std::find_if(std::begin(str), std::end(str), find_ws); - if(it != std::end(str)) { - std::string value = std::string(str.begin(), it); - output.push_back(value); - str = std::string(it + 1, str.end()); - } else { - output.push_back(str); - str.clear(); - } - } - trim(str); +CLI11_INLINE std::size_t close_string_quote(const std::string &str, + std::size_t start, + char closure_char) { + std::size_t loc{0}; + for (loc = start + 1; loc < str.size(); ++loc) { + if (str[loc] == closure_char) { + break; } - return output; + if (str[loc] == '\\') { + // skip the next character for escaped sequences + ++loc; + } + } + return loc; +} + +CLI11_INLINE std::size_t close_literal_quote(const std::string &str, + std::size_t start, + char closure_char) { + auto loc = str.find_first_of(closure_char, start + 1); + return (loc != std::string::npos ? loc : str.size()); +} + +CLI11_INLINE std::size_t close_sequence(const std::string &str, + std::size_t start, char closure_char) { + + auto bracket_loc = matchBracketChars.find(closure_char); + switch (bracket_loc) { + case 0: + return close_string_quote(str, start, closure_char); + case 1: + case 2: + case std::string::npos: + return close_literal_quote(str, start, closure_char); + default: + break; + } + + std::string closures(1, closure_char); + auto loc = start + 1; + + while (loc < str.size()) { + if (str[loc] == closures.back()) { + closures.pop_back(); + if (closures.empty()) { + return loc; + } + } + bracket_loc = bracketChars.find(str[loc]); + if (bracket_loc != std::string::npos) { + switch (bracket_loc) { + case 0: + loc = close_string_quote(str, loc, str[loc]); + break; + case 1: + case 2: + loc = close_literal_quote(str, loc, str[loc]); + break; + default: + closures.push_back(matchBracketChars[bracket_loc]); + break; + } + } + ++loc; + } + if (loc > str.size()) { + loc = str.size(); + } + return loc; +} + +CLI11_INLINE std::vector split_up(std::string str, + char delimiter) { + + auto find_ws = [delimiter](char ch) { + return (delimiter == '\0') ? std::isspace(ch, std::locale()) + : (ch == delimiter); + }; + trim(str); + + std::vector output; + while (!str.empty()) { + if (bracketChars.find_first_of(str[0]) != std::string::npos) { + auto bracketLoc = bracketChars.find_first_of(str[0]); + auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]); + if (end >= str.size()) { + output.push_back(std::move(str)); + str.clear(); + } else { + output.push_back(str.substr(0, end + 1)); + if (end + 2 < str.size()) { + str = str.substr(end + 2); + } else { + str.clear(); + } + } + + } else { + auto it = std::find_if(std::begin(str), std::end(str), find_ws); + if (it != std::end(str)) { + std::string value = std::string(str.begin(), it); + output.push_back(value); + str = std::string(it + 1, str.end()); + } else { + output.push_back(str); + str.clear(); + } + } + trim(str); + } + return output; } CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) { - auto next = str[offset + 1]; - if((next == '\"') || (next == '\'') || (next == '`')) { - auto astart = str.find_last_of("-/ \"\'`", offset - 1); - if(astart != std::string::npos) { - if(str[astart] == ((str[offset] == '=') ? '-' : '/')) - str[offset] = ' '; // interpret this as a space so the split_up works properly - } + auto next = str[offset + 1]; + if ((next == '\"') || (next == '\'') || (next == '`')) { + auto astart = str.find_last_of("-/ \"\'`", offset - 1); + if (astart != std::string::npos) { + if (str[astart] == ((str[offset] == '=') ? '-' : '/')) + str[offset] = + ' '; // interpret this as a space so the split_up works properly } - return offset + 1; + } + return offset + 1; } -CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape) { - // s is our escaped output string - std::string escaped_string{}; - // loop through all characters - for(char c : string_to_escape) { - // check if a given character is printable - // the cast is necessary to avoid undefined behaviour - if(isprint(static_cast(c)) == 0) { - std::stringstream stream; - // if the character is not printable - // we'll convert it to a hex string using a stringstream - // note that since char is signed we have to cast it to unsigned first - stream << std::hex << static_cast(static_cast(c)); - std::string code = stream.str(); - escaped_string += std::string("\\x") + (code.size() < 2 ? "0" : "") + code; - } else if(c == 'x' || c == 'X') { - // need to check for inadvertent binary sequences - if(!escaped_string.empty() && escaped_string.back() == '\\') { - escaped_string += std::string("\\x") + (c == 'x' ? "78" : "58"); - } else { - escaped_string.push_back(c); - } +CLI11_INLINE std::string +binary_escape_string(const std::string &string_to_escape) { + // s is our escaped output string + std::string escaped_string{}; + // loop through all characters + for (char c : string_to_escape) { + // check if a given character is printable + // the cast is necessary to avoid undefined behaviour + if (isprint(static_cast(c)) == 0) { + std::stringstream stream; + // if the character is not printable + // we'll convert it to a hex string using a stringstream + // note that since char is signed we have to cast it to unsigned first + stream << std::hex + << static_cast(static_cast(c)); + std::string code = stream.str(); + escaped_string += + std::string("\\x") + (code.size() < 2 ? "0" : "") + code; + } else if (c == 'x' || c == 'X') { + // need to check for inadvertent binary sequences + if (!escaped_string.empty() && escaped_string.back() == '\\') { + escaped_string += std::string("\\x") + (c == 'x' ? "78" : "58"); + } else { + escaped_string.push_back(c); + } - } else { - escaped_string.push_back(c); - } + } else { + escaped_string.push_back(c); } - if(escaped_string != string_to_escape) { - auto sqLoc = escaped_string.find('\''); - while(sqLoc != std::string::npos) { - escaped_string[sqLoc] = '\\'; - escaped_string.insert(sqLoc + 1, "x27"); - sqLoc = escaped_string.find('\''); - } - escaped_string.insert(0, "'B\"("); - escaped_string.push_back(')'); - escaped_string.push_back('"'); - escaped_string.push_back('\''); + } + if (escaped_string != string_to_escape) { + auto sqLoc = escaped_string.find('\''); + while (sqLoc != std::string::npos) { + escaped_string[sqLoc] = '\\'; + escaped_string.insert(sqLoc + 1, "x27"); + sqLoc = escaped_string.find('\''); } - return escaped_string; + escaped_string.insert(0, "'B\"("); + escaped_string.push_back(')'); + escaped_string.push_back('"'); + escaped_string.push_back('\''); + } + return escaped_string; } CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string) { - size_t ssize = escaped_string.size(); - if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) { - return true; - } - return (escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0); + size_t ssize = escaped_string.size(); + if (escaped_string.compare(0, 3, "B\"(") == 0 && + escaped_string.compare(ssize - 2, 2, ")\"") == 0) { + return true; + } + return (escaped_string.compare(0, 4, "'B\"(") == 0 && + escaped_string.compare(ssize - 3, 3, ")\"'") == 0); } -CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string) { - std::size_t start{0}; - std::size_t tail{0}; - size_t ssize = escaped_string.size(); - if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) { - start = 3; - tail = 2; - } else if(escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0) { - start = 4; - tail = 3; - } +CLI11_INLINE std::string +extract_binary_string(const std::string &escaped_string) { + std::size_t start{0}; + std::size_t tail{0}; + size_t ssize = escaped_string.size(); + if (escaped_string.compare(0, 3, "B\"(") == 0 && + escaped_string.compare(ssize - 2, 2, ")\"") == 0) { + start = 3; + tail = 2; + } else if (escaped_string.compare(0, 4, "'B\"(") == 0 && + escaped_string.compare(ssize - 3, 3, ")\"'") == 0) { + start = 4; + tail = 3; + } - if(start == 0) { - return escaped_string; - } - std::string outstring; + if (start == 0) { + return escaped_string; + } + std::string outstring; - outstring.reserve(ssize - start - tail); - std::size_t loc = start; - while(loc < ssize - tail) { - // ssize-2 to skip )" at the end - if(escaped_string[loc] == '\\' && (escaped_string[loc + 1] == 'x' || escaped_string[loc + 1] == 'X')) { - auto c1 = escaped_string[loc + 2]; - auto c2 = escaped_string[loc + 3]; + outstring.reserve(ssize - start - tail); + std::size_t loc = start; + while (loc < ssize - tail) { + // ssize-2 to skip )" at the end + if (escaped_string[loc] == '\\' && + (escaped_string[loc + 1] == 'x' || escaped_string[loc + 1] == 'X')) { + auto c1 = escaped_string[loc + 2]; + auto c2 = escaped_string[loc + 3]; - std::uint32_t res1 = hexConvert(c1); - std::uint32_t res2 = hexConvert(c2); - if(res1 <= 0x0F && res2 <= 0x0F) { - loc += 4; - outstring.push_back(static_cast(res1 * 16 + res2)); - continue; - } - } - outstring.push_back(escaped_string[loc]); - ++loc; + std::uint32_t res1 = hexConvert(c1); + std::uint32_t res2 = hexConvert(c2); + if (res1 <= 0x0F && res2 <= 0x0F) { + loc += 4; + outstring.push_back(static_cast(res1 * 16 + res2)); + continue; + } } - return outstring; + outstring.push_back(escaped_string[loc]); + ++loc; + } + return outstring; } CLI11_INLINE void remove_quotes(std::vector &args) { - for(auto &arg : args) { - if(arg.front() == '\"' && arg.back() == '\"') { - remove_quotes(arg); - // only remove escaped for string arguments not literal strings - arg = remove_escaped_characters(arg); - } else { - remove_quotes(arg); - } + for (auto &arg : args) { + if (arg.front() == '\"' && arg.back() == '\"') { + remove_quotes(arg); + // only remove escaped for string arguments not literal strings + arg = remove_escaped_characters(arg); + } else { + remove_quotes(arg); } + } } CLI11_INLINE void handle_secondary_array(std::string &str) { - if(str.size() >= 2 && str.front() == '[' && str.back() == ']') { - // handle some special array processing for arguments if it might be interpreted as a secondary array - std::string tstr{"[["}; - for(std::size_t ii = 1; ii < str.size(); ++ii) { - tstr.push_back(str[ii]); - tstr.push_back(str[ii]); - } - str = std::move(tstr); + if (str.size() >= 2 && str.front() == '[' && str.back() == ']') { + // handle some special array processing for arguments if it might be + // interpreted as a secondary array + std::string tstr{"[["}; + for (std::size_t ii = 1; ii < str.size(); ++ii) { + tstr.push_back(str[ii]); + tstr.push_back(str[ii]); } + str = std::move(tstr); + } } -CLI11_INLINE bool process_quoted_string(std::string &str, char string_char, char literal_char) { - if(str.size() <= 1) { - return false; - } - if(detail::is_binary_escaped_string(str)) { - str = detail::extract_binary_string(str); - handle_secondary_array(str); - return true; - } - if(str.front() == string_char && str.back() == string_char) { - detail::remove_outer(str, string_char); - if(str.find_first_of('\\') != std::string::npos) { - str = detail::remove_escaped_characters(str); - } - handle_secondary_array(str); - return true; - } - if((str.front() == literal_char || str.front() == '`') && str.back() == str.front()) { - detail::remove_outer(str, str.front()); - handle_secondary_array(str); - return true; - } +CLI11_INLINE bool process_quoted_string(std::string &str, char string_char, + char literal_char) { + if (str.size() <= 1) { return false; + } + if (detail::is_binary_escaped_string(str)) { + str = detail::extract_binary_string(str); + handle_secondary_array(str); + return true; + } + if (str.front() == string_char && str.back() == string_char) { + detail::remove_outer(str, string_char); + if (str.find_first_of('\\') != std::string::npos) { + str = detail::remove_escaped_characters(str); + } + handle_secondary_array(str); + return true; + } + if ((str.front() == literal_char || str.front() == '`') && + str.back() == str.front()) { + detail::remove_outer(str, str.front()); + handle_secondary_array(str); + return true; + } + return false; } std::string get_environment_value(const std::string &env_name) { - char *buffer = nullptr; - std::string ename_string; + char *buffer = nullptr; + std::string ename_string; #ifdef _MSC_VER - // Windows version - std::size_t sz = 0; - if(_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer != nullptr) { - ename_string = std::string(buffer); - free(buffer); - } + // Windows version + std::size_t sz = 0; + if (_dupenv_s(&buffer, &sz, env_name.c_str()) == 0 && buffer != nullptr) { + ename_string = std::string(buffer); + free(buffer); + } #else - // This also works on Windows, but gives a warning - buffer = std::getenv(env_name.c_str()); - if(buffer != nullptr) { - ename_string = std::string(buffer); - } + // This also works on Windows, but gives a warning + buffer = std::getenv(env_name.c_str()); + if (buffer != nullptr) { + ename_string = std::string(buffer); + } #endif - return ename_string; + return ename_string; } CLI11_INLINE std::ostream &streamOutAsParagraph(std::ostream &out, @@ -1270,72 +1354,73 @@ CLI11_INLINE std::ostream &streamOutAsParagraph(std::ostream &out, std::size_t paragraphWidth, const std::string &linePrefix, bool skipPrefixOnFirstLine) { - if(!skipPrefixOnFirstLine) - out << linePrefix; // First line prefix + if (!skipPrefixOnFirstLine) out << linePrefix; // First line prefix - std::istringstream lss(text); - std::string line = ""; - while(std::getline(lss, line)) { - std::istringstream iss(line); - std::string word = ""; - std::size_t charsWritten = 0; + std::istringstream lss(text); + std::string line = ""; + while (std::getline(lss, line)) { + std::istringstream iss(line); + std::string word = ""; + std::size_t charsWritten = 0; - while(iss >> word) { - if(word.length() + charsWritten > paragraphWidth) { - out << '\n' << linePrefix; - charsWritten = 0; - } + while (iss >> word) { + if (word.length() + charsWritten > paragraphWidth) { + out << '\n' << linePrefix; + charsWritten = 0; + } - out << word << " "; - charsWritten += word.length() + 1; - } - - if(!lss.eof()) - out << '\n' << linePrefix; + out << word << " "; + charsWritten += word.length() + 1; } - return out; + + if (!lss.eof()) out << '\n' << linePrefix; + } + return out; } -} // namespace detail - - +} // namespace detail // Use one of these on all error classes. // These are temporary and are undef'd at the end of this file. -#define CLI11_ERROR_DEF(parent, name) \ - protected: \ - name(std::string ename, std::string msg, int exit_code) : parent(std::move(ename), std::move(msg), exit_code) {} \ - name(std::string ename, std::string msg, ExitCodes exit_code) \ - : parent(std::move(ename), std::move(msg), exit_code) {} \ - \ - public: \ - name(std::string msg, ExitCodes exit_code) : parent(#name, std::move(msg), exit_code) {} \ - name(std::string msg, int exit_code) : parent(#name, std::move(msg), exit_code) {} +#define CLI11_ERROR_DEF(parent, name) \ +protected: \ + name(std::string ename, std::string msg, int exit_code) \ + : parent(std::move(ename), std::move(msg), exit_code) {} \ + name(std::string ename, std::string msg, ExitCodes exit_code) \ + : parent(std::move(ename), std::move(msg), exit_code) {} \ + \ +public: \ + name(std::string msg, ExitCodes exit_code) \ + : parent(#name, std::move(msg), exit_code) {} \ + name(std::string msg, int exit_code) \ + : parent(#name, std::move(msg), exit_code) {} -// This is added after the one above if a class is used directly and builds its own message -#define CLI11_ERROR_SIMPLE(name) \ - explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} +// This is added after the one above if a class is used directly and builds its +// own message +#define CLI11_ERROR_SIMPLE(name) \ + explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {} -/// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut, -/// int values from e.get_error_code(). +/// These codes are part of every error in CLI. They can be obtained from e +/// using e.exit_code or as a quick shortcut, int values from +/// e.get_error_code(). enum class ExitCodes { - Success = 0, - IncorrectConstruction = 100, - BadNameString, - OptionAlreadyAdded, - FileError, - ConversionError, - ValidationError, - RequiredError, - RequiresError, - ExcludesError, - ExtrasError, - ConfigError, - InvalidError, - HorribleError, - OptionNotFound, - ArgumentMismatch, - BaseClass = 127 + Success = 0, + IncorrectConstruction = 100, + BadNameString, + OptionAlreadyAdded, + FileError, + ConversionError, + ValidationError, + RequiredError, + RequiresError, + ExcludesError, + ExtrasError, + ConfigError, + InvalidError, + HorribleError, + OptionNotFound, + ArgumentMismatch, + BaseClass = 127 }; // Error definitions @@ -1343,291 +1428,352 @@ enum class ExitCodes { /// @defgroup error_group Errors /// @brief Errors thrown by CLI11 /// -/// These are the errors that can be thrown. Some of them, like CLI::Success, are not really errors. +/// These are the errors that can be thrown. Some of them, like CLI::Success, +/// are not really errors. /// @{ /// All errors derive from this one class Error : public std::runtime_error { - int actual_exit_code; - std::string error_name{"Error"}; + int actual_exit_code; + std::string error_name{"Error"}; - public: - CLI11_NODISCARD int get_exit_code() const { return actual_exit_code; } +public: + CLI11_NODISCARD int get_exit_code() const { return actual_exit_code; } - CLI11_NODISCARD std::string get_name() const { return error_name; } + CLI11_NODISCARD std::string get_name() const { return error_name; } - Error(std::string name, std::string msg, int exit_code = static_cast(ExitCodes::BaseClass)) - : runtime_error(msg), actual_exit_code(exit_code), error_name(std::move(name)) {} + Error(std::string name, std::string msg, + int exit_code = static_cast(ExitCodes::BaseClass)) + : runtime_error(msg), actual_exit_code(exit_code), + error_name(std::move(name)) {} - Error(std::string name, std::string msg, ExitCodes exit_code) : Error(name, msg, static_cast(exit_code)) {} + Error(std::string name, std::string msg, ExitCodes exit_code) + : Error(name, msg, static_cast(exit_code)) {} }; // Note: Using Error::Error constructors does not work on GCC 4.7 /// Construction errors (not in parsing) class ConstructionError : public Error { - CLI11_ERROR_DEF(Error, ConstructionError) + CLI11_ERROR_DEF(Error, ConstructionError) }; -/// Thrown when an option is set to conflicting values (non-vector and multi args, for example) +/// Thrown when an option is set to conflicting values (non-vector and multi +/// args, for example) class IncorrectConstruction : public ConstructionError { - CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) - CLI11_ERROR_SIMPLE(IncorrectConstruction) - static IncorrectConstruction PositionalFlag(std::string name) { - return IncorrectConstruction(name + ": Flags cannot be positional"); - } - static IncorrectConstruction Set0Opt(std::string name) { - return IncorrectConstruction(name + ": Cannot set 0 expected, use a flag instead"); - } - static IncorrectConstruction SetFlag(std::string name) { - return IncorrectConstruction(name + ": Cannot set an expected number for flags"); - } - static IncorrectConstruction ChangeNotVector(std::string name) { - return IncorrectConstruction(name + ": You can only change the expected arguments for vectors"); - } - static IncorrectConstruction AfterMultiOpt(std::string name) { - return IncorrectConstruction( - name + ": You can't change expected arguments after you've changed the multi option policy!"); - } - static IncorrectConstruction MissingOption(std::string name) { - return IncorrectConstruction("Option " + name + " is not defined"); - } - static IncorrectConstruction MultiOptionPolicy(std::string name) { - return IncorrectConstruction(name + ": multi_option_policy only works for flags and exact value options"); - } + CLI11_ERROR_DEF(ConstructionError, IncorrectConstruction) + CLI11_ERROR_SIMPLE(IncorrectConstruction) + static IncorrectConstruction PositionalFlag(std::string name) { + return IncorrectConstruction(name + ": Flags cannot be positional"); + } + static IncorrectConstruction Set0Opt(std::string name) { + return IncorrectConstruction(name + + ": Cannot set 0 expected, use a flag instead"); + } + static IncorrectConstruction SetFlag(std::string name) { + return IncorrectConstruction(name + + ": Cannot set an expected number for flags"); + } + static IncorrectConstruction ChangeNotVector(std::string name) { + return IncorrectConstruction( + name + ": You can only change the expected arguments for vectors"); + } + static IncorrectConstruction AfterMultiOpt(std::string name) { + return IncorrectConstruction(name + + ": You can't change expected arguments after " + "you've changed the multi option policy!"); + } + static IncorrectConstruction MissingOption(std::string name) { + return IncorrectConstruction("Option " + name + " is not defined"); + } + static IncorrectConstruction MultiOptionPolicy(std::string name) { + return IncorrectConstruction( + name + + ": multi_option_policy only works for flags and exact value options"); + } }; /// Thrown on construction of a bad name class BadNameString : public ConstructionError { - CLI11_ERROR_DEF(ConstructionError, BadNameString) - CLI11_ERROR_SIMPLE(BadNameString) - static BadNameString OneCharName(std::string name) { return BadNameString("Invalid one char name: " + name); } - static BadNameString MissingDash(std::string name) { - return BadNameString("Long names strings require 2 dashes " + name); - } - static BadNameString BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } - static BadNameString BadPositionalName(std::string name) { - return BadNameString("Invalid positional Name: " + name); - } - static BadNameString ReservedName(std::string name) { - return BadNameString("Names '-','--','++' are reserved and not allowed as option names " + name); - } - static BadNameString MultiPositionalNames(std::string name) { - return BadNameString("Only one positional name allowed, remove: " + name); - } + CLI11_ERROR_DEF(ConstructionError, BadNameString) + CLI11_ERROR_SIMPLE(BadNameString) + static BadNameString OneCharName(std::string name) { + return BadNameString("Invalid one char name: " + name); + } + static BadNameString MissingDash(std::string name) { + return BadNameString("Long names strings require 2 dashes " + name); + } + static BadNameString BadLongName(std::string name) { + return BadNameString("Bad long name: " + name); + } + static BadNameString BadPositionalName(std::string name) { + return BadNameString("Invalid positional Name: " + name); + } + static BadNameString ReservedName(std::string name) { + return BadNameString( + "Names '-','--','++' are reserved and not allowed as option names " + + name); + } + static BadNameString MultiPositionalNames(std::string name) { + return BadNameString("Only one positional name allowed, remove: " + name); + } }; /// Thrown when an option already exists class OptionAlreadyAdded : public ConstructionError { - CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) - explicit OptionAlreadyAdded(std::string name) - : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} - static OptionAlreadyAdded Requires(std::string name, std::string other) { - return {name + " requires " + other, ExitCodes::OptionAlreadyAdded}; - } - static OptionAlreadyAdded Excludes(std::string name, std::string other) { - return {name + " excludes " + other, ExitCodes::OptionAlreadyAdded}; - } + CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded) + explicit OptionAlreadyAdded(std::string name) + : OptionAlreadyAdded(name + " is already added", + ExitCodes::OptionAlreadyAdded) {} + static OptionAlreadyAdded Requires(std::string name, std::string other) { + return {name + " requires " + other, ExitCodes::OptionAlreadyAdded}; + } + static OptionAlreadyAdded Excludes(std::string name, std::string other) { + return {name + " excludes " + other, ExitCodes::OptionAlreadyAdded}; + } }; // Parsing errors /// Anything that can error in Parse class ParseError : public Error { - CLI11_ERROR_DEF(Error, ParseError) + CLI11_ERROR_DEF(Error, ParseError) }; // Not really "errors" /// This is a successful completion on parsing, supposed to exit class Success : public ParseError { - CLI11_ERROR_DEF(ParseError, Success) - Success() : Success("Successfully completed, should be caught and quit", ExitCodes::Success) {} + CLI11_ERROR_DEF(ParseError, Success) + Success() + : Success("Successfully completed, should be caught and quit", + ExitCodes::Success) {} }; /// -h or --help on command line class CallForHelp : public Success { - CLI11_ERROR_DEF(Success, CallForHelp) - CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} + CLI11_ERROR_DEF(Success, CallForHelp) + CallForHelp() + : CallForHelp("This should be caught in your main function, see examples", + ExitCodes::Success) {} }; /// Usually something like --help-all on command line class CallForAllHelp : public Success { - CLI11_ERROR_DEF(Success, CallForAllHelp) - CallForAllHelp() - : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} + CLI11_ERROR_DEF(Success, CallForAllHelp) + CallForAllHelp() + : CallForAllHelp( + "This should be caught in your main function, see examples", + ExitCodes::Success) {} }; /// -v or --version on command line class CallForVersion : public Success { - CLI11_ERROR_DEF(Success, CallForVersion) - CallForVersion() - : CallForVersion("This should be caught in your main function, see examples", ExitCodes::Success) {} + CLI11_ERROR_DEF(Success, CallForVersion) + CallForVersion() + : CallForVersion( + "This should be caught in your main function, see examples", + ExitCodes::Success) {} }; -/// Does not output a diagnostic in CLI11_PARSE, but allows main() to return with a specific error code. +/// Does not output a diagnostic in CLI11_PARSE, but allows main() to return +/// with a specific error code. class RuntimeError : public ParseError { - CLI11_ERROR_DEF(ParseError, RuntimeError) - explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {} + CLI11_ERROR_DEF(ParseError, RuntimeError) + explicit RuntimeError(int exit_code = 1) + : RuntimeError("Runtime error", exit_code) {} }; /// Thrown when parsing an INI file and it is missing class FileError : public ParseError { - CLI11_ERROR_DEF(ParseError, FileError) - CLI11_ERROR_SIMPLE(FileError) - static FileError Missing(std::string name) { return FileError(name + " was not readable (missing?)"); } + CLI11_ERROR_DEF(ParseError, FileError) + CLI11_ERROR_SIMPLE(FileError) + static FileError Missing(std::string name) { + return FileError(name + " was not readable (missing?)"); + } }; -/// Thrown when conversion call back fails, such as when an int fails to coerce to a string +/// Thrown when conversion call back fails, such as when an int fails to coerce +/// to a string class ConversionError : public ParseError { - CLI11_ERROR_DEF(ParseError, ConversionError) - CLI11_ERROR_SIMPLE(ConversionError) - ConversionError(std::string member, std::string name) - : ConversionError("The value " + member + " is not an allowed value for " + name) {} - ConversionError(std::string name, std::vector results) - : ConversionError("Could not convert: " + name + " = " + detail::join(results)) {} - static ConversionError TooManyInputsFlag(std::string name) { - return ConversionError(name + ": too many inputs for a flag"); - } - static ConversionError TrueFalse(std::string name) { - return ConversionError(name + ": Should be true/false or a number"); - } + CLI11_ERROR_DEF(ParseError, ConversionError) + CLI11_ERROR_SIMPLE(ConversionError) + ConversionError(std::string member, std::string name) + : ConversionError("The value " + member + + " is not an allowed value for " + name) {} + ConversionError(std::string name, std::vector results) + : ConversionError("Could not convert: " + name + " = " + + detail::join(results)) {} + static ConversionError TooManyInputsFlag(std::string name) { + return ConversionError(name + ": too many inputs for a flag"); + } + static ConversionError TrueFalse(std::string name) { + return ConversionError(name + ": Should be true/false or a number"); + } }; /// Thrown when validation of results fails class ValidationError : public ParseError { - CLI11_ERROR_DEF(ParseError, ValidationError) - CLI11_ERROR_SIMPLE(ValidationError) - explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {} + CLI11_ERROR_DEF(ParseError, ValidationError) + CLI11_ERROR_SIMPLE(ValidationError) + explicit ValidationError(std::string name, std::string msg) + : ValidationError(name + ": " + msg) {} }; /// Thrown when a required option is missing class RequiredError : public ParseError { - CLI11_ERROR_DEF(ParseError, RequiredError) - explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} - static RequiredError Subcommand(std::size_t min_subcom) { - if(min_subcom == 1) { - return RequiredError("A subcommand"); - } - return {"Requires at least " + std::to_string(min_subcom) + " subcommands", ExitCodes::RequiredError}; + CLI11_ERROR_DEF(ParseError, RequiredError) + explicit RequiredError(std::string name) + : RequiredError(name + " is required", ExitCodes::RequiredError) {} + static RequiredError Subcommand(std::size_t min_subcom) { + if (min_subcom == 1) { + return RequiredError("A subcommand"); } - static RequiredError - Option(std::size_t min_option, std::size_t max_option, std::size_t used, const std::string &option_list) { - if((min_option == 1) && (max_option == 1) && (used == 0)) - return RequiredError("Exactly 1 option from [" + option_list + "]"); - if((min_option == 1) && (max_option == 1) && (used > 1)) { - return {"Exactly 1 option from [" + option_list + "] is required but " + std::to_string(used) + - " were given", - ExitCodes::RequiredError}; - } - if((min_option == 1) && (used == 0)) - return RequiredError("At least 1 option from [" + option_list + "]"); - if(used < min_option) { - return {"Requires at least " + std::to_string(min_option) + " options used but only " + - std::to_string(used) + " were given from [" + option_list + "]", - ExitCodes::RequiredError}; - } - if(max_option == 1) - return {"Requires at most 1 options be given from [" + option_list + "]", ExitCodes::RequiredError}; + return {"Requires at least " + std::to_string(min_subcom) + " subcommands", + ExitCodes::RequiredError}; + } + static RequiredError Option(std::size_t min_option, std::size_t max_option, + std::size_t used, + const std::string &option_list) { + if ((min_option == 1) && (max_option == 1) && (used == 0)) + return RequiredError("Exactly 1 option from [" + option_list + "]"); + if ((min_option == 1) && (max_option == 1) && (used > 1)) { + return {"Exactly 1 option from [" + option_list + "] is required but " + + std::to_string(used) + " were given", + ExitCodes::RequiredError}; + } + if ((min_option == 1) && (used == 0)) + return RequiredError("At least 1 option from [" + option_list + "]"); + if (used < min_option) { + return {"Requires at least " + std::to_string(min_option) + + " options used but only " + std::to_string(used) + + " were given from [" + option_list + "]", + ExitCodes::RequiredError}; + } + if (max_option == 1) + return {"Requires at most 1 options be given from [" + option_list + "]", + ExitCodes::RequiredError}; - return {"Requires at most " + std::to_string(max_option) + " options be used but " + std::to_string(used) + - " were given from [" + option_list + "]", - ExitCodes::RequiredError}; - } + return {"Requires at most " + std::to_string(max_option) + + " options be used but " + std::to_string(used) + + " were given from [" + option_list + "]", + ExitCodes::RequiredError}; + } }; /// Thrown when the wrong number of arguments has been received class ArgumentMismatch : public ParseError { - CLI11_ERROR_DEF(ParseError, ArgumentMismatch) - CLI11_ERROR_SIMPLE(ArgumentMismatch) - ArgumentMismatch(std::string name, int expected, std::size_t received) - : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + - ", got " + std::to_string(received)) - : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + - ", got " + std::to_string(received)), - ExitCodes::ArgumentMismatch) {} + CLI11_ERROR_DEF(ParseError, ArgumentMismatch) + CLI11_ERROR_SIMPLE(ArgumentMismatch) + ArgumentMismatch(std::string name, int expected, std::size_t received) + : ArgumentMismatch(expected > 0 + ? ("Expected exactly " + std::to_string(expected) + + " arguments to " + name + ", got " + + std::to_string(received)) + : ("Expected at least " + + std::to_string(-expected) + " arguments to " + + name + ", got " + std::to_string(received)), + ExitCodes::ArgumentMismatch) {} - static ArgumentMismatch AtLeast(std::string name, int num, std::size_t received) { - return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required but received " + - std::to_string(received)); - } - static ArgumentMismatch AtMost(std::string name, int num, std::size_t received) { - return ArgumentMismatch(name + ": At Most " + std::to_string(num) + " required but received " + - std::to_string(received)); - } - static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { - return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); - } - static ArgumentMismatch FlagOverride(std::string name) { - return ArgumentMismatch(name + " was given a disallowed flag override"); - } - static ArgumentMismatch PartialType(std::string name, int num, std::string type) { - return ArgumentMismatch(name + ": " + type + " only partially specified: " + std::to_string(num) + - " required for each element"); - } + static ArgumentMismatch AtLeast(std::string name, int num, + std::size_t received) { + return ArgumentMismatch(name + ": At least " + std::to_string(num) + + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch AtMost(std::string name, int num, + std::size_t received) { + return ArgumentMismatch(name + ": At Most " + std::to_string(num) + + " required but received " + + std::to_string(received)); + } + static ArgumentMismatch TypedAtLeast(std::string name, int num, + std::string type) { + return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + + type + " missing"); + } + static ArgumentMismatch FlagOverride(std::string name) { + return ArgumentMismatch(name + " was given a disallowed flag override"); + } + static ArgumentMismatch PartialType(std::string name, int num, + std::string type) { + return ArgumentMismatch(name + ": " + type + " only partially specified: " + + std::to_string(num) + " required for each element"); + } }; /// Thrown when a requires option is missing class RequiresError : public ParseError { - CLI11_ERROR_DEF(ParseError, RequiresError) - RequiresError(std::string curname, std::string subname) - : RequiresError(curname + " requires " + subname, ExitCodes::RequiresError) {} + CLI11_ERROR_DEF(ParseError, RequiresError) + RequiresError(std::string curname, std::string subname) + : RequiresError(curname + " requires " + subname, + ExitCodes::RequiresError) {} }; /// Thrown when an excludes option is present class ExcludesError : public ParseError { - CLI11_ERROR_DEF(ParseError, ExcludesError) - ExcludesError(std::string curname, std::string subname) - : ExcludesError(curname + " excludes " + subname, ExitCodes::ExcludesError) {} + CLI11_ERROR_DEF(ParseError, ExcludesError) + ExcludesError(std::string curname, std::string subname) + : ExcludesError(curname + " excludes " + subname, + ExitCodes::ExcludesError) {} }; /// Thrown when too many positionals or options are found class ExtrasError : public ParseError { - CLI11_ERROR_DEF(ParseError, ExtrasError) - explicit ExtrasError(std::vector args) - : ExtrasError((args.size() > 1 ? "The following arguments were not expected: " - : "The following argument was not expected: ") + - detail::rjoin(args, " "), - ExitCodes::ExtrasError) {} - ExtrasError(const std::string &name, std::vector args) - : ExtrasError(name, - (args.size() > 1 ? "The following arguments were not expected: " - : "The following argument was not expected: ") + - detail::rjoin(args, " "), - ExitCodes::ExtrasError) {} + CLI11_ERROR_DEF(ParseError, ExtrasError) + explicit ExtrasError(std::vector args) + : ExtrasError((args.size() > 1 + ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} + ExtrasError(const std::string &name, std::vector args) + : ExtrasError(name, + (args.size() > 1 + ? "The following arguments were not expected: " + : "The following argument was not expected: ") + + detail::rjoin(args, " "), + ExitCodes::ExtrasError) {} }; /// Thrown when extra values are found in an INI file class ConfigError : public ParseError { - CLI11_ERROR_DEF(ParseError, ConfigError) - CLI11_ERROR_SIMPLE(ConfigError) - static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); } - static ConfigError NotConfigurable(std::string item) { - return ConfigError(item + ": This option is not allowed in a configuration file"); - } + CLI11_ERROR_DEF(ParseError, ConfigError) + CLI11_ERROR_SIMPLE(ConfigError) + static ConfigError Extras(std::string item) { + return ConfigError("INI was not able to parse " + item); + } + static ConfigError NotConfigurable(std::string item) { + return ConfigError(item + + ": This option is not allowed in a configuration file"); + } }; /// Thrown when validation fails before parsing class InvalidError : public ParseError { - CLI11_ERROR_DEF(ParseError, InvalidError) - explicit InvalidError(std::string name) - : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) { - } + CLI11_ERROR_DEF(ParseError, InvalidError) + explicit InvalidError(std::string name) + : InvalidError( + name + + ": Too many positional arguments with unlimited expected args", + ExitCodes::InvalidError) {} }; -/// This is just a safety check to verify selection and parsing match - you should not ever see it -/// Strings are directly added to this error, but again, it should never be seen. +/// This is just a safety check to verify selection and parsing match - you +/// should not ever see it Strings are directly added to this error, but again, +/// it should never be seen. class HorribleError : public ParseError { - CLI11_ERROR_DEF(ParseError, HorribleError) - CLI11_ERROR_SIMPLE(HorribleError) + CLI11_ERROR_DEF(ParseError, HorribleError) + CLI11_ERROR_SIMPLE(HorribleError) }; // After parsing /// Thrown when counting a nonexistent option class OptionNotFound : public Error { - CLI11_ERROR_DEF(Error, OptionNotFound) - explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} + CLI11_ERROR_DEF(Error, OptionNotFound) + explicit OptionNotFound(std::string name) + : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} }; #undef CLI11_ERROR_DEF @@ -1635,9 +1781,6 @@ class OptionNotFound : public Error { /// @} - - - // Type tools // Utilities for type enabling @@ -1648,25 +1791,30 @@ enum class enabler {}; /// An instance to use in EnableIf constexpr enabler dummy = {}; -} // namespace detail +} // namespace detail /// A copy of enable_if_t from C++14, compatible with C++11. /// -/// We could check to see if C++14 is being used, but it does not hurt to redefine this -/// (even Google does this: https://github.com/google/skia/blob/main/include/private/SkTLogic.h) -/// It is not in the std namespace anyway, so no harm done. -template using enable_if_t = typename std::enable_if::type; +/// We could check to see if C++14 is being used, but it does not hurt to +/// redefine this (even Google does this: +/// https://github.com/google/skia/blob/main/include/private/SkTLogic.h) It is +/// not in the std namespace anyway, so no harm done. +template +using enable_if_t = typename std::enable_if::type; /// A copy of std::void_t from C++17 (helper for C++11 and C++14) template struct make_void { - using type = void; + using type = void; }; -/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does not hurt to redefine +/// A copy of std::void_t from C++17 - same reasoning as enable_if_t, it does +/// not hurt to redefine template using void_t = typename make_void::type; -/// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it does not hurt to redefine -template using conditional_t = typename std::conditional::type; +/// A copy of std::conditional_t from C++14 - same reasoning as enable_if_t, it +/// does not hurt to redefine +template +using conditional_t = typename std::conditional::type; /// Check to see if something is bool (fail check by default) template struct is_bool : std::false_type {}; @@ -1677,117 +1825,142 @@ template <> struct is_bool : std::true_type {}; /// Check to see if something is a shared pointer template struct is_shared_ptr : std::false_type {}; -/// Check to see if something is a shared pointer (True if really a shared pointer) -template struct is_shared_ptr> : std::true_type {}; +/// Check to see if something is a shared pointer (True if really a shared +/// pointer) +template +struct is_shared_ptr> : std::true_type {}; -/// Check to see if something is a shared pointer (True if really a shared pointer) -template struct is_shared_ptr> : std::true_type {}; +/// Check to see if something is a shared pointer (True if really a shared +/// pointer) +template +struct is_shared_ptr> : std::true_type {}; /// Check to see if something is copyable pointer template struct is_copyable_ptr { - static bool const value = is_shared_ptr::value || std::is_pointer::value; + static bool const value = + is_shared_ptr::value || std::is_pointer::value; }; /// This can be specialized to override the type deduction for IsMember. template struct IsMemberType { - using type = T; + using type = T; }; /// The main custom type needed here is const char * should be a string. template <> struct IsMemberType { - using type = std::string; + using type = std::string; }; namespace adl_detail { /// Check for existence of user-supplied lexical_cast. /// -/// This struct has to be in a separate namespace so that it doesn't see our lexical_cast overloads in CLI::detail. -/// Standard says it shouldn't see them if it's defined before the corresponding lexical_cast declarations, but this -/// requires a working implementation of two-phase lookup, and not all compilers can boast that (msvc, ahem). +/// This struct has to be in a separate namespace so that it doesn't see our +/// lexical_cast overloads in CLI::detail. Standard says it shouldn't see them +/// if it's defined before the corresponding lexical_cast declarations, but this +/// requires a working implementation of two-phase lookup, and not all compilers +/// can boast that (msvc, ahem). template class is_lexical_castable { - template - static auto test(int) -> decltype(lexical_cast(std::declval(), std::declval()), std::true_type()); + template + static auto test(int) -> decltype(lexical_cast(std::declval(), + std::declval()), + std::true_type()); - template static auto test(...) -> std::false_type; + template static auto test(...) -> std::false_type; - public: - static constexpr bool value = decltype(test(0))::value; +public: + static constexpr bool value = decltype(test(0))::value; }; -} // namespace adl_detail +} // namespace adl_detail namespace detail { // These are utilities for IsMember and other transforming objects -/// Handy helper to access the element_type generically. This is not part of is_copyable_ptr because it requires that -/// pointer_traits be valid. +/// Handy helper to access the element_type generically. This is not part of +/// is_copyable_ptr because it requires that pointer_traits be valid. /// not a pointer template struct element_type { - using type = T; + using type = T; }; -template struct element_type::value>::type> { - using type = typename std::pointer_traits::element_type; -}; - -/// Combination of the element type and value type - remove pointer (including smart pointers) and get the value_type of -/// the container -template struct element_value_type { - using type = typename element_type::type::value_type; -}; - -/// Adaptor for set-like structure: This just wraps a normal container in a few utilities that do almost nothing. -template struct pair_adaptor : std::false_type { - using value_type = typename T::value_type; - using first_type = typename std::remove_const::type; - using second_type = typename std::remove_const::type; - - /// Get the first value (really just the underlying value) - template static auto first(Q &&pair_value) -> decltype(std::forward(pair_value)) { - return std::forward(pair_value); - } - /// Get the second value (really just the underlying value) - template static auto second(Q &&pair_value) -> decltype(std::forward(pair_value)) { - return std::forward(pair_value); - } -}; - -/// Adaptor for map-like structure (true version, must have key_type and mapped_type). -/// This wraps a mapped container in a few utilities access it in a general way. template -struct pair_adaptor< - T, - conditional_t, void>> - : std::true_type { - using value_type = typename T::value_type; - using first_type = typename std::remove_const::type; - using second_type = typename std::remove_const::type; - - /// Get the first value (really just the underlying value) - template static auto first(Q &&pair_value) -> decltype(std::get<0>(std::forward(pair_value))) { - return std::get<0>(std::forward(pair_value)); - } - /// Get the second value (really just the underlying value) - template static auto second(Q &&pair_value) -> decltype(std::get<1>(std::forward(pair_value))) { - return std::get<1>(std::forward(pair_value)); - } +struct element_type::value>::type> { + using type = typename std::pointer_traits::element_type; }; -// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled that generates a -Wnarrowing warning -// in the unevaluated context even if the function that was using this wasn't used. The standard says narrowing in -// brace initialization shouldn't be allowed but for backwards compatibility gcc allows it in some contexts. It is a -// little fuzzy what happens in template constructs and I think that was something GCC took a little while to work out. -// But regardless some versions of gcc generate a warning when they shouldn't from the following code so that should be -// suppressed +/// Combination of the element type and value type - remove pointer (including +/// smart pointers) and get the value_type of the container +template struct element_value_type { + using type = typename element_type::type::value_type; +}; + +/// Adaptor for set-like structure: This just wraps a normal container in a few +/// utilities that do almost nothing. +template struct pair_adaptor : std::false_type { + using value_type = typename T::value_type; + using first_type = typename std::remove_const::type; + using second_type = typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template + static auto first(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } + /// Get the second value (really just the underlying value) + template + static auto second(Q &&pair_value) -> decltype(std::forward(pair_value)) { + return std::forward(pair_value); + } +}; + +/// Adaptor for map-like structure (true version, must have key_type and +/// mapped_type). This wraps a mapped container in a few utilities access it in +/// a general way. +template +struct pair_adaptor, + void>> : std::true_type { + using value_type = typename T::value_type; + using first_type = + typename std::remove_const::type; + using second_type = + typename std::remove_const::type; + + /// Get the first value (really just the underlying value) + template + static auto first(Q &&pair_value) + -> decltype(std::get<0>(std::forward(pair_value))) { + return std::get<0>(std::forward(pair_value)); + } + /// Get the second value (really just the underlying value) + template + static auto second(Q &&pair_value) + -> decltype(std::get<1>(std::forward(pair_value))) { + return std::get<1>(std::forward(pair_value)); + } +}; + +// Warning is suppressed due to "bug" in gcc<5.0 and gcc 7.0 with c++17 enabled +// that generates a -Wnarrowing warning in the unevaluated context even if the +// function that was using this wasn't used. The standard says narrowing in +// brace initialization shouldn't be allowed but for backwards compatibility gcc +// allows it in some contexts. It is a little fuzzy what happens in template +// constructs and I think that was something GCC took a little while to work +// out. But regardless some versions of gcc generate a warning when they +// shouldn't from the following code so that should be suppressed #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnarrowing" #endif -// check for constructibility from a specific type and copy assignable used in the parse detection +// check for constructibility from a specific type and copy assignable used in +// the parse detection template class is_direct_constructible { - template - static auto test(int, std::true_type) -> decltype( + template + static auto test(int, std::true_type) -> decltype( // NVCC warns about narrowing conversions here #ifdef __CUDACC__ #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ @@ -1796,7 +1969,7 @@ template class is_direct_constructible { #pragma diag_suppress 2361 #endif #endif - TT{std::declval()} + TT{std::declval()} #ifdef __CUDACC__ #ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ #pragma nv_diag_default 2361 @@ -1804,303 +1977,364 @@ template class is_direct_constructible { #pragma diag_default 2361 #endif #endif - , - std::is_move_assignable()); + , + std::is_move_assignable()); - template static auto test(int, std::false_type) -> std::false_type; + template + static auto test(int, std::false_type) -> std::false_type; - template static auto test(...) -> std::false_type; + template static auto test(...) -> std::false_type; - public: - static constexpr bool value = decltype(test(0, typename std::is_constructible::type()))::value; +public: + static constexpr bool value = decltype(test( + 0, typename std::is_constructible::type()))::value; }; #ifdef __GNUC__ #pragma GCC diagnostic pop #endif // Check for output streamability -// Based on https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream +// Based on +// https://stackoverflow.com/questions/22758291/how-can-i-detect-if-a-type-can-be-streamed-to-an-stdostream template class is_ostreamable { - template - static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); - template static auto test(...) -> std::false_type; + template static auto test(...) -> std::false_type; - public: - static constexpr bool value = decltype(test(0))::value; +public: + static constexpr bool value = decltype(test(0))::value; }; /// Check for input streamability template class is_istreamable { - template - static auto test(int) -> decltype(std::declval() >> std::declval(), std::true_type()); + template + static auto test(int) + -> decltype(std::declval() >> std::declval(), + std::true_type()); - template static auto test(...) -> std::false_type; + template static auto test(...) -> std::false_type; - public: - static constexpr bool value = decltype(test(0))::value; +public: + static constexpr bool value = decltype(test(0))::value; }; /// Check for complex template class is_complex { - template - static auto test(int) -> decltype(std::declval().real(), std::declval().imag(), std::true_type()); + template + static auto test(int) + -> decltype(std::declval().real(), std::declval().imag(), + std::true_type()); - template static auto test(...) -> std::false_type; + template static auto test(...) -> std::false_type; - public: - static constexpr bool value = decltype(test(0))::value; +public: + static constexpr bool value = decltype(test(0))::value; }; /// Templated operation to get a value from a stream -template ::value, detail::enabler> = detail::dummy> +template ::value, detail::enabler> = + detail::dummy> bool from_stream(const std::string &istring, T &obj) { - std::istringstream is; - is.str(istring); - is >> obj; - return !is.fail() && !is.rdbuf()->in_avail(); + std::istringstream is; + is.str(istring); + is >> obj; + return !is.fail() && !is.rdbuf()->in_avail(); } -template ::value, detail::enabler> = detail::dummy> +template ::value, detail::enabler> = + detail::dummy> bool from_stream(const std::string & /*istring*/, T & /*obj*/) { - return false; + return false; } // check to see if an object is a mutable container (fail by default) -template struct is_mutable_container : std::false_type {}; +template +struct is_mutable_container : std::false_type {}; -/// type trait to test if a type is a mutable container meaning it has a value_type, it has an iterator, a clear, and -/// end methods and an insert function. And for our purposes we exclude std::string and types that can be constructed -/// from a std::string +/// type trait to test if a type is a mutable container meaning it has a +/// value_type, it has an iterator, a clear, and end methods and an insert +/// function. And for our purposes we exclude std::string and types that can be +/// constructed from a std::string template struct is_mutable_container< - T, - conditional_t().end()), - decltype(std::declval().clear()), - decltype(std::declval().insert(std::declval().end())>(), - std::declval()))>, - void>> : public conditional_t::value || - std::is_constructible::value, - std::false_type, - std::true_type> {}; + T, conditional_t< + false, + void_t().end()), + decltype(std::declval().clear()), + decltype(std::declval().insert( + std::declval().end())>(), + std::declval()))>, + void>> + : public conditional_t::value || + std::is_constructible::value, + std::false_type, std::true_type> {}; // check to see if an object is a mutable container (fail by default) -template struct is_readable_container : std::false_type {}; +template +struct is_readable_container : std::false_type {}; -/// type trait to test if a type is a container meaning it has a value_type, it has an iterator, and an end -/// method. +/// type trait to test if a type is a container meaning it has a value_type, it +/// has an iterator, and an end method. template struct is_readable_container< - T, - conditional_t().end()), decltype(std::declval().begin())>, void>> - : public std::true_type {}; + T, conditional_t().end()), + decltype(std::declval().begin())>, + void>> : public std::true_type {}; // check to see if an object is a wrapper (fail by default) template struct is_wrapper : std::false_type {}; // check if an object is a wrapper (it has a value_type defined) template -struct is_wrapper, void>> : public std::true_type {}; +struct is_wrapper, void>> + : public std::true_type {}; // Check for tuple like types, as in classes with a tuple_size type trait -// Even though in C++26 std::complex gains a std::tuple interface, for our purposes we treat is as NOT a tuple +// Even though in C++26 std::complex gains a std::tuple interface, for our +// purposes we treat is as NOT a tuple template class is_tuple_like { - template ::value, detail::enabler> = detail::dummy> - // static auto test(int) - // -> decltype(std::conditional<(std::tuple_size::value > 0), std::true_type, std::false_type>::type()); - static auto test(int) -> decltype(std::tuple_size::type>::value, std::true_type{}); - template static auto test(...) -> std::false_type; + template ::value, detail::enabler> = + detail::dummy> + // static auto test(int) + // -> decltype(std::conditional<(std::tuple_size::value > 0), + // std::true_type, std::false_type>::type()); + static auto test(int) + -> decltype(std::tuple_size::type>::value, + std::true_type{}); + template static auto test(...) -> std::false_type; - public: - static constexpr bool value = decltype(test(0))::value; +public: + static constexpr bool value = decltype(test(0))::value; }; /// This will only trigger for actual void type template struct type_count_base { - static const int value{0}; + static const int value{0}; }; /// Type size for regular object types that do not look like a tuple template -struct type_count_base::value && !is_mutable_container::value && - !std::is_void::value>::type> { - static constexpr int value{1}; +struct type_count_base< + T, typename std::enable_if::value && + !is_mutable_container::value && + !std::is_void::value>::type> { + static constexpr int value{1}; }; /// the base tuple size template -struct type_count_base::value && !is_mutable_container::value>::type> { - static constexpr int value{// cppcheck-suppress unusedStructMember - std::tuple_size::type>::value}; +struct type_count_base< + T, typename std::enable_if::value && + !is_mutable_container::value>::type> { + static constexpr int value{ + // cppcheck-suppress unusedStructMember + std::tuple_size::type>::value}; }; -/// Type count base for containers is the type_count_base of the individual element -template struct type_count_base::value>::type> { - static constexpr int value{type_count_base::value}; +/// Type count base for containers is the type_count_base of the individual +/// element +template +struct type_count_base< + T, typename std::enable_if::value>::type> { + static constexpr int value{type_count_base::value}; }; /// Convert an object to a string (directly forward if this can become a string) -template ::value, detail::enabler> = detail::dummy> +template ::value, + detail::enabler> = detail::dummy> auto to_string(T &&value) -> decltype(std::forward(value)) { - return std::forward(value); + return std::forward(value); } /// Construct a string from the object template ::value && !std::is_convertible::value, + enable_if_t::value && + !std::is_convertible::value, detail::enabler> = detail::dummy> std::string to_string(T &&value) { - return std::string(value); // NOLINT(google-readability-casting) + return std::string(value); // NOLINT(google-readability-casting) } /// Convert an object to a string (streaming must be supported for that type) template ::value && !std::is_constructible::value && + enable_if_t::value && + !std::is_constructible::value && is_ostreamable::value, detail::enabler> = detail::dummy> std::string to_string(T &&value) { - std::stringstream stream; - stream << value; - return stream.str(); + std::stringstream stream; + stream << value; + return stream.str(); } // additional forward declarations /// Print tuple value string for tuples of size ==1 -template ::value && !std::is_constructible::value && - !is_ostreamable::value && is_tuple_like::value && type_count_base::value == 1, - detail::enabler> = detail::dummy> +template < + typename T, + enable_if_t::value && + !std::is_constructible::value && + !is_ostreamable::value && is_tuple_like::value && + type_count_base::value == 1, + detail::enabler> = detail::dummy> inline std::string to_string(T &&value); /// Print tuple value string for tuples of size > 1 -template ::value && !std::is_constructible::value && - !is_ostreamable::value && is_tuple_like::value && type_count_base::value >= 2, - detail::enabler> = detail::dummy> -inline std::string to_string(T &&value); - -/// If conversion is not supported, return an empty string (streaming is not supported for that type) template < typename T, - enable_if_t::value && !std::is_constructible::value && - !is_ostreamable::value && !is_readable_container::type>::value && - !is_tuple_like::value, + enable_if_t::value && + !std::is_constructible::value && + !is_ostreamable::value && is_tuple_like::value && + type_count_base::value >= 2, detail::enabler> = detail::dummy> +inline std::string to_string(T &&value); + +/// If conversion is not supported, return an empty string (streaming is not +/// supported for that type) +template ::value && + !std::is_constructible::value && + !is_ostreamable::value && + !is_readable_container< + typename std::remove_const::type>::value && + !is_tuple_like::value, + detail::enabler> = detail::dummy> inline std::string to_string(T &&) { - return {}; + return {}; } /// convert a readable container to a string template ::value && !std::is_constructible::value && - !is_ostreamable::value && is_readable_container::value, + enable_if_t::value && + !std::is_constructible::value && + !is_ostreamable::value && + is_readable_container::value, detail::enabler> = detail::dummy> inline std::string to_string(T &&variable) { - auto cval = variable.begin(); - auto end = variable.end(); - if(cval == end) { - return {"{}"}; - } - std::vector defaults; - while(cval != end) { - defaults.emplace_back(CLI::detail::to_string(*cval)); - ++cval; - } - return {"[" + detail::join(defaults) + "]"}; + auto cval = variable.begin(); + auto end = variable.end(); + if (cval == end) { + return {"{}"}; + } + std::vector defaults; + while (cval != end) { + defaults.emplace_back(CLI::detail::to_string(*cval)); + ++cval; + } + return {"[" + detail::join(defaults) + "]"}; } /// Convert a tuple like object to a string /// forward declarations for tuple_value_strings template -inline typename std::enable_if::value, std::string>::type tuple_value_string(T && /*value*/); +inline + typename std::enable_if::value, std::string>::type + tuple_value_string(T && /*value*/); /// Recursively generate the tuple value string template -inline typename std::enable_if<(I < type_count_base::value), std::string>::type tuple_value_string(T &&value); +inline + typename std::enable_if<(I < type_count_base::value), std::string>::type + tuple_value_string(T &&value); /// Print tuple value string for tuples of size ==1 -template ::value && !std::is_constructible::value && - !is_ostreamable::value && is_tuple_like::value && type_count_base::value == 1, - detail::enabler>> +template < + typename T, + enable_if_t::value && + !std::is_constructible::value && + !is_ostreamable::value && is_tuple_like::value && + type_count_base::value == 1, + detail::enabler>> inline std::string to_string(T &&value) { - return to_string(std::get<0>(value)); + return to_string(std::get<0>(value)); } /// Print tuple value string for tuples of size > 1 -template ::value && !std::is_constructible::value && - !is_ostreamable::value && is_tuple_like::value && type_count_base::value >= 2, - detail::enabler>> +template < + typename T, + enable_if_t::value && + !std::is_constructible::value && + !is_ostreamable::value && is_tuple_like::value && + type_count_base::value >= 2, + detail::enabler>> inline std::string to_string(T &&value) { - auto tname = std::string(1, '[') + tuple_value_string(value); - tname.push_back(']'); - return tname; + auto tname = std::string(1, '[') + tuple_value_string(value); + tname.push_back(']'); + return tname; } /// Empty string if the index > tuple size template -inline typename std::enable_if::value, std::string>::type tuple_value_string(T && /*value*/) { - return std::string{}; +inline + typename std::enable_if::value, std::string>::type + tuple_value_string(T && /*value*/) { + return std::string{}; } /// Recursively generate the tuple value string template -inline typename std::enable_if<(I < type_count_base::value), std::string>::type tuple_value_string(T &&value) { - auto str = std::string{to_string(std::get(value))} + ',' + tuple_value_string(value); - if(str.back() == ',') - str.pop_back(); - return str; +inline + typename std::enable_if<(I < type_count_base::value), std::string>::type + tuple_value_string(T &&value) { + auto str = std::string{to_string(std::get(value))} + ',' + + tuple_value_string(value); + if (str.back() == ',') str.pop_back(); + return str; } /// special template overload -template ::value, detail::enabler> = detail::dummy> -auto checked_to_string(T &&value) -> decltype(to_string(std::forward(value))) { - return to_string(std::forward(value)); +template < + typename T1, typename T2, typename T, + enable_if_t::value, detail::enabler> = detail::dummy> +auto checked_to_string(T &&value) + -> decltype(to_string(std::forward(value))) { + return to_string(std::forward(value)); } /// special template overload -template ::value, detail::enabler> = detail::dummy> +template < + typename T1, typename T2, typename T, + enable_if_t::value, detail::enabler> = detail::dummy> std::string checked_to_string(T &&) { - return std::string{}; + return std::string{}; } /// get a string as a convertible value for arithmetic types -template ::value, detail::enabler> = detail::dummy> +template ::value, + detail::enabler> = detail::dummy> std::string value_string(const T &value) { - return std::to_string(value); + return std::to_string(value); } /// get a string as a convertible value for enumerations -template ::value, detail::enabler> = detail::dummy> +template ::value, detail::enabler> = detail::dummy> std::string value_string(const T &value) { - return std::to_string(static_cast::type>(value)); + return std::to_string( + static_cast::type>(value)); } /// for other types just use the regular to_string function template ::value && !std::is_arithmetic::value, detail::enabler> = detail::dummy> + enable_if_t::value && !std::is_arithmetic::value, + detail::enabler> = detail::dummy> auto value_string(const T &value) -> decltype(to_string(value)) { - return to_string(value); + return to_string(value); } /// template to get the underlying value type if it exists or use a default -template struct wrapped_type { - using type = def; +template +struct wrapped_type { + using type = def; }; /// Type size for regular object types that do not look like a tuple -template struct wrapped_type::value>::type> { - using type = typename T::value_type; +template +struct wrapped_type::value>::type> { + using type = typename T::value_type; }; /// Set of overloads to get the type size of an object @@ -2113,153 +2347,184 @@ template struct subtype_count_min; /// This will only trigger for actual void type template struct type_count { - static const int value{0}; + static const int value{0}; }; /// Type size for regular object types that do not look like a tuple template struct type_count::value && !is_tuple_like::value && !is_complex::value && - !std::is_void::value>::type> { - static constexpr int value{1}; + typename std::enable_if< + !is_wrapper::value && !is_tuple_like::value && + !is_complex::value && !std::is_void::value>::type> { + static constexpr int value{1}; }; /// Type size for complex since it sometimes looks like a wrapper -template struct type_count::value>::type> { - static constexpr int value{2}; -}; - -/// Type size of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) -template struct type_count::value>::type> { - static constexpr int value{subtype_count::value}; -}; - -/// Type size of types that are wrappers,except containers complex and tuples(which can also be wrappers sometimes) template -struct type_count::value && !is_complex::value && !is_tuple_like::value && - !is_mutable_container::value>::type> { - static constexpr int value{type_count::value}; +struct type_count::value>::type> { + static constexpr int value{2}; +}; + +/// Type size of types that are wrappers,except complex and tuples(which can +/// also be wrappers sometimes) +template +struct type_count< + T, typename std::enable_if::value>::type> { + static constexpr int value{subtype_count::value}; +}; + +/// Type size of types that are wrappers,except containers complex and +/// tuples(which can also be wrappers sometimes) +template +struct type_count< + T, typename std::enable_if::value && !is_complex::value && + !is_tuple_like::value && + !is_mutable_container::value>::type> { + static constexpr int value{type_count::value}; }; /// 0 if the index > tuple size template -constexpr typename std::enable_if::value, int>::type tuple_type_size() { - return 0; +constexpr typename std::enable_if::value, int>::type +tuple_type_size() { + return 0; } /// Recursively generate the tuple type name template - constexpr typename std::enable_if < I::value, int>::type tuple_type_size() { - return subtype_count::type>::value + tuple_type_size(); + constexpr typename std::enable_if < + I::value, int>::type tuple_type_size() { + return subtype_count::type>::value + + tuple_type_size(); } -/// Get the type size of the sum of type sizes for all the individual tuple types -template struct type_count::value>::type> { - static constexpr int value{tuple_type_size()}; +/// Get the type size of the sum of type sizes for all the individual tuple +/// types +template +struct type_count::value>::type> { + static constexpr int value{tuple_type_size()}; }; /// definition of subtype count template struct subtype_count { - static constexpr int value{is_mutable_container::value ? expected_max_vector_size : type_count::value}; + static constexpr int value{is_mutable_container::value + ? expected_max_vector_size + : type_count::value}; }; /// This will only trigger for actual void type template struct type_count_min { - static const int value{0}; + static const int value{0}; }; /// Type size for regular object types that do not look like a tuple template struct type_count_min< - T, - typename std::enable_if::value && !is_tuple_like::value && !is_wrapper::value && - !is_complex::value && !std::is_void::value>::type> { - static constexpr int value{type_count::value}; + T, typename std::enable_if::value && + !is_tuple_like::value && + !is_wrapper::value && !is_complex::value && + !std::is_void::value>::type> { + static constexpr int value{type_count::value}; }; /// Type size for complex since it sometimes looks like a wrapper -template struct type_count_min::value>::type> { - static constexpr int value{1}; +template +struct type_count_min::value>::type> { + static constexpr int value{1}; }; -/// Type size min of types that are wrappers,except complex and tuples(which can also be wrappers sometimes) +/// Type size min of types that are wrappers,except complex and tuples(which can +/// also be wrappers sometimes) template struct type_count_min< - T, - typename std::enable_if::value && !is_complex::value && !is_tuple_like::value>::type> { - static constexpr int value{subtype_count_min::value}; + T, typename std::enable_if::value && !is_complex::value && + !is_tuple_like::value>::type> { + static constexpr int value{subtype_count_min::value}; }; /// 0 if the index > tuple size template -constexpr typename std::enable_if::value, int>::type tuple_type_size_min() { - return 0; +constexpr typename std::enable_if::value, int>::type +tuple_type_size_min() { + return 0; } /// Recursively generate the tuple type name template - constexpr typename std::enable_if < I::value, int>::type tuple_type_size_min() { - return subtype_count_min::type>::value + tuple_type_size_min(); + constexpr typename std::enable_if < + I::value, int>::type tuple_type_size_min() { + return subtype_count_min::type>::value + + tuple_type_size_min(); } -/// Get the type size of the sum of type sizes for all the individual tuple types -template struct type_count_min::value>::type> { - static constexpr int value{tuple_type_size_min()}; +/// Get the type size of the sum of type sizes for all the individual tuple +/// types +template +struct type_count_min::value>::type> { + static constexpr int value{tuple_type_size_min()}; }; /// definition of subtype count template struct subtype_count_min { - static constexpr int value{is_mutable_container::value - ? ((type_count::value < expected_max_vector_size) ? type_count::value : 0) - : type_count_min::value}; + static constexpr int value{ + is_mutable_container::value + ? ((type_count::value < expected_max_vector_size) + ? type_count::value + : 0) + : type_count_min::value}; }; /// This will only trigger for actual void type template struct expected_count { - static const int value{0}; + static const int value{0}; }; /// For most types the number of expected items is 1 template struct expected_count::value && !is_wrapper::value && + typename std::enable_if::value && + !is_wrapper::value && !std::is_void::value>::type> { - static constexpr int value{1}; + static constexpr int value{1}; }; /// number of expected items in a vector -template struct expected_count::value>::type> { - static constexpr int value{expected_max_vector_size}; +template +struct expected_count< + T, typename std::enable_if::value>::type> { + static constexpr int value{expected_max_vector_size}; }; /// number of expected items in a vector template -struct expected_count::value && is_wrapper::value>::type> { - static constexpr int value{expected_count::value}; +struct expected_count::value && + is_wrapper::value>::type> { + static constexpr int value{expected_count::value}; }; // Enumeration of the different supported categorizations of objects enum class object_category : int { - char_value = 1, - integral_value = 2, - unsigned_integral = 4, - enumeration = 6, - boolean_value = 8, - floating_point = 10, - number_constructible = 12, - double_constructible = 14, - integer_constructible = 16, - // string like types - string_assignable = 23, - string_constructible = 24, - wstring_assignable = 25, - wstring_constructible = 26, - other = 45, - // special wrapper or container types - wrapper_value = 50, - complex_number = 60, - tuple_value = 70, - container_value = 80, + char_value = 1, + integral_value = 2, + unsigned_integral = 4, + enumeration = 6, + boolean_value = 8, + floating_point = 10, + number_constructible = 12, + double_constructible = 14, + integer_constructible = 16, + // string like types + string_assignable = 23, + string_constructible = 24, + wstring_assignable = 25, + wstring_constructible = 26, + other = 45, + // special wrapper or container types + wrapper_value = 50, + complex_number = 60, + tuple_value = 70, + container_value = 80, }; @@ -2267,165 +2532,197 @@ enum class object_category : int { /// some type that is not otherwise recognized template struct classify_object { - static constexpr object_category value{object_category::other}; + static constexpr object_category value{object_category::other}; }; /// Signed integers template struct classify_object< - T, - typename std::enable_if::value && !std::is_same::value && std::is_signed::value && - !is_bool::value && !std::is_enum::value>::type> { - static constexpr object_category value{object_category::integral_value}; + T, typename std::enable_if::value && + !std::is_same::value && + std::is_signed::value && !is_bool::value && + !std::is_enum::value>::type> { + static constexpr object_category value{object_category::integral_value}; }; /// Unsigned integers template -struct classify_object::value && std::is_unsigned::value && - !std::is_same::value && !is_bool::value>::type> { - static constexpr object_category value{object_category::unsigned_integral}; +struct classify_object< + T, typename std::enable_if< + std::is_integral::value && std::is_unsigned::value && + !std::is_same::value && !is_bool::value>::type> { + static constexpr object_category value{object_category::unsigned_integral}; }; /// single character values template -struct classify_object::value && !std::is_enum::value>::type> { - static constexpr object_category value{object_category::char_value}; +struct classify_object::value && + !std::is_enum::value>::type> { + static constexpr object_category value{object_category::char_value}; }; /// Boolean values -template struct classify_object::value>::type> { - static constexpr object_category value{object_category::boolean_value}; +template +struct classify_object::value>::type> { + static constexpr object_category value{object_category::boolean_value}; }; /// Floats -template struct classify_object::value>::type> { - static constexpr object_category value{object_category::floating_point}; +template +struct classify_object< + T, typename std::enable_if::value>::type> { + static constexpr object_category value{object_category::floating_point}; }; #if defined _MSC_VER -// in MSVC wstring should take precedence if available this isn't as useful on other compilers due to the broader use of -// utf-8 encoding -#define WIDE_STRING_CHECK \ - !std::is_assignable::value && !std::is_constructible::value +// in MSVC wstring should take precedence if available this isn't as useful on +// other compilers due to the broader use of utf-8 encoding +#define WIDE_STRING_CHECK \ + !std::is_assignable::value && \ + !std::is_constructible::value #define STRING_CHECK true #else #define WIDE_STRING_CHECK true -#define STRING_CHECK !std::is_assignable::value && !std::is_constructible::value +#define STRING_CHECK \ + !std::is_assignable::value && \ + !std::is_constructible::value #endif /// String and similar direct assignment template -struct classify_object< - T, - typename std::enable_if::value && !std::is_integral::value && WIDE_STRING_CHECK && - std::is_assignable::value>::type> { - static constexpr object_category value{object_category::string_assignable}; +struct classify_object::value && + !std::is_integral::value && WIDE_STRING_CHECK && + std::is_assignable::value>::type> { + static constexpr object_category value{object_category::string_assignable}; }; /// String and similar constructible and copy assignment template struct classify_object< - T, - typename std::enable_if::value && !std::is_integral::value && - !std::is_assignable::value && (type_count::value == 1) && - WIDE_STRING_CHECK && std::is_constructible::value>::type> { - static constexpr object_category value{object_category::string_constructible}; + T, typename std::enable_if< + !std::is_floating_point::value && !std::is_integral::value && + !std::is_assignable::value && + (type_count::value == 1) && WIDE_STRING_CHECK && + std::is_constructible::value>::type> { + static constexpr object_category value{object_category::string_constructible}; }; /// Wide strings template -struct classify_object::value && !std::is_integral::value && - STRING_CHECK && std::is_assignable::value>::type> { - static constexpr object_category value{object_category::wstring_assignable}; +struct classify_object< + T, + typename std::enable_if< + !std::is_floating_point::value && !std::is_integral::value && + STRING_CHECK && std::is_assignable::value>::type> { + static constexpr object_category value{object_category::wstring_assignable}; }; template struct classify_object< - T, - typename std::enable_if::value && !std::is_integral::value && - !std::is_assignable::value && (type_count::value == 1) && - STRING_CHECK && std::is_constructible::value>::type> { - static constexpr object_category value{object_category::wstring_constructible}; + T, typename std::enable_if< + !std::is_floating_point::value && !std::is_integral::value && + !std::is_assignable::value && + (type_count::value == 1) && STRING_CHECK && + std::is_constructible::value>::type> { + static constexpr object_category value{ + object_category::wstring_constructible}; }; /// Enumerations -template struct classify_object::value>::type> { - static constexpr object_category value{object_category::enumeration}; +template +struct classify_object::value>::type> { + static constexpr object_category value{object_category::enumeration}; }; -template struct classify_object::value>::type> { - static constexpr object_category value{object_category::complex_number}; +template +struct classify_object::value>::type> { + static constexpr object_category value{object_category::complex_number}; }; -/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, -/// vectors, and enumerations +/// Handy helper to contain a bunch of checks that rule out many common types +/// (integers, string like, floating point, vectors, and enumerations template struct uncommon_type { - using type = typename std::conditional< - !std::is_floating_point::value && !std::is_integral::value && - !std::is_assignable::value && !std::is_constructible::value && - !std::is_assignable::value && !std::is_constructible::value && - !is_complex::value && !is_mutable_container::value && !std::is_enum::value, - std::true_type, - std::false_type>::type; - static constexpr bool value = type::value; + using type = typename std::conditional< + !std::is_floating_point::value && !std::is_integral::value && + !std::is_assignable::value && + !std::is_constructible::value && + !std::is_assignable::value && + !std::is_constructible::value && + !is_complex::value && !is_mutable_container::value && + !std::is_enum::value, + std::true_type, std::false_type>::type; + static constexpr bool value = type::value; }; /// wrapper type template -struct classify_object::value && is_wrapper::value && - !is_tuple_like::value && uncommon_type::value)>::type> { - static constexpr object_category value{object_category::wrapper_value}; +struct classify_object< + T, typename std::enable_if<( + !is_mutable_container::value && is_wrapper::value && + !is_tuple_like::value && uncommon_type::value)>::type> { + static constexpr object_category value{object_category::wrapper_value}; }; /// Assignable from double or int template -struct classify_object::value && type_count::value == 1 && - !is_wrapper::value && is_direct_constructible::value && - is_direct_constructible::value>::type> { - static constexpr object_category value{object_category::number_constructible}; +struct classify_object< + T, typename std::enable_if< + uncommon_type::value && type_count::value == 1 && + !is_wrapper::value && is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::number_constructible}; }; /// Assignable from int template -struct classify_object::value && type_count::value == 1 && - !is_wrapper::value && !is_direct_constructible::value && - is_direct_constructible::value>::type> { - static constexpr object_category value{object_category::integer_constructible}; +struct classify_object< + T, + typename std::enable_if< + uncommon_type::value && type_count::value == 1 && + !is_wrapper::value && !is_direct_constructible::value && + is_direct_constructible::value>::type> { + static constexpr object_category value{ + object_category::integer_constructible}; }; /// Assignable from double template -struct classify_object::value && type_count::value == 1 && - !is_wrapper::value && is_direct_constructible::value && - !is_direct_constructible::value>::type> { - static constexpr object_category value{object_category::double_constructible}; +struct classify_object< + T, typename std::enable_if< + uncommon_type::value && type_count::value == 1 && + !is_wrapper::value && is_direct_constructible::value && + !is_direct_constructible::value>::type> { + static constexpr object_category value{object_category::double_constructible}; }; /// Tuple type template struct classify_object< - T, - typename std::enable_if::value && - ((type_count::value >= 2 && !is_wrapper::value) || - (uncommon_type::value && !is_direct_constructible::value && - !is_direct_constructible::value) || - (uncommon_type::value && type_count::value >= 2))>::type> { - static constexpr object_category value{object_category::tuple_value}; - // the condition on this class requires it be like a tuple, but on some compilers (like Xcode) tuples can be - // constructed from just the first element so tuples of can be constructed from a string, which - // could lead to issues so there are two variants of the condition, the first isolates things with a type size >=2 - // mainly to get tuples on Xcode with the exception of wrappers, the second is the main one and just separating out - // those cases that are caught by other object classifications + T, typename std::enable_if< + is_tuple_like::value && + ((type_count::value >= 2 && !is_wrapper::value) || + (uncommon_type::value && + !is_direct_constructible::value && + !is_direct_constructible::value) || + (uncommon_type::value && type_count::value >= 2))>::type> { + static constexpr object_category value{object_category::tuple_value}; + // the condition on this class requires it be like a tuple, but on some + // compilers (like Xcode) tuples can be constructed from just the first + // element so tuples of can be constructed from a string, + // which could lead to issues so there are two variants of the condition, the + // first isolates things with a type size >=2 mainly to get tuples on Xcode + // with the exception of wrappers, the second is the main one and just + // separating out those cases that are caught by other object classifications }; /// container type -template struct classify_object::value>::type> { - static constexpr object_category value{object_category::container_value}; +template +struct classify_object< + T, typename std::enable_if::value>::type> { + static constexpr object_category value{object_category::container_value}; }; // Type name print @@ -2435,1344 +2732,1485 @@ template struct classify_object::value == object_category::char_value, detail::enabler> = detail::dummy> -constexpr const char *type_name() { - return "CHAR"; -} - -template ::value == object_category::integral_value || - classify_object::value == object_category::integer_constructible, + enable_if_t::value == object_category::char_value, detail::enabler> = detail::dummy> constexpr const char *type_name() { - return "INT"; + return "CHAR"; } -template ::value == object_category::unsigned_integral, detail::enabler> = detail::dummy> +template < + typename T, + enable_if_t::value == object_category::integral_value || + classify_object::value == + object_category::integer_constructible, + detail::enabler> = detail::dummy> constexpr const char *type_name() { - return "UINT"; + return "INT"; } +template ::value == + object_category::unsigned_integral, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "UINT"; +} + +template < + typename T, + enable_if_t::value == object_category::floating_point || + classify_object::value == + object_category::number_constructible || + classify_object::value == + object_category::double_constructible, + detail::enabler> = detail::dummy> +constexpr const char *type_name() { + return "FLOAT"; +} + +/// Print name for enumeration types template ::value == object_category::floating_point || - classify_object::value == object_category::number_constructible || - classify_object::value == object_category::double_constructible, + enable_if_t::value == object_category::enumeration, detail::enabler> = detail::dummy> constexpr const char *type_name() { - return "FLOAT"; + return "ENUM"; } /// Print name for enumeration types -template ::value == object_category::enumeration, detail::enabler> = detail::dummy> +template ::value == + object_category::boolean_value, + detail::enabler> = detail::dummy> constexpr const char *type_name() { - return "ENUM"; + return "BOOLEAN"; } /// Print name for enumeration types -template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +template ::value == + object_category::complex_number, + detail::enabler> = detail::dummy> constexpr const char *type_name() { - return "BOOLEAN"; -} - -/// Print name for enumeration types -template ::value == object_category::complex_number, detail::enabler> = detail::dummy> -constexpr const char *type_name() { - return "COMPLEX"; + return "COMPLEX"; } /// Print for all other types template ::value >= object_category::string_assignable && + enable_if_t::value >= + object_category::string_assignable && classify_object::value <= object_category::other, detail::enabler> = detail::dummy> constexpr const char *type_name() { - return "TEXT"; + return "TEXT"; } /// typename for tuple value -template ::value == object_category::tuple_value && type_count_base::value >= 2, - detail::enabler> = detail::dummy> -std::string type_name(); // forward declaration +template ::value == + object_category::tuple_value && + type_count_base::value >= 2, + detail::enabler> = detail::dummy> +std::string type_name(); // forward declaration /// Generate type name for a wrapper or container value -template ::value == object_category::container_value || - classify_object::value == object_category::wrapper_value, - detail::enabler> = detail::dummy> -std::string type_name(); // forward declaration +template < + typename T, + enable_if_t::value == object_category::container_value || + classify_object::value == object_category::wrapper_value, + detail::enabler> = detail::dummy> +std::string type_name(); // forward declaration /// Print name for single element tuple types -template ::value == object_category::tuple_value && type_count_base::value == 1, - detail::enabler> = detail::dummy> +template ::value == + object_category::tuple_value && + type_count_base::value == 1, + detail::enabler> = detail::dummy> inline std::string type_name() { - return type_name::type>::type>(); + return type_name< + typename std::decay::type>::type>(); } /// Empty string if the index > tuple size template -inline typename std::enable_if::value, std::string>::type tuple_name() { - return std::string{}; +inline + typename std::enable_if::value, std::string>::type + tuple_name() { + return std::string{}; } /// Recursively generate the tuple type name template -inline typename std::enable_if<(I < type_count_base::value), std::string>::type tuple_name() { - auto str = std::string{type_name::type>::type>()} + ',' + - tuple_name(); - if(str.back() == ',') - str.pop_back(); - return str; +inline + typename std::enable_if<(I < type_count_base::value), std::string>::type + tuple_name() { + auto str = std::string{type_name::type>::type>()} + + ',' + tuple_name(); + if (str.back() == ',') str.pop_back(); + return str; } /// Print type name for tuples with 2 or more elements -template ::value == object_category::tuple_value && type_count_base::value >= 2, - detail::enabler>> +template ::value == + object_category::tuple_value && + type_count_base::value >= 2, + detail::enabler>> inline std::string type_name() { - auto tname = std::string(1, '[') + tuple_name(); - tname.push_back(']'); - return tname; + auto tname = std::string(1, '[') + tuple_name(); + tname.push_back(']'); + return tname; } /// get the type name for a type that has a value_type member -template ::value == object_category::container_value || - classify_object::value == object_category::wrapper_value, - detail::enabler>> +template < + typename T, + enable_if_t::value == object_category::container_value || + classify_object::value == object_category::wrapper_value, + detail::enabler>> inline std::string type_name() { - return type_name(); + return type_name(); } // Lexical cast /// Convert to an unsigned integral -template ::value, detail::enabler> = detail::dummy> +template ::value, detail::enabler> = + detail::dummy> bool integral_conversion(const std::string &input, T &output) noexcept { - if(input.empty() || input.front() == '-') { - return false; - } - char *val{nullptr}; + if (input.empty() || input.front() == '-') { + return false; + } + char *val{nullptr}; + errno = 0; + std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0); + if (errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + if (val == (input.c_str() + input.size()) && + static_cast(output) == output_ll) { + return true; + } + val = nullptr; + std::int64_t output_sll = std::strtoll(input.c_str(), &val, 0); + if (val == (input.c_str() + input.size())) { + output = (output_sll < 0) ? static_cast(0) : static_cast(output_sll); + return (static_cast(output) == output_sll); + } + // remove separators + if (input.find_first_of("_'") != std::string::npos) { + std::string nstring = input; + nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), + nstring.end()); + nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), + nstring.end()); + return integral_conversion(nstring, output); + } + if (std::isspace(static_cast(input.back()))) { + return integral_conversion(trim_copy(input), output); + } + if (input.compare(0, 2, "0o") == 0 || input.compare(0, 2, "0O") == 0) { + val = nullptr; errno = 0; - std::uint64_t output_ll = std::strtoull(input.c_str(), &val, 0); - if(errno == ERANGE) { - return false; + output_ll = std::strtoull(input.c_str() + 2, &val, 8); + if (errno == ERANGE) { + return false; } output = static_cast(output_ll); - if(val == (input.c_str() + input.size()) && static_cast(output) == output_ll) { - return true; - } + return (val == (input.c_str() + input.size()) && + static_cast(output) == output_ll); + } + if (input.compare(0, 2, "0b") == 0 || input.compare(0, 2, "0B") == 0) { + // LCOV_EXCL_START + // In some new compilers including the coverage testing one binary strings + // are handled properly in strtoull automatically so this coverage is + // missing but is well tested in other compilers val = nullptr; - std::int64_t output_sll = std::strtoll(input.c_str(), &val, 0); - if(val == (input.c_str() + input.size())) { - output = (output_sll < 0) ? static_cast(0) : static_cast(output_sll); - return (static_cast(output) == output_sll); + errno = 0; + output_ll = std::strtoull(input.c_str() + 2, &val, 2); + if (errno == ERANGE) { + return false; } - // remove separators - if(input.find_first_of("_'") != std::string::npos) { - std::string nstring = input; - nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end()); - nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end()); - return integral_conversion(nstring, output); - } - if(std::isspace(static_cast(input.back()))) { - return integral_conversion(trim_copy(input), output); - } - if(input.compare(0, 2, "0o") == 0 || input.compare(0, 2, "0O") == 0) { - val = nullptr; - errno = 0; - output_ll = std::strtoull(input.c_str() + 2, &val, 8); - if(errno == ERANGE) { - return false; - } - output = static_cast(output_ll); - return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); - } - if(input.compare(0, 2, "0b") == 0 || input.compare(0, 2, "0B") == 0) { - // LCOV_EXCL_START - // In some new compilers including the coverage testing one binary strings are handled properly in strtoull - // automatically so this coverage is missing but is well tested in other compilers - val = nullptr; - errno = 0; - output_ll = std::strtoull(input.c_str() + 2, &val, 2); - if(errno == ERANGE) { - return false; - } - output = static_cast(output_ll); - return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); - // LCOV_EXCL_STOP - } - return false; + output = static_cast(output_ll); + return (val == (input.c_str() + input.size()) && + static_cast(output) == output_ll); + // LCOV_EXCL_STOP + } + return false; } /// Convert to a signed integral -template ::value, detail::enabler> = detail::dummy> +template ::value, detail::enabler> = + detail::dummy> bool integral_conversion(const std::string &input, T &output) noexcept { - if(input.empty()) { - return false; - } - char *val = nullptr; + if (input.empty()) { + return false; + } + char *val = nullptr; + errno = 0; + std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0); + if (errno == ERANGE) { + return false; + } + output = static_cast(output_ll); + if (val == (input.c_str() + input.size()) && + static_cast(output) == output_ll) { + return true; + } + if (input == "true") { + // this is to deal with a few oddities with flags and wrapper int types + output = static_cast(1); + return true; + } + // remove separators and trailing spaces + if (input.find_first_of("_'") != std::string::npos) { + std::string nstring = input; + nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), + nstring.end()); + nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), + nstring.end()); + return integral_conversion(nstring, output); + } + if (std::isspace(static_cast(input.back()))) { + return integral_conversion(trim_copy(input), output); + } + if (input.compare(0, 2, "0o") == 0 || input.compare(0, 2, "0O") == 0) { + val = nullptr; errno = 0; - std::int64_t output_ll = std::strtoll(input.c_str(), &val, 0); - if(errno == ERANGE) { - return false; + output_ll = std::strtoll(input.c_str() + 2, &val, 8); + if (errno == ERANGE) { + return false; } output = static_cast(output_ll); - if(val == (input.c_str() + input.size()) && static_cast(output) == output_ll) { - return true; + return (val == (input.c_str() + input.size()) && + static_cast(output) == output_ll); + } + if (input.compare(0, 2, "0b") == 0 || input.compare(0, 2, "0B") == 0) { + // LCOV_EXCL_START + // In some new compilers including the coverage testing one binary strings + // are handled properly in strtoll automatically so this coverage is missing + // but is well tested in other compilers + val = nullptr; + errno = 0; + output_ll = std::strtoll(input.c_str() + 2, &val, 2); + if (errno == ERANGE) { + return false; } - if(input == "true") { - // this is to deal with a few oddities with flags and wrapper int types - output = static_cast(1); - return true; - } - // remove separators and trailing spaces - if(input.find_first_of("_'") != std::string::npos) { - std::string nstring = input; - nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end()); - nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end()); - return integral_conversion(nstring, output); - } - if(std::isspace(static_cast(input.back()))) { - return integral_conversion(trim_copy(input), output); - } - if(input.compare(0, 2, "0o") == 0 || input.compare(0, 2, "0O") == 0) { - val = nullptr; - errno = 0; - output_ll = std::strtoll(input.c_str() + 2, &val, 8); - if(errno == ERANGE) { - return false; - } - output = static_cast(output_ll); - return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); - } - if(input.compare(0, 2, "0b") == 0 || input.compare(0, 2, "0B") == 0) { - // LCOV_EXCL_START - // In some new compilers including the coverage testing one binary strings are handled properly in strtoll - // automatically so this coverage is missing but is well tested in other compilers - val = nullptr; - errno = 0; - output_ll = std::strtoll(input.c_str() + 2, &val, 2); - if(errno == ERANGE) { - return false; - } - output = static_cast(output_ll); - return (val == (input.c_str() + input.size()) && static_cast(output) == output_ll); - // LCOV_EXCL_STOP - } - return false; + output = static_cast(output_ll); + return (val == (input.c_str() + input.size()) && + static_cast(output) == output_ll); + // LCOV_EXCL_STOP + } + return false; } -/// Convert a flag into an integer value typically binary flags sets errno to nonzero if conversion failed +/// Convert a flag into an integer value typically binary flags sets errno to +/// nonzero if conversion failed inline std::int64_t to_flag_value(std::string val) noexcept { - static const std::string trueString("true"); - static const std::string falseString("false"); - if(val == trueString) { - return 1; + static const std::string trueString("true"); + static const std::string falseString("false"); + if (val == trueString) { + return 1; + } + if (val == falseString) { + return -1; + } + val = detail::to_lower(val); + std::int64_t ret = 0; + if (val.size() == 1) { + if (val[0] >= '1' && val[0] <= '9') { + return (static_cast(val[0]) - '0'); } - if(val == falseString) { - return -1; - } - val = detail::to_lower(val); - std::int64_t ret = 0; - if(val.size() == 1) { - if(val[0] >= '1' && val[0] <= '9') { - return (static_cast(val[0]) - '0'); - } - switch(val[0]) { - case '0': - case 'f': - case 'n': - case '-': - ret = -1; - break; - case 't': - case 'y': - case '+': - ret = 1; - break; - default: - errno = EINVAL; - return -1; - } - return ret; - } - if(val == trueString || val == "on" || val == "yes" || val == "enable") { - ret = 1; - } else if(val == falseString || val == "off" || val == "no" || val == "disable") { - ret = -1; - } else { - char *loc_ptr{nullptr}; - ret = std::strtoll(val.c_str(), &loc_ptr, 0); - if(loc_ptr != (val.c_str() + val.size()) && errno == 0) { - errno = EINVAL; - } + switch (val[0]) { + case '0': + case 'f': + case 'n': + case '-': + ret = -1; + break; + case 't': + case 'y': + case '+': + ret = 1; + break; + default: + errno = EINVAL; + return -1; } return ret; + } + if (val == trueString || val == "on" || val == "yes" || val == "enable") { + ret = 1; + } else if (val == falseString || val == "off" || val == "no" || + val == "disable") { + ret = -1; + } else { + char *loc_ptr{nullptr}; + ret = std::strtoll(val.c_str(), &loc_ptr, 0); + if (loc_ptr != (val.c_str() + val.size()) && errno == 0) { + errno = EINVAL; + } + } + return ret; } /// Integer conversion -template ::value == object_category::integral_value || - classify_object::value == object_category::unsigned_integral, - detail::enabler> = detail::dummy> +template ::value == + object_category::integral_value || + classify_object::value == + object_category::unsigned_integral, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - return integral_conversion(input, output); + return integral_conversion(input, output); } /// char values template ::value == object_category::char_value, detail::enabler> = detail::dummy> + enable_if_t::value == object_category::char_value, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - if(input.size() == 1) { - output = static_cast(input[0]); - return true; - } - return integral_conversion(input, output); + if (input.size() == 1) { + output = static_cast(input[0]); + return true; + } + return integral_conversion(input, output); } /// Boolean values -template ::value == object_category::boolean_value, detail::enabler> = detail::dummy> +template ::value == + object_category::boolean_value, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - errno = 0; - auto out = to_flag_value(input); - if(errno == 0) { - output = (out > 0); - } else if(errno == ERANGE) { - output = (input[0] != '-'); - } else { - return false; - } - return true; + errno = 0; + auto out = to_flag_value(input); + if (errno == 0) { + output = (out > 0); + } else if (errno == ERANGE) { + output = (input[0] != '-'); + } else { + return false; + } + return true; } /// Floats -template ::value == object_category::floating_point, detail::enabler> = detail::dummy> +template ::value == + object_category::floating_point, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - if(input.empty()) { - return false; - } - char *val = nullptr; - auto output_ld = std::strtold(input.c_str(), &val); - output = static_cast(output_ld); - if(val == (input.c_str() + input.size())) { - return true; - } - while(std::isspace(static_cast(*val))) { - ++val; - if(val == (input.c_str() + input.size())) { - return true; - } - } - - // remove separators - if(input.find_first_of("_'") != std::string::npos) { - std::string nstring = input; - nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), nstring.end()); - nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), nstring.end()); - return lexical_cast(nstring, output); - } + if (input.empty()) { return false; + } + char *val = nullptr; + auto output_ld = std::strtold(input.c_str(), &val); + output = static_cast(output_ld); + if (val == (input.c_str() + input.size())) { + return true; + } + while (std::isspace(static_cast(*val))) { + ++val; + if (val == (input.c_str() + input.size())) { + return true; + } + } + + // remove separators + if (input.find_first_of("_'") != std::string::npos) { + std::string nstring = input; + nstring.erase(std::remove(nstring.begin(), nstring.end(), '_'), + nstring.end()); + nstring.erase(std::remove(nstring.begin(), nstring.end(), '\''), + nstring.end()); + return lexical_cast(nstring, output); + } + return false; } /// complex -template ::value == object_category::complex_number, detail::enabler> = detail::dummy> +template ::value == + object_category::complex_number, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - using XC = typename wrapped_type::type; - XC x{0.0}, y{0.0}; - auto str1 = input; - bool worked = false; - auto nloc = str1.find_last_of("+-"); - if(nloc != std::string::npos && nloc > 0) { - worked = lexical_cast(str1.substr(0, nloc), x); - str1 = str1.substr(nloc); - if(str1.back() == 'i' || str1.back() == 'j') - str1.pop_back(); - worked = worked && lexical_cast(str1, y); + using XC = typename wrapped_type::type; + XC x{0.0}, y{0.0}; + auto str1 = input; + bool worked = false; + auto nloc = str1.find_last_of("+-"); + if (nloc != std::string::npos && nloc > 0) { + worked = lexical_cast(str1.substr(0, nloc), x); + str1 = str1.substr(nloc); + if (str1.back() == 'i' || str1.back() == 'j') str1.pop_back(); + worked = worked && lexical_cast(str1, y); + } else { + if (str1.back() == 'i' || str1.back() == 'j') { + str1.pop_back(); + worked = lexical_cast(str1, y); + x = XC{0}; } else { - if(str1.back() == 'i' || str1.back() == 'j') { - str1.pop_back(); - worked = lexical_cast(str1, y); - x = XC{0}; - } else { - worked = lexical_cast(str1, x); - y = XC{0}; - } + worked = lexical_cast(str1, x); + y = XC{0}; } - if(worked) { - output = T{x, y}; - return worked; - } - return from_stream(input, output); + } + if (worked) { + output = T{x, y}; + return worked; + } + return from_stream(input, output); } /// String and similar direct assignment -template ::value == object_category::string_assignable, detail::enabler> = detail::dummy> +template ::value == + object_category::string_assignable, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - output = input; - return true; + output = input; + return true; } /// String and similar constructible and copy assignment -template < - typename T, - enable_if_t::value == object_category::string_constructible, detail::enabler> = detail::dummy> +template ::value == + object_category::string_constructible, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - output = T(input); - return true; + output = T(input); + return true; } /// Wide strings -template < - typename T, - enable_if_t::value == object_category::wstring_assignable, detail::enabler> = detail::dummy> +template ::value == + object_category::wstring_assignable, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - output = widen(input); - return true; + output = widen(input); + return true; } -template < - typename T, - enable_if_t::value == object_category::wstring_constructible, detail::enabler> = detail::dummy> +template ::value == + object_category::wstring_constructible, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - output = T{widen(input)}; - return true; + output = T{widen(input)}; + return true; } /// Enumerations template ::value == object_category::enumeration, detail::enabler> = detail::dummy> + enable_if_t::value == object_category::enumeration, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - typename std::underlying_type::type val; - if(!integral_conversion(input, val)) { - return false; - } - output = static_cast(val); - return true; + typename std::underlying_type::type val; + if (!integral_conversion(input, val)) { + return false; + } + output = static_cast(val); + return true; } /// wrapper types -template ::value == object_category::wrapper_value && - std::is_assignable::value, - detail::enabler> = detail::dummy> +template < + typename T, + enable_if_t::value == object_category::wrapper_value && + std::is_assignable::value, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - typename T::value_type val; - if(lexical_cast(input, val)) { - output = val; - return true; - } - return from_stream(input, output); + typename T::value_type val; + if (lexical_cast(input, val)) { + output = val; + return true; + } + return from_stream(input, output); } -template ::value == object_category::wrapper_value && - !std::is_assignable::value && std::is_assignable::value, - detail::enabler> = detail::dummy> +template < + typename T, + enable_if_t::value == object_category::wrapper_value && + !std::is_assignable::value && + std::is_assignable::value, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - typename T::value_type val; - if(lexical_cast(input, val)) { - output = T{val}; - return true; - } - return from_stream(input, output); + typename T::value_type val; + if (lexical_cast(input, val)) { + output = T{val}; + return true; + } + return from_stream(input, output); } /// Assignable from double or int -template < - typename T, - enable_if_t::value == object_category::number_constructible, detail::enabler> = detail::dummy> +template ::value == + object_category::number_constructible, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - int val = 0; - if(integral_conversion(input, val)) { - output = T(val); - return true; - } + int val = 0; + if (integral_conversion(input, val)) { + output = T(val); + return true; + } - double dval = 0.0; - if(lexical_cast(input, dval)) { - output = T{dval}; - return true; - } + double dval = 0.0; + if (lexical_cast(input, dval)) { + output = T{dval}; + return true; + } - return from_stream(input, output); + return from_stream(input, output); } /// Assignable from int -template < - typename T, - enable_if_t::value == object_category::integer_constructible, detail::enabler> = detail::dummy> +template ::value == + object_category::integer_constructible, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - int val = 0; - if(integral_conversion(input, val)) { - output = T(val); - return true; - } - return from_stream(input, output); + int val = 0; + if (integral_conversion(input, val)) { + output = T(val); + return true; + } + return from_stream(input, output); } /// Assignable from double -template < - typename T, - enable_if_t::value == object_category::double_constructible, detail::enabler> = detail::dummy> +template ::value == + object_category::double_constructible, + detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - double val = 0.0; - if(lexical_cast(input, val)) { - output = T{val}; - return true; - } - return from_stream(input, output); + double val = 0.0; + if (lexical_cast(input, val)) { + output = T{val}; + return true; + } + return from_stream(input, output); } /// Non-string convertible from an int template ::value == object_category::other && std::is_assignable::value, + enable_if_t::value == object_category::other && + std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - int val = 0; - if(integral_conversion(input, val)) { + int val = 0; + if (integral_conversion(input, val)) { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4800) #endif - // with Atomic this could produce a warning due to the conversion but if atomic gets here it is an old style - // so will most likely still work - output = val; + // with Atomic this could produce a warning due to the conversion but if + // atomic gets here it is an old style so will most likely still work + output = val; #ifdef _MSC_VER #pragma warning(pop) #endif - return true; - } - // LCOV_EXCL_START - // This version of cast is only used for odd cases in an older compilers the fail over - // from_stream is tested elsewhere an not relevant for coverage here - return from_stream(input, output); - // LCOV_EXCL_STOP + return true; + } + // LCOV_EXCL_START + // This version of cast is only used for odd cases in an older compilers the + // fail over from_stream is tested elsewhere an not relevant for coverage here + return from_stream(input, output); + // LCOV_EXCL_STOP } /// Non-string parsable by a stream template ::value == object_category::other && !std::is_assignable::value && + enable_if_t::value == object_category::other && + !std::is_assignable::value && is_istreamable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string &input, T &output) { - return from_stream(input, output); + return from_stream(input, output); } -/// Fallback overload that prints a human-readable error for types that we don't recognize and that don't have a -/// user-supplied lexical_cast overload. +/// Fallback overload that prints a human-readable error for types that we don't +/// recognize and that don't have a user-supplied lexical_cast overload. template ::value == object_category::other && !std::is_assignable::value && - !is_istreamable::value && !adl_detail::is_lexical_castable::value, + enable_if_t::value == object_category::other && + !std::is_assignable::value && + !is_istreamable::value && + !adl_detail::is_lexical_castable::value, detail::enabler> = detail::dummy> bool lexical_cast(const std::string & /*input*/, T & /*output*/) { - static_assert(!std::is_same::value, // Can't just write false here. - "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it " - "is convertible from another type use the add_option(...) with XC being the known type"); - return false; + static_assert(!std::is_same::value, // Can't just write false here. + "option object type must have a lexical cast overload or " + "streaming input operator(>>) defined, if it " + "is convertible from another type use the add_option(...) with XC being the known type"); + return false; } /// Assign a value through lexical cast operations /// Strings can be empty so we need to do a little different -template ::value && - (classify_object::value == object_category::string_assignable || - classify_object::value == object_category::string_constructible || - classify_object::value == object_category::wstring_assignable || - classify_object::value == object_category::wstring_constructible), + (classify_object::value == + object_category::string_assignable || + classify_object::value == + object_category::string_constructible || + classify_object::value == + object_category::wstring_assignable || + classify_object::value == + object_category::wstring_constructible), detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { - return lexical_cast(input, output); + return lexical_cast(input, output); } /// Assign a value through lexical cast operations -template ::value && std::is_assignable::value && - classify_object::value != object_category::string_assignable && - classify_object::value != object_category::string_constructible && - classify_object::value != object_category::wstring_assignable && - classify_object::value != object_category::wstring_constructible, +template ::value && + std::is_assignable::value && + classify_object::value != + object_category::string_assignable && + classify_object::value != + object_category::string_constructible && + classify_object::value != + object_category::wstring_assignable && + classify_object::value != + object_category::wstring_constructible, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { - if(input.empty()) { - output = AssignTo{}; - return true; - } + if (input.empty()) { + output = AssignTo{}; + return true; + } - return lexical_cast(input, output); -} // LCOV_EXCL_LINE + return lexical_cast(input, output); +} // LCOV_EXCL_LINE /// Assign a value through lexical cast operations -template ::value && !std::is_assignable::value && - classify_object::value == object_category::wrapper_value, +template ::value && + !std::is_assignable::value && + classify_object::value == + object_category::wrapper_value, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { - if(input.empty()) { - typename AssignTo::value_type emptyVal{}; - output = emptyVal; - return true; - } - return lexical_cast(input, output); + if (input.empty()) { + typename AssignTo::value_type emptyVal{}; + output = emptyVal; + return true; + } + return lexical_cast(input, output); } /// Assign a value through lexical cast operations for int compatible values /// mainly for atomic operations on some compilers -template ::value && !std::is_assignable::value && - classify_object::value != object_category::wrapper_value && +template ::value && + !std::is_assignable::value && + classify_object::value != + object_category::wrapper_value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { - if(input.empty()) { - output = 0; - return true; - } - int val{0}; - if(lexical_cast(input, val)) { + if (input.empty()) { + output = 0; + return true; + } + int val{0}; + if (lexical_cast(input, val)) { #if defined(__clang__) /* on some older clang compilers */ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-conversion" #endif - output = val; + output = val; #if defined(__clang__) #pragma clang diagnostic pop #endif - return true; - } - return false; + return true; + } + return false; } -/// Assign a value converted from a string in lexical cast to the output value directly -template ::value && std::is_assignable::value, +/// Assign a value converted from a string in lexical cast to the output value +/// directly +template ::value && + std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { - ConvertTo val{}; - bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; - if(parse_result) { - output = val; - } - return parse_result; + ConvertTo val{}; + bool parse_result = (!input.empty()) ? lexical_cast(input, val) : true; + if (parse_result) { + output = val; + } + return parse_result; } -/// Assign a value from a lexical cast through constructing a value and move assigning it -template < - typename AssignTo, - typename ConvertTo, - enable_if_t::value && !std::is_assignable::value && - std::is_move_assignable::value, - detail::enabler> = detail::dummy> +/// Assign a value from a lexical cast through constructing a value and move +/// assigning it +template ::value && + !std::is_assignable::value && + std::is_move_assignable::value, + detail::enabler> = detail::dummy> bool lexical_assign(const std::string &input, AssignTo &output) { - ConvertTo val{}; - bool parse_result = input.empty() ? true : lexical_cast(input, val); - if(parse_result) { - output = AssignTo(val); // use () form of constructor to allow some implicit conversions - } - return parse_result; + ConvertTo val{}; + bool parse_result = input.empty() ? true : lexical_cast(input, val); + if (parse_result) { + output = AssignTo( + val); // use () form of constructor to allow some implicit conversions + } + return parse_result; } /// primary lexical conversion operation, 1 string to 1 type of some kind -template ::value <= object_category::other && - classify_object::value <= object_category::wrapper_value, - detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - return lexical_assign(strings[0], output); +template < + typename AssignTo, typename ConvertTo, + enable_if_t::value <= object_category::other && + classify_object::value <= + object_category::wrapper_value, + detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + return lexical_assign(strings[0], output); } -/// Lexical conversion if there is only one element but the conversion type is for two, then call a two element -/// constructor -template ::value <= 2) && expected_count::value == 1 && - is_tuple_like::value && type_count_base::value == 2, +/// Lexical conversion if there is only one element but the conversion type is +/// for two, then call a two element constructor +template ::value <= 2) && + expected_count::value == 1 && + is_tuple_like::value && + type_count_base::value == 2, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - // the remove const is to handle pair types coming from a container - using FirstType = typename std::remove_const::type>::type; - using SecondType = typename std::tuple_element<1, ConvertTo>::type; - FirstType v1; - SecondType v2; - bool retval = lexical_assign(strings[0], v1); - retval = retval && lexical_assign((strings.size() > 1) ? strings[1] : std::string{}, v2); - if(retval) { - output = AssignTo{v1, v2}; - } - return retval; +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + // the remove const is to handle pair types coming from a container + using FirstType = typename std::remove_const< + typename std::tuple_element<0, ConvertTo>::type>::type; + using SecondType = typename std::tuple_element<1, ConvertTo>::type; + FirstType v1; + SecondType v2; + bool retval = lexical_assign(strings[0], v1); + retval = retval && lexical_assign( + (strings.size() > 1) ? strings[1] : std::string{}, v2); + if (retval) { + output = AssignTo{v1, v2}; + } + return retval; } /// Lexical conversion of a container types of single elements -template ::value && is_mutable_container::value && +template ::value && + is_mutable_container::value && type_count::value == 1, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - output.erase(output.begin(), output.end()); - if(strings.empty()) { - return true; +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + output.erase(output.begin(), output.end()); + if (strings.empty()) { + return true; + } + if (strings.size() == 1 && strings[0] == "{}") { + return true; + } + bool skip_remaining = false; + if (strings.size() == 2 && strings[0] == "{}" && is_separator(strings[1])) { + skip_remaining = true; + } + for (const auto &elem : strings) { + typename AssignTo::value_type out; + bool retval = lexical_assign(elem, out); + if (!retval) { + return false; } - if(strings.size() == 1 && strings[0] == "{}") { - return true; + output.insert(output.end(), std::move(out)); + if (skip_remaining) { + break; } - bool skip_remaining = false; - if(strings.size() == 2 && strings[0] == "{}" && is_separator(strings[1])) { - skip_remaining = true; - } - for(const auto &elem : strings) { - typename AssignTo::value_type out; - bool retval = lexical_assign(elem, out); - if(!retval) { - return false; - } - output.insert(output.end(), std::move(out)); - if(skip_remaining) { - break; - } - } - return (!output.empty()); + } + return (!output.empty()); } /// Lexical conversion for complex types -template ::value, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { +template < + class AssignTo, class ConvertTo, + enable_if_t::value, detail::enabler> = detail::dummy> +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { - if(strings.size() >= 2 && !strings[1].empty()) { - using XC2 = typename wrapped_type::type; - XC2 x{0.0}, y{0.0}; - auto str1 = strings[1]; - if(str1.back() == 'i' || str1.back() == 'j') { - str1.pop_back(); - } - auto worked = lexical_cast(strings[0], x) && lexical_cast(str1, y); - if(worked) { - output = ConvertTo{x, y}; - } - return worked; + if (strings.size() >= 2 && !strings[1].empty()) { + using XC2 = typename wrapped_type::type; + XC2 x{0.0}, y{0.0}; + auto str1 = strings[1]; + if (str1.back() == 'i' || str1.back() == 'j') { + str1.pop_back(); } - return lexical_assign(strings[0], output); + auto worked = lexical_cast(strings[0], x) && lexical_cast(str1, y); + if (worked) { + output = ConvertTo{x, y}; + } + return worked; + } + return lexical_assign(strings[0], output); } -/// Conversion to a vector type using a particular single type as the conversion type -template ::value && (expected_count::value == 1) && +/// Conversion to a vector type using a particular single type as the conversion +/// type +template ::value && + (expected_count::value == 1) && (type_count::value == 1), detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - bool retval = true; - output.clear(); - output.reserve(strings.size()); - for(const auto &elem : strings) { +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + bool retval = true; + output.clear(); + output.reserve(strings.size()); + for (const auto &elem : strings) { - output.emplace_back(); - retval = retval && lexical_assign(elem, output.back()); - } - return (!output.empty()) && retval; + output.emplace_back(); + retval = retval && lexical_assign( + elem, output.back()); + } + return (!output.empty()) && retval; } // forward declaration /// Lexical conversion of a container types with conversion type of two elements -template ::value && is_mutable_container::value && +template ::value && + is_mutable_container::value && type_count_base::value == 2, detail::enabler> = detail::dummy> bool lexical_conversion(std::vector strings, AssignTo &output); /// Lexical conversion of a vector types with type_size >2 forward declaration -template ::value && is_mutable_container::value && +template ::value && + is_mutable_container::value && type_count_base::value != 2 && ((type_count::value > 2) || - (type_count::value > type_count_base::value)), + (type_count::value > + type_count_base::value)), detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output); +bool lexical_conversion(const std::vector &strings, + AssignTo &output); /// Conversion for tuples -template ::value && is_tuple_like::value && - (type_count_base::value != type_count::value || +template ::value && + is_tuple_like::value && + (type_count_base::value != + type_count::value || type_count::value > 2), detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output); // forward declaration +bool lexical_conversion(const std::vector &strings, + AssignTo &output); // forward declaration -/// Conversion for operations where the assigned type is some class but the conversion is a mutable container or large -/// tuple -template ::value && !is_mutable_container::value && - classify_object::value != object_category::wrapper_value && - (is_mutable_container::value || type_count::value > 2), +/// Conversion for operations where the assigned type is some class but the +/// conversion is a mutable container or large tuple +template ::value && + !is_mutable_container::value && + classify_object::value != + object_category::wrapper_value && + (is_mutable_container::value || + type_count::value > 2), detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { - if(strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { - ConvertTo val; - auto retval = lexical_conversion(strings, val); - output = AssignTo{val}; - return retval; - } - output = AssignTo{}; - return true; -} - -/// function template for converting tuples if the static Index is greater than the tuple size -template -inline typename std::enable_if<(I >= type_count_base::value), bool>::type -tuple_conversion(const std::vector &, AssignTo &) { - return true; -} - -/// Conversion of a tuple element where the type size ==1 and not a mutable container -template -inline typename std::enable_if::value && type_count::value == 1, bool>::type -tuple_type_conversion(std::vector &strings, AssignTo &output) { - auto retval = lexical_assign(strings[0], output); - strings.erase(strings.begin()); + if (strings.size() > 1 || (!strings.empty() && !(strings.front().empty()))) { + ConvertTo val; + auto retval = lexical_conversion(strings, val); + output = AssignTo{val}; return retval; + } + output = AssignTo{}; + return true; } -/// Conversion of a tuple element where the type size !=1 but the size is fixed and not a mutable container +/// function template for converting tuples if the static Index is greater than +/// the tuple size +template +inline + typename std::enable_if<(I >= type_count_base::value), bool>::type + tuple_conversion(const std::vector &, AssignTo &) { + return true; +} + +/// Conversion of a tuple element where the type size ==1 and not a mutable +/// container template -inline typename std::enable_if::value && (type_count::value > 1) && - type_count::value == type_count_min::value, +inline typename std::enable_if::value && + type_count::value == 1, bool>::type tuple_type_conversion(std::vector &strings, AssignTo &output) { - auto retval = lexical_conversion(strings, output); - strings.erase(strings.begin(), strings.begin() + type_count::value); - return retval; + auto retval = lexical_assign(strings[0], output); + strings.erase(strings.begin()); + return retval; } -/// Conversion of a tuple element where the type is a mutable container or a type with different min and max type sizes +/// Conversion of a tuple element where the type size !=1 but the size is fixed +/// and not a mutable container +template +inline typename std::enable_if::value && + (type_count::value > 1) && + type_count::value == + type_count_min::value, + bool>::type +tuple_type_conversion(std::vector &strings, AssignTo &output) { + auto retval = lexical_conversion(strings, output); + strings.erase(strings.begin(), + strings.begin() + type_count::value); + return retval; +} + +/// Conversion of a tuple element where the type is a mutable container or a +/// type with different min and max type sizes template inline typename std::enable_if::value || - type_count::value != type_count_min::value, + type_count::value != + type_count_min::value, bool>::type tuple_type_conversion(std::vector &strings, AssignTo &output) { - std::size_t index{subtype_count_min::value}; - const std::size_t mx_count{subtype_count::value}; - const std::size_t mx{(std::min)(mx_count, strings.size() - 1)}; + std::size_t index{subtype_count_min::value}; + const std::size_t mx_count{subtype_count::value}; + const std::size_t mx{(std::min)(mx_count, strings.size() - 1)}; - while(index < mx) { - if(is_separator(strings[index])) { - break; - } - ++index; + while (index < mx) { + if (is_separator(strings[index])) { + break; } - bool retval = lexical_conversion( - std::vector(strings.begin(), strings.begin() + static_cast(index)), output); - if(strings.size() > index) { - strings.erase(strings.begin(), strings.begin() + static_cast(index) + 1); - } else { - strings.clear(); - } - return retval; + ++index; + } + bool retval = lexical_conversion( + std::vector(strings.begin(), + strings.begin() + + static_cast(index)), + output); + if (strings.size() > index) { + strings.erase(strings.begin(), + strings.begin() + static_cast(index) + 1); + } else { + strings.clear(); + } + return retval; } /// Tuple conversion operation template -inline typename std::enable_if<(I < type_count_base::value), bool>::type -tuple_conversion(std::vector strings, AssignTo &output) { - bool retval = true; - using ConvertToElement = typename std:: - conditional::value, typename std::tuple_element::type, ConvertTo>::type; - if(!strings.empty()) { - retval = retval && tuple_type_conversion::type, ConvertToElement>( - strings, std::get(output)); - } - retval = retval && tuple_conversion(std::move(strings), output); - return retval; +inline + typename std::enable_if<(I < type_count_base::value), bool>::type + tuple_conversion(std::vector strings, AssignTo &output) { + bool retval = true; + using ConvertToElement = + typename std::conditional::value, + typename std::tuple_element::type, + ConvertTo>::type; + if (!strings.empty()) { + retval = + retval && + tuple_type_conversion::type, + ConvertToElement>(strings, std::get(output)); + } + retval = retval && tuple_conversion( + std::move(strings), output); + return retval; } /// Lexical conversion of a container types with tuple elements of size 2 -template ::value && is_mutable_container::value && +template ::value && + is_mutable_container::value && type_count_base::value == 2, detail::enabler>> bool lexical_conversion(std::vector strings, AssignTo &output) { - output.clear(); - while(!strings.empty()) { + output.clear(); + while (!strings.empty()) { - typename std::remove_const::type>::type v1; - typename std::tuple_element<1, typename ConvertTo::value_type>::type v2; - bool retval = tuple_type_conversion(strings, v1); - if(!strings.empty()) { - retval = retval && tuple_type_conversion(strings, v2); - } - if(retval) { - output.insert(output.end(), typename AssignTo::value_type{v1, v2}); - } else { - return false; - } + typename std::remove_const::type>::type v1; + typename std::tuple_element<1, typename ConvertTo::value_type>::type v2; + bool retval = + tuple_type_conversion(strings, v1); + if (!strings.empty()) { + retval = retval && + tuple_type_conversion(strings, v2); } - return (!output.empty()); + if (retval) { + output.insert(output.end(), typename AssignTo::value_type{v1, v2}); + } else { + return false; + } + } + return (!output.empty()); } -/// lexical conversion of tuples with type count>2 or tuples of types of some element with a type size>=2 -template ::value && is_tuple_like::value && - (type_count_base::value != type_count::value || +/// lexical conversion of tuples with type count>2 or tuples of types of some +/// element with a type size>=2 +template ::value && + is_tuple_like::value && + (type_count_base::value != + type_count::value || type_count::value > 2), detail::enabler>> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - static_assert( - !is_tuple_like::value || type_count_base::value == type_count_base::value, - "if the conversion type is defined as a tuple it must be the same size as the type you are converting to"); - return tuple_conversion(strings, output); +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + static_assert(!is_tuple_like::value || + type_count_base::value == + type_count_base::value, + "if the conversion type is defined as a tuple it must be the " + "same size as the type you are converting to"); + return tuple_conversion(strings, output); } -/// Lexical conversion of a vector types for everything but tuples of two elements and types of size 1 -template ::value && is_mutable_container::value && +/// Lexical conversion of a vector types for everything but tuples of two +/// elements and types of size 1 +template ::value && + is_mutable_container::value && type_count_base::value != 2 && ((type_count::value > 2) || - (type_count::value > type_count_base::value)), + (type_count::value > + type_count_base::value)), detail::enabler>> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - bool retval = true; - output.clear(); - std::vector temp; - std::size_t ii{0}; - std::size_t icount{0}; - std::size_t xcm{type_count::value}; - auto ii_max = strings.size(); - while(ii < ii_max) { - temp.push_back(strings[ii]); - ++ii; - ++icount; - if(icount == xcm || is_separator(temp.back()) || ii == ii_max) { - if(static_cast(xcm) > type_count_min::value && is_separator(temp.back())) { - temp.pop_back(); - } - typename AssignTo::value_type temp_out; - retval = retval && - lexical_conversion(temp, temp_out); - temp.clear(); - if(!retval) { - return false; - } - output.insert(output.end(), std::move(temp_out)); - icount = 0; - } +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + bool retval = true; + output.clear(); + std::vector temp; + std::size_t ii{0}; + std::size_t icount{0}; + std::size_t xcm{type_count::value}; + auto ii_max = strings.size(); + while (ii < ii_max) { + temp.push_back(strings[ii]); + ++ii; + ++icount; + if (icount == xcm || is_separator(temp.back()) || ii == ii_max) { + if (static_cast(xcm) > type_count_min::value && + is_separator(temp.back())) { + temp.pop_back(); + } + typename AssignTo::value_type temp_out; + retval = retval && lexical_conversion( + temp, temp_out); + temp.clear(); + if (!retval) { + return false; + } + output.insert(output.end(), std::move(temp_out)); + icount = 0; } - return retval; + } + return retval; } /// conversion for wrapper types -template ::value == object_category::wrapper_value && +template ::value == + object_category::wrapper_value && std::is_assignable::value, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - if(strings.empty() || strings.front().empty()) { - output = ConvertTo{}; - return true; - } - typename ConvertTo::value_type val; - if(lexical_conversion(strings, val)) { - output = ConvertTo{val}; - return true; - } - return false; +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + if (strings.empty() || strings.front().empty()) { + output = ConvertTo{}; + return true; + } + typename ConvertTo::value_type val; + if (lexical_conversion(strings, val)) { + output = ConvertTo{val}; + return true; + } + return false; } /// conversion for wrapper types -template ::value == object_category::wrapper_value && +template ::value == + object_category::wrapper_value && !std::is_assignable::value, detail::enabler> = detail::dummy> -bool lexical_conversion(const std::vector &strings, AssignTo &output) { - using ConvertType = typename ConvertTo::value_type; - if(strings.empty() || strings.front().empty()) { - output = ConvertType{}; - return true; - } - ConvertType val; - if(lexical_conversion(strings, val)) { - output = val; - return true; - } - return false; +bool lexical_conversion(const std::vector &strings, + AssignTo &output) { + using ConvertType = typename ConvertTo::value_type; + if (strings.empty() || strings.front().empty()) { + output = ConvertType{}; + return true; + } + ConvertType val; + if (lexical_conversion(strings, val)) { + output = val; + return true; + } + return false; } /// Sum a vector of strings inline std::string sum_string_vector(const std::vector &values) { - double val{0.0}; - bool fail{false}; - std::string output; - for(const auto &arg : values) { - double tv{0.0}; - auto comp = lexical_cast(arg, tv); - if(!comp) { - errno = 0; - auto fv = detail::to_flag_value(arg); - fail = (errno != 0); - if(fail) { - break; - } - tv = static_cast(fv); - } - val += tv; + double val{0.0}; + bool fail{false}; + std::string output; + for (const auto &arg : values) { + double tv{0.0}; + auto comp = lexical_cast(arg, tv); + if (!comp) { + errno = 0; + auto fv = detail::to_flag_value(arg); + fail = (errno != 0); + if (fail) { + break; + } + tv = static_cast(fv); } - if(fail) { - for(const auto &arg : values) { - output.append(arg); - } - } else { - std::ostringstream out; - out.precision(16); - out << val; - output = out.str(); + val += tv; + } + if (fail) { + for (const auto &arg : values) { + output.append(arg); } - return output; + } else { + std::ostringstream out; + out.precision(16); + out << val; + output = out.str(); + } + return output; } -} // namespace detail - - +} // namespace detail namespace detail { -// Returns false if not a short option. Otherwise, sets opt name and rest and returns true -CLI11_INLINE bool split_short(const std::string ¤t, std::string &name, std::string &rest); +// Returns false if not a short option. Otherwise, sets opt name and rest and +// returns true +CLI11_INLINE bool split_short(const std::string ¤t, std::string &name, + std::string &rest); -// Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true -CLI11_INLINE bool split_long(const std::string ¤t, std::string &name, std::string &value); +// Returns false if not a long option. Otherwise, sets opt name and other side +// of = and returns true +CLI11_INLINE bool split_long(const std::string ¤t, std::string &name, + std::string &value); -// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true -CLI11_INLINE bool split_windows_style(const std::string ¤t, std::string &name, std::string &value); +// Returns false if not a windows style option. Otherwise, sets opt name and +// value and returns true +CLI11_INLINE bool split_windows_style(const std::string ¤t, + std::string &name, std::string &value); // Splits a string into multiple long and short names CLI11_INLINE std::vector split_names(std::string current); /// extract default flag values either {def} or starting with a ! -CLI11_INLINE std::vector> get_default_flag_values(const std::string &str); +CLI11_INLINE std::vector> +get_default_flag_values(const std::string &str); /// Get a vector of short names, one of long names, and a single name -CLI11_INLINE std::tuple, std::vector, std::string> -get_names(const std::vector &input, bool allow_non_standard = false); - -} // namespace detail - +CLI11_INLINE + std::tuple, std::vector, std::string> + get_names(const std::vector &input, + bool allow_non_standard = false); +} // namespace detail namespace detail { -CLI11_INLINE bool split_short(const std::string ¤t, std::string &name, std::string &rest) { - if(current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) { - name = current.substr(1, 1); - rest = current.substr(2); - return true; - } - return false; +CLI11_INLINE bool split_short(const std::string ¤t, std::string &name, + std::string &rest) { + if (current.size() > 1 && current[0] == '-' && valid_first_char(current[1])) { + name = current.substr(1, 1); + rest = current.substr(2); + return true; + } + return false; } -CLI11_INLINE bool split_long(const std::string ¤t, std::string &name, std::string &value) { - if(current.size() > 2 && current.compare(0, 2, "--") == 0 && valid_first_char(current[2])) { - auto loc = current.find_first_of('='); - if(loc != std::string::npos) { - name = current.substr(2, loc - 2); - value = current.substr(loc + 1); - } else { - name = current.substr(2); - value = ""; - } - return true; +CLI11_INLINE bool split_long(const std::string ¤t, std::string &name, + std::string &value) { + if (current.size() > 2 && current.compare(0, 2, "--") == 0 && + valid_first_char(current[2])) { + auto loc = current.find_first_of('='); + if (loc != std::string::npos) { + name = current.substr(2, loc - 2); + value = current.substr(loc + 1); + } else { + name = current.substr(2); + value = ""; } - return false; + return true; + } + return false; } -CLI11_INLINE bool split_windows_style(const std::string ¤t, std::string &name, std::string &value) { - if(current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) { - auto loc = current.find_first_of(':'); - if(loc != std::string::npos) { - name = current.substr(1, loc - 1); - value = current.substr(loc + 1); - } else { - name = current.substr(1); - value = ""; - } - return true; +CLI11_INLINE bool split_windows_style(const std::string ¤t, + std::string &name, std::string &value) { + if (current.size() > 1 && current[0] == '/' && valid_first_char(current[1])) { + auto loc = current.find_first_of(':'); + if (loc != std::string::npos) { + name = current.substr(1, loc - 1); + value = current.substr(loc + 1); + } else { + name = current.substr(1); + value = ""; } - return false; + return true; + } + return false; } CLI11_INLINE std::vector split_names(std::string current) { - std::vector output; - std::size_t val = 0; - while((val = current.find(',')) != std::string::npos) { - output.push_back(trim_copy(current.substr(0, val))); - current = current.substr(val + 1); - } - output.push_back(trim_copy(current)); - return output; + std::vector output; + std::size_t val = 0; + while ((val = current.find(',')) != std::string::npos) { + output.push_back(trim_copy(current.substr(0, val))); + current = current.substr(val + 1); + } + output.push_back(trim_copy(current)); + return output; } -CLI11_INLINE std::vector> get_default_flag_values(const std::string &str) { - std::vector flags = split_names(str); - flags.erase(std::remove_if(flags.begin(), - flags.end(), - [](const std::string &name) { - return ((name.empty()) || (!(((name.find_first_of('{') != std::string::npos) && - (name.back() == '}')) || - (name[0] == '!')))); - }), - flags.end()); - std::vector> output; - output.reserve(flags.size()); - for(auto &flag : flags) { - auto def_start = flag.find_first_of('{'); - std::string defval = "false"; - if((def_start != std::string::npos) && (flag.back() == '}')) { - defval = flag.substr(def_start + 1); - defval.pop_back(); - flag.erase(def_start, std::string::npos); // NOLINT(readability-suspicious-call-argument) - } - flag.erase(0, flag.find_first_not_of("-!")); - output.emplace_back(flag, defval); +CLI11_INLINE std::vector> +get_default_flag_values(const std::string &str) { + std::vector flags = split_names(str); + flags.erase(std::remove_if(flags.begin(), flags.end(), + [](const std::string &name) { + return ((name.empty()) || + (!(((name.find_first_of('{') != + std::string::npos) && + (name.back() == '}')) || + (name[0] == '!')))); + }), + flags.end()); + std::vector> output; + output.reserve(flags.size()); + for (auto &flag : flags) { + auto def_start = flag.find_first_of('{'); + std::string defval = "false"; + if ((def_start != std::string::npos) && (flag.back() == '}')) { + defval = flag.substr(def_start + 1); + defval.pop_back(); + flag.erase( + def_start, + std::string::npos); // NOLINT(readability-suspicious-call-argument) } - return output; + flag.erase(0, flag.find_first_not_of("-!")); + output.emplace_back(flag, defval); + } + return output; } -CLI11_INLINE std::tuple, std::vector, std::string> -get_names(const std::vector &input, bool allow_non_standard) { +CLI11_INLINE + std::tuple, std::vector, std::string> + get_names(const std::vector &input, bool allow_non_standard) { - std::vector short_names; - std::vector long_names; - std::string pos_name; - for(std::string name : input) { - if(name.length() == 0) { - continue; - } - if(name.length() > 1 && name[0] == '-' && name[1] != '-') { - if(name.length() == 2 && valid_first_char(name[1])) { - short_names.emplace_back(1, name[1]); - } else if(name.length() > 2) { - if(allow_non_standard) { - name = name.substr(1); - if(valid_name_string(name)) { - short_names.push_back(name); - } else { - throw BadNameString::BadLongName(name); - } - } else { - throw BadNameString::MissingDash(name); - } - } else { - throw BadNameString::OneCharName(name); - } - } else if(name.length() > 2 && name.substr(0, 2) == "--") { - name = name.substr(2); - if(valid_name_string(name)) { - long_names.push_back(name); - } else { - throw BadNameString::BadLongName(name); - } - } else if(name == "-" || name == "--" || name == "++") { - throw BadNameString::ReservedName(name); + std::vector short_names; + std::vector long_names; + std::string pos_name; + for (std::string name : input) { + if (name.length() == 0) { + continue; + } + if (name.length() > 1 && name[0] == '-' && name[1] != '-') { + if (name.length() == 2 && valid_first_char(name[1])) { + short_names.emplace_back(1, name[1]); + } else if (name.length() > 2) { + if (allow_non_standard) { + name = name.substr(1); + if (valid_name_string(name)) { + short_names.push_back(name); + } else { + throw BadNameString::BadLongName(name); + } } else { - if(!pos_name.empty()) { - throw BadNameString::MultiPositionalNames(name); - } - if(valid_name_string(name)) { - pos_name = name; - } else { - throw BadNameString::BadPositionalName(name); - } + throw BadNameString::MissingDash(name); } + } else { + throw BadNameString::OneCharName(name); + } + } else if (name.length() > 2 && name.substr(0, 2) == "--") { + name = name.substr(2); + if (valid_name_string(name)) { + long_names.push_back(name); + } else { + throw BadNameString::BadLongName(name); + } + } else if (name == "-" || name == "--" || name == "++") { + throw BadNameString::ReservedName(name); + } else { + if (!pos_name.empty()) { + throw BadNameString::MultiPositionalNames(name); + } + if (valid_name_string(name)) { + pos_name = name; + } else { + throw BadNameString::BadPositionalName(name); + } } - return std::make_tuple(short_names, long_names, pos_name); + } + return std::make_tuple(short_names, long_names, pos_name); } -} // namespace detail - - +} // namespace detail class App; /// Holds values to load into Options struct ConfigItem { - /// This is the list of parents - std::vector parents{}; + /// This is the list of parents + std::vector parents{}; - /// This is the name - std::string name{}; - /// Listing of inputs - std::vector inputs{}; - /// @brief indicator if a multiline vector separator was inserted - bool multiline{false}; - /// The list of parents and name joined by "." - CLI11_NODISCARD std::string fullname() const { - std::vector tmp = parents; - tmp.emplace_back(name); - return detail::join(tmp, "."); - (void)multiline; // suppression for cppcheck false positive - } + /// This is the name + std::string name{}; + /// Listing of inputs + std::vector inputs{}; + /// @brief indicator if a multiline vector separator was inserted + bool multiline{false}; + /// The list of parents and name joined by "." + CLI11_NODISCARD std::string fullname() const { + std::vector tmp = parents; + tmp.emplace_back(name); + return detail::join(tmp, "."); + (void)multiline; // suppression for cppcheck false positive + } }; /// This class provides a converter for configuration files. class Config { - protected: - std::vector items{}; +protected: + std::vector items{}; - public: - /// Convert an app into a configuration - virtual std::string to_config(const App *, bool, bool, std::string) const = 0; +public: + /// Convert an app into a configuration + virtual std::string to_config(const App *, bool, bool, std::string) const = 0; - /// Convert a configuration into an app - virtual std::vector from_config(std::istream &) const = 0; + /// Convert a configuration into an app + virtual std::vector from_config(std::istream &) const = 0; - /// Get a flag value - CLI11_NODISCARD virtual std::string to_flag(const ConfigItem &item) const { - if(item.inputs.size() == 1) { - return item.inputs.at(0); - } - if(item.inputs.empty()) { - return "{}"; - } - throw ConversionError::TooManyInputsFlag(item.fullname()); // LCOV_EXCL_LINE + /// Get a flag value + CLI11_NODISCARD virtual std::string to_flag(const ConfigItem &item) const { + if (item.inputs.size() == 1) { + return item.inputs.at(0); } - - /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure - CLI11_NODISCARD std::vector from_file(const std::string &name) const { - std::ifstream input{name}; - if(!input.good()) - throw FileError::Missing(name); - - return from_config(input); + if (item.inputs.empty()) { + return "{}"; } + throw ConversionError::TooManyInputsFlag(item.fullname()); // LCOV_EXCL_LINE + } - /// Virtual destructor - virtual ~Config() = default; + /// Parse a config file, throw an error (ParseError:ConfigParseError or + /// FileError) on failure + CLI11_NODISCARD std::vector + from_file(const std::string &name) const { + std::ifstream input{name}; + if (!input.good()) throw FileError::Missing(name); + + return from_config(input); + } + + /// Virtual destructor + virtual ~Config() = default; }; /// This converter works with INI/TOML files; to write INI files use ConfigINI class ConfigBase : public Config { - protected: - /// the character used for comments - char commentChar = '#'; - /// the character used to start an array '\0' is a default to not use - char arrayStart = '['; - /// the character used to end an array '\0' is a default to not use - char arrayEnd = ']'; - /// the character used to separate elements in an array - char arraySeparator = ','; - /// the character used separate the name from the value - char valueDelimiter = '='; - /// the character to use around strings - char stringQuote = '"'; - /// the character to use around single characters and literal strings - char literalQuote = '\''; - /// the maximum number of layers to allow - uint8_t maximumLayers{255}; - /// the separator used to separator parent layers - char parentSeparatorChar{'.'}; - /// comment default values - bool commentDefaultsBool = false; - /// specify the config reader should collapse repeated field names to a single vector - bool allowMultipleDuplicateFields{false}; - /// Specify the configuration index to use for arrayed sections - int16_t configIndex{-1}; - /// Specify the configuration section that should be used - std::string configSection{}; +protected: + /// the character used for comments + char commentChar = '#'; + /// the character used to start an array '\0' is a default to not use + char arrayStart = '['; + /// the character used to end an array '\0' is a default to not use + char arrayEnd = ']'; + /// the character used to separate elements in an array + char arraySeparator = ','; + /// the character used separate the name from the value + char valueDelimiter = '='; + /// the character to use around strings + char stringQuote = '"'; + /// the character to use around single characters and literal strings + char literalQuote = '\''; + /// the maximum number of layers to allow + uint8_t maximumLayers{255}; + /// the separator used to separator parent layers + char parentSeparatorChar{'.'}; + /// comment default values + bool commentDefaultsBool = false; + /// specify the config reader should collapse repeated field names to a single + /// vector + bool allowMultipleDuplicateFields{false}; + /// Specify the configuration index to use for arrayed sections + int16_t configIndex{-1}; + /// Specify the configuration section that should be used + std::string configSection{}; - public: - std::string - to_config(const App * /*app*/, bool default_also, bool write_description, std::string prefix) const override; +public: + std::string to_config(const App * /*app*/, bool default_also, + bool write_description, + std::string prefix) const override; - std::vector from_config(std::istream &input) const override; - /// Specify the configuration for comment characters - ConfigBase *comment(char cchar) { - commentChar = cchar; - return this; - } - /// Specify the start and end characters for an array - ConfigBase *arrayBounds(char aStart, char aEnd) { - arrayStart = aStart; - arrayEnd = aEnd; - return this; - } - /// Specify the delimiter character for an array - ConfigBase *arrayDelimiter(char aSep) { - arraySeparator = aSep; - return this; - } - /// Specify the delimiter between a name and value - ConfigBase *valueSeparator(char vSep) { - valueDelimiter = vSep; - return this; - } - /// Specify the quote characters used around strings and literal strings - ConfigBase *quoteCharacter(char qString, char literalChar) { - stringQuote = qString; - literalQuote = literalChar; - return this; - } - /// Specify the maximum number of parents - ConfigBase *maxLayers(uint8_t layers) { - maximumLayers = layers; - return this; - } - /// Specify the separator to use for parent layers - ConfigBase *parentSeparator(char sep) { - parentSeparatorChar = sep; - return this; - } - /// comment default value options - ConfigBase *commentDefaults(bool comDef = true) { - commentDefaultsBool = comDef; - return this; - } - /// get a reference to the configuration section - std::string §ionRef() { return configSection; } - /// get the section - CLI11_NODISCARD const std::string §ion() const { return configSection; } - /// specify a particular section of the configuration file to use - ConfigBase *section(const std::string §ionName) { - configSection = sectionName; - return this; - } + std::vector from_config(std::istream &input) const override; + /// Specify the configuration for comment characters + ConfigBase *comment(char cchar) { + commentChar = cchar; + return this; + } + /// Specify the start and end characters for an array + ConfigBase *arrayBounds(char aStart, char aEnd) { + arrayStart = aStart; + arrayEnd = aEnd; + return this; + } + /// Specify the delimiter character for an array + ConfigBase *arrayDelimiter(char aSep) { + arraySeparator = aSep; + return this; + } + /// Specify the delimiter between a name and value + ConfigBase *valueSeparator(char vSep) { + valueDelimiter = vSep; + return this; + } + /// Specify the quote characters used around strings and literal strings + ConfigBase *quoteCharacter(char qString, char literalChar) { + stringQuote = qString; + literalQuote = literalChar; + return this; + } + /// Specify the maximum number of parents + ConfigBase *maxLayers(uint8_t layers) { + maximumLayers = layers; + return this; + } + /// Specify the separator to use for parent layers + ConfigBase *parentSeparator(char sep) { + parentSeparatorChar = sep; + return this; + } + /// comment default value options + ConfigBase *commentDefaults(bool comDef = true) { + commentDefaultsBool = comDef; + return this; + } + /// get a reference to the configuration section + std::string §ionRef() { return configSection; } + /// get the section + CLI11_NODISCARD const std::string §ion() const { return configSection; } + /// specify a particular section of the configuration file to use + ConfigBase *section(const std::string §ionName) { + configSection = sectionName; + return this; + } - /// get a reference to the configuration index - int16_t &indexRef() { return configIndex; } - /// get the section index - CLI11_NODISCARD int16_t index() const { return configIndex; } - /// specify a particular index in the section to use (-1) for all sections to use - ConfigBase *index(int16_t sectionIndex) { - configIndex = sectionIndex; - return this; - } - /// specify that multiple duplicate arguments should be merged even if not sequential - ConfigBase *allowDuplicateFields(bool value = true) { - allowMultipleDuplicateFields = value; - return this; - } + /// get a reference to the configuration index + int16_t &indexRef() { return configIndex; } + /// get the section index + CLI11_NODISCARD int16_t index() const { return configIndex; } + /// specify a particular index in the section to use (-1) for all sections to + /// use + ConfigBase *index(int16_t sectionIndex) { + configIndex = sectionIndex; + return this; + } + /// specify that multiple duplicate arguments should be merged even if not + /// sequential + ConfigBase *allowDuplicateFields(bool value = true) { + allowMultipleDuplicateFields = value; + return this; + } }; /// the default Config is the TOML file format @@ -3781,160 +4219,168 @@ using ConfigTOML = ConfigBase; /// ConfigINI generates a "standard" INI compliant output class ConfigINI : public ConfigTOML { - public: - ConfigINI() { - commentChar = ';'; - arrayStart = '\0'; - arrayEnd = '\0'; - arraySeparator = ' '; - valueDelimiter = '='; - } +public: + ConfigINI() { + commentChar = ';'; + arrayStart = '\0'; + arrayEnd = '\0'; + arraySeparator = ' '; + valueDelimiter = '='; + } }; - - class Option; /// @defgroup validator_group Validators /// @brief Some validators that are provided /// -/// These are simple `std::string(const std::string&)` validators that are useful. They return -/// a string if the validation fails. A custom struct is provided, as well, with the same user -/// semantics, but with the ability to provide a new type name. +/// These are simple `std::string(const std::string&)` validators that are +/// useful. They return a string if the validation fails. A custom struct is +/// provided, as well, with the same user semantics, but with the ability to +/// provide a new type name. /// @{ /// class Validator { - protected: - /// This is the description function, if empty the description_ will be used - std::function desc_function_{[]() { return std::string{}; }}; +protected: + /// This is the description function, if empty the description_ will be used + std::function desc_function_{[]() { return std::string{}; }}; - /// This is the base function that is to be called. - /// Returns a string error message if validation fails. - std::function func_{[](std::string &) { return std::string{}; }}; - /// The name for search purposes of the Validator - std::string name_{}; - /// A Validator will only apply to an indexed value (-1 is all elements) - int application_index_ = -1; - /// Enable for Validator to allow it to be disabled if need be - bool active_{true}; - /// specify that a validator should not modify the input - bool non_modifying_{false}; + /// This is the base function that is to be called. + /// Returns a string error message if validation fails. + std::function func_{ + [](std::string &) { return std::string{}; }}; + /// The name for search purposes of the Validator + std::string name_{}; + /// A Validator will only apply to an indexed value (-1 is all elements) + int application_index_ = -1; + /// Enable for Validator to allow it to be disabled if need be + bool active_{true}; + /// specify that a validator should not modify the input + bool non_modifying_{false}; - Validator(std::string validator_desc, std::function func) - : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(func)) {} + Validator(std::string validator_desc, + std::function func) + : desc_function_([validator_desc]() { return validator_desc; }), + func_(std::move(func)) {} - public: - Validator() = default; - /// Construct a Validator with just the description string - explicit Validator(std::string validator_desc) : desc_function_([validator_desc]() { return validator_desc; }) {} - /// Construct Validator from basic information - Validator(std::function op, std::string validator_desc, std::string validator_name = "") - : desc_function_([validator_desc]() { return validator_desc; }), func_(std::move(op)), - name_(std::move(validator_name)) {} - /// Set the Validator operation function - Validator &operation(std::function op) { - func_ = std::move(op); - return *this; +public: + Validator() = default; + /// Construct a Validator with just the description string + explicit Validator(std::string validator_desc) + : desc_function_([validator_desc]() { return validator_desc; }) {} + /// Construct Validator from basic information + Validator(std::function op, + std::string validator_desc, std::string validator_name = "") + : desc_function_([validator_desc]() { return validator_desc; }), + func_(std::move(op)), name_(std::move(validator_name)) {} + /// Set the Validator operation function + Validator &operation(std::function op) { + func_ = std::move(op); + return *this; + } + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(std::string &str) const; + + /// This is the required operator for a Validator - provided to help + /// users (CLI11 uses the member `func` directly) + std::string operator()(const std::string &str) const { + std::string value = str; + return (active_) ? func_(value) : std::string{}; + } + + /// Specify the type string + Validator &description(std::string validator_desc) { + desc_function_ = [validator_desc]() { return validator_desc; }; + return *this; + } + /// Specify the type string + CLI11_NODISCARD Validator description(std::string validator_desc) const; + + /// Generate type description information for the Validator + CLI11_NODISCARD std::string get_description() const { + if (active_) { + return desc_function_(); } - /// This is the required operator for a Validator - provided to help - /// users (CLI11 uses the member `func` directly) - std::string operator()(std::string &str) const; + return std::string{}; + } + /// Specify the type string + Validator &name(std::string validator_name) { + name_ = std::move(validator_name); + return *this; + } + /// Specify the type string + CLI11_NODISCARD Validator name(std::string validator_name) const { + Validator newval(*this); + newval.name_ = std::move(validator_name); + return newval; + } + /// Get the name of the Validator + CLI11_NODISCARD const std::string &get_name() const { return name_; } + /// Specify whether the Validator is active or not + Validator &active(bool active_val = true) { + active_ = active_val; + return *this; + } + /// Specify whether the Validator is active or not + CLI11_NODISCARD Validator active(bool active_val = true) const { + Validator newval(*this); + newval.active_ = active_val; + return newval; + } - /// This is the required operator for a Validator - provided to help - /// users (CLI11 uses the member `func` directly) - std::string operator()(const std::string &str) const { - std::string value = str; - return (active_) ? func_(value) : std::string{}; - } + /// Specify whether the Validator can be modifying or not + Validator &non_modifying(bool no_modify = true) { + non_modifying_ = no_modify; + return *this; + } + /// Specify the application index of a validator + Validator &application_index(int app_index) { + application_index_ = app_index; + return *this; + } + /// Specify the application index of a validator + CLI11_NODISCARD Validator application_index(int app_index) const { + Validator newval(*this); + newval.application_index_ = app_index; + return newval; + } + /// Get the current value of the application index + CLI11_NODISCARD int get_application_index() const { + return application_index_; + } + /// Get a boolean if the validator is active + CLI11_NODISCARD bool get_active() const { return active_; } - /// Specify the type string - Validator &description(std::string validator_desc) { - desc_function_ = [validator_desc]() { return validator_desc; }; - return *this; - } - /// Specify the type string - CLI11_NODISCARD Validator description(std::string validator_desc) const; + /// Get a boolean if the validator is allowed to modify the input returns true + /// if it can modify the input + CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; } - /// Generate type description information for the Validator - CLI11_NODISCARD std::string get_description() const { - if(active_) { - return desc_function_(); - } - return std::string{}; - } - /// Specify the type string - Validator &name(std::string validator_name) { - name_ = std::move(validator_name); - return *this; - } - /// Specify the type string - CLI11_NODISCARD Validator name(std::string validator_name) const { - Validator newval(*this); - newval.name_ = std::move(validator_name); - return newval; - } - /// Get the name of the Validator - CLI11_NODISCARD const std::string &get_name() const { return name_; } - /// Specify whether the Validator is active or not - Validator &active(bool active_val = true) { - active_ = active_val; - return *this; - } - /// Specify whether the Validator is active or not - CLI11_NODISCARD Validator active(bool active_val = true) const { - Validator newval(*this); - newval.active_ = active_val; - return newval; - } + /// Combining validators is a new validator. Type comes from left validator if + /// function, otherwise only set if the same. + Validator operator&(const Validator &other) const; - /// Specify whether the Validator can be modifying or not - Validator &non_modifying(bool no_modify = true) { - non_modifying_ = no_modify; - return *this; - } - /// Specify the application index of a validator - Validator &application_index(int app_index) { - application_index_ = app_index; - return *this; - } - /// Specify the application index of a validator - CLI11_NODISCARD Validator application_index(int app_index) const { - Validator newval(*this); - newval.application_index_ = app_index; - return newval; - } - /// Get the current value of the application index - CLI11_NODISCARD int get_application_index() const { return application_index_; } - /// Get a boolean if the validator is active - CLI11_NODISCARD bool get_active() const { return active_; } + /// Combining validators is a new validator. Type comes from left validator if + /// function, otherwise only set if the same. + Validator operator|(const Validator &other) const; - /// Get a boolean if the validator is allowed to modify the input returns true if it can modify the input - CLI11_NODISCARD bool get_modifying() const { return !non_modifying_; } + /// Create a validator that fails when a given validator succeeds + Validator operator!() const; - /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the - /// same. - Validator operator&(const Validator &other) const; - - /// Combining validators is a new validator. Type comes from left validator if function, otherwise only set if the - /// same. - Validator operator|(const Validator &other) const; - - /// Create a validator that fails when a given validator succeeds - Validator operator!() const; - - private: - void _merge_description(const Validator &val1, const Validator &val2, const std::string &merger); +private: + void _merge_description(const Validator &val1, const Validator &val2, + const std::string &merger); }; /// Class wrapping some of the accessors of Validator class CustomValidator : public Validator { - public: +public: }; // The implementation of the built in validators is using the Validator class; -// the user is only expected to use the const (static) versions (since there's no setup). -// Therefore, this is in detail. +// the user is only expected to use the const (static) versions (since there's +// no setup). Therefore, this is in detail. namespace detail { /// CLI enumeration of different file types @@ -3945,40 +4391,40 @@ CLI11_INLINE path_type check_path(const char *file) noexcept; /// Check for an existing file (returns error message if check fails) class ExistingFileValidator : public Validator { - public: - ExistingFileValidator(); +public: + ExistingFileValidator(); }; /// Check for an existing directory (returns error message if check fails) class ExistingDirectoryValidator : public Validator { - public: - ExistingDirectoryValidator(); +public: + ExistingDirectoryValidator(); }; /// Check for an existing path class ExistingPathValidator : public Validator { - public: - ExistingPathValidator(); +public: + ExistingPathValidator(); }; /// Check for an non-existing path class NonexistentPathValidator : public Validator { - public: - NonexistentPathValidator(); +public: + NonexistentPathValidator(); }; /// Validate the given string is a legal ipv4 address class IPV4Validator : public Validator { - public: - IPV4Validator(); +public: + IPV4Validator(); }; class EscapedStringTransformer : public Validator { - public: - EscapedStringTransformer(); +public: + EscapedStringTransformer(); }; -} // namespace detail +} // namespace detail // Static is not needed here, because global const implies static. @@ -4002,459 +4448,575 @@ const detail::EscapedStringTransformer EscapedString; /// Validate the input as a particular type template class TypeValidator : public Validator { - public: - explicit TypeValidator(const std::string &validator_name) - : Validator(validator_name, [](std::string &input_string) { - using CLI::detail::lexical_cast; - auto val = DesiredType(); - if(!lexical_cast(input_string, val)) { - return std::string("Failed parsing ") + input_string + " as a " + detail::type_name(); - } - return std::string(); - }) {} - TypeValidator() : TypeValidator(detail::type_name()) {} +public: + explicit TypeValidator(const std::string &validator_name) + : Validator(validator_name, [](std::string &input_string) { + using CLI::detail::lexical_cast; + auto val = DesiredType(); + if (!lexical_cast(input_string, val)) { + return std::string("Failed parsing ") + input_string + " as a " + + detail::type_name(); + } + return std::string(); + }) {} + TypeValidator() : TypeValidator(detail::type_name()) {} }; /// Check for a number const TypeValidator Number("NUMBER"); -/// Modify a path if the file is a particular default location, can be used as Check or transform -/// with the error return optionally disabled +/// Modify a path if the file is a particular default location, can be used as +/// Check or transform with the error return optionally disabled class FileOnDefaultPath : public Validator { - public: - explicit FileOnDefaultPath(std::string default_path, bool enableErrorReturn = true); +public: + explicit FileOnDefaultPath(std::string default_path, + bool enableErrorReturn = true); }; /// Produce a range (factory). Min and max are inclusive. class Range : public Validator { - public: - /// This produces a range with min and max inclusive. - /// - /// Note that the constructor is templated, but the struct is not, so C++17 is not - /// needed to provide nice syntax for Range(a,b). - template - Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) { - if(validator_name.empty()) { - std::stringstream out; - out << detail::type_name() << " in [" << min_val << " - " << max_val << "]"; - description(out.str()); - } - - func_ = [min_val, max_val](std::string &input) { - using CLI::detail::lexical_cast; - T val; - bool converted = lexical_cast(input, val); - if((!converted) || (val < min_val || val > max_val)) { - std::stringstream out; - out << "Value " << input << " not in range ["; - out << min_val << " - " << max_val << "]"; - return out.str(); - } - return std::string{}; - }; +public: + /// This produces a range with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is + /// not needed to provide nice syntax for Range(a,b). + template + Range(T min_val, T max_val, const std::string &validator_name = std::string{}) + : Validator(validator_name) { + if (validator_name.empty()) { + std::stringstream out; + out << detail::type_name() << " in [" << min_val << " - " << max_val + << "]"; + description(out.str()); } - /// Range of one value is 0 to value - template - explicit Range(T max_val, const std::string &validator_name = std::string{}) - : Range(static_cast(0), max_val, validator_name) {} + func_ = [min_val, max_val](std::string &input) { + using CLI::detail::lexical_cast; + T val; + bool converted = lexical_cast(input, val); + if ((!converted) || (val < min_val || val > max_val)) { + std::stringstream out; + out << "Value " << input << " not in range ["; + out << min_val << " - " << max_val << "]"; + return out.str(); + } + return std::string{}; + }; + } + + /// Range of one value is 0 to value + template + explicit Range(T max_val, const std::string &validator_name = std::string{}) + : Range(static_cast(0), max_val, validator_name) {} }; /// Check for a non negative number -const Range NonNegativeNumber((std::numeric_limits::max)(), "NONNEGATIVE"); +const Range NonNegativeNumber((std::numeric_limits::max)(), + "NONNEGATIVE"); -/// Check for a positive valued number (val>0.0), ::min here is the smallest positive number -const Range PositiveNumber((std::numeric_limits::min)(), (std::numeric_limits::max)(), "POSITIVE"); +/// Check for a positive valued number (val>0.0), ::min here is the +/// smallest positive number +const Range PositiveNumber((std::numeric_limits::min)(), + (std::numeric_limits::max)(), "POSITIVE"); /// Produce a bounded range (factory). Min and max are inclusive. class Bound : public Validator { - public: - /// This bounds a value with min and max inclusive. - /// - /// Note that the constructor is templated, but the struct is not, so C++17 is not - /// needed to provide nice syntax for Range(a,b). - template Bound(T min_val, T max_val) { - std::stringstream out; - out << detail::type_name() << " bounded to [" << min_val << " - " << max_val << "]"; - description(out.str()); +public: + /// This bounds a value with min and max inclusive. + /// + /// Note that the constructor is templated, but the struct is not, so C++17 is + /// not needed to provide nice syntax for Range(a,b). + template Bound(T min_val, T max_val) { + std::stringstream out; + out << detail::type_name() << " bounded to [" << min_val << " - " + << max_val << "]"; + description(out.str()); - func_ = [min_val, max_val](std::string &input) { - using CLI::detail::lexical_cast; - T val; - bool converted = lexical_cast(input, val); - if(!converted) { - return std::string("Value ") + input + " could not be converted"; - } - if(val < min_val) - input = detail::to_string(min_val); - else if(val > max_val) - input = detail::to_string(max_val); + func_ = [min_val, max_val](std::string &input) { + using CLI::detail::lexical_cast; + T val; + bool converted = lexical_cast(input, val); + if (!converted) { + return std::string("Value ") + input + " could not be converted"; + } + if (val < min_val) input = detail::to_string(min_val); + else if (val > max_val) input = detail::to_string(max_val); - return std::string{}; - }; - } + return std::string{}; + }; + } - /// Range of one value is 0 to value - template explicit Bound(T max_val) : Bound(static_cast(0), max_val) {} + /// Range of one value is 0 to value + template + explicit Bound(T max_val) : Bound(static_cast(0), max_val) {} }; namespace detail { -template ::type>::value, detail::enabler> = detail::dummy> -auto smart_deref(T value) -> decltype(*value) { - return *value; -} - template < typename T, - enable_if_t::type>::value, detail::enabler> = detail::dummy> + enable_if_t::type>::value, + detail::enabler> = detail::dummy> +auto smart_deref(T value) -> decltype(*value) { + return *value; +} + +template ::type>::value, + detail::enabler> = detail::dummy> typename std::remove_reference::type &smart_deref(T &value) { - return value; + return value; } /// Generate a string representation of a set template std::string generate_set(const T &set) { - using element_t = typename detail::element_type::type; - using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair - std::string out(1, '{'); - out.append(detail::join( - detail::smart_deref(set), - [](const iteration_type_t &v) { return detail::pair_adaptor::first(v); }, - ",")); - out.push_back('}'); - return out; + using element_t = typename detail::element_type::type; + using iteration_type_t = + typename detail::pair_adaptor::value_type; // the type of the + // object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(set), + [](const iteration_type_t &v) { + return detail::pair_adaptor::first(v); + }, + ",")); + out.push_back('}'); + return out; } /// Generate a string representation of a map -template std::string generate_map(const T &map, bool key_only = false) { - using element_t = typename detail::element_type::type; - using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair - std::string out(1, '{'); - out.append(detail::join( - detail::smart_deref(map), - [key_only](const iteration_type_t &v) { - std::string res{detail::to_string(detail::pair_adaptor::first(v))}; +template +std::string generate_map(const T &map, bool key_only = false) { + using element_t = typename detail::element_type::type; + using iteration_type_t = + typename detail::pair_adaptor::value_type; // the type of the + // object pair + std::string out(1, '{'); + out.append(detail::join( + detail::smart_deref(map), + [key_only](const iteration_type_t &v) { + std::string res{ + detail::to_string(detail::pair_adaptor::first(v))}; - if(!key_only) { - res.append("->"); - res += detail::to_string(detail::pair_adaptor::second(v)); - } - return res; - }, - ",")); - out.push_back('}'); - return out; + if (!key_only) { + res.append("->"); + res += detail::to_string(detail::pair_adaptor::second(v)); + } + return res; + }, + ",")); + out.push_back('}'); + return out; } template struct has_find { - template - static auto test(int) -> decltype(std::declval().find(std::declval()), std::true_type()); - template static auto test(...) -> decltype(std::false_type()); + template + static auto test(int) -> decltype(std::declval().find(std::declval()), + std::true_type()); + template + static auto test(...) -> decltype(std::false_type()); - static const auto value = decltype(test(0))::value; - using type = std::integral_constant; + static const auto value = decltype(test(0))::value; + using type = std::integral_constant; }; /// A search function -template ::value, detail::enabler> = detail::dummy> -auto search(const T &set, const V &val) -> std::pair { - using element_t = typename detail::element_type::type; - auto &setref = detail::smart_deref(set); - auto it = std::find_if(std::begin(setref), std::end(setref), [&val](decltype(*std::begin(setref)) v) { - return (detail::pair_adaptor::first(v) == val); - }); - return {(it != std::end(setref)), it}; +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) + -> std::pair { + using element_t = typename detail::element_type::type; + auto &setref = detail::smart_deref(set); + auto it = + std::find_if(std::begin(setref), std::end(setref), + [&val](decltype(*std::begin(setref)) v) { + return (detail::pair_adaptor::first(v) == val); + }); + return {(it != std::end(setref)), it}; } /// A search function that uses the built in find function -template ::value, detail::enabler> = detail::dummy> -auto search(const T &set, const V &val) -> std::pair { - auto &setref = detail::smart_deref(set); - auto it = setref.find(val); - return {(it != std::end(setref)), it}; +template ::value, detail::enabler> = detail::dummy> +auto search(const T &set, const V &val) + -> std::pair { + auto &setref = detail::smart_deref(set); + auto it = setref.find(val); + return {(it != std::end(setref)), it}; } /// A search function with a filter function template -auto search(const T &set, const V &val, const std::function &filter_function) +auto search(const T &set, const V &val, + const std::function &filter_function) -> std::pair { - using element_t = typename detail::element_type::type; - // do the potentially faster first search - auto res = search(set, val); - if((res.first) || (!(filter_function))) { - return res; - } - // if we haven't found it do the longer linear search with all the element translations - auto &setref = detail::smart_deref(set); - auto it = std::find_if(std::begin(setref), std::end(setref), [&](decltype(*std::begin(setref)) v) { - V a{detail::pair_adaptor::first(v)}; - a = filter_function(a); - return (a == val); - }); - return {(it != std::end(setref)), it}; + using element_t = typename detail::element_type::type; + // do the potentially faster first search + auto res = search(set, val); + if ((res.first) || (!(filter_function))) { + return res; + } + // if we haven't found it do the longer linear search with all the element + // translations + auto &setref = detail::smart_deref(set); + auto it = std::find_if(std::begin(setref), std::end(setref), + [&](decltype(*std::begin(setref)) v) { + V a{detail::pair_adaptor::first(v)}; + a = filter_function(a); + return (a == val); + }); + return {(it != std::end(setref)), it}; } // the following suggestion was made by Nikita Ofitserov(@himikof) -// done in templates to prevent compiler warnings on negation of unsigned numbers +// done in templates to prevent compiler warnings on negation of unsigned +// numbers /// Do a check for overflow on signed numbers template -inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { - if((a > 0) == (b > 0)) { - return ((std::numeric_limits::max)() / (std::abs)(a) < (std::abs)(b)); - } - return ((std::numeric_limits::min)() / (std::abs)(a) > -(std::abs)(b)); +inline typename std::enable_if::value, T>::type +overflowCheck(const T &a, const T &b) { + if ((a > 0) == (b > 0)) { + return ((std::numeric_limits::max)() / (std::abs)(a) < (std::abs)(b)); + } + return ((std::numeric_limits::min)() / (std::abs)(a) > -(std::abs)(b)); } /// Do a check for overflow on unsigned numbers template -inline typename std::enable_if::value, T>::type overflowCheck(const T &a, const T &b) { - return ((std::numeric_limits::max)() / a < b); +inline typename std::enable_if::value, T>::type +overflowCheck(const T &a, const T &b) { + return ((std::numeric_limits::max)() / a < b); } -/// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. -template typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { - if(a == 0 || b == 0 || a == 1 || b == 1) { - a *= b; - return true; - } - if(a == (std::numeric_limits::min)() || b == (std::numeric_limits::min)()) { - return false; - } - if(overflowCheck(a, b)) { - return false; - } +/// Performs a *= b; if it doesn't cause integer overflow. Returns false +/// otherwise. +template +typename std::enable_if::value, bool>::type +checked_multiply(T &a, T b) { + if (a == 0 || b == 0 || a == 1 || b == 1) { a *= b; return true; + } + if (a == (std::numeric_limits::min)() || + b == (std::numeric_limits::min)()) { + return false; + } + if (overflowCheck(a, b)) { + return false; + } + a *= b; + return true; } /// Performs a *= b; if it doesn't equal infinity. Returns false otherwise. template -typename std::enable_if::value, bool>::type checked_multiply(T &a, T b) { - T c = a * b; - if(std::isinf(c) && !std::isinf(a) && !std::isinf(b)) { - return false; - } - a = c; - return true; +typename std::enable_if::value, bool>::type +checked_multiply(T &a, T b) { + T c = a * b; + if (std::isinf(c) && !std::isinf(a) && !std::isinf(b)) { + return false; + } + a = c; + return true; } -} // namespace detail +} // namespace detail /// Verify items are in a set class IsMember : public Validator { - public: - using filter_fn_t = std::function; +public: + using filter_fn_t = std::function; - /// This allows in-place construction using an initializer list - template - IsMember(std::initializer_list values, Args &&...args) - : IsMember(std::vector(values), std::forward(args)...) {} + /// This allows in-place construction using an initializer list + template + IsMember(std::initializer_list values, Args &&...args) + : IsMember(std::vector(values), std::forward(args)...) {} - /// This checks to see if an item is in a set (empty function) - template explicit IsMember(T &&set) : IsMember(std::forward(set), nullptr) {} + /// This checks to see if an item is in a set (empty function) + template + explicit IsMember(T &&set) : IsMember(std::forward(set), nullptr) {} - /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter - /// both sides of the comparison before computing the comparison. - template explicit IsMember(T set, F filter_function) { + /// This checks to see if an item is in a set: pointer or copy version. You + /// can pass in a function that will filter both sides of the comparison + /// before computing the comparison. + template + explicit IsMember(T set, F filter_function) { - // Get the type of the contained item - requires a container have ::value_type - // if the type does not have first_type and second_type, these are both value_type - using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed - using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map + // Get the type of the contained item - requires a container have + // ::value_type if the type does not have first_type and second_type, these + // are both value_type + using element_t = + typename detail::element_type::type; // Removes (smart) pointers if + // needed + using item_t = + typename detail::pair_adaptor::first_type; // Is value_type + // if not a map - using local_item_t = typename IsMemberType::type; // This will convert bad types to good ones - // (const char * to std::string) + using local_item_t = + typename IsMemberType::type; // This will convert bad types to + // good ones (const char * to + // std::string) - // Make a local copy of the filter function, using a std::function if not one already - std::function filter_fn = filter_function; + // Make a local copy of the filter function, using a std::function if not + // one already + std::function filter_fn = filter_function; - // This is the type name for help, it will take the current version of the set contents - desc_function_ = [set]() { return detail::generate_set(detail::smart_deref(set)); }; + // This is the type name for help, it will take the current version of the + // set contents + desc_function_ = [set]() { + return detail::generate_set(detail::smart_deref(set)); + }; - // This is the function that validates - // It stores a copy of the set pointer-like, so shared_ptr will stay alive - func_ = [set, filter_fn](std::string &input) { - using CLI::detail::lexical_cast; - local_item_t b; - if(!lexical_cast(input, b)) { - throw ValidationError(input); // name is added later - } - if(filter_fn) { - b = filter_fn(b); - } - auto res = detail::search(set, b, filter_fn); - if(res.first) { - // Make sure the version in the input string is identical to the one in the set - if(filter_fn) { - input = detail::value_string(detail::pair_adaptor::first(*(res.second))); - } + // This is the function that validates + // It stores a copy of the set pointer-like, so shared_ptr will stay alive + func_ = [set, filter_fn](std::string &input) { + using CLI::detail::lexical_cast; + local_item_t b; + if (!lexical_cast(input, b)) { + throw ValidationError(input); // name is added later + } + if (filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(set, b, filter_fn); + if (res.first) { + // Make sure the version in the input string is identical to the one in + // the set + if (filter_fn) { + input = detail::value_string( + detail::pair_adaptor::first(*(res.second))); + } - // Return empty error string (success) - return std::string{}; - } + // Return empty error string (success) + return std::string{}; + } - // If you reach this point, the result was not found - return input + " not in " + detail::generate_set(detail::smart_deref(set)); - }; - } + // If you reach this point, the result was not found + return input + " not in " + + detail::generate_set(detail::smart_deref(set)); + }; + } - /// You can pass in as many filter functions as you like, they nest (string only currently) - template - IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) - : IsMember( - std::forward(set), - [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, - other...) {} + /// You can pass in as many filter functions as you like, they nest (string + /// only currently) + template + IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, + Args &&...other) + : IsMember( + std::forward(set), + [filter_fn_1, filter_fn_2](std::string a) { + return filter_fn_2(filter_fn_1(a)); + }, + other...) {} }; /// definition of the default transformation object -template using TransformPairs = std::vector>; +template +using TransformPairs = std::vector>; /// Translate named items to other or a value set class Transformer : public Validator { - public: - using filter_fn_t = std::function; +public: + using filter_fn_t = std::function; - /// This allows in-place construction - template - Transformer(std::initializer_list> values, Args &&...args) - : Transformer(TransformPairs(values), std::forward(args)...) {} + /// This allows in-place construction + template + Transformer(std::initializer_list> values, + Args &&...args) + : Transformer(TransformPairs(values), + std::forward(args)...) {} - /// direct map of std::string to std::string - template explicit Transformer(T &&mapping) : Transformer(std::forward(mapping), nullptr) {} + /// direct map of std::string to std::string + template + explicit Transformer(T &&mapping) + : Transformer(std::forward(mapping), nullptr) {} - /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter - /// both sides of the comparison before computing the comparison. - template explicit Transformer(T mapping, F filter_function) { + /// This checks to see if an item is in a set: pointer or copy version. You + /// can pass in a function that will filter both sides of the comparison + /// before computing the comparison. + template + explicit Transformer(T mapping, F filter_function) { - static_assert(detail::pair_adaptor::type>::value, - "mapping must produce value pairs"); - // Get the type of the contained item - requires a container have ::value_type - // if the type does not have first_type and second_type, these are both value_type - using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed - using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map - using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones - // (const char * to std::string) + static_assert( + detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have + // ::value_type if the type does not have first_type and second_type, these + // are both value_type + using element_t = + typename detail::element_type::type; // Removes (smart) pointers if + // needed + using item_t = + typename detail::pair_adaptor::first_type; // Is value_type + // if not a map + using local_item_t = + typename IsMemberType::type; // Will convert bad types to good + // ones (const char * to + // std::string) - // Make a local copy of the filter function, using a std::function if not one already - std::function filter_fn = filter_function; + // Make a local copy of the filter function, using a std::function if not + // one already + std::function filter_fn = filter_function; - // This is the type name for help, it will take the current version of the set contents - desc_function_ = [mapping]() { return detail::generate_map(detail::smart_deref(mapping)); }; + // This is the type name for help, it will take the current version of the + // set contents + desc_function_ = [mapping]() { + return detail::generate_map(detail::smart_deref(mapping)); + }; - func_ = [mapping, filter_fn](std::string &input) { - using CLI::detail::lexical_cast; - local_item_t b; - if(!lexical_cast(input, b)) { - return std::string(); - // there is no possible way we can match anything in the mapping if we can't convert so just return - } - if(filter_fn) { - b = filter_fn(b); - } - auto res = detail::search(mapping, b, filter_fn); - if(res.first) { - input = detail::value_string(detail::pair_adaptor::second(*res.second)); - } - return std::string{}; - }; - } + func_ = [mapping, filter_fn](std::string &input) { + using CLI::detail::lexical_cast; + local_item_t b; + if (!lexical_cast(input, b)) { + return std::string(); + // there is no possible way we can match anything in the mapping if we + // can't convert so just return + } + if (filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if (res.first) { + input = detail::value_string( + detail::pair_adaptor::second(*res.second)); + } + return std::string{}; + }; + } - /// You can pass in as many filter functions as you like, they nest - template - Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) - : Transformer( - std::forward(mapping), - [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, - other...) {} + /// You can pass in as many filter functions as you like, they nest + template + Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, + Args &&...other) + : Transformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { + return filter_fn_2(filter_fn_1(a)); + }, + other...) {} }; /// translate named items to other or a value set class CheckedTransformer : public Validator { - public: - using filter_fn_t = std::function; +public: + using filter_fn_t = std::function; - /// This allows in-place construction - template - CheckedTransformer(std::initializer_list> values, Args &&...args) - : CheckedTransformer(TransformPairs(values), std::forward(args)...) {} + /// This allows in-place construction + template + CheckedTransformer( + std::initializer_list> values, + Args &&...args) + : CheckedTransformer(TransformPairs(values), + std::forward(args)...) {} - /// direct map of std::string to std::string - template explicit CheckedTransformer(T mapping) : CheckedTransformer(std::move(mapping), nullptr) {} + /// direct map of std::string to std::string + template + explicit CheckedTransformer(T mapping) + : CheckedTransformer(std::move(mapping), nullptr) {} - /// This checks to see if an item is in a set: pointer or copy version. You can pass in a function that will filter - /// both sides of the comparison before computing the comparison. - template explicit CheckedTransformer(T mapping, F filter_function) { + /// This checks to see if an item is in a set: pointer or copy version. You + /// can pass in a function that will filter both sides of the comparison + /// before computing the comparison. + template + explicit CheckedTransformer(T mapping, F filter_function) { - static_assert(detail::pair_adaptor::type>::value, - "mapping must produce value pairs"); - // Get the type of the contained item - requires a container have ::value_type - // if the type does not have first_type and second_type, these are both value_type - using element_t = typename detail::element_type::type; // Removes (smart) pointers if needed - using item_t = typename detail::pair_adaptor::first_type; // Is value_type if not a map - using local_item_t = typename IsMemberType::type; // Will convert bad types to good ones - // (const char * to std::string) - using iteration_type_t = typename detail::pair_adaptor::value_type; // the type of the object pair + static_assert( + detail::pair_adaptor::type>::value, + "mapping must produce value pairs"); + // Get the type of the contained item - requires a container have + // ::value_type if the type does not have first_type and second_type, these + // are both value_type + using element_t = + typename detail::element_type::type; // Removes (smart) pointers if + // needed + using item_t = + typename detail::pair_adaptor::first_type; // Is value_type + // if not a map + using local_item_t = + typename IsMemberType::type; // Will convert bad types to good + // ones (const char * to + // std::string) + using iteration_type_t = + typename detail::pair_adaptor::value_type; // the type of the + // object pair - // Make a local copy of the filter function, using a std::function if not one already - std::function filter_fn = filter_function; + // Make a local copy of the filter function, using a std::function if not + // one already + std::function filter_fn = filter_function; - auto tfunc = [mapping]() { - std::string out("value in "); - out += detail::generate_map(detail::smart_deref(mapping)) + " OR {"; - out += detail::join( - detail::smart_deref(mapping), - [](const iteration_type_t &v) { return detail::to_string(detail::pair_adaptor::second(v)); }, - ","); - out.push_back('}'); - return out; - }; + auto tfunc = [mapping]() { + std::string out("value in "); + out += detail::generate_map(detail::smart_deref(mapping)) + " OR {"; + out += detail::join( + detail::smart_deref(mapping), + [](const iteration_type_t &v) { + return detail::to_string( + detail::pair_adaptor::second(v)); + }, + ","); + out.push_back('}'); + return out; + }; - desc_function_ = tfunc; + desc_function_ = tfunc; - func_ = [mapping, tfunc, filter_fn](std::string &input) { - using CLI::detail::lexical_cast; - local_item_t b; - bool converted = lexical_cast(input, b); - if(converted) { - if(filter_fn) { - b = filter_fn(b); - } - auto res = detail::search(mapping, b, filter_fn); - if(res.first) { - input = detail::value_string(detail::pair_adaptor::second(*res.second)); - return std::string{}; - } - } - for(const auto &v : detail::smart_deref(mapping)) { - auto output_string = detail::value_string(detail::pair_adaptor::second(v)); - if(output_string == input) { - return std::string(); - } - } + func_ = [mapping, tfunc, filter_fn](std::string &input) { + using CLI::detail::lexical_cast; + local_item_t b; + bool converted = lexical_cast(input, b); + if (converted) { + if (filter_fn) { + b = filter_fn(b); + } + auto res = detail::search(mapping, b, filter_fn); + if (res.first) { + input = detail::value_string( + detail::pair_adaptor::second(*res.second)); + return std::string{}; + } + } + for (const auto &v : detail::smart_deref(mapping)) { + auto output_string = + detail::value_string(detail::pair_adaptor::second(v)); + if (output_string == input) { + return std::string(); + } + } - return "Check " + input + " " + tfunc() + " FAILED"; - }; - } + return "Check " + input + " " + tfunc() + " FAILED"; + }; + } - /// You can pass in as many filter functions as you like, they nest - template - CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other) - : CheckedTransformer( - std::forward(mapping), - [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); }, - other...) {} + /// You can pass in as many filter functions as you like, they nest + template + CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, + filter_fn_t filter_fn_2, Args &&...other) + : CheckedTransformer( + std::forward(mapping), + [filter_fn_1, filter_fn_2](std::string a) { + return filter_fn_2(filter_fn_1(a)); + }, + other...) {} }; /// Helper function to allow ignore_case to be passed to IsMember or Transform -inline std::string ignore_case(std::string item) { return detail::to_lower(item); } +inline std::string ignore_case(std::string item) { + return detail::to_lower(item); +} -/// Helper function to allow ignore_underscore to be passed to IsMember or Transform -inline std::string ignore_underscore(std::string item) { return detail::remove_underscore(item); } +/// Helper function to allow ignore_underscore to be passed to IsMember or +/// Transform +inline std::string ignore_underscore(std::string item) { + return detail::remove_underscore(item); +} -/// Helper function to allow checks to ignore spaces to be passed to IsMember or Transform +/// Helper function to allow checks to ignore spaces to be passed to IsMember or +/// Transform inline std::string ignore_space(std::string item) { - item.erase(std::remove(std::begin(item), std::end(item), ' '), std::end(item)); - item.erase(std::remove(std::begin(item), std::end(item), '\t'), std::end(item)); - return item; + item.erase(std::remove(std::begin(item), std::end(item), ' '), + std::end(item)); + item.erase(std::remove(std::begin(item), std::end(item), '\t'), + std::end(item)); + return item; } /// Multiply a number by a factor using given mapping. @@ -4469,527 +5031,552 @@ inline std::string ignore_space(std::string item) { /// Therefore, if it is required to interpret real inputs like "0.42 s", /// the mapping should be of a type or . class AsNumberWithUnit : public Validator { - public: - /// Adjust AsNumberWithUnit behavior. - /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched. - /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError - /// if UNIT_REQUIRED is set and unit literal is not found. - enum Options { - CASE_SENSITIVE = 0, - CASE_INSENSITIVE = 1, - UNIT_OPTIONAL = 0, - UNIT_REQUIRED = 2, - DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL +public: + /// Adjust AsNumberWithUnit behavior. + /// CASE_SENSITIVE/CASE_INSENSITIVE controls how units are matched. + /// UNIT_OPTIONAL/UNIT_REQUIRED throws ValidationError + /// if UNIT_REQUIRED is set and unit literal is not found. + enum Options { + CASE_SENSITIVE = 0, + CASE_INSENSITIVE = 1, + UNIT_OPTIONAL = 0, + UNIT_REQUIRED = 2, + DEFAULT = CASE_INSENSITIVE | UNIT_OPTIONAL + }; + + template + explicit AsNumberWithUnit(std::map mapping, + Options opts = DEFAULT, + const std::string &unit_name = "UNIT") { + description(generate_description(unit_name, opts)); + validate_mapping(mapping, opts); + + // transform function + func_ = [mapping, opts](std::string &input) -> std::string { + Number num{}; + + detail::rtrim(input); + if (input.empty()) { + throw ValidationError("Input is empty"); + } + + // Find split position between number and prefix + auto unit_begin = input.end(); + while (unit_begin > input.begin() && + std::isalpha(*(unit_begin - 1), std::locale())) { + --unit_begin; + } + + std::string unit{unit_begin, input.end()}; + input.resize( + static_cast(std::distance(input.begin(), unit_begin))); + detail::trim(input); + + if (opts & UNIT_REQUIRED && unit.empty()) { + throw ValidationError("Missing mandatory unit"); + } + if (opts & CASE_INSENSITIVE) { + unit = detail::to_lower(unit); + } + if (unit.empty()) { + using CLI::detail::lexical_cast; + if (!lexical_cast(input, num)) { + throw ValidationError(std::string("Value ") + input + + " could not be converted to " + + detail::type_name()); + } + // No need to modify input if no unit passed + return {}; + } + + // find corresponding factor + auto it = mapping.find(unit); + if (it == mapping.end()) { + throw ValidationError(unit + + " unit not recognized. " + "Allowed values: " + + detail::generate_map(mapping, true)); + } + + if (!input.empty()) { + using CLI::detail::lexical_cast; + bool converted = lexical_cast(input, num); + if (!converted) { + throw ValidationError(std::string("Value ") + input + + " could not be converted to " + + detail::type_name()); + } + // perform safe multiplication + bool ok = detail::checked_multiply(num, it->second); + if (!ok) { + throw ValidationError( + detail::to_string(num) + " multiplied by " + unit + + " factor would cause number overflow. Use smaller value."); + } + } else { + num = static_cast(it->second); + } + + input = detail::to_string(num); + + return {}; }; + } - template - explicit AsNumberWithUnit(std::map mapping, - Options opts = DEFAULT, - const std::string &unit_name = "UNIT") { - description(generate_description(unit_name, opts)); - validate_mapping(mapping, opts); - - // transform function - func_ = [mapping, opts](std::string &input) -> std::string { - Number num{}; - - detail::rtrim(input); - if(input.empty()) { - throw ValidationError("Input is empty"); - } - - // Find split position between number and prefix - auto unit_begin = input.end(); - while(unit_begin > input.begin() && std::isalpha(*(unit_begin - 1), std::locale())) { - --unit_begin; - } - - std::string unit{unit_begin, input.end()}; - input.resize(static_cast(std::distance(input.begin(), unit_begin))); - detail::trim(input); - - if(opts & UNIT_REQUIRED && unit.empty()) { - throw ValidationError("Missing mandatory unit"); - } - if(opts & CASE_INSENSITIVE) { - unit = detail::to_lower(unit); - } - if(unit.empty()) { - using CLI::detail::lexical_cast; - if(!lexical_cast(input, num)) { - throw ValidationError(std::string("Value ") + input + " could not be converted to " + - detail::type_name()); - } - // No need to modify input if no unit passed - return {}; - } - - // find corresponding factor - auto it = mapping.find(unit); - if(it == mapping.end()) { - throw ValidationError(unit + - " unit not recognized. " - "Allowed values: " + - detail::generate_map(mapping, true)); - } - - if(!input.empty()) { - using CLI::detail::lexical_cast; - bool converted = lexical_cast(input, num); - if(!converted) { - throw ValidationError(std::string("Value ") + input + " could not be converted to " + - detail::type_name()); - } - // perform safe multiplication - bool ok = detail::checked_multiply(num, it->second); - if(!ok) { - throw ValidationError(detail::to_string(num) + " multiplied by " + unit + - " factor would cause number overflow. Use smaller value."); - } - } else { - num = static_cast(it->second); - } - - input = detail::to_string(num); - - return {}; - }; +private: + /// Check that mapping contains valid units. + /// Update mapping for CASE_INSENSITIVE mode. + template + static void validate_mapping(std::map &mapping, + Options opts) { + for (auto &kv : mapping) { + if (kv.first.empty()) { + throw ValidationError("Unit must not be empty."); + } + if (!detail::isalpha(kv.first)) { + throw ValidationError("Unit must contain only letters."); + } } - private: - /// Check that mapping contains valid units. - /// Update mapping for CASE_INSENSITIVE mode. - template static void validate_mapping(std::map &mapping, Options opts) { - for(auto &kv : mapping) { - if(kv.first.empty()) { - throw ValidationError("Unit must not be empty."); - } - if(!detail::isalpha(kv.first)) { - throw ValidationError("Unit must contain only letters."); - } - } - - // make all units lowercase if CASE_INSENSITIVE - if(opts & CASE_INSENSITIVE) { - std::map lower_mapping; - for(auto &kv : mapping) { - auto s = detail::to_lower(kv.first); - if(lower_mapping.count(s)) { - throw ValidationError(std::string("Several matching lowercase unit representations are found: ") + - s); - } - lower_mapping[detail::to_lower(kv.first)] = kv.second; - } - mapping = std::move(lower_mapping); + // make all units lowercase if CASE_INSENSITIVE + if (opts & CASE_INSENSITIVE) { + std::map lower_mapping; + for (auto &kv : mapping) { + auto s = detail::to_lower(kv.first); + if (lower_mapping.count(s)) { + throw ValidationError(std::string("Several matching lowercase unit " + "representations are found: ") + + s); } + lower_mapping[detail::to_lower(kv.first)] = kv.second; + } + mapping = std::move(lower_mapping); } + } - /// Generate description like this: NUMBER [UNIT] - template static std::string generate_description(const std::string &name, Options opts) { - std::stringstream out; - out << detail::type_name() << ' '; - if(opts & UNIT_REQUIRED) { - out << name; - } else { - out << '[' << name << ']'; - } - return out.str(); + /// Generate description like this: NUMBER [UNIT] + template + static std::string generate_description(const std::string &name, + Options opts) { + std::stringstream out; + out << detail::type_name() << ' '; + if (opts & UNIT_REQUIRED) { + out << name; + } else { + out << '[' << name << ']'; } + return out.str(); + } }; -inline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a, const AsNumberWithUnit::Options &b) { - return static_cast(static_cast(a) | static_cast(b)); +inline AsNumberWithUnit::Options operator|(const AsNumberWithUnit::Options &a, + const AsNumberWithUnit::Options &b) { + return static_cast(static_cast(a) | + static_cast(b)); } /// Converts a human-readable size string (with unit literal) to uin64_t size. /// Example: /// "100" => 100 /// "1 b" => 100 -/// "10Kb" => 10240 // you can configure this to be interpreted as kilobyte (*1000) or kibibyte (*1024) -/// "10 KB" => 10240 -/// "10 kb" => 10240 -/// "10 kib" => 10240 // *i, *ib are always interpreted as *bibyte (*1024) -/// "10kb" => 10240 +/// "10Kb" => 10240 // you can configure this to be interpreted as kilobyte +/// (*1000) or kibibyte (*1024) "10 KB" => 10240 "10 kb" => 10240 "10 kib" => +/// 10240 // *i, *ib are always interpreted as *bibyte (*1024) "10kb" => 10240 /// "2 MB" => 2097152 /// "2 EiB" => 2^61 // Units up to exibyte are supported class AsSizeValue : public AsNumberWithUnit { - public: - using result_t = std::uint64_t; +public: + using result_t = std::uint64_t; - /// If kb_is_1000 is true, - /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024 - /// (same applies to higher order units as well). - /// Otherwise, interpret all literals as factors of 1024. - /// The first option is formally correct, but - /// the second interpretation is more wide-spread - /// (see https://en.wikipedia.org/wiki/Binary_prefix). - explicit AsSizeValue(bool kb_is_1000); + /// If kb_is_1000 is true, + /// interpret 'kb', 'k' as 1000 and 'kib', 'ki' as 1024 + /// (same applies to higher order units as well). + /// Otherwise, interpret all literals as factors of 1024. + /// The first option is formally correct, but + /// the second interpretation is more wide-spread + /// (see https://en.wikipedia.org/wiki/Binary_prefix). + explicit AsSizeValue(bool kb_is_1000); - private: - /// Get mapping - static std::map init_mapping(bool kb_is_1000); +private: + /// Get mapping + static std::map init_mapping(bool kb_is_1000); - /// Cache calculated mapping - static std::map get_mapping(bool kb_is_1000); + /// Cache calculated mapping + static std::map get_mapping(bool kb_is_1000); }; namespace detail { /// Split a string into a program name and command line arguments /// the string is assumed to contain a file name followed by other arguments -/// the return value contains is a pair with the first argument containing the program name and the second -/// everything else. -CLI11_INLINE std::pair split_program_name(std::string commandline); +/// the return value contains is a pair with the first argument containing the +/// program name and the second everything else. +CLI11_INLINE std::pair +split_program_name(std::string commandline); -} // namespace detail +} // namespace detail /// @} - - - CLI11_INLINE std::string Validator::operator()(std::string &str) const { - std::string retstring; - if(active_) { - if(non_modifying_) { - std::string value = str; - retstring = func_(value); - } else { - retstring = func_(str); - } + std::string retstring; + if (active_) { + if (non_modifying_) { + std::string value = str; + retstring = func_(value); + } else { + retstring = func_(str); } - return retstring; + } + return retstring; } -CLI11_NODISCARD CLI11_INLINE Validator Validator::description(std::string validator_desc) const { - Validator newval(*this); - newval.desc_function_ = [validator_desc]() { return validator_desc; }; - return newval; +CLI11_NODISCARD CLI11_INLINE Validator +Validator::description(std::string validator_desc) const { + Validator newval(*this); + newval.desc_function_ = [validator_desc]() { return validator_desc; }; + return newval; } CLI11_INLINE Validator Validator::operator&(const Validator &other) const { - Validator newval; + Validator newval; - newval._merge_description(*this, other, " AND "); + newval._merge_description(*this, other, " AND "); - // Give references (will make a copy in lambda function) - const std::function &f1 = func_; - const std::function &f2 = other.func_; + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; - newval.func_ = [f1, f2](std::string &input) { - std::string s1 = f1(input); - std::string s2 = f2(input); - if(!s1.empty() && !s2.empty()) - return std::string("(") + s1 + ") AND (" + s2 + ")"; - return s1 + s2; - }; + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if (!s1.empty() && !s2.empty()) + return std::string("(") + s1 + ") AND (" + s2 + ")"; + return s1 + s2; + }; - newval.active_ = active_ && other.active_; - newval.application_index_ = application_index_; - return newval; + newval.active_ = active_ && other.active_; + newval.application_index_ = application_index_; + return newval; } CLI11_INLINE Validator Validator::operator|(const Validator &other) const { - Validator newval; + Validator newval; - newval._merge_description(*this, other, " OR "); + newval._merge_description(*this, other, " OR "); - // Give references (will make a copy in lambda function) - const std::function &f1 = func_; - const std::function &f2 = other.func_; + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; + const std::function &f2 = other.func_; - newval.func_ = [f1, f2](std::string &input) { - std::string s1 = f1(input); - std::string s2 = f2(input); - if(s1.empty() || s2.empty()) - return std::string(); + newval.func_ = [f1, f2](std::string &input) { + std::string s1 = f1(input); + std::string s2 = f2(input); + if (s1.empty() || s2.empty()) return std::string(); - return std::string("(") + s1 + ") OR (" + s2 + ")"; - }; - newval.active_ = active_ && other.active_; - newval.application_index_ = application_index_; - return newval; + return std::string("(") + s1 + ") OR (" + s2 + ")"; + }; + newval.active_ = active_ && other.active_; + newval.application_index_ = application_index_; + return newval; } CLI11_INLINE Validator Validator::operator!() const { - Validator newval; - const std::function &dfunc1 = desc_function_; - newval.desc_function_ = [dfunc1]() { - auto str = dfunc1(); - return (!str.empty()) ? std::string("NOT ") + str : std::string{}; - }; - // Give references (will make a copy in lambda function) - const std::function &f1 = func_; + Validator newval; + const std::function &dfunc1 = desc_function_; + newval.desc_function_ = [dfunc1]() { + auto str = dfunc1(); + return (!str.empty()) ? std::string("NOT ") + str : std::string{}; + }; + // Give references (will make a copy in lambda function) + const std::function &f1 = func_; - newval.func_ = [f1, dfunc1](std::string &test) -> std::string { - std::string s1 = f1(test); - if(s1.empty()) { - return std::string("check ") + dfunc1() + " succeeded improperly"; - } - return std::string{}; - }; - newval.active_ = active_; - newval.application_index_ = application_index_; - return newval; + newval.func_ = [f1, dfunc1](std::string &test) -> std::string { + std::string s1 = f1(test); + if (s1.empty()) { + return std::string("check ") + dfunc1() + " succeeded improperly"; + } + return std::string{}; + }; + newval.active_ = active_; + newval.application_index_ = application_index_; + return newval; } -CLI11_INLINE void -Validator::_merge_description(const Validator &val1, const Validator &val2, const std::string &merger) { +CLI11_INLINE void Validator::_merge_description(const Validator &val1, + const Validator &val2, + const std::string &merger) { - const std::function &dfunc1 = val1.desc_function_; - const std::function &dfunc2 = val2.desc_function_; + const std::function &dfunc1 = val1.desc_function_; + const std::function &dfunc2 = val2.desc_function_; - desc_function_ = [=]() { - std::string f1 = dfunc1(); - std::string f2 = dfunc2(); - if((f1.empty()) || (f2.empty())) { - return f1 + f2; - } - return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')'; - }; + desc_function_ = [=]() { + std::string f1 = dfunc1(); + std::string f2 = dfunc2(); + if ((f1.empty()) || (f2.empty())) { + return f1 + f2; + } + return std::string(1, '(') + f1 + ')' + merger + '(' + f2 + ')'; + }; } namespace detail { #if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0 CLI11_INLINE path_type check_path(const char *file) noexcept { - std::error_code ec; - auto stat = std::filesystem::status(to_path(file), ec); - if(ec) { - return path_type::nonexistent; - } - switch(stat.type()) { - case std::filesystem::file_type::none: // LCOV_EXCL_LINE - case std::filesystem::file_type::not_found: - return path_type::nonexistent; // LCOV_EXCL_LINE - case std::filesystem::file_type::directory: - return path_type::directory; - case std::filesystem::file_type::symlink: - case std::filesystem::file_type::block: - case std::filesystem::file_type::character: - case std::filesystem::file_type::fifo: - case std::filesystem::file_type::socket: - case std::filesystem::file_type::regular: - case std::filesystem::file_type::unknown: - default: - return path_type::file; - } + std::error_code ec; + auto stat = std::filesystem::status(to_path(file), ec); + if (ec) { + return path_type::nonexistent; + } + switch (stat.type()) { + case std::filesystem::file_type::none: // LCOV_EXCL_LINE + case std::filesystem::file_type::not_found: + return path_type::nonexistent; // LCOV_EXCL_LINE + case std::filesystem::file_type::directory: + return path_type::directory; + case std::filesystem::file_type::symlink: + case std::filesystem::file_type::block: + case std::filesystem::file_type::character: + case std::filesystem::file_type::fifo: + case std::filesystem::file_type::socket: + case std::filesystem::file_type::regular: + case std::filesystem::file_type::unknown: + default: + return path_type::file; + } } #else CLI11_INLINE path_type check_path(const char *file) noexcept { #if defined(_MSC_VER) - struct __stat64 buffer; - if(_stat64(file, &buffer) == 0) { - return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; - } + struct __stat64 buffer; + if (_stat64(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory + : path_type::file; + } #else - struct stat buffer; - if(stat(file, &buffer) == 0) { - return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory : path_type::file; + struct stat buffer; + if (stat(file, &buffer) == 0) { + return ((buffer.st_mode & S_IFDIR) != 0) ? path_type::directory + : path_type::file; + } +#endif + return path_type::nonexistent; +} +#endif + +CLI11_INLINE ExistingFileValidator::ExistingFileValidator() + : Validator("FILE") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if (path_result == path_type::nonexistent) { + return "File does not exist: " + filename; } -#endif - return path_type::nonexistent; -} -#endif - -CLI11_INLINE ExistingFileValidator::ExistingFileValidator() : Validator("FILE") { - func_ = [](std::string &filename) { - auto path_result = check_path(filename.c_str()); - if(path_result == path_type::nonexistent) { - return "File does not exist: " + filename; - } - if(path_result == path_type::directory) { - return "File is actually a directory: " + filename; - } - return std::string(); - }; + if (path_result == path_type::directory) { + return "File is actually a directory: " + filename; + } + return std::string(); + }; } -CLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator() : Validator("DIR") { - func_ = [](std::string &filename) { - auto path_result = check_path(filename.c_str()); - if(path_result == path_type::nonexistent) { - return "Directory does not exist: " + filename; - } - if(path_result == path_type::file) { - return "Directory is actually a file: " + filename; - } - return std::string(); - }; +CLI11_INLINE ExistingDirectoryValidator::ExistingDirectoryValidator() + : Validator("DIR") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if (path_result == path_type::nonexistent) { + return "Directory does not exist: " + filename; + } + if (path_result == path_type::file) { + return "Directory is actually a file: " + filename; + } + return std::string(); + }; } -CLI11_INLINE ExistingPathValidator::ExistingPathValidator() : Validator("PATH(existing)") { - func_ = [](std::string &filename) { - auto path_result = check_path(filename.c_str()); - if(path_result == path_type::nonexistent) { - return "Path does not exist: " + filename; - } - return std::string(); - }; +CLI11_INLINE ExistingPathValidator::ExistingPathValidator() + : Validator("PATH(existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if (path_result == path_type::nonexistent) { + return "Path does not exist: " + filename; + } + return std::string(); + }; } -CLI11_INLINE NonexistentPathValidator::NonexistentPathValidator() : Validator("PATH(non-existing)") { - func_ = [](std::string &filename) { - auto path_result = check_path(filename.c_str()); - if(path_result != path_type::nonexistent) { - return "Path already exists: " + filename; - } - return std::string(); - }; +CLI11_INLINE NonexistentPathValidator::NonexistentPathValidator() + : Validator("PATH(non-existing)") { + func_ = [](std::string &filename) { + auto path_result = check_path(filename.c_str()); + if (path_result != path_type::nonexistent) { + return "Path already exists: " + filename; + } + return std::string(); + }; } CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") { - func_ = [](std::string &ip_addr) { - auto result = CLI::detail::split(ip_addr, '.'); - if(result.size() != 4) { - return std::string("Invalid IPV4 address must have four parts (") + ip_addr + ')'; - } - int num = 0; - for(const auto &var : result) { - using CLI::detail::lexical_cast; - bool retval = lexical_cast(var, num); - if(!retval) { - return std::string("Failed parsing number (") + var + ')'; - } - if(num < 0 || num > 255) { - return std::string("Each IP number must be between 0 and 255 ") + var; - } - } - return std::string{}; - }; + func_ = [](std::string &ip_addr) { + auto result = CLI::detail::split(ip_addr, '.'); + if (result.size() != 4) { + return std::string("Invalid IPV4 address must have four parts (") + + ip_addr + ')'; + } + int num = 0; + for (const auto &var : result) { + using CLI::detail::lexical_cast; + bool retval = lexical_cast(var, num); + if (!retval) { + return std::string("Failed parsing number (") + var + ')'; + } + if (num < 0 || num > 255) { + return std::string("Each IP number must be between 0 and 255 ") + var; + } + } + return std::string{}; + }; } CLI11_INLINE EscapedStringTransformer::EscapedStringTransformer() { - func_ = [](std::string &str) { - try { - if(str.size() > 1 && (str.front() == '\"' || str.front() == '\'' || str.front() == '`') && - str.front() == str.back()) { - process_quoted_string(str); - } else if(str.find_first_of('\\') != std::string::npos) { - if(detail::is_binary_escaped_string(str)) { - str = detail::extract_binary_string(str); - } else { - str = remove_escaped_characters(str); - } - } - return std::string{}; - } catch(const std::invalid_argument &ia) { - return std::string(ia.what()); + func_ = [](std::string &str) { + try { + if (str.size() > 1 && + (str.front() == '\"' || str.front() == '\'' || str.front() == '`') && + str.front() == str.back()) { + process_quoted_string(str); + } else if (str.find_first_of('\\') != std::string::npos) { + if (detail::is_binary_escaped_string(str)) { + str = detail::extract_binary_string(str); + } else { + str = remove_escaped_characters(str); } - }; + } + return std::string{}; + } catch (const std::invalid_argument &ia) { + return std::string(ia.what()); + } + }; } -} // namespace detail +} // namespace detail -CLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path, bool enableErrorReturn) +CLI11_INLINE FileOnDefaultPath::FileOnDefaultPath(std::string default_path, + bool enableErrorReturn) : Validator("FILE") { - func_ = [default_path, enableErrorReturn](std::string &filename) { - auto path_result = detail::check_path(filename.c_str()); - if(path_result == detail::path_type::nonexistent) { - std::string test_file_path = default_path; - if(default_path.back() != '/' && default_path.back() != '\\') { - // Add folder separator - test_file_path += '/'; - } - test_file_path.append(filename); - path_result = detail::check_path(test_file_path.c_str()); - if(path_result == detail::path_type::file) { - filename = test_file_path; - } else { - if(enableErrorReturn) { - return "File does not exist: " + filename; - } - } + func_ = [default_path, enableErrorReturn](std::string &filename) { + auto path_result = detail::check_path(filename.c_str()); + if (path_result == detail::path_type::nonexistent) { + std::string test_file_path = default_path; + if (default_path.back() != '/' && default_path.back() != '\\') { + // Add folder separator + test_file_path += '/'; + } + test_file_path.append(filename); + path_result = detail::check_path(test_file_path.c_str()); + if (path_result == detail::path_type::file) { + filename = test_file_path; + } else { + if (enableErrorReturn) { + return "File does not exist: " + filename; } - return std::string{}; - }; + } + } + return std::string{}; + }; } -CLI11_INLINE AsSizeValue::AsSizeValue(bool kb_is_1000) : AsNumberWithUnit(get_mapping(kb_is_1000)) { - if(kb_is_1000) { - description("SIZE [b, kb(=1000b), kib(=1024b), ...]"); - } else { - description("SIZE [b, kb(=1024b), ...]"); - } +CLI11_INLINE AsSizeValue::AsSizeValue(bool kb_is_1000) + : AsNumberWithUnit(get_mapping(kb_is_1000)) { + if (kb_is_1000) { + description("SIZE [b, kb(=1000b), kib(=1024b), ...]"); + } else { + description("SIZE [b, kb(=1024b), ...]"); + } } -CLI11_INLINE std::map AsSizeValue::init_mapping(bool kb_is_1000) { - std::map m; - result_t k_factor = kb_is_1000 ? 1000 : 1024; - result_t ki_factor = 1024; - result_t k = 1; - result_t ki = 1; - m["b"] = 1; - for(std::string p : {"k", "m", "g", "t", "p", "e"}) { - k *= k_factor; - ki *= ki_factor; - m[p] = k; - m[p + "b"] = k; - m[p + "i"] = ki; - m[p + "ib"] = ki; - } - return m; -} - -CLI11_INLINE std::map AsSizeValue::get_mapping(bool kb_is_1000) { - if(kb_is_1000) { - static auto m = init_mapping(true); - return m; - } - static auto m = init_mapping(false); +CLI11_INLINE std::map +AsSizeValue::init_mapping(bool kb_is_1000) { + std::map m; + result_t k_factor = kb_is_1000 ? 1000 : 1024; + result_t ki_factor = 1024; + result_t k = 1; + result_t ki = 1; + m["b"] = 1; + for (std::string p : {"k", "m", "g", "t", "p", "e"}) { + k *= k_factor; + ki *= ki_factor; + m[p] = k; + m[p + "b"] = k; + m[p + "i"] = ki; + m[p + "ib"] = ki; + } + return m; +} + +CLI11_INLINE std::map +AsSizeValue::get_mapping(bool kb_is_1000) { + if (kb_is_1000) { + static auto m = init_mapping(true); return m; + } + static auto m = init_mapping(false); + return m; } namespace detail { -CLI11_INLINE std::pair split_program_name(std::string commandline) { - // try to determine the programName - std::pair vals; - trim(commandline); - auto esp = commandline.find_first_of(' ', 1); - while(detail::check_path(commandline.substr(0, esp).c_str()) != path_type::file) { - esp = commandline.find_first_of(' ', esp + 1); - if(esp == std::string::npos) { - // if we have reached the end and haven't found a valid file just assume the first argument is the - // program name - if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') { - bool embeddedQuote = false; - auto keyChar = commandline[0]; - auto end = commandline.find_first_of(keyChar, 1); - while((end != std::string::npos) && (commandline[end - 1] == '\\')) { // deal with escaped quotes - end = commandline.find_first_of(keyChar, end + 1); - embeddedQuote = true; - } - if(end != std::string::npos) { - vals.first = commandline.substr(1, end - 1); - esp = end + 1; - if(embeddedQuote) { - vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar)); - } - } else { - esp = commandline.find_first_of(' ', 1); - } - } else { - esp = commandline.find_first_of(' ', 1); - } - - break; +CLI11_INLINE std::pair +split_program_name(std::string commandline) { + // try to determine the programName + std::pair vals; + trim(commandline); + auto esp = commandline.find_first_of(' ', 1); + while (detail::check_path(commandline.substr(0, esp).c_str()) != + path_type::file) { + esp = commandline.find_first_of(' ', esp + 1); + if (esp == std::string::npos) { + // if we have reached the end and haven't found a valid file just assume + // the first argument is the program name + if (commandline[0] == '"' || commandline[0] == '\'' || + commandline[0] == '`') { + bool embeddedQuote = false; + auto keyChar = commandline[0]; + auto end = commandline.find_first_of(keyChar, 1); + while ((end != std::string::npos) && + (commandline[end - 1] == '\\')) { // deal with escaped quotes + end = commandline.find_first_of(keyChar, end + 1); + embeddedQuote = true; } - } - if(vals.first.empty()) { - vals.first = commandline.substr(0, esp); - rtrim(vals.first); - } + if (end != std::string::npos) { + vals.first = commandline.substr(1, end - 1); + esp = end + 1; + if (embeddedQuote) { + vals.first = + find_and_replace(vals.first, std::string("\\") + keyChar, + std::string(1, keyChar)); + } + } else { + esp = commandline.find_first_of(' ', 1); + } + } else { + esp = commandline.find_first_of(' ', 1); + } - // strip the program name - vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1) : std::string{}; - ltrim(vals.second); - return vals; + break; + } + } + if (vals.first.empty()) { + vals.first = commandline.substr(0, esp); + rtrim(vals.first); + } + + // strip the program name + vals.second = (esp < commandline.length() - 1) ? commandline.substr(esp + 1) + : std::string{}; + ltrim(vals.second); + return vals; } -} // namespace detail +} // namespace detail /// @} - - - class Option; class App; @@ -4999,9 +5586,9 @@ class App; /// the second argument. enum class AppFormatMode { - Normal, ///< The normal, detailed help - All, ///< A fully expanded help - Sub, ///< Used when printed as part of expanded subcommand + Normal, ///< The normal, detailed help + All, ///< A fully expanded help + Sub, ///< Used when printed as part of expanded subcommand }; /// This is the minimum requirements to run a formatter. @@ -5009,178 +5596,195 @@ enum class AppFormatMode { /// A user can subclass this is if they do not care at all /// about the structure in CLI::Formatter. class FormatterBase { - protected: - /// @name Options - ///@{ +protected: + /// @name Options + ///@{ - /// The width of the left column (options/flags/subcommands) - std::size_t column_width_{30}; + /// The width of the left column (options/flags/subcommands) + std::size_t column_width_{30}; - /// The width of the right column (description of options/flags/subcommands) - std::size_t right_column_width_{65}; + /// The width of the right column (description of options/flags/subcommands) + std::size_t right_column_width_{65}; - /// The width of the description paragraph at the top of help - std::size_t description_paragraph_width_{80}; + /// The width of the description paragraph at the top of help + std::size_t description_paragraph_width_{80}; - /// The width of the footer paragraph - std::size_t footer_paragraph_width_{80}; + /// The width of the footer paragraph + std::size_t footer_paragraph_width_{80}; - /// @brief The required help printout labels (user changeable) - /// Values are Needs, Excludes, etc. - std::map labels_{}; + /// @brief The required help printout labels (user changeable) + /// Values are Needs, Excludes, etc. + std::map labels_{}; - ///@} - /// @name Basic - ///@{ + ///@} + /// @name Basic + ///@{ - public: - FormatterBase() = default; - FormatterBase(const FormatterBase &) = default; - FormatterBase(FormatterBase &&) = default; - FormatterBase &operator=(const FormatterBase &) = default; - FormatterBase &operator=(FormatterBase &&) = default; +public: + FormatterBase() = default; + FormatterBase(const FormatterBase &) = default; + FormatterBase(FormatterBase &&) = default; + FormatterBase &operator=(const FormatterBase &) = default; + FormatterBase &operator=(FormatterBase &&) = default; - /// Adding a destructor in this form to work around bug in GCC 4.7 - virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default) + /// Adding a destructor in this form to work around bug in GCC 4.7 + virtual ~FormatterBase() noexcept {} // NOLINT(modernize-use-equals-default) - /// This is the key method that puts together help - virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0; + /// This is the key method that puts together help + virtual std::string make_help(const App *, std::string, + AppFormatMode) const = 0; - ///@} - /// @name Setters - ///@{ + ///@} + /// @name Setters + ///@{ - /// Set the "REQUIRED" label - void label(std::string key, std::string val) { labels_[key] = val; } + /// Set the "REQUIRED" label + void label(std::string key, std::string val) { labels_[key] = val; } - /// Set the left column width (options/flags/subcommands) - void column_width(std::size_t val) { column_width_ = val; } + /// Set the left column width (options/flags/subcommands) + void column_width(std::size_t val) { column_width_ = val; } - /// Set the right column width (description of options/flags/subcommands) - void right_column_width(std::size_t val) { right_column_width_ = val; } + /// Set the right column width (description of options/flags/subcommands) + void right_column_width(std::size_t val) { right_column_width_ = val; } - /// Set the description paragraph width at the top of help - void description_paragraph_width(std::size_t val) { description_paragraph_width_ = val; } + /// Set the description paragraph width at the top of help + void description_paragraph_width(std::size_t val) { + description_paragraph_width_ = val; + } - /// Set the footer paragraph width - void footer_paragraph_width(std::size_t val) { footer_paragraph_width_ = val; } + /// Set the footer paragraph width + void footer_paragraph_width(std::size_t val) { + footer_paragraph_width_ = val; + } - ///@} - /// @name Getters - ///@{ + ///@} + /// @name Getters + ///@{ - /// Get the current value of a name (REQUIRED, etc.) - CLI11_NODISCARD std::string get_label(std::string key) const { - if(labels_.find(key) == labels_.end()) - return key; - return labels_.at(key); - } + /// Get the current value of a name (REQUIRED, etc.) + CLI11_NODISCARD std::string get_label(std::string key) const { + if (labels_.find(key) == labels_.end()) return key; + return labels_.at(key); + } - /// Get the current left column width (options/flags/subcommands) - CLI11_NODISCARD std::size_t get_column_width() const { return column_width_; } + /// Get the current left column width (options/flags/subcommands) + CLI11_NODISCARD std::size_t get_column_width() const { return column_width_; } - /// Get the current right column width (description of options/flags/subcommands) - CLI11_NODISCARD std::size_t get_right_column_width() const { return right_column_width_; } + /// Get the current right column width (description of + /// options/flags/subcommands) + CLI11_NODISCARD std::size_t get_right_column_width() const { + return right_column_width_; + } - /// Get the current description paragraph width at the top of help - CLI11_NODISCARD std::size_t get_description_paragraph_width() const { return description_paragraph_width_; } + /// Get the current description paragraph width at the top of help + CLI11_NODISCARD std::size_t get_description_paragraph_width() const { + return description_paragraph_width_; + } - /// Get the current footer paragraph width - CLI11_NODISCARD std::size_t get_footer_paragraph_width() const { return footer_paragraph_width_; } + /// Get the current footer paragraph width + CLI11_NODISCARD std::size_t get_footer_paragraph_width() const { + return footer_paragraph_width_; + } - ///@} + ///@} }; /// This is a specialty override for lambda functions class FormatterLambda final : public FormatterBase { - using funct_t = std::function; + using funct_t = + std::function; - /// The lambda to hold and run - funct_t lambda_; + /// The lambda to hold and run + funct_t lambda_; - public: - /// Create a FormatterLambda with a lambda function - explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} +public: + /// Create a FormatterLambda with a lambda function + explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {} - /// Adding a destructor (mostly to make GCC 4.7 happy) - ~FormatterLambda() noexcept override {} // NOLINT(modernize-use-equals-default) + /// Adding a destructor (mostly to make GCC 4.7 happy) + ~FormatterLambda() noexcept override { + } // NOLINT(modernize-use-equals-default) - /// This will simply call the lambda function - std::string make_help(const App *app, std::string name, AppFormatMode mode) const override { - return lambda_(app, name, mode); - } + /// This will simply call the lambda function + std::string make_help(const App *app, std::string name, + AppFormatMode mode) const override { + return lambda_(app, name, mode); + } }; -/// This is the default Formatter for CLI11. It pretty prints help output, and is broken into quite a few -/// overridable methods, to be highly customizable with minimal effort. +/// This is the default Formatter for CLI11. It pretty prints help output, and +/// is broken into quite a few overridable methods, to be highly customizable +/// with minimal effort. class Formatter : public FormatterBase { - public: - Formatter() = default; - Formatter(const Formatter &) = default; - Formatter(Formatter &&) = default; - Formatter &operator=(const Formatter &) = default; - Formatter &operator=(Formatter &&) = default; +public: + Formatter() = default; + Formatter(const Formatter &) = default; + Formatter(Formatter &&) = default; + Formatter &operator=(const Formatter &) = default; + Formatter &operator=(Formatter &&) = default; - /// @name Overridables - ///@{ + /// @name Overridables + ///@{ - /// This prints out a group of options with title - /// - CLI11_NODISCARD virtual std::string - make_group(std::string group, bool is_positional, std::vector opts) const; + /// This prints out a group of options with title + /// + CLI11_NODISCARD virtual std::string + make_group(std::string group, bool is_positional, + std::vector opts) const; - /// This prints out just the positionals "group" - virtual std::string make_positionals(const App *app) const; + /// This prints out just the positionals "group" + virtual std::string make_positionals(const App *app) const; - /// This prints out all the groups of options - std::string make_groups(const App *app, AppFormatMode mode) const; + /// This prints out all the groups of options + std::string make_groups(const App *app, AppFormatMode mode) const; - /// This prints out all the subcommands - virtual std::string make_subcommands(const App *app, AppFormatMode mode) const; + /// This prints out all the subcommands + virtual std::string make_subcommands(const App *app, + AppFormatMode mode) const; - /// This prints out a subcommand - virtual std::string make_subcommand(const App *sub) const; + /// This prints out a subcommand + virtual std::string make_subcommand(const App *sub) const; - /// This prints out a subcommand in help-all - virtual std::string make_expanded(const App *sub, AppFormatMode mode) const; + /// This prints out a subcommand in help-all + virtual std::string make_expanded(const App *sub, AppFormatMode mode) const; - /// This prints out all the groups of options - virtual std::string make_footer(const App *app) const; + /// This prints out all the groups of options + virtual std::string make_footer(const App *app) const; - /// This displays the description line - virtual std::string make_description(const App *app) const; + /// This displays the description line + virtual std::string make_description(const App *app) const; - /// This displays the usage line - virtual std::string make_usage(const App *app, std::string name) const; + /// This displays the usage line + virtual std::string make_usage(const App *app, std::string name) const; - /// This puts everything together - std::string make_help(const App *app, std::string, AppFormatMode mode) const override; + /// This puts everything together + std::string make_help(const App *app, std::string, + AppFormatMode mode) const override; - ///@} - /// @name Options - ///@{ + ///@} + /// @name Options + ///@{ - /// This prints out an option help line, either positional or optional form - virtual std::string make_option(const Option *, bool) const; + /// This prints out an option help line, either positional or optional form + virtual std::string make_option(const Option *, bool) const; - /// @brief This is the name part of an option, Default: left column - virtual std::string make_option_name(const Option *, bool) const; + /// @brief This is the name part of an option, Default: left column + virtual std::string make_option_name(const Option *, bool) const; - /// @brief This is the options part of the name, Default: combined into left column - virtual std::string make_option_opts(const Option *) const; + /// @brief This is the options part of the name, Default: combined into left + /// column + virtual std::string make_option_opts(const Option *) const; - /// @brief This is the description. Default: Right column, on new line if left column too large - virtual std::string make_option_desc(const Option *) const; + /// @brief This is the description. Default: Right column, on new line if left + /// column too large + virtual std::string make_option_desc(const Option *) const; - /// @brief This is used to print the name on the USAGE line - virtual std::string make_option_usage(const Option *opt) const; + /// @brief This is used to print the name on the USAGE line + virtual std::string make_option_usage(const Option *opt) const; - ///@} + ///@} }; - - - using results_t = std::vector; /// callback function definition using callback_t = std::function; @@ -5191,1517 +5795,1643 @@ class App; using Option_p = std::unique_ptr