cmArgumentParser.h 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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/string_view>
  11. #include <cmext/string_view>
  12. namespace ArgumentParser {
  13. using StringList = std::vector<std::string>;
  14. using MultiStringList = std::vector<StringList>;
  15. class Instance;
  16. using Action = std::function<void(Instance&, void*)>;
  17. // using ActionMap = cm::flat_map<cm::string_view, Action>;
  18. class ActionMap : public std::vector<std::pair<cm::string_view, Action>>
  19. {
  20. public:
  21. std::pair<iterator, bool> Emplace(cm::string_view name, Action action);
  22. const_iterator Find(cm::string_view name) const;
  23. };
  24. class Instance
  25. {
  26. public:
  27. Instance(ActionMap const& bindings)
  28. : Bindings(bindings)
  29. {
  30. }
  31. void Bind(bool& val);
  32. void Bind(std::string& val);
  33. void Bind(StringList& val);
  34. void Bind(MultiStringList& val);
  35. void Consume(cm::string_view arg, void* result,
  36. std::vector<std::string>* unparsedArguments,
  37. std::vector<cm::string_view>* keywordsMissingValue,
  38. std::vector<cm::string_view>* parsedKeywords);
  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<cm::string_view>* keywordsMissingValue = nullptr,
  69. std::vector<cm::string_view>* parsedKeywords = nullptr) const
  70. {
  71. ArgumentParser::Instance instance(this->Bindings);
  72. for (cm::string_view arg : args) {
  73. instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue,
  74. parsedKeywords);
  75. }
  76. }
  77. template <typename Range>
  78. Result Parse(Range const& args,
  79. std::vector<std::string>* unparsedArguments = nullptr,
  80. std::vector<cm::string_view>* keywordsMissingValue = nullptr,
  81. std::vector<cm::string_view>* parsedKeywords = nullptr) const
  82. {
  83. Result result;
  84. this->Parse(result, args, unparsedArguments, keywordsMissingValue,
  85. parsedKeywords);
  86. return result;
  87. }
  88. private:
  89. ArgumentParser::ActionMap Bindings;
  90. };
  91. template <>
  92. class cmArgumentParser<void>
  93. {
  94. public:
  95. template <typename T>
  96. cmArgumentParser& Bind(cm::static_string_view name, T& ref)
  97. {
  98. bool const inserted = this->Bind(cm::string_view(name), ref);
  99. assert(inserted), (void)inserted;
  100. return *this;
  101. }
  102. template <typename Range>
  103. void Parse(Range const& args,
  104. std::vector<std::string>* unparsedArguments = nullptr,
  105. std::vector<cm::string_view>* keywordsMissingValue = nullptr,
  106. std::vector<cm::string_view>* parsedKeywords = nullptr) const
  107. {
  108. ArgumentParser::Instance instance(this->Bindings);
  109. for (cm::string_view arg : args) {
  110. instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue,
  111. parsedKeywords);
  112. }
  113. }
  114. protected:
  115. template <typename T>
  116. bool Bind(cm::string_view name, T& ref)
  117. {
  118. return this->Bindings
  119. .Emplace(name,
  120. [&ref](ArgumentParser::Instance& instance, void*) {
  121. instance.Bind(ref);
  122. })
  123. .second;
  124. }
  125. private:
  126. ArgumentParser::ActionMap Bindings;
  127. };