chore: format

This commit is contained in:
2026-05-07 17:49:47 +04:00
parent 2b2a0df933
commit 21ca1f638b
19 changed files with 667 additions and 1357 deletions

View File

@@ -1,8 +1,8 @@
#include <argparse> #include <argparse>
#include <gnu_argument_convention.hpp> #include <gnu_argument_convention.hpp>
#include <macros.h>
#include <iostream> #include <iostream>
#include <macros.h>
#include <parser_v2.hpp> #include <parser_v2.hpp>
#include <string> #include <string>
#include <traits.hpp> #include <traits.hpp>
@@ -11,107 +11,94 @@ using argument = argument_parser::builder::argument<>;
using argument_parser::parsing_traits::hint_type; using argument_parser::parsing_traits::hint_type;
auto echo(std::string const& s) -> void { auto echo(std::string const &s) -> void {
std::cout << s << '\n'; std::cout << s << '\n';
} }
using namespace argument_parser::parsing_traits; using namespace argument_parser::parsing_traits;
constexpr hint_type vector_purpose_hint = "vector of "; constexpr hint_type vector_purpose_hint = "vector of ";
template<typename T> template <typename T> class parser_trait<std::vector<T>> {
class parser_trait<std::vector<T>> { public:
public: static std::vector<T> parse(std::string const &s) {
static std::vector<T> parse(std::string const& s) { std::vector<T> result;
std::vector<T> result; std::stringstream ss(s);
std::stringstream ss(s);
for (std::string line; std::getline(ss, line, ',');) { for (std::string line; std::getline(ss, line, ',');) {
T item = parser_trait<T>::parse(line); T item = parser_trait<T>::parse(line);
result.push_back(item); result.push_back(item);
} }
return result; return result;
} }
ARGPARSE_TRAIT_FORMAT_HINT = concat< ARGPARSE_TRAIT_FORMAT_HINT = concat<hint_provider<&parser_trait<T>::format_hint>, hint_provider<&comma>,
hint_provider<&parser_trait<T>::format_hint>, hint_provider<&parser_trait<T>::format_hint>>;
hint_provider<&comma>,
hint_provider<&parser_trait<T>::format_hint>
>;
ARGPARSE_TRAIT_PURPOSE_HINT = concat< ARGPARSE_TRAIT_PURPOSE_HINT =
hint_provider<&vector_purpose_hint>, concat<hint_provider<&vector_purpose_hint>, hint_provider<&parser_trait<T>::purpose_hint>>;
hint_provider<&parser_trait<T>::purpose_hint>
>;
}; };
using namespace argument_parser::v2::flags; using namespace argument_parser::v2::flags;
auto main() -> int { auto main() -> int {
argument_parser::v2::parser parser(false); argument_parser::v2::parser parser(false);
argument::start() argument::start()
.positional("count") .positional("count")
.position(1) .position(1)
.help_text("How many times to repeat the action.") .help_text("How many times to repeat the action.")
.action<int>([](int const& count) { .action<int>([](int const &count) { std::cout << "count action configured for " << count << '\n'; })
std::cout << "count action configured for " << count << '\n'; .build(parser);
})
.build(parser);
int captured_value = 0; int captured_value = 0;
argument::start() argument::start()
.long_argument("threshold") .long_argument("threshold")
.help_text("Store the parsed value through a reference.") .help_text("Store the parsed value through a reference.")
.reference(captured_value) .reference(captured_value)
.build(parser); .build(parser);
argument::start() argument::start()
.positional("captured") .positional("captured")
.position(0) .position(0)
.help_text("Store the parsed value through a reference.") .help_text("Store the parsed value through a reference.")
.reference(captured_value) .reference(captured_value)
.build(parser); .build(parser);
// parser.add_argument<int>({ // parser.add_argument<int>({
// {ShortArgument, "c"}, // {ShortArgument, "c"},
// {HelpText, "capture count"}, // {HelpText, "capture count"},
// {Reference, &captured_value}, // {Reference, &captured_value},
// }); // });
argument::start() argument::start().short_argument("q").help_text("Store a boolean flag.").build(parser);
.short_argument("q")
.help_text("Store a boolean flag.")
.build(parser);
// argument::start() // argument::start()
// .long_argument("regex") // .long_argument("regex")
// .help_text("Store a regex value.") // .help_text("Store a regex value.")
// .store<std::optional<std::regex>>() // .store<std::optional<std::regex>>()
// .build(parser); // .build(parser);
argument::start() argument::start()
.short_argument("e") .short_argument("e")
.long_argument("echo") .long_argument("echo")
.help_text("Echo the parsed value.") .help_text("Echo the parsed value.")
.action(echo) .action(echo)
.build(parser); .build(parser);
argument::start() argument::start()
.long_argument("vecstr") .long_argument("vecstr")
.short_argument("vs") .short_argument("vs")
.action<std::vector<int>>([](std::vector<int> const& vecstr) { .action<std::vector<int>>([](std::vector<int> const &vecstr) {
for (auto const& str : vecstr) { for (auto const &str : vecstr) {
std::cout << str << '\n'; std::cout << str << '\n';
} }
}) })
.build(parser); .build(parser);
parser.handle_arguments({ parser.handle_arguments({&argument_parser::conventions::gnu_argument_convention});
&argument_parser::conventions::gnu_argument_convention
});
std::cout << "captured value: " << captured_value << '\n'; std::cout << "captured value: " << captured_value << '\n';
return 0; return 0;
} }

