refactor(parser): replace variant-based actions with polymorphic interface
Simplify action handling by introducing a polymorphic base class for actions Remove std::variant usage and improve type safety with proper cloning Clean up unused includes and test code in main.cpp
This commit is contained in:
@@ -12,64 +12,106 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
namespace argument_parser {
|
namespace argument_parser {
|
||||||
template <typename T>
|
class action_base {
|
||||||
class parametered_action {
|
|
||||||
public:
|
public:
|
||||||
// Type alias to expose the parameter type, crucial for the visitor.
|
virtual ~action_base() = default;
|
||||||
|
virtual bool expects_parameter() const = 0;
|
||||||
|
virtual void invoke() const = 0;
|
||||||
|
virtual void invoke_with_parameter(const std::string& param) const = 0;
|
||||||
|
virtual std::unique_ptr<action_base> clone() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class parametered_action : public action_base {
|
||||||
|
public:
|
||||||
|
explicit parametered_action(std::function<void(const T&)> const& handler) : handler(handler) {}
|
||||||
|
|
||||||
using parameter_type = T;
|
using parameter_type = T;
|
||||||
|
|
||||||
parametered_action(std::function<void(const T&)> const& function) : handler(function) {}
|
|
||||||
|
|
||||||
void invoke(const T& arg) const {
|
void invoke(const T& arg) const {
|
||||||
handler(arg);
|
handler(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool expects_parameter() const override { return true; }
|
||||||
|
|
||||||
|
void invoke() const override {
|
||||||
|
throw std::runtime_error("Parametered action requires a parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
void invoke_with_parameter(const std::string& param) const override {
|
||||||
|
T parsed_value = parsing_traits::parser_trait<T>::parse(param);
|
||||||
|
invoke(parsed_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<action_base> clone() const override {
|
||||||
|
return std::make_unique<parametered_action<T>>(handler);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void(const T&)> handler;
|
std::function<void(const T&)> handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
class non_parametered_action {
|
class non_parametered_action : public action_base {
|
||||||
public:
|
public:
|
||||||
non_parametered_action(std::function<void()> const& function) : handler(function) {}
|
explicit non_parametered_action(std::function<void()> const& handler) : handler(handler) {}
|
||||||
|
|
||||||
void invoke() const {
|
void invoke() const override {
|
||||||
handler();
|
handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool expects_parameter() const override { return false; }
|
||||||
|
|
||||||
|
void invoke_with_parameter(const std::string& param) const override {
|
||||||
|
invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<action_base> clone() const override {
|
||||||
|
return std::make_unique<non_parametered_action>(handler);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void()> handler;
|
std::function<void()> handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
using action_variant = std::variant<
|
|
||||||
non_parametered_action,
|
|
||||||
parametered_action<std::string>,
|
|
||||||
parametered_action<int>,
|
|
||||||
parametered_action<float>,
|
|
||||||
parametered_action<bool>
|
|
||||||
>;
|
|
||||||
|
|
||||||
class base_parser;
|
class base_parser;
|
||||||
|
|
||||||
class argument {
|
class argument {
|
||||||
public:
|
public:
|
||||||
argument() : id(0), name(), required(false), invoked(false), action(non_parametered_action([](){})) {}
|
argument() : id(0), name(), required(false), invoked(false), action(std::make_unique<non_parametered_action>([](){})) {}
|
||||||
|
|
||||||
template <typename ActionType>
|
template <typename ActionType>
|
||||||
argument(int id, std::string const& name, ActionType const& action)
|
argument(int id, std::string const& name, ActionType const& action)
|
||||||
: id(id), name(name), action(action), required(false), invoked(false) {}
|
: id(id), name(name), action(action.clone()), required(false), invoked(false) {}
|
||||||
|
|
||||||
|
argument(const argument& other)
|
||||||
|
: id(other.id), name(other.name), action(other.action->clone()),
|
||||||
|
required(other.required), invoked(other.invoked), help_text(other.help_text) {}
|
||||||
|
|
||||||
|
argument& operator=(const argument& other) {
|
||||||
|
if (this != &other) {
|
||||||
|
id = other.id;
|
||||||
|
name = other.name;
|
||||||
|
action = other.action->clone();
|
||||||
|
required = other.required;
|
||||||
|
invoked = other.invoked;
|
||||||
|
help_text = other.help_text;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
argument(argument&& other) noexcept = default;
|
||||||
|
argument& operator=(argument&& other) noexcept = default;
|
||||||
|
|
||||||
bool is_required() const { return required; }
|
bool is_required() const { return required; }
|
||||||
std::string get_name() const { return name; }
|
std::string get_name() const { return name; }
|
||||||
bool is_invoked() const { return invoked; }
|
bool is_invoked() const { return invoked; }
|
||||||
|
|
||||||
bool expects_parameter() const {
|
bool expects_parameter() const {
|
||||||
return !std::holds_alternative<non_parametered_action>(action);
|
return action->expects_parameter();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -81,7 +123,7 @@ namespace argument_parser {
|
|||||||
|
|
||||||
int id;
|
int id;
|
||||||
std::string name;
|
std::string name;
|
||||||
action_variant action;
|
std::unique_ptr<action_base> action;
|
||||||
bool required;
|
bool required;
|
||||||
bool invoked;
|
bool invoked;
|
||||||
std::string help_text;
|
std::string help_text;
|
||||||
@@ -138,24 +180,15 @@ namespace argument_parser {
|
|||||||
try {
|
try {
|
||||||
argument& corresponding_argument = get_argument(extracted);
|
argument& corresponding_argument = get_argument(extracted);
|
||||||
|
|
||||||
std::visit([&](auto&& action) {
|
if (corresponding_argument.expects_parameter()) {
|
||||||
using ActionType = std::decay_t<decltype(action)>;
|
if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) {
|
||||||
|
throw std::runtime_error("expected value for argument " + extracted.second);
|
||||||
if constexpr (std::is_same_v<ActionType, non_parametered_action>) {
|
|
||||||
action.invoke();
|
|
||||||
} else {
|
|
||||||
if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) {
|
|
||||||
throw std::runtime_error("expected value for argument " + extracted.second);
|
|
||||||
}
|
|
||||||
auto value_raw = convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
|
|
||||||
|
|
||||||
using ParamType = typename ActionType::parameter_type;
|
|
||||||
|
|
||||||
auto value = parsing_traits::parser_trait<ParamType>::parse(value_raw);
|
|
||||||
|
|
||||||
action.invoke(value);
|
|
||||||
}
|
}
|
||||||
}, corresponding_argument.action);
|
auto value_raw = convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
|
||||||
|
corresponding_argument.action->invoke_with_parameter(value_raw);
|
||||||
|
} else {
|
||||||
|
corresponding_argument.action->invoke();
|
||||||
|
}
|
||||||
|
|
||||||
corresponding_argument.set_invoked(true);
|
corresponding_argument.set_invoked(true);
|
||||||
arg_correctly_handled = true;
|
arg_correctly_handled = true;
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
#include "argument_parser.hpp"
|
|
||||||
#include "fake_parser.hpp"
|
|
||||||
#include <argparse>
|
#include <argparse>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace argument_parser::conventions;
|
using namespace argument_parser::conventions;
|
||||||
|
|
||||||
@@ -108,8 +105,9 @@ auto make_grep_action(argument_parser::base_parser& parser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
std::vector<std::string> fake_args = { "-g", "add", "-f", "src/main.cpp", "-ep", "1,2" };
|
// std::vector<std::string> fake_args = { "-g", "add", "-f", "src/main.cpp", "-ep", "1,2" };
|
||||||
auto parser = argument_parser::fake_parser{"test", std::move(fake_args)};
|
// auto parser = argument_parser::fake_parser{"test", std::move(fake_args)};
|
||||||
|
auto parser = argument_parser::parser{};
|
||||||
auto [file, grep] = make_grep_action(parser);
|
auto [file, grep] = make_grep_action(parser);
|
||||||
parser.add_argument("e", "echo", "echoes given variable", echo, false);
|
parser.add_argument("e", "echo", "echoes given variable", echo, false);
|
||||||
parser.add_argument("ep", "echo-point", "echoes given point", echo_point, false);
|
parser.add_argument("ep", "echo-point", "echoes given point", echo_point, false);
|
||||||
|
|||||||
Reference in New Issue
Block a user