Squashed 'libs/CommService/' content from commit 7ccc0fc

git-subtree-dir: libs/CommService
git-subtree-split: 7ccc0fce88bbc5969df060058cf0fb57abe3bcf9
This commit is contained in:
Henry Winkel
2022-09-15 09:53:53 +02:00
commit cc67e4840f
799 changed files with 179487 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// CLI Library includes
// Order is important for combiner script
#include "Version.hpp"
#include "Macros.hpp"
#include "StringTools.hpp"
#include "Error.hpp"
#include "TypeTools.hpp"
#include "Split.hpp"
#include "ConfigFwd.hpp"
#include "Validators.hpp"
#include "FormatterFwd.hpp"
#include "Option.hpp"
#include "App.hpp"
#include "Config.hpp"
#include "Formatter.hpp"

View File

@@ -0,0 +1,396 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:public_includes:set]
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
// [CLI11:public_includes:set]
#include "App.hpp"
#include "ConfigFwd.hpp"
#include "StringTools.hpp"
namespace CLI {
// [CLI11:config_hpp:verbatim]
namespace detail {
inline std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'') {
if(arg.empty()) {
return std::string(2, stringQuote);
}
// some specifically supported strings
if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
return arg;
}
// floating point conversion can convert some hex codes, but don't try that here
if(arg.compare(0, 2, "0x") != 0 && arg.compare(0, 2, "0X") != 0) {
double val;
if(detail::lexical_cast(arg, val)) {
return arg;
}
}
// just quote a single non numeric character
if(arg.size() == 1) {
return std::string(1, characterQuote) + arg + characterQuote;
}
// handle hex, binary or octal arguments
if(arg.front() == '0') {
if(arg[1] == 'x') {
if(std::all_of(arg.begin() + 2, arg.end(), [](char x) {
return (x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f');
})) {
return arg;
}
} else if(arg[1] == 'o') {
if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x >= '0' && x <= '7'); })) {
return arg;
}
} else if(arg[1] == 'b') {
if(std::all_of(arg.begin() + 2, arg.end(), [](char x) { return (x == '0' || x == '1'); })) {
return arg;
}
}
}
if(arg.find_first_of(stringQuote) == std::string::npos) {
return std::string(1, stringQuote) + arg + stringQuote;
} else {
return characterQuote + arg + characterQuote;
}
}
/// Comma separated join, adds quotes if needed
inline std::string ini_join(const std::vector<std::string> &args,
char sepChar = ',',
char arrayStart = '[',
char arrayEnd = ']',
char stringQuote = '"',
char characterQuote = '\'') {
std::string joined;
if(args.size() > 1 && arrayStart != '\0') {
joined.push_back(arrayStart);
}
std::size_t start = 0;
for(const auto &arg : args) {
if(start++ > 0) {
joined.push_back(sepChar);
if(isspace(sepChar) == 0) {
joined.push_back(' ');
}
}
joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
}
if(args.size() > 1 && arrayEnd != '\0') {
joined.push_back(arrayEnd);
}
return joined;
}
inline std::vector<std::string> generate_parents(const std::string &section, std::string &name, char parentSeparator) {
std::vector<std::string> parents;
if(detail::to_lower(section) != "default") {
if(section.find(parentSeparator) != std::string::npos) {
parents = detail::split(section, parentSeparator);
} else {
parents = {section};
}
}
if(name.find(parentSeparator) != std::string::npos) {
std::vector<std::string> plist = detail::split(name, parentSeparator);
name = plist.back();
detail::remove_quotes(name);
plist.pop_back();
parents.insert(parents.end(), plist.begin(), plist.end());
}
// clean up quotes on the parents
for(auto &parent : parents) {
detail::remove_quotes(parent);
}
return parents;
}
/// assuming non default segments do a check on the close and open of the segments in a configItem structure
inline void
checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
std::string estring;
auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
if(!output.empty() && output.back().name == "--") {
std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
while(output.back().parents.size() >= msize) {
output.push_back(output.back());
output.back().parents.pop_back();
}
if(parents.size() > 1) {
std::size_t common = 0;
std::size_t mpair = (std::min)(output.back().parents.size(), parents.size() - 1);
for(std::size_t ii = 0; ii < mpair; ++ii) {
if(output.back().parents[ii] != parents[ii]) {
break;
}
++common;
}
if(common == mpair) {
output.pop_back();
} else {
while(output.back().parents.size() > common + 1) {
output.push_back(output.back());
output.back().parents.pop_back();
}
}
for(std::size_t ii = common; ii < parents.size() - 1; ++ii) {
output.emplace_back();
output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
output.back().name = "++";
}
}
} else if(parents.size() > 1) {
for(std::size_t ii = 0; ii < parents.size() - 1; ++ii) {
output.emplace_back();
output.back().parents.assign(parents.begin(), parents.begin() + static_cast<std::ptrdiff_t>(ii) + 1);
output.back().name = "++";
}
}
// insert a section end which is just an empty items_buffer
output.emplace_back();
output.back().parents = std::move(parents);
output.back().name = "++";
}
} // namespace detail
inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
std::string line;
std::string currentSection = "default";
std::string previousSection = "default";
std::vector<ConfigItem> output;
bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
bool inSection{false};
char aStart = (isINIArray) ? '[' : arrayStart;
char aEnd = (isINIArray) ? ']' : arrayEnd;
char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
int currentSectionIndex{0};
while(getline(input, line)) {
std::vector<std::string> items_buffer;
std::string name;
detail::trim(line);
std::size_t len = line.length();
// lines have to be at least 3 characters to have any meaning to CLI just skip the rest
if(len < 3) {
continue;
}
if(line.front() == '[' && line.back() == ']') {
if(currentSection != "default") {
// insert a section end which is just an empty items_buffer
output.emplace_back();
output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
output.back().name = "--";
}
currentSection = line.substr(1, len - 2);
// deal with double brackets for TOML
if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
currentSection = currentSection.substr(1, currentSection.size() - 2);
}
if(detail::to_lower(currentSection) == "default") {
currentSection = "default";
} else {
detail::checkParentSegments(output, currentSection, parentSeparatorChar);
}
inSection = false;
if(currentSection == previousSection) {
++currentSectionIndex;
} else {
currentSectionIndex = 0;
previousSection = currentSection;
}
continue;
}
// comment lines
if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
continue;
}
// Find = in string, split and recombine
auto pos = line.find(valueDelimiter);
if(pos != std::string::npos) {
name = detail::trim_copy(line.substr(0, pos));
std::string item = detail::trim_copy(line.substr(pos + 1));
auto cloc = item.find(commentChar);
if(cloc != std::string::npos) {
item.erase(cloc, std::string::npos);
detail::trim(item);
}
if(item.size() > 1 && item.front() == aStart) {
for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
detail::trim(multiline);
item += multiline;
}
items_buffer = detail::split_up(item.substr(1, item.length() - 2), aSep);
} else if((isDefaultArray || isINIArray) && item.find_first_of(aSep) != std::string::npos) {
items_buffer = detail::split_up(item, aSep);
} else if((isDefaultArray || isINIArray) && item.find_first_of(' ') != std::string::npos) {
items_buffer = detail::split_up(item);
} else {
items_buffer = {item};
}
} else {
name = detail::trim_copy(line);
auto cloc = name.find(commentChar);
if(cloc != std::string::npos) {
name.erase(cloc, std::string::npos);
detail::trim(name);
}
items_buffer = {"true"};
}
if(name.find(parentSeparatorChar) == std::string::npos) {
detail::remove_quotes(name);
}
// clean up quotes on the items
for(auto &it : items_buffer) {
detail::remove_quotes(it);
}
std::vector<std::string> parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
if(parents.size() > maximumLayers) {
continue;
}
if(!configSection.empty() && !inSection) {
if(parents.empty() || parents.front() != configSection) {
continue;
}
if(configIndex >= 0 && currentSectionIndex != configIndex) {
continue;
}
parents.erase(parents.begin());
inSection = true;
}
if(!output.empty() && name == output.back().name && parents == output.back().parents) {
output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
} else {
output.emplace_back();
output.back().parents = std::move(parents);
output.back().name = std::move(name);
output.back().inputs = std::move(items_buffer);
}
}
if(currentSection != "default") {
// insert a section end which is just an empty items_buffer
std::string ename;
output.emplace_back();
output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
output.back().name = "--";
while(output.back().parents.size() > 1) {
output.push_back(output.back());
output.back().parents.pop_back();
}
}
return output;
}
inline std::string
ConfigBase::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
std::stringstream out;
std::string commentLead;
commentLead.push_back(commentChar);
commentLead.push_back(' ');
std::vector<std::string> groups = app->get_groups();
bool defaultUsed = false;
groups.insert(groups.begin(), std::string("Options"));
if(write_description && (app->get_configurable() || app->get_parent() == nullptr || app->get_name().empty())) {
out << commentLead << detail::fix_newlines(commentLead, app->get_description()) << '\n';
}
for(auto &group : groups) {
if(group == "Options" || group.empty()) {
if(defaultUsed) {
continue;
}
defaultUsed = true;
}
if(write_description && group != "Options" && !group.empty()) {
out << '\n' << commentLead << group << " Options\n";
}
for(const Option *opt : app->get_options({})) {
// Only process options that are configurable
if(opt->get_configurable()) {
if(opt->get_group() != group) {
if(!(group == "Options" && opt->get_group().empty())) {
continue;
}
}
std::string name = prefix + opt->get_single_name();
std::string value = detail::ini_join(
opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);
if(value.empty() && default_also) {
if(!opt->get_default_str().empty()) {
value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
} else if(opt->get_expected_min() == 0) {
value = "false";
} else if(opt->get_run_callback_for_default()) {
value = "\"\""; // empty string default value
}
}
if(!value.empty()) {
if(write_description && opt->has_description()) {
out << '\n';
out << commentLead << detail::fix_newlines(commentLead, opt->get_description()) << '\n';
}
out << name << valueDelimiter << value << '\n';
}
}
}
}
auto subcommands = app->get_subcommands({});
for(const App *subcom : subcommands) {
if(subcom->get_name().empty()) {
if(write_description && !subcom->get_group().empty()) {
out << '\n' << commentLead << subcom->get_group() << " Options\n";
}
out << to_config(subcom, default_also, write_description, prefix);
}
}
for(const App *subcom : subcommands) {
if(!subcom->get_name().empty()) {
if(subcom->get_configurable() && app->got_subcommand(subcom)) {
if(!prefix.empty() || app->get_parent() == nullptr) {
out << '[' << prefix << subcom->get_name() << "]\n";
} else {
std::string subname = app->get_name() + parentSeparatorChar + subcom->get_name();
auto p = app->get_parent();
while(p->get_parent() != nullptr) {
subname = p->get_name() + parentSeparatorChar + subname;
p = p->get_parent();
}
out << '[' << subname << "]\n";
}
out << to_config(subcom, default_also, write_description, "");
} else {
out << to_config(
subcom, default_also, write_description, prefix + subcom->get_name() + parentSeparatorChar);
}
}
}
return out.str();
}
// [CLI11:config_hpp:end]
} // namespace CLI