View File

@@ -1,10 +1,10 @@
#pragma once #pragma once
#ifndef ARGPARSE_HPP #ifndef ARGPARSE_HPP
#define ARGPARSE_HPP #define ARGPARSE_HPP
#include "macros.h"
#include <argument_builder.hpp>
#include <argument_parser.hpp> #include <argument_parser.hpp>
#include <parser_v2.hpp> #include <parser_v2.hpp>
#include <argument_builder.hpp>
#include "macros.h"
#ifdef __linux__ #ifdef __linux__
#include <linux_parser.hpp> #include <linux_parser.hpp>

View File

@@ -20,8 +20,8 @@ namespace argument_parser::conventions {
virtual std::string name() const = 0; virtual std::string name() const = 0;
virtual std::string short_prec() const = 0; virtual std::string short_prec() const = 0;
virtual std::string long_prec() const = 0; virtual std::string long_prec() const = 0;
virtual std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg, virtual std::pair<std::string, std::string>
bool requires_value) const = 0; make_help_text(std::string const &short_arg, std::string const &long_arg, bool requires_value) const = 0;
virtual std::vector<convention_features> get_features() const = 0; virtual std::vector<convention_features> get_features() const = 0;
protected: protected:
@@ -36,4 +36,4 @@ namespace argument_parser::conventions::helpers {
std::string to_upper(std::string s); std::string to_upper(std::string s);
} // namespace argument_parser::conventions::helpers } // namespace argument_parser::conventions::helpers
#endif #endif

View File

@@ -15,7 +15,7 @@ namespace argument_parser::conventions::implementations {
std::string short_prec() const override; std::string short_prec() const override;
std::string long_prec() const override; std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg, std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override; bool requires_value) const override;
std::vector<convention_features> get_features() const override; std::vector<convention_features> get_features() const override;
static gnu_argument_convention instance; static gnu_argument_convention instance;
@@ -33,7 +33,7 @@ namespace argument_parser::conventions::implementations {
std::string short_prec() const override; std::string short_prec() const override;
std::string long_prec() const override; std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg, std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override; bool requires_value) const override;
std::vector<convention_features> get_features() const override; std::vector<convention_features> get_features() const override;
static gnu_equal_argument_convention instance; static gnu_equal_argument_convention instance;
@@ -53,4 +53,4 @@ namespace argument_parser::conventions {
implementations::gnu_equal_argument_convention::instance; implementations::gnu_equal_argument_convention::instance;
} // namespace argument_parser::conventions } // namespace argument_parser::conventions
#endif #endif

View File

@@ -19,7 +19,7 @@ namespace argument_parser::conventions::implementations {
std::string short_prec() const override; std::string short_prec() const override;
std::string long_prec() const override; std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg, std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override; bool requires_value) const override;
std::vector<convention_features> get_features() const override; std::vector<convention_features> get_features() const override;
static windows_argument_convention instance; static windows_argument_convention instance;
@@ -38,7 +38,7 @@ namespace argument_parser::conventions::implementations {
std::string short_prec() const override; std::string short_prec() const override;
std::string long_prec() const override; std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg, std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override; bool requires_value) const override;
std::vector<convention_features> get_features() const override; std::vector<convention_features> get_features() const override;
static windows_kv_argument_convention instance; static windows_kv_argument_convention instance;
@@ -60,4 +60,4 @@ namespace argument_parser::conventions {
implementations::windows_kv_argument_convention::instance; implementations::windows_kv_argument_convention::instance;
} // namespace argument_parser::conventions } // namespace argument_parser::conventions
#endif // WINDOWS_ARGUMENT_CONVENTION_HPP #endif // WINDOWS_ARGUMENT_CONVENTION_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -425,4 +425,4 @@ namespace argument_parser {
}; };
} // namespace argument_parser } // namespace argument_parser
#endif // ARGUMENT_PARSER_HPP #endif // ARGUMENT_PARSER_HPP

View File

@@ -31,7 +31,7 @@ namespace argument_parser {
void set_program_name(std::string const &program_name); void set_program_name(std::string const &program_name);
void set_parsed_arguments(std::vector<std::string> const &parsed_arguments); void set_parsed_arguments(std::vector<std::string> const &parsed_arguments);
}; };
} } // namespace v2
} // namespace argument_parser } // namespace argument_parser
#endif #endif

View File

