diff --git a/db/config.cc b/db/config.cc index 15407650a8..0c8de58739 100644 --- a/db/config.cc +++ b/db/config.cc @@ -273,6 +273,34 @@ const config_type& config_type_for +const config_type& config_type_for>() { + static config_type ct( + "experimental feature", printable_to_json>); + return ct; +} + +template <> +const config_type& config_type_for>() { + static config_type ct( + "replication strategy", printable_to_json>); + return ct; +} + +template <> +const config_type& config_type_for>() { + static config_type ct( + "consistency level", printable_to_json>); + return ct; +} + +template <> +const config_type& config_type_for() { + static config_type ct( + "error injection", printable_to_json); + return ct; +} + template <> const config_type& config_type_for>() { static config_type ct( @@ -1942,6 +1970,47 @@ std::unordered_map db::tablets_mode_t::map() template struct utils::config_file::named_value; +// Explicit instantiation definitions for all named_value specializations +// declared extern in config_file.hh and config.hh. This file is the only +// translation unit that includes config_file_impl.hh (which contains the +// full template bodies), so all the heavy boost / yaml-cpp machinery is +// compiled exactly once here instead of in every TU that includes config.hh. + +// Primitive / standard types (extern-declared in utils/config_file.hh): +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; + +// db-specific types (extern-declared in db/config.hh): +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value>; +template struct utils::config_file::named_value>; +template struct utils::config_file::named_value>; +template struct utils::config_file::named_value>; +template struct utils::config_file::named_value>; +template struct utils::config_file::named_value; +template struct utils::config_file::named_value>>; +template struct utils::config_file::named_value>>; +template struct utils::config_file::named_value>>; +template struct utils::config_file::named_value>; +template struct utils::config_file::named_value>>; +template struct utils::config_file::named_value>; +template struct utils::config_file::named_value>; + namespace utils { sstring diff --git a/db/config.hh b/db/config.hh index 4c4d5884af..e3a57e7ec5 100644 --- a/db/config.hh +++ b/db/config.hh @@ -689,3 +689,42 @@ future<> update_relabel_config_from_file(const std::string& name); std::vector split_comma_separated_list(std::string_view comma_separated_list); } // namespace utils + +namespace utils { + +// Declaration of the explicit specialization for seed_provider_type's +// add_command_line_option must appear before the extern template declaration +// below, to satisfy [temp.expl.spec]: a member specialization must be +// declared before any explicit instantiation (including extern template) of +// the enclosing class template. +template <> +void config_file::named_value::add_command_line_option( + boost::program_options::options_description_easy_init&); + +} // namespace utils + +// Explicit instantiation declarations for named_value specializations +// that use db-specific types. The definitions live in db/config.cc. +// Together with the declarations in utils/config_file.hh (for primitive +// types), this ensures the heavy template bodies from config_file_impl.hh +// (boost::program_options, boost::lexical_cast, boost::regex, yaml-cpp) +// are compiled only once. +extern template struct utils::config_file::named_value; +extern template struct utils::config_file::named_value; +extern template struct utils::config_file::named_value; +extern template struct utils::config_file::named_value; +extern template struct utils::config_file::named_value; +extern template struct utils::config_file::named_value; +extern template struct utils::config_file::named_value>; +extern template struct utils::config_file::named_value>; +extern template struct utils::config_file::named_value>; +extern template struct utils::config_file::named_value>; +extern template struct utils::config_file::named_value>; +extern template struct utils::config_file::named_value; +extern template struct utils::config_file::named_value>>; +extern template struct utils::config_file::named_value>>; +extern template struct utils::config_file::named_value>>; +extern template struct utils::config_file::named_value>; +extern template struct utils::config_file::named_value>>; +extern template struct utils::config_file::named_value>; +extern template struct utils::config_file::named_value>; diff --git a/ent/encryption/encryption_config.cc b/ent/encryption/encryption_config.cc index 6978efdd0a..549a440d01 100644 --- a/ent/encryption/encryption_config.cc +++ b/ent/encryption/encryption_config.cc @@ -175,3 +175,5 @@ public: }; } } cfg; + +template struct utils::config_file::named_value; diff --git a/ent/encryption/encryption_config.hh b/ent/encryption/encryption_config.hh index 5975cc4faf..86fd892b95 100644 --- a/ent/encryption/encryption_config.hh +++ b/ent/encryption/encryption_config.hh @@ -32,3 +32,5 @@ public: }; } + +extern template struct utils::config_file::named_value; diff --git a/utils/config_file.hh b/utils/config_file.hh index 9a99ebf3da..9d70ec47c0 100644 --- a/utils/config_file.hh +++ b/utils/config_file.hh @@ -168,96 +168,40 @@ public: config_source _source = config_source::None; value_status _value_status; struct the_value_type final : any_value { - the_value_type(T value) : value(std::move(value)) {} + the_value_type(T value); utils::updateable_value_source value; - virtual std::unique_ptr clone() const override { - return std::make_unique(value()); - } - virtual void update_from(const any_value* source) override { - auto typed_source = static_cast(source); - value.set(typed_source->value()); - } + std::unique_ptr clone() const override; + void update_from(const any_value* source) override; }; liveness _liveness; std::vector _allowed_values; protected: - updateable_value_source& the_value() { - any_value* av = _cf->_per_shard_values[_cf->s_shard_id][_per_shard_values_offset].get(); - return static_cast(av)->value; - } - const updateable_value_source& the_value() const { - return const_cast(this)->the_value(); - } - virtual const void* current_value() const override { - return &the_value().get(); - } + updateable_value_source& the_value(); + const updateable_value_source& the_value() const; + const void* current_value() const override; public: typedef T type; typedef named_value MyType; named_value(config_file* file, std::string_view name, std::string_view alias, liveness liveness_, value_status vs, const T& t = T(), std::string_view desc = {}, - std::initializer_list allowed_values = {}) - : config_src(file, name, alias, &config_type_for(), desc) - , _value_status(vs) - , _liveness(liveness_) - , _allowed_values(std::move(allowed_values)) { - file->add(*this, std::make_unique(std::move(t))); - } + std::initializer_list allowed_values = {}); named_value(config_file* file, std::string_view name, liveness liveness_, value_status vs, const T& t = T(), std::string_view desc = {}, - std::initializer_list allowed_values = {}) - : named_value(file, name, {}, liveness_, vs, t, desc, std::move(allowed_values)) { - } + std::initializer_list allowed_values = {}); named_value(config_file* file, std::string_view name, std::string_view alias, value_status vs, const T& t = T(), std::string_view desc = {}, - std::initializer_list allowed_values = {}) - : named_value(file, name, alias, liveness::MustRestart, vs, t, desc, allowed_values) { - } + std::initializer_list allowed_values = {}); named_value(config_file* file, std::string_view name, value_status vs, const T& t = T(), std::string_view desc = {}, - std::initializer_list allowed_values = {}) - : named_value(file, name, {}, liveness::MustRestart, vs, t, desc, allowed_values) { - } - value_status status() const noexcept override { - return _value_status; - } - config_source source() const noexcept override { - return _source; - } - bool is_set() const { - return _source > config_source::None; - } - MyType & operator()(const T& t, config_source src = config_source::Internal) { - if (!_allowed_values.empty() && std::find(_allowed_values.begin(), _allowed_values.end(), t) == _allowed_values.end()) { - throw std::invalid_argument(fmt::format("Invalid value for {}: got {} which is not inside the set of allowed values {}", name(), t, _allowed_values)); - } - the_value().set(t); - if (src > config_source::None) { - _source = src; - } - return *this; - } - MyType & operator()(T&& t, config_source src = config_source::Internal) { - if (!_allowed_values.empty() && std::find(_allowed_values.begin(), _allowed_values.end(), t) == _allowed_values.end()) { - throw std::invalid_argument(fmt::format("Invalid value for {}: got {} which is not inside the set of allowed values {}", name(), t, _allowed_values)); - } - the_value().set(std::move(t)); - if (src > config_source::None) { - _source = src; - } - return *this; - } - void set(T&& t, config_source src = config_source::None) { - operator()(std::move(t), src); - } - const T& operator()() const { - return the_value().get(); - } + std::initializer_list allowed_values = {}); + value_status status() const noexcept override; + config_source source() const noexcept override; + bool is_set() const; + MyType & operator()(const T& t, config_source src = config_source::Internal); + MyType & operator()(T&& t, config_source src = config_source::Internal); + void set(T&& t, config_source src = config_source::None); + const T& operator()() const; - operator updateable_value() const & { - return updateable_value(the_value()); - } + operator updateable_value() const &; - observer observe(std::function callback) const { - return the_value().observe(std::move(callback)); - } + observer observe(std::function callback) const; void add_command_line_option(bpo::options_description_easy_init&) override; void set_value(const YAML::Node&, config_source) override; @@ -336,5 +280,24 @@ const config_file::named_value& operator||(const config_file::named_value& extern template struct config_file::named_value; +// Explicit instantiation declarations for the most common named_value +// specializations. The definitions are in db/config.cc (which is the only +// TU that includes config_file_impl.hh and therefore has the full template +// bodies). This avoids re-compiling the heavy boost::program_options / +// boost::lexical_cast / yaml-cpp machinery in every TU that includes +// config.hh. +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; +extern template struct config_file::named_value; + } diff --git a/utils/config_file_impl.hh b/utils/config_file_impl.hh index e5841d8473..bd9fe23a10 100644 --- a/utils/config_file_impl.hh +++ b/utils/config_file_impl.hh @@ -186,6 +186,125 @@ sstring hyphenate(const std::string_view&); } +template +utils::config_file::named_value::the_value_type::the_value_type(T value) + : value(std::move(value)) {} + +template +std::unique_ptr +utils::config_file::named_value::the_value_type::clone() const { + return std::make_unique(value()); +} + +template +void utils::config_file::named_value::the_value_type::update_from(const any_value* source) { + auto typed_source = static_cast(source); + value.set(typed_source->value()); +} + +template +utils::updateable_value_source& utils::config_file::named_value::the_value() { + any_value* av = _cf->_per_shard_values[_cf->s_shard_id][_per_shard_values_offset].get(); + return static_cast(av)->value; +} + +template +const utils::updateable_value_source& utils::config_file::named_value::the_value() const { + return const_cast(this)->the_value(); +} + +template +const void* utils::config_file::named_value::current_value() const { + return &the_value().get(); +} + +template +utils::config_file::named_value::named_value(config_file* file, std::string_view name, std::string_view alias, liveness liveness_, value_status vs, const T& t, std::string_view desc, + std::initializer_list allowed_values) + : config_src(file, name, alias, &config_type_for(), desc) + , _value_status(vs) + , _liveness(liveness_) + , _allowed_values(std::move(allowed_values)) { + file->add(*this, std::make_unique(std::move(t))); +} + +template +utils::config_file::named_value::named_value(config_file* file, std::string_view name, liveness liveness_, value_status vs, const T& t, std::string_view desc, + std::initializer_list allowed_values) + : named_value(file, name, {}, liveness_, vs, t, desc, std::move(allowed_values)) { +} + +template +utils::config_file::named_value::named_value(config_file* file, std::string_view name, std::string_view alias, value_status vs, const T& t, std::string_view desc, + std::initializer_list allowed_values) + : named_value(file, name, alias, liveness::MustRestart, vs, t, desc, allowed_values) { +} + +template +utils::config_file::named_value::named_value(config_file* file, std::string_view name, value_status vs, const T& t, std::string_view desc, + std::initializer_list allowed_values) + : named_value(file, name, {}, liveness::MustRestart, vs, t, desc, allowed_values) { +} + +template +utils::config_file::value_status utils::config_file::named_value::status() const noexcept { + return _value_status; +} + +template +utils::config_file::config_source utils::config_file::named_value::source() const noexcept { + return _source; +} + +template +bool utils::config_file::named_value::is_set() const { + return _source > config_source::None; +} + +template +utils::config_file::named_value& utils::config_file::named_value::operator()(const T& t, config_source src) { + if (!_allowed_values.empty() && std::find(_allowed_values.begin(), _allowed_values.end(), t) == _allowed_values.end()) { + throw std::invalid_argument(fmt::format("Invalid value for {}: got {} which is not inside the set of allowed values {}", name(), t, _allowed_values)); + } + the_value().set(t); + if (src > config_source::None) { + _source = src; + } + return *this; +} + +template +utils::config_file::named_value& utils::config_file::named_value::operator()(T&& t, config_source src) { + if (!_allowed_values.empty() && std::find(_allowed_values.begin(), _allowed_values.end(), t) == _allowed_values.end()) { + throw std::invalid_argument(fmt::format("Invalid value for {}: got {} which is not inside the set of allowed values {}", name(), t, _allowed_values)); + } + the_value().set(std::move(t)); + if (src > config_source::None) { + _source = src; + } + return *this; +} + +template +void utils::config_file::named_value::set(T&& t, config_source src) { + operator()(std::move(t), src); +} + +template +const T& utils::config_file::named_value::operator()() const { + return the_value().get(); +} + +template +utils::config_file::named_value::operator utils::updateable_value() const & { + return updateable_value(the_value()); +} + +template +utils::observer utils::config_file::named_value::observe(std::function callback) const { + return the_value().observe(std::move(callback)); +} + template void utils::config_file::named_value::add_command_line_option(boost::program_options::options_description_easy_init& init) { const auto hyphenated_name = hyphenate(name());