View File

@@ -0,0 +1,185 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:public_includes:set]
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
// [CLI11:public_includes:end]
#include "Error.hpp"
#include "StringTools.hpp"
namespace CLI {
// [CLI11:config_fwd_hpp:verbatim]
class App;
/// Holds values to load into Options
struct ConfigItem {
/// This is the list of parents
std::vector<std::string> parents{};
/// This is the name
std::string name{};
/// Listing of inputs
std::vector<std::string> inputs{};
/// The list of parents and name joined by "."
std::string fullname() const {
std::vector<std::string> tmp = parents;
tmp.emplace_back(name);
return detail::join(tmp, ".");
}
};
/// This class provides a converter for configuration files.
class Config {
protected:
std::vector<ConfigItem> items{};
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<ConfigItem> from_config(std::istream &) const = 0;
/// Get a flag value
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());
}
/// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure
std::vector<ConfigItem> from_file(const std::string &name) {
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
char characterQuote = '\'';
/// the maximum number of layers to allow
uint8_t maximumLayers{255};
/// the separator used to separator parent layers
char parentSeparatorChar{'.'};
/// 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;
std::vector<ConfigItem> 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 characters
ConfigBase *quoteCharacter(char qString, char qChar) {
stringQuote = qString;
characterQuote = qChar;
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;
}
/// get a reference to the configuration section
std::string &sectionRef() { return configSection; }
/// get the section
const std::string &section() const { return configSection; }
/// specify a particular section of the configuration file to use
ConfigBase *section(const std::string &sectionName) {
configSection = sectionName;
return this;
}
/// get a reference to the configuration index
int16_t &indexRef() { return configIndex; }
/// get the section index
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;
}
};
/// the default Config is the TOML file format
using ConfigTOML = ConfigBase;
/// ConfigINI generates a "standard" INI compliant output
class ConfigINI : public ConfigTOML {
public:
ConfigINI() {
commentChar = ';';
arrayStart = '\0';
arrayEnd = '\0';
arraySeparator = ' ';
valueDelimiter = '=';
}
};
// [CLI11:config_fwd_hpp:end]
} // namespace CLI