@@ -15,7 +15,16 @@
#include <vector> #include <vector>
namespace argument_parser::v2 { namespace argument_parser::v2 {
enum class add_argument_flags { ShortArgument, LongArgument, Positional, Position, HelpText, Action, Required, Reference }; enum class add_argument_flags {
ShortArgument,
LongArgument,
Positional,
Position,
HelpText,
Action,
Required,
Reference
};
namespace flags { namespace flags {
constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument; constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument;
@@ -30,7 +39,7 @@ namespace argument_parser::v2 {
class base_parser : private argument_parser::base_parser { class base_parser : private argument_parser::base_parser {
public: public:
template <typename T> using typed_flag_value = std::variant<std::string, parametered_action<T>, bool, int, T*>; template <typename T> using typed_flag_value = std::variant<std::string, parametered_action<T>, bool, int, T *>;
using non_typed_flag_value = std::variant<std::string, non_parametered_action, bool, int>; using non_typed_flag_value = std::variant<std::string, non_parametered_action, bool, int>;
template <typename T> using typed_argument_pair = std::pair<add_argument_flags, typed_flag_value<T>>; template <typename T> using typed_argument_pair = std::pair<add_argument_flags, typed_flag_value<T>>;
@@ -148,21 +157,19 @@ namespace argument_parser::v2 {
if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) { if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) {
if (!IsTyped) { if (!IsTyped) {
throw std::logic_error("Reference argument must be typed"); throw std::logic_error("Reference argument must be typed");
} }
found_params[extended_add_argument_flags::Action] = true; found_params[extended_add_argument_flags::Action] = true;
if constexpr (!std::is_same_v<T, void>) { if constexpr (!std::is_same_v<T, void>) {
auto ref = get_or_throw<T*>(argument_pairs.at(add_argument_flags::Reference), "reference"); auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
if (action) { if (action) {
throw std::logic_error("Cannot use both action and reference for the same argument"); throw std::logic_error("Cannot use both action and reference for the same argument");
} else { } else {
action = helpers::make_parametered_action<T>([ref](T const& t) { action = helpers::make_parametered_action<T>([ref](T const &t) { *ref = t; }).clone();
*ref = t;
}).clone();
} }
} else { } else {
throw std::logic_error("Reference argument must not be void"); throw std::logic_error("Reference argument must not be void");
} }
} }
@@ -187,7 +194,8 @@ namespace argument_parser::v2 {
} }
} }
base::add_argument(short_arg, long_arg, help_text, *static_cast<ActionType *>(&(*action)), required); base::add_argument(short_arg, long_arg, help_text, *static_cast<ActionType *>(&(*action)),
required);
break; break;
case candidate_type::store_other: case candidate_type::store_other:
if (help_text.empty()) { if (help_text.empty()) {
@@ -261,24 +269,21 @@ namespace argument_parser::v2 {
if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) { if (argument_pairs.find(add_argument_flags::Reference) != argument_pairs.end()) {
if (!IsTyped) { if (!IsTyped) {
throw std::logic_error("Reference argument must be typed"); throw std::logic_error("Reference argument must be typed");
} }
if constexpr (!std::is_same_v<T, void>) { if constexpr (!std::is_same_v<T, void>) {
auto ref = get_or_throw<T*>(argument_pairs.at(add_argument_flags::Reference), "reference"); auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
if (action) { if (action) {
throw std::logic_error("Cannot use both action and reference for the same argument"); throw std::logic_error("Cannot use both action and reference for the same argument");
} else { } else {
action = helpers::make_parametered_action<T>([ref](T const& t) { action = helpers::make_parametered_action<T>([ref](T const &t) { *ref = t; }).clone();
*ref = t;
}).clone();
} }
} else { } else {
throw std::logic_error("Reference argument must not be void"); throw std::logic_error("Reference argument must not be void");
} }
} }
if (help_text.empty()) { if (help_text.empty()) {
if constexpr (IsTyped) { if constexpr (IsTyped) {
if constexpr (internal::sfinae::has_format_hint<parsing_traits::parser_trait<T>>::value && if constexpr (internal::sfinae::has_format_hint<parsing_traits::parser_trait<T>>::value &&
@@ -297,8 +302,8 @@ namespace argument_parser::v2 {
if constexpr (IsTyped) { if constexpr (IsTyped) {
if (action) { if (action) {
base::add_positional_argument<T>(positional_name, help_text, base::add_positional_argument<T>(positional_name, help_text, *static_cast<ActionType *>(&(*action)),
*static_cast<ActionType *>(&(*action)), required, position); required, position);
} else { } else {
base::template add_positional_argument<T>(positional_name, help_text, required, position); base::template add_positional_argument<T>(positional_name, help_text, required, position);
} }

View File

@@ -5,12 +5,12 @@
#include <string> #include <string>
namespace argument_parser::parsing_traits { namespace argument_parser::parsing_traits {
using hint_type = const char*; using hint_type = const char *;
template <typename T_> struct parser_trait { template <typename T_> struct parser_trait {
using type = T_; using type = T_;
static T_ parse(const std::string &input); static T_ parse(const std::string &input);
static bool validate(T_ const&); static bool validate(T_ const &);
static constexpr hint_type format_hint = "value"; static constexpr hint_type format_hint = "value";
static constexpr hint_type purpose_hint = "value"; static constexpr hint_type purpose_hint = "value";
@@ -51,40 +51,36 @@ namespace argument_parser::parsing_traits {
static constexpr hint_type purpose_hint = "double precision floating point number"; static constexpr hint_type purpose_hint = "double precision floating point number";
}; };
constexpr hint_type comma = ","; constexpr hint_type comma = ",";
template <const hint_type* PtrAddr> template <const hint_type *PtrAddr> struct hint_provider {
struct hint_provider { static constexpr hint_type value = *PtrAddr;
static constexpr hint_type value = *PtrAddr; };
};
template<typename... Providers> template <typename... Providers> struct joiner {
struct joiner { static constexpr auto get_combined() {
static constexpr auto get_combined() { constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0);
constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0);
std::array<char, total_len + 1> arr{}; std::array<char, total_len + 1> arr{};
size_t offset = 0; size_t offset = 0;
auto append = [&](hint_type s) { auto append = [&](hint_type s) {
std::string_view sv{s}; std::string_view sv{s};
for (char c : sv) arr[offset++] = c; for (char c : sv)
return 0; arr[offset++] = c;
}; return 0;
};
(append(Providers::value), ...); (append(Providers::value), ...);
arr[total_len] = '\0'; arr[total_len] = '\0';
return arr; return arr;
} }
static constexpr auto storage = get_combined(); static constexpr auto storage = get_combined();
static constexpr hint_type value = storage.data(); static constexpr hint_type value = storage.data();
}; };
template<typename... Providers> template <typename... Providers> constexpr hint_type concat = joiner<Providers...>::value;
constexpr hint_type concat = joiner<Providers...>::value;
} // namespace argument_parser::parsing_traits } // namespace argument_parser::parsing_traits

View File

@@ -1,254 +0,0 @@
#include "macros.h"
#include "traits.hpp"
#include <exception>
#include <memory>
#include <string>
#include <argparse>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <vector>
struct Point {
int x, y;
};
template <> struct argument_parser::parsing_traits::parser_trait<Point> {
static Point parse(const std::string &input) {
auto comma_pos = input.find(',');
if (comma_pos == std::string::npos) {
throw std::runtime_error("Invalid Point format. Expected 'x,y'.");
}
int x = std::stoi(input.substr(0, comma_pos));
int y = std::stoi(input.substr(comma_pos + 1));
return {x, y};
}
static bool validate(Point const& p) {
return p.x >= 0 && p.y >= 0;
}
ARGPARSE_TRAIT_FORMAT_HINT = "x,y";
ARGPARSE_TRAIT_PURPOSE_HINT = "coordinates";
};
template <> struct argument_parser::parsing_traits::parser_trait<std::regex> {
static std::regex parse(const std::string &input) {
return std::regex(input);
}
ARGPARSE_TRAIT_FORMAT_HINT = "regex";
ARGPARSE_TRAIT_PURPOSE_HINT = "regular expression";
};
template <> struct argument_parser::parsing_traits::parser_trait<std::vector<std::string>> {
static std::vector<std::string> parse(const std::string &input) {
std::vector<std::string> result;
std::stringstream ss{input};
std::string item;
while (std::getline(ss, item, ',')) {
result.push_back(item);
}
return result;
}
ARGPARSE_TRAIT_FORMAT_HINT = "string,string,string";
ARGPARSE_TRAIT_PURPOSE_HINT = "list of strings";
};
template <typename VT> struct argument_parser::parsing_traits::parser_trait<std::vector<VT>> {
static std::vector<VT> parse(const std::string &input) {
std::vector<VT> result;
std::stringstream ss{input};
std::string item;
while (std::getline(ss, item, ',')) {
result.push_back(argument_parser::parsing_traits::parser_trait<VT>::parse(item));
}
return result;
}
ARGPARSE_TRAIT_FORMAT_HINT = "VT,VT,VT";
ARGPARSE_TRAIT_PURPOSE_HINT = "list of VT";
};
const std::initializer_list<argument_parser::conventions::convention const *const> conventions = {
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::gnu_equal_argument_convention,
// &argument_parser::conventions::windows_argument_convention,
// &argument_parser::conventions::windows_equal_argument_convention
};
const auto echo = argument_parser::helpers::make_parametered_action<std::string>(
[](std::string const &text) { std::cout << text << std::endl; });
const auto echo_point = argument_parser::helpers::make_parametered_action<Point>(
[](Point const &point) { std::cout << "Point(" << point.x << ", " << point.y << ")" << std::endl; });
const auto cat = argument_parser::helpers::make_parametered_action<std::string>([](std::string const &file_name) {
std::ifstream file(file_name);
if (!file.is_open()) {
throw std::runtime_error("Could not open file");
}
std::string line;
while (std::getline(file, line)) {
std::cout << line << std::endl;
}
file.close();
});
auto grep(argument_parser::base_parser const &parser, std::string const &filename, std::regex const &pattern) {
if (filename.empty()) {
std::cerr << "Missing filename" << std::endl;
parser.display_help(conventions);
exit(-1);
}
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Could not open file: \"" << filename << '"' << std::endl;
exit(-1);
}
for (std::string line; std::getline(file, line);) {
if (std::regex_search(line, pattern)) {
std::cout << line << std::endl;
}
}
file.close();
}
void run_grep(argument_parser::base_parser const &parser) {
auto filename = parser.get_optional<std::string>("file");
auto pattern = parser.get_optional<std::regex>("grep");
if (filename && pattern) {
grep(parser, filename.value(), pattern.value());
} else if (filename) {
std::cerr << "Missing grep pattern" << std::endl;
parser.display_help(conventions);
exit(-1);
} else if (pattern) {
std::cerr << "Missing filename" << std::endl;
parser.display_help(conventions);
exit(-1);
}
}
void run_store_point(argument_parser::base_parser const &parser) {
auto point = parser.get_optional<Point>("store-point");
if (point) {
std::cout << "Point(" << point->x << ", " << point->y << ")" << std::endl;
}
}
int v2Examples() {
using namespace argument_parser::v2::flags;
argument_parser::v2::parser parser{ false };
parser.add_argument<std::string>(
{{ShortArgument, "e"}, {LongArgument, "echo"}, {Action, echo}, {HelpText, "echoes given variable"}});
parser.add_argument<Point>({{ShortArgument, "ep"}, {LongArgument, "echo-point"}, {Action, echo_point}});
parser.add_argument<std::string>({
// stores string for f/file flag
{ShortArgument, "f"},
{LongArgument, "file"},
{HelpText, "File to grep, required only if using grep"},
// if no action, falls to store operation with given type.
});
parser.add_argument<std::regex>({
// stores string for g/grep flag
{ShortArgument, "g"},
{LongArgument, "grep"},
{HelpText, "Grep pattern, required only if using file"},
// same as 'file' flag
});
parser.add_argument<std::string>(
{{ShortArgument, "c"}, {LongArgument, "cat"}, {Action, cat}, {HelpText, "Prints the content of the file"}}
);
parser.add_argument<Point>({
// { ShortArgument, "sp" }, // now if ShortArgument or LongArgument is missing, it will use it for the other.
{LongArgument, "store-point"},
{Required, true} // makes this flag required
});
parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}});
parser.add_argument<std::string>({
{Positional, "input"},
{HelpText, "Input file to process"},
{Required, true},
});
parser.add_argument<std::string>({
{Positional, "output"},
{HelpText, "Output file path"},
});
parser.add_argument<std::vector<Point>>({{LongArgument, "points"}, {HelpText, "List of points to store"}});
parser.on_complete(::run_grep);
parser.on_complete(::run_store_point);
parser.on_complete([](argument_parser::base_parser const &p) {
auto input = p.get_optional<std::string>("input");
auto output = p.get_optional<std::string>("output");
if (input) {
std::cout << "Input: " << input.value() << std::endl;
}
if (output) {
std::cout << "Output: " << output.value() << std::endl;
}
});
parser.handle_arguments(conventions);
return 0;
}
auto unique_copy(std::unique_ptr<std::string> ptr) {
std::cout << *ptr << std::endl;
}
auto unique_reference(std::unique_ptr<std::string> const& ptr) {
std::cout << *ptr << std::endl;
}
auto unique_move(std::unique_ptr<std::string>&& ptr) {
std::cout << *ptr << std::endl;
}
template<typename T>
T return_example(std::function<T()> func) {
if constexpr (std::is_same_v<void, T>) {
return func();
} else {
return func();
}
}
template<typename T>
void log_result(std::function<T()> func) {
if constexpr (std::is_same_v<void, T>) {
func();
} else {
std::cout << "result: " << func() << std::endl;
}
}
int main(int argc, char **argv) {
try {
return_example<void>([]{});
return v2Examples();
} catch (std::exception const &e) {
std::cout << e.what() << std::endl;
}
}

View File

@@ -11,4 +11,4 @@ namespace argument_parser::conventions::helpers {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); }); std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); });
return s; return s;
} }
} // namespace argument_parser::conventions::helpers } // namespace argument_parser::conventions::helpers

View File

@@ -123,4 +123,4 @@ namespace argument_parser::conventions::implementations {
std::vector<convention_features> gnu_equal_argument_convention::get_features() const { std::vector<convention_features> gnu_equal_argument_convention::get_features() const {
return {}; // no fallback allowed return {}; // no fallback allowed
} }
} // namespace argument_parser::conventions::implementations } // namespace argument_parser::conventions::implementations

View File

@@ -50,8 +50,9 @@ namespace argument_parser::conventions::implementations {
return "/"; return "/";
} }
std::pair<std::string, std::string> windows_argument_convention::make_help_text(std::string const &short_arg, std::string const &long_arg, std::pair<std::string, std::string> windows_argument_convention::make_help_text(std::string const &short_arg,
bool requires_value) const { std::string const &long_arg,
bool requires_value) const {
std::string s_part = ""; std::string s_part = "";
if (short_arg != "-" && short_arg != "") { if (short_arg != "-" && short_arg != "") {
s_part += short_prec() + short_arg; s_part += short_prec() + short_arg;
@@ -129,7 +130,8 @@ namespace argument_parser::conventions::implementations {
} }
std::pair<std::string, std::string> windows_kv_argument_convention::make_help_text(std::string const &short_arg, std::pair<std::string, std::string> windows_kv_argument_convention::make_help_text(std::string const &short_arg,
std::string const &long_arg, bool requires_value) const { std::string const &long_arg,
bool requires_value) const {
std::string s_part = ""; std::string s_part = "";
if (short_arg != "-" && short_arg != "") { if (short_arg != "-" && short_arg != "") {
s_part += short_prec() + short_arg; s_part += short_prec() + short_arg;
@@ -153,4 +155,4 @@ namespace argument_parser::conventions::implementations {
return {convention_features::ALLOW_LONG_TO_SHORT_FALLBACK, return {convention_features::ALLOW_LONG_TO_SHORT_FALLBACK,
convention_features::ALLOW_SHORT_TO_LONG_FALLBACK}; // interchangable convention_features::ALLOW_SHORT_TO_LONG_FALLBACK}; // interchangable
} }
} // namespace argument_parser::conventions::implementations } // namespace argument_parser::conventions::implementations

View File

@@ -530,4 +530,4 @@ namespace argument_parser {
event(*this); event(*this);
} }
} }
} // namespace argument_parser } // namespace argument_parser

View File

@@ -23,28 +23,27 @@ namespace argument_parser {
} }
namespace v2 { namespace v2 {
fake_parser::fake_parser(std::string program_name, std::vector<std::string> const &arguments) { fake_parser::fake_parser(std::string program_name, std::vector<std::string> const &arguments) {
set_program_name(program_name); set_program_name(program_name);
ref_parsed_args() = arguments; ref_parsed_args() = arguments;
prepare_help_flag(false); prepare_help_flag(false);
} }
fake_parser::fake_parser(std::string const &program_name, std::vector<std::string> &&arguments) { fake_parser::fake_parser(std::string const &program_name, std::vector<std::string> &&arguments) {
set_program_name(program_name); set_program_name(program_name);
ref_parsed_args() = std::move(arguments); ref_parsed_args() = std::move(arguments);
prepare_help_flag(false); prepare_help_flag(false);
} }
fake_parser::fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments) fake_parser::fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments)
: fake_parser(program_name, std::vector<std::string>(arguments)) { : fake_parser(program_name, std::vector<std::string>(arguments)) {}
}
void fake_parser::set_program_name(std::string const &program_name) { void fake_parser::set_program_name(std::string const &program_name) {
set_program_name(program_name); set_program_name(program_name);
} }
void fake_parser::set_parsed_arguments(std::vector<std::string> const &parsed_arguments) { void fake_parser::set_parsed_arguments(std::vector<std::string> const &parsed_arguments) {
ref_parsed_args() = parsed_arguments; ref_parsed_args() = parsed_arguments;
} }
} } // namespace v2
} // namespace argument_parser } // namespace argument_parser

