fully expand constructors in argparse

master
Daniel Kolesa 2018-01-10 02:30:01 +01:00
parent 6c21802041
commit 1f5e7dc193
1 changed files with 194 additions and 135 deletions

View File

@ -362,88 +362,24 @@ struct arg_optional: arg_argument {
protected: protected:
arg_optional() = delete; arg_optional() = delete;
/** @brief Constructs the optional argument with one name. /** @brief Constructs the optional argument.
* *
* This version takes an explicit value requirement, see the * See arg_description_container::add_optional().
* appropriate constructors of ostd::arg_argument. The value
* requirement can be `EXACTLY`, `OPTIONAL` or `ALL` but never
* `REST`. If it's not one of the allowed ones, ostd::arg_error
* is thrown.
*
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/ */
arg_optional( arg_optional(arg_value req, std::size_t nargs, bool required):
string_range name, arg_value req, std::size_t nargs = 1,
bool required = false
):
arg_argument(req, nargs), p_required(required) arg_argument(req, nargs), p_required(required)
{ {
validate_req(req); validate_req(req);
p_names.emplace_back(name);
} }
/** @brief Constructs the optional argument with one name. /** @brief Constructs the optional argument.
* *
* This version takes only a number, see the appropriate * See arg_description_container::add_optional(). The ostd::arg_value
* constructors of ostd::arg_argument. * requirement is `EXACTLY` here.
*
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/ */
arg_optional(string_range name, std::size_t nargs, bool required = false): arg_optional(std::size_t nargs, bool required):
arg_argument(nargs), p_required(required) arg_argument(nargs), p_required(required)
{ {}
p_names.emplace_back(name);
}
/** @brief Constructs the optional argument with two names.
*
* The typical combination is a short and a long name.
*
* This version takes an explicit value requirement, see the
* appropriate constructors of ostd::arg_argument. The value
* requirement can be `EXACTLY`, `OPTIONAL` or `ALL` but never
* `REST`. If it's not one of the allowed ones, ostd::arg_error
* is thrown.
*
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/
arg_optional(
string_range name1, string_range name2, arg_value req,
std::size_t nargs = 1, bool required = false
):
arg_argument(req, nargs), p_required(required)
{
validate_req(req);
p_names.emplace_back(name1);
p_names.emplace_back(name2);
}
/** @brief Constructs the optional argument with two names.
*
* The typical combination is a short and a long name.
*
* This version takes only a number, see the appropriate
* constructors of ostd::arg_argument.
*
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/
arg_optional(
string_range name1, string_range name2, std::size_t nargs,
bool required = false
):
arg_argument(nargs), p_required(required)
{
p_names.emplace_back(name1);
p_names.emplace_back(name2);
}
/** @brief See arg_description::find_arg(). /** @brief See arg_description::find_arg().
* *
@ -602,21 +538,17 @@ protected:
/** @brief Constructs the positional argument. /** @brief Constructs the positional argument.
* *
* This version takes an explicit value requirement, * See arg_description_container::add_positional().
* see the appropriate constructors of ostd::arg_argument.
*/ */
arg_positional( arg_positional(string_range name, arg_value req, std::size_t nargs):
string_range name, arg_value req = arg_value::EXACTLY,
std::size_t nargs = 1
):
arg_argument(req, nargs), arg_argument(req, nargs),
p_name(name) p_name(name)
{} {}
/** @brief Constructs the positional argument. /** @brief Constructs the positional argument.
* *
* This version takes only a number, see the appropriate * See arg_description_container::add_positional(). The ostd::arg_value
* constructors of ostd::arg_argument. * requirement is `EXACTLY` here.
*/ */
arg_positional(string_range name, std::size_t nargs): arg_positional(string_range name, std::size_t nargs):
arg_argument(nargs), arg_argument(nargs),
@ -685,24 +617,61 @@ struct arg_mutually_exclusive_group: arg_description {
return arg_type::MUTUALLY_EXCLUSIVE_GROUP; return arg_type::MUTUALLY_EXCLUSIVE_GROUP;
} }
/** @brief Constructs an optional argument in the group. /** @brief Adds an optional argument with one name.
* *
* The arguments are perfect-forwarded to the ostd::arg_optional * See ostd::arg_description_container::add_optional(). The
* constructors. The constructors are protected but may be used * only difference is that "required" arguments are not allowed,
* from here. * so that will always be `false` and cannot be specified.
*/ */
template<typename ...A> arg_optional &add_optional(
arg_optional &add_optional(A &&...args) { string_range name, arg_value req, std::size_t nargs = 1
auto *opt = new arg_optional(std::forward<A>(args)...); ) {
std::unique_ptr<arg_description> p{opt}; auto &ret = add_member<arg_optional>(req, nargs, false);
if (opt->required()) { ret.add_name(name);
throw arg_error{ return ret;
"required optional arguments not allowed " }
"in mutually exclusive groups"
}; /** @brief Adds an optional argument with one name.
} *
p_opts.push_back(std::move(p)); * See ostd::arg_description_container::add_optional(). The
return *opt; * only difference is that "required" arguments are not allowed,
* so that will always be `false` and cannot be specified.
*/
arg_optional &add_optional(string_range name, std::size_t nargs) {
auto &ret = add_member<arg_optional>(nargs, false);
ret.add_name(name);
return ret;
}
/** @brief Adds an optional argument with two names.
*
* See ostd::arg_description_container::add_optional(). The
* only difference is that "required" arguments are not allowed,
* so that will always be `false` and cannot be specified.
*/
arg_optional &add_optional(
string_range name1, string_range name2, arg_value req,
std::size_t nargs = 1
) {
auto &ret = add_member<arg_optional>(req, nargs, false);
ret.add_name(name1);
ret.add_name(name2);
return ret;
}
/** @brief Adds an optional argument with two names.
*
* See ostd::arg_description_container::add_optional(). The
* only difference is that "required" arguments are not allowed,
* so that will always be `false` and cannot be specified.
*/
arg_optional &add_optional(
string_range name1, string_range name2, std::size_t nargs
) {
auto &ret = add_member<arg_optional>(nargs, false);
ret.add_name(name1);
ret.add_name(name2);
return ret;
} }
/** @brief Calls `func` for each argument in the group. /** @brief Calls `func` for each argument in the group.
@ -764,6 +733,15 @@ protected:
return nullptr; return nullptr;
} }
/** @brief See arg_description_container::add_member(). */
template<typename T, typename ...A>
T &add_member(A &&...args) {
auto *ap = new T(std::forward<A>(args)...);
std::unique_ptr<arg_description> p{ap};
p_opts.push_back(std::move(p));
return *ap;
}
private: private:
std::vector<std::unique_ptr<arg_description>> p_opts; std::vector<std::unique_ptr<arg_description>> p_opts;
bool p_required; bool p_required;
@ -777,46 +755,117 @@ private:
* may only be inserted from a parser. * may only be inserted from a parser.
*/ */
struct arg_description_container { struct arg_description_container {
/** @brief Constructs an optional argument in the container. /** @brief Adds an optional argument with one name.
* *
* The arguments are perfect-forwarded to the ostd::arg_optional * This version takes an explicit value requirement, see the
* constructors. The constructors are protected but may be used * appropriate constructors of ostd::arg_argument. The value
* from here. * requirement can be `EXACTLY`, `OPTIONAL` or `ALL` but never
* `REST`. If it's not one of the allowed ones, ostd::arg_error
* is thrown.
*
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/ */
template<typename ...A> arg_optional &add_optional(
arg_optional &add_optional(A &&...args) { string_range name, arg_value req,
auto *opt = new arg_optional(std::forward<A>(args)...); std::size_t nargs = 1, bool required = false
std::unique_ptr<arg_description> p{opt}; ) {
p_opts.push_back(std::move(p)); auto &ret = add_member<arg_optional>(req, nargs, required);
return *opt; ret.add_name(name);
return ret;
} }
/** @brief Constructs a positional argument in the container. /** @brief Adds an optional argument with one name.
* *
* The arguments are perfect-forwarded to the ostd::arg_positional * This version takes only a number, see the appropriate
* constructors. The constructors are protected but may be used * constructors of ostd::arg_argument.
* from here. *
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/ */
template<typename ...A> arg_optional &add_optional(
arg_positional &add_positional(A &&...args) { string_range name, std::size_t nargs, bool required = false
auto *pos = new arg_positional(std::forward<A>(args)...); ) {
std::unique_ptr<arg_description> p{pos}; auto &ret = add_member<arg_optional>(nargs, required);
p_opts.push_back(std::move(p)); ret.add_name(name);
return *pos; return ret;
} }
/** @brief Constructs a mutually exclusive group in the container. /** @brief Adds an optional argument with two names.
* *
* The arguments are perfect-forwarded to the * The typical combination is a short and a long name.
* ostd::arg_mutually_exclusive_group constructor. The *
* constructor is protected but may be used from here. * This version takes an explicit value requirement, see the
* appropriate constructors of ostd::arg_argument. The value
* requirement can be `EXACTLY`, `OPTIONAL` or `ALL` but never
* `REST`. If it's not one of the allowed ones, ostd::arg_error
* is thrown.
*
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/ */
arg_optional &add_optional(
string_range name1, string_range name2, arg_value req,
std::size_t nargs = 1, bool required = false
) {
auto &ret = add_member<arg_optional>(req, nargs, required);
ret.add_name(name1);
ret.add_name(name2);
return ret;
}
/** @brief Adds an optional argument with two names.
*
* The typical combination is a short and a long name.
*
* This version takes only a number, see the appropriate
* constructors of ostd::arg_argument.
*
* The `required` argument specifies whether this optional
* argument must be specified. In most cases optional
* arguments are optional, but not always.
*/
arg_optional &add_optional(
string_range name1, string_range name2,
std::size_t nargs, bool required = false
) {
auto &ret = add_member<arg_optional>(nargs, required);
ret.add_name(name1);
ret.add_name(name2);
return ret;
}
/** @brief Adds a positional argument.
*
* This version takes an explicit value requirement,
* see the appropriate constructors of ostd::arg_argument.
*/
arg_positional &add_positional(
string_range name, arg_value req = arg_value::EXACTLY,
std::size_t nargs = 1
) {
return add_member<arg_positional>(name, req, nargs);
}
/** @brief Adds a positional argument.
*
* This version takes only a number, see the appropriate
* constructors of ostd::arg_argument.
*/
arg_positional &add_positional(string_range name, std::size_t nargs) {
return add_member<arg_positional>(name, nargs);
}
/** @brief Adds a mutually exclusive group. */
template<typename ...A> template<typename ...A>
arg_mutually_exclusive_group &add_mutually_exclusive_group(A &&...args) { arg_mutually_exclusive_group &add_mutually_exclusive_group(
auto *mgrp = new arg_mutually_exclusive_group(std::forward<A>(args)...); bool required = false
std::unique_ptr<arg_description> p{mgrp}; ) {
p_opts.push_back(std::move(p)); return add_member<arg_mutually_exclusive_group>(required);
return *mgrp;
} }
/** @brief Calls `func` for each argument in the container. /** @brief Calls `func` for each argument in the container.
@ -865,6 +914,20 @@ protected:
throw arg_error{"unknown argument '%s'", name}; throw arg_error{"unknown argument '%s'", name};
} }
/** @brief Adds a member in the container.
*
* The `T` is the type to add, inherited from ostd::arg_description.
* The arguments are forwarded to the constructor. It also makes sure
* that when something throws, no resources are leaked.
*/
template<typename T, typename ...A>
T &add_member(A &&...args) {
auto *ap = new T(std::forward<A>(args)...);
std::unique_ptr<arg_description> p{ap};
p_opts.push_back(std::move(p));
return *ap;
}
std::vector<std::unique_ptr<arg_description>> p_opts; std::vector<std::unique_ptr<arg_description>> p_opts;
}; };
@ -878,8 +941,7 @@ protected:
* displayed in help listing. If not set, the name is displayed. * displayed in help listing. If not set, the name is displayed.
*/ */
struct arg_group: arg_description, arg_description_container { struct arg_group: arg_description, arg_description_container {
template<typename HelpFormatter> friend struct arg_description_container;
friend struct basic_arg_parser;
/* empty, for vtable placement */ /* empty, for vtable placement */
virtual ~arg_group(); virtual ~arg_group();
@ -911,7 +973,7 @@ struct arg_group: arg_description, arg_description_container {
protected: protected:
arg_group() = delete; arg_group() = delete;
/** @brief Constructs the group with a name and an optional title. */ /** @brief Constructs the group. See basic_arg_parser::add_group(). */
arg_group(string_range name, string_range title = string_range{}): arg_group(string_range name, string_range title = string_range{}):
arg_description(), arg_description_container(), arg_description(), arg_description_container(),
p_name(name), p_title(title) p_name(name), p_title(title)
@ -1133,16 +1195,13 @@ public:
} }
} }
/** @brief Constructs a group in the container. /** @brief Adds a group in the container.
* *
* The arguments are perfect-forwarded to the ostd::arg_group constructor. * Groups require a name and optionally a title for help formatting.
* The constructor is protected but may be used from here. Groups cannot
* be constructed inside ostd::arg_description_container itself.
*/ */
template<typename ...A> template<typename ...A>
arg_group &add_group(A &&...args) { arg_group &add_group(string_range name, string_range title = string_range{}) {
arg_description *p = new arg_group(std::forward<A>(args)...); return add_member<arg_group>(name, title);
return static_cast<arg_group &>(*p_opts.emplace_back(p));
} }
/** @brief Parses arguments using `argc` and `argv`. /** @brief Parses arguments using `argc` and `argv`.