View File

@@ -0,0 +1,355 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:public_includes:set]
#include <exception>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
// [CLI11:public_includes:end]
// CLI library includes
#include "StringTools.hpp"
namespace CLI {
// [CLI11:error_hpp:verbatim]
// 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) {}
// 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().
enum class ExitCodes {
Success = 0,
IncorrectConstruction = 100,
BadNameString,
OptionAlreadyAdded,
FileError,
ConversionError,
ValidationError,
RequiredError,
RequiresError,
ExcludesError,
ExtrasError,
ConfigError,
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 actual_exit_code;
std::string error_name{"Error"};
public:
int get_exit_code() const { return actual_exit_code; }
std::string get_name() const { return error_name; }
Error(std::string name, std::string msg, int exit_code = static_cast<int>(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<int>(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 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 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)
explicit 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 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) {}
};
/// -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) {}
};
/// 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) {}
};
/// 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<std::string> 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) {}
};
/// 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 RequiredError("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 RequiredError("Exactly 1 option from [" + option_list + "] is required and " + 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 RequiredError("Requires at least " + std::to_string(min_option) + " options used and only " +
std::to_string(used) + "were given from [" + option_list + "]",
ExitCodes::RequiredError);
}
if(max_option == 1)
return RequiredError("Requires at most 1 options be given from [" + option_list + "]",
ExitCodes::RequiredError);
return RequiredError("Requires at most " + std::to_string(max_option) + " options be used and " +
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) {}
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) {}
};
/// 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)
explicit ExtrasError(std::vector<std::string> 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<std::string> 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");
}
};
/// 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) {
}
};
/// 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)
explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
};
#undef CLI11_ERROR_DEF
#undef CLI11_ERROR_SIMPLE
/// @}
// [CLI11:error_hpp:end]
} // namespace CLI

