149 lines
4.2 KiB
C++
149 lines
4.2 KiB
C++
#ifndef YAZE_UTIL_FLAG_H_
|
|
#define YAZE_UTIL_FLAG_H_
|
|
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace yaze {
|
|
namespace util {
|
|
|
|
// Base interface for all flags.
|
|
class IFlag {
|
|
public:
|
|
virtual ~IFlag() = default;
|
|
|
|
// Returns the full name (e.g. "--count") used for this flag.
|
|
virtual const std::string& name() const = 0;
|
|
|
|
// Returns help text describing how to use this flag.
|
|
virtual const std::string& help() const = 0;
|
|
|
|
// Parses a string value into the underlying type.
|
|
virtual void ParseValue(const std::string& text) = 0;
|
|
};
|
|
|
|
template <typename T>
|
|
class Flag : public IFlag {
|
|
public:
|
|
Flag(const std::string& name, const T& default_value,
|
|
const std::string& help_text)
|
|
: name_(name),
|
|
value_(default_value),
|
|
default_(default_value),
|
|
help_(help_text) {}
|
|
|
|
const std::string& name() const override { return name_; }
|
|
const std::string& help() const override { return help_; }
|
|
|
|
// Attempts to parse a string into type T using a stringstream.
|
|
void ParseValue(const std::string& text) override {
|
|
std::stringstream ss(text);
|
|
T parsed;
|
|
if (!(ss >> parsed)) {
|
|
throw std::runtime_error("Failed to parse flag: " + name_);
|
|
}
|
|
value_ = parsed;
|
|
}
|
|
|
|
// Returns the current (parsed or default) value of the flag.
|
|
const T& Get() const { return value_; }
|
|
|
|
private:
|
|
std::string name_;
|
|
T value_;
|
|
T default_;
|
|
std::string help_;
|
|
};
|
|
|
|
class FlagRegistry {
|
|
public:
|
|
// Registers a flag in the global registry.
|
|
// The return type is a pointer to the newly created flag.
|
|
template <typename T>
|
|
Flag<T>* RegisterFlag(const std::string& name, const T& default_value,
|
|
const std::string& help_text) {
|
|
auto flag = std::make_unique<Flag<T>>(name, default_value, help_text);
|
|
Flag<T>* raw_ptr =
|
|
flag.get(); // We keep a non-owning pointer to use later.
|
|
flags_[name] = std::move(flag);
|
|
return raw_ptr;
|
|
}
|
|
|
|
// Returns a shared interface pointer if found, otherwise nullptr.
|
|
IFlag* GetFlag(const std::string& name) const {
|
|
auto it = flags_.find(name);
|
|
if (it == flags_.end()) {
|
|
return nullptr;
|
|
}
|
|
return it->second.get();
|
|
}
|
|
|
|
// Returns all registered flags for iteration, help text, etc.
|
|
std::vector<IFlag*> AllFlags() const {
|
|
std::vector<IFlag*> result;
|
|
result.reserve(flags_.size());
|
|
for (auto const& kv : flags_) {
|
|
result.push_back(kv.second.get());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
std::unordered_map<std::string, std::unique_ptr<IFlag>> flags_;
|
|
};
|
|
|
|
inline FlagRegistry* global_flag_registry() {
|
|
// Guaranteed to be initialized once per process.
|
|
static FlagRegistry* registry = new FlagRegistry();
|
|
return registry;
|
|
}
|
|
|
|
// Defines a global Flag<type>* FLAGS_<name> and registers it.
|
|
#define DEFINE_FLAG(type, name, default_val, help_text) \
|
|
yaze::util::Flag<type>* FLAGS_##name = \
|
|
yaze::util::global_flag_registry()->RegisterFlag<type>( \
|
|
"--" #name, (default_val), (help_text))
|
|
|
|
// Retrieves the current value of a declared flag.
|
|
#define FLAG_VALUE(name) (FLAGS_##name->Get())
|
|
|
|
class FlagParser {
|
|
public:
|
|
explicit FlagParser(FlagRegistry* registry) : registry_(registry) {}
|
|
|
|
// Parses flags out of the given command line arguments.
|
|
void Parse(int argc, char** argv) {
|
|
std::vector<std::string> tokens;
|
|
for (int i = 0; i < argc; i++) {
|
|
tokens.push_back(argv[i]);
|
|
}
|
|
Parse(&tokens);
|
|
}
|
|
|
|
// Parses flags out of the given token list. Recognizes forms:
|
|
// --flag=value or --flag value
|
|
// Any token not recognized as a flag is left in `leftover`.
|
|
void Parse(std::vector<std::string>* tokens);
|
|
|
|
private:
|
|
FlagRegistry* registry_;
|
|
|
|
// Checks if there is an '=' sign in the token, extracting flag name and
|
|
// value. e.g. "--count=42" -> flag_name = "--count", value_string = "42"
|
|
// returns true if '=' was found
|
|
bool ExtractFlagAndValue(const std::string& token, std::string* flag_name,
|
|
std::string* value_string);
|
|
|
|
// Mode flag '-'
|
|
bool ExtractFlag(const std::string& token, std::string* flag_name);
|
|
};
|
|
|
|
} // namespace util
|
|
} // namespace yaze
|
|
|
|
#endif // YAZE_UTIL_FLAG_H_
|