From e940c816707a198d9323b95cd5e9bbdb90fb2035 Mon Sep 17 00:00:00 2001 From: Riyyi Date: Wed, 17 Aug 2022 16:24:45 +0200 Subject: [PATCH] Util+Test: Add code snapshot from the riyyi/manafiles project Add all utility and unit test code from the riyyi/manafiles project at commit 6f0e3d6063ab75ad81899135689569e440ddb813, link at: github.com/riyyi/manafiles/tree/6f0e3d6063ab75ad81899135689569e440ddb813 --- src/util/argparser.cpp | 568 +++++++++++++++++++ src/util/argparser.h | 120 ++++ src/util/file.cpp | 89 +++ src/util/file.h | 34 ++ src/util/format/.clang-tidy | 6 + src/util/format/builder.cpp | 222 ++++++++ src/util/format/builder.h | 85 +++ src/util/format/color.cpp | 176 ++++++ src/util/format/color.h | 156 ++++++ src/util/format/format.cpp | 76 +++ src/util/format/format.h | 119 ++++ src/util/format/formatter.cpp | 117 ++++ src/util/format/formatter.h | 325 +++++++++++ src/util/format/log.cpp | 109 ++++ src/util/format/log.h | 97 ++++ src/util/format/parser.cpp | 419 ++++++++++++++ src/util/format/parser.h | 61 ++ src/util/format/print.cpp | 52 ++ src/util/format/print.h | 69 +++ src/util/genericlexer.cpp | 71 +++ src/util/genericlexer.h | 41 ++ src/util/json/array.cpp | 26 + src/util/json/array.h | 56 ++ src/util/json/fromjson.h | 133 +++++ src/util/json/job.cpp | 114 ++++ src/util/json/job.h | 41 ++ src/util/json/json.h | 15 + src/util/json/lexer.cpp | 217 ++++++++ src/util/json/lexer.h | 65 +++ src/util/json/object.cpp | 26 + src/util/json/object.h | 52 ++ src/util/json/parser.cpp | 497 +++++++++++++++++ src/util/json/parser.h | 45 ++ src/util/json/serializer.cpp | 155 ++++++ src/util/json/serializer.h | 35 ++ src/util/json/tojson.h | 153 +++++ src/util/json/value.cpp | 340 ++++++++++++ src/util/json/value.h | 165 ++++++ src/util/meta/assert.h | 96 ++++ src/util/meta/compiler.h | 27 + src/util/meta/concepts.h | 22 + src/util/meta/odr.h | 20 + src/util/shell.cpp | 53 ++ src/util/shell.h | 33 ++ src/util/singleton.h | 53 ++ src/util/system.cpp | 328 +++++++++++ src/util/system.h | 68 +++ src/util/timer.cpp | 131 +++++ src/util/timer.h | 51 ++ test/macro.h | 79 +++ test/main.cpp | 8 + test/testcase.h | 45 ++ test/testsuite.cpp | 76 +++ test/testsuite.h | 36 ++ test/unit/testutilargparser.cpp | 949 ++++++++++++++++++++++++++++++++ test/unit/testutilformat.cpp | 517 +++++++++++++++++ test/unit/testutiljson.cpp | 570 +++++++++++++++++++ test/unit/testutilshell.cpp | 39 ++ test/unit/testutilsystem.cpp | 98 ++++ 59 files changed, 8446 insertions(+) create mode 100644 src/util/argparser.cpp create mode 100644 src/util/argparser.h create mode 100644 src/util/file.cpp create mode 100644 src/util/file.h create mode 100644 src/util/format/.clang-tidy create mode 100644 src/util/format/builder.cpp create mode 100644 src/util/format/builder.h create mode 100644 src/util/format/color.cpp create mode 100644 src/util/format/color.h create mode 100644 src/util/format/format.cpp create mode 100644 src/util/format/format.h create mode 100644 src/util/format/formatter.cpp create mode 100644 src/util/format/formatter.h create mode 100644 src/util/format/log.cpp create mode 100644 src/util/format/log.h create mode 100644 src/util/format/parser.cpp create mode 100644 src/util/format/parser.h create mode 100644 src/util/format/print.cpp create mode 100644 src/util/format/print.h create mode 100644 src/util/genericlexer.cpp create mode 100644 src/util/genericlexer.h create mode 100644 src/util/json/array.cpp create mode 100644 src/util/json/array.h create mode 100644 src/util/json/fromjson.h create mode 100644 src/util/json/job.cpp create mode 100644 src/util/json/job.h create mode 100644 src/util/json/json.h create mode 100644 src/util/json/lexer.cpp create mode 100644 src/util/json/lexer.h create mode 100644 src/util/json/object.cpp create mode 100644 src/util/json/object.h create mode 100644 src/util/json/parser.cpp create mode 100644 src/util/json/parser.h create mode 100644 src/util/json/serializer.cpp create mode 100644 src/util/json/serializer.h create mode 100644 src/util/json/tojson.h create mode 100644 src/util/json/value.cpp create mode 100644 src/util/json/value.h create mode 100644 src/util/meta/assert.h create mode 100644 src/util/meta/compiler.h create mode 100644 src/util/meta/concepts.h create mode 100644 src/util/meta/odr.h create mode 100644 src/util/shell.cpp create mode 100644 src/util/shell.h create mode 100644 src/util/singleton.h create mode 100644 src/util/system.cpp create mode 100644 src/util/system.h create mode 100644 src/util/timer.cpp create mode 100644 src/util/timer.h create mode 100644 test/macro.h create mode 100644 test/main.cpp create mode 100644 test/testcase.h create mode 100644 test/testsuite.cpp create mode 100644 test/testsuite.h create mode 100644 test/unit/testutilargparser.cpp create mode 100644 test/unit/testutilformat.cpp create mode 100644 test/unit/testutiljson.cpp create mode 100644 test/unit/testutilshell.cpp create mode 100644 test/unit/testutilsystem.cpp diff --git a/src/util/argparser.cpp b/src/util/argparser.cpp new file mode 100644 index 0000000..63b9968 --- /dev/null +++ b/src/util/argparser.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2021-2022 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#include // find_if +#include // size_t +#include // uint8_t +#include // printf +#include // strcmp +#include // numeric_limits +#include // stod, stoi, stoul +#include +#include + +#include "util/argparser.h" + +namespace Util { + +ArgParser::ArgParser() +{ +} + +ArgParser::~ArgParser() +{ +} + +void ArgParser::printError(char parameter, Error error) +{ + char tmp[] { parameter, '\0' }; + printError(tmp, error, false); +} + +void ArgParser::printError(const char* parameter, Error error, bool longName) +{ + if (!m_errorReporting) { + return; + } + + if (error == Error::OptionInvalid) { + printf("%s: invalid option -- '%s'\n", m_name, parameter); + } + else if (error == Error::OptionUnrecognized) { + printf("%s: unrecognized option -- '%s'\n", m_name, parameter); + } + else if (error == Error::OptionDoesntAllowArgument) { + printf("%s: option '--%s' doesn't allow an argument\n", m_name, parameter); + } + else if (error == Error::OptionRequiresArgument) { + if (longName) { + printf("%s: option '--%s' requires an argument\n", m_name, parameter); + } + else { + printf("%s: option requires an argument -- '%s'\n", m_name, parameter); + } + } + else if (error == Error::OptionInvalidArgumentType) { + if (longName) { + printf("%s: option '--%s' invalid type\n", m_name, parameter); + } + else { + printf("%s: option invalid type -- '%s'\n", m_name, parameter); + } + } + else if (error == Error::ArgumentExtraOperand) { + printf("%s: extra operand '%s'\n", m_name, parameter); + } + else if (error == Error::ArgumentRequired) { + printf("%s: missing required argument '%s'\n", m_name, parameter); + } + else if (error == Error::ArgumentInvalidType) { + printf("%s: invalid argument type '%s'\n", m_name, parameter); + } + + // TODO: Print command usage, if it's enabled. +} + +// Required: directly after || separated by space +// Optional: directly after +bool ArgParser::parseShortOption(std::string_view option, std::string_view next) +{ + bool result = true; + +#ifndef NDEBUG + printf("Parsing short option: '%s'\n", option.data()); +#endif + + char c; + std::string_view value; + for (size_t i = 0; i < option.size(); ++i) { + c = option.at(i); + +#ifndef NDEBUG + printf("short '%c'\n", c); +#endif + + auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&c](Option& it) -> bool { + return it.shortName == c; + }); + + // Option does not exist + if (foundOption == m_options.cend()) { + printError(c, Error::OptionInvalid); + + if (m_exitOnFirstError) { + return false; + } + } + else if (foundOption->requiresArgument == Required::No) { + // FIXME: Figure out why providing a nullptr breaks the lambda here. + result = foundOption->acceptValue(""); + } + else if (foundOption->requiresArgument == Required::Yes) { + value = option.substr(i + 1); + if (value.empty() && next.empty()) { + foundOption->error = Error::OptionRequiresArgument; + printError(c, Error::OptionRequiresArgument); + result = false; + } + else if (!value.empty()) { + result = foundOption->acceptValue(value.data()); + if (!result) { + printError(c, Error::OptionInvalidArgumentType); + } + } + else if (next[0] == '-') { + foundOption->error = Error::OptionRequiresArgument; + printError(c, Error::OptionRequiresArgument); + result = false; + } + else { + result = foundOption->acceptValue(next.data()); + m_optionIndex++; + if (!result) { + printError(c, Error::OptionInvalidArgumentType); + } + } + + break; + } + else if (foundOption->requiresArgument == Required::Optional) { + value = option.substr(i + 1); + if (!value.empty()) { + result = foundOption->acceptValue(value.data()); + if (!result) { + printError(c, Error::OptionInvalidArgumentType); + } + break; + } + } + } + + return result; +} + +// Required: directly after, separated by '=' || separated by space +// Optional: directly after, separated by '=' +bool ArgParser::parseLongOption(std::string_view option, std::string_view next) +{ + std::string name = std::string(option.substr(0, option.find_first_of('='))); + std::string_view value = option.substr(option.find_first_of('=') + 1); + + auto foundOption = std::find_if(m_options.begin(), m_options.end(), [&name](Option& it) -> bool { + return it.longName && it.longName == name; + }); + + if (foundOption == m_options.cend()) { + printError(name.data(), Error::OptionUnrecognized); + return false; + } + + enum class ArgumentProvided : uint8_t { + No, + DirectlyAfter, + Seperated, + }; + + auto argument = ArgumentProvided::No; + if (name != value || option.find('=') != std::string_view::npos) { + argument = ArgumentProvided::DirectlyAfter; + } + else if (!next.empty() && next[0] != '-') { + argument = ArgumentProvided::Seperated; + value = next; + } + + bool result = true; + + if (foundOption->requiresArgument == Required::No) { + if (argument == ArgumentProvided::DirectlyAfter) { + foundOption->error = Error::OptionDoesntAllowArgument; + printError(name.data(), Error::OptionDoesntAllowArgument); + return false; + } + + result = foundOption->acceptValue(""); + } + else if (foundOption->requiresArgument == Required::Yes) { + if (argument == ArgumentProvided::No) { + foundOption->error = Error::OptionRequiresArgument; + printError(name.data(), Error::OptionRequiresArgument); + return false; + } + + result = foundOption->acceptValue(value.data()); + if (!result) { + printError(name.data(), Error::OptionInvalidArgumentType); + } + + if (argument == ArgumentProvided::Seperated) { + m_optionIndex++; + } + } + else if (foundOption->requiresArgument == Required::Optional) { + if (argument == ArgumentProvided::DirectlyAfter) { + result = foundOption->acceptValue(value.data()); + if (!result) { + printError(name.data(), Error::OptionInvalidArgumentType); + } + } + } + + return result; +} + +bool ArgParser::parseArgument(std::string_view argument) +{ + bool result = true; + + for (;;) { + // Run out of argument handlers + if (m_argumentIndex >= m_arguments.size()) { + printError(argument.data(), Error::ArgumentExtraOperand); + return false; + } + + Argument& currentArgument = m_arguments.at(m_argumentIndex); + result = currentArgument.acceptValue(argument.data()); + + if (result) { + currentArgument.addedValues++; + if (currentArgument.addedValues >= currentArgument.maxValues) { + m_argumentIndex++; + } + } + else if (currentArgument.minValues == 0) { + m_argumentIndex++; + continue; + } + else { + printError(argument.data(), Error::ArgumentInvalidType); + } + + break; + } + + return result; +} + +bool ArgParser::parse(int argc, const char* argv[]) +{ + bool result = true; + + // Set looping indices + m_optionIndex = 1; + m_argumentIndex = 0; + + // By default parse all '-' prefixed parameters as options + m_nonOptionMode = false; + + // Get program name + m_name = argv[0] + std::string_view(argv[0]).find_last_of('/') + 1; + + std::string_view argument; + std::string_view next; + for (; m_optionIndex < (size_t)argc; ++m_optionIndex) { + +#ifndef NDEBUG + printf("argv[%zu]: %s\n", m_optionIndex, argv[m_optionIndex]); +#endif + + // Get the current and next parameter + argument = argv[m_optionIndex]; + if (m_optionIndex + 1 < (size_t)argc && argv[m_optionIndex + 1][0] != '-') { + next = argv[m_optionIndex + 1]; + } + else { + next = {}; + } + + // Stop parsing '-' prefixed parameters as options afer '--' + if (argument.compare("--") == 0) { + m_nonOptionMode = true; + continue; + } + + // Long Option + if (!m_nonOptionMode && argument[0] == '-' && argument[1] == '-') { + argument = argument.substr(argument.find_first_not_of('-')); + if (!parseLongOption(argument, next)) { + result = false; + } + } + // Short Option + else if (!m_nonOptionMode && argument[0] == '-') { + argument = argument.substr(argument.find_first_not_of('-')); + if (!parseShortOption(argument, next)) { + result = false; + } + } + // Argument + else { + if (m_stopParsingOnFirstNonOption) { + m_nonOptionMode = true; + } + if (!parseArgument(argument)) { + result = false; + } + } + + if (m_exitOnFirstError && !result) { + break; + } + } + + // Check any leftover arguments for required + for (; m_argumentIndex < m_arguments.size(); ++m_argumentIndex) { + Argument& currentArgument = m_arguments.at(m_argumentIndex); + if (currentArgument.minValues > currentArgument.addedValues) { + result = false; + printError(currentArgument.name ? currentArgument.name : "", Error::ArgumentRequired); + } + } + + if (result) { + return true; + } + + for (auto& option : m_options) { + if (option.longName && strcmp(option.longName, "help") == 0) { + printf("Try '%s --help' for more information.\n", m_name); + break; + } + if (option.shortName == 'h') { + printf("Try '%s -h' for more information.\n", m_name); + break; + } + } + + return false; +} + +// ----------------------------------------- + +void ArgParser::addOption(Option&& option) +{ + m_options.push_back(option); +} + +void ArgParser::addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString) +{ + addOption({ shortName, longName, nullptr, usageString, manString, Required::No, getAcceptFunction(value) }); +} + +void ArgParser::addOption(const char*& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument) +{ + addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) }); +} + +void ArgParser::addOption(std::string& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument) +{ + addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) }); +} + +void ArgParser::addOption(std::string_view& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument) +{ + addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) }); +} + +void ArgParser::addOption(int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument) +{ + addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) }); +} + +void ArgParser::addOption(unsigned int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument) +{ + addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) }); +} + +void ArgParser::addOption(double& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument) +{ + addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(value) }); +} + +void ArgParser::addOption(std::vector& values, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName, Required requiresArgument) +{ + addOption({ shortName, longName, argumentName, usageString, manString, requiresArgument, getAcceptFunction(values) }); +} + +// ----------------------------------------- + +void ArgParser::addArgument(Argument&& argument) +{ + m_arguments.push_back(argument); +} + +void ArgParser::addArgument(bool& value, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) }); +} + +void ArgParser::addArgument(const char*& value, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) }); +} + +void ArgParser::addArgument(std::string& value, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) }); +} + +void ArgParser::addArgument(std::string_view& value, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) }); +} + +void ArgParser::addArgument(int& value, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) }); +} + +void ArgParser::addArgument(unsigned int& value, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) }); +} + +void ArgParser::addArgument(double& value, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, 1, 0, getAcceptFunction(value) }); +} + +void ArgParser::addArgument(std::vector& values, const char* name, const char* usageString, const char* manString, Required required) +{ + size_t minValues = required == Required::Yes ? 1 : 0; + addArgument({ name, usageString, manString, minValues, values.max_size(), 0, getAcceptFunction(values) }); +} + +// ----------------------------------------- + +AcceptFunction ArgParser::getAcceptFunction(bool& value) +{ + return [&value](const char*) -> bool { + value = true; + return true; + }; +} + +AcceptFunction ArgParser::getAcceptFunction(const char*& value) +{ + return [&value](const char* input) -> bool { + value = input; + return true; + }; +} + +AcceptFunction ArgParser::getAcceptFunction(std::string& value) +{ + return [&value](const char* input) -> bool { + value = input; + return true; + }; +} + +AcceptFunction ArgParser::getAcceptFunction(std::string_view& value) +{ + return [&value](const char* input) -> bool { + value = input; + return true; + }; +} + +AcceptFunction ArgParser::getAcceptFunction(int& value) +{ + return [&value](const char* input) -> bool { + const char* validate = input; + for (; *validate != '\0'; ++validate) { + // - [0-9] + if (*validate != 45 && (*validate < 48 || *validate > 57)) { + return false; + } + } + + try { + value = std::stoi(input); + return true; + } + catch (...) { + return false; + } + }; +} + +AcceptFunction ArgParser::getAcceptFunction(unsigned int& value) +{ + return [&value](const char* input) -> bool { + const char* validate = input; + for (; *validate != '\0'; ++validate) { + // [0-9] + if (*validate < 48 || *validate > 57) { + return false; + } + } + + unsigned long convert = 0; + try { + convert = std::stoul(input); + } + catch (...) { + return false; + } + + if (convert <= std::numeric_limits::max()) { + value = static_cast(convert); + return true; + } + + return false; + }; +} + +AcceptFunction ArgParser::getAcceptFunction(double& value) +{ + return [&value](const char* input) -> bool { + const char* validate = input; + for (; *validate != '\0'; ++validate) { + // . [0-9] + if (*validate != 46 && (*validate < 48 || *validate > 57)) { + return false; + } + } + + try { + value = std::stod(input); + return true; + } + catch (...) { + return false; + } + }; +} + +AcceptFunction ArgParser::getAcceptFunction(std::vector& value) +{ + return [&value](const char* input) -> bool { + value.push_back(input); + return true; + }; +} + +} // namespace Util diff --git a/src/util/argparser.h b/src/util/argparser.h new file mode 100644 index 0000000..1d45053 --- /dev/null +++ b/src/util/argparser.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 Riyyi + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include // size_t +#include // uint8_t +#include // function +#include +#include +#include + +namespace Util { + +using AcceptFunction = std::function; + +class ArgParser final { +public: + ArgParser(); + virtual ~ArgParser(); + + enum class Required : uint8_t { + No, + Yes, + Optional, + }; + + enum class Error : uint8_t { + None, + OptionInvalid, // For short options + OptionUnrecognized, // For long options + OptionDoesntAllowArgument, + OptionRequiresArgument, + OptionInvalidArgumentType, + ArgumentExtraOperand, + ArgumentRequired, + ArgumentInvalidType, + }; + + struct Option { + char shortName { 0 }; + const char* longName { nullptr }; + const char* argumentName { nullptr }; + const char* usageString { nullptr }; + const char* manString { nullptr }; + Required requiresArgument; + AcceptFunction acceptValue; + + Error error = Error::None; + }; + + struct Argument { + const char* name { nullptr }; + const char* usageString { nullptr }; + const char* manString { nullptr }; + size_t minValues { 0 }; + size_t maxValues { 1 }; + size_t addedValues { 0 }; + AcceptFunction acceptValue; + }; + + bool parse(int argc, const char* argv[]); + + void addOption(Option&& option); + void addOption(bool& value, char shortName, const char* longName, const char* usageString, const char* manString); + void addOption(const char*& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No); + void addOption(std::string& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No); + void addOption(std::string_view& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No); + void addOption(int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No); + void addOption(unsigned int& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No); + void addOption(double& value, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No); + void addOption(std::vector& values, char shortName, const char* longName, const char* usageString, const char* manString, const char* argumentName = "", Required requiresArgument = Required::No); + + void addArgument(Argument&& argument); + void addArgument(bool& value, const char* name, const char* usageString, const char* manString, Required required = Required::No); + void addArgument(const char*& value, const char* name, const char* usageString, const char* manString, Required required = Required::No); + void addArgument(std::string& value, const char* name, const char* usageString, const char* manString, Required required = Required::No); + void addArgument(std::string_view& value, const char* name, const char* usageString, const char* manString, Required required = Required::No); + void addArgument(int& value, const char* name, const char* usageString, const char* manString, Required required = Required::No); + void addArgument(unsigned int& value, const char* name, const char* usageString, const char* manString, Required required = Required::No); + void addArgument(double& value, const char* name, const char* usageString, const char* manString, Required required = Required::No); + void addArgument(std::vector& values, const char* name, const char* usageString, const char* manString, Required required = Required::No); + + void setErrorReporting(bool state) { m_errorReporting = state; } + void setExitOnFirstError(bool state) { m_exitOnFirstError = state; } + void setStopParsingOnFirstNonOption(bool state) { m_stopParsingOnFirstNonOption = state; } + +private: + void printError(char parameter, Error error); + void printError(const char* parameter, Error error, bool longName = true); + bool parseShortOption(std::string_view option, std::string_view next); + bool parseLongOption(std::string_view option, std::string_view next); + bool parseArgument(std::string_view argument); + + AcceptFunction getAcceptFunction(bool& value); + AcceptFunction getAcceptFunction(const char*& value); + AcceptFunction getAcceptFunction(std::string& value); + AcceptFunction getAcceptFunction(std::string_view& value); + AcceptFunction getAcceptFunction(int& value); + AcceptFunction getAcceptFunction(unsigned int& value); + AcceptFunction getAcceptFunction(double& value); + AcceptFunction getAcceptFunction(std::vector& value); + + bool m_errorReporting { true }; + bool m_exitOnFirstError { true }; + bool m_stopParsingOnFirstNonOption { false }; + + size_t m_optionIndex { 1 }; + size_t m_argumentIndex { 0 }; + bool m_nonOptionMode { false }; + + const char* m_name { nullptr }; + std::vector