summaryrefslogtreecommitdiffstats
path: root/Source/cmArgumentParser.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmArgumentParser.h')
-rw-r--r--Source/cmArgumentParser.h143
1 files changed, 143 insertions, 0 deletions
diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h
new file mode 100644
index 0000000..6cfe946
--- /dev/null
+++ b/Source/cmArgumentParser.h
@@ -0,0 +1,143 @@
+/* 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