diff --git a/src/conky.cc b/src/conky.cc index 63bea8cf..17b0e83e 100644 --- a/src/conky.cc +++ b/src/conky.cc @@ -4300,7 +4300,7 @@ int main(int argc, char **argv) "print(conky.asnumber(conky.variables.zxcv{}));\n" "print(conky.variables.asdf{}.text);\n" "print(conky.variables.asdf{}.xxx);\n" - "conky.config = { a='z', asdf=47, [42]=47 };\n" + "conky.config = { a='z', asdf=47, [42]=47, foo='bar' };\n" ); l.call(0, 0); conky::check_config_settings(l); @@ -4309,6 +4309,10 @@ int main(int argc, char **argv) "print('config.asdf = ', conky.config.asdf);\n" "conky.config.asdf = 42;\n" "print('config.asdf = ', conky.config.asdf);\n" + "conky.config.foo='asdf';\n" + "print('config.foo = ', conky.config.foo);\n" + "conky.config.foo='baz';\n" + "print('config.foo = ', conky.config.foo);\n" ); l.call(0, 0); } diff --git a/src/setting.cc b/src/setting.cc index 5046a625..e2f1c95f 100644 --- a/src/setting.cc +++ b/src/setting.cc @@ -25,8 +25,6 @@ #include "setting.hh" -#include "logging.h" - namespace conky { namespace priv { /* @@ -154,6 +152,7 @@ namespace conky { } /////////// example settings, remove after real settings are available /////// + enum_config_setting::Map foo_map = { {"bar", bar}, {"baz", baz} }; config_setting asdf("asdf"); - config_setting aasf("aasf"); + enum_config_setting foo("foo", foo_map, false, bar); } diff --git a/src/setting.hh b/src/setting.hh index 29d7ca3d..df4daa1e 100644 --- a/src/setting.hh +++ b/src/setting.hh @@ -28,6 +28,7 @@ #include #include +#include "logging.h" #include "luamm.hh" namespace conky { @@ -37,18 +38,15 @@ namespace conky { // converts standard lua types to C types template::value or std::is_enum::value, + bool integral = std::is_integral::value, bool floating_point = std::is_floating_point::value> struct simple_getter { - // integral_or_enum is here to force the compiler to evaluate the assert at instantiation - // time - static_assert(integral_or_enum && false, - "Only specializations for string, integral, enum" - "and floating point types are available" ); + // 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"); }; - // Specialization for integral type and enums. In case of enums, one should provide a setter - // function that makes sure the user sets a sane value + // Specialization for integral type. template struct simple_getter { static T do_it_def(lua::state *l, T def) @@ -56,8 +54,7 @@ namespace conky { if(l->isnil(-1)) return def; - // for enums we need to force a conversion - T t = static_cast(l->tointeger(-1)); + T t = l->tointeger(-1); l->pop(); return t; } @@ -109,6 +106,7 @@ namespace conky { const lua_setter_t lua_setter; config_setting_base(const std::string &name_, const lua_setter_t &lua_setter_); + virtual ~config_setting_base() {} }; typedef std::unordered_map config_settings_t; @@ -160,12 +158,97 @@ namespace conky { --s; return getter(&l); } + 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(); --s; + + // 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-=2; + } + void check_config_settings(lua::state &l); /////////// example settings, remove after real settings are available /////// enum foo { bar, baz }; + extern enum_config_setting::Map foo_map; extern config_setting asdf; - extern config_setting aasf; } #endif /* SETTING_HH */