Files
yaze/src/util/flag.h
2025-03-31 09:42:10 -04:00

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_