| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- #pragma once
- #include <map>
- #include <set>
- #include <string>
- #include <vector>
- #include "program_args/any.h"
- namespace program {
- /**
- * @brief A string-literal object that requires input arguments to be two or
- * more characters
- */
- struct LongArg {
- template <size_t N> constexpr LongArg(char const (&str)[N]) : str(str) {
- static_assert(N > 1, "cannot create longargs with 0 or 1 character");
- }
- operator std::string const &() const { return str; }
- std::string str;
- };
- constexpr struct require_action_t {
- } require_action;
- template <typename Impl> class Arguments {
- private:
- using option_id = std::string;
- struct Action;
- struct Argument;
- struct Flag;
- struct Option;
- struct Rest;
- template <typename, typename> struct WithDefault;
- public:
- Arguments() = default;
- Arguments(int argc, char const * const * const argv)
- : Arguments(argc, argv, (void const *)nullptr) {}
- std::vector<std::string> args() const {
- size_t const offset = std::min(arguments.size(), argument_names.size());
- return {arguments.begin() + offset, arguments.end()};
- }
- protected:
- template <typename T> Arguments(T const * parent) : parent_(parent) {}
- template <typename T>
- Arguments(int argc, char const * const * const argv, T const * parent);
- template <typename T> T const * parent() const { return parent_.get<T>(); }
- template <typename DA, typename T, size_t... Is, typename... Ps>
- int invoke_action(DA const & default_action, T const & tuple,
- std::index_sequence<Is...>, Ps const &... ps) const;
- protected:
- /**
- * @brief Define an "action"/"subcommand"/"verb" to be executed. An example of
- * this pattern would be how git visualizes sub-commands in its interface,
- * even though in implementation it appears different.
- * @invariant Will fail to compile if bound to something that isn't a subtype
- * of Arguments
- * @param name A name of the action for usage messages
- * @param description An optional description object
- * @return A binding object that can implicitly cast to a subtype of Arguments
- *
- * @throws ArgumentMixingError if both argument() and action() have been
- * invoked in this
- */
- auto action(LongArg name, std::string const & description = "");
- /**
- * @brief Define an argument to be passed in to this program
- *
- * @param index The 0-based index of this argument in the program args array.
- * @param name A name of the argument for usage messages
- * @param description An optional description object
- * @return A binding object that will write its data in to an object of arity
- * 1
- *
- * @throws ArgumentMixingError if both argument() and action() have been
- * invoked in this
- * @throws ArgumentStructureError if this argument is required, but a prior
- * argument was optional
- * @throws IllegalPositionError if index is already in use in this
- * @throws IllegalPositionError if no argument is available at index
- */
- auto argument(size_t index, LongArg name,
- std::string const & description = "");
- /**
- * @brief Return all unbound arguments
- * @param name An optional name to describe these arguments
- * @param description An optional description object
- * @returns All unbound arguments in {@sa Arguments::arguments}
- * @throws ArgumentMixingError if both argument() and action() have been
- invoked in this
- * @throws ArgumentStructureError if rest is called with multiple names
- */
- auto rest(LongArg name = "args", std::string const & description = "");
- /**
- * @brief Define a flag (option with an arity of zero).
- * If a flag binds to a boolean and has a name, then t will be invertable with
- * "--no-" prefix. If a flag binds to an integer, then it increments the value
- * with each repetition.
- * @invariant One or both of name and abbrev must be provided
- * @param name The name of the flag, to be parsed from the commandline with a
- * "--" prefix
- * @param abbrev A single-character version of a flag, to be parsed from the
- * commandline with a '-' prefix
- * @param description An optional description object
- */
- auto flag(LongArg name, char abbrev, std::string const & description = "");
- auto flag(LongArg name, std::string const & description = "");
- auto flag(char abbrev, std::string const & description = "");
- /**
- * @brief Define an option. If an option is repeated, then it can be used to
- * fill out multiple entries in a map or vector.
- * @invariant One or both of name and abbrev must be provided
- * @param name The name of the option, to be parsed from the commandline with
- * a "--" prefix
- * @param abbrev A single-character version of an option, to be parsed from
- * the commandline with a '-' prefix
- * @param description An optional description object
- */
- auto option(LongArg name, char abbrev, std::string const & description = "");
- auto option(LongArg name, std::string const & description = "");
- auto option(char abbrev, std::string const & description = "");
- private:
- void usage() const;
- void add_options(std::string const & name, char abbrev,
- std::string const & description,
- std::vector<std::string> aliases = {});
- bool has_actions() const { return !action_descriptions.empty(); }
- bool has_arguments() const {
- return !(rest_name.empty() && argument_names.empty());
- }
- option_id id(std::string const & arg) const;
- option_id id(char arg) const { return id({'-', arg}); }
- bool is_option(std::string const & arg) const;
- bool is_option(char arg) const { return is_option({'-', arg}); }
- bool is_flag(std::string const & arg) const;
- bool is_flag(char arg) const { return is_flag({'-', arg}); }
- private:
- // A helper for what limited introspection is required for handling actions in
- // test
- friend struct ArgumentTestHelper;
- template <typename> friend class Arguments;
- friend int main(int, char const * const *);
- operator bool() const { return primed_; }
- auto const & self() const { return static_cast<Impl const &>(*this); }
- template <typename... Parents> int invoke(Parents const &... parents) const {
- return typed_main(parents..., self());
- }
- template <typename T>
- std::shared_ptr<T> make_action(std::string const & name) const {
- if (action_name_ != name) { return nullptr; }
- auto ptr = make_action_(static_cast<Impl const &>(*this));
- return std::static_pointer_cast<T>(ptr);
- }
- private:
- using make_action_t = std::function<std::shared_ptr<void>(Impl const &)>;
- using action_hook_t =
- std::function<make_action_t(size_t, char const * const *)>;
- constexpr static size_t const no_optional_args{~0ul};
- // Metadata variables
- bool primed_{false};
- detail::Any parent_;
- std::string rest_name;
- std::map<std::string, std::string> argument_descriptions;
- std::map<size_t, option_id> argument_names;
- size_t optional_from{no_optional_args};
- std::map<std::string, std::string> action_descriptions;
- std::map<std::string, action_hook_t> actions;
- std::map<std::string, std::string> option_descriptions;
- std::map<std::string, option_id> option_names;
- std::set<std::string> flag_names;
- // Data/Output variables
- std::string program;
- std::vector<std::string> arguments;
- std::string action_name_{""};
- make_action_t make_action_{nullptr};
- std::map<std::string, std::vector<std::string>> options;
- std::map<std::string, int> flags;
- };
- template <typename Impl> struct Arguments<Impl>::Action {
- Arguments<Impl> * self;
- std::string name;
- std::string description;
- template <typename T> operator T() const;
- bool primed() const;
- explicit operator bool() const;
- };
- template <typename Impl> struct Arguments<Impl>::Argument {
- Arguments<Impl> * self;
- size_t index;
- bool is_optional;
- std::string name;
- std::string description;
- template <typename T> auto operator=(T && value);
- template <typename T> operator T() const;
- bool primed() const;
- explicit operator bool() const;
- };
- template <typename Impl> struct Arguments<Impl>::Flag {
- Arguments<Impl> * self;
- std::string name;
- char abbrev;
- std::string description;
- bool default_value;
- auto operator=(bool && value);
- operator bool() const;
- template <typename T> operator T() const;
- bool primed(bool inv) const;
- };
- template <typename Impl> struct Arguments<Impl>::Option {
- Arguments<Impl> * self;
- std::string name;
- char abbrev;
- std::string description;
- template <typename T> auto operator=(std::initializer_list<T> value);
- template <typename T> auto operator=(T && value);
- template <typename T> operator T() const;
- bool primed() const;
- explicit operator bool() const;
- };
- template <typename Impl> struct Arguments<Impl>::Rest {
- Arguments<Impl> * self;
- template <typename T> auto operator=(T && value);
- operator std::vector<std::string>() const;
- explicit operator bool() const;
- };
- template <typename Impl>
- template <typename B, typename V>
- struct Arguments<Impl>::WithDefault {
- B impl;
- V default_value;
- template <typename T> operator T() const;
- };
- }
- #include "arguments_impl.hpp"
- template <typename Args, typename... Actions>
- int typed_main(Args const &, Actions const &...);
- #define PROGRAM_ARGS_INVOKE_WITH_DEFAULT_P(super, default, ...) \
- template <typename... Parents> \
- int invoke(Parents const &... parents) const { \
- return invoke_action(default, program::actions(__VA_ARGS__), \
- program::count(__VA_ARGS__), parents...); \
- } \
- using super::super
- #define PROGRAM_ARGS_INVOKE_WITH_DEFAULT(default, ...) \
- PROGRAM_ARGS_INVOKE_WITH_DEFAULT_P(Arguments, default, __VA_ARGS__)
- #define PROGRAM_ARGS_INVOKE_P(super, ...) \
- PROGRAM_ARGS_INVOKE_WITH_DEFAULT_P(super, program::require_action, \
- __VA_ARGS__)
- #define PROGRAM_ARGS_INVOKE(...) \
- PROGRAM_ARGS_INVOKE_WITH_DEFAULT(program::require_action, __VA_ARGS__)
- #define PROGRAM_ARGS_MAIN(tname) \
- int main(int argc, char const * const * const argv) try { \
- return tname(argc, argv).invoke(); \
- } catch (program::ProgramArgumentsError const & pae) { \
- std::cerr << "Error in program argument handling: " << pae.what() << "\n"; \
- }
- #define TYPED_MAIN(...) template <> int typed_main(__VA_ARGS__)
- #define PROGRAM_DEFER(...) [this]() { return __VA_ARGS__; }
|