| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #ifndef cmArgumentParser_h |
| #define cmArgumentParser_h |
| |
| #include "cmConfigure.h" // IWYU pragma: keep |
| |
| #include "cm_static_string_view.hxx" |
| #include "cm_string_view.hxx" |
| |
| #include <cassert> |
| #include <functional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| namespace ArgumentParser { |
| |
| using StringList = std::vector<std::string>; |
| using MultiStringList = std::vector<StringList>; |
| |
| class Instance; |
| using Action = std::function<void(Instance&, void*)>; |
| |
| // using ActionMap = cm::flat_map<cm::string_view, Action>; |
| class ActionMap : public std::vector<std::pair<cm::string_view, Action>> |
| { |
| public: |
| std::pair<iterator, bool> Emplace(cm::string_view name, Action action); |
| const_iterator Find(cm::string_view name) const; |
| }; |
| |
| class Instance |
| { |
| public: |
| Instance(ActionMap const& bindings) |
| : Bindings(bindings) |
| { |
| } |
| |
| void Bind(bool& val); |
| void Bind(std::string& val); |
| void Bind(StringList& val); |
| void Bind(MultiStringList& val); |
| |
| void Consume(cm::string_view arg, void* result, |
| std::vector<std::string>* unparsedArguments, |
| std::vector<std::string>* keywordsMissingValue); |
| |
| private: |
| ActionMap const& Bindings; |
| std::string* CurrentString = nullptr; |
| StringList* CurrentList = nullptr; |
| bool ExpectValue = false; |
| }; |
| |
| } // namespace ArgumentParser |
| |
| template <typename Result> |
| class cmArgumentParser |
| { |
| public: |
| // I *think* this function could be made `constexpr` when the code is |
| // compiled as C++20. This would allow building a parser at compile time. |
| template <typename T> |
| cmArgumentParser& Bind(cm::static_string_view name, T Result::*member) |
| { |
| bool const inserted = |
| this->Bindings |
| .Emplace(name, |
| [member](ArgumentParser::Instance& instance, void* result) { |
| instance.Bind(static_cast<Result*>(result)->*member); |
| }) |
| .second; |
| assert(inserted), (void)inserted; |
| return *this; |
| } |
| |
| template <typename Range> |
| void Parse(Result& result, Range const& args, |
| std::vector<std::string>* unparsedArguments = nullptr, |
| std::vector<std::string>* keywordsMissingValue = nullptr) const |
| { |
| ArgumentParser::Instance instance(this->Bindings); |
| for (cm::string_view arg : args) { |
| instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue); |
| } |
| } |
| |
| template <typename Range> |
| Result Parse(Range const& args, |
| std::vector<std::string>* unparsedArguments = nullptr, |
| std::vector<std::string>* keywordsMissingValue = nullptr) const |
| { |
| Result result; |
| this->Parse(result, args, unparsedArguments, keywordsMissingValue); |
| return result; |
| } |
| |
| private: |
| ArgumentParser::ActionMap Bindings; |
| }; |
| |
| template <> |
| class cmArgumentParser<void> |
| { |
| public: |
| template <typename T> |
| cmArgumentParser& Bind(cm::static_string_view name, T& ref) |
| { |
| bool const inserted = this->Bind(cm::string_view(name), ref); |
| assert(inserted), (void)inserted; |
| return *this; |
| } |
| |
| template <typename Range> |
| void Parse(Range const& args, |
| std::vector<std::string>* unparsedArguments = nullptr, |
| std::vector<std::string>* keywordsMissingValue = nullptr) const |
| { |
| ArgumentParser::Instance instance(this->Bindings); |
| for (cm::string_view arg : args) { |
| instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue); |
| } |
| } |
| |
| protected: |
| template <typename T> |
| bool Bind(cm::string_view name, T& ref) |
| { |
| return this->Bindings |
| .Emplace(name, |
| [&ref](ArgumentParser::Instance& instance, void*) { |
| instance.Bind(ref); |
| }) |
| .second; |
| } |
| |
| private: |
| ArgumentParser::ActionMap Bindings; |
| }; |
| |
| #endif |