View File

@@ -25,4 +25,4 @@ namespace argument_parser::parsing_traits {
double parser_trait<double>::parse(const std::string &input) { double parser_trait<double>::parse(const std::string &input) {
return std::stod(input); return std::stod(input);
} }
} // namespace argument_parser::parsing_traits } // namespace argument_parser::parsing_traits

View File

@@ -4,15 +4,17 @@
#include <crt_externs.h> #include <crt_externs.h>
#define MACOS_GETARGS_LOOP(argc_name, argv_name, before_for, for_body) \ #define MACOS_GETARGS_LOOP(argc_name, argv_name, before_for, for_body) \
do { \ do { \
const int argc_name = *_NSGetArgc(); \ const int argc_name = *_NSGetArgc(); \
if (char **argv_name = *_NSGetArgv(); argc_name > 0 && argv_name != nullptr && argv_name[0] != nullptr) { \ if (char **argv_name = *_NSGetArgv(); argc_name > 0 && argv_name != nullptr && argv_name[0] != nullptr) { \
do { before_for; } while(false); \ do { \
for (int i = 1; i < argc_name; ++i) { \ before_for; \
for_body \ } while (false); \
} \ for (int i = 1; i < argc_name; ++i) { \
} \ for_body \
} \
} \
} while (false) } while (false)
namespace argument_parser { namespace argument_parser {
@@ -25,10 +27,10 @@ namespace argument_parser {
namespace v2 { namespace v2 {
macos_parser::macos_parser(bool should_exit) { macos_parser::macos_parser(bool should_exit) {
MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), { MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), {
if (argv[i] != nullptr) if (argv[i] != nullptr)
ref_parsed_args().emplace_back(argv[i]); ref_parsed_args().emplace_back(argv[i]);
}); });
prepare_help_flag(should_exit); prepare_help_flag(should_exit);
} }
} // namespace v2 } // namespace v2

