chore: clean up c++20/23 features to move to 17.

This commit is contained in:
2026-03-15 23:38:08 +04:00
parent 609a730e8f
commit 6ac10dda33

View File

@@ -1,10 +1,9 @@
#pragma once #pragma once
#include <argument_parser.hpp> #include <argument_parser.hpp>
#include <array>
#include <cstdlib> #include <cstdlib>
#include <exception>
#include <fake_parser.hpp> #include <fake_parser.hpp>
#include <initializer_list> #include <initializer_list>
#include <iostream>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
@@ -19,13 +18,7 @@ namespace argument_parser::v2 {
static inline fake_parser fake_parser{}; static inline fake_parser fake_parser{};
} }
enum class add_argument_flags { enum class add_argument_flags { ShortArgument, LongArgument, HelpText, Action, Required };
ShortArgument,
LongArgument,
HelpText,
Action,
Required
};
namespace flags { namespace flags {
constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument; constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument;
@@ -33,23 +26,20 @@ namespace argument_parser::v2 {
constexpr static inline add_argument_flags HelpText = add_argument_flags::HelpText; constexpr static inline add_argument_flags HelpText = add_argument_flags::HelpText;
constexpr static inline add_argument_flags Action = add_argument_flags::Action; constexpr static inline add_argument_flags Action = add_argument_flags::Action;
constexpr static inline add_argument_flags Required = add_argument_flags::Required; constexpr static inline add_argument_flags Required = add_argument_flags::Required;
} } // namespace flags
class base_parser : private argument_parser::base_parser { class base_parser : private argument_parser::base_parser {
public: public:
template<typename T> template <typename T> using typed_flag_value = std::variant<std::string, parametered_action<T>, bool>;
using typed_flag_value = std::variant<std::string, parametered_action<T>, bool>;
using non_typed_flag_value = std::variant<std::string, non_parametered_action, bool>; using non_typed_flag_value = std::variant<std::string, non_parametered_action, bool>;
template<typename T> template <typename T> using typed_argument_pair = std::pair<add_argument_flags, typed_flag_value<T>>;
using typed_argument_pair = std::pair<add_argument_flags, typed_flag_value<T>>;
using non_typed_argument_pair = std::pair<add_argument_flags, non_typed_flag_value>; using non_typed_argument_pair = std::pair<add_argument_flags, non_typed_flag_value>;
template <typename T> template <typename T>
void add_argument(std::unordered_map<add_argument_flags, typed_flag_value<T>> const &argument_pairs) { void add_argument(std::unordered_map<add_argument_flags, typed_flag_value<T>> const &argument_pairs) {
std::unordered_map<extended_add_argument_flags, bool> found_params {{ std::unordered_map<extended_add_argument_flags, bool> found_params{
extended_add_argument_flags::IsTyped, true {extended_add_argument_flags::IsTyped, true}};
}};
std::string short_arg, long_arg, help_text; std::string short_arg, long_arg, help_text;
std::unique_ptr<action_base> action; std::unique_ptr<action_base> action;
@@ -62,31 +52,37 @@ namespace argument_parser::v2 {
if (argument_pairs.contains(add_argument_flags::LongArgument)) { if (argument_pairs.contains(add_argument_flags::LongArgument)) {
found_params[extended_add_argument_flags::LongArgument] = true; found_params[extended_add_argument_flags::LongArgument] = true;
long_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::LongArgument), "long"); long_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::LongArgument), "long");
if (short_arg.empty()) short_arg = long_arg; if (short_arg.empty())
short_arg = long_arg;
} else { } else {
if (!short_arg.empty()) long_arg = short_arg; if (!short_arg.empty())
long_arg = short_arg;
} }
if (argument_pairs.contains(add_argument_flags::Action)) { if (argument_pairs.contains(add_argument_flags::Action)) {
found_params[extended_add_argument_flags::Action] = true; found_params[extended_add_argument_flags::Action] = true;
action = get_or_throw<parametered_action<T>>(argument_pairs.at(add_argument_flags::Action), "action").clone(); action = get_or_throw<parametered_action<T>>(argument_pairs.at(add_argument_flags::Action), "action")
.clone();
} }
if (argument_pairs.contains(add_argument_flags::HelpText)) { if (argument_pairs.contains(add_argument_flags::HelpText)) {
help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help"); help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
} else { } else {
help_text = short_arg + ", " + long_arg; help_text = short_arg + ", " + long_arg;
} }
if (argument_pairs.contains(add_argument_flags::Required) && get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) { if (argument_pairs.contains(add_argument_flags::Required) &&
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) {
required = true; required = true;
} }
auto suggested_add = suggest_candidate(found_params); auto suggested_add = suggest_candidate(found_params);
if (suggested_add == candidate_type::unknown) { if (suggested_add == candidate_type::unknown) {
throw std::runtime_error("Could not match any add argument overload to given parameters. Are you missing some required parameter?"); throw std::runtime_error("Could not match any add argument overload to given parameters. Are you "
"missing some required parameter?");
} }
switch (suggested_add) { switch (suggested_add) {
case candidate_type::typed_action: case candidate_type::typed_action:
base::add_argument(short_arg, long_arg, help_text, *static_cast<parametered_action<T>*>(&(*action)), required); base::add_argument(short_arg, long_arg, help_text, *static_cast<parametered_action<T> *>(&(*action)),
required);
break; break;
case candidate_type::store_other: case candidate_type::store_other:
base::add_argument(short_arg, long_arg, help_text, required); base::add_argument(short_arg, long_arg, help_text, required);
@@ -96,8 +92,7 @@ namespace argument_parser::v2 {
} }
} }
template<typename T> template <typename T> void add_argument(std::initializer_list<typed_argument_pair<T>> const &pairs) {
void add_argument(std::initializer_list<typed_argument_pair<T>> const& pairs) {
std::unordered_map<add_argument_flags, typed_flag_value<T>> args; std::unordered_map<add_argument_flags, typed_flag_value<T>> args;
for (auto &[k, v] : pairs) { for (auto &[k, v] : pairs) {
@@ -118,54 +113,60 @@ namespace argument_parser::v2 {
} }
void add_argument(std::unordered_map<add_argument_flags, non_typed_flag_value> const &argument_pairs) { void add_argument(std::unordered_map<add_argument_flags, non_typed_flag_value> const &argument_pairs) {
std::unordered_map<extended_add_argument_flags, bool> found_params {{ std::unordered_map<extended_add_argument_flags, bool> found_params{
extended_add_argument_flags::IsTyped, false {extended_add_argument_flags::IsTyped, false}};
}};
std::string short_arg, long_arg, help_text; std::string short_arg, long_arg, help_text;
std::unique_ptr<action_base> action; std::unique_ptr<action_base> action;
bool required = false; bool required = false;
if (argument_pairs.contains(add_argument_flags::ShortArgument)) { if (argument_pairs.find(add_argument_flags::ShortArgument) != argument_pairs.end()) {
found_params[extended_add_argument_flags::ShortArgument] = true; found_params[extended_add_argument_flags::ShortArgument] = true;
short_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::ShortArgument), "short"); short_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::ShortArgument), "short");
} }
if (argument_pairs.contains(add_argument_flags::LongArgument)) { if (argument_pairs.find(add_argument_flags::LongArgument) != argument_pairs.end()) {
found_params[extended_add_argument_flags::LongArgument] = true; found_params[extended_add_argument_flags::LongArgument] = true;
long_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::LongArgument), "long"); long_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::LongArgument), "long");
if (short_arg.empty()) short_arg = long_arg; if (short_arg.empty())
short_arg = long_arg;
} else { } else {
if (!short_arg.empty()) long_arg = short_arg; if (!short_arg.empty())
long_arg = short_arg;
} }
if (argument_pairs.contains(add_argument_flags::Action)) { if (argument_pairs.find(add_argument_flags::Action) != argument_pairs.end()) {
found_params[extended_add_argument_flags::Action] = true; found_params[extended_add_argument_flags::Action] = true;
action = get_or_throw<non_parametered_action>(argument_pairs.at(add_argument_flags::Action), "action").clone(); action = get_or_throw<non_parametered_action>(argument_pairs.at(add_argument_flags::Action), "action")
.clone();
} }
if (argument_pairs.contains(add_argument_flags::HelpText)) { if (argument_pairs.find(add_argument_flags::HelpText) != argument_pairs.end()) {
help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help"); help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
} else { } else {
help_text = short_arg + ", " + long_arg; help_text = short_arg + ", " + long_arg;
} }
if (argument_pairs.contains(add_argument_flags::Required) && get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) { if (argument_pairs.find(add_argument_flags::Required) != argument_pairs.end() &&
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) {
required = true; required = true;
} }
auto suggested_add = suggest_candidate(found_params); auto suggested_add = suggest_candidate(found_params);
if (suggested_add == candidate_type::unknown) { if (suggested_add == candidate_type::unknown) {
throw std::runtime_error("Could not match any add argument overload to given parameters. Are you missing some required parameter?"); throw std::runtime_error("Could not match any add argument overload to given parameters. Are you "
"missing some required parameter?");
} }
switch (suggested_add) { switch (suggested_add) {
case candidate_type::non_typed_action: case candidate_type::non_typed_action:
base::add_argument(short_arg, long_arg, help_text, *static_cast<non_parametered_action*>(&(*action)), required); base::add_argument(short_arg, long_arg, help_text, *static_cast<non_parametered_action *>(&(*action)),
required);
break; break;
case candidate_type::store_boolean: case candidate_type::store_boolean:
base::add_argument(short_arg, long_arg, help_text, required); base::add_argument(short_arg, long_arg, help_text, required);
break; break;
default: default:
throw std::runtime_error("Could not match the arguments against any overload. The suggested candidate was: " + std::to_string((int(suggested_add)))); throw std::runtime_error(
"Could not match the arguments against any overload. The suggested candidate was: " +
std::to_string((int(suggested_add))));
} }
} }
@@ -177,8 +178,7 @@ namespace argument_parser::v2 {
base::handle_arguments(convention_types); base::handle_arguments(convention_types);
} }
template<typename T> template <typename T> std::optional<T> get_optional(std::string const &arg) {
std::optional<T> get_optional(std::string const& arg) {
return base::get_optional<T>(arg); return base::get_optional<T>(arg);
} }
@@ -197,53 +197,47 @@ namespace argument_parser::v2 {
private: private:
using base = argument_parser::base_parser; using base = argument_parser::base_parser;
enum class extended_add_argument_flags { enum class extended_add_argument_flags { ShortArgument, LongArgument, Action, IsTyped };
ShortArgument,
LongArgument,
Action,
IsTyped
};
enum class candidate_type { enum class candidate_type { store_boolean, store_other, typed_action, non_typed_action, unknown };
store_boolean,
store_other,
typed_action,
non_typed_action,
unknown
};
template <typename T, size_t S> template <typename T, size_t S>
bool satisfies_at_least_one(std::array<T, S> const &arr, std::unordered_map<T, bool> const &map) { bool satisfies_at_least_one(std::array<T, S> const &arr, std::unordered_map<T, bool> const &map) {
for (const auto &req : arr) { for (const auto &req : arr) {
if (map.contains(req)) return true; if (map.find(req) != map.end())
return true;
} }
return false; return false;
} }
candidate_type suggest_candidate(std::unordered_map<extended_add_argument_flags, bool> const &available_vars) { candidate_type suggest_candidate(std::unordered_map<extended_add_argument_flags, bool> const &available_vars) {
auto constexpr required_at_least_one = std::array<extended_add_argument_flags, 2> { extended_add_argument_flags::ShortArgument, extended_add_argument_flags::LongArgument }; auto constexpr required_at_least_one = std::array<extended_add_argument_flags, 2>{
if (!satisfies_at_least_one(required_at_least_one, available_vars)) return candidate_type::unknown; extended_add_argument_flags::ShortArgument, extended_add_argument_flags::LongArgument};
if (!satisfies_at_least_one(required_at_least_one, available_vars))
return candidate_type::unknown;
if (available_vars.contains(extended_add_argument_flags::Action)) { if (available_vars.find(extended_add_argument_flags::Action) != available_vars.end()) {
if (available_vars.at(extended_add_argument_flags::IsTyped)) if (available_vars.at(extended_add_argument_flags::IsTyped))
return candidate_type::typed_action; return candidate_type::typed_action;
else return candidate_type::non_typed_action; else
return candidate_type::non_typed_action;
} }
if (available_vars.at(extended_add_argument_flags::IsTyped)) return candidate_type::store_other; if (available_vars.at(extended_add_argument_flags::IsTyped))
return candidate_type::store_other;
return candidate_type::store_boolean; return candidate_type::store_boolean;
} }
template<typename T, typename I> template <typename T, typename I> T get_or_throw(typed_flag_value<I> const &v, std::string_view key) {
T get_or_throw(typed_flag_value<I> const& v, std::string_view key) { if (auto p = std::get_if<T>(&v))
if (auto p = std::get_if<T>(&v)) return *p; return *p;
throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key)); throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key));
} }
template<typename T> template <typename T> T get_or_throw(non_typed_flag_value const &v, std::string_view key) {
T get_or_throw(non_typed_flag_value const& v, std::string_view key) { if (auto p = std::get_if<T>(&v))
if (auto p = std::get_if<T>(&v)) return *p; return *p;
throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key)); throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key));
} }
}; };
} } // namespace argument_parser::v2