diff --git a/src/setting.cc b/src/setting.cc index 780e082a..c2092ef9 100644 --- a/src/setting.cc +++ b/src/setting.cc @@ -35,9 +35,8 @@ namespace conky { */ config_settings_t *config_settings; - config_setting_base::config_setting_base(const std::string &name_, - const lua_setter_t &lua_setter_) - : name(name_), lua_setter(lua_setter_) + config_setting_base::config_setting_base(const std::string &name_) + : name(name_) { struct config_settings_constructor { config_settings_constructor() { priv::config_settings = new config_settings_t; } @@ -66,16 +65,14 @@ namespace conky { l.settable(-3); l.pop(); } - } - namespace { /* * Performs the actual assignment of settings. Calls the setting-specific setter after * some sanity-checking. * stack on entry: | ..., new_config_table, key, value, old_value | * stack on exit: | ..., new_config_table, key | */ - void process_setting(lua::state &l, bool init) + void config_setting_base::process_setting(lua::state &l, bool init) { lua::stack_sentry s(l, -2); @@ -92,7 +89,7 @@ namespace conky { return; } - iter->second->lua_setter(&l, init); + iter->second->call_lua_setter(&l, init); l.pushvalue(-2); l.insert(-2); l.rawset(-4); @@ -103,7 +100,7 @@ namespace conky { * stack on entry: | config_table, key, value | * stack on exit: | | */ - int config__newindex(lua::state *l) + int config_setting_base::config__newindex(lua::state *l) { lua::stack_sentry s(*l, -3); l->checkstack(1); @@ -119,9 +116,6 @@ namespace conky { } } - void simple_lua_setter(lua::state *l, bool) - { l->pop(); } - /* * Called after the initial loading of the config file. Performs the initial assignments. * at least one setting should always be registered, so config_settings will not be null @@ -143,7 +137,7 @@ namespace conky { l.pushnil(); while(l.next(-3)) { l.pushnil(); - process_setting(l, true); + priv::config_setting_base::process_setting(l, true); } } l.replace(-2); @@ -153,7 +147,7 @@ namespace conky { l.pushvalue(-1); l.rawsetfield(-2, "__index"); - l.pushfunction(&config__newindex); + l.pushfunction(&priv::config_setting_base::config__newindex); l.rawsetfield(-2, "__newindex"); // conky.config will not be a table, but a userdata with some metamethods diff --git a/src/setting.hh b/src/setting.hh index b5cea3f3..755951f5 100644 --- a/src/setting.hh +++ b/src/setting.hh @@ -33,80 +33,159 @@ namespace conky { - // performs the assignment without any error checking - void simple_lua_setter(lua::state *l, bool init); + void check_config_settings(lua::state &l); - // converts standard lua types to C types template::value, - bool floating_point = std::is_floating_point::value> - struct simple_getter { + bool is_integral = std::is_integral::value, + bool floating_point = std::is_floating_point::value, + bool is_enum = std::is_enum::value> + struct lua_traits + { // integral is here to force the compiler to evaluate the assert at instantiation time - static_assert(integral && false, - "Only specializations for string, integral and floating point types are available"); + static_assert(is_integral && false, + "Only specializations for enum, string, integral and floating point types are available"); }; - // Specialization for integral type. + // specialization for integral types template - struct simple_getter { - static T do_it_def(lua::state *l, T def) - { - if(l->isnil(-1)) - return def; + struct lua_traits { + static const lua::Type type = lua::TNUMBER; - T t = l->tointeger(-1); - l->pop(); - return t; - } - - static T do_it(lua::state *l) - { return do_it_def(l, static_cast(0)); } + static std::pair convert(lua::state *l, int index, const std::string &) + { return {l->tointeger(index), true}; } }; // specialization for floating point types template - struct simple_getter { - static T do_it_def(lua::state *l, T def) - { - if(l->isnil(-1)) - return def; + struct lua_traits { + static const lua::Type type = lua::TNUMBER; - T t = l->tonumber(-1); - l->pop(); - return t; - } - - static T do_it(lua::state *l) - { return do_it_def(l, T(0)); } + static std::pair convert(lua::state *l, int index, const std::string &) + { return {l->tonumber(index), true}; } }; // specialization for std::string template<> - struct simple_getter { - static std::string do_it_def(lua::state *l, const std::string &def) - { - if(l->isnil(-1)) - return def; + struct lua_traits { + static const lua::Type type = lua::TSTRING; - std::string t = l->tostring(-1); - l->pop(); - return t; + static std::pair convert(lua::state *l, int index, const std::string &) + { return {l->tostring(index), true}; } + }; + + // specialization for bool + template<> + struct lua_traits { + static const lua::Type type = lua::TBOOLEAN; + + static std::pair convert(lua::state *l, int index, const std::string &) + { return {l->toboolean(index), true}; } + }; + + // specialization for enums + // to use this, one first has to declare string<->value map + template + struct lua_traits { + static const lua::Type type = lua::TSTRING; + + typedef std::initializer_list> Map; + static Map map; + + static std::pair convert(lua::state *l, int index, const std::string &name) + { + std::string val = l->tostring(index); + + for(auto i = map.begin(); i != map.end(); ++i) { + if(i->first == val) + return {i->second, true}; + } + + std::string msg = "Invalid value '" + val + "' for setting '" + + name + "'. Valid values are: "; + for(auto i = map.begin(); i != map.end(); ++i) { + if(i != map.begin()) + msg += ", "; + msg += "'" + i->first + "'"; + } + msg += "."; + NORM_ERR("%s", msg.c_str()); + + return {T(), false}; + } + }; + + + // standard getters and setters for basic types. They try to do The Right Thing(tm) (accept + // only values of correct type and print an error message otherwise). For something more + // elaborate, one can always write a new accessor class + template> + class simple_accessors { + const T default_value; + bool modifiable; + + std::pair do_convert(lua::state *l, int index, const std::string &name) + { + if(l->isnil(index)) + return {default_value, true}; + + if(l->type(index) != Traits::type) { + NORM_ERR("Invalid value of type '%s' for setting '%s'. " + "Expected value of type '%s'.", l->type_name(l->type(index)), + name.c_str(), l->type_name(Traits::type) ); + return {default_value, false}; + } + + return Traits::convert(l, index, name); } - static std::string do_it(lua::state *l) - { return do_it_def(l, std::string()); } + public: + simple_accessors(T default_value_ = T(), bool modifiable_ = false) + : default_value(default_value_), modifiable(modifiable_) + {} + + T getter(lua::state *l, const std::string &name) + { + lua::stack_sentry s(*l, -1); + auto ret = do_convert(l, -1, name); + l->pop(); + + // setter function should make sure the value is valid + assert(ret.second); + + return ret.first; + } + + void lua_setter(lua::state *l, bool init, const std::string &name) + { + lua::stack_sentry s(*l, -2); + + if(!init && !modifiable) { + NORM_ERR("Setting '%s' is not modifiable", name.c_str()); + l->replace(-2); + } else { + auto ret = do_convert(l, -2, name); + if(ret.second) + l->pop(); + else + l->replace(-2); + } + ++s; + } }; namespace priv { class config_setting_base { + private: + static void process_setting(lua::state &l, bool init); + static int config__newindex(lua::state *l); + + protected: + virtual void call_lua_setter(lua::state *l, bool init) = 0; + public: - typedef std::function lua_setter_t; - const std::string name; - const lua_setter_t lua_setter; - config_setting_base(const std::string &name_, const lua_setter_t &lua_setter_); - virtual ~config_setting_base() {} + config_setting_base(const std::string &name_); /* * Set the setting manually. @@ -114,6 +193,8 @@ namespace conky { * stack on exit: | ... | */ void lua_set(lua::state &l); + + friend void conky::check_config_settings(lua::state &l); }; typedef std::unordered_map config_settings_t; @@ -135,24 +216,25 @@ namespace conky { * be changed (easily?) when conky is running, but some (e.g. x/y position of the window) * can. */ - template + template> class config_setting: public priv::config_setting_base { public: - typedef std::function getter_t; - - config_setting(const std::string &name_, - const getter_t &getter_ = &simple_getter::do_it, - const lua_setter_t &lua_setter_ = &simple_lua_setter) - : config_setting_base(name_, lua_setter_), getter(getter_) + config_setting(const std::string &name_, const Accessors &accessors_ = Accessors()) + : config_setting_base(name_), accessors(accessors_) {} T get(lua::state &l); + + protected: + virtual void call_lua_setter(lua::state *l, bool init) + { accessors.lua_setter(l, init, name); } + private: - getter_t getter; + Accessors accessors; }; - template - T config_setting::get(lua::state &l) + template + T config_setting::get(lua::state &l) { lua::stack_sentry s(l); l.checkstack(2); @@ -164,96 +246,9 @@ namespace conky { l.getfield(-1, name.c_str()); l.replace(-2); - return getter(&l); + return accessors.getter(&l, name); } - template - class enum_config_setting: public config_setting { - // In theory, this class may be useful for other types too. If you think think you have a - // use for that, remove this assert. - static_assert(std::is_enum::value, "Only enum types allowed"); - - typedef config_setting Base; - - std::pair convert(lua::state *l, int index); - T enum_getter(lua::state *l); - void enum_lua_setter(lua::state *l, bool init); - - public: - typedef std::initializer_list> Map; - enum_config_setting(const std::string &name_, Map map_, bool modifiable_, T default_value_) - : Base(name_, - std::bind(&enum_config_setting::enum_getter, this, std::placeholders::_1), - std::bind(&enum_config_setting::enum_lua_setter, this, std::placeholders::_1, - std::placeholders::_2)), - map(map_), modifiable(modifiable_), default_value(default_value_) - {} - - private: - Map map; - bool modifiable; - T default_value; - }; - - template - std::pair enum_config_setting::convert(lua::state *l, int index) - { - if(l->isnil(index)) - return {default_value, true}; - - std::string val = l->tostring(index); - - for(auto i = map.begin(); i != map.end(); ++i) { - if(i->first == val) - return {i->second, true}; - } - - std::string msg = "Invalid value '" + val + "' for setting '" - + Base::name + "'. Valid values are: "; - for(auto i = map.begin(); i != map.end(); ++i) { - if(i != map.begin()) - msg += ", "; - msg += "'" + i->first + "'"; - } - msg += "."; - NORM_ERR("%s", msg.c_str()); - - return {default_value, false}; - } - - template - T enum_config_setting::enum_getter(lua::state *l) - { - lua::stack_sentry s(*l, -1); - auto ret = convert(l, -1); - l->pop(); - - // setter function should make sure the value is valid - assert(ret.second); - - return ret.first; - } - - template - void enum_config_setting::enum_lua_setter(lua::state *l, bool init) - { - lua::stack_sentry s(*l, -2); - - if(!init && !modifiable) { - NORM_ERR("Setting '%s' is not modifiable", Base::name.c_str()); - l->replace(-2); - } else { - auto ret = convert(l, -2); - if(ret.second) - l->pop(); - else - l->replace(-2); - } - ++s; - } - - void check_config_settings(lua::state &l); - /////////// example settings, remove after real settings are available /////// extern config_setting asdf; } diff --git a/src/x11.cc b/src/x11.cc index e8895148..653d20fa 100644 --- a/src/x11.cc +++ b/src/x11.cc @@ -912,7 +912,8 @@ void xdbe_swap_buffers(void) } #endif /* BUILD_XDBE */ -conky::enum_config_setting::Map text_alignment_map = { +template<> +conky::lua_traits::Map conky::lua_traits::map = { { "top_left", TOP_LEFT }, { "top_right", TOP_RIGHT }, { "top_middle", TOP_MIDDLE }, @@ -924,5 +925,5 @@ conky::enum_config_setting::Map text_alignment_map = { { "middle_right", MIDDLE_RIGHT }, { "none", NONE } }; -conky::enum_config_setting text_alignment("alignment", text_alignment_map, false, NONE); +conky::config_setting text_alignment("alignment", conky::simple_accessors(NONE, false)); diff --git a/src/x11.h b/src/x11.h index 0b4ed830..465804fb 100644 --- a/src/x11.h +++ b/src/x11.h @@ -153,8 +153,7 @@ enum alignment { NONE }; -extern conky::enum_config_setting::Map text_alignment_map; -extern conky::enum_config_setting text_alignment; +extern conky::config_setting text_alignment; #endif /*X11_H_*/ #endif /* BUILD_X11 */