arguments.h 4.8 KB

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