View File

@@ -0,0 +1,292 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:public_includes:set]
#include <algorithm>
#include <string>
#include <vector>
// [CLI11:public_includes:end]
#include "App.hpp"
#include "FormatterFwd.hpp"
namespace CLI {
// [CLI11:formatter_hpp:verbatim]
inline std::string
Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
std::stringstream out;
out << "\n" << group << ":\n";
for(const Option *opt : opts) {
out << make_option(opt, is_positional);
}
return out.str();
}
inline std::string Formatter::make_positionals(const App *app) const {
std::vector<const Option *> opts =
app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
if(opts.empty())
return std::string();
return make_group(get_label("Positionals"), true, opts);
}
inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
std::stringstream out;
std::vector<std::string> groups = app->get_groups();
// Options
for(const std::string &group : groups) {
std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) {
return opt->get_group() == group // Must be in the right group
&& opt->nonpositional() // Must not be a positional
&& (mode != AppFormatMode::Sub // If mode is Sub, then
|| (app->get_help_ptr() != opt // Ignore help pointer
&& app->get_help_all_ptr() != opt)); // Ignore help all pointer
});
if(!group.empty() && !opts.empty()) {
out << make_group(group, false, opts);
if(group != groups.back())
out << "\n";
}
}
return out.str();
}
inline std::string Formatter::make_description(const App *app) const {
std::string desc = app->get_description();
auto min_options = app->get_require_option_min();
auto max_options = app->get_require_option_max();
if(app->get_required()) {
desc += " REQUIRED ";
}
if((max_options == min_options) && (min_options > 0)) {
if(min_options == 1) {
desc += " \n[Exactly 1 of the following options is required]";
} else {
desc += " \n[Exactly " + std::to_string(min_options) + "options from the following list are required]";
}
} else if(max_options > 0) {
if(min_options > 0) {
desc += " \n[Between " + std::to_string(min_options) + " and " + std::to_string(max_options) +
" of the follow options are required]";
} else {
desc += " \n[At most " + std::to_string(max_options) + " of the following options are allowed]";
}
} else if(min_options > 0) {
desc += " \n[At least " + std::to_string(min_options) + " of the following options are required]";
}
return (!desc.empty()) ? desc + "\n" : std::string{};
}
inline std::string Formatter::make_usage(const App *app, std::string name) const {
std::stringstream out;
out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
std::vector<std::string> groups = app->get_groups();
// Print an Options badge if any options exist
std::vector<const Option *> non_pos_options =
app->get_options([](const Option *opt) { return opt->nonpositional(); });
if(!non_pos_options.empty())
out << " [" << get_label("OPTIONS") << "]";
// Positionals need to be listed here
std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
// Print out positionals if any are left
if(!positionals.empty()) {
// Convert to help names
std::vector<std::string> positional_names(positionals.size());
std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
return make_option_usage(opt);
});
out << " " << detail::join(positional_names, " ");
}
// Add a marker if subcommands are expected or optional
if(!app->get_subcommands(
[](const CLI::App *subc) { return ((!subc->get_disabled()) && (!subc->get_name().empty())); })
.empty()) {
out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
<< get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
: "SUBCOMMANDS")
<< (app->get_require_subcommand_min() == 0 ? "]" : "");
}
out << std::endl;
return out.str();
}
inline std::string Formatter::make_footer(const App *app) const {
std::string footer = app->get_footer();
if(footer.empty()) {
return std::string{};
}
return footer + "\n";
}
inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
// This immediately forwards to the make_expanded method. This is done this way so that subcommands can
// have overridden formatters
if(mode == AppFormatMode::Sub)
return make_expanded(app);
std::stringstream out;
if((app->get_name().empty()) && (app->get_parent() != nullptr)) {
if(app->get_group() != "Subcommands") {
out << app->get_group() << ':';
}
}
out << make_description(app);
out << make_usage(app, name);
out << make_positionals(app);
out << make_groups(app, mode);
out << make_subcommands(app, mode);
out << '\n' << make_footer(app);
return out.str();
}
inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
std::stringstream out;
std::vector<const App *> subcommands = app->get_subcommands({});
// Make a list in definition order of the groups seen
std::vector<std::string> subcmd_groups_seen;
for(const App *com : subcommands) {
if(com->get_name().empty()) {
if(!com->get_group().empty()) {
out << make_expanded(com);
}
continue;
}
std::string group_key = com->get_group();
if(!group_key.empty() &&
std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
return detail::to_lower(a) == detail::to_lower(group_key);
}) == subcmd_groups_seen.end())
subcmd_groups_seen.push_back(group_key);
}
// For each group, filter out and print subcommands
for(const std::string &group : subcmd_groups_seen) {
out << "\n" << group << ":\n";
std::vector<const App *> subcommands_group = app->get_subcommands(
[&group](const App *sub_app) { return detail::to_lower(sub_app->get_group()) == detail::to_lower(group); });
for(const App *new_com : subcommands_group) {
if(new_com->get_name().empty())
continue;
if(mode != AppFormatMode::All) {
out << make_subcommand(new_com);
} else {
out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
out << "\n";
}
}
}
return out.str();
}
inline std::string Formatter::make_subcommand(const App *sub) const {
std::stringstream out;
detail::format_help(out, sub->get_display_name(true), sub->get_description(), column_width_);
return out.str();
}
inline std::string Formatter::make_expanded(const App *sub) const {
std::stringstream out;
out << sub->get_display_name(true) << "\n";
out << make_description(sub);
if(sub->get_name().empty() && !sub->get_aliases().empty()) {
detail::format_aliases(out, sub->get_aliases(), column_width_ + 2);
}
out << make_positionals(sub);
out << make_groups(sub, AppFormatMode::Sub);
out << make_subcommands(sub, AppFormatMode::Sub);
// Drop blank spaces
std::string tmp = detail::find_and_replace(out.str(), "\n\n", "\n");
tmp = tmp.substr(0, tmp.size() - 1); // Remove the final '\n'
// Indent all but the first line (the name)
return detail::find_and_replace(tmp, "\n", "\n ") + "\n";
}
inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
if(is_positional)
return opt->get_name(true, false);
return opt->get_name(false, true);
}
inline std::string Formatter::make_option_opts(const Option *opt) const {
std::stringstream out;
if(!opt->get_option_text().empty()) {
out << " " << opt->get_option_text();
} else {
if(opt->get_type_size() != 0) {
if(!opt->get_type_name().empty())
out << " " << get_label(opt->get_type_name());
if(!opt->get_default_str().empty())
out << " [" << opt->get_default_str() << "] ";
if(opt->get_expected_max() == detail::expected_max_vector_size)
out << " ...";
else if(opt->get_expected_min() > 1)
out << " x " << opt->get_expected();
if(opt->get_required())
out << " " << get_label("REQUIRED");
}
if(!opt->get_envname().empty())
out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
if(!opt->get_needs().empty()) {
out << " " << get_label("Needs") << ":";
for(const Option *op : opt->get_needs())
out << " " << op->get_name();
}
if(!opt->get_excludes().empty()) {
out << " " << get_label("Excludes") << ":";
for(const Option *op : opt->get_excludes())
out << " " << op->get_name();
}
}
return out.str();
}
inline std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
inline std::string Formatter::make_option_usage(const Option *opt) const {
// Note that these are positionals usages
std::stringstream out;
out << make_option_name(opt, true);
if(opt->get_expected_max() >= detail::expected_max_vector_size)
out << "...";
else if(opt->get_expected_max() > 1)
out << "(" << opt->get_expected() << "x)";
return opt->get_required() ? out.str() : "[" + out.str() + "]";
}
// [CLI11:formatter_hpp:end]
} // namespace CLI

