| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing for details. */ | |
| #pragma once | |
| #include "cmConfigure.h" // IWYU pragma: keep | |
| #include <cassert> | |
| #include <functional> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/string_view> | |
| #include <cmext/string_view> | |
| 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, | |
| std::vector<std::string>* parsedKeywords); | |
| 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, | |
| std::vector<std::string>* parsedKeywords = nullptr) const | |
| { | |
| ArgumentParser::Instance instance(this->Bindings); | |
| for (cm::string_view arg : args) { | |
| instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue, | |
| parsedKeywords); | |
| } | |
| } | |
| template <typename Range> | |
| Result Parse(Range const& args, | |
| std::vector<std::string>* unparsedArguments = nullptr, | |
| std::vector<std::string>* keywordsMissingValue = nullptr, | |
| std::vector<std::string>* parsedKeywords = nullptr) const | |
| { | |
| Result result; | |
| this->Parse(result, args, unparsedArguments, keywordsMissingValue, | |
| parsedKeywords); | |
| 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, | |
| std::vector<std::string>* parsedKeywords = nullptr) const | |
| { | |
| ArgumentParser::Instance instance(this->Bindings); | |
| for (cm::string_view arg : args) { | |
| instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue, | |
| parsedKeywords); | |
| } | |
| } | |
| 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; | |
| }; |