cmArgumentParser.h 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #ifndef cmArgumentParser_h
  4. #define cmArgumentParser_h
  5. #include "cmConfigure.h" // IWYU pragma: keep
  6. #include "cm_static_string_view.hxx"
  7. #include <cm/string_view>
  8. #include <cassert>
  9. #include <functional>
  10. #include <string>
  11. #include <utility>
  12. #include <vector>
  13. namespace ArgumentParser {
  14. using StringList = std::vector<std::string>;
  15. using MultiStringList = std::vector<StringList>;
  16. class Instance;
  17. using Action = std::function<void(Instance&, void*)>;
  18. // using ActionMap = cm::flat_map<cm::string_view, Action>;
  19. class ActionMap : public std::vector<std::pair<cm::string_view, Action>>
  20. {
  21. public:
  22. std::pair<iterator, bool> Emplace(cm::string_view name, Action action);
  23. const_iterator Find(cm::string_view name) const;
  24. };
  25. class Instance
  26. {
  27. public:
  28. Instance(ActionMap const& bindings)
  29. : Bindings(bindings)
  30. {
  31. }
  32. void Bind(bool& val);
  33. void Bind(std::string& val);
  34. void Bind(StringList& val);
  35. void Bind(MultiStringList& val);
  36. void Consume(cm::string_view arg, void* result,
  37. std::vector<std::string>* unparsedArguments,
  38. std::vector<std::string>* keywordsMissingValue);
  39. private:
  40. ActionMap const& Bindings;
  41. std::string* CurrentString = nullptr;
  42. StringList* CurrentList = nullptr;
  43. bool ExpectValue = false;
  44. };
  45. } // namespace ArgumentParser
  46. template <typename Result>
  47. class cmArgumentParser
  48. {
  49. public:
  50. // I *think* this function could be made `constexpr` when the code is
  51. // compiled as C++20. This would allow building a parser at compile time.
  52. template <typename T>
  53. cmArgumentParser& Bind(cm::static_string_view name, T Result::*member)
  54. {
  55. bool const inserted =
  56. this->Bindings
  57. .Emplace(name,
  58. [member](ArgumentParser::Instance& instance, void* result) {
  59. instance.Bind(static_cast<Result*>(result)->*member);
  60. })
  61. .second;
  62. assert(inserted), (void)inserted;
  63. return *this;
  64. }
  65. template <typename Range>
  66. void Parse(Result& result, Range const& args,
  67. std::vector<std::string>* unparsedArguments = nullptr,
  68. std::vector<std::string>* keywordsMissingValue = nullptr) const
  69. {
  70. ArgumentParser::Instance instance(this->Bindings);
  71. for (cm::string_view arg : args) {
  72. instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue);
  73. }
  74. }
  75. template <typename Range>
  76. Result Parse(Range const& args,
  77. std::vector<std::string>* unparsedArguments = nullptr,
  78. std::vector<std::string>* keywordsMissingValue = nullptr) const
  79. {
  80. Result result;
  81. this->Parse(result, args, unparsedArguments, keywordsMissingValue);
  82. return result;
  83. }
  84. private:
  85. ArgumentParser::ActionMap Bindings;
  86. };
  87. template <>
  88. class cmArgumentParser<void>
  89. {
  90. public:
  91. template <typename T>
  92. cmArgumentParser& Bind(cm::static_string_view name, T& ref)
  93. {
  94. bool const inserted = this->Bind(cm::string_view(name), ref);
  95. assert(inserted), (void)inserted;
  96. return *this;
  97. }
  98. template <typename Range>
  99. void Parse(Range const& args,
  100. std::vector<std::string>* unparsedArguments = nullptr,
  101. std::vector<std::string>* keywordsMissingValue = nullptr) const
  102. {
  103. ArgumentParser::Instance instance(this->Bindings);
  104. for (cm::string_view arg : args) {
  105. instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue);
  106. }
  107. }
  108. protected:
  109. template <typename T>
  110. bool Bind(cm::string_view name, T& ref)
  111. {
  112. return this->Bindings
  113. .Emplace(name,
  114. [&ref](ArgumentParser::Instance& instance, void*) {
  115. instance.Bind(ref);
  116. })
  117. .second;
  118. }
  119. private:
  120. ArgumentParser::ActionMap Bindings;
  121. };
  122. #endif