View File

@@ -0,0 +1,185 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:public_includes:set]
#include <functional>
#include <map>
#include <string>
#include <utility>
#include <vector>
// [CLI11:public_includes:end]
#include "StringTools.hpp"
namespace CLI {
// [CLI11:formatter_fwd_hpp:verbatim]
class Option;
class App;
/// This enum signifies the type of help requested
///
/// This is passed in by App; all user classes must accept this as
/// 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
};
/// This is the minimum requirements to run a formatter.
///
/// A user can subclass this is if they do not care at all
/// about the structure in CLI::Formatter.
class FormatterBase {
protected:
/// @name Options
///@{
/// The width of the first column
std::size_t column_width_{30};
/// @brief The required help printout labels (user changeable)
/// Values are Needs, Excludes, etc.
std::map<std::string, std::string> labels_{};
///@}
/// @name Basic
///@{
public:
FormatterBase() = default;
FormatterBase(const FormatterBase &) = default;
FormatterBase(FormatterBase &&) = 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;
///@}
/// @name Setters
///@{
/// Set the "REQUIRED" label
void label(std::string key, std::string val) { labels_[key] = val; }
/// Set the column width
void column_width(std::size_t val) { column_width_ = val; }
///@}
/// @name Getters
///@{
/// Get the current value of a name (REQUIRED, etc.)
std::string get_label(std::string key) const {
if(labels_.find(key) == labels_.end())
return key;
else
return labels_.at(key);
}
/// Get the current column width
std::size_t get_column_width() const { return column_width_; }
///@}
};
/// This is a specialty override for lambda functions
class FormatterLambda final : public FormatterBase {
using funct_t = std::function<std::string(const App *, std::string, AppFormatMode)>;
/// 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)) {}
/// 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 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;
/// @name Overridables
///@{
/// This prints out a group of options with title
///
virtual std::string make_group(std::string group, bool is_positional, std::vector<const Option *> opts) 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 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 in help-all
virtual std::string make_expanded(const App *sub) 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 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) const override;
///@}
/// @name Options
///@{
/// This prints out an option help line, either positional or optional form
virtual std::string make_option(const Option *opt, bool is_positional) const {
std::stringstream out;
detail::format_help(
out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_);
return out.str();
}
/// @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 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;
///@}
};
// [CLI11:formatter_fwd_hpp:end]
} // namespace CLI

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:macros_hpp:verbatim]
// The following version macro is very similar to the one in pybind11
#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
#if __cplusplus >= 201402L
#define CLI11_CPP14
#if __cplusplus >= 201703L
#define CLI11_CPP17
#if __cplusplus > 201703L
#define CLI11_CPP20
#endif
#endif
#endif
#elif defined(_MSC_VER) && __cplusplus == 199711L
// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard is 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
#define CLI11_CPP17
#if _MSVC_LANG > 201703L && _MSC_VER >= 1910
#define CLI11_CPP20
#endif
#endif
#endif
#endif
#if defined(CLI11_CPP14)
#define CLI11_DEPRECATED(reason) [[deprecated(reason)]]
#elif defined(_MSC_VER)
#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason))
#else
#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))
#endif
/** detection of rtti */
#ifndef CLI11_USE_STATIC_RTTI
#if(defined(_HAS_STATIC_RTTI) && _HAS_STATIC_RTTI)
#define CLI11_USE_STATIC_RTTI 1
#elif defined(__cpp_rtti)
#if(defined(_CPPRTTI) && _CPPRTTI == 0)
#define CLI11_USE_STATIC_RTTI 1
#else
#define CLI11_USE_STATIC_RTTI 0
#endif
#elif(defined(__GCC_RTTI) && __GXX_RTTI)
#define CLI11_USE_STATIC_RTTI 0
#else
#define CLI11_USE_STATIC_RTTI 1
#endif
#endif
// [CLI11:macros_hpp:end]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,143 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:public_includes:set]
#include <string>
#include <tuple>
#include <utility>
#include <vector>
// [CLI11:public_includes:end]
#include "Error.hpp"
#include "StringTools.hpp"
namespace CLI {
// [CLI11:split_hpp:verbatim]
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 &current, 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;
}
// 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 &current, std::string &name, std::string &value) {
if(current.size() > 2 && current.substr(0, 2) == "--" && 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;
}
return false;
}
// Returns false if not a windows style option. Otherwise, sets opt name and value and returns true
inline bool split_windows_style(const std::string &current, 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;
}
return false;
}
// Splits a string into multiple long and short names
inline std::vector<std::string> split_names(std::string current) {
std::vector<std::string> output;
std::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;
}
/// extract default flag values either {def} or starting with a !
inline std::vector<std::pair<std::string, std::string>> get_default_flag_values(const std::string &str) {
std::vector<std::string> 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<std::pair<std::string, std::string>> 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);
}
flag.erase(0, flag.find_first_not_of("-!"));
output.emplace_back(flag, defval);
}
return output;
}
/// Get a vector of short names, one of long names, and a single name
inline std::tuple<std::vector<std::string>, std::vector<std::string>, std::string>
get_names(const std::vector<std::string> &input) {
std::vector<std::string> short_names;
std::vector<std::string> 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
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>, std::vector<std::string>, std::string>(
short_names, long_names, pos_name);
}
} // namespace detail
// [CLI11:split_hpp:end]
} // namespace CLI

