#ifndef YAZE_UTIL_FLAG_H_ #define YAZE_UTIL_FLAG_H_ #include #include #include #include #include #include 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 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; } // Set the value directly (used by specializations) void SetValue(const T& val) { value_ = val; } // 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_; }; // Specialization for bool to handle "true"/"false" strings template <> inline void Flag::ParseValue(const std::string& text) { if (text == "true" || text == "1" || text == "yes" || text == "on") { SetValue(true); } else if (text == "false" || text == "0" || text == "no" || text == "off") { SetValue(false); } else { throw std::runtime_error("Failed to parse boolean flag: " + name() + " (expected true/false/1/0/yes/no/on/off, got: " + text + ")"); } } class FlagRegistry { public: // Registers a flag in the global registry. // The return type is a pointer to the newly created flag. template Flag* RegisterFlag(const std::string& name, const T& default_value, const std::string& help_text) { auto flag = std::make_unique>(name, default_value, help_text); Flag* 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 AllFlags() const { std::vector result; result.reserve(flags_.size()); for (auto const& kv : flags_) { result.push_back(kv.second.get()); } return result; } private: std::unordered_map> flags_; }; inline FlagRegistry* global_flag_registry() { // Guaranteed to be initialized once per process. static FlagRegistry* registry = new FlagRegistry(); return registry; } // Defines a global Flag* FLAGS_ and registers it. #define DEFINE_FLAG(type, name, default_val, help_text) \ yaze::util::Flag* FLAGS_##name = \ yaze::util::global_flag_registry()->RegisterFlag( \ "--" #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 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* 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_