diff --git a/.clang-format b/.clang-format index 35cdf3cd..2cfd8b19 100644 --- a/.clang-format +++ b/.clang-format @@ -20,7 +20,7 @@ AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackArguments: true BinPackParameters: true -BraceWrapping: +BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false @@ -55,12 +55,12 @@ DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true -ForEachMacros: +ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve -IncludeCategories: +IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 - Regex: '^(<|"(gtest|gmock|isl|json)/)' @@ -110,7 +110,7 @@ SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp11 +Standard: c++17 TabWidth: 8 UseTab: Never ... diff --git a/.clang-tidy b/.clang-tidy index 56d7f643..d0b28f4f 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,24 +1,21 @@ Checks: -*, + clang-analyzer-*, + cppcoreguidelines-avoid-c-arrays, + cppcoreguidelines-special-member-functions, readability-*, - -readability-braces-around-statements, - -readability-container-size-empty, - -readability-else-after-return, - -readability-implicit-bool-conversion, - -readability-function-cognitive-complexity, - -readability-magic-numbers, - -readability-named-parameter, - -readability-qualified-auto, - -readability-static-accessed-through-instance, CheckOptions: - { key: readability-identifier-naming.ClassCase, value: CamelCase } - { key: readability-identifier-naming.ConstexprVariableCase, value: lower_case } + - { key: readability-identifier-naming.ConstexprVariableIgnoredRegexp, value: "^Is.+" } - { key: readability-identifier-naming.FunctionCase, value: lower_case } - - { key: readability-identifier-naming.LocalVariableIgnoredRegexp, value: "^[a-z][a-z_]+" } - { key: readability-identifier-naming.NamespaceCase, value: lower_case } - - { key: readability-identifier-naming.PrivateMemberPrefix, value: m } - - { key: readability-identifier-naming.StructCase, value: lower_case } - - { key: readability-identifier-naming.VariableCase, value: camelBack } + - { key: readability-identifier-naming.ParameterCase, value: lower_case } + - { key: readability-identifier-naming.PrivateMemberCase, value: lower_case } + - { key: readability-identifier-naming.PrivateMemberPrefix, value: m_ } + - { key: readability-identifier-naming.StructCase, value: CamelCase } + - { key: readability-identifier-naming.StructIgnoredRegexp, value: "parse_number" } + - { key: readability-identifier-naming.VariableCase, value: lower_case } HeaderFilterRegex: '.*' diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index 2aa06fd5..4cb17b80 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -31,6 +31,7 @@ SOFTWARE. #pragma once #include #include +#include #include #include #include @@ -55,34 +56,30 @@ namespace argparse { namespace details { // namespace for helper methods template -struct is_container : std::false_type {}; +struct HasContainerTraits : std::false_type {}; -template <> struct is_container : std::false_type {}; +template <> struct HasContainerTraits : std::false_type {}; template -struct is_container().begin()), - decltype(std::declval().end()), - decltype(std::declval().size())>> - : std::true_type {}; +struct HasContainerTraits< + T, std::void_t().begin()), + decltype(std::declval().end()), + decltype(std::declval().size())>> : std::true_type {}; template -static constexpr bool is_container_v = is_container::value; +static constexpr bool IsContainer = HasContainerTraits::value; template -struct is_streamable : std::false_type {}; +struct HasStreamableTraits : std::false_type {}; template -struct is_streamable< - T, std::void_t() << std::declval())>> - : std::true_type {}; +struct HasStreamableTraits< + T, + std::void_t() << std::declval())>> + : std::true_type {}; template -static constexpr bool is_streamable_v = is_streamable::value; - -template -static constexpr bool is_representable_v = - is_streamable_v || is_container_v; +static constexpr bool IsStreamable = HasStreamableTraits::value; constexpr std::size_t repr_max_container_size = 5; @@ -91,7 +88,7 @@ template std::string repr(T const &val) { return val ? "true" : "false"; } else if constexpr (std::is_convertible_v) { return '"' + std::string{std::string_view{val}} + '"'; - } else if constexpr (is_container_v) { + } else if constexpr (IsContainer) { std::stringstream out; out << "{"; const auto size = val.size(); @@ -99,18 +96,21 @@ template std::string repr(T const &val) { out << repr(*val.begin()); std::for_each( std::next(val.begin()), - std::next(val.begin(), std::min(size, repr_max_container_size) - 1), + std::next(val.begin(), + std::min(size, repr_max_container_size) - 1), [&out](const auto &v) { out << " " << repr(v); }); - if (size <= repr_max_container_size) + if (size <= repr_max_container_size) { out << " "; - else + } else { out << "..."; + } } - if (size > 0) + if (size > 0) { out << repr(*std::prev(val.end())); + } out << "}"; return out.str(); - } else if constexpr (is_streamable_v) { + } else if constexpr (IsStreamable) { std::stringstream out; out << val; return out.str(); @@ -138,13 +138,17 @@ constexpr bool standard_unsigned_integer = true; } // namespace +constexpr int radix_8 = 8; +constexpr int radix_10 = 10; +constexpr int radix_16 = 16; + template constexpr bool standard_integer = standard_signed_integer || standard_unsigned_integer; template constexpr decltype(auto) apply_plus_one_impl(F &&f, Tuple &&t, Extra &&x, - std::index_sequence) { + std::index_sequence unused) { return std::invoke(std::forward(f), std::get(std::forward(t))..., std::forward(x)); } @@ -174,7 +178,7 @@ enum class chars_format { general = fixed | scientific }; -struct consume_hex_prefix_result { +struct ConsumeHexPrefixResult { bool is_hexadecimal; std::string_view rest; }; @@ -182,13 +186,12 @@ struct consume_hex_prefix_result { using namespace std::literals; constexpr auto consume_hex_prefix(std::string_view s) - -> consume_hex_prefix_result { + -> ConsumeHexPrefixResult { if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) { s.remove_prefix(2); return {true, s}; - } else { - return {false, s}; } + return {false, s}; } template @@ -197,17 +200,18 @@ inline auto do_from_chars(std::string_view s) -> T { auto [first, last] = pointer_range(s); auto [ptr, ec] = std::from_chars(first, last, x, Param); if (ec == std::errc()) { - if (ptr == last) + if (ptr == last) { return x; - else - throw std::invalid_argument{"pattern does not match to the end"}; - } else if (ec == std::errc::invalid_argument) { + } + throw std::invalid_argument{"pattern does not match to the end"}; + } + if (ec == std::errc::invalid_argument) { throw std::invalid_argument{"pattern not found"}; - } else if (ec == std::errc::result_out_of_range) { + } + if (ec == std::errc::result_out_of_range) { throw std::range_error{"not representable"}; - } else { - return x; // unreachable } + return x; // unreachable } template struct parse_number { @@ -216,23 +220,25 @@ template struct parse_number { } }; -template struct parse_number { +template struct parse_number { auto operator()(std::string_view s) -> T { - if (auto [ok, rest] = consume_hex_prefix(s); ok) - return do_from_chars(rest); - else - throw std::invalid_argument{"pattern not found"}; + if (auto [ok, rest] = consume_hex_prefix(s); ok) { + return do_from_chars(rest); + } + throw std::invalid_argument{"pattern not found"}; } }; template struct parse_number { auto operator()(std::string_view s) -> T { - if (auto [ok, rest] = consume_hex_prefix(s); ok) - return do_from_chars(rest); - else if (starts_with("0"sv, s)) - return do_from_chars(rest); - else - return do_from_chars(rest); + auto [ok, rest] = consume_hex_prefix(s); + if (ok) { + return do_from_chars(rest); + } + if (starts_with("0"sv, s)) { + return do_from_chars(rest); + } + return do_from_chars(rest); } }; @@ -246,30 +252,33 @@ template <> constexpr auto generic_strtod = strtold; } // namespace template inline auto do_strtod(std::string const &s) -> T { - if (isspace(static_cast(s[0])) || s[0] == '+') + if (isspace(static_cast(s[0])) || s[0] == '+') { throw std::invalid_argument{"pattern not found"}; + } auto [first, last] = pointer_range(s); char *ptr; errno = 0; - if (auto x = generic_strtod(first, &ptr); errno == 0) { - if (ptr == last) + auto x = generic_strtod(first, &ptr); + if (errno == 0) { + if (ptr == last) { return x; - else - throw std::invalid_argument{"pattern does not match to the end"}; - } else if (errno == ERANGE) { + } + throw std::invalid_argument{"pattern does not match to the end"}; + } + if (errno == ERANGE) { throw std::range_error{"not representable"}; - } else { - return x; // unreachable } + return x; // unreachable } template struct parse_number { auto operator()(std::string const &s) -> T { - if (auto r = consume_hex_prefix(s); r.is_hexadecimal) + if (auto r = consume_hex_prefix(s); r.is_hexadecimal) { throw std::invalid_argument{ "chars_format::general does not parse hexfloat"}; + } return do_strtod(s); } @@ -277,8 +286,9 @@ template struct parse_number { template struct parse_number { auto operator()(std::string const &s) -> T { - if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) + if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) { throw std::invalid_argument{"chars_format::hex parses hexfloat"}; + } return do_strtod(s); } @@ -286,12 +296,14 @@ template struct parse_number { template struct parse_number { auto operator()(std::string const &s) -> T { - if (auto r = consume_hex_prefix(s); r.is_hexadecimal) + if (auto r = consume_hex_prefix(s); r.is_hexadecimal) { throw std::invalid_argument{ "chars_format::scientific does not parse hexfloat"}; - if (s.find_first_of("eE") == s.npos) + } + if (s.find_first_of("eE") == std::string::npos) { throw std::invalid_argument{ "chars_format::scientific requires exponent part"}; + } return do_strtod(s); } @@ -299,12 +311,14 @@ template struct parse_number { template struct parse_number { auto operator()(std::string const &s) -> T { - if (auto r = consume_hex_prefix(s); r.is_hexadecimal) + if (auto r = consume_hex_prefix(s); r.is_hexadecimal) { throw std::invalid_argument{ "chars_format::fixed does not parse hexfloat"}; - if (s.find_first_of("eE") != s.npos) + } + if (s.find_first_of("eE") != std::string::npos) { throw std::invalid_argument{ "chars_format::fixed does not parse exponent part"}; + } return do_strtod(s); } @@ -319,76 +333,81 @@ enum class default_arguments : unsigned int { all = help | version, }; -inline bool operator& (const default_arguments &a, const default_arguments &b) { - return static_cast(a) & static_cast(b); +inline default_arguments operator&(const default_arguments &a, + const default_arguments &b) { + return static_cast( + static_cast::type>(a) & + static_cast::type>(b)); } class ArgumentParser; class Argument { friend class ArgumentParser; - friend auto operator<<(std::ostream &, ArgumentParser const &) + friend auto operator<<(std::ostream &stream, const ArgumentParser &parser) -> std::ostream &; template - explicit Argument(std::string_view(&&a)[N], std::index_sequence) - : mIsOptional((is_optional(a[I]) || ...)), mIsRequired(false), - mIsRepeatable(false), mIsUsed(false) { - ((void)mNames.emplace_back(a[I]), ...); + explicit Argument(std::array &&a, + std::index_sequence unused) + : m_is_optional((is_optional(a[I]) || ...)), m_is_required(false), + m_is_repeatable(false), m_is_used(false) { + ((void)m_names.emplace_back(a[I]), ...); std::sort( - mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) { + m_names.begin(), m_names.end(), [](const auto &lhs, const auto &rhs) { return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size(); }); } public: template - explicit Argument(std::string_view(&&a)[N]) + explicit Argument(std::array &&a) : Argument(std::move(a), std::make_index_sequence{}) {} - Argument &help(std::string aHelp) { - mHelp = std::move(aHelp); + Argument &help(std::string help_text) { + m_help = std::move(help_text); return *this; } - template Argument &default_value(T &&aDefaultValue) { - mDefaultValueRepr = details::repr(aDefaultValue); - mDefaultValue = std::forward(aDefaultValue); + template Argument &default_value(T &&value) { + m_default_value_repr = details::repr(value); + m_default_value = std::forward(value); return *this; } Argument &required() { - mIsRequired = true; + m_is_required = true; return *this; } - Argument &implicit_value(std::any aImplicitValue) { - mImplicitValue = std::move(aImplicitValue); - mNumArgs = 0; + Argument &implicit_value(std::any value) { + m_implicit_value = std::move(value); + m_num_args = 0; return *this; } template - auto action(F &&aAction, Args &&... aBound) + auto action(F &&callable, Args &&...bound_args) -> std::enable_if_t, Argument &> { using action_type = std::conditional_t< std::is_void_v>, void_action, valued_action>; - if constexpr (sizeof...(Args) == 0) - mAction.emplace(std::forward(aAction)); - else - mAction.emplace( - [f = std::forward(aAction), - tup = std::make_tuple(std::forward(aBound)...)]( + if constexpr (sizeof...(Args) == 0) { + m_action.emplace(std::forward(callable)); + } else { + m_action.emplace( + [f = std::forward(callable), + tup = std::make_tuple(std::forward(bound_args)...)]( std::string const &opt) mutable { return details::apply_plus_one(f, tup, opt); }); + } return *this; } auto &append() { - mIsRepeatable = true; + m_is_repeatable = true; return *this; } @@ -400,62 +419,65 @@ class Argument { return ((c == x) || ...); }; - if constexpr (is_one_of(Shape, 'd') && details::standard_integer) - action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'i') && details::standard_integer) + if constexpr (is_one_of(Shape, 'd') && details::standard_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'i') && details::standard_integer) { action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'u') && - details::standard_unsigned_integer) - action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'o') && - details::standard_unsigned_integer) - action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'x', 'X') && - details::standard_unsigned_integer) - action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'a', 'A') && - std::is_floating_point_v) + } else if constexpr (is_one_of(Shape, 'u') && + details::standard_unsigned_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'o') && + details::standard_unsigned_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'x', 'X') && + details::standard_unsigned_integer) { + action(details::parse_number()); + } else if constexpr (is_one_of(Shape, 'a', 'A') && + std::is_floating_point_v) { action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'e', 'E') && - std::is_floating_point_v) + } else if constexpr (is_one_of(Shape, 'e', 'E') && + std::is_floating_point_v) { action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'f', 'F') && - std::is_floating_point_v) + } else if constexpr (is_one_of(Shape, 'f', 'F') && + std::is_floating_point_v) { action(details::parse_number()); - else if constexpr (is_one_of(Shape, 'g', 'G') && - std::is_floating_point_v) + } else if constexpr (is_one_of(Shape, 'g', 'G') && + std::is_floating_point_v) { action(details::parse_number()); - else + } else { static_assert(alignof(T) == 0, "No scan specification for T"); + } return *this; } - Argument &nargs(int aNumArgs) { - if (aNumArgs < 0) + Argument &nargs(int num_args) { + if (num_args < 0) { throw std::logic_error("Number of arguments must be non-negative"); - mNumArgs = aNumArgs; + } + m_num_args = num_args; return *this; } Argument &remaining() { - mNumArgs = -1; + m_num_args = -1; return *this; } template Iterator consume(Iterator start, Iterator end, - std::string_view usedName = {}) { - if (!mIsRepeatable && mIsUsed) { + std::string_view used_name = {}) { + if (!m_is_repeatable && m_is_used) { throw std::runtime_error("Duplicate argument"); } - mIsUsed = true; - mUsedName = usedName; - if (mNumArgs == 0) { - mValues.emplace_back(mImplicitValue); - std::visit([](const auto &aAction) { aAction({}); }, mAction); + m_is_used = true; + m_used_name = used_name; + if (m_num_args == 0) { + m_values.emplace_back(m_implicit_value); + std::visit([](const auto &f) { f({}); }, m_action); return start; - } else if (mNumArgs <= std::distance(start, end)) { + } + if (m_num_args <= std::distance(start, end)) { if (auto expected = maybe_nargs()) { end = std::next(start, *expected); if (std::any_of(start, end, Argument::is_optional)) { @@ -463,30 +485,31 @@ class Argument { } } - struct action_apply { + struct ActionApply { void operator()(valued_action &f) { - std::transform(first, last, std::back_inserter(self.mValues), f); + std::transform(first, last, std::back_inserter(self.m_values), f); } void operator()(void_action &f) { std::for_each(first, last, f); - if (!self.mDefaultValue.has_value()) { - if (auto expected = self.maybe_nargs()) - self.mValues.resize(*expected); + if (!self.m_default_value.has_value()) { + if (auto expected = self.maybe_nargs()) { + self.m_values.resize(*expected); + } } } Iterator first, last; Argument &self; }; - std::visit(action_apply{start, end, *this}, mAction); + std::visit(ActionApply{start, end, *this}, m_action); return end; - } else if (mDefaultValue.has_value()) { + } + if (m_default_value.has_value()) { return start; - } else { - throw std::runtime_error("Too few arguments for '" + - std::string(mUsedName) + "'."); } + throw std::runtime_error("Too few arguments for '" + + std::string(m_used_name) + "'."); } /* @@ -494,49 +517,47 @@ class Argument { */ void validate() const { if (auto expected = maybe_nargs()) { - if (mIsOptional) { - if (mIsUsed && mValues.size() != *expected && !mIsRepeatable && - !mDefaultValue.has_value()) { + if (m_is_optional) { + if (m_is_used && m_values.size() != *expected && !m_is_repeatable && + !m_default_value.has_value()) { std::stringstream stream; - stream << mUsedName << ": expected " << *expected << " argument(s). " - << mValues.size() << " provided."; + stream << m_used_name << ": expected " << *expected + << " argument(s). " << m_values.size() << " provided."; throw std::runtime_error(stream.str()); - } else { - // TODO: check if an implicit value was programmed for this argument - if (!mIsUsed && !mDefaultValue.has_value() && mIsRequired) { - std::stringstream stream; - stream << mNames[0] << ": required."; - throw std::runtime_error(stream.str()); - } - if (mIsUsed && mIsRequired && mValues.size() == 0) { - std::stringstream stream; - stream << mUsedName << ": no value provided."; - throw std::runtime_error(stream.str()); - } } - } else { - if (mValues.size() != expected && !mDefaultValue.has_value()) { + // TODO: check if an implicit value was programmed for this argument + if (!m_is_used && !m_default_value.has_value() && m_is_required) { + std::stringstream stream; + stream << m_names[0] << ": required."; + throw std::runtime_error(stream.str()); + } + if (m_is_used && m_is_required && m_values.empty()) { std::stringstream stream; - if (!mUsedName.empty()) - stream << mUsedName << ": "; - stream << *expected << " argument(s) expected. " << mValues.size() - << " provided."; + stream << m_used_name << ": no value provided."; throw std::runtime_error(stream.str()); } + } else if (m_values.size() != expected && !m_default_value.has_value()) { + std::stringstream stream; + if (!m_used_name.empty()) { + stream << m_used_name << ": "; + } + stream << *expected << " argument(s) expected. " << m_values.size() + << " provided."; + throw std::runtime_error(stream.str()); } } } auto maybe_nargs() const -> std::optional { - if (mNumArgs < 0) + if (m_num_args < 0) { return std::nullopt; - else - return static_cast(mNumArgs); + } + return static_cast(m_num_args); } std::size_t get_arguments_length() const { - return std::accumulate(std::begin(mNames), std::end(mNames), std::size_t(0), - [](const auto &sum, const auto &s) { + return std::accumulate(std::begin(m_names), std::end(m_names), + std::size_t(0), [](const auto &sum, const auto &s) { return sum + s.size() + 1; // +1 for space between names }); @@ -544,39 +565,41 @@ class Argument { friend std::ostream &operator<<(std::ostream &stream, const Argument &argument) { - std::stringstream nameStream; - std::copy(std::begin(argument.mNames), std::end(argument.mNames), - std::ostream_iterator(nameStream, " ")); - stream << nameStream.str() << "\t" << argument.mHelp; - if (argument.mDefaultValue.has_value()) { - if (!argument.mHelp.empty()) + std::stringstream name_stream; + std::copy(std::begin(argument.m_names), std::end(argument.m_names), + std::ostream_iterator(name_stream, " ")); + stream << name_stream.str() << "\t" << argument.m_help; + if (argument.m_default_value.has_value()) { + if (!argument.m_help.empty()) { stream << " "; - stream << "[default: " << argument.mDefaultValueRepr << "]"; - } else if (argument.mIsRequired) { - if (!argument.mHelp.empty()) + } + stream << "[default: " << argument.m_default_value_repr << "]"; + } else if (argument.m_is_required) { + if (!argument.m_help.empty()) { stream << " "; + } stream << "[required]"; } stream << "\n"; return stream; } - template bool operator!=(const T &aRhs) const { - return !(*this == aRhs); + template bool operator!=(const T &rhs) const { + return !(*this == rhs); } /* * Compare to an argument value of known type * @throws std::logic_error in case of incompatible types */ - template bool operator==(const T &aRhs) const { - if constexpr (!details::is_container_v) { - return get() == aRhs; + template bool operator==(const T &rhs) const { + if constexpr (!details::IsContainer) { + return get() == rhs; } else { using ValueType = typename T::value_type; - auto tLhs = get(); - return std::equal(std::begin(tLhs), std::end(tLhs), std::begin(aRhs), - std::end(aRhs), [](const auto &lhs, const auto &rhs) { + auto lhs = get(); + return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs), + std::end(rhs), [](const auto &lhs, const auto &rhs) { return std::any_cast(lhs) == rhs; }); } @@ -586,10 +609,10 @@ class Argument { static constexpr int eof = std::char_traits::eof(); static auto lookahead(std::string_view s) -> int { - if (s.empty()) + if (s.empty()) { return eof; - else - return static_cast(static_cast(s[0])); + } + return static_cast(static_cast(s[0])); } /* @@ -641,6 +664,7 @@ class Argument { // precondition: we have consumed or will consume at least one digit auto consume_digits = [=](std::string_view s) { + // NOLINTNEXTLINE(readability-qualified-auto) auto it = std::find_if_not(std::begin(s), std::end(s), is_digit); return s.substr(it - std::begin(s)); }; @@ -648,10 +672,10 @@ class Argument { switch (lookahead(s)) { case '0': { s.remove_prefix(1); - if (s.empty()) + if (s.empty()) { return true; - else - goto integer_part; + } + goto integer_part; } case '1': case '2': @@ -663,10 +687,10 @@ class Argument { case '8': case '9': { s = consume_digits(s); - if (s.empty()) + if (s.empty()) { return true; - else - goto integer_part_consumed; + } + goto integer_part_consumed; } case '.': { s.remove_prefix(1); @@ -682,10 +706,11 @@ class Argument { switch (lookahead(s)) { case '.': { s.remove_prefix(1); - if (is_digit(lookahead(s))) + if (is_digit(lookahead(s))) { goto post_decimal_point; - else + } else { goto exponent_part_opt; + } } case 'e': case 'E': { @@ -700,9 +725,8 @@ class Argument { if (is_digit(lookahead(s))) { s = consume_digits(s); goto exponent_part_opt; - } else { - return false; } + return false; exponent_part_opt: switch (lookahead(s)) { @@ -726,13 +750,12 @@ class Argument { if (is_digit(lookahead(s))) { s = consume_digits(s); return s.empty(); - } else { - return false; } + return false; } - static bool is_optional(std::string_view aName) { - return !is_positional(aName); + static bool is_optional(std::string_view name) { + return !is_positional(name); } /* @@ -742,16 +765,16 @@ class Argument { * '-' decimal-literal * !'-' anything */ - static bool is_positional(std::string_view aName) { - switch (lookahead(aName)) { + static bool is_positional(std::string_view name) { + switch (lookahead(name)) { case eof: return true; case '-': { - aName.remove_prefix(1); - if (aName.empty()) + name.remove_prefix(1); + if (name.empty()) { return true; - else - return is_decimal_literal(aName); + } + return is_decimal_literal(name); } default: return true; @@ -763,16 +786,17 @@ class Argument { * @throws std::logic_error in case of incompatible types */ template T get() const { - if (!mValues.empty()) { - if constexpr (details::is_container_v) - return any_cast_container(mValues); - else - return std::any_cast(mValues.front()); + if (!m_values.empty()) { + if constexpr (details::IsContainer) { + return any_cast_container(m_values); + } else { + return std::any_cast(m_values.front()); + } } - if (mDefaultValue.has_value()) { - return std::any_cast(mDefaultValue); + if (m_default_value.has_value()) { + return std::any_cast(m_default_value); } - throw std::logic_error("No value provided for '" + mNames.back() + "'."); + throw std::logic_error("No value provided for '" + m_names.back() + "'."); } /* @@ -781,74 +805,75 @@ class Argument { * @returns The stored value if any, std::nullopt otherwise. */ template auto present() const -> std::optional { - if (mDefaultValue.has_value()) + if (m_default_value.has_value()) { throw std::logic_error("Argument with default value always presents"); - - if (mValues.empty()) + } + if (m_values.empty()) { return std::nullopt; - else if constexpr (details::is_container_v) - return any_cast_container(mValues); - else - return std::any_cast(mValues.front()); + } + if constexpr (details::IsContainer) { + return any_cast_container(m_values); + } + return std::any_cast(m_values.front()); } template - static auto any_cast_container(const std::vector &aOperand) -> T { + static auto any_cast_container(const std::vector &operand) -> T { using ValueType = typename T::value_type; - T tResult; + T result; std::transform( - std::begin(aOperand), std::end(aOperand), std::back_inserter(tResult), + std::begin(operand), std::end(operand), std::back_inserter(result), [](const auto &value) { return std::any_cast(value); }); - return tResult; + return result; } - std::vector mNames; - std::string_view mUsedName; - std::string mHelp; - std::any mDefaultValue; - std::string mDefaultValueRepr; - std::any mImplicitValue; + std::vector m_names; + std::string_view m_used_name; + std::string m_help; + std::any m_default_value; + std::string m_default_value_repr; + std::any m_implicit_value; using valued_action = std::function; using void_action = std::function; - std::variant mAction{ + std::variant m_action{ std::in_place_type, - [](const std::string &aValue) { return aValue; }}; - std::vector mValues; - int mNumArgs = 1; - bool mIsOptional : true; - bool mIsRequired : true; - bool mIsRepeatable : true; - bool mIsUsed : true; // True if the optional argument is used by user + [](const std::string &value) { return value; }}; + std::vector m_values; + int m_num_args = 1; + bool m_is_optional : true; + bool m_is_required : true; + bool m_is_repeatable : true; + bool m_is_used : true; // True if the optional argument is used by user }; class ArgumentParser { public: - explicit ArgumentParser(std::string aProgramName = {}, - std::string aVersion = "1.0", - default_arguments aArgs = default_arguments::all) - : mProgramName(std::move(aProgramName)), mVersion(std::move(aVersion)) { - if (aArgs & default_arguments::help) { + explicit ArgumentParser(std::string program_name = {}, + std::string version = "1.0", + default_arguments add_args = default_arguments::all) + : m_program_name(std::move(program_name)), m_version(std::move(version)) { + if ((add_args & default_arguments::help) == default_arguments::help) { add_argument("-h", "--help") - .action([&](const auto &) { - std::cout << help().str(); - std::exit(0); - }) - .default_value(false) - .help("shows help message and exits") - .implicit_value(true) - .nargs(0); - } - if (aArgs & default_arguments::version) { + .action([&](const auto &unused) { + std::cout << help().str(); + std::exit(0); + }) + .default_value(false) + .help("shows help message and exits") + .implicit_value(true) + .nargs(0); + } + if ((add_args & default_arguments::version) == default_arguments::version) { add_argument("-v", "--version") - .action([&](const auto &) { - std::cout << mVersion; - std::exit(0); - }) - .default_value(false) - .help("prints version information and exits") - .implicit_value(true) - .nargs(0); + .action([&](const auto &unused) { + std::cout << m_version; + std::exit(0); + }) + .default_value(false) + .help("prints version information and exits") + .implicit_value(true) + .nargs(0); } } @@ -856,21 +881,25 @@ class ArgumentParser { ArgumentParser &operator=(ArgumentParser &&) = default; ArgumentParser(const ArgumentParser &other) - : mProgramName(other.mProgramName), - mVersion(other.mVersion), - mDescription(other.mDescription), - mEpilog(other.mEpilog), - mIsParsed(other.mIsParsed), - mPositionalArguments(other.mPositionalArguments), - mOptionalArguments(other.mOptionalArguments) { - for (auto it = std::begin(mPositionalArguments); it != std::end(mPositionalArguments); - ++it) + : m_program_name(other.m_program_name), + m_version(other.m_version), + m_description(other.m_description), + m_epilog(other.m_epilog), + m_is_parsed(other.m_is_parsed), + m_positional_arguments(other.m_positional_arguments), + m_optional_arguments(other.m_optional_arguments) { + for (auto it = std::begin(m_positional_arguments); + it != std::end(m_positional_arguments); ++it) { index_argument(it); - for (auto it = std::begin(mOptionalArguments); it != std::end(mOptionalArguments); - ++it) + } + for (auto it = std::begin(m_optional_arguments); + it != std::end(m_optional_arguments); ++it) { index_argument(it); + } } + ~ArgumentParser() = default; + ArgumentParser &operator=(const ArgumentParser &other) { auto tmp = other; std::swap(*this, tmp); @@ -879,45 +908,46 @@ class ArgumentParser { // Parameter packing // Call add_argument with variadic number of string arguments - template Argument &add_argument(Targs... Fargs) { - using array_of_sv = std::string_view[sizeof...(Targs)]; - auto tArgument = mOptionalArguments.emplace(cend(mOptionalArguments), - array_of_sv{Fargs...}); - - if (!tArgument->mIsOptional) - mPositionalArguments.splice(cend(mPositionalArguments), - mOptionalArguments, tArgument); + template Argument &add_argument(Targs... f_args) { + using array_of_sv = std::array; + auto argument = m_optional_arguments.emplace(cend(m_optional_arguments), + array_of_sv{f_args...}); + + if (!argument->m_is_optional) { + m_positional_arguments.splice(cend(m_positional_arguments), + m_optional_arguments, argument); + } - index_argument(tArgument); - return *tArgument; + index_argument(argument); + return *argument; } // Parameter packed add_parents method // Accepts a variadic number of ArgumentParser objects template - ArgumentParser &add_parents(const Targs &... Fargs) { - for (const ArgumentParser &tParentParser : {std::ref(Fargs)...}) { - for (auto &tArgument : tParentParser.mPositionalArguments) { - auto it = - mPositionalArguments.insert(cend(mPositionalArguments), tArgument); + ArgumentParser &add_parents(const Targs &...f_args) { + for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) { + for (const auto &argument : parent_parser.m_positional_arguments) { + auto it = m_positional_arguments.insert(cend(m_positional_arguments), + argument); index_argument(it); } - for (auto &tArgument : tParentParser.mOptionalArguments) { + for (const auto &argument : parent_parser.m_optional_arguments) { auto it = - mOptionalArguments.insert(cend(mOptionalArguments), tArgument); + m_optional_arguments.insert(cend(m_optional_arguments), argument); index_argument(it); } } return *this; } - ArgumentParser &add_description(std::string aDescription) { - mDescription = std::move(aDescription); + ArgumentParser &add_description(std::string description) { + m_description = std::move(description); return *this; } - ArgumentParser &add_epilog(std::string aEpilog) { - mEpilog = std::move(aEpilog); + ArgumentParser &add_epilog(std::string epilog) { + m_epilog = std::move(epilog); return *this; } @@ -926,8 +956,8 @@ class ArgumentParser { * This variant is used mainly for testing * @throws std::runtime_error in case of any invalid argument */ - void parse_args(const std::vector &aArguments) { - parse_args_internal(aArguments); + void parse_args(const std::vector &arguments) { + parse_args_internal(arguments); parse_args_validate(); } @@ -935,6 +965,7 @@ class ArgumentParser { * ArgumentParser * @throws std::runtime_error in case of any invalid argument */ + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) void parse_args(int argc, const char *const argv[]) { std::vector arguments; std::copy(argv, argv + argc, std::back_inserter(arguments)); @@ -947,12 +978,11 @@ class ArgumentParser { * @throws std::logic_error if the option has no value * @throws std::bad_any_cast if the option is not of type T */ - template - T get(std::string_view aArgumentName) const { - if (!mIsParsed) { + template T get(std::string_view arg_name) const { + if (!m_is_parsed) { throw std::logic_error("Nothing parsed, no arguments are available."); } - return (*this)[aArgumentName].get(); + return (*this)[arg_name].get(); } /* Getter for options without default values. @@ -961,78 +991,82 @@ class ArgumentParser { * @throws std::bad_any_cast if the option is not of type T */ template - auto present(std::string_view aArgumentName) const -> std::optional { - return (*this)[aArgumentName].present(); + auto present(std::string_view arg_name) const -> std::optional { + return (*this)[arg_name].present(); } /* Getter that returns true for user-supplied options. Returns false if not * user-supplied, even with a default value. */ - auto is_used(std::string_view aArgumentName) const { - return (*this)[aArgumentName].mIsUsed; + auto is_used(std::string_view arg_name) const { + return (*this)[arg_name].m_is_used; } /* Indexing operator. Return a reference to an Argument object * Used in conjuction with Argument.operator== e.g., parser["foo"] == true * @throws std::logic_error in case of an invalid argument name */ - Argument &operator[](std::string_view aArgumentName) const { - auto tIterator = mArgumentMap.find(aArgumentName); - if (tIterator != mArgumentMap.end()) { - return *(tIterator->second); - } - if (aArgumentName.front() != '-') { - std::string nameStr(aArgumentName); - // "-" + aArgumentName - nameStr = "-" + nameStr; - tIterator = mArgumentMap.find(nameStr); - if (tIterator != mArgumentMap.end()) { - return *(tIterator->second); + Argument &operator[](std::string_view arg_name) const { + auto it = m_argument_map.find(arg_name); + if (it != m_argument_map.end()) { + return *(it->second); + } + if (arg_name.front() != '-') { + std::string name(arg_name); + // "-" + arg_name + name = "-" + name; + it = m_argument_map.find(name); + if (it != m_argument_map.end()) { + return *(it->second); } - // "--" + aArgumentName - nameStr = "-" + nameStr; - tIterator = mArgumentMap.find(nameStr); - if (tIterator != mArgumentMap.end()) { - return *(tIterator->second); + // "--" + arg_name + name = "-" + name; + it = m_argument_map.find(name); + if (it != m_argument_map.end()) { + return *(it->second); } } - throw std::logic_error("No such argument: " + std::string(aArgumentName)); + throw std::logic_error("No such argument: " + std::string(arg_name)); } // Print help message friend auto operator<<(std::ostream &stream, const ArgumentParser &parser) -> std::ostream & { stream.setf(std::ios_base::left); - stream << "Usage: " << parser.mProgramName << " [options] "; - std::size_t tLongestArgumentLength = parser.get_length_of_longest_argument(); + stream << "Usage: " << parser.m_program_name << " [options] "; + std::size_t longest_arg_length = parser.get_length_of_longest_argument(); - for (const auto &argument : parser.mPositionalArguments) { - stream << argument.mNames.front() << " "; + for (const auto &argument : parser.m_positional_arguments) { + stream << argument.m_names.front() << " "; } stream << "\n\n"; - if (!parser.mDescription.empty()) - stream << parser.mDescription << "\n\n"; + if (!parser.m_description.empty()) { + stream << parser.m_description << "\n\n"; + } - if (!parser.mPositionalArguments.empty()) + if (!parser.m_positional_arguments.empty()) { stream << "Positional arguments:\n"; + } - for (const auto &mPositionalArgument : parser.mPositionalArguments) { - stream.width(tLongestArgumentLength); - stream << mPositionalArgument; + for (const auto &argument : parser.m_positional_arguments) { + stream.width(longest_arg_length); + stream << argument; } - if (!parser.mOptionalArguments.empty()) - stream << (parser.mPositionalArguments.empty() ? "" : "\n") + if (!parser.m_optional_arguments.empty()) { + stream << (parser.m_positional_arguments.empty() ? "" : "\n") << "Optional arguments:\n"; + } - for (const auto &mOptionalArgument : parser.mOptionalArguments) { - stream.width(tLongestArgumentLength); - stream << mOptionalArgument; + for (const auto &argument : parser.m_optional_arguments) { + stream.width(longest_arg_length); + stream << argument; } - if (!parser.mEpilog.empty()) - stream << parser.mEpilog << "\n\n"; + if (!parser.m_epilog.empty()) { + stream << parser.m_epilog << "\n\n"; + } return stream; } @@ -1057,47 +1091,47 @@ class ArgumentParser { /* * @throws std::runtime_error in case of any invalid argument */ - void parse_args_internal(const std::vector &aArguments) { - if (mProgramName.empty() && !aArguments.empty()) { - mProgramName = aArguments.front(); - } - auto end = std::end(aArguments); - auto positionalArgumentIt = std::begin(mPositionalArguments); - for (auto it = std::next(std::begin(aArguments)); it != end;) { - const auto &tCurrentArgument = *it; - if (Argument::is_positional(tCurrentArgument)) { - if (positionalArgumentIt == std::end(mPositionalArguments)) { + void parse_args_internal(const std::vector &arguments) { + if (m_program_name.empty() && !arguments.empty()) { + m_program_name = arguments.front(); + } + auto end = std::end(arguments); + auto positional_argument_it = std::begin(m_positional_arguments); + for (auto it = std::next(std::begin(arguments)); it != end;) { + const auto ¤t_argument = *it; + if (Argument::is_positional(current_argument)) { + if (positional_argument_it == std::end(m_positional_arguments)) { throw std::runtime_error( "Maximum number of positional arguments exceeded"); } - auto tArgument = positionalArgumentIt++; - it = tArgument->consume(it, end); + auto argument = positional_argument_it++; + it = argument->consume(it, end); continue; } - auto tIterator = mArgumentMap.find(tCurrentArgument); - if (tIterator != mArgumentMap.end()) { - auto tArgument = tIterator->second; - it = tArgument->consume(std::next(it), end, tIterator->first); - } else if (const auto &tCompoundArgument = tCurrentArgument; - tCompoundArgument.size() > 1 && tCompoundArgument[0] == '-' && - tCompoundArgument[1] != '-') { + auto arg_map_it = m_argument_map.find(current_argument); + if (arg_map_it != m_argument_map.end()) { + auto argument = arg_map_it->second; + it = argument->consume(std::next(it), end, arg_map_it->first); + } else if (const auto &compound_arg = current_argument; + compound_arg.size() > 1 && compound_arg[0] == '-' && + compound_arg[1] != '-') { ++it; - for (std::size_t j = 1; j < tCompoundArgument.size(); j++) { - auto tHypotheticalArgument = std::string{'-', tCompoundArgument[j]}; - auto tIterator2 = mArgumentMap.find(tHypotheticalArgument); - if (tIterator2 != mArgumentMap.end()) { - auto tArgument = tIterator2->second; - it = tArgument->consume(it, end, tIterator2->first); + for (std::size_t j = 1; j < compound_arg.size(); j++) { + auto hypothetical_arg = std::string{'-', compound_arg[j]}; + auto arg_map_it2 = m_argument_map.find(hypothetical_arg); + if (arg_map_it2 != m_argument_map.end()) { + auto argument = arg_map_it2->second; + it = argument->consume(it, end, arg_map_it2->first); } else { - throw std::runtime_error("Unknown argument: " + tCurrentArgument); + throw std::runtime_error("Unknown argument: " + current_argument); } } } else { - throw std::runtime_error("Unknown argument: " + tCurrentArgument); + throw std::runtime_error("Unknown argument: " + current_argument); } } - mIsParsed = true; + m_is_parsed = true; } /* @@ -1105,42 +1139,44 @@ class ArgumentParser { */ void parse_args_validate() { // Check if all arguments are parsed - std::for_each(std::begin(mArgumentMap), std::end(mArgumentMap), - [](const auto &argPair) { - const auto &tArgument = argPair.second; - tArgument->validate(); + std::for_each(std::begin(m_argument_map), std::end(m_argument_map), + [](const auto &pair) { + const auto &argument = pair.second; + argument->validate(); }); } // Used by print_help. std::size_t get_length_of_longest_argument() const { - if (mArgumentMap.empty()) + if (m_argument_map.empty()) { return 0; - std::vector argumentLengths(mArgumentMap.size()); - std::transform(std::begin(mArgumentMap), std::end(mArgumentMap), - std::begin(argumentLengths), [](const auto &argPair) { - const auto &tArgument = argPair.second; - return tArgument->get_arguments_length(); + } + std::vector argument_lengths(m_argument_map.size()); + std::transform(std::begin(m_argument_map), std::end(m_argument_map), + std::begin(argument_lengths), [](const auto &pair) { + const auto &argument = pair.second; + return argument->get_arguments_length(); }); - return *std::max_element(std::begin(argumentLengths), - std::end(argumentLengths)); + return *std::max_element(std::begin(argument_lengths), + std::end(argument_lengths)); } using list_iterator = std::list::iterator; - void index_argument(list_iterator argIt) { - for (auto &mName : std::as_const(argIt->mNames)) - mArgumentMap.insert_or_assign(mName, argIt); + void index_argument(list_iterator it) { + for (const auto &name : std::as_const(it->m_names)) { + m_argument_map.insert_or_assign(name, it); + } } - std::string mProgramName; - std::string mVersion; - std::string mDescription; - std::string mEpilog; - bool mIsParsed = false; - std::list mPositionalArguments; - std::list mOptionalArguments; - std::map> mArgumentMap; + std::string m_program_name; + std::string m_version; + std::string m_description; + std::string m_epilog; + bool m_is_parsed = false; + std::list m_positional_arguments; + std::list m_optional_arguments; + std::map> m_argument_map; }; } // namespace argparse