From 999ae9661388840326b2e849b7517b8a25891494 Mon Sep 17 00:00:00 2001 From: Celtic Minstrel Date: Sat, 20 May 2017 21:08:34 -0400 Subject: [PATCH] Implement variadic initializer list for config class --- src/config.cpp | 10 ++++++ src/config.hpp | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/src/config.cpp b/src/config.cpp index f9f104510f71..8d46ccfccd05 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -39,6 +39,16 @@ static lg::log_domain log_config("config"); #define ERR_CF LOG_STREAM(err, log_config) #define DBG_CF LOG_STREAM(debug, log_config) +// Quick tests of config initializer list syntax + +config cfg1 {"key", 5}; +config cfg2 {"key", true}; +config cfg3 {"key", "value"}; +config cfg4 {"key1", "value", "key2", true, "key3", 42}; + +config cfg5 {"tag", config{}}; +config cfg6 {"tag", config{"value", 42}, "tag", config{"value", "fifty"}}; + namespace { //std::map::operator[] does not support heterogenous lookup so we need this to work around. template diff --git a/src/config.hpp b/src/config.hpp index cd47b0459261..0612f21c7d05 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -120,6 +121,46 @@ class config * Creates a config object with an empty child of name @a child. */ explicit config(config_key_type child); +#if 0 + /** + * Creates a config with a single key. + * @param key The name of the key + * @param value The key value, or the subchild config + */ + template + config(config_key_type key, T&& val) + { + operator[](key) = std::forward(val); + } + + /** + * Creates a config with a single subchild. + * @param key The name of the subchild + * @param value The subchild config + */ + config(config_key_type tag, const config& val) + { + this->add_child(key, val); + } + + /** + * Creates a config with a single subchild. + * @param key The name of the subchild + * @param value The subchild config + */ + config(config_key_type tag, config&& val) + { + this->add_child(key, val); + } +#endif + + /** + * Creates a config with several attributes and children. + * Pass the keys/tags and values/children alternately. + * @example config("key", 42, "value", config()) + */ + template + explicit config(config_key_type first, T&&... args); ~config(); @@ -744,6 +785,58 @@ class config std::vector ordered_children; }; +namespace detail { + template + struct config_construct_unpacker; + + // A few dummy specializations to trigger SFINAE... + template<> struct config_construct_unpacker {}; + template<> struct config_construct_unpacker {}; + template<> struct config_construct_unpacker {}; + template<> struct config_construct_unpacker {}; + template<> struct config_construct_unpacker {}; + template<> struct config_construct_unpacker {}; + + template<> + struct config_construct_unpacker<> + { + void visit(config&) {} + }; + + template + struct config_construct_unpacker + { + //static_assert(!std::is_same>>::value, "This is the attribute value specialization."); + void visit(config& cfg, K&& key, V&& val, Rest... fwd) + { + cfg[std::forward(key)] = std::forward(val); + detail::config_construct_unpacker unpack; + unpack.visit(cfg, std::forward(fwd)...); + } + }; + + template + struct config_construct_unpacker + { + //static_assert(std::is_same>>::value, "This is the config child specialization."); + void visit(config& cfg, T&& tag, config&& child, Rest... fwd) + { + cfg.add_child(std::forward(tag), std::forward(child)); + detail::config_construct_unpacker unpack; + unpack.visit(cfg, std::forward(fwd)...); + } + }; +} + +template +inline config::config(config_key_type first, T&&... args) +// : config(detail::config_construct_unpacker(args...)) +{ + //static_assert((sizeof...(T) & 1) == 0, "General config constructor requires an even number of arguments"); + detail::config_construct_unpacker unpack; + unpack.visit(*this, first, std::forward(args)...); +} + class variable_set { public: