From 64b682c4ea49e014e87221a42d02730e38601212 Mon Sep 17 00:00:00 2001 From: etenie Date: Mon, 25 Nov 2024 02:51:30 +0100 Subject: [PATCH] argparser: refactor the entire argparser --- src/ArgParser.hpp | 145 ++++++++++++++++++++++++++-------------------- src/main.cpp | 6 +- 2 files changed, 85 insertions(+), 66 deletions(-) diff --git a/src/ArgParser.hpp b/src/ArgParser.hpp index 3f96b6b..f3dd58a 100644 --- a/src/ArgParser.hpp +++ b/src/ArgParser.hpp @@ -1,63 +1,117 @@ #pragma once +#include #include #include #include -#include +#include #include #include class ArgParser { public: - void add(const std::string& shortOpt, const std::string& longOpt, const std::string& description, bool requiresValue = true) { - Argument arg{shortOpt, longOpt, description, std::nullopt, requiresValue, false}; - m_arguments.push_back(arg); - if (!shortOpt.empty()) - m_optMap[shortOpt] = &m_arguments.back(); - if (!longOpt.empty()) - m_optMap[longOpt] = &m_arguments.back(); + void add(std::string_view shortOpt, std::string_view longOpt, const std::string_view desc, const bool hasValue = true, const bool isRequired = false, + std::string_view defaultValue = "") { + const Argument arg{shortOpt, longOpt, desc, defaultValue, hasValue, isRequired}; + m_arguments.emplace_back(arg); + + m_opts.emplace(shortOpt, std::ref(m_arguments.back())); + m_opts.emplace(longOpt, std::ref(m_arguments.back())); } - bool parse(int argc, char* argv[]) { + [[nodiscard]] bool parse(const int argc, const char* argv[]) const { if (argc == 1) { printHelp(); return false; } for (int i = 1; i < argc; ++i) { - std::string arg = argv[i]; + std::string_view arg = argv[i]; if (arg == "-h" || arg == "--help") { printHelp(); return false; } - if (auto* argument = findArgument(arg); argument) { - argument->exists = true; - if (argument->requiresValue) { - if (i + 1 >= argc) - throw std::invalid_argument("Missing value for " + arg); - argument->value = argv[++i]; + if (auto argument = findArgument(arg); argument.has_value()) { + auto& argRef = argument->get(); + argRef.isPresent = true; + + if (argRef.requiresValue) { + if (i + 1 >= argc) { + throw std::invalid_argument(std::format("Missing value for: {}", arg)); + } + argRef.value = argv[++i]; } } else { - throw std::invalid_argument("Unknown option: " + arg); + throw std::invalid_argument(std::format("Unknown option: {}", arg)); + } + } + for (const auto& arg : m_opts) { + const auto& argRef = arg.second.get(); + if (argRef.isRequired && !argRef.isPresent) { + throw std::invalid_argument(std::format("Required argument: [{}, {}] not provided", argRef.shortOpt, argRef.longOpt)); } } return true; } template - T get(const std::string& arg) const { - const Argument* argument = findArgument(arg); - if (!argument || !argument->value) - throw std::runtime_error(arg + " has no value"); - - return convertToType(*argument->value); + T get(const std::string_view arg) const { + if (const auto argument = findArgument(arg); argument.has_value()) { + return convertToType(argument->get().value.value()); + } else { + throw std::runtime_error(std::format("{} has no value", arg)); + } } - bool has(const std::string& arg) const { - const Argument* argument = findArgument(arg); - return argument && argument->exists; + bool has(std::string_view arg) const { + if (const auto argument = findArgument(arg); argument.has_value()) { + return argument->get().isPresent; + } + return false; + } + +private: + struct Argument { + std::string_view shortOpt; + std::string_view longOpt; + std::string_view description; + std::optional value; + bool requiresValue; + bool isRequired = false; + bool isPresent = false; + }; + + template + T convertToType(const std::string_view value) const { + if constexpr (std::is_same_v) { + if (value == "true") + return true; + if (value == "false") + return false; + throw std::runtime_error("Invalid boolean string"); + } else if constexpr (std::is_arithmetic_v) { + T result{}; + auto [ptr, ec] = std::from_chars(value.data(), value.data() + value.size(), result); + if (ec != std::errc()) { + throw std::runtime_error(std::format("Failed to convert the value: {}", value)); + } + return result; + } else if constexpr (std::is_same_v) { + return std::string(value); + } else if constexpr (std::is_same_v) { + return value; + } else { + throw std::runtime_error("Unsupported type for conversion"); + } + } + + std::optional> findArgument(const std::string_view opt) const { + if (const auto it = m_opts.find(opt); it != m_opts.end()) { + return it->second; + } + return std::nullopt; } void printHelp() const { @@ -67,41 +121,6 @@ public: } } -private: - struct Argument { - std::string shortOpt; - std::string longOpt; - std::string description; - std::optional value; - bool requiresValue; - bool exists = false; - }; - - Argument* findArgument(const std::string& opt) { - if (auto it = m_optMap.find(opt); it != m_optMap.end()) - return it->second; - return nullptr; - } - - const Argument* findArgument(const std::string& opt) const { - if (auto it = m_optMap.find(opt); it != m_optMap.end()) - return it->second; - return nullptr; - } - - template - T convertToType(const std::string& value) const { - if constexpr (std::is_same_v) { - return value; - } else if constexpr (std::is_same_v) { - return std::stoi(value); - } else if constexpr (std::is_same_v) { - return std::stod(value); - } else { - throw std::runtime_error("Unsupported type for argument conversion"); - } - } - - std::vector m_arguments; - std::unordered_map m_optMap; + std::vector m_arguments; + std::unordered_map> m_opts; }; diff --git a/src/main.cpp b/src/main.cpp index 2f8f998..457d22d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,12 +2,12 @@ #include "Helpers/WolframAlpha.hpp" #include -int main(const int argc, char* argv[]) { +int main(const int argc, const char* argv[]) { try { ArgParser args; args.add("-h", "--help", "Print help", false); - args.add("-f", "--food", "Specify the food item"); - args.add("-a", "--amount", "Specify the amount (in grams)"); + args.add("-f", "--food", "Specify the food item", true, true); + args.add("-a", "--amount", "Specify the amount (in grams)", true, false, "100"); if (!args.parse(argc, argv)) { return 0;