From f29799d3b185c255f0366a56920c43fa6c7f70a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeromos=20Kov=C3=A1cs?= Date: Wed, 16 Apr 2025 17:13:13 +0200 Subject: [PATCH 01/31] feat(utils): add and use 'to_upper' util --- include/Arg.hpp | 2 +- include/utils.hpp | 7 ++++++- src/Parser.cpp | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 4809ff1..2ea3f92 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -107,7 +107,7 @@ class Arg { // auto_env_name_ // [[nodiscard]] inline const std::string get__auto_env_name() const { // std::string env_name = PROGRAM_NAME() + '_' + this->get__name(); - // std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); }); + // to_upper(env_name); // return env_name; // } diff --git a/include/utils.hpp b/include/utils.hpp index cfd0dec..387a618 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -1,9 +1,10 @@ #pragma once +#include #include +#include #include #include -#include template inline T ok_or(std::optional opt, E&& err) { @@ -44,3 +45,7 @@ inline void print_indent(std::ostream& os, int indent_level) { for (int i = 0; i < indent_level; ++i) os << '\t'; } + +inline void to_upper(std::string &s) { + std::ranges::transform(s, s.begin(), [](const unsigned char& c) { return std::toupper(c); }); +} diff --git a/src/Parser.cpp b/src/Parser.cpp index dcdfc44..31c44e0 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -63,7 +63,7 @@ void ClapParser::check_env() { for (auto& arg : args_) { if (arg.get__auto_env()) { std::string env_name = this->program_name_ + '_' + arg.get__name(); - std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); }); + to_upper(env_name); auto value_from_env = std::getenv(env_name.c_str()); if (value_from_env) { arg.set__value(value_from_env); From fcffbc652ab6dab1c6bab45c6414130452f5a880 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 13 May 2025 13:09:00 +0200 Subject: [PATCH 02/31] fix(Parser): removed unneeded value array --- include/Parser.hpp | 2 -- src/Parser.cpp | 20 ++++++++------------ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index fc8d483..1f0f494 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include class ClapParser { @@ -30,7 +29,6 @@ class ClapParser { friend std::ostream& operator<<(std::ostream& os, const ClapParser& parser); private: std::vector args_; - std::unordered_map values_; std::string program_name_; // Helper methods diff --git a/src/Parser.cpp b/src/Parser.cpp index 31c44e0..ae28942 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -99,14 +99,14 @@ void ClapParser::check_env() { // } // } -void ClapParser::handle_missing_positional(const Arg& arg) { - if (arg.get__is_required()) { - throw std::runtime_error("missing required positional argument: " + arg.get__name()); - } - if (arg.has_default()) { - values_[arg.get__name()] = std::string(arg.get__default_value()); - } -} +// void ClapParser::handle_missing_positional(const Arg& arg) { +// if (arg.get__is_required()) { +// throw std::runtime_error("missing required positional argument: " + arg.get__name()); +// } +// if (arg.has_default()) { +// values_[arg.get__name()] = std::string(arg.get__default_value()); +// } +// } inline bool ClapParser::is_option(const std::string& token) const { return token.substr(0, 2) == "--" || (token[0] == '-' && token.size() > 1); @@ -202,10 +202,6 @@ void ClapParser::print_parser(std::ostream& os, const ClapParser& parser, int in } print_indent(os, indent + 1); os << "],\n"; - print_indent(os, indent + 1); os << "values: {\n"; - for (const auto& [key, val] : parser.values_) { - print_indent(os, indent + 2); os << "\"" << key << "\": \"" << val << "\",\n"; - } print_indent(os, indent + 1); os << "}\n"; print_indent(os, indent); os << "}"; From b830bc097ad31f1c18f42375ea1e755d134b2571 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 13 May 2025 13:12:44 +0200 Subject: [PATCH 03/31] fix(tests): test_priority: remove unused import --- tests/test_priority.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_priority.cpp b/tests/test_priority.cpp index 49d3c89..722a4a6 100644 --- a/tests/test_priority.cpp +++ b/tests/test_priority.cpp @@ -2,7 +2,6 @@ #include "Parser.hpp" #include "Arg.hpp" #include "utils.hpp" -#include #include #include #include From 1859a560b4daf8a48f47ae82bf37c7db5ea4e58b Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 13 May 2025 13:14:27 +0200 Subject: [PATCH 04/31] fix(tests): test_combo: debug print parser for logs --- tests/test_combo.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_combo.cpp b/tests/test_combo.cpp index b07170d..9aa7ab0 100644 --- a/tests/test_combo.cpp +++ b/tests/test_combo.cpp @@ -27,6 +27,7 @@ int main(int argc, char* argv[]) { auto actual_val = ok_or_throw_str(p.get_one_as("val"), "test argument: 'val' is missing"); auto actual_boolean = ok_or_throw_str(p.get_one_as("flag"), "test argument: 'flag' is missing"); + std::cerr << p << '\n'; assert(actual_val == expected_val); assert(actual_boolean == expected_boolean); return 0; From 9d3efa2a1fe2e765d04d586d043a3c83550216c1 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 13 May 2025 13:19:18 +0200 Subject: [PATCH 05/31] misc(Parser): rename parse_options() -> parse_cli_args() --- include/Parser.hpp | 2 +- src/Parser.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 1f0f494..89c61cc 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -39,7 +39,7 @@ class ClapParser { std::vector get_positional_args() const; void apply_defaults(); - void parse_options(const std::vector& args); + void parse_cli_args(const std::vector& args); void check_env(); void parse_positional_args(const std::vector& args); void handle_missing_positional(const Arg& arg); diff --git a/src/Parser.cpp b/src/Parser.cpp index ae28942..b38e168 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -22,7 +22,7 @@ void ClapParser::parse(const int& argc, char* argv[]) { this->apply_defaults(); this->check_env(); - this->parse_options(args); // parse from cli (argc, argv) + this->parse_cli_args(args); // parse from cli (argc, argv) // parse_positional_args(args); // Validate all arguments that need values received them @@ -36,7 +36,7 @@ void ClapParser::parse(const int& argc, char* argv[]) { void ClapParser::add_arg(const Arg& arg) { args_.emplace_back(arg); } -void ClapParser::parse_options(const std::vector& args) { +void ClapParser::parse_cli_args(const std::vector& args) { for (size_t i = 0; i < args.size(); ++i) { const auto& token = args.at(i); From a30b12dd6f6e466af43dc90b6275e9e531c92595 Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 02:03:20 +0200 Subject: [PATCH 06/31] feat(Arg): add a functionality to accept many values --- include/Arg.hpp | 10 ++++++++++ src/Arg.cpp | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/include/Arg.hpp b/include/Arg.hpp index 2ea3f92..d1487a2 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -17,6 +17,7 @@ class Arg { Arg& help(const std::string& help); Arg& required(bool is_required); Arg& is_flag(); + Arg& accepts_many(); Arg& default_value(const std::string& default_val); Arg& from_env(const char* env_var_name); Arg& auto_env(); @@ -33,6 +34,7 @@ class Arg { std::string help_; bool is_required_; bool is_flag_; + bool accepts_many_; std::string env_name_; bool auto_env_; // std::string auto_env_name_; @@ -88,6 +90,14 @@ class Arg { this->is_flag_ = takes_value; } + // accepts_many_ + [[nodiscard]] inline bool get__accepts_many() const { + return this->accepts_many_; + } + inline void set__accepts_many(const bool& accepts_many) { + this->accepts_many_ = accepts_many; + } + // env_name_ [[nodiscard]] inline const std::string& get__env_name() const { return this->env_name_; diff --git a/src/Arg.cpp b/src/Arg.cpp index b7766e9..21b4f5e 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -12,6 +12,7 @@ Arg::Arg(const std::string& name) : help_(""), is_required_(false), is_flag_(false), + accepts_many_(false), env_name_(""), auto_env_(false), default_value_(""), @@ -36,6 +37,10 @@ Arg& Arg::is_flag() { default_value_ = "0"; return *this; } +Arg& Arg::accepts_many() { + accepts_many_ = true; + return *this; +} Arg& Arg::default_value(const std::string& default_value) { default_value_ = default_value; return *this; From 9cb542759bb2caac470be3ec1c0bf197020d0461 Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 02:04:13 +0200 Subject: [PATCH 07/31] fix(Arg): print 'accepts_many' as well --- src/Arg.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Arg.cpp b/src/Arg.cpp index 21b4f5e..be10381 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -74,6 +74,7 @@ void Arg::print_arg(std::ostream& os, const Arg& arg, int indent) { print_indent(os, indent + 1); os << "help: \"" << arg.help_ << "\",\n"; print_indent(os, indent + 1); os << "required: " << std::boolalpha << arg.is_required_ << ",\n"; print_indent(os, indent + 1); os << "is_flag: " << std::boolalpha << arg.is_flag_ << ",\n"; + print_indent(os, indent + 1); os << "accepts_many: " << std::boolalpha << arg.accepts_many_ << ",\n"; print_indent(os, indent + 1); os << "default: \"" << arg.default_value_ << "\",\n"; print_indent(os, indent + 1); os << "value: "; if (arg.value_) From 176c0f710497f8972f543fb9f082163018229fde Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 02:05:34 +0200 Subject: [PATCH 08/31] misc(Arg): correct a comment, remove a space --- include/Arg.hpp | 2 +- src/Arg.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index d1487a2..96c0cba 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -82,7 +82,7 @@ class Arg { this->is_required_ = is_required; } - // takes_value_ + // is_flag_ [[nodiscard]] inline bool get__is_flag() const { return this->is_flag_; } diff --git a/src/Arg.cpp b/src/Arg.cpp index be10381..ac18a58 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -32,7 +32,7 @@ Arg& Arg::required(bool is_required) { is_required_ = is_required; return *this; } -Arg& Arg::is_flag() { +Arg& Arg::is_flag() { is_flag_ = true; default_value_ = "0"; return *this; From 1b400400fe6bc3efc50042c1c90c06e63670cf84 Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 02:26:02 +0200 Subject: [PATCH 09/31] fix(Parser): corrected some helper func def and decl --- include/Parser.hpp | 6 +++--- src/Parser.cpp | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 89c61cc..84e58a1 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -32,9 +32,9 @@ class ClapParser { std::string program_name_; // Helper methods - inline bool is_option(const std::string& token) const ; - inline bool is_long_option(const std::string& token) const ; - inline bool is_short_option(const std::string& token) const ; + static bool is_option(const std::string& token); + static bool is_long_option(const std::string& token); + static bool is_short_option(const std::string& token); static std::optional find_arg(ClapParser& parser, const std::string& name); std::vector get_positional_args() const; void apply_defaults(); diff --git a/src/Parser.cpp b/src/Parser.cpp index b38e168..6a5966e 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -108,15 +108,17 @@ void ClapParser::check_env() { // } // } - inline bool ClapParser::is_option(const std::string& token) const { - return token.substr(0, 2) == "--" || (token[0] == '-' && token.size() > 1); - } +bool ClapParser::is_option(const std::string& token) { + return token.substr(0, 2) == "--" || (token[0] == '-' && token.size() > 1); +} - inline bool ClapParser::is_long_option(const std::string& token) const { return token.substr(0, 2) == "--"; } +bool ClapParser::is_long_option(const std::string& token) { + return token.substr(0, 2) == "--"; +} - inline bool ClapParser::is_short_option(const std::string& token) const { - return token[0] == '-' && token.size() > 1 && token[1] != '-'; - } +bool ClapParser::is_short_option(const std::string& token) { + return token[0] == '-' && token.size() > 1 && token[1] != '-'; +} void ClapParser::print_help() const { std::cout << "Usage: " << this->program_name_ << " [OPTIONS]"; From f69c2134e9a0a186f3f8ec22114afd0ae3e456f1 Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 02:27:08 +0200 Subject: [PATCH 10/31] fix(Parser): remove old/unused logic --- src/Parser.cpp | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/Parser.cpp b/src/Parser.cpp index 6a5966e..b0414f9 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -23,7 +23,6 @@ void ClapParser::parse(const int& argc, char* argv[]) { this->apply_defaults(); this->check_env(); this->parse_cli_args(args); // parse from cli (argc, argv) - // parse_positional_args(args); // Validate all arguments that need values received them for (const auto& arg : args_) { @@ -78,36 +77,6 @@ void ClapParser::check_env() { } }; -// void ClapParser::parse_positional_args(const std::vector& args) { -// std::vector positional_args; - -// // Collect positional arguments (tokens not starting with '-') -// for (const auto& token : args) { -// if (!is_option(token)) { -// positional_args.push_back(token); -// } -// } - -// // Assign positional arguments to their respective slots -// auto positional_specs = get_positional_args(); -// for (size_t j = 0; j < positional_specs.size(); ++j) { -// if (j < positional_args.size()) { -// values_[positional_specs[j].name()] = positional_specs[j].convert(positional_args[j]); -// } else { -// handle_missing_positional(positional_specs[j]); -// } -// } -// } - -// void ClapParser::handle_missing_positional(const Arg& arg) { -// if (arg.get__is_required()) { -// throw std::runtime_error("missing required positional argument: " + arg.get__name()); -// } -// if (arg.has_default()) { -// values_[arg.get__name()] = std::string(arg.get__default_value()); -// } -// } - bool ClapParser::is_option(const std::string& token) { return token.substr(0, 2) == "--" || (token[0] == '-' && token.size() > 1); } From 77d2741ae9494b4fb7c1e83557f5c8261b770bf3 Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 02:27:59 +0200 Subject: [PATCH 11/31] feat(Parser): WIP added ability to parse positionals from cli --- include/Parser.hpp | 1 + src/Parser.cpp | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 84e58a1..89eddfa 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -42,5 +42,6 @@ class ClapParser { void parse_cli_args(const std::vector& args); void check_env(); void parse_positional_args(const std::vector& args); + static void parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std::vector& args); void handle_missing_positional(const Arg& arg); }; diff --git a/src/Parser.cpp b/src/Parser.cpp index b0414f9..a4cc87f 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -3,11 +3,13 @@ #include "utils.hpp" #include +#include #include #include #include #include #include +#include void ClapParser::parse(const int& argc, char* argv[]) { const std::string& raw_program_name = argv[0]; @@ -45,19 +47,33 @@ void ClapParser::parse_cli_args(const std::vector& args) { } auto arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: \'" + token); + if (!arg->get__is_flag()) { - if (i + 1 < args.size() && !is_option(args[i + 1])) { - arg->set__value(args.at(i + 1)); - i++; // Skip the value in the next iteration - } else { - throw std::runtime_error("option '" + token + "' requires a value but none was provided"); - } + ClapParser::parse_value_for_non_flag(arg, i, args); } else { arg->set__value("1"); } } } +void ClapParser::parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std::vector& args) { + if (cli_index + 1 < args.size() && !is_option(args.at(cli_index + 1))) { + if (arg->get__accepts_many()) { + std::string value; + while (cli_index + 1 < args.size() && !is_option(args.at(cli_index + 1))) { + value += args.at(cli_index + 1) + ' '; + cli_index++; + } + arg->set__value(value); + } else { + arg->set__value(args.at(cli_index + 1)); + cli_index++; // Skip the value in the next iteration + } + } else { + throw std::runtime_error("option '" + arg->get__name() + "' requires a value but none was provided"); + } +} + void ClapParser::check_env() { for (auto& arg : args_) { if (arg.get__auto_env()) { From 52f1f060557985a16c427a51a5de22a441b2a3cd Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 03:01:50 +0200 Subject: [PATCH 12/31] feat(Parser): impl Parse for: int, std::string, bool --- include/Parser.hpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/include/Parser.hpp b/include/Parser.hpp index 89eddfa..302e229 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -3,11 +3,42 @@ #include "Arg.hpp" #include "utils.hpp" +#include #include #include #include #include +template +struct Parse { + static_assert(sizeof(T) == 0, "No Parse specialization defined for this type"); +}; + +template<> +struct Parse { + static std::optional parse(std::string_view s) { + int value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + if (ec == std::errc()) return value; + return std::nullopt; + } +}; + +template<> +struct Parse { + static std::optional parse(std::string_view s) { + return std::string(s); + } +}; + +template<> +struct Parse { + static std::optional parse(std::string_view s) { + auto as_int = Parse::parse(s).value(); + return as_int; + } +}; + class ClapParser { public: void add_arg(const Arg& arg); From 1bc25c8e6dcd94de313e4820023bc984b7665c3d Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 03:03:09 +0200 Subject: [PATCH 13/31] feat(Parser): added a 'Parseable' c++ concept --- include/Parser.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/Parser.hpp b/include/Parser.hpp index 302e229..4046498 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -39,6 +39,11 @@ struct Parse { } }; +template +concept Parseable = requires(std::string_view s) { + { Parse::parse(s) } -> std::convertible_to>; +}; + class ClapParser { public: void add_arg(const Arg& arg); From 950d6575417c1e5fb4a85cb83f34acb9191176ca Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 03:04:24 +0200 Subject: [PATCH 14/31] feat(Parser): parser uses way better parsing method! --- include/Parser.hpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 4046498..3caf513 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -50,15 +50,19 @@ class ClapParser { void parse(const int& argc, char* argv[]); void print_help() const; - template inline std::optional get_one_as(const std::string& name) { + template + requires Parseable + inline std::optional get_one_as(const std::string& name) { Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), []{ return std::nullopt; }); - if (auto arg_value = arg->get__value(); arg_value) { - T value; - std::istringstream(*arg_value) >> value; - return value; - } - return std::nullopt; + // if (auto arg_value = arg->get__value(); arg_value) { + // T value; + // std::istringstream(*arg_value) >> value; + // return value; + // } + // return std::nullopt; + + return Parse::parse(arg->get__value().value()); } static void print_parser(std::ostream& os, const ClapParser& parser, int indent); From 0da288c6c481e10bae980d4d91847e0ecb1ee945 Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 03:16:06 +0200 Subject: [PATCH 15/31] feat(Parser): impl Parse for (most) numeric types --- include/Parser.hpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 3caf513..c2d69ff 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -11,16 +11,22 @@ template struct Parse { - static_assert(sizeof(T) == 0, "No Parse specialization defined for this type"); -}; - -template<> -struct Parse { - static std::optional parse(std::string_view s) { - int value; - auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); - if (ec == std::errc()) return value; - return std::nullopt; + static std::optional parse(std::string_view s) { + if constexpr (std::is_integral_v) { + T value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + if (ec == std::errc()) return value; + return std::nullopt; + } + else if constexpr (std::is_floating_point_v) { + T value; + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); + if (ec == std::errc()) return value; + return std::nullopt; + } + else { + static_assert(sizeof(T) == 0, "No Parse specialization defined for this type"); + } } }; From 8545e16826c2f35710ca3792bad7d6e5c0ac659d Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 03:19:28 +0200 Subject: [PATCH 16/31] fix(Parser): include 'charconv', remove 'cmath' --- include/Parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index c2d69ff..aea750e 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -3,7 +3,7 @@ #include "Arg.hpp" #include "utils.hpp" -#include +#include #include #include #include From fee7117ea8386988556c63031b66a62350510f5c Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 14 May 2025 03:27:11 +0200 Subject: [PATCH 17/31] fix(Parser): more explicit number casting for MacOS compatibility --- include/Parser.hpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index aea750e..dd7b0c7 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -3,6 +3,11 @@ #include "Arg.hpp" #include "utils.hpp" +#include +#include +#include +#include +#include #include #include #include @@ -18,10 +23,22 @@ struct Parse { if (ec == std::errc()) return value; return std::nullopt; } - else if constexpr (std::is_floating_point_v) { - T value; - auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); - if (ec == std::errc()) return value; + else if constexpr (std::is_same_v) { + char* end = nullptr; + float value = std::strtof(s.data(), &end); + if (end == s.data() + s.size()) return value; + return std::nullopt; + } + else if constexpr (std::is_same_v) { + char* end = nullptr; + double value = std::strtod(s.data(), &end); + if (end == s.data() + s.size()) return value; + return std::nullopt; + } + else if constexpr (std::is_same_v) { + char* end = nullptr; + long double value = std::strtold(s.data(), &end); + if (end == s.data() + s.size()) return value; return std::nullopt; } else { From 807243decf29cf330cced114abf45651b3cde15c Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:21:18 +0200 Subject: [PATCH 18/31] feat(Parser): add macros for code generation --- include/Macros.hpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 include/Macros.hpp diff --git a/include/Macros.hpp b/include/Macros.hpp new file mode 100644 index 0000000..85f8df1 --- /dev/null +++ b/include/Macros.hpp @@ -0,0 +1,27 @@ +#pragma once + +#define DEFINE_PARSABLE_BASIC_TYPE(TYPE) \ +template<> \ +struct Parse { \ + static std::optional parse(std::string_view s) { \ + TYPE value; \ + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); \ + if (ec == std::errc()) return value; \ + return std::nullopt; \ + } \ +}; + +#define DEFINE_PARSABLE_FLOAT_TYPE(TYPE, CONVERT_FN) \ +template<> \ +struct Parse { \ + static std::optional parse(std::string_view s) { \ + char* end = nullptr; \ + TYPE value = CONVERT_FN(s.data(), &end); \ + if (end == s.data() + s.size()) return value; \ + return std::nullopt; \ + } \ +}; + +#define DEFINE_GETTER_SETTER(NAME, TYPE) \ + [[nodiscard]] inline const TYPE& get__##NAME() const { return this->NAME##_; } \ + inline void set__##NAME(const TYPE& NAME) { this->NAME##_ = NAME; } From 5a211ac853bcae7ee9734fbbc2d5a7a62bea1b3b Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:24:02 +0200 Subject: [PATCH 19/31] feat(Parser): move 'Parsable' and implementations --> Parsables.hpp --- include/Parsables.hpp | 52 +++++++++++++++++++++++++++++++++++++++++ include/Parser.hpp | 54 +------------------------------------------ 2 files changed, 53 insertions(+), 53 deletions(-) create mode 100644 include/Parsables.hpp diff --git a/include/Parsables.hpp b/include/Parsables.hpp new file mode 100644 index 0000000..18bb7e9 --- /dev/null +++ b/include/Parsables.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "Macros.hpp" + +#include +#include +#include +#include +#include +#include + +template +struct Parse { + static_assert(sizeof(T) == 0, "No Parse specialization defined for this type"); +}; + +template +concept Parseable = requires(std::string_view s) { + { Parse::parse(s) } -> std::convertible_to>; +}; + +// Integer types +DEFINE_PARSABLE_BASIC_TYPE(long long) +DEFINE_PARSABLE_BASIC_TYPE(unsigned long long) +DEFINE_PARSABLE_BASIC_TYPE(int8_t) +DEFINE_PARSABLE_BASIC_TYPE(uint8_t) +DEFINE_PARSABLE_BASIC_TYPE(int16_t) +DEFINE_PARSABLE_BASIC_TYPE(uint16_t) +DEFINE_PARSABLE_BASIC_TYPE(int32_t) +DEFINE_PARSABLE_BASIC_TYPE(uint32_t) +DEFINE_PARSABLE_BASIC_TYPE(int64_t) +DEFINE_PARSABLE_BASIC_TYPE(uint64_t) + +// Floating-point types +DEFINE_PARSABLE_FLOAT_TYPE(float, std::strtof) +DEFINE_PARSABLE_FLOAT_TYPE(double, std::strtod) +DEFINE_PARSABLE_FLOAT_TYPE(long double, std::strtold) + +template<> +struct Parse { + static std::optional parse(std::string_view s) { + return std::string(s.data()); + } +}; + +template<> +struct Parse { + static std::optional parse(std::string_view s) { + auto as_int = Parse::parse(s).value(); + return as_int; + } +}; diff --git a/include/Parser.hpp b/include/Parser.hpp index dd7b0c7..8168d43 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -2,6 +2,7 @@ #include "Arg.hpp" #include "utils.hpp" +#include "Parsables.hpp" #include #include @@ -14,59 +15,6 @@ #include #include -template -struct Parse { - static std::optional parse(std::string_view s) { - if constexpr (std::is_integral_v) { - T value; - auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); - if (ec == std::errc()) return value; - return std::nullopt; - } - else if constexpr (std::is_same_v) { - char* end = nullptr; - float value = std::strtof(s.data(), &end); - if (end == s.data() + s.size()) return value; - return std::nullopt; - } - else if constexpr (std::is_same_v) { - char* end = nullptr; - double value = std::strtod(s.data(), &end); - if (end == s.data() + s.size()) return value; - return std::nullopt; - } - else if constexpr (std::is_same_v) { - char* end = nullptr; - long double value = std::strtold(s.data(), &end); - if (end == s.data() + s.size()) return value; - return std::nullopt; - } - else { - static_assert(sizeof(T) == 0, "No Parse specialization defined for this type"); - } - } -}; - -template<> -struct Parse { - static std::optional parse(std::string_view s) { - return std::string(s); - } -}; - -template<> -struct Parse { - static std::optional parse(std::string_view s) { - auto as_int = Parse::parse(s).value(); - return as_int; - } -}; - -template -concept Parseable = requires(std::string_view s) { - { Parse::parse(s) } -> std::convertible_to>; -}; - class ClapParser { public: void add_arg(const Arg& arg); From 4626943aa6d18e01563817489b6bae301f0072ad Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:25:16 +0200 Subject: [PATCH 20/31] feat(Arg): use macro for getters and setters --- include/Arg.hpp | 114 +++++++----------------------------------------- 1 file changed, 16 insertions(+), 98 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 96c0cba..4b12a8a 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -1,6 +1,7 @@ #pragma once #include "utils.hpp" +#include "Macros.hpp" #include #include @@ -42,78 +43,18 @@ class Arg { std::optional value_; // ----| Getters & Setters |---- - // name_ - [[nodiscard]] inline const std::string& get__name() const { - return this->name_; - } - inline void set__name(const std::string& name) { - this->name_ = name; - } - - // short_ - [[nodiscard]] inline const std::string& get__short_name() const { - return this->short_name_; - } - inline void set__short_name(const std::string& short_name) { - this->short_name_ = short_name; - } - - // long_ - [[nodiscard]] inline const std::string& get__long_name() const { - return this->long_name_; - } - inline void set__long_name(const std::string& long_name) { - this->long_name_ = long_name; - } - - // help_ - [[nodiscard]] inline const std::string& get__help() const { - return this->help_; - } - inline void set__help(const std::string& help) { - this->help_ = help; - } - - // required_ - [[nodiscard]] inline bool get__is_required() const { - return this->is_required_; - } - inline void set__is_required(const bool& is_required) { - this->is_required_ = is_required; - } - - // is_flag_ - [[nodiscard]] inline bool get__is_flag() const { - return this->is_flag_; - } - inline void set__is_flag(const bool& takes_value) { - this->is_flag_ = takes_value; - } - - // accepts_many_ - [[nodiscard]] inline bool get__accepts_many() const { - return this->accepts_many_; - } - inline void set__accepts_many(const bool& accepts_many) { - this->accepts_many_ = accepts_many; - } + DEFINE_GETTER_SETTER(name, std::string) + DEFINE_GETTER_SETTER(short_name, std::string) + DEFINE_GETTER_SETTER(long_name, std::string) + DEFINE_GETTER_SETTER(help, std::string) + DEFINE_GETTER_SETTER(is_required, bool) + DEFINE_GETTER_SETTER(is_flag, bool) + DEFINE_GETTER_SETTER(accepts_many, bool) + DEFINE_GETTER_SETTER(env_name, std::string) + DEFINE_GETTER_SETTER(auto_env, bool) + DEFINE_GETTER_SETTER(default_value, std::string) + DEFINE_GETTER_SETTER(value, std::optional) - // env_name_ - [[nodiscard]] inline const std::string& get__env_name() const { - return this->env_name_; - } - inline void set__env_name(const std::string& env_name) { - this->env_name_ = env_name; - } - - // auto_env_ - [[nodiscard]] inline bool get__auto_env() const { - return this->auto_env_; - } - inline void set__auto_env(const bool& auto_env) { - this->auto_env_ = auto_env; - } - // auto_env_name_ // [[nodiscard]] inline const std::string get__auto_env_name() const { // std::string env_name = PROGRAM_NAME() + '_' + this->get__name(); @@ -121,36 +62,13 @@ class Arg { // return env_name; // } - // default_ - [[nodiscard]] inline const std::string& get__default_value() const { - return this->default_value_; - } - inline void set__default_value(const std::string& default_value) { - this->default_value_ = default_value; - } - - // value_ - [[nodiscard]] inline const std::optional get__value() const { - return this->value_; - } - inline void set__value(const std::string& value) { - this->value_ = value; - } - // ----| Checkers |---- // has_env_ - [[nodiscard]] inline bool has_env() const { - return !this->env_name_.empty(); - } - + [[nodiscard]] inline bool has_env() const { return !this->env_name_.empty(); } + // has_default_ - [[nodiscard]] inline bool has_default() const { - return !this->default_value_.empty(); - } + [[nodiscard]] inline bool has_default() const { return !this->default_value_.empty(); } // has_value_ - [[nodiscard]] inline bool has_value() const { - return this->value_.has_value(); - } - + [[nodiscard]] inline bool has_value() const { return this->value_.has_value(); } }; From a8f24581142dca1330a2bc68391748917c6dc463 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:26:59 +0200 Subject: [PATCH 21/31] fix(Arg): remove unused code --- include/Arg.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 4b12a8a..f3a76b8 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -38,7 +38,6 @@ class Arg { bool accepts_many_; std::string env_name_; bool auto_env_; - // std::string auto_env_name_; std::string default_value_; std::optional value_; @@ -55,13 +54,6 @@ class Arg { DEFINE_GETTER_SETTER(default_value, std::string) DEFINE_GETTER_SETTER(value, std::optional) - // auto_env_name_ - // [[nodiscard]] inline const std::string get__auto_env_name() const { - // std::string env_name = PROGRAM_NAME() + '_' + this->get__name(); - // to_upper(env_name); - // return env_name; - // } - // ----| Checkers |---- // has_env_ [[nodiscard]] inline bool has_env() const { return !this->env_name_.empty(); } From 851903f316d8421c3a0a46d0191dbd4b378727e5 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:30:41 +0200 Subject: [PATCH 22/31] fix(Parser): cleanup old/unused code --- include/Parser.hpp | 9 --------- src/Parser.cpp | 24 ------------------------ 2 files changed, 33 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 8168d43..314b131 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -25,14 +25,6 @@ class ClapParser { requires Parseable inline std::optional get_one_as(const std::string& name) { Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), []{ return std::nullopt; }); - - // if (auto arg_value = arg->get__value(); arg_value) { - // T value; - // std::istringstream(*arg_value) >> value; - // return value; - // } - // return std::nullopt; - return Parse::parse(arg->get__value().value()); } @@ -54,5 +46,4 @@ class ClapParser { void check_env(); void parse_positional_args(const std::vector& args); static void parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std::vector& args); - void handle_missing_positional(const Arg& arg); }; diff --git a/src/Parser.cpp b/src/Parser.cpp index a4cc87f..dcaf32c 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -107,10 +107,6 @@ bool ClapParser::is_short_option(const std::string& token) { void ClapParser::print_help() const { std::cout << "Usage: " << this->program_name_ << " [OPTIONS]"; - auto positionals = get_positional_args(); - for (const auto& pos : positionals) { - std::cout << " [" << pos.get__name() << "]"; - } std::cout << "\n\nOptions:\n"; for (const auto& arg : args_) { @@ -135,16 +131,6 @@ void ClapParser::print_help() const { std::cout << "--help"; std::cout << "\t" << "Prints this help message"; std::cout << "\n"; - - if (!positionals.empty()) { - std::cout << "\nPositional arguments:\n"; - for (const auto& pos : positionals) { - std::cout << " " << pos.get__name() << "\t" << pos.get__help(); - if (pos.has_default()) - std::cout << " (default: " << pos.get__default_value() << ")"; - std::cout << "\n"; - } - } } // Helper methods @@ -159,16 +145,6 @@ std::optional ClapParser::find_arg(ClapParser& parser, const std::string& return &(*it); } -std::vector ClapParser::get_positional_args() const { - std::vector positional; - for (const auto& arg : args_) { - if (arg.get__short_name().empty() && arg.get__long_name().empty()) { - positional.push_back(arg); - } - } - return positional; -} - void ClapParser::apply_defaults() { for (auto& arg : args_) { if (!arg.has_value() && arg.has_default()) { From 7fac396a542a13d506eef9b2ced3ddc0309db14f Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:33:16 +0200 Subject: [PATCH 23/31] fix: remove all unused imports everywhere --- include/Arg.hpp | 2 -- include/Parser.hpp | 4 ---- src/Arg.cpp | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index f3a76b8..593b2a7 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -1,9 +1,7 @@ #pragma once -#include "utils.hpp" #include "Macros.hpp" -#include #include #include #include diff --git a/include/Parser.hpp b/include/Parser.hpp index 314b131..bc45e95 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -5,11 +5,7 @@ #include "Parsables.hpp" #include -#include -#include -#include #include -#include #include #include #include diff --git a/src/Arg.cpp b/src/Arg.cpp index ac18a58..5ed46d4 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -1,6 +1,6 @@ #include "Arg.hpp" +#include "utils.hpp" -#include #include #include #include From 80beb8ceabb5b6fe129537541729f68940eb51a2 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 02:41:03 +0200 Subject: [PATCH 24/31] fix(Parsables): remove redefinition of i64 and u64 --- include/Parsables.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/Parsables.hpp b/include/Parsables.hpp index 18bb7e9..d8b3ccc 100644 --- a/include/Parsables.hpp +++ b/include/Parsables.hpp @@ -20,8 +20,6 @@ concept Parseable = requires(std::string_view s) { }; // Integer types -DEFINE_PARSABLE_BASIC_TYPE(long long) -DEFINE_PARSABLE_BASIC_TYPE(unsigned long long) DEFINE_PARSABLE_BASIC_TYPE(int8_t) DEFINE_PARSABLE_BASIC_TYPE(uint8_t) DEFINE_PARSABLE_BASIC_TYPE(int16_t) From 5075533581748b89281db3c479098305fe5b3f8a Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:40:03 +0200 Subject: [PATCH 25/31] fix(Arg): remove unused code --- src/Arg.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Arg.cpp b/src/Arg.cpp index 5ed46d4..2bec9c9 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -47,17 +47,6 @@ Arg& Arg::default_value(const std::string& default_value) { } Arg& Arg::from_env(const char* env_var_name) { this->env_name_ = env_var_name; - // std::string value_from_env = ptr_unwrap_or(std::getenv(env_var_name), concat("value \'", env_var_name, "\' not present in env for: ", this->name_)); - // std::optional value_from_env = ptr_unwrap_or(std::getenv(env_var_name), std::nullopt); - // this->value_ = ptr_unwrap_or(std::getenv(env_var_name), std::nullopt); -/* - auto ptr = std::getenv(env_var_name); - if (!ptr) { - this->value_ = std::nullopt; - } else { - this->value_ = ptr; - } -*/ return *this; }; Arg& Arg::auto_env() { From 5d18b5ab9c982f7a4468074d7ee6ea3222669b70 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 01:40:50 +0200 Subject: [PATCH 26/31] feat: add .clang-tidy file --- .clang-tidy | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 .clang-tidy diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..4d00d9a --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,109 @@ +Checks: + abseil-*, + altera-*, + android-*, + bugprone-*, + cert-*, + clang-*analyzer-*, + concurrency-*, + cppcoreguidelines-*, + darwin-*, + fuchsia-*, + google-*, + hicpp-*, + linuxkernel-*, + llvm-*, + misc-*, + modernize-*, + mpi-*, + objc-*, + openmp-*, + performance-*, + portability-*, + readability-*, + zircon-*, + -boost-*, + -llvmlibc-*, + -altera-unroll-loops, + -hicpp-vararg, + -hicpp-signed-bitwise, + -hicpp-no-array-decay, + -hicpp-explicit-conversions, + -cppcoreguidelines-pro-type-vararg, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -google-readability-todo, + -google-explicit-constructor, + -google-build-using-namespace, + -fuchsia-overloaded-operator, + -fuchsia-statically-constructed-objects, + -fuchsia-default-arguments-declarations, + -fuchsia-default-arguments-calls, + -modernize-use-trailing-return-type, + -readability-identifier-length, + -readability-magic-numbers, + -altera-struct-pack-align, + -misc-non-private-member-variables-in-classes, + -misc-use-anonymous-namespace, + -misc-use-internal-linkage, + -concurrency-mt-unsafe, + -cert-env33-c, + -cert-err58-cpp, + -bugprone-easily-swappable-parameters, + + # -abseil-cleanup-ctad + # -abseil-duration-addition + # -abseil-duration-comparison + # -abseil-duration-conversion-cast + # -abseil-duration-division + # -abseil-duration-factory-float + # -abseil-duration-factory-scale + # -abseil-duration-subtraction + # -abseil-duration-unnecessary-conversion + # -abseil-faster-strsplit-delimiter + # -abseil-no-internal-dependencies + # -abseil-no-namespace + # -abseil-redundant-strcat-calls + # -abseil-str-cat-append + # -abseil-string-find-startswith + # -abseil-string-find-str-contains + # -abseil-time-comparison + # -abseil-time-subtraction + # -abseil-upgrade-duration-conversions + # -altera-id-dependent-backward-branch + # -altera-struct-pack-align + # -altera-unroll-loops + # -bugprone-easily-swappable-parameters + # -cppcoreguidelines-avoid-c-arrays + # -cppcoreguidelines-avoid-magic-numbers + # -cppcoreguidelines-avoid-non-const-global-variables + # -cppcoreguidelines-pro-bounds-array-to-pointer-decay + # -cppcoreguidelines-pro-bounds-constant-array-index + # -cppcoreguidelines-pro-bounds-pointer-arithmetic + # -cppcoreguidelines-special-member-functions + # -fuchsia-default-arguments-calls + # -fuchsia-default-arguments-declarations + # -fuchsia-overloaded-operator + # -fuchsia-trailing-return + # -google-default-arguments + # -google-explicit-constructor + # -google-global-names-in-headers + # -google-objc-function-naming + # -google-objc-global-variable-declaration + # -google-runtime-int + # -hicpp-avoid-c-arrays + # -hicpp-explicit-conversions + # -hicpp-no-array-decay + # -hicpp-special-member-functions + # -llvmlibc-callee-namespace + # -llvmlibc-implementation-in-namespace + # -llvmlibc-restrict-system-libc-headers + # -modernize-avoid-c-arrays + # -modernize-use-nodiscard + # -modernize-use-trailing-return-type + # -readability-identifier-length + # -readability-isolate-declaration + # -readability-magic-numbers From 7309d775e44cc005fe87c5ab7ebfddbc440e98f4 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 02:12:47 +0200 Subject: [PATCH 27/31] misc: listen to clang-tidy --- include/Arg.hpp | 10 ++++---- include/Macros.hpp | 2 +- include/Parser.hpp | 6 ++--- include/utils.hpp | 57 +++++++++++++++++++++++++++------------------- src/Arg.cpp | 13 ++++------- src/Parser.cpp | 23 +++++++++---------- src/example.cpp | 3 +-- 7 files changed, 58 insertions(+), 56 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 593b2a7..5da27e1 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -2,14 +2,14 @@ #include "Macros.hpp" -#include #include #include +#include #include class Arg { public: - Arg(const std::string& name); + Arg(std::string name); Arg& short_name(const std::string& short_name); Arg& long_name(const std::string& long_name); @@ -54,11 +54,11 @@ class Arg { // ----| Checkers |---- // has_env_ - [[nodiscard]] inline bool has_env() const { return !this->env_name_.empty(); } + [[nodiscard]] bool has_env() const { return !this->env_name_.empty(); } // has_default_ - [[nodiscard]] inline bool has_default() const { return !this->default_value_.empty(); } + [[nodiscard]] bool has_default() const { return !this->default_value_.empty(); } // has_value_ - [[nodiscard]] inline bool has_value() const { return this->value_.has_value(); } + [[nodiscard]] bool has_value() const { return this->value_.has_value(); } }; diff --git a/include/Macros.hpp b/include/Macros.hpp index 85f8df1..a9f8973 100644 --- a/include/Macros.hpp +++ b/include/Macros.hpp @@ -17,7 +17,7 @@ struct Parse { \ static std::optional parse(std::string_view s) { \ char* end = nullptr; \ TYPE value = CONVERT_FN(s.data(), &end); \ - if (end == s.data() + s.size()) return value; \ + if (end == s.data() + s.size()) { return value; }\ return std::nullopt; \ } \ }; diff --git a/include/Parser.hpp b/include/Parser.hpp index bc45e95..eb057cc 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -1,10 +1,9 @@ #pragma once #include "Arg.hpp" -#include "utils.hpp" #include "Parsables.hpp" +#include "utils.hpp" -#include #include #include #include @@ -19,7 +18,7 @@ class ClapParser { template requires Parseable - inline std::optional get_one_as(const std::string& name) { + std::optional get_one_as(const std::string& name) { Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), []{ return std::nullopt; }); return Parse::parse(arg->get__value().value()); } @@ -35,7 +34,6 @@ class ClapParser { static bool is_long_option(const std::string& token); static bool is_short_option(const std::string& token); static std::optional find_arg(ClapParser& parser, const std::string& name); - std::vector get_positional_args() const; void apply_defaults(); void parse_cli_args(const std::vector& args); diff --git a/include/utils.hpp b/include/utils.hpp index 387a618..ae998d7 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -7,45 +7,54 @@ #include template -inline T ok_or(std::optional opt, E&& err) { - if (!opt) std::forward(err)(); - return *opt; +inline T ok_or(std::optional opt, E &&err) { + if (!opt) { + std::forward(err)(); + } + return *opt; } template -inline T ok_or_throw_str(std::optional opt, const std::string& err) { - if (!opt) throw std::runtime_error(err); - return *opt; +inline T ok_or_throw_str(std::optional opt, const std::string &err) { + if (!opt) { + throw std::runtime_error(err); + } + return *opt; } template -inline T ptr_ok_or_throw_str(T pointer, const std::string& err) { - if (!pointer) throw std::runtime_error(err); - return pointer; +inline T ptr_ok_or_throw_str(T pointer, const std::string &err) { + if (!pointer) { + throw std::runtime_error(err); + } + return pointer; } template -inline E ptr_unwrap_or(P pointer, const E other) { - if (!pointer) return other; +inline E ptr_unwrap_or(P pointer, const E other) { + if (!pointer) { + return other; + } } - + // variadic template function to concatenate any number of arguments -template inline std::string concat(Args&&... args) { - std::ostringstream oss; - (void)std::initializer_list{ - (oss << std::forward(args), 0)...}; // using initializer_list for fold-like behavior - return oss.str(); +template inline std::string concat(Args &&...args) { + std::ostringstream oss; + (void)std::initializer_list{ + (oss << std::forward(args), + 0)...}; // using initializer_list for fold-like behavior + return oss.str(); } -inline const std::string quote(const std::string& name) { - return '\'' + name + '\''; -} +inline std::string quote(const std::string &name) { return '\'' + name + '\''; } -inline void print_indent(std::ostream& os, int indent_level) { - for (int i = 0; i < indent_level; ++i) - os << '\t'; +inline void print_indent(std::ostream &os, int indent_level) { + for (int i = 0; i < indent_level; ++i) { + os << '\t'; + } } inline void to_upper(std::string &s) { - std::ranges::transform(s, s.begin(), [](const unsigned char& c) { return std::toupper(c); }); + std::ranges::transform( + s, s.begin(), [](const unsigned char &c) { return std::toupper(c); }); } diff --git a/src/Arg.cpp b/src/Arg.cpp index 2bec9c9..f911a04 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -5,17 +5,13 @@ #include #include -Arg::Arg(const std::string& name) : - name_(name), - short_name_(""), +Arg::Arg(std::string name) : + name_(std::move(name)), long_name_(this->name_), - help_(""), is_required_(false), is_flag_(false), accepts_many_(false), - env_name_(""), auto_env_(false), - default_value_(""), value_(std::nullopt) {} @@ -66,10 +62,11 @@ void Arg::print_arg(std::ostream& os, const Arg& arg, int indent) { print_indent(os, indent + 1); os << "accepts_many: " << std::boolalpha << arg.accepts_many_ << ",\n"; print_indent(os, indent + 1); os << "default: \"" << arg.default_value_ << "\",\n"; print_indent(os, indent + 1); os << "value: "; - if (arg.value_) + if (arg.value_) { os << "\"" << arg.value_.value() << "\""; - else + } else { os << "std::nullopt"; + } os << '\n'; print_indent(os, indent); os << "}"; diff --git a/src/Parser.cpp b/src/Parser.cpp index dcaf32c..06a085a 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -2,13 +2,12 @@ #include "Arg.hpp" #include "utils.hpp" -#include +#include #include #include #include #include #include -#include #include void ClapParser::parse(const int& argc, char* argv[]) { @@ -46,7 +45,7 @@ void ClapParser::parse_cli_args(const std::vector& args) { exit(0); } - auto arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: \'" + token); + auto *arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: \'" + token); if (!arg->get__is_flag()) { ClapParser::parse_value_for_non_flag(arg, i, args); @@ -79,14 +78,14 @@ void ClapParser::check_env() { if (arg.get__auto_env()) { std::string env_name = this->program_name_ + '_' + arg.get__name(); to_upper(env_name); - auto value_from_env = std::getenv(env_name.c_str()); - if (value_from_env) { + auto *value_from_env = std::getenv(env_name.c_str()); + if (value_from_env != nullptr) { arg.set__value(value_from_env); } } if (arg.has_env()) { - auto value_from_env = std::getenv(arg.get__env_name().c_str()); - if (value_from_env) { + auto *value_from_env = std::getenv(arg.get__env_name().c_str()); + if (value_from_env != nullptr) { arg.set__value(value_from_env); } } @@ -94,15 +93,15 @@ void ClapParser::check_env() { }; bool ClapParser::is_option(const std::string& token) { - return token.substr(0, 2) == "--" || (token[0] == '-' && token.size() > 1); + return token.starts_with("--") || (token.starts_with('-') && token.size() > 1); } bool ClapParser::is_long_option(const std::string& token) { - return token.substr(0, 2) == "--"; + return token.starts_with("--"); } bool ClapParser::is_short_option(const std::string& token) { - return token[0] == '-' && token.size() > 1 && token[1] != '-'; + return token.starts_with("-") && token.size() > 1 && token.at(1) != '-'; } void ClapParser::print_help() const { @@ -121,7 +120,7 @@ void ClapParser::print_help() const { } if (arg.get__auto_env()) { std::string env_name = this->program_name_ + '_' + arg.get__name(); - std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); }); + to_upper(env_name); std::cout << " [def.env: " << env_name << "]"; } std::cout << "\n"; @@ -135,7 +134,7 @@ void ClapParser::print_help() const { // Helper methods std::optional ClapParser::find_arg(ClapParser& parser, const std::string& arg_name) { - auto it = std::find_if(parser.args_.begin(), parser.args_.end(), [&](Arg& arg) { + auto it = std::ranges::find_if(parser.args_, [&](Arg& arg) { return ( "--" + arg.get__long_name() == arg_name || "-" + arg.get__short_name() == arg_name ); }); diff --git a/src/example.cpp b/src/example.cpp index 89b609f..52e3da3 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -2,10 +2,9 @@ #include "Arg.hpp" #include "utils.hpp" -#include #include -void run(ClapParser& parsed_args); +void run(ClapParser& arg_parser); int main(const int argc, char* argv[]) { ClapParser arg_parser; From 788fb51ea193e02981e8f7098db953ed9a047bf0 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 02:24:21 +0200 Subject: [PATCH 28/31] feat: add .clang-format file --- .clang-format | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..afc21b5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +NamespaceIndentation: All +PointerAlignment: Left +IndentWidth: 4 +ColumnLimit: 120 +BasedOnStyle: Google From ffb74c09955706a4bf1623a50ffe56ea50d2d27f Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 02:24:45 +0200 Subject: [PATCH 29/31] misc: clang-format everything --- include/Arg.hpp | 10 +++---- include/Macros.hpp | 47 +++++++++++++++--------------- include/Parsables.hpp | 19 +++++------- include/Parser.hpp | 5 ++-- include/utils.hpp | 68 ++++++++++++++++++++----------------------- src/Arg.cpp | 49 +++++++++++++++++-------------- src/Parser.cpp | 40 +++++++++++++------------ src/example.cpp | 53 +++++++++++++++++---------------- 8 files changed, 145 insertions(+), 146 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 5da27e1..d99170d 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -51,14 +51,14 @@ class Arg { DEFINE_GETTER_SETTER(auto_env, bool) DEFINE_GETTER_SETTER(default_value, std::string) DEFINE_GETTER_SETTER(value, std::optional) - + // ----| Checkers |---- // has_env_ - [[nodiscard]] bool has_env() const { return !this->env_name_.empty(); } - + [[nodiscard]] bool has_env() const { return !this->env_name_.empty(); } + // has_default_ - [[nodiscard]] bool has_default() const { return !this->default_value_.empty(); } + [[nodiscard]] bool has_default() const { return !this->default_value_.empty(); } // has_value_ - [[nodiscard]] bool has_value() const { return this->value_.has_value(); } + [[nodiscard]] bool has_value() const { return this->value_.has_value(); } }; diff --git a/include/Macros.hpp b/include/Macros.hpp index a9f8973..7e43219 100644 --- a/include/Macros.hpp +++ b/include/Macros.hpp @@ -1,27 +1,28 @@ #pragma once -#define DEFINE_PARSABLE_BASIC_TYPE(TYPE) \ -template<> \ -struct Parse { \ - static std::optional parse(std::string_view s) { \ - TYPE value; \ - auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); \ - if (ec == std::errc()) return value; \ - return std::nullopt; \ - } \ -}; +#define DEFINE_PARSABLE_BASIC_TYPE(TYPE) \ + template <> struct Parse { \ + static std::optional parse(std::string_view s) { \ + TYPE value; \ + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); \ + if (ec == std::errc()) \ + return value; \ + return std::nullopt; \ + } \ + }; -#define DEFINE_PARSABLE_FLOAT_TYPE(TYPE, CONVERT_FN) \ -template<> \ -struct Parse { \ - static std::optional parse(std::string_view s) { \ - char* end = nullptr; \ - TYPE value = CONVERT_FN(s.data(), &end); \ - if (end == s.data() + s.size()) { return value; }\ - return std::nullopt; \ - } \ -}; +#define DEFINE_PARSABLE_FLOAT_TYPE(TYPE, CONVERT_FN) \ + template <> struct Parse { \ + static std::optional parse(std::string_view s) { \ + char* end = nullptr; \ + TYPE value = CONVERT_FN(s.data(), &end); \ + if (end == s.data() + s.size()) { \ + return value; \ + } \ + return std::nullopt; \ + } \ + }; -#define DEFINE_GETTER_SETTER(NAME, TYPE) \ - [[nodiscard]] inline const TYPE& get__##NAME() const { return this->NAME##_; } \ - inline void set__##NAME(const TYPE& NAME) { this->NAME##_ = NAME; } +#define DEFINE_GETTER_SETTER(NAME, TYPE) \ + [[nodiscard]] inline const TYPE& get__##NAME() const { return this->NAME##_; } \ + inline void set__##NAME(const TYPE& NAME) { this->NAME##_ = NAME; } diff --git a/include/Parsables.hpp b/include/Parsables.hpp index d8b3ccc..143e2bc 100644 --- a/include/Parsables.hpp +++ b/include/Parsables.hpp @@ -9,12 +9,11 @@ #include #include -template -struct Parse { +template struct Parse { static_assert(sizeof(T) == 0, "No Parse specialization defined for this type"); }; -template +template concept Parseable = requires(std::string_view s) { { Parse::parse(s) } -> std::convertible_to>; }; @@ -34,17 +33,13 @@ DEFINE_PARSABLE_FLOAT_TYPE(float, std::strtof) DEFINE_PARSABLE_FLOAT_TYPE(double, std::strtod) DEFINE_PARSABLE_FLOAT_TYPE(long double, std::strtold) -template<> -struct Parse { - static std::optional parse(std::string_view s) { - return std::string(s.data()); - } +template <> struct Parse { + static std::optional parse(std::string_view s) { return std::string(s.data()); } }; -template<> -struct Parse { +template <> struct Parse { static std::optional parse(std::string_view s) { - auto as_int = Parse::parse(s).value(); - return as_int; + auto as_int = Parse::parse(s).value(); + return as_int; } }; diff --git a/include/Parser.hpp b/include/Parser.hpp index eb057cc..27921e9 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -17,14 +17,15 @@ class ClapParser { void print_help() const; template - requires Parseable + requires Parseable std::optional get_one_as(const std::string& name) { - Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), []{ return std::nullopt; }); + Arg* arg = ok_or(ClapParser::find_arg(*this, "--" + name), [] { return std::nullopt; }); return Parse::parse(arg->get__value().value()); } static void print_parser(std::ostream& os, const ClapParser& parser, int indent); friend std::ostream& operator<<(std::ostream& os, const ClapParser& parser); + private: std::vector args_; std::string program_name_; diff --git a/include/utils.hpp b/include/utils.hpp index ae998d7..2f0a5a2 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -6,55 +6,49 @@ #include #include -template -inline T ok_or(std::optional opt, E &&err) { - if (!opt) { - std::forward(err)(); - } - return *opt; +template inline T ok_or(std::optional opt, E&& err) { + if (!opt) { + std::forward(err)(); + } + return *opt; } -template -inline T ok_or_throw_str(std::optional opt, const std::string &err) { - if (!opt) { - throw std::runtime_error(err); - } - return *opt; +template inline T ok_or_throw_str(std::optional opt, const std::string& err) { + if (!opt) { + throw std::runtime_error(err); + } + return *opt; } -template -inline T ptr_ok_or_throw_str(T pointer, const std::string &err) { - if (!pointer) { - throw std::runtime_error(err); - } - return pointer; +template inline T ptr_ok_or_throw_str(T pointer, const std::string& err) { + if (!pointer) { + throw std::runtime_error(err); + } + return pointer; } -template -inline E ptr_unwrap_or(P pointer, const E other) { - if (!pointer) { - return other; - } +template inline E ptr_unwrap_or(P pointer, const E other) { + if (!pointer) { + return other; + } } // variadic template function to concatenate any number of arguments -template inline std::string concat(Args &&...args) { - std::ostringstream oss; - (void)std::initializer_list{ - (oss << std::forward(args), - 0)...}; // using initializer_list for fold-like behavior - return oss.str(); +template inline std::string concat(Args&&... args) { + std::ostringstream oss; + (void)std::initializer_list{ + (oss << std::forward(args), 0)...}; // using initializer_list for fold-like behavior + return oss.str(); } -inline std::string quote(const std::string &name) { return '\'' + name + '\''; } +inline std::string quote(const std::string& name) { return '\'' + name + '\''; } -inline void print_indent(std::ostream &os, int indent_level) { - for (int i = 0; i < indent_level; ++i) { - os << '\t'; - } +inline void print_indent(std::ostream& os, int indent_level) { + for (int i = 0; i < indent_level; ++i) { + os << '\t'; + } } -inline void to_upper(std::string &s) { - std::ranges::transform( - s, s.begin(), [](const unsigned char &c) { return std::toupper(c); }); +inline void to_upper(std::string& s) { + std::ranges::transform(s, s.begin(), [](const unsigned char& c) { return std::toupper(c); }); } diff --git a/src/Arg.cpp b/src/Arg.cpp index f911a04..b1a31ad 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -5,15 +5,9 @@ #include #include -Arg::Arg(std::string name) : - name_(std::move(name)), - long_name_(this->name_), - is_required_(false), - is_flag_(false), - accepts_many_(false), - auto_env_(false), - value_(std::nullopt) -{} +Arg::Arg(std::string name) + : name_(std::move(name)), long_name_(this->name_), is_required_(false), is_flag_(false), accepts_many_(false), + auto_env_(false), value_(std::nullopt) {} // Setters Arg& Arg::short_name(const std::string& short_name) { @@ -43,25 +37,35 @@ Arg& Arg::default_value(const std::string& default_value) { } Arg& Arg::from_env(const char* env_var_name) { this->env_name_ = env_var_name; - return *this; + return *this; }; Arg& Arg::auto_env() { - this -> auto_env_ = true; + this->auto_env_ = true; return *this; }; void Arg::print_arg(std::ostream& os, const Arg& arg, int indent) { - print_indent(os, indent); os << "Arg {\n"; + print_indent(os, indent); + os << "Arg {\n"; - print_indent(os, indent + 1); os << "name: \"" << arg.name_ << "\",\n"; - print_indent(os, indent + 1); os << "short: \"" << arg.short_name_ << "\",\n"; - print_indent(os, indent + 1); os << "long: \"" << arg.long_name_ << "\",\n"; - print_indent(os, indent + 1); os << "help: \"" << arg.help_ << "\",\n"; - print_indent(os, indent + 1); os << "required: " << std::boolalpha << arg.is_required_ << ",\n"; - print_indent(os, indent + 1); os << "is_flag: " << std::boolalpha << arg.is_flag_ << ",\n"; - print_indent(os, indent + 1); os << "accepts_many: " << std::boolalpha << arg.accepts_many_ << ",\n"; - print_indent(os, indent + 1); os << "default: \"" << arg.default_value_ << "\",\n"; - print_indent(os, indent + 1); os << "value: "; + print_indent(os, indent + 1); + os << "name: \"" << arg.name_ << "\",\n"; + print_indent(os, indent + 1); + os << "short: \"" << arg.short_name_ << "\",\n"; + print_indent(os, indent + 1); + os << "long: \"" << arg.long_name_ << "\",\n"; + print_indent(os, indent + 1); + os << "help: \"" << arg.help_ << "\",\n"; + print_indent(os, indent + 1); + os << "required: " << std::boolalpha << arg.is_required_ << ",\n"; + print_indent(os, indent + 1); + os << "is_flag: " << std::boolalpha << arg.is_flag_ << ",\n"; + print_indent(os, indent + 1); + os << "accepts_many: " << std::boolalpha << arg.accepts_many_ << ",\n"; + print_indent(os, indent + 1); + os << "default: \"" << arg.default_value_ << "\",\n"; + print_indent(os, indent + 1); + os << "value: "; if (arg.value_) { os << "\"" << arg.value_.value() << "\""; } else { @@ -69,7 +73,8 @@ void Arg::print_arg(std::ostream& os, const Arg& arg, int indent) { } os << '\n'; - print_indent(os, indent); os << "}"; + print_indent(os, indent); + os << "}"; } std::ostream& operator<<(std::ostream& os, const Arg& arg) { diff --git a/src/Parser.cpp b/src/Parser.cpp index 06a085a..b9fab50 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -45,7 +45,7 @@ void ClapParser::parse_cli_args(const std::vector& args) { exit(0); } - auto *arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: \'" + token); + auto* arg = ok_or_throw_str(ClapParser::find_arg(*this, token), "unknown option: \'" + token); if (!arg->get__is_flag()) { ClapParser::parse_value_for_non_flag(arg, i, args); @@ -68,7 +68,7 @@ void ClapParser::parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std arg->set__value(args.at(cli_index + 1)); cli_index++; // Skip the value in the next iteration } - } else { + } else { throw std::runtime_error("option '" + arg->get__name() + "' requires a value but none was provided"); } } @@ -78,13 +78,13 @@ void ClapParser::check_env() { if (arg.get__auto_env()) { std::string env_name = this->program_name_ + '_' + arg.get__name(); to_upper(env_name); - auto *value_from_env = std::getenv(env_name.c_str()); + auto* value_from_env = std::getenv(env_name.c_str()); if (value_from_env != nullptr) { arg.set__value(value_from_env); } } if (arg.has_env()) { - auto *value_from_env = std::getenv(arg.get__env_name().c_str()); + auto* value_from_env = std::getenv(arg.get__env_name().c_str()); if (value_from_env != nullptr) { arg.set__value(value_from_env); } @@ -95,11 +95,9 @@ void ClapParser::check_env() { bool ClapParser::is_option(const std::string& token) { return token.starts_with("--") || (token.starts_with('-') && token.size() > 1); } - -bool ClapParser::is_long_option(const std::string& token) { - return token.starts_with("--"); -} - + +bool ClapParser::is_long_option(const std::string& token) { return token.starts_with("--"); } + bool ClapParser::is_short_option(const std::string& token) { return token.starts_with("-") && token.size() > 1 && token.at(1) != '-'; } @@ -109,7 +107,7 @@ void ClapParser::print_help() const { std::cout << "\n\nOptions:\n"; for (const auto& arg : args_) { - arg.get__short_name().empty()? std::cout << " " : std::cout << " -" << arg.get__short_name() << ", "; + arg.get__short_name().empty() ? std::cout << " " : std::cout << " -" << arg.get__short_name() << ", "; std::cout << "--" << arg.get__long_name(); std::cout << "\t" << arg.get__help(); if (arg.has_default()) { @@ -134,8 +132,8 @@ void ClapParser::print_help() const { // Helper methods std::optional ClapParser::find_arg(ClapParser& parser, const std::string& arg_name) { - auto it = std::ranges::find_if(parser.args_, [&](Arg& arg) { - return ( "--" + arg.get__long_name() == arg_name || "-" + arg.get__short_name() == arg_name ); + auto it = std::ranges::find_if(parser.args_, [&](Arg& arg) { + return ("--" + arg.get__long_name() == arg_name || "-" + arg.get__short_name() == arg_name); }); if (it == parser.args_.end()) { @@ -153,20 +151,26 @@ void ClapParser::apply_defaults() { } void ClapParser::print_parser(std::ostream& os, const ClapParser& parser, int indent) { - print_indent(os, indent); os << "ClapParser {\n"; + print_indent(os, indent); + os << "ClapParser {\n"; - print_indent(os, indent + 1); os << "program_name: \"" << parser.program_name_ << "\",\n"; + print_indent(os, indent + 1); + os << "program_name: \"" << parser.program_name_ << "\",\n"; - print_indent(os, indent + 1); os << "args: [\n"; + print_indent(os, indent + 1); + os << "args: [\n"; for (const auto& arg : parser.args_) { Arg::print_arg(os, arg, indent + 2); os << ",\n"; } - print_indent(os, indent + 1); os << "],\n"; + print_indent(os, indent + 1); + os << "],\n"; - print_indent(os, indent + 1); os << "}\n"; + print_indent(os, indent + 1); + os << "}\n"; - print_indent(os, indent); os << "}"; + print_indent(os, indent); + os << "}"; } std::ostream& operator<<(std::ostream& os, const ClapParser& parser) { diff --git a/src/example.cpp b/src/example.cpp index 52e3da3..cc21ba8 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -1,5 +1,5 @@ -#include "Parser.hpp" #include "Arg.hpp" +#include "Parser.hpp" #include "utils.hpp" #include @@ -7,35 +7,34 @@ void run(ClapParser& arg_parser); int main(const int argc, char* argv[]) { - ClapParser arg_parser; - auto num1 = Arg("num1").from_env("ASDF").auto_env().required(true); - // std::cerr << num1 << "\n"; - arg_parser.add_arg(num1); - auto num2 = Arg("num2").short_name("N").from_env("TES").default_value("99"); - arg_parser.add_arg(num2); - - arg_parser.add_arg(Arg("test").is_flag()); - // arg_parser.add_arg(Arg("test").is_flag(true)); - - try { - arg_parser.parse(argc, argv); - // std::cerr << arg_parser; - run(arg_parser); - } - catch (const std::exception& e) { - std::cerr << "\n\n\nerror: " << e.what() << "\n\n\n"; - // arg_parser.print_help(); - } + ClapParser arg_parser; + auto num1 = Arg("num1").from_env("ASDF").auto_env().required(true); + // std::cerr << num1 << "\n"; + arg_parser.add_arg(num1); + auto num2 = Arg("num2").short_name("N").from_env("TES").default_value("99"); + arg_parser.add_arg(num2); + + arg_parser.add_arg(Arg("test").is_flag()); + // arg_parser.add_arg(Arg("test").is_flag(true)); + + try { + arg_parser.parse(argc, argv); + // std::cerr << arg_parser; + run(arg_parser); + } catch (const std::exception& e) { + std::cerr << "\n\n\nerror: " << e.what() << "\n\n\n"; + // arg_parser.print_help(); + } } void run(ClapParser& arg_parser) { - // std::cerr << "running\n"; - auto num1 = ok_or_throw_str(arg_parser.get_one_as("num1"), "num1 not defined"); - auto num2 = ok_or_throw_str(arg_parser.get_one_as("num2"), "num2 not defined"); + // std::cerr << "running\n"; + auto num1 = ok_or_throw_str(arg_parser.get_one_as("num1"), "num1 not defined"); + auto num2 = ok_or_throw_str(arg_parser.get_one_as("num2"), "num2 not defined"); - std::cout << "num1: " << num1 << '\n'; - std::cout.precision(5); - std::cout << std::fixed << "num2: " << num2 << '\n'; + std::cout << "num1: " << num1 << '\n'; + std::cout.precision(5); + std::cout << std::fixed << "num2: " << num2 << '\n'; - // std::cerr << arg_parser; + // std::cerr << arg_parser; } From 7f6a3803c006866cce167cbef2512dcc38056f41 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 02:29:47 +0200 Subject: [PATCH 30/31] misc: clang-format everything (Google style) --- include/Arg.hpp | 8 +++---- include/Macros.hpp | 43 ++++++++++++++++++------------------ include/Parsables.hpp | 13 ++++++----- include/Parser.hpp | 12 +++++----- include/utils.hpp | 17 +++++++++----- src/Arg.cpp | 15 +++++++++---- src/Parser.cpp | 9 ++++---- src/example.cpp | 4 ++-- tests/test_combo.cpp | 49 +++++++++++++++++++++-------------------- tests/test_flag.cpp | 33 +++++++++++++-------------- tests/test_priority.cpp | 35 +++++++++++++++-------------- 11 files changed, 129 insertions(+), 109 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index d99170d..49b3275 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -1,14 +1,14 @@ #pragma once -#include "Macros.hpp" - #include #include #include #include +#include "Macros.hpp" + class Arg { - public: + public: Arg(std::string name); Arg& short_name(const std::string& short_name); @@ -24,7 +24,7 @@ class Arg { static void print_arg(std::ostream& os, const Arg& arg, int indent); friend std::ostream& operator<<(std::ostream& os, const Arg& arg); - private: + private: friend class ClapParser; std::string name_; diff --git a/include/Macros.hpp b/include/Macros.hpp index 7e43219..49e1049 100644 --- a/include/Macros.hpp +++ b/include/Macros.hpp @@ -1,28 +1,29 @@ #pragma once -#define DEFINE_PARSABLE_BASIC_TYPE(TYPE) \ - template <> struct Parse { \ - static std::optional parse(std::string_view s) { \ - TYPE value; \ - auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); \ - if (ec == std::errc()) \ - return value; \ - return std::nullopt; \ - } \ +#define DEFINE_PARSABLE_BASIC_TYPE(TYPE) \ + template <> \ + struct Parse { \ + static std::optional parse(std::string_view s) { \ + TYPE value; \ + auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value); \ + if (ec == std::errc()) return value; \ + return std::nullopt; \ + } \ }; -#define DEFINE_PARSABLE_FLOAT_TYPE(TYPE, CONVERT_FN) \ - template <> struct Parse { \ - static std::optional parse(std::string_view s) { \ - char* end = nullptr; \ - TYPE value = CONVERT_FN(s.data(), &end); \ - if (end == s.data() + s.size()) { \ - return value; \ - } \ - return std::nullopt; \ - } \ +#define DEFINE_PARSABLE_FLOAT_TYPE(TYPE, CONVERT_FN) \ + template <> \ + struct Parse { \ + static std::optional parse(std::string_view s) { \ + char* end = nullptr; \ + TYPE value = CONVERT_FN(s.data(), &end); \ + if (end == s.data() + s.size()) { \ + return value; \ + } \ + return std::nullopt; \ + } \ }; -#define DEFINE_GETTER_SETTER(NAME, TYPE) \ - [[nodiscard]] inline const TYPE& get__##NAME() const { return this->NAME##_; } \ +#define DEFINE_GETTER_SETTER(NAME, TYPE) \ + [[nodiscard]] inline const TYPE& get__##NAME() const { return this->NAME##_; } \ inline void set__##NAME(const TYPE& NAME) { this->NAME##_ = NAME; } diff --git a/include/Parsables.hpp b/include/Parsables.hpp index 143e2bc..343545f 100644 --- a/include/Parsables.hpp +++ b/include/Parsables.hpp @@ -1,7 +1,5 @@ #pragma once -#include "Macros.hpp" - #include #include #include @@ -9,7 +7,10 @@ #include #include -template struct Parse { +#include "Macros.hpp" + +template +struct Parse { static_assert(sizeof(T) == 0, "No Parse specialization defined for this type"); }; @@ -33,11 +34,13 @@ DEFINE_PARSABLE_FLOAT_TYPE(float, std::strtof) DEFINE_PARSABLE_FLOAT_TYPE(double, std::strtod) DEFINE_PARSABLE_FLOAT_TYPE(long double, std::strtold) -template <> struct Parse { +template <> +struct Parse { static std::optional parse(std::string_view s) { return std::string(s.data()); } }; -template <> struct Parse { +template <> +struct Parse { static std::optional parse(std::string_view s) { auto as_int = Parse::parse(s).value(); return as_int; diff --git a/include/Parser.hpp b/include/Parser.hpp index 27921e9..2c29de0 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -1,17 +1,17 @@ #pragma once -#include "Arg.hpp" -#include "Parsables.hpp" -#include "utils.hpp" - #include #include #include #include #include +#include "Arg.hpp" +#include "Parsables.hpp" +#include "utils.hpp" + class ClapParser { - public: + public: void add_arg(const Arg& arg); void parse(const int& argc, char* argv[]); void print_help() const; @@ -26,7 +26,7 @@ class ClapParser { static void print_parser(std::ostream& os, const ClapParser& parser, int indent); friend std::ostream& operator<<(std::ostream& os, const ClapParser& parser); - private: + private: std::vector args_; std::string program_name_; diff --git a/include/utils.hpp b/include/utils.hpp index 2f0a5a2..73e4093 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -6,38 +6,43 @@ #include #include -template inline T ok_or(std::optional opt, E&& err) { +template +inline T ok_or(std::optional opt, E&& err) { if (!opt) { std::forward(err)(); } return *opt; } -template inline T ok_or_throw_str(std::optional opt, const std::string& err) { +template +inline T ok_or_throw_str(std::optional opt, const std::string& err) { if (!opt) { throw std::runtime_error(err); } return *opt; } -template inline T ptr_ok_or_throw_str(T pointer, const std::string& err) { +template +inline T ptr_ok_or_throw_str(T pointer, const std::string& err) { if (!pointer) { throw std::runtime_error(err); } return pointer; } -template inline E ptr_unwrap_or(P pointer, const E other) { +template +inline E ptr_unwrap_or(P pointer, const E other) { if (!pointer) { return other; } } // variadic template function to concatenate any number of arguments -template inline std::string concat(Args&&... args) { +template +inline std::string concat(Args&&... args) { std::ostringstream oss; (void)std::initializer_list{ - (oss << std::forward(args), 0)...}; // using initializer_list for fold-like behavior + (oss << std::forward(args), 0)...}; // using initializer_list for fold-like behavior return oss.str(); } diff --git a/src/Arg.cpp b/src/Arg.cpp index b1a31ad..b4b3811 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -1,13 +1,20 @@ #include "Arg.hpp" -#include "utils.hpp" -#include #include + +#include #include +#include "utils.hpp" + Arg::Arg(std::string name) - : name_(std::move(name)), long_name_(this->name_), is_required_(false), is_flag_(false), accepts_many_(false), - auto_env_(false), value_(std::nullopt) {} + : name_(std::move(name)), + long_name_(this->name_), + is_required_(false), + is_flag_(false), + accepts_many_(false), + auto_env_(false), + value_(std::nullopt) {} // Setters Arg& Arg::short_name(const std::string& short_name) { diff --git a/src/Parser.cpp b/src/Parser.cpp index b9fab50..cdfb31c 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,6 +1,4 @@ #include "Parser.hpp" -#include "Arg.hpp" -#include "utils.hpp" #include #include @@ -10,6 +8,9 @@ #include #include +#include "Arg.hpp" +#include "utils.hpp" + void ClapParser::parse(const int& argc, char* argv[]) { const std::string& raw_program_name = argv[0]; #ifdef _WIN32 @@ -23,7 +24,7 @@ void ClapParser::parse(const int& argc, char* argv[]) { this->apply_defaults(); this->check_env(); - this->parse_cli_args(args); // parse from cli (argc, argv) + this->parse_cli_args(args); // parse from cli (argc, argv) // Validate all arguments that need values received them for (const auto& arg : args_) { @@ -66,7 +67,7 @@ void ClapParser::parse_value_for_non_flag(Arg* arg, size_t& cli_index, const std arg->set__value(value); } else { arg->set__value(args.at(cli_index + 1)); - cli_index++; // Skip the value in the next iteration + cli_index++; // Skip the value in the next iteration } } else { throw std::runtime_error("option '" + arg->get__name() + "' requires a value but none was provided"); diff --git a/src/example.cpp b/src/example.cpp index cc21ba8..b16ce2a 100644 --- a/src/example.cpp +++ b/src/example.cpp @@ -1,9 +1,9 @@ +#include + #include "Arg.hpp" #include "Parser.hpp" #include "utils.hpp" -#include - void run(ClapParser& arg_parser); int main(const int argc, char* argv[]) { diff --git a/tests/test_combo.cpp b/tests/test_combo.cpp index 9aa7ab0..c70c91b 100644 --- a/tests/test_combo.cpp +++ b/tests/test_combo.cpp @@ -1,34 +1,35 @@ // tests/test_combo.cpp -#include "Parser.hpp" -#include "Arg.hpp" -#include "utils.hpp" -#include #include +#include #include +#include "Arg.hpp" +#include "Parser.hpp" +#include "utils.hpp" + int main(int argc, char* argv[]) { - ClapParser p; - // a value arg with full priority stack: - auto val = Arg("val").from_env("VAL").auto_env().default_value("10"); - auto boolean = Arg("flag").is_flag(); - p.add_arg(val); - p.add_arg(boolean); - p.parse(argc, argv); + ClapParser p; + // a value arg with full priority stack: + auto val = Arg("val").from_env("VAL").auto_env().default_value("10"); + auto boolean = Arg("flag").is_flag(); + p.add_arg(val); + p.add_arg(boolean); + p.parse(argc, argv); - // EXPECTED_V -> any int - // EXPECTED_B -> "1" or "0" - const auto ev = std::getenv("EXPECTED_V"); - const auto eb = std::getenv("EXPECTED_B"); - assert(ev && eb && "EXPECTED_V/B must be set"); + // EXPECTED_V -> any int + // EXPECTED_B -> "1" or "0" + const auto ev = std::getenv("EXPECTED_V"); + const auto eb = std::getenv("EXPECTED_B"); + assert(ev && eb && "EXPECTED_V/B must be set"); - int expected_val = std::stoi(ev); - bool expected_boolean = std::stoi(eb); + int expected_val = std::stoi(ev); + bool expected_boolean = std::stoi(eb); - auto actual_val = ok_or_throw_str(p.get_one_as("val"), "test argument: 'val' is missing"); - auto actual_boolean = ok_or_throw_str(p.get_one_as("flag"), "test argument: 'flag' is missing"); + auto actual_val = ok_or_throw_str(p.get_one_as("val"), "test argument: 'val' is missing"); + auto actual_boolean = ok_or_throw_str(p.get_one_as("flag"), "test argument: 'flag' is missing"); - std::cerr << p << '\n'; - assert(actual_val == expected_val); - assert(actual_boolean == expected_boolean); - return 0; + std::cerr << p << '\n'; + assert(actual_val == expected_val); + assert(actual_boolean == expected_boolean); + return 0; } diff --git a/tests/test_flag.cpp b/tests/test_flag.cpp index 9c2676d..9ffb2cc 100644 --- a/tests/test_flag.cpp +++ b/tests/test_flag.cpp @@ -1,24 +1,25 @@ // tests/test_flag.cpp -#include "Parser.hpp" -#include "Arg.hpp" -#include "utils.hpp" -#include #include +#include #include +#include "Arg.hpp" +#include "Parser.hpp" +#include "utils.hpp" + int main(int argc, char* argv[]) { - ClapParser p; - auto f = Arg("opt").is_flag(); - p.add_arg(f); - p.parse(argc, argv); + ClapParser p; + auto f = Arg("opt").is_flag(); + p.add_arg(f); + p.parse(argc, argv); - // EXPECTED should be "1" or "0" (present or absent) - const auto e = std::getenv("EXPECTED"); - assert(e && "EXPECTED must be set"); - bool expected = std::string(e) == "1"; + // EXPECTED should be "1" or "0" (present or absent) + const auto e = std::getenv("EXPECTED"); + assert(e && "EXPECTED must be set"); + bool expected = std::string(e) == "1"; - auto actual = ok_or_throw_str(p.get_one_as("opt"), "test argument: 'opt' is missing"); - std::cerr << p << '\n'; - assert(actual == expected); - return 0; + auto actual = ok_or_throw_str(p.get_one_as("opt"), "test argument: 'opt' is missing"); + std::cerr << p << '\n'; + assert(actual == expected); + return 0; } diff --git a/tests/test_priority.cpp b/tests/test_priority.cpp index 722a4a6..2d3f104 100644 --- a/tests/test_priority.cpp +++ b/tests/test_priority.cpp @@ -1,33 +1,34 @@ // tests/test_priority.cpp -#include "Parser.hpp" -#include "Arg.hpp" -#include "utils.hpp" -#include #include +#include #include #include +#include "Arg.hpp" +#include "Parser.hpp" +#include "utils.hpp" + /* VALUE PRIORITY: 1. cli arg: '--val' 2. env var: 'VAL' 3. auto env var: 'PRIORITY_MANUAL_VAL' - 4. default value: '1' + 4. default value: '1' */ int main(const int argc, char* argv[]) { - ClapParser p; + ClapParser p; - auto a = Arg("val").from_env("VAL").auto_env().default_value("1"); - p.add_arg(a); - p.parse(argc, argv); + auto a = Arg("val").from_env("VAL").auto_env().default_value("1"); + p.add_arg(a); + p.parse(argc, argv); - // gets read from meson test env - const auto e = std::getenv("EXPECTED"); - assert(e && "EXPECTED must be set"); - int expected = std::stoi(e); + // gets read from meson test env + const auto e = std::getenv("EXPECTED"); + assert(e && "EXPECTED must be set"); + int expected = std::stoi(e); - auto actual = ok_or_throw_str(p.get_one_as("val"), "test argument: 'val' is missing"); - std::cerr << p << '\n'; - assert(actual == expected); - return 0; + auto actual = ok_or_throw_str(p.get_one_as("val"), "test argument: 'val' is missing"); + std::cerr << p << '\n'; + assert(actual == expected); + return 0; } From 95776e0a5cd031acc50f5da3277b5ca33bba3346 Mon Sep 17 00:00:00 2001 From: csboo Date: Thu, 15 May 2025 02:32:33 +0200 Subject: [PATCH 31/31] misc(tests): listen to clang-tidy --- tests/test_combo.cpp | 6 +++--- tests/test_flag.cpp | 2 +- tests/test_priority.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_combo.cpp b/tests/test_combo.cpp index c70c91b..6ad23e6 100644 --- a/tests/test_combo.cpp +++ b/tests/test_combo.cpp @@ -18,12 +18,12 @@ int main(int argc, char* argv[]) { // EXPECTED_V -> any int // EXPECTED_B -> "1" or "0" - const auto ev = std::getenv("EXPECTED_V"); - const auto eb = std::getenv("EXPECTED_B"); + auto *const ev = std::getenv("EXPECTED_V"); + auto *const eb = std::getenv("EXPECTED_B"); assert(ev && eb && "EXPECTED_V/B must be set"); int expected_val = std::stoi(ev); - bool expected_boolean = std::stoi(eb); + bool expected_boolean = std::stoi(eb) != 0; auto actual_val = ok_or_throw_str(p.get_one_as("val"), "test argument: 'val' is missing"); auto actual_boolean = ok_or_throw_str(p.get_one_as("flag"), "test argument: 'flag' is missing"); diff --git a/tests/test_flag.cpp b/tests/test_flag.cpp index 9ffb2cc..69fe8c7 100644 --- a/tests/test_flag.cpp +++ b/tests/test_flag.cpp @@ -14,7 +14,7 @@ int main(int argc, char* argv[]) { p.parse(argc, argv); // EXPECTED should be "1" or "0" (present or absent) - const auto e = std::getenv("EXPECTED"); + auto *const e = std::getenv("EXPECTED"); assert(e && "EXPECTED must be set"); bool expected = std::string(e) == "1"; diff --git a/tests/test_priority.cpp b/tests/test_priority.cpp index 2d3f104..4d900ef 100644 --- a/tests/test_priority.cpp +++ b/tests/test_priority.cpp @@ -23,7 +23,7 @@ int main(const int argc, char* argv[]) { p.parse(argc, argv); // gets read from meson test env - const auto e = std::getenv("EXPECTED"); + auto *const e = std::getenv("EXPECTED"); assert(e && "EXPECTED must be set"); int expected = std::stoi(e);