/* 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 #include #include #include #include #include #include #include namespace ArgumentParser { class Instance; using Action = std::function; // using ActionMap = cm::flat_map; class ActionMap : public std::vector> { public: std::pair 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(std::vector& val); void Bind(std::vector>& val); // cm::optional<> records the presence the keyword to which it binds. template void Bind(cm::optional& optVal) { if (!optVal) { optVal.emplace(); } this->Bind(*optVal); } void Consume(cm::string_view arg, void* result, std::vector* unparsedArguments, std::vector* keywordsMissingValue, std::vector* parsedKeywords); private: ActionMap const& Bindings; std::string* CurrentString = nullptr; std::vector* CurrentList = nullptr; bool ExpectValue = false; }; } // namespace ArgumentParser template 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 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)->*member); }) .second; assert(inserted), (void)inserted; return *this; } template void Parse(Result& result, Range const& args, std::vector* unparsedArguments, std::vector* keywordsMissingValue = nullptr, std::vector* parsedKeywords = nullptr) const { ArgumentParser::Instance instance(this->Bindings); for (cm::string_view arg : args) { instance.Consume(arg, &result, unparsedArguments, keywordsMissingValue, parsedKeywords); } } template Result Parse(Range const& args, std::vector* unparsedArguments, std::vector* keywordsMissingValue = nullptr, std::vector* parsedKeywords = nullptr) const { Result result; this->Parse(result, args, unparsedArguments, keywordsMissingValue, parsedKeywords); return result; } private: ArgumentParser::ActionMap Bindings; }; template <> class cmArgumentParser { public: template 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 void Parse(Range const& args, std::vector* unparsedArguments, std::vector* keywordsMissingValue = nullptr, std::vector* parsedKeywords = nullptr) const { ArgumentParser::Instance instance(this->Bindings); for (cm::string_view arg : args) { instance.Consume(arg, nullptr, unparsedArguments, keywordsMissingValue, parsedKeywords); } } protected: template 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; };