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