Skip to content

Commit

Permalink
feat(wip): use tuple of args instead of array
Browse files Browse the repository at this point in the history
  • Loading branch information
ggabriel96 committed Feb 22, 2025
1 parent 7e2d0ec commit e5ae5cd
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 34 deletions.
3 changes: 3 additions & 0 deletions examples/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ int main(int argc, char const *argv[]) {
.Flg<"disable-content-trust">({.help = "Skip image verification"})
.Flg<"quiet", "q">({.help = "Supress verbose output"});

// auto const r = FindArg(p.args, [](auto const view) { return view.name == "platform"; });
// std::print("{}\n", r->name);

auto const map = parse(p, std::span<char const *>{argv, static_cast<size_t>(argc)});
std::print("\nargs map (size {}):\n", map.args.size());
std::print("name: {}\n", map.get<"name">());
Expand Down
22 changes: 19 additions & 3 deletions include/experimental/arg.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ std::string_view ToString(ArgType at) noexcept {
}
}

template <typename T>
struct Arg {
using value_type = T;

ArgType type = ArgType::POS;
std::string_view name{};
std::string_view abbrev{};
Expand All @@ -44,7 +47,8 @@ struct Arg {
std::string format_for_usage_summary() const noexcept;
};

constexpr bool operator<(Arg const &lhs, Arg const &rhs) noexcept {
template <typename T, typename U>
constexpr bool operator<(Arg<T> const &lhs, Arg<U> const &rhs) noexcept {
bool const lhs_is_positional = lhs.type == ArgType::POS;
bool const rhs_is_positional = rhs.type == ArgType::POS;

Expand All @@ -57,10 +61,22 @@ constexpr bool operator<(Arg const &lhs, Arg const &rhs) noexcept {
return lhs_is_positional; // sort positionals before other types
}

constexpr bool operator==(Arg const &lhs, Arg const &rhs) noexcept {
template <typename T, typename U>
constexpr bool operator==(Arg<T> const &lhs, Arg<U> const &rhs) noexcept {
auto const same_name = lhs.name == rhs.name;
auto const same_abbrev = lhs.has_abbrev() && rhs.has_abbrev() && lhs.abbrev == rhs.abbrev;
return same_name || same_abbrev;
}

#endif
struct ArgView {
ArgType type = ArgType::POS;
std::string_view name{};
std::string_view abbrev{};
bool is_required = false;

template <typename T>
ArgView(Arg<T> const &other)
: type(other.type), name(other.name), abbrev(other.abbrev), is_required(other.is_required) {}
};

#endif
57 changes: 37 additions & 20 deletions include/experimental/parsing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <algorithm>
#include <any>
#include <cctype>
#include <concepts>
#include <map>
#include <print>
#include <span>
Expand Down Expand Up @@ -39,6 +40,22 @@ constexpr std::string_view get_if_short_flags(std::string_view const) noexcept;
constexpr std::string_view get_if_long_flag(std::string_view const) noexcept;
constexpr ParsedOption try_parse_option(std::string_view const) noexcept;

template <typename... Ts>
auto FindArg(std::tuple<Arg<Ts>...> haystack, std::predicate<ArgView> auto p) {
return std::apply(
[&p](auto&&... elem) {
std::optional<ArgView> ret = std::nullopt;

(
(p(elem) ? (ret = elem, true) : (false)) || ...
);

return ret;
},
haystack
);
}

// +-----------------------+
// | main parsing code |
// +-----------------------+
Expand Down Expand Up @@ -144,8 +161,8 @@ struct ArgParser<StringList<Names...>, TypeList<Types...>> {
return 1;
}

auto const it = std::ranges::find_if(program.args, [&option](auto const &a) { return option.name == a.name || option.name == a.abbrev; });
if (it == program.args.end()) {
auto const it = FindArg(program.args, [&option](auto const &a) { return option.name == a.name || option.name == a.abbrev; });
if (!it) {
throw opzioni::UnknownArgument(option.name);
}
if (it->type != ArgType::OPT) {
Expand All @@ -163,8 +180,8 @@ struct ArgParser<StringList<Names...>, TypeList<Types...>> {
}

std::size_t assign_long_flag(ArgsView &view, std::string_view const flag) const {
auto const it = std::ranges::find_if(program.args, [&flag](auto const &a) { return a.name == flag; });
if (it == program.args.end()) {
auto const it = FindArg(program.args, [&flag](auto const &a) { return a.name == flag; });
if (!it) {
throw opzioni::UnknownArgument(flag);
}
if (it->type != ArgType::FLG) {
Expand All @@ -178,8 +195,8 @@ struct ArgParser<StringList<Names...>, TypeList<Types...>> {
std::size_t assign_short_flags(ArgsView &view, std::string_view const flags) const {
for (std::size_t i = 0; i < flags.size(); ++i) {
auto const flag = flags.substr(i, 1);
auto const it = std::ranges::find_if(program.args, [&flag](auto const &a) { return a.abbrev == flag; });
if (it == program.args.end()) {
auto const it = FindArg(program.args, [&flag](auto const &a) { return a.abbrev == flag; });
if (!it) {
throw opzioni::UnknownArgument(flag);
}
if (it->type != ArgType::FLG) {
Expand All @@ -194,9 +211,9 @@ struct ArgParser<StringList<Names...>, TypeList<Types...>> {

auto get_args_map(ArgsView const &view) const {
auto map = ArgsMap<arg_names, arg_types>{.exec_path = view.exec_path};
std::size_t idx = sizeof...(Types) - 1;
std::size_t idx_pos = 0;
((process<Names>(idx, map, view, idx_pos), idx--), ...);
// std::size_t idx = sizeof...(Types) - 1;
// std::size_t idx_pos = 0;
// ((process<Names>(idx, map, view, idx_pos), idx--), ...);
return map;
}

Expand Down Expand Up @@ -233,16 +250,16 @@ struct ArgParser<StringList<Names...>, TypeList<Types...>> {
}

void check_contains_required(ArgsMap<arg_names, arg_types> const &map) {
using std::ranges::transform;
using std::views::filter;
auto get_name = [](auto const &arg) -> std::string_view { return arg.name; };
auto wasnt_parsed = [&map](auto const &arg) { return !map.has(arg.name); };
auto is_required = [](auto const &arg) { return arg.is_required; };
std::vector<std::string_view> missing_arg_names;
auto insert = std::back_inserter(missing_arg_names);
transform(program.args | filter(wasnt_parsed) | filter(is_required), insert, get_name);
if (!missing_arg_names.empty())
throw opzioni::MissingRequiredArguments(missing_arg_names);
// using std::ranges::transform;
// using std::views::filter;
// auto get_name = [](auto const &arg) -> std::string_view { return arg.name; };
// auto wasnt_parsed = [&map](auto const &arg) { return !map.has(arg.name); };
// auto is_required = [](auto const &arg) { return arg.is_required; };
// std::vector<std::string_view> missing_arg_names;
// auto insert = std::back_inserter(missing_arg_names);
// transform(program.args | filter(wasnt_parsed) | filter(is_required), insert, get_name);
// if (!missing_arg_names.empty())
// throw opzioni::MissingRequiredArguments(missing_arg_names);
}
};

Expand Down Expand Up @@ -331,4 +348,4 @@ constexpr ParsedOption try_parse_option(std::string_view const whole_arg) noexce
return {"", std::nullopt};
}

#endif
#endif
21 changes: 11 additions & 10 deletions include/experimental/program.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct Program<StringList<Names...>, TypeList<Types...>> {
std::string_view name{};
std::string_view version{};
std::string_view intro{};
std::array<Arg, sizeof...(Names)> args;
std::tuple<Arg<Types>...> args;
std::size_t amount_pos = 0;

consteval Program() = default;
Expand All @@ -33,7 +33,8 @@ struct Program<StringList<Names...>, TypeList<Types...>> {
version = other.version;
intro = other.intro;
amount_pos = other.amount_pos;
std::copy_n(other.args.begin(), sizeof...(OtherNames), args.begin());
if constexpr (sizeof...(Names) == sizeof...(OtherNames))
args = other.args;
}

consteval auto Intro(std::string_view intro) const noexcept {
Expand All @@ -45,13 +46,13 @@ struct Program<StringList<Names...>, TypeList<Types...>> {
template <fixed_string Name, typename T>
consteval auto Pos(ArgMeta meta) {
Program<StringList<Name, Names...>, TypeList<T, Types...>> new_program(*this);
new_program.args[sizeof...(Names)] = Arg{
new_program.args = std::tuple_cat(std::make_tuple(Arg<T>{
.type = ArgType::POS,
.name = Name,
.abbrev = "",
.help = meta.help,
.is_required = meta.is_required.value_or(true),
};
}), args);
new_program.amount_pos += 1;
return new_program;
}
Expand All @@ -63,13 +64,13 @@ struct Program<StringList<Names...>, TypeList<Types...>> {
static_assert(Abbrev.size <= 2, "Abbreviations must be a single character");

Program<StringList<Name, Names...>, TypeList<T, Types...>> new_program(*this);
new_program.args[sizeof...(Names)] = Arg{
new_program.args = std::tuple_cat(std::make_tuple(Arg<T>{
.type = ArgType::OPT,
.name = Name,
.abbrev = Abbrev,
.help = meta.help,
.is_required = meta.is_required.value_or(false),
};
}), args);
return new_program;
}

Expand All @@ -81,13 +82,13 @@ struct Program<StringList<Names...>, TypeList<Types...>> {
template <fixed_string Name, fixed_string Abbrev = "">
consteval auto Flg(ArgMeta meta) {
Program<StringList<Name, Names...>, TypeList<bool, Types...>> new_program(*this);
new_program.args[sizeof...(Names)] = Arg{
new_program.args = std::tuple_cat(std::make_tuple(Arg<bool>{
.type = ArgType::FLG,
.name = Name,
.abbrev = Abbrev,
.help = meta.help,
.is_required = false,
};
}), args);
return new_program;
}

Expand All @@ -111,7 +112,7 @@ struct Program<StringList<Names...>, TypeList<Types...>> {

consteval auto DefaultProgram(std::string_view name, std::string_view version = "") {
auto p = Program<StringList<"help">, TypeList<bool>>(name);
p.args[0] = Arg{
std::get<0>(p.args) = Arg<bool>{
.type = ArgType::FLG,
.name = "help",
.abbrev = "h",
Expand All @@ -121,4 +122,4 @@ consteval auto DefaultProgram(std::string_view name, std::string_view version =
return p;
}

#endif
#endif
2 changes: 1 addition & 1 deletion src/experimental/parsing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ void ArgsView::print_debug() const noexcept {
for (auto &&o : options) {
std::print("- {} = {}\n", o.first, o.second);
}
}
}

0 comments on commit e5ae5cd

Please sign in to comment.