/** CLI11 is a command line parser for C++11. * * This file was generated using MakeSingleHeader.py in CLI11/scripts from: v1.4.0 * This has the complete CLI library in one file. * See https://github.com/CLIUtils/CLI11 for details * * SPDX-FileCopyrightText: 2017 University of Cincinnati, developed by Henry Schreiner under NSF AWARD 141473 * SPDX-License-Identifier: BSD-3-Clause *********************************************************************************/ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // From CLI/Version.hpp namespace CLI { // Note that all code in CLI11 must be in a namespace, even if it just a define. #define CLI11_VERSION_MAJOR 1 #define CLI11_VERSION_MINOR 4 #define CLI11_VERSION_PATCH 0 #define CLI11_VERSION "1.4.0" } // namespace CLI // From CLI/StringTools.hpp namespace CLI { namespace detail { // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c /// Split a string by a delim 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); } } return elems; } /// Simple function to join a string template std::string join(const T &v, std::string delim = ",") { std::ostringstream s; size_t start = 0; for (const auto &i : v) { if (start++ > 0) s << delim; s << i; } return s.str(); } /// Join a string in reverse order template std::string rjoin(const T &v, std::string delim = ",") { std::ostringstream s; for (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 /// Trim whitespace from left of string 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; } /// Trim anything from left of string 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; } /// Trim whitespace from right of string 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; } /// Trim anything from right of string 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; } /// Trim whitespace from string 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); } /// 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); } /// 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 a two part "help" string inline void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) { name = " " + name; out << std::setw(static_cast(wid)) << std::left << name; if (!description.empty()) { if (name.length() >= wid) out << std::endl << std::setw(static_cast(wid)) << ""; out << description; } out << std::endl; } /// Verify the first character of an option template bool valid_first_char(T c) { return std::isalpha(c, std::locale()) || c == '_'; } /// Verify following characters of an option template bool valid_later_char(T c) { return std::isalnum(c, std::locale()) || c == '_' || c == '.' || c == '-'; } /// Verify an option name inline bool valid_name_string(const std::string &str) { if (str.empty() || !valid_first_char(str[0])) return false; for (auto c : str.substr(1)) if (!valid_later_char(c)) return false; return true; } /// 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; } /// Split a string '"one two" "three"' into 'one two', 'three' inline std::vector split_up(std::string str) { std::vector delims = {'\'', '\"'}; auto find_ws = [](char ch) { return std::isspace(ch, std::locale()); }; trim(str); std::vector output; while (!str.empty()) { if (str[0] == '\'') { auto end = str.find('\'', 1); if (end != std::string::npos) { output.push_back(str.substr(1, end - 1)); str = str.substr(end + 1); } else { output.push_back(str.substr(1)); str = ""; } } else if (str[0] == '\"') { auto end = str.find('\"', 1); if (end != std::string::npos) { output.push_back(str.substr(1, end - 1)); str = str.substr(end + 1); } else { output.push_back(str.substr(1)); str = ""; } } 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, str.end()); } else { output.push_back(str); str = ""; } } trim(str); } return output; } /// 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. inline std::string fix_newlines(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; } } // namespace detail } // namespace CLI // From CLI/Error.hpp namespace CLI { // Use one of these on all error classes #define CLI11_ERROR_DEF(parent, name) \ protected: \ name(std::string name, std::string msg, int exit_code) \ : parent(std::move(name), std::move(msg), exit_code) {} \ name(std::string name, std::string msg, ExitCodes exit_code) \ : parent(std::move(name), 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) \ 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(). enum class ExitCodes { Success = 0, IncorrectConstruction = 100, BadNameString, OptionAlreadyAdded, FileError, ConversionError, ValidationError, RequiredError, RequiresError, ExcludesError, ExtrasError, INIError, InvalidError, HorribleError, OptionNotFound, ArgumentMismatch, BaseClass = 127 }; // Error definitions /// @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. /// @{ /// All errors derive from this one class Error : public std::runtime_error { int exit_code; std::string name{"Error"}; public: int get_exit_code() const { return exit_code; } std::string get_name() const { return name; } Error(std::string name, std::string msg, int exit_code = static_cast(ExitCodes::BaseClass)) : runtime_error(msg), exit_code(exit_code), name(std::move(name)) {} 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) }; /// 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 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 single 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 BadLongName(std::string name) { return BadNameString("Bad long name: " + name); } static BadNameString DashesOnly(std::string name) { return BadNameString("Must have a name, not just dashes: " + 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) OptionAlreadyAdded(std::string name) : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {} static OptionAlreadyAdded Requires(std::string name, std::string other) { return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded); } static OptionAlreadyAdded Excludes(std::string name, std::string other) { return OptionAlreadyAdded(name + " excludes " + other, ExitCodes::OptionAlreadyAdded); } }; // Parsing errors /// Anything that can error in Parse class ParseError : public Error { 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) {} }; /// -h or --help on command line class CallForHelp : public ParseError { CLI11_ERROR_DEF(ParseError, CallForHelp) CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {} }; /// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code. class RuntimeError : public ParseError { CLI11_ERROR_DEF(ParseError, RuntimeError) 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?)"); } }; /// 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"); } }; /// Thrown when validation of results fails class ValidationError : public ParseError { CLI11_ERROR_DEF(ParseError, ValidationError) CLI11_ERROR_SIMPLE(ValidationError) 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) RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {} static RequiredError Subcommand(size_t min_subcom) { if (min_subcom == 1) return RequiredError("A subcommand"); else return RequiredError("Requires at least " + std::to_string(min_subcom) + " subcommands", 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, size_t recieved) : ArgumentMismatch(expected > 0 ? ("Expected exactly " + std::to_string(expected) + " arguments to " + name + ", got " + std::to_string(recieved)) : ("Expected at least " + std::to_string(-expected) + " arguments to " + name + ", got " + std::to_string(recieved)), ExitCodes::ArgumentMismatch) {} static ArgumentMismatch AtLeast(std::string name, int num) { return ArgumentMismatch(name + ": At least " + std::to_string(num) + " required"); } static ArgumentMismatch TypedAtLeast(std::string name, int num, std::string type) { return ArgumentMismatch(name + ": " + std::to_string(num) + " required " + type + " missing"); } }; /// 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) {} }; /// 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) {} }; /// Thrown when too many positionals or options are found class ExtrasError : public ParseError { CLI11_ERROR_DEF(ParseError, ExtrasError) 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) {} }; /// Thrown when extra values are found in an INI file class INIError : public ParseError { CLI11_ERROR_DEF(ParseError, INIError) CLI11_ERROR_SIMPLE(INIError) static INIError Extras(std::string item) { return INIError("INI was not able to parse " + item); } static INIError NotConfigurable(std::string item) { return INIError(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) 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. class HorribleError : public ParseError { CLI11_ERROR_DEF(ParseError, HorribleError) CLI11_ERROR_SIMPLE(HorribleError) }; // After parsing /// Thrown when counting a non-existent option class OptionNotFound : public Error { CLI11_ERROR_DEF(Error, OptionNotFound) OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {} }; /// @} } // namespace CLI // From CLI/TypeTools.hpp namespace CLI { // Type tools // 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/master/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; template struct is_vector { static const bool value = false; }; template struct is_vector> { static bool const value = true; }; template struct is_bool { static const bool value = false; }; template <> struct is_bool { static bool const value = true; }; namespace detail { // Based generally on https://rmf.io/cxx11/almost-static-if /// Simple empty scoped class enum class enabler {}; /// An instance to use in EnableIf constexpr enabler dummy = {}; // Type name print /// Was going to be based on /// http://stackoverflow.com/questions/1055452/c-get-name-of-type-in-template /// But this is cleaner and works better in this case template ::value && std::is_signed::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "INT"; } template ::value && std::is_unsigned::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "UINT"; } template ::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "FLOAT"; } /// This one should not be used, since vector types print the internal type template ::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "VECTOR"; } template ::value && !std::is_integral::value && !is_vector::value, detail::enabler> = detail::dummy> constexpr const char *type_name() { return "TEXT"; } // Lexical cast /// Signed integers / enums template ::value && std::is_signed::value) || std::is_enum::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { try { size_t n = 0; long long output_ll = std::stoll(input, &n, 0); output = static_cast(output_ll); return n == input.size() && static_cast(output) == output_ll; } catch (const std::invalid_argument &) { return false; } catch (const std::out_of_range &) { return false; } } /// Unsigned integers template ::value && std::is_unsigned::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { if (!input.empty() && input.front() == '-') return false; // std::stoull happily converts negative values to junk without any errors. try { size_t n = 0; unsigned long long output_ll = std::stoull(input, &n, 0); output = static_cast(output_ll); return n == input.size() && static_cast(output) == output_ll; } catch (const std::invalid_argument &) { return false; } catch (const std::out_of_range &) { return false; } } /// Floats template ::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { try { size_t n = 0; output = static_cast(std::stold(input, &n)); return n == input.size(); } catch (const std::invalid_argument &) { return false; } catch (const std::out_of_range &) { return false; } } /// String and similar template < typename T, enable_if_t::value && !std::is_integral::value && !std::is_enum::value && std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { output = input; return true; } /// Non-string parsable template < typename T, enable_if_t::value && !std::is_integral::value && !std::is_enum::value && !std::is_assignable::value, detail::enabler> = detail::dummy> bool lexical_cast(std::string input, T &output) { // On GCC 4.7, thread_local is not available, so this optimization // is turned off (avoiding multiple initialisations on multiple usages #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ __GNUC__ == 4 && (__GNUC_MINOR__ < 8) std::istringstream is; #else static thread_local std::istringstream is; #endif is.str(input); is >> output; return !is.fail() && !is.rdbuf()->in_avail(); } } // namespace detail } // namespace CLI // From CLI/Split.hpp namespace CLI { namespace detail { // Returns false if not a short option. Otherwise, sets opt name and rest and returns true 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; } else return false; } // Returns false if not a long option. Otherwise, sets opt name and other side of = and returns true inline bool split_long(const std::string ¤t, std::string &name, std::string &value) { if (current.size() > 2 && current.substr(0, 2) == "--" && valid_first_char(current[2])) { auto loc = current.find("="); if (loc != std::string::npos) { name = current.substr(2, loc - 2); value = current.substr(loc + 1); } else { name = current.substr(2); value = ""; } return true; } else return false; } // Splits a string into multiple long and short names inline std::vector split_names(std::string current) { std::vector output; size_t val; 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; } /// Get a vector of short names, one of long names, and a single name inline std::tuple, std::vector, std::string> get_names(const std::vector &input) { std::vector short_names; std::vector long_names; std::string pos_name; for (std::string name : input) { if (name.length() == 0) continue; else 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 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 == "--") { throw BadNameString::DashesOnly(name); } else { if (pos_name.length() > 0) throw BadNameString::MultiPositionalNames(name); pos_name = name; } } return std::tuple, std::vector, std::string>(short_names, long_names, pos_name); } } // namespace detail } // namespace CLI // From CLI/Ini.hpp namespace CLI { namespace detail { inline std::string inijoin(std::vector args) { std::ostringstream s; size_t start = 0; for (const auto &arg : args) { if (start++ > 0) s << " "; auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace(ch, std::locale()); }); if (it == arg.end()) s << arg; else if (arg.find(R"(")") == std::string::npos) s << R"(")" << arg << R"(")"; else s << R"(')" << arg << R"(')"; } return s.str(); } struct ini_ret_t { /// This is the full name with dots std::string fullname; /// Listing of inputs std::vector inputs; /// Current parent level size_t level = 0; /// Return parent or empty string, based on level /// /// Level 0, a.b.c would return a /// Level 1, a.b.c could return b std::string parent() const { std::vector plist = detail::split(fullname, '.'); if (plist.size() > (level + 1)) return plist[level]; else return ""; } /// Return name std::string name() const { std::vector plist = detail::split(fullname, '.'); return plist.at(plist.size() - 1); } }; /// Internal parsing function inline std::vector parse_ini(std::istream &input) { std::string name, line; std::string section = "default"; std::vector output; while (getline(input, line)) { std::vector items; detail::trim(line); size_t len = line.length(); if (len > 1 && line[0] == '[' && line[len - 1] == ']') { section = line.substr(1, len - 2); } else if (len > 0 && line[0] != ';') { output.emplace_back(); ini_ret_t &out = output.back(); // Find = in string, split and recombine auto pos = line.find("="); if (pos != std::string::npos) { name = detail::trim_copy(line.substr(0, pos)); std::string item = detail::trim_copy(line.substr(pos + 1)); items = detail::split_up(item); } else { name = detail::trim_copy(line); items = {"ON"}; } if (detail::to_lower(section) == "default") out.fullname = name; else out.fullname = section + "." + name; out.inputs.insert(std::end(out.inputs), std::begin(items), std::end(items)); } } return output; } /// Parse an INI file, throw an error (ParseError:INIParseError or FileError) on failure inline std::vector parse_ini(const std::string &name) { std::ifstream input{name}; if (!input.good()) throw FileError::Missing(name); return parse_ini(input); } } // namespace detail } // namespace CLI // From CLI/Validators.hpp namespace CLI { /// @defgroup validator_group Validators /// @brief Some validators that are provided /// /// These are simple `void(std::string&)` validators that are useful. They throw /// a ValidationError if they fail (or the normally expected error if the cast fails) /// @{ /// Check for an existing file inline std::string ExistingFile(const std::string &filename) { struct stat buffer; bool exist = stat(filename.c_str(), &buffer) == 0; bool is_dir = (buffer.st_mode & S_IFDIR) != 0; if (!exist) { return "File does not exist: " + filename; } else if (is_dir) { return "File is actually a directory: " + filename; } return std::string(); } /// Check for an existing directory inline std::string ExistingDirectory(const std::string &filename) { struct stat buffer; bool exist = stat(filename.c_str(), &buffer) == 0; bool is_dir = (buffer.st_mode & S_IFDIR) != 0; if (!exist) { return "Directory does not exist: " + filename; } else if (!is_dir) { return "Directory is actually a file: " + filename; } return std::string(); } /// Check for an existing path inline std::string ExistingPath(const std::string &filename) { struct stat buffer; bool const exist = stat(filename.c_str(), &buffer) == 0; if (!exist) { return "Path does not exist: " + filename; } return std::string(); } /// Check for a non-existing path inline std::string NonexistentPath(const std::string &filename) { struct stat buffer; bool exist = stat(filename.c_str(), &buffer) == 0; if (exist) { return "Path already exists: " + filename; } return std::string(); } /// Produce a range validator function template std::function Range(T min, T max) { return [min, max](std::string input) { T val; detail::lexical_cast(input, val); if (val < min || val > max) return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max); return std::string(); }; } /// Range of one value is 0 to value template std::function Range(T max) { return Range(static_cast(0), max); } /// @} } // namespace CLI // From CLI/Option.hpp namespace CLI { using results_t = std::vector; using callback_t = std::function; class Option; class App; using Option_p = std::unique_ptr