arguments.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #pragma once
  2. #include <map>
  3. #include <set>
  4. #include <string>
  5. #include <vector>
  6. namespace program {
  7. struct LongArg {
  8. template <size_t N> constexpr LongArg(char const (&str)[N]) : str(str) {
  9. static_assert(N > 1, "cannot create longargs with 0 or 1 character");
  10. }
  11. operator std::string const &() const { return str; }
  12. std::string str;
  13. };
  14. template <typename Impl> class Arguments {
  15. private:
  16. using option_id = std::string;
  17. struct Action;
  18. struct Argument;
  19. struct Flag;
  20. struct Option;
  21. template <typename, typename> struct WithDefault;
  22. public:
  23. Arguments() = default;
  24. Arguments(int argc, char const * const * const argv);
  25. std::vector<std::string> args() const {
  26. return {arguments.begin() + argument_names.size(), arguments.end()};
  27. }
  28. protected:
  29. auto action(LongArg name, std::string const &description = "");
  30. auto argument(size_t index, LongArg name,
  31. std::string const & description = "");
  32. std::vector<std::string> rest(LongArg name = "args") {
  33. rest_name_ = name;
  34. size_t const i = std::min(arguments.size(), argument_names.size());
  35. return {arguments.begin() + i, arguments.end()};
  36. }
  37. auto flag(LongArg name, std::string const & description = "");
  38. auto flag(LongArg name, char abbrev, std::string const & description = "");
  39. auto flag(char abbrev, std::string const & description = "");
  40. auto option(LongArg name, std::string const & description = "");
  41. auto option(LongArg name, char abbrev,
  42. std::string const & description = "");
  43. auto option(char abbrev, std::string const & description = "");
  44. private:
  45. void usage() const;
  46. void add_options(std::string const & name, char abbrev,
  47. std::string const & description,
  48. std::vector<std::string> aliases = {});
  49. option_id id(std::string const & arg) const;
  50. option_id id(char arg) const { return id({'-', arg}); }
  51. bool is_option(std::string const & arg) const;
  52. bool is_option(char arg) const { return is_option({'-', arg}); }
  53. bool is_flag(std::string const & arg) const;
  54. bool is_flag(char arg) const { return is_flag({'-', arg}); }
  55. private:
  56. friend struct ArgumentTestHelper;
  57. friend int main(int, char const * const *);
  58. private:
  59. using main_callback_t = std::function<int(Arguments const &)>;
  60. using make_hook_t = std::function<main_callback_t(size_t, char const * const *)>;
  61. // Metadata variables
  62. bool primed_{false};
  63. std::string rest_name_;
  64. std::map<option_id, std::string> argument_descriptions;
  65. std::map<size_t, option_id> argument_names;
  66. std::map<option_id, std::string> option_descriptions;
  67. std::map<std::string, option_id> option_names;
  68. std::set<option_id> flag_names;
  69. std::map<std::string, make_hook_t> actions;
  70. std::map<std::string, std::string> action_descriptions;
  71. // Data/Output variables
  72. std::string program;
  73. main_callback_t main_callback{nullptr};
  74. constexpr static size_t const no_optional_args{~0ul};
  75. size_t optional_from{no_optional_args};
  76. std::vector<std::string> arguments;
  77. std::map<std::string, std::vector<std::string>> options;
  78. std::map<std::string, int> flags;
  79. };
  80. template <typename Impl> struct Arguments<Impl>::Action {
  81. Arguments<Impl> * self;
  82. std::string name;
  83. std::string description;
  84. template <typename T> operator T() const;
  85. bool primed() const;
  86. explicit operator bool() const;
  87. };
  88. template <typename Impl> struct Arguments<Impl>::Argument {
  89. Arguments<Impl> * self;
  90. size_t index;
  91. bool is_optional;
  92. std::string name;
  93. std::string description;
  94. template <typename T> auto operator=(T && value);
  95. template <typename T> operator T() const;
  96. bool primed() const;
  97. explicit operator bool() const;
  98. };
  99. template <typename Impl> struct Arguments<Impl>::Flag {
  100. Arguments<Impl> * self;
  101. std::string name;
  102. char abbrev;
  103. std::string description;
  104. bool default_value;
  105. auto operator=(bool && value);
  106. operator bool() const;
  107. template <typename T> operator T() const;
  108. bool primed(bool inv) const;
  109. };
  110. template <typename Impl> struct Arguments<Impl>::Option {
  111. Arguments<Impl> * self;
  112. std::string name;
  113. char abbrev;
  114. std::string description;
  115. template <typename T> auto operator=(std::initializer_list<T> value);
  116. template <typename T> auto operator=(T && value);
  117. template <typename T> operator T() const;
  118. bool primed() const;
  119. explicit operator bool() const;
  120. };
  121. template <typename Impl>
  122. template <typename B, typename V>
  123. struct Arguments<Impl>::WithDefault {
  124. B impl;
  125. V default_value;
  126. template <typename T> operator T() const {
  127. if (impl) { return impl; }
  128. if constexpr (std::is_invocable_r<T, V>{}) {
  129. return T(default_value());
  130. } else {
  131. return T{default_value};
  132. }
  133. }
  134. };
  135. }
  136. #include "arguments_impl.hpp"
  137. template <typename Args, typename Action>
  138. int typed_main(Args const &, Action const &);
  139. #define TYPED_MAIN(tname) \
  140. int typed_main(tname const & args); \
  141. int main(int argc, char const * const * const argv) try { \
  142. tname args(argc, argv); \
  143. return (args.main_callback) ? args.main_callback(args) : typed_main(args); \
  144. } catch (program::ProgramArgumentsError const & pae) { \
  145. std::cerr << "Error in program argument handling: " << pae.what() << "\n"; \
  146. } \
  147. int typed_main(tname const & args)
  148. #define PROGRAM_DEFER(...) [this]() { return __VA_ARGS__; }