fully expand constructors in argparse
parent
6c21802041
commit
1f5e7dc193
329
ostd/argparse.hh
329
ostd/argparse.hh
|
@ -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`.
|
||||||
|
|
Loading…
Reference in New Issue