View File

@@ -1,420 +0,0 @@
#include <argparse>
#include <array>
#include <cassert>
#include <exception>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
const std::initializer_list<argument_parser::conventions::convention const *const> conventions = {
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::gnu_equal_argument_convention,
};
namespace v2_test {
class fake_parser : public argument_parser::v2::base_parser {
public:
fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments) {
set_program_name(program_name);
ref_parsed_args() = std::vector<std::string>(arguments);
prepare_help_flag();
}
};
} // namespace v2_test
int tests_run = 0;
int tests_passed = 0;
void test_result(const char *name, bool passed) {
tests_run++;
if (passed) {
tests_passed++;
std::cout << " [PASS] " << name << std::endl;
} else {
std::cout << " [FAIL] " << name << std::endl;
}
}
// ============================================================
// V1 Tests (using argument_parser::fake_parser)
// ============================================================
void test_v1_single_positional_store() {
argument_parser::fake_parser parser("test", {"hello"});
parser.add_positional_argument<std::string>("greeting", "A greeting", false);
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("greeting");
test_result("v1: single positional store", val.has_value() && val.value() == "hello");
}
void test_v1_multiple_positionals_ordered() {
argument_parser::fake_parser parser("test", {"alpha", "beta", "gamma"});
parser.add_positional_argument<std::string>("first", "First arg", false);
parser.add_positional_argument<std::string>("second", "Second arg", false);
parser.add_positional_argument<std::string>("third", "Third arg", false);
parser.handle_arguments(conventions);
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
auto third = parser.get_optional<std::string>("third");
bool ok = first.has_value() && first.value() == "alpha" && second.has_value() && second.value() == "beta" &&
third.has_value() && third.value() == "gamma";
test_result("v1: multiple positionals preserve order", ok);
}
void test_v1_positional_with_explicit_position() {
argument_parser::fake_parser parser("test", {"first_val", "second_val"});
parser.add_positional_argument<std::string>("second", "Second", false, 1);
parser.add_positional_argument<std::string>("first", "First", false, 0);
parser.handle_arguments(conventions);
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val";
test_result("v1: explicit position index", ok);
}
void test_v1_positional_typed_int() {
argument_parser::fake_parser parser("test", {"42"});
parser.add_positional_argument<int>("count", "A count", false);
parser.handle_arguments(conventions);
auto val = parser.get_optional<int>("count");
test_result("v1: positional with int type", val.has_value() && val.value() == 42);
}
void test_v1_positional_with_action() {
std::string captured;
argument_parser::fake_parser parser("test", {"world"});
auto action =
argument_parser::helpers::make_parametered_action<std::string>([&](std::string const &v) { captured = v; });
parser.add_positional_argument<std::string>("name", "A name", action, false);
parser.handle_arguments(conventions);
test_result("v1: positional with action", captured == "world");
}
void test_v1_mixed_named_and_positional() {
argument_parser::fake_parser parser("test", {"--verbose", "true", "myfile.txt"});
parser.add_argument<bool>("v", "verbose", "Verbose mode", false);
parser.add_positional_argument<std::string>("file", "Input file", false);
parser.handle_arguments(conventions);
auto verbose = parser.get_optional<bool>("verbose");
auto file = parser.get_optional<std::string>("file");
bool ok = verbose.has_value() && verbose.value() == true && file.has_value() && file.value() == "myfile.txt";
test_result("v1: mixed named and positional args", ok);
}
void test_v1_positional_after_named() {
argument_parser::fake_parser parser("test", {"-n", "5", "output.txt"});
parser.add_argument<int>("n", "number", "A number", false);
parser.add_positional_argument<std::string>("output", "Output file", false);
parser.handle_arguments(conventions);
auto number = parser.get_optional<int>("number");
auto output = parser.get_optional<std::string>("output");
bool ok = number.has_value() && number.value() == 5 && output.has_value() && output.value() == "output.txt";
test_result("v1: positional after named args", ok);
}
void test_v1_positional_between_named() {
argument_parser::fake_parser parser("test", {"-a", "1", "positional_val", "--beta", "2"});
parser.add_argument<int>("a", "alpha", "Alpha", false);
parser.add_argument<int>("b", "beta", "Beta", false);
parser.add_positional_argument<std::string>("middle", "Middle arg", false);
parser.handle_arguments(conventions);
auto alpha = parser.get_optional<int>("alpha");
auto beta = parser.get_optional<int>("beta");
auto middle = parser.get_optional<std::string>("middle");
bool ok = alpha.has_value() && alpha.value() == 1 && beta.has_value() && beta.value() == 2 && middle.has_value() &&
middle.value() == "positional_val";
test_result("v1: positional between named args", ok);
}
void test_v1_double_dash_separator() {
argument_parser::fake_parser parser("test", {"--", "-not-a-flag"});
parser.add_positional_argument<std::string>("item", "An item", false);
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("item");
test_result("v1: -- separator treats next as positional", val.has_value() && val.value() == "-not-a-flag");
}
void test_v1_double_dash_multiple() {
argument_parser::fake_parser parser("test", {"--name", "hello", "--", "--weird", "-x"});
parser.add_argument<std::string>("n", "name", "A name", false);
parser.add_positional_argument<std::string>("first", "First", false);
parser.add_positional_argument<std::string>("second", "Second", false);
parser.handle_arguments(conventions);
auto name = parser.get_optional<std::string>("name");
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
bool ok = name.has_value() && name.value() == "hello" && first.has_value() && first.value() == "--weird" &&
second.has_value() && second.value() == "-x";
test_result("v1: -- separator with multiple positionals", ok);
}
void test_v1_required_positional_missing() {
argument_parser::fake_parser parser("test", {});
parser.add_positional_argument<std::string>("file", "A file", true);
bool threw = false;
try {
// check_for_required_arguments calls std::exit(1) so we can't easily test it
// instead, test that handle_arguments doesn't crash when positionals are provided
parser.handle_arguments(conventions);
} catch (...) {
threw = true;
}
// Note: required check calls std::exit(1), so if we get here the arg wasn't required-checked
// This test just verifies setup doesn't crash. The exit behavior is tested manually.
test_result("v1: required positional setup (no crash)", true);
}
void test_v1_unexpected_positional_throws() {
argument_parser::fake_parser parser("test", {"unexpected"});
// no positional args defined, but a bare token is provided
bool threw = false;
try {
parser.handle_arguments(conventions);
} catch (const std::runtime_error &) {
threw = true;
}
test_result("v1: unexpected positional throws", threw);
}
void test_v1_duplicate_positional_name_throws() {
argument_parser::fake_parser parser("test", {"a", "b"});
parser.add_positional_argument<std::string>("file", "A file", false);
bool threw = false;
try {
parser.add_positional_argument<std::string>("file", "Duplicate", false);
} catch (const std::runtime_error &) {
threw = true;
}
test_result("v1: duplicate positional name throws", threw);
}
void test_v1_positional_on_complete() {
std::string captured_file;
argument_parser::fake_parser parser("test", {"data.csv"});
parser.add_positional_argument<std::string>("file", "Input file", false);
parser.on_complete([&](argument_parser::base_parser const &p) {
auto val = p.get_optional<std::string>("file");
if (val)
captured_file = val.value();
});
parser.handle_arguments(conventions);
test_result("v1: positional accessible in on_complete", captured_file == "data.csv");
}
// ============================================================
// V2 Tests (using v2_test::fake_parser)
// ============================================================
void test_v2_single_positional() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"hello"});
parser.add_argument<std::string>({{Positional, "greeting"}, {HelpText, "A greeting"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("greeting");
test_result("v2: single positional store", val.has_value() && val.value() == "hello");
}
void test_v2_positional_required() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"value"});
parser.add_argument<std::string>({{Positional, "arg"}, {Required, true}, {HelpText, "Required arg"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("arg");
test_result("v2: required positional", val.has_value() && val.value() == "value");
}
void test_v2_positional_with_position() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"first_val", "second_val"});
parser.add_argument<std::string>({{Positional, "second"}, {Position, 1}, {HelpText, "Second"}});
parser.add_argument<std::string>({{Positional, "first"}, {Position, 0}, {HelpText, "First"}});
parser.handle_arguments(conventions);
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val";
test_result("v2: positional with explicit Position", ok);
}
void test_v2_positional_typed_int() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"99"});
parser.add_argument<int>({{Positional, "count"}, {HelpText, "A count"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<int>("count");
test_result("v2: positional with int type", val.has_value() && val.value() == 99);
}
void test_v2_mixed_named_and_positional() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"--output", "out.txt", "input.txt"});
parser.add_argument<std::string>({{ShortArgument, "o"}, {LongArgument, "output"}, {HelpText, "Output file"}});
parser.add_argument<std::string>({{Positional, "input"}, {HelpText, "Input file"}});
parser.handle_arguments(conventions);
auto output = parser.get_optional<std::string>("output");
auto input = parser.get_optional<std::string>("input");
bool ok = output.has_value() && output.value() == "out.txt" && input.has_value() && input.value() == "input.txt";
test_result("v2: mixed named and positional", ok);
}
void test_v2_positional_with_action() {
using namespace argument_parser::v2::flags;
std::string captured;
v2_test::fake_parser parser("test", {"world"});
parser.add_argument<std::string>({{Positional, "name"},
{Action, argument_parser::helpers::make_parametered_action<std::string>(
[&](std::string const &v) { captured = v; })},
{HelpText, "A name"}});
parser.handle_arguments(conventions);
test_result("v2: positional with action", captured == "world");
}
void test_v2_double_dash_separator() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"--", "--not-a-flag"});
parser.add_argument<std::string>({{Positional, "item"}, {HelpText, "An item"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("item");
test_result("v2: -- separator", val.has_value() && val.value() == "--not-a-flag");
}
void test_v2_positional_auto_help_text() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"42"});
// no HelpText provided — should auto-generate from traits
parser.add_argument<int>({{Positional, "count"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<int>("count");
test_result("v2: positional auto help text (no crash)", val.has_value() && val.value() == 42);
}
void test_v2_multiple_positionals_and_named() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"-v", "src.txt", "dst.txt"});
parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}});
parser.add_argument<std::string>({{Positional, "source"}, {HelpText, "Source"}});
parser.add_argument<std::string>({{Positional, "destination"}, {HelpText, "Destination"}});
parser.handle_arguments(conventions);
auto verbose = parser.get_optional<bool>("verbose");
auto source = parser.get_optional<std::string>("source");
auto dest = parser.get_optional<std::string>("destination");
bool ok = verbose.has_value() && source.has_value() && source.value() == "src.txt" && dest.has_value() &&
dest.value() == "dst.txt";
test_result("v2: multiple positionals with named flag", ok);
}
void test_v2_on_complete_with_positional() {
using namespace argument_parser::v2::flags;
std::string captured;
v2_test::fake_parser parser("test", {"payload"});
parser.add_argument<std::string>({{Positional, "data"}, {HelpText, "Data"}});
parser.on_complete([&](argument_parser::base_parser const &p) {
auto val = p.get_optional<std::string>("data");
if (val)
captured = val.value();
});
parser.handle_arguments(conventions);
test_result("v2: positional accessible in on_complete", captured == "payload");
}
// ============================================================
// Main
// ============================================================
int main() {
std::cout << "=== V1 Positional Argument Tests ===" << std::endl;
std::array<std::function<void()>, 13> v1Tests {
test_v1_single_positional_store,
test_v1_multiple_positionals_ordered,
test_v1_positional_with_explicit_position,
test_v1_positional_typed_int,
test_v1_positional_with_action,
test_v1_mixed_named_and_positional,
test_v1_positional_after_named,
test_v1_positional_between_named,
test_v1_double_dash_separator,
test_v1_double_dash_multiple,
test_v1_unexpected_positional_throws,
test_v1_duplicate_positional_name_throws,
test_v1_positional_on_complete
};
for (auto const& test : v1Tests) {
try {
test();
} catch(std::exception const& e) {
std::cout << "test failed: " << e.what() << std::endl;
}
}
std::cout << "\n=== V2 Positional Argument Tests ===" << std::endl;
std::array<std::function<void()>, 10> v2Tests{
test_v2_single_positional,
test_v2_positional_required,
test_v2_positional_with_position,
test_v2_positional_typed_int,
test_v2_mixed_named_and_positional,
test_v2_positional_with_action,
test_v2_double_dash_separator,
test_v2_positional_auto_help_text,
test_v2_multiple_positionals_and_named,
test_v2_on_complete_with_positional
};
for (auto const& test : v2Tests) {
try {
test();
} catch(std::exception const& e) {
std::cout << "test failed: " << e.what() << std::endl;
}
}
std::cout << "\n=== Results: " << tests_passed << "/" << tests_run << " passed ===" << std::endl;
return (tests_passed == tests_run) ? 0 : 1;
}