You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.8 KiB
144 lines
3.8 KiB
5 years ago
|
/* 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
|