argparse parsing cleanup and nicer api

This commit is contained in:
q66 2017-05-16 19:25:20 +02:00
parent ff4620d3f5
commit d1ad559461
2 changed files with 75 additions and 95 deletions

View file

@ -148,31 +148,31 @@ int main(int argc, char **argv) {
auto &help = ap.add_help("print this message and exit"); auto &help = ap.add_help("print this message and exit");
ap.add_optional(0, "no-examples", ostd::arg_value::NONE) ap.add_optional("--no-examples", ostd::arg_value::NONE)
.help("do not build examples") .help("do not build examples")
.action(ostd::arg_store_false(build_examples)); .action(ostd::arg_store_false(build_examples));
ap.add_optional(0, "no-test-suite", ostd::arg_value::NONE) ap.add_optional("--no-test-suite", ostd::arg_value::NONE)
.help("do not build test suite") .help("do not build test suite")
.action(ostd::arg_store_false(build_testsuite)); .action(ostd::arg_store_false(build_testsuite));
ap.add_optional(0, "no-static", ostd::arg_value::NONE) ap.add_optional("--no-static", ostd::arg_value::NONE)
.help("do not build static libostd") .help("do not build static libostd")
.action(ostd::arg_store_false(build_static)); .action(ostd::arg_store_false(build_static));
ap.add_optional(0, "shared", ostd::arg_value::NONE) ap.add_optional("--shared", ostd::arg_value::NONE)
.help("build shared libostd") .help("build shared libostd")
.action(ostd::arg_store_true(build_shared)); .action(ostd::arg_store_true(build_shared));
ap.add_optional(0, "config", ostd::arg_value::REQUIRED) ap.add_optional("--config", ostd::arg_value::REQUIRED)
.help("build configuration (debug/release)") .help("build configuration (debug/release)")
.action(ostd::arg_store_str(build_cfg)); .action(ostd::arg_store_str(build_cfg));
ap.add_optional('v', "verbose", ostd::arg_value::NONE) ap.add_optional("-v", "--verbose", ostd::arg_value::NONE)
.help("print entire commands") .help("print entire commands")
.action(ostd::arg_store_true(verbose)); .action(ostd::arg_store_true(verbose));
ap.add_optional(0, "clean", ostd::arg_value::NONE) ap.add_optional("--clean", ostd::arg_value::NONE)
.help("remove generated files and exit") .help("remove generated files and exit")
.action(ostd::arg_store_true(clean)); .action(ostd::arg_store_true(clean));

View file

@ -16,6 +16,7 @@
#ifndef OSTD_ARGPARSE_HH #ifndef OSTD_ARGPARSE_HH
#define OSTD_ARGPARSE_HH #define OSTD_ARGPARSE_HH
#include <optional>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
@ -101,11 +102,10 @@ struct arg_optional: arg_argument {
} }
bool is_arg(string_range name) const { bool is_arg(string_range name) const {
if (starts_with(name, "--")) { for (auto const &nm: p_names) {
return name.slice(2) == ostd::citer(p_lname); if (name == iter(nm)) {
} return true;
if (name[0] == '-') { }
return name[1] == p_sname;
} }
return false; return false;
} }
@ -134,17 +134,39 @@ struct arg_optional: arg_argument {
return *this; return *this;
} }
arg_optional &add_name(string_range name) {
p_names.emplace_back(name);
return *this;
}
protected: protected:
arg_optional() = delete; arg_optional() = delete;
arg_optional(string_range name, arg_value req, int nargs = 1):
arg_argument(req, nargs)
{
p_names.emplace_back(name);
}
arg_optional(string_range name, int nargs):
arg_argument(nargs)
{
p_names.emplace_back(name);
}
arg_optional( arg_optional(
char short_name, string_range long_name, string_range name1, string_range name2, arg_value req, int nargs = 1
arg_value req, int nargs = 1
): ):
arg_argument(req, nargs), p_lname(long_name), p_sname(short_name) arg_argument(req, nargs)
{} {
arg_optional(char short_name, string_range long_name, int nargs): p_names.emplace_back(name1);
arg_argument(nargs), p_lname(long_name), p_sname(short_name) p_names.emplace_back(name2);
{} }
arg_optional(string_range name1, string_range name2, int nargs):
arg_argument(nargs)
{
p_names.emplace_back(name1);
p_names.emplace_back(name2);
}
void set_values(iterator_range<string_range const *> vals) { void set_values(iterator_range<string_range const *> vals) {
if (p_action) { if (p_action) {
@ -156,8 +178,7 @@ protected:
private: private:
std::function<void(iterator_range<string_range const *>)> p_action; std::function<void(iterator_range<string_range const *>)> p_action;
std::string p_lname; std::vector<std::string> p_names;
char p_sname;
bool p_used = false; bool p_used = false;
}; };
@ -300,7 +321,7 @@ struct arg_parser: arg_description_container {
template<typename OutputRange> template<typename OutputRange>
arg_optional &add_help(OutputRange out, string_range msg) { arg_optional &add_help(OutputRange out, string_range msg) {
auto &opt = add_optional('h', "help", arg_value::NONE); auto &opt = add_optional("-h", "--help", arg_value::NONE);
opt.help(msg); opt.help(msg);
opt.action([this, out = std::move(out)](auto) mutable { opt.action([this, out = std::move(out)](auto) mutable {
this->print_help(out); this->print_help(out);
@ -351,15 +372,10 @@ private:
case arg_type::OPTIONAL: { case arg_type::OPTIONAL: {
auto &opt = static_cast<arg_optional &>(*p); auto &opt = static_cast<arg_optional &>(*p);
std::size_t nl = 0; std::size_t nl = 0;
if (opt.p_sname) { for (auto &s: opt.p_names) {
nl += 2; nl += s.size();
}
if (!opt.p_lname.empty()) {
if (opt.p_sname) {
nl += 2;
}
nl += opt.p_lname.size() + 2;
} }
nl += 2 * (opt.p_names.size() - 1);
opt_namel = std::max(opt_namel, nl); opt_namel = std::max(opt_namel, nl);
break; break;
} }
@ -405,17 +421,13 @@ private:
auto &parg = static_cast<arg_optional &>(*p.get()); auto &parg = static_cast<arg_optional &>(*p.get());
format(out, " "); format(out, " ");
std::size_t nd = 0; std::size_t nd = 0;
if (parg.p_sname) { for (auto &s: parg.p_names) {
nd += 2; if (nd) {
format(out, "-%c", parg.p_sname);
if (!parg.p_lname.empty()) {
nd += 2; nd += 2;
format(out, ", "); format(out, ", ");
} }
} nd += s.size();
if (!parg.p_lname.empty()) { format(out, "%s", s);
nd += parg.p_lname.size() + 2;
format(out, "--%s", parg.p_lname);
} }
if (parg.p_helpstr.empty()) { if (parg.p_helpstr.empty()) {
out.put('\n'); out.put('\n');
@ -432,83 +444,51 @@ private:
template<typename R> template<typename R>
void parse_long(string_range arg, R &args) { void parse_long(string_range arg, R &args) {
string_range val = find(arg, '='); std::optional<string_range> val;
bool has_val = !val.empty(), arg_val = false; if (auto sv = find(arg, '='); !sv.empty()) {
if (has_val) { arg = arg.slice(0, arg.size() - sv.size());
arg = arg.slice(0, arg.size() - val.size()); sv.pop_front();
val.pop_front(); val = sv;
}
auto &desc = find_arg<arg_optional>(arg);
args.pop_front();
auto needs = desc.needs_value();
if (needs == arg_value::NONE) {
if (has_val) {
throw arg_error{format(
appender<std::string>(), "argument '--%s' takes no value",
desc.p_lname
).get()};
}
desc.set_values(nullptr);
return;
}
if (!has_val) {
if (args.empty()) {
if (needs == arg_value::REQUIRED) {
throw arg_error{format(
appender<std::string>(), "argument '--%s' needs a value",
desc.p_lname
).get()};
}
desc.set_values(nullptr);
return;
}
string_range tval = args.front();
if ((needs != arg_value::OPTIONAL) || !find_arg_ptr(tval)) {
val = tval;
has_val = arg_val = true;
}
}
if (has_val) {
desc.set_values(iter({ val }));
if (arg_val) {
args.pop_front();
}
} else {
desc.set_values(nullptr);
} }
parse_arg(arg, std::move(val), args);
} }
template<typename R> template<typename R>
void parse_short(string_range arg, R &args) { void parse_short(string_range arg, R &args) {
string_range val; std::optional<string_range> val;
bool has_val = (arg.size() > 2), arg_val = false; if (arg.size() > 2) {
if (has_val) {
val = arg.slice(2); val = arg.slice(2);
arg = arg.slice(0, 2); arg = arg.slice(0, 2);
} }
parse_arg(arg, std::move(val), args);
}
template<typename R>
void parse_arg(
string_range arg, std::optional<string_range> val, R &args
) {
bool arg_val = false;
std::string argname{arg};
auto &desc = find_arg<arg_optional>(arg); auto &desc = find_arg<arg_optional>(arg);
args.pop_front(); args.pop_front();
auto needs = desc.needs_value(); auto needs = desc.needs_value();
if (needs == arg_value::NONE) { if (needs == arg_value::NONE) {
if (has_val) { if (val) {
throw arg_error{format( throw arg_error{format(
appender<std::string>(), "argument '-%c' takes no value", appender<std::string>(), "argument '%s' takes no value",
desc.p_sname argname
).get()}; ).get()};
} }
desc.set_values(nullptr); desc.set_values(nullptr);
return; return;
} }
if (!has_val) { if (!val) {
if (args.empty()) { if (args.empty()) {
if (needs == arg_value::REQUIRED) { if (needs == arg_value::REQUIRED) {
throw arg_error{format( throw arg_error{format(
appender<std::string>(), "argument '-%c' needs a value", appender<std::string>(), "argument '%s' needs a value",
desc.p_sname argname
).get()}; ).get()};
} }
desc.set_values(nullptr); desc.set_values(nullptr);
@ -517,11 +497,11 @@ private:
string_range tval = args.front(); string_range tval = args.front();
if ((needs != arg_value::OPTIONAL) || !find_arg_ptr(tval)) { if ((needs != arg_value::OPTIONAL) || !find_arg_ptr(tval)) {
val = tval; val = tval;
has_val = arg_val = true; arg_val = true;
} }
} }
if (has_val) { if (val) {
desc.set_values(iter({ val })); desc.set_values(iter({ *val }));
if (arg_val) { if (arg_val) {
args.pop_front(); args.pop_front();
} }