From b1ca8a5a2bc1724936a4a77e6ea643bb0bef23e9 Mon Sep 17 00:00:00 2001 From: csboo Date: Mon, 14 Apr 2025 13:20:57 +0200 Subject: [PATCH 01/22] misc: all error msg lowercase --- src/Parser.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Parser.cpp b/src/Parser.cpp index 5047260..2f09099 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -21,7 +21,7 @@ void ClapParser::parse(int argc, char* argv[]) { for (const auto& arg : args_) { if (arg.takes_value() && args_with_values.count(arg.name()) == 0) { if (arg.is_required() && !arg.has_default()) { - throw std::runtime_error("Argument '" + arg.name() + "' requires a value"); + throw std::runtime_error("argument '" + arg.name() + "' requires a value"); } } } @@ -102,7 +102,7 @@ size_t ClapParser::handle_long_option(const std::string& token, const std::vecto } const Arg* arg = find_option(opt_name); if (arg == nullptr) { - throw std::runtime_error("Unknown option: " + token); + throw std::runtime_error("unknown option: " + token); } if (arg->takes_value()) { @@ -145,7 +145,7 @@ size_t ClapParser::handle_option_with_value(const Arg* arg, const std::vectorname()] = std::string(arg->default_value()); } else { - throw std::runtime_error("Option '" + token + "' requires a value but none was provided"); + throw std::runtime_error("option '" + token + "' requires a value but none was provided"); } return i; @@ -153,7 +153,7 @@ size_t ClapParser::handle_option_with_value(const Arg* arg, const std::vector Date: Tue, 15 Apr 2025 17:19:43 +0200 Subject: [PATCH 02/22] feat(utils): get clean program name --- src/utils.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/utils.cpp b/src/utils.cpp index ab9cf8b..95a11b1 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -33,3 +33,8 @@ template std::string concat(Args&&... args) { (oss << std::forward(args), 0)...}; // using initializer_list for fold-like behavior return oss.str(); } + +const std::string PROGRAM_NAME() { + const std::string& raw_program_name = program_invocation_name; + return raw_program_name.substr(raw_program_name.rfind('/') + 1); +} From 0805eb49589921815429ea17e293fa73f846746a Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:20:29 +0200 Subject: [PATCH 03/22] misc(meson) don't build the static lib, simpler for now --- meson.build | 76 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/meson.build b/meson.build index ec4fc4e..ca4c312 100644 --- a/meson.build +++ b/meson.build @@ -1,41 +1,65 @@ project('claplusplus', 'cpp', - version : '0.0.3', - default_options : [ + default_options: [ 'cpp_std=c++23', 'warning_level=3', - 'werror=true', + # 'werror=true', 'optimization=g', 'debug=true', 'b_ndebug=if-release' ] ) -# Include directory +# Add include directory for headers include_dir = include_directories('include') -# Library -source_dir = files( - 'src/Arg.cpp', - 'src/Parser.cpp', - 'src/utils.cpp' -) - -claplusplus_lib = static_library('claplusplus', - sources: source_dir, +# Build executable directly from all necessary sources +executable('claPlusPlus', + sources: [ + 'src/old_main.cpp', + 'src/Arg.cpp', + 'src/Parser.cpp' + ], include_directories: include_dir, - install: true + install: false ) + +# project('claplusplus', 'cpp', +# version : '0.0.3', +# default_options : [ +# 'cpp_std=c++23', +# 'warning_level=3', +# 'optimization=g', +# 'debug=true', +# 'b_ndebug=if-release' +# ] +# ) + +# # Include directory +# include_dir = include_directories('include') + +# # Library +# source_dir = files( +# 'src/Arg.cpp', +# 'src/Parser.cpp', +# 'src/utils.cpp' +# ) + +# claplusplus_lib = static_library('claplusplus', +# sources: source_dir, +# include_directories: include_dir, +# install: true +# ) -# Install headers -install_headers( - 'include/Arg.hpp', - 'include/Parser.hpp', - subdir: 'claplusplus' -) +# # Install headers +# install_headers( +# 'include/Arg.hpp', +# 'include/Parser.hpp', +# subdir: 'claplusplus' +# ) -executable('app', - sources : 'src/old_main.cpp', - include_directories: include_dir, - link_with: claplusplus_lib, - install : false -) +# executable('app', +# sources : 'src/old_main.cpp', +# include_directories: include_dir, +# link_with: claplusplus_lib, +# install : false +# ) From 6513976b163d5ca02c1d0b5a8dca7f3d334ee8ba Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:29:32 +0200 Subject: [PATCH 04/22] BROKEN misc(Arg): better private member names --- include/Arg.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index f988cfb..be21dd2 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -36,16 +36,15 @@ class Arg { friend class ClapParser; std::string name_; - std::string short_; - std::string long_; + std::string short_name_; + std::string long_name_; std::string help_; - bool required_; + bool is_required_; bool takes_value_; - bool has_env_; std::string env_name_; bool try_env_; - std::string try_env_name_; - std::string default_; + // std::string try_env_name_; + std::string default_value_; std::optional value_; [[nodiscard]] bool has_default() const; From d076ad851e27093ccd49d517956d9d6cbb677cc3 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:34:32 +0200 Subject: [PATCH 05/22] BROKEN feat(Arg): getters and setters a A LOT better + more usable --- include/Arg.hpp | 117 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 13 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index be21dd2..2093aa8 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -20,15 +20,6 @@ class Arg { Arg& default_value(const std::string& default_val); Arg& from_env(const char* env_var_name); Arg& try_env(); - void set_try_env_name(const std::string& s); - - // Getters - [[nodiscard]] const std::string& short_name() const; - [[nodiscard]] const std::string& long_name() const; - [[nodiscard]] const std::string& help() const; - [[nodiscard]] const std::string& default_value() const; - [[nodiscard]] bool is_required() const; - [[nodiscard]] bool requires_value() const; friend std::ostream& operator<<(std::ostream& os, const Arg& arg); @@ -47,8 +38,108 @@ class Arg { std::string default_value_; std::optional value_; - [[nodiscard]] bool has_default() const; - [[nodiscard]] bool has_env() const; - [[nodiscard]] bool takes_value() const; - [[nodiscard]] const std::string& name() const; + // ----| 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; + } + + // takes_value_ + [[nodiscard]] inline bool get__takes_value() const { + return this->takes_value_; + } + inline void set__takes_value(const bool& takes_value) { + this->takes_value_ = takes_value; + } + + // 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; + } + + // try_env_ + [[nodiscard]] inline bool get__try_env() const { + return this->try_env_; + } + inline void set__try_env(const bool& try_env) { + this->try_env_ = try_env; + } + + // try_env_name_ + [[nodiscard]] inline const std::string get__try_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); }); + 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(); + } + + // has_default_ + [[nodiscard]] inline bool has_default() const { + return !this->default_value_.empty(); + } + + // has_value_ + [[nodiscard]] inline bool has_value() const { + return !this->value_.has_value(); + } + }; From f68891845858a1035eaf53bb232829838fceeb8c Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:36:37 +0200 Subject: [PATCH 06/22] BROKEN misc(Arg): better nameing in source file --- src/Arg.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Arg.cpp b/src/Arg.cpp index 0f08f6a..3bd2626 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -10,24 +10,24 @@ Arg::Arg(std::string name) : name_(std::move(name)), long_(name_), required_(false), takes_value_(true), has_env_(false), try_env_(false), value_(std::nullopt) {} // Setters -Arg& Arg::short_name(const std::string& s) { - short_ = s; +Arg& Arg::short_name(const std::string& short_name) { + short_name_ = short_name; return *this; } -Arg& Arg::help(const std::string& h) { - help_ = h; +Arg& Arg::help(const std::string& help) { + help_ = help; return *this; } Arg& Arg::required(bool is_required) { - required_ = is_required; + is_required_ = is_required; return *this; } -Arg& Arg::takes_value(bool takes) { - takes_value_ = takes; +Arg& Arg::takes_value(bool takes_value) { + takes_value_ = takes_value; return *this; } -Arg& Arg::default_value(const std::string& default_val) { - default_ = default_val; +Arg& Arg::default_value(const std::string& default_value) { + default_value_ = default_value; return *this; } bool Arg::requires_value() const { return takes_value_; } From ef7c2f8ea9b64f684ca2f3f8c268e05a8cfd06bd Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:38:30 +0200 Subject: [PATCH 07/22] BROKEN fix(Arg): adjust '<<' overload for new privates --- src/Arg.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Arg.cpp b/src/Arg.cpp index 3bd2626..c8e37a1 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -64,17 +64,17 @@ const std::string& Arg::default_value() const { return default_; } std::ostream& operator<<(std::ostream& os, const Arg& arg) { os << "Arg {\n" << " name: \"" << arg.name_ << "\",\n" - << " short: \"" << arg.short_ << "\",\n" - << " long: \"" << arg.long_ << "\",\n" + << " short: \"" << arg.short_name_ << "\",\n" + << " long: \"" << arg.long_name_ << "\",\n" << " help: \"" << arg.help_ << "\",\n" - << " required: " << std::boolalpha << arg.required_ << ",\n" + << " required: " << std::boolalpha << arg.is_required_ << ",\n" << " takes_value: " << std::boolalpha << arg.takes_value_ << ",\n" - << " default: \"" << arg.default_ << "\",\n" + << " default: \"" << arg.default_value_ << "\",\n" << " value: "; if (arg.value_) os << "\"" << arg.value_.value() << "\""; else - os << "nullopt"; + os << "std::nullopt"; os << "\n}"; return os; } From 57eaf45adfb7e83ebb6d685795f1427c70c35727 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:40:40 +0200 Subject: [PATCH 08/22] BROKEN fix(Arg): removed old getter/setter defs --- src/Arg.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/Arg.cpp b/src/Arg.cpp index c8e37a1..b08ce90 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -30,9 +30,7 @@ Arg& Arg::default_value(const std::string& default_value) { default_value_ = default_value; return *this; } -bool Arg::requires_value() const { return takes_value_; } Arg& Arg::from_env(const char* env_var_name) { - this->has_env_ = true; 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); @@ -50,17 +48,6 @@ Arg& Arg::try_env() { return *this; }; -// Getters -const std::string& Arg::name() const { return name_; } -const std::string& Arg::short_name() const { return short_; } -const std::string& Arg::long_name() const { return long_; } -const std::string& Arg::help() const { return help_; } -bool Arg::is_required() const { return required_; } -bool Arg::takes_value() const { return takes_value_; } -bool Arg::has_default() const { return !default_.empty(); } -bool Arg::has_env() const { return has_env_; } -const std::string& Arg::default_value() const { return default_; } - std::ostream& operator<<(std::ostream& os, const Arg& arg) { os << "Arg {\n" << " name: \"" << arg.name_ << "\",\n" @@ -78,7 +65,3 @@ std::ostream& operator<<(std::ostream& os, const Arg& arg) { os << "\n}"; return os; } - -void Arg::set_try_env_name(const std::string& s){ - this->try_env_name_ = s; -} From 3403cb5cddcb84ef71a98ec64fdaf557ca63c75e Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:42:23 +0200 Subject: [PATCH 09/22] BROKEN fix(Arg): remove unneeded redeclaration --- include/Arg.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 2093aa8..3ea6761 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -6,8 +6,6 @@ #include #include -class ClapParser; - class Arg { public: explicit Arg(std::string name); From 84ddf8976456b8b8d8e08fe368e00f3ddb0478f5 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:43:25 +0200 Subject: [PATCH 10/22] BROKEN misc(Arg): use more explicit names again and even more --- include/Arg.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 3ea6761..3ced495 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -10,8 +10,8 @@ class Arg { public: explicit Arg(std::string name); - Arg& short_name(const std::string& s); - Arg& long_name(const std::string& l); + Arg& short_name(const std::string& short_name); + Arg& long_name(const std::string& long_name); Arg& help(const std::string& help); Arg& required(bool is_required); Arg& takes_value(bool takes); From a87a18f7353163c571f16fee35e737f15f0f3317 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:44:17 +0200 Subject: [PATCH 11/22] BROKEN fix(Arg): reworked the constructor --- include/Arg.hpp | 2 +- src/Arg.cpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 3ced495..285f06a 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -8,7 +8,7 @@ class Arg { public: - explicit Arg(std::string name); + Arg(const std::string& name); Arg& short_name(const std::string& short_name); Arg& long_name(const std::string& long_name); diff --git a/src/Arg.cpp b/src/Arg.cpp index b08ce90..21305cd 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -7,7 +7,18 @@ #include "../include/Arg.hpp" #include "../src/utils.cpp" -Arg::Arg(std::string name) : name_(std::move(name)), long_(name_), required_(false), takes_value_(true), has_env_(false), try_env_(false), value_(std::nullopt) {} +Arg::Arg(const std::string& name) : + name_(name), + short_name_(""), + long_name_(this->name_), + help_(""), + is_required_(false), + takes_value_(true), + env_name_(""), + try_env_(false), + default_value_(""), + value_(std::nullopt) +{} // Setters Arg& Arg::short_name(const std::string& short_name) { From 4d4b668ad0c1d9ccf5b9f002ceccede493fe8f2b Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:45:15 +0200 Subject: [PATCH 12/22] BROKEN fix(Arg): clean up includes --- include/Arg.hpp | 3 +-- src/Arg.cpp | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index 285f06a..dc9e11f 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -1,8 +1,7 @@ #pragma once +#include #include #include -#include -#include #include #include diff --git a/src/Arg.cpp b/src/Arg.cpp index 21305cd..86699bf 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -1,11 +1,9 @@ +#include "Arg.hpp" + #include #include #include #include -#include - -#include "../include/Arg.hpp" -#include "../src/utils.cpp" Arg::Arg(const std::string& name) : name_(name), From ac7ca5b926174362484b5b8111c5507ef3a768c9 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 18:47:06 +0200 Subject: [PATCH 13/22] BROKEN feat(utils): move src/utils.cpp -> include/utils.hpp --- src/utils.cpp => include/utils.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) rename src/utils.cpp => include/utils.hpp (69%) diff --git a/src/utils.cpp b/include/utils.hpp similarity index 69% rename from src/utils.cpp rename to include/utils.hpp index 95a11b1..f8c3993 100644 --- a/src/utils.cpp +++ b/include/utils.hpp @@ -1,40 +1,42 @@ +#pragma once + #include #include #include #include template -T ok_or(std::optional opt, E&& err) { +inline T ok_or(std::optional opt, E&& err) { if (!opt) std::forward(err)(); return *opt; } template -T ok_or_throw_str(std::optional opt, const std::string& err) { +inline T ok_or_throw_str(std::optional opt, const std::string& err) { if (!opt) throw std::runtime_error(err); return *opt; } template -T ptr_ok_or_throw_str(T pointer, const std::string& err) { +inline T ptr_ok_or_throw_str(T pointer, const std::string& err) { if (!pointer) throw std::runtime_error(err); return pointer; } template -E ptr_unwrap_or(P pointer, const E 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 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 return oss.str(); } -const std::string PROGRAM_NAME() { +inline const std::string PROGRAM_NAME() { const std::string& raw_program_name = program_invocation_name; return raw_program_name.substr(raw_program_name.rfind('/') + 1); } From 4f46a229cb41f3d1f412f96a73d836ce4efc8477 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 19:03:59 +0200 Subject: [PATCH 14/22] BROKEN fix: cleaned all includes in all files --- include/Arg.hpp | 3 +++ include/Parser.hpp | 3 ++- src/Parser.cpp | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index dc9e11f..b13ae2b 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -1,4 +1,7 @@ #pragma once + +#include "utils.hpp" + #include #include #include diff --git a/include/Parser.hpp b/include/Parser.hpp index fec0746..7d8d88e 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -1,9 +1,10 @@ #pragma once + #include "Arg.hpp" + #include #include #include -#include #include #include #include diff --git a/src/Parser.cpp b/src/Parser.cpp index 2f09099..230185b 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -1,8 +1,9 @@ -#include "../include/Parser.hpp" +#include "Parser.hpp" #include "Arg.hpp" +#include "utils.hpp" + #include #include -#include #include #include #include From 078905f74786bb31f6ea9b257c03c66f8c64e23a Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 19:06:30 +0200 Subject: [PATCH 15/22] BROKEN misc(Parser): added a const ref, removed unneeded --- include/Parser.hpp | 2 +- src/Parser.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 7d8d88e..3434aa1 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -12,7 +12,7 @@ class ClapParser { public: void add_arg(const Arg& arg); - void parse(int argc, char* argv[]); + void parse(const int& argc, char* argv[]); void print_help() const; template std::optional get_one_as(const std::string& name) const; diff --git a/src/Parser.cpp b/src/Parser.cpp index 230185b..1d2695d 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -8,8 +8,7 @@ #include #include -void ClapParser::parse(int argc, char* argv[]) { - program_name_ = argv[0]; +void ClapParser::parse(const int& argc, char* argv[]) { std::vector args(argv + 1, argv + argc); std::unordered_set args_with_values; From ee66d93280a480d4d4cc83f8dece12b75444934e Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 19:41:29 +0200 Subject: [PATCH 16/22] BROKEN fix(Parser): using new arg getters/setters, we gettin' there! --- src/Parser.cpp | 62 ++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/src/Parser.cpp b/src/Parser.cpp index 1d2695d..60e595c 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -19,9 +19,9 @@ void ClapParser::parse(const int& argc, char* argv[]) { // Validate all arguments that need values received them for (const auto& arg : args_) { - if (arg.takes_value() && args_with_values.count(arg.name()) == 0) { - if (arg.is_required() && !arg.has_default()) { - throw std::runtime_error("argument '" + arg.name() + "' requires a value"); + if (arg.get__takes_value() && args_with_values.count(arg.get__name()) == 0) { + if (arg.get__is_required() && !arg.has_default()) { + throw std::runtime_error("argument '" + arg.get__name() + "' requires a value"); } } } @@ -52,15 +52,13 @@ void ClapParser::check_env() { std::string program_name = this->program_name_.substr(this->program_name_.rfind('/') + 1); std::string env_name = program_name + '_' + arg.name(); std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); }); - // std::cerr << env_name << "\n"; - arg.set_try_env_name(env_name); auto value_from_env = std::getenv(env_name.c_str()); if (value_from_env) { - values_[arg.name()] = value_from_env; + values_[arg.get__name()] = value_from_env; } } if (arg.has_env() && arg.value_.has_value()) { - values_[arg.name()] = arg.value_.value(); + values_[arg.get__name()] = arg.value_.value(); } } }; @@ -88,8 +86,8 @@ void ClapParser::check_env() { void ClapParser::check_required_args() { for (const auto& arg : args_) { - if (arg.is_required() && values_.find(arg.name()) == values_.end()) { - throw std::runtime_error("missing required argument: " + arg.name()); + if (arg.get__is_required() && values_.find(arg.get__name()) == values_.end()) { + throw std::runtime_error("missing required argument: " + arg.get__name()); } } } @@ -105,10 +103,10 @@ size_t ClapParser::handle_long_option(const std::string& token, const std::vecto throw std::runtime_error("unknown option: " + token); } - if (arg->takes_value()) { + if (arg->get__takes_value()) { i = handle_option_with_value(arg, args, i, token); } else { - values_[arg->name()] = true; // Boolean flag + values_[arg->get__name()] = true; // Boolean flag } return i; @@ -125,10 +123,10 @@ size_t ClapParser::handle_short_option(const std::string& token, const std::vect throw std::runtime_error("unknown option: " + token); } - if (arg->takes_value()) { + if (arg->get__takes_value()) { i = handle_option_with_value(arg, args, i, token); } else { - values_[arg->name()] = true; // Boolean flag + values_[arg->get__name()] = true; // Boolean flag } return i; @@ -138,12 +136,12 @@ size_t ClapParser::handle_option_with_value(const Arg* arg, const std::vectorname()] = std::string(args[i + 1]); + values_[arg->get__name()] = std::string(args[i + 1]); return i + 1; // Skip the value in the next iteration } if (arg->has_default()) { // Use default value - values_[arg->name()] = std::string(arg->default_value()); + values_[arg->get__name()] = std::string(arg->get__default_value()); } else { throw std::runtime_error("option '" + token + "' requires a value but none was provided"); } @@ -152,11 +150,11 @@ size_t ClapParser::handle_option_with_value(const Arg* arg, const std::vector ClapParser::get_positional_args() const { std::vector positional; for (const auto& arg : args_) { - if (arg.short_name().empty() && arg.long_name().empty()) { + if (arg.get__short_name().empty() && arg.get__long_name().empty()) { positional.push_back(arg); } } @@ -232,8 +230,8 @@ std::vector ClapParser::get_positional_args() const { void ClapParser::apply_defaults() { for (const auto& arg : args_) { - if (values_.find(arg.name()) == values_.end() && arg.has_default()) { - values_[arg.name()] = std::string(arg.default_value()); + if (values_.find(arg.get__name()) == values_.end() && arg.has_default()) { + values_[arg.get__name()] = std::string(arg.get__default_value()); } } } From 11d7ca27297430bc80730a571b6cf527a6123a2d Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 19:42:32 +0200 Subject: [PATCH 17/22] fix(Parser): switch to PROGRAM_NAME() util, all fixes done maybe? --- src/Parser.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Parser.cpp b/src/Parser.cpp index 60e595c..722eec4 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -49,8 +49,7 @@ void ClapParser::parse_options(const std::vector& args) { void ClapParser::check_env() { for (auto& arg : args_) { if (arg.try_env_) { - std::string program_name = this->program_name_.substr(this->program_name_.rfind('/') + 1); - std::string env_name = program_name + '_' + arg.name(); + std::string env_name = PROGRAM_NAME() + '_' + arg.get__name(); std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); }); auto value_from_env = std::getenv(env_name.c_str()); if (value_from_env) { @@ -169,7 +168,7 @@ void ClapParser::handle_missing_positional(const Arg& arg) { } void ClapParser::print_help() const { - std::cout << "Usage: " << program_name_ << " [OPTIONS]"; + std::cout << "Usage: " << PROGRAM_NAME() << " [OPTIONS]"; auto positionals = get_positional_args(); for (const auto& pos : positionals) { std::cout << " [" << pos.get__name() << "]"; @@ -240,7 +239,7 @@ bool ClapParser::has(const std::string& name) const { return values_.find(name) std::ostream& operator<<(std::ostream& os, const ClapParser& parser) { os << "ClapParser {\n"; - os << " program_name: \"" << parser.program_name_ << "\",\n"; + os << " program_name: \"" << PROGRAM_NAME() << "\",\n"; os << " args: [\n"; for (const auto& arg : parser.args_) { From ea9cf88b459f6c7269c52a6d478a44c8dd15fc67 Mon Sep 17 00:00:00 2001 From: csboo Date: Tue, 15 Apr 2025 19:46:46 +0200 Subject: [PATCH 18/22] misc(Arg): rename try_env -> auto_env --- include/Arg.hpp | 20 ++++++++++---------- src/Arg.cpp | 6 +++--- src/Parser.cpp | 6 +++--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index b13ae2b..be8d3f4 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -19,7 +19,7 @@ class Arg { Arg& takes_value(bool takes); Arg& default_value(const std::string& default_val); Arg& from_env(const char* env_var_name); - Arg& try_env(); + Arg& auto_env(); friend std::ostream& operator<<(std::ostream& os, const Arg& arg); @@ -33,8 +33,8 @@ class Arg { bool is_required_; bool takes_value_; std::string env_name_; - bool try_env_; - // std::string try_env_name_; + bool auto_env_; + // std::string auto_env_name_; std::string default_value_; std::optional value_; @@ -95,16 +95,16 @@ class Arg { this->env_name_ = env_name; } - // try_env_ - [[nodiscard]] inline bool get__try_env() const { - return this->try_env_; + // auto_env_ + [[nodiscard]] inline bool get__auto_env() const { + return this->auto_env_; } - inline void set__try_env(const bool& try_env) { - this->try_env_ = try_env; + inline void set__auto_env(const bool& auto_env) { + this->auto_env_ = auto_env; } - // try_env_name_ - [[nodiscard]] inline const std::string get__try_env_name() const { + // 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); }); return env_name; diff --git a/src/Arg.cpp b/src/Arg.cpp index 86699bf..2958857 100644 --- a/src/Arg.cpp +++ b/src/Arg.cpp @@ -13,7 +13,7 @@ Arg::Arg(const std::string& name) : is_required_(false), takes_value_(true), env_name_(""), - try_env_(false), + auto_env_(false), default_value_(""), value_(std::nullopt) {} @@ -52,8 +52,8 @@ Arg& Arg::from_env(const char* env_var_name) { } return *this; }; -Arg& Arg::try_env() { - this -> try_env_ = true; +Arg& Arg::auto_env() { + this -> auto_env_ = true; return *this; }; diff --git a/src/Parser.cpp b/src/Parser.cpp index 722eec4..07af2d6 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -48,7 +48,7 @@ void ClapParser::parse_options(const std::vector& args) { void ClapParser::check_env() { for (auto& arg : args_) { - if (arg.try_env_) { + if (arg.auto_env_) { std::string env_name = PROGRAM_NAME() + '_' + arg.get__name(); std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); }); auto value_from_env = std::getenv(env_name.c_str()); @@ -185,8 +185,8 @@ void ClapParser::print_help() const { if (arg.has_env()) { std::cout << " [env: " << arg.get__env_name() << "]"; } - if (arg.get__try_env()) { - std::cout << " [def.env: " << arg.get__try_env_name() << "]"; + if (arg.get__auto_env()) { + std::cout << " [def.env: " << arg.get__auto_env_name() << "]"; } std::cout << "\n"; } From 3006b4c2e11e56c14ac6b1a7c3c59fcbe1e87fa1 Mon Sep 17 00:00:00 2001 From: csboo Date: Wed, 16 Apr 2025 11:28:23 +0200 Subject: [PATCH 19/22] feat(example): WIP added example --- meson.build | 2 +- src/example.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/example.cpp diff --git a/meson.build b/meson.build index ca4c312..c949920 100644 --- a/meson.build +++ b/meson.build @@ -15,7 +15,7 @@ include_dir = include_directories('include') # Build executable directly from all necessary sources executable('claPlusPlus', sources: [ - 'src/old_main.cpp', + 'src/example.cpp', 'src/Arg.cpp', 'src/Parser.cpp' ], diff --git a/src/example.cpp b/src/example.cpp new file mode 100644 index 0000000..5a48700 --- /dev/null +++ b/src/example.cpp @@ -0,0 +1,39 @@ +#include "Parser.hpp" +#include "Arg.hpp" +#include "utils.hpp" + +#include +#include + +void run(const ClapParser& parsed_args); + +int main(const int argc, char* argv[]) { + ClapParser arg_parser; + auto num1 = Arg("num1").from_env("ASDF").auto_env(); + // 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 test("testarg"); + arg_parser.add_arg(test); + + try { + arg_parser.parse(argc, argv); + // std::cerr << arg_parser; + run(arg_parser); + } + catch (const std::exception& e) { + std::cout << "\n\n\nerror: " << e.what() << "\n\n\n"; + // arg_parser.print_help(); + } +} + +void run(const ClapParser& parsed_args) { + auto num1 = ok_or_throw_str(parsed_args.get_one_as("num1"), "num1 not defined"); + auto num2 = ok_or_throw_str(parsed_args.get_one_as("num2"), "num2 not defined"); + + std::cout << "num1: " << num1 << '\n'; + std::cout.precision(5); + std::cout << std::fixed << "num2: " << num2 << '\n'; +} From b70152a9e847a2cf934d0a489867a82c15f1e98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeromos=20Kov=C3=A1cs?= Date: Wed, 16 Apr 2025 15:31:14 +0200 Subject: [PATCH 20/22] refactor(parser): add `this` where possible --- src/Parser.cpp | 68 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Parser.cpp b/src/Parser.cpp index 07af2d6..c53d545 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -12,13 +12,13 @@ void ClapParser::parse(const int& argc, char* argv[]) { std::vector args(argv + 1, argv + argc); std::unordered_set args_with_values; - apply_defaults(); - check_env(); - parse_options(args); - // parse_positional_args(args); + this->apply_defaults(); + this->check_env(); + this->parse_options(args); + // this->parse_positional_args(args); // Validate all arguments that need values received them - for (const auto& arg : args_) { + for (const auto& arg : this->args_) { if (arg.get__takes_value() && args_with_values.count(arg.get__name()) == 0) { if (arg.get__is_required() && !arg.has_default()) { throw std::runtime_error("argument '" + arg.get__name() + "' requires a value"); @@ -26,19 +26,19 @@ void ClapParser::parse(const int& argc, char* argv[]) { } } - check_required_args(); + this->check_required_args(); } -void ClapParser::add_arg(const Arg& arg) { args_.push_back(arg); } +void ClapParser::add_arg(const Arg& arg) { this->args_.push_back(arg); } void ClapParser::parse_options(const std::vector& args) { for (size_t i = 0; i < args.size(); ++i) { const std::string& token = args[i]; - if (is_long_option(token)) { - i = handle_long_option(token, args, i); - } else if (is_short_option(token)) { - i = handle_short_option(token, args, i); + if (this->is_long_option(token)) { + i = this->handle_long_option(token, args, i); + } else if (this->is_short_option(token)) { + i = this->handle_short_option(token, args, i); } else { // Positional arguments are handled separately break; @@ -47,17 +47,17 @@ void ClapParser::parse_options(const std::vector& args) { } void ClapParser::check_env() { - for (auto& arg : args_) { + for (auto& arg : this->args_) { if (arg.auto_env_) { std::string env_name = PROGRAM_NAME() + '_' + arg.get__name(); std::transform(env_name.begin(), env_name.end(), env_name.begin(), [](const unsigned char& c) { return std::toupper(c); }); auto value_from_env = std::getenv(env_name.c_str()); if (value_from_env) { - values_[arg.get__name()] = value_from_env; + this->values_[arg.get__name()] = value_from_env; } } if (arg.has_env() && arg.value_.has_value()) { - values_[arg.get__name()] = arg.value_.value(); + this->values_[arg.get__name()] = arg.value_.value(); } } }; @@ -84,8 +84,8 @@ void ClapParser::check_env() { // } void ClapParser::check_required_args() { - for (const auto& arg : args_) { - if (arg.get__is_required() && values_.find(arg.get__name()) == values_.end()) { + for (const auto& arg : this->args_) { + if (arg.get__is_required() && this->values_.find(arg.get__name()) == this->values_.end()) { throw std::runtime_error("missing required argument: " + arg.get__name()); } } @@ -94,18 +94,18 @@ void ClapParser::check_required_args() { size_t ClapParser::handle_long_option(const std::string& token, const std::vector& args, size_t i) { std::string opt_name = token.substr(2); if (opt_name == "help") { - print_help(); + this->print_help(); exit(0); } - const Arg* arg = find_option(opt_name); + const Arg* arg = this->find_option(opt_name); if (arg == nullptr) { throw std::runtime_error("unknown option: " + token); } if (arg->get__takes_value()) { - i = handle_option_with_value(arg, args, i, token); + i = this->handle_option_with_value(arg, args, i, token); } else { - values_[arg->get__name()] = true; // Boolean flag + this->values_[arg->get__name()] = true; // Boolean flag } return i; @@ -114,18 +114,18 @@ size_t ClapParser::handle_long_option(const std::string& token, const std::vecto size_t ClapParser::handle_short_option(const std::string& token, const std::vector& args, size_t i) { std::string opt_name = token.substr(1); if (opt_name == "h") { - print_help(); + this->print_help(); exit(0); } - const Arg* arg = find_option(opt_name); + const Arg* arg = this->find_option(opt_name); if (arg == nullptr) { throw std::runtime_error("unknown option: " + token); } if (arg->get__takes_value()) { - i = handle_option_with_value(arg, args, i, token); + i = this->handle_option_with_value(arg, args, i, token); } else { - values_[arg->get__name()] = true; // Boolean flag + this->values_[arg->get__name()] = true; // Boolean flag } return i; @@ -135,12 +135,12 @@ size_t ClapParser::handle_option_with_value(const Arg* arg, const std::vectorget__name()] = std::string(args[i + 1]); + this->values_[arg->get__name()] = std::string(args[i + 1]); return i + 1; // Skip the value in the next iteration } if (arg->has_default()) { // Use default value - values_[arg->get__name()] = std::string(arg->get__default_value()); + this->values_[arg->get__name()] = std::string(arg->get__default_value()); } else { throw std::runtime_error("option '" + token + "' requires a value but none was provided"); } @@ -153,7 +153,7 @@ void ClapParser::handle_missing_positional(const Arg& arg) { 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()); + this->values_[arg.get__name()] = std::string(arg.get__default_value()); } } @@ -169,13 +169,13 @@ void ClapParser::handle_missing_positional(const Arg& arg) { void ClapParser::print_help() const { std::cout << "Usage: " << PROGRAM_NAME() << " [OPTIONS]"; - auto positionals = get_positional_args(); + auto positionals = this->get_positional_args(); for (const auto& pos : positionals) { std::cout << " [" << pos.get__name() << "]"; } std::cout << "\n\nOptions:\n"; - for (const auto& arg : args_) { + for (const auto& arg : this->args_) { 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(); @@ -209,7 +209,7 @@ void ClapParser::print_help() const { // Helper methods const Arg* ClapParser::find_option(const std::string& name) const { - for (const auto& arg : args_) { + for (const auto& arg : this->args_) { if (arg.get__long_name() == name || arg.get__short_name() == name) { return &arg; } @@ -219,7 +219,7 @@ const Arg* ClapParser::find_option(const std::string& name) const { std::vector ClapParser::get_positional_args() const { std::vector positional; - for (const auto& arg : args_) { + for (const auto& arg : this->args_) { if (arg.get__short_name().empty() && arg.get__long_name().empty()) { positional.push_back(arg); } @@ -228,9 +228,9 @@ std::vector ClapParser::get_positional_args() const { } void ClapParser::apply_defaults() { - for (const auto& arg : args_) { - if (values_.find(arg.get__name()) == values_.end() && arg.has_default()) { - values_[arg.get__name()] = std::string(arg.get__default_value()); + for (const auto& arg : this->args_) { + if (this->values_.find(arg.get__name()) == this->values_.end() && arg.has_default()) { + this->values_[arg.get__name()] = std::string(arg.get__default_value()); } } } From 5425386bc379bf4c6aa67b2fb86ffb00f57e53e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeromos=20Kov=C3=A1cs?= Date: Wed, 16 Apr 2025 17:04:58 +0200 Subject: [PATCH 21/22] fix(parser): know what's static and what's const --- include/Parser.hpp | 12 ++++++------ src/Parser.cpp | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/Parser.hpp b/include/Parser.hpp index 3434aa1..0a1dd02 100644 --- a/include/Parser.hpp +++ b/include/Parser.hpp @@ -25,16 +25,16 @@ 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 inline bool is_option(const std::string& token); + static inline bool is_long_option(const std::string& token); + static inline bool is_short_option(const std::string& token); const Arg* find_option(const std::string& name) const; std::vector get_positional_args() const; void apply_defaults(); void parse_options(const std::vector& args); void parse_positional_args(const std::vector& args); - void check_required_args(); + void check_required_args() const; void check_env(); void handle_missing_positional(const Arg& arg); @@ -46,8 +46,8 @@ class ClapParser { }; template std::optional ClapParser::get_one_as(const std::string& name) const { - auto it = values_.find(name); - if (it == values_.end()) { + auto it = this->values_.find(name); + if (it == this->values_.end()) { return std::nullopt; } diff --git a/src/Parser.cpp b/src/Parser.cpp index c53d545..3db81cc 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -35,9 +35,9 @@ void ClapParser::parse_options(const std::vector& args) { for (size_t i = 0; i < args.size(); ++i) { const std::string& token = args[i]; - if (this->is_long_option(token)) { + if (ClapParser::is_long_option(token)) { i = this->handle_long_option(token, args, i); - } else if (this->is_short_option(token)) { + } else if (ClapParser::is_short_option(token)) { i = this->handle_short_option(token, args, i); } else { // Positional arguments are handled separately @@ -83,7 +83,7 @@ void ClapParser::check_env() { // } // } -void ClapParser::check_required_args() { +void ClapParser::check_required_args() const { for (const auto& arg : this->args_) { if (arg.get__is_required() && this->values_.find(arg.get__name()) == this->values_.end()) { throw std::runtime_error("missing required argument: " + arg.get__name()); @@ -157,13 +157,13 @@ void ClapParser::handle_missing_positional(const Arg& arg) { } } - inline bool ClapParser::is_option(const std::string& token) const { + inline 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) == "--"; } + inline 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 { + inline bool ClapParser::is_short_option(const std::string& token) { return token[0] == '-' && token.size() > 1 && token[1] != '-'; } From d6009e95e16bf790a3f20233046523bb711b6674 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 22/22] refactor: dry: `to_upper(string) in utils, other minor --- include/Arg.hpp | 2 +- include/utils.hpp | 7 ++++++- src/Parser.cpp | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/Arg.hpp b/include/Arg.hpp index be8d3f4..41aa96a 100644 --- a/include/Arg.hpp +++ b/include/Arg.hpp @@ -106,7 +106,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 f8c3993..61f08f7 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) { @@ -36,6 +37,10 @@ template inline std::string concat(Args&&... args) { return oss.str(); } +inline void to_upper(std::string &s) { + std::ranges::transform(s, s.begin(), [](const unsigned char& c) { return std::toupper(c); }); +} + inline const std::string PROGRAM_NAME() { const std::string& raw_program_name = program_invocation_name; return raw_program_name.substr(raw_program_name.rfind('/') + 1); diff --git a/src/Parser.cpp b/src/Parser.cpp index 3db81cc..47b7c70 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -19,7 +19,7 @@ void ClapParser::parse(const int& argc, char* argv[]) { // Validate all arguments that need values received them for (const auto& arg : this->args_) { - if (arg.get__takes_value() && args_with_values.count(arg.get__name()) == 0) { + if (arg.get__takes_value() && !args_with_values.contains(arg.get__name())) { if (arg.get__is_required() && !arg.has_default()) { throw std::runtime_error("argument '" + arg.get__name() + "' requires a value"); } @@ -50,7 +50,7 @@ void ClapParser::check_env() { for (auto& arg : this->args_) { if (arg.auto_env_) { std::string env_name = 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) { this->values_[arg.get__name()] = value_from_env;