View File

@@ -0,0 +1,430 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:public_includes:set]
#include <algorithm>
#include <iomanip>
#include <locale>
#include <sstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>
// [CLI11:public_includes:end]
namespace CLI {
// [CLI11:string_tools_hpp:verbatim]
/// 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 <typename T, typename = typename std::enable_if<std::is_enum<T>::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<typename std::underlying_type<T>::type>(item);
}
} // 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
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
inline std::vector<std::string> split(const std::string &s, char delim) {
std::vector<std::string> 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 <typename T> 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++;
}
return s.str();
}
/// Simple function to join a string from processed elements
template <typename T,
typename Callable,
typename = typename std::enable_if<!std::is_constructible<std::string, Callable>::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++);
}
return s.str();
}
/// Join a string in reverse order
template <typename T> 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();
}
// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
/// Trim whitespace from left of string
inline std::string &ltrim(std::string &str) {
auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
str.erase(str.begin(), it);
return str;
}
/// Trim anything from left of string
inline std::string &ltrim(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<char>(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);
}
/// remove quotes at the front and back of a string either '"' or '\''
inline std::string &remove_quotes(std::string &str) {
if(str.length() > 1 && (str.front() == '"' || str.front() == '\'')) {
if(str.front() == str.back()) {
str.pop_back();
str.erase(str.begin(), str.begin() + 1);
}
}
return str;
}
/// 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(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;
}
/// 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 std::ostream &format_help(std::ostream &out, std::string name, const std::string &description, std::size_t wid) {
name = " " + name;
out << std::setw(static_cast<int>(wid)) << std::left << name;
if(!description.empty()) {
if(name.length() >= wid)
out << "\n" << std::setw(static_cast<int>(wid)) << "";
for(const char c : description) {
out.put(c);
if(c == '\n') {
out << std::setw(static_cast<int>(wid)) << "";
}
}
}
out << "\n";
return out;
}
/// Print subcommand aliases
inline std::ostream &format_aliases(std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid) {
if(!aliases.empty()) {
out << std::setw(static_cast<int>(wid)) << " aliases: ";
bool front = true;
for(const auto &alias : aliases) {
if(!front) {
out << ", ";
} else {
front = false;
}
out << detail::fix_newlines(" ", alias);
}
out << "\n";
}
return out;
}
/// Verify the first character of an option
/// - is a trigger character, ! has special meaning and new lines would just be annoying to deal with
template <typename T> bool valid_first_char(T c) { return ((c != '-') && (c != '!') && (c != ' ') && c != '\n'); }
/// Verify following characters of an option
template <typename T> bool valid_later_char(T c) {
// = and : are value separators, { has special meaning for option defaults,
// and \n 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 != '{') && (c != ' ') && c != '\n');
}
/// Verify an option/subcommand name
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;
}
/// 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);
}
/// 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);
}
/// 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 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;
}
/// 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;
}
/// Find and replace a substring with another substring
inline std::string find_and_replace(std::string str, std::string from, std::string to) {
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();
}
return str;
}
/// 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);
}
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<std::ptrdiff_t>(loc),
flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
}
loc = flags.find_first_of('{', loc + 1);
}
flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
}
/// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores
inline std::ptrdiff_t find_member(std::string name,
const std::vector<std::string> names,
bool ignore_case = false,
bool ignore_underscore = false) {
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;
});
} else {
it = std::find(std::begin(names), std::end(names), name);
}
return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
}
/// 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 <typename Callable> 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;
}
/// Split a string '"one two" "three"' into 'one two', 'three'
/// Quote characters can be ` ' or "
inline std::vector<std::string> split_up(std::string str, char delimiter = '\0') {
const std::string delims("\'\"`");
auto find_ws = [delimiter](char ch) {
return (delimiter == '\0') ? (std::isspace<char>(ch, std::locale()) != 0) : (ch == delimiter);
};
trim(str);
std::vector<std::string> output;
bool embeddedQuote = false;
char keyChar = ' ';
while(!str.empty()) {
if(delims.find_first_of(str[0]) != std::string::npos) {
keyChar = str[0];
auto end = str.find_first_of(keyChar, 1);
while((end != std::string::npos) && (str[end - 1] == '\\')) { // deal with escaped quotes
end = str.find_first_of(keyChar, end + 1);
embeddedQuote = true;
}
if(end != std::string::npos) {
output.push_back(str.substr(1, end - 1));
if(end + 2 < str.size()) {
str = str.substr(end + 2);
} else {
str.clear();
}
} 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 + 1, str.end());
} else {
output.push_back(str);
str = "";
}
}
// transform any embedded quotes into the regular character
if(embeddedQuote) {
output.back() = find_and_replace(output.back(), std::string("\\") + keyChar, std::string(1, keyChar));
embeddedQuote = false;
}
trim(str);
}
return output;
}
/// 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.
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
}
}
return offset + 1;
}
/// Add quotes if the string contains spaces
inline std::string &add_quotes_if_needed(std::string &str) {
if((str.front() != '"' && str.front() != '\'') || str.front() != str.back()) {
char quote = str.find('"') < str.find('\'') ? '\'' : '"';
if(str.find(' ') != std::string::npos) {
str.insert(0, 1, quote);
str.append(1, quote);
}
}
return str;
}
} // namespace detail
// [CLI11:string_tools_hpp:end]
} // namespace CLI

View File

@@ -0,0 +1,134 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// On GCC < 4.8, the following define is often missing. Due to the
// fact that this library only uses sleep_for, this should be safe
#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 && __GNUC_MINOR__ < 8
#define _GLIBCXX_USE_NANOSLEEP
#endif
#include <array>
#include <chrono>
#include <functional>
#include <iostream>
#include <string>
#include <utility>
namespace CLI {
/// This is a simple timer with pretty printing. Creating the timer starts counting.
class Timer {
protected:
/// This is a typedef to make clocks easier to use
using clock = std::chrono::steady_clock;
/// This typedef is for points in time
using time_point = std::chrono::time_point<clock>;
/// This is the type of a printing function, you can make your own
using time_print_t = std::function<std::string(std::string, std::string)>;
/// This is the title of the timer
std::string title_;
/// This is the function that is used to format most of the timing message
time_print_t time_print_;
/// This is the starting point (when the timer was created)
time_point start_;
/// This is the number of times cycles (print divides by this number)
std::size_t cycles{1};
public:
/// Standard print function, this one is set by default
static std::string Simple(std::string title, std::string time) { return title + ": " + time; }
/// This is a fancy print function with --- headers
static std::string Big(std::string title, std::string time) {
return std::string("-----------------------------------------\n") + "| " + title + " | Time = " + time + "\n" +
"-----------------------------------------";
}
public:
/// Standard constructor, can set title and print function
explicit Timer(std::string title = "Timer", time_print_t time_print = Simple)
: title_(std::move(title)), time_print_(std::move(time_print)), start_(clock::now()) {}
/// Time a function by running it multiple times. Target time is the len to target.
std::string time_it(std::function<void()> f, double target_time = 1) {
time_point start = start_;
double total_time;
start_ = clock::now();
std::size_t n = 0;
do {
f();
std::chrono::duration<double> elapsed = clock::now() - start_;
total_time = elapsed.count();
} while(n++ < 100u && total_time < target_time);
std::string out = make_time_str(total_time / static_cast<double>(n)) + " for " + std::to_string(n) + " tries";
start_ = start;
return out;
}
/// This formats the numerical value for the time string
std::string make_time_str() const {
time_point stop = clock::now();
std::chrono::duration<double> elapsed = stop - start_;
double time = elapsed.count() / static_cast<double>(cycles);
return make_time_str(time);
}
// LCOV_EXCL_START
/// This prints out a time string from a time
std::string make_time_str(double time) const {
auto print_it = [](double x, std::string unit) {
const unsigned int buffer_length = 50;
std::array<char, buffer_length> buffer;
std::snprintf(buffer.data(), buffer_length, "%.5g", x);
return buffer.data() + std::string(" ") + unit;
};
if(time < .000001)
return print_it(time * 1000000000, "ns");
else if(time < .001)
return print_it(time * 1000000, "us");
else if(time < 1)
return print_it(time * 1000, "ms");
else
return print_it(time, "s");
}
// LCOV_EXCL_STOP
/// This is the main function, it creates a string
std::string to_string() const { return time_print_(title_, make_time_str()); }
/// Division sets the number of cycles to divide by (no graphical change)
Timer &operator/(std::size_t val) {
cycles = val;
return *this;
}
};
/// This class prints out the time upon destruction
class AutoTimer : public Timer {
public:
/// Reimplementing the constructor is required in GCC 4.7
explicit AutoTimer(std::string title = "Timer", time_print_t time_print = Simple) : Timer(title, time_print) {}
// GCC 4.7 does not support using inheriting constructors.
/// This destructor prints the string
~AutoTimer() { std::cout << to_string() << std::endl; }
};
} // namespace CLI
/// This prints out the time if shifted into a std::cout like stream.
inline std::ostream &operator<<(std::ostream &in, const CLI::Timer &timer) { return in << timer.to_string(); }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
// Copyright (c) 2017-2022, University of Cincinnati, developed by Henry Schreiner
// under NSF AWARD 1414736 and by the respective contributors.
// All rights reserved.
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
// [CLI11:version_hpp:verbatim]
#define CLI11_VERSION_MAJOR 2
#define CLI11_VERSION_MINOR 2
#define CLI11_VERSION_PATCH 0
#define CLI11_VERSION "2.2.0"
// [CLI11:version_hpp:end]