浏览代码

cmArgumentParser: Offer binding for list of parsed keywords

Some clients ask for this list in their `Parse()` call.
Offer them a way to express this request as a binding.
Brad King 3 年之前
父节点
当前提交
f7e81802f2
共有 3 个文件被更改,包括 50 次插入0 次删除
  1. 3 0
      Source/cmArgumentParser.cxx
  2. 24 0
      Source/cmArgumentParser.h
  3. 23 0
      Tests/CMakeLib/testArgumentParser.cxx

+ 3 - 0
Source/cmArgumentParser.cxx

@@ -86,6 +86,9 @@ void Instance::Consume(cm::string_view arg)
     if (this->ParsedKeywords != nullptr) {
     if (this->ParsedKeywords != nullptr) {
       this->ParsedKeywords->emplace_back(it->first);
       this->ParsedKeywords->emplace_back(it->first);
     }
     }
+    if (this->Bindings.ParsedKeyword) {
+      this->Bindings.ParsedKeyword(*this, it->first);
+    }
     it->second(*this);
     it->second(*this);
     return;
     return;
   }
   }

+ 24 - 0
Source/cmArgumentParser.h

@@ -81,6 +81,7 @@ class ActionMap
 public:
 public:
   KeywordActionMap Keywords;
   KeywordActionMap Keywords;
   KeywordNameAction KeywordMissingValue;
   KeywordNameAction KeywordMissingValue;
+  KeywordNameAction ParsedKeyword;
 };
 };
 
 
 class Base
 class Base
@@ -103,6 +104,12 @@ public:
     static_cast<void>(inserted);
     static_cast<void>(inserted);
   }
   }
 
 
+  void BindParsedKeyword(KeywordNameAction action)
+  {
+    assert(!this->Bindings.ParsedKeyword);
+    this->Bindings.ParsedKeyword = std::move(action);
+  }
+
   void BindKeywordMissingValue(KeywordNameAction action)
   void BindKeywordMissingValue(KeywordNameAction action)
   {
   {
     assert(!this->Bindings.KeywordMissingValue);
     assert(!this->Bindings.KeywordMissingValue);
@@ -187,6 +194,16 @@ public:
     return *this;
     return *this;
   }
   }
 
 
+  cmArgumentParser& BindParsedKeywords(
+    std::vector<cm::string_view> Result::*member)
+  {
+    this->Base::BindParsedKeyword(
+      [member](Instance& instance, cm::string_view arg) {
+        (static_cast<Result*>(instance.Result)->*member).emplace_back(arg);
+      });
+    return *this;
+  }
+
   template <typename Range>
   template <typename Range>
   bool Parse(Result& result, Range const& args,
   bool Parse(Result& result, Range const& args,
              std::vector<std::string>* unparsedArguments,
              std::vector<std::string>* unparsedArguments,
@@ -221,6 +238,13 @@ public:
     return *this;
     return *this;
   }
   }
 
 
+  cmArgumentParser& BindParsedKeywords(std::vector<cm::string_view>& ref)
+  {
+    this->Base::BindParsedKeyword(
+      [&ref](Instance&, cm::string_view arg) { ref.emplace_back(arg); });
+    return *this;
+  }
+
   template <typename Range>
   template <typename Range>
   ParseResult Parse(
   ParseResult Parse(
     Range const& args, std::vector<std::string>* unparsedArguments,
     Range const& args, std::vector<std::string>* unparsedArguments,

+ 23 - 0
Tests/CMakeLib/testArgumentParser.cxx

@@ -38,6 +38,8 @@ struct Result : public ArgumentParser::ParseResult
   std::vector<std::vector<std::string>> Multi2;
   std::vector<std::vector<std::string>> Multi2;
   cm::optional<std::vector<std::vector<std::string>>> Multi3;
   cm::optional<std::vector<std::vector<std::string>>> Multi3;
   cm::optional<std::vector<std::vector<std::string>>> Multi4;
   cm::optional<std::vector<std::vector<std::string>>> Multi4;
+
+  std::vector<cm::string_view> ParsedKeywords;
 };
 };
 
 
 std::initializer_list<cm::string_view> const args = {
 std::initializer_list<cm::string_view> const args = {
@@ -67,6 +69,23 @@ bool verifyResult(Result const& result,
 {
 {
   static std::vector<std::string> const foobar = { "foo", "bar" };
   static std::vector<std::string> const foobar = { "foo", "bar" };
   static std::vector<std::string> const barfoo = { "bar", "foo" };
   static std::vector<std::string> const barfoo = { "bar", "foo" };
+  static std::vector<cm::string_view> const parsedKeywords = {
+    /* clang-format off */
+    "OPTION_1",
+    "STRING_1",
+    "STRING_2",
+    "STRING_4",
+    "LIST_1",
+    "LIST_2",
+    "LIST_3",
+    "LIST_3",
+    "LIST_4",
+    "LIST_6",
+    "MULTI_2",
+    "MULTI_3",
+    "MULTI_3",
+    /* clang-format on */
+  };
   static std::map<cm::string_view, std::string> const keywordErrors = {
   static std::map<cm::string_view, std::string> const keywordErrors = {
     { "STRING_1"_s, "  missing required value\n" },
     { "STRING_1"_s, "  missing required value\n" },
     { "LIST_1"_s, "  missing required value\n" },
     { "LIST_1"_s, "  missing required value\n" },
@@ -114,6 +133,8 @@ bool verifyResult(Result const& result,
   ASSERT_TRUE(unparsedArguments.size() == 1);
   ASSERT_TRUE(unparsedArguments.size() == 1);
   ASSERT_TRUE(unparsedArguments[0] == "bar");
   ASSERT_TRUE(unparsedArguments[0] == "bar");
 
 
+  ASSERT_TRUE(result.ParsedKeywords == parsedKeywords);
+
   ASSERT_TRUE(result.GetKeywordErrors().size() == keywordErrors.size());
   ASSERT_TRUE(result.GetKeywordErrors().size() == keywordErrors.size());
   for (auto const& ke : result.GetKeywordErrors()) {
   for (auto const& ke : result.GetKeywordErrors()) {
     auto const ki = keywordErrors.find(ke.first);
     auto const ki = keywordErrors.find(ke.first);
@@ -147,6 +168,7 @@ bool testArgumentParserDynamic()
       .Bind("MULTI_2"_s, result.Multi2)
       .Bind("MULTI_2"_s, result.Multi2)
       .Bind("MULTI_3"_s, result.Multi3)
       .Bind("MULTI_3"_s, result.Multi3)
       .Bind("MULTI_4"_s, result.Multi4)
       .Bind("MULTI_4"_s, result.Multi4)
+      .BindParsedKeywords(result.ParsedKeywords)
       .Parse(args, &unparsedArguments);
       .Parse(args, &unparsedArguments);
 
 
   return verifyResult(result, unparsedArguments);
   return verifyResult(result, unparsedArguments);
@@ -170,6 +192,7 @@ static auto const parserStatic = //
     .Bind("MULTI_2"_s, &Result::Multi2)
     .Bind("MULTI_2"_s, &Result::Multi2)
     .Bind("MULTI_3"_s, &Result::Multi3)
     .Bind("MULTI_3"_s, &Result::Multi3)
     .Bind("MULTI_4"_s, &Result::Multi4)
     .Bind("MULTI_4"_s, &Result::Multi4)
+    .BindParsedKeywords(&Result::ParsedKeywords)
   /* keep semicolon on own line */;
   /* keep semicolon on own line */;
 
 
 bool testArgumentParserStatic()
 bool testArgumentParserStatic()