cmArgumentParser.h 4.2 KB

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