#pragma once #include #include #include #include #include namespace VW { namespace config { struct base_option { base_option(std::string name, size_t type_hash) : m_name(std::move(name)), m_type_hash(type_hash) {} std::string m_name; size_t m_type_hash; std::string m_help = ""; std::string m_short_name = ""; bool m_keep = false; virtual ~base_option() = default; }; template struct typed_option : base_option { typed_option(const std::string& name, T& location) : base_option(name, typeid(T).hash_code()), m_location{location} {} static size_t type_hash() { return typeid(T).hash_code(); } typed_option& default_value(T value) { m_default_value = std::make_shared(value); return *this; } bool default_value_supplied() { return m_default_value.get() != nullptr; } T default_value() { return m_default_value ? *m_default_value : T(); } typed_option& short_name(const std::string& short_name) { m_short_name = short_name; return *this; } typed_option& help(const std::string& help) { m_help = help; return *this; } typed_option& keep(bool keep = true) { m_keep = keep; return *this; } bool value_supplied() { return m_value.get() != nullptr; } typed_option& value(T value) { m_value = std::make_shared(value); return *this; } T value() { return m_value ? *m_value : T(); } T& m_location; private: // Would prefer to use std::optional (C++17) here but we are targeting C++11 std::shared_ptr m_value{nullptr}; std::shared_ptr m_default_value{nullptr}; }; template typed_option make_option(std::string name, T& location) { return typed_option(name, location); } struct option_group_definition { option_group_definition(const std::string& name) : m_name(name) {} template option_group_definition& add(T&& op) { m_options.push_back(std::make_shared::type>(op)); return *this; } template option_group_definition& operator()(T&& op) { add(std::forward(op)); return *this; } std::string m_name; std::vector> m_options; }; struct options_i { virtual void add_and_parse(const option_group_definition& group) = 0; virtual bool was_supplied(const std::string& key) = 0; virtual std::string help() = 0; virtual std::vector> get_all_options() = 0; virtual std::shared_ptr get_option(const std::string& key) = 0; virtual void insert(const std::string& key, const std::string& value) = 0; virtual void replace(const std::string& key, const std::string& value) = 0; template typed_option& get_typed_option(const std::string& key) { base_option& base = *get_option(key); if (base.m_type_hash != typed_option::type_hash()) { throw std::bad_cast(); } return dynamic_cast&>(base); } // Will throw if any options were supplied that do not having a matching argument specification. virtual void check_unregistered() = 0; virtual ~options_i() = default; }; struct options_serializer_i { virtual void add(base_option& argument) = 0; virtual std::string str() = 0; virtual const char* data() = 0; virtual size_t size() = 0; }; template bool operator==(typed_option& lhs, typed_option& rhs) { return lhs.m_name == rhs.m_name && lhs.m_type_hash == rhs.m_type_hash && lhs.m_help == rhs.m_help && lhs.m_short_name == rhs.m_short_name && lhs.m_keep == rhs.m_keep && lhs.default_value() == rhs.default_value(); } template bool operator!=(typed_option& lhs, typed_option& rhs) { return !(lhs == rhs); } bool operator==(const base_option& lhs, const base_option& rhs); bool operator!=(const base_option& lhs, const base_option& rhs); inline bool operator==(const base_option& lhs, const base_option& rhs) { return lhs.m_name == rhs.m_name && lhs.m_type_hash == rhs.m_type_hash && lhs.m_help == rhs.m_help && lhs.m_short_name == rhs.m_short_name && lhs.m_keep == rhs.m_keep; } inline bool operator!=(const base_option& lhs, const base_option& rhs) { return !(lhs == rhs); } } // namespace config } // namespace VW