diff --git a/src/data-source.cc b/src/data-source.cc index 46697838..fc95f4e5 100644 --- a/src/data-source.cc +++ b/src/data-source.cc @@ -26,7 +26,6 @@ #include "data-source.hh" #include -#include #include namespace conky { @@ -46,7 +45,46 @@ namespace conky { */ data_sources_t *data_sources; - void register_data_source_(const std::string &name, const data_source_factory &factory_func) + data_source_base& get_data_source(lua::state *l) + { + if(l->gettop() != 1) + throw std::runtime_error("Wrong number of parameters"); + + l->rawgetfield(lua::REGISTRYINDEX, priv::data_source_metatable); + if(not l->getmetatable(-2) or not l->rawequal(-1, -2)) + throw std::runtime_error("Invalid parameter"); + + return *static_cast(l->touserdata(1)); + } + + int data_source_asnumber(lua::state *l) + { + double x = get_data_source(l).get_number(); + l->pushnumber(x); + return 1; + } + + int data_source_astext(lua::state *l) + { + std::string x = get_data_source(l).get_text(); + l->pushstring(x.c_str()); + return 1; + } + + const char data_source__index[] = + "local table, key = ...;\n" + "if key == 'num' then\n" + " return conky.asnumber(table);\n" + "elseif key == 'text' then\n" + " return conky.astext(table);\n" + "else\n" + " print(string.format([[Invalid data source operation: '%s']], key));\n" + " return 0/0;\n" + "end\n"; + } + + namespace priv { + void do_register_data_source(const std::string &name, const lua::cpp_function &fn) { struct data_source_constructor { data_source_constructor() { data_sources = new data_sources_t(); } @@ -54,20 +92,19 @@ namespace conky { }; static data_source_constructor constructor; - bool inserted = data_sources->insert({name, factory_func}).second; + bool inserted = data_sources->insert({name, fn}).second; if(not inserted) throw std::logic_error("Data source with name '" + name + "' already registered"); } - static void - disabled_source_factory(lua::state &l, const std::string &name, const std::string &setting) + disabled_data_source::disabled_data_source(lua::state *l, const std::string &name, + const std::string &setting) + : simple_numeric_source(l, name, &NaN) { // XXX some generic way of reporting errors? NORM_ERR? std::cerr << "Support for variable '" << name << "' has been disabled during compilation. Please recompile with '" << setting << "'" << std::endl; - - simple_numeric_source::factory(l, name, &NaN); } } @@ -81,37 +118,45 @@ namespace conky { return s.str(); } - template - void - simple_numeric_source::factory(lua::state &l, const std::string &name, const T *source) - { - l.pop(); - l.createuserdata>(name, source); - } - - register_data_source::register_data_source(const std::string &name, - const data_source_factory &factory_func) - { register_data_source_(name, factory_func); } - register_disabled_data_source::register_disabled_data_source(const std::string &name, const std::string &setting) - { - register_data_source_(name, - std::bind(disabled_source_factory, - std::placeholders::_1, std::placeholders::_2, setting) - ); - } + : register_data_source(name, setting) + {} - // at least one data source should always be registered, so the pointer will not be null - const data_sources_t& get_data_sources() - { return *data_sources; } + // at least one data source should always be registered, so data_sources will not be null + void export_data_sources(lua::state &l) + { + lua::stack_sentry s(l); + l.checkstack(2); + + l.newmetatable(priv::data_source_metatable); ++s; { + l.pushboolean(false); ++s; + l.rawsetfield(-2, "__metatable"); --s; + + l.pushdestructor(); ++s; + l.rawsetfield(-2, "__gc"); --s; + + l.loadstring(data_source__index); ++s; + l.rawsetfield(-2, "__index"); --s; + } l.pop(); --s; + + l.newtable(); ++s; { + for(auto i = data_sources->begin(); i != data_sources->end(); ++i) { + l.pushfunction(i->second); ++s; + l.rawsetfield(-2, i->first.c_str()); --s; + } + } l.rawsetfield(-2, "variables"); --s; + + l.pushfunction(data_source_asnumber); ++s; + l.rawsetfield(-2, "asnumber"); --s; + + l.pushfunction(data_source_astext); ++s; + l.rawsetfield(-2, "astext"); --s; + } } /////////// example data sources, remove after real data sources are available /////// int asdf_ = 47; -conky::register_data_source asdf("asdf", std::bind( - conky::simple_numeric_source::factory, - std::placeholders::_1, std::placeholders::_2, &asdf_) - ); +conky::register_data_source> asdf("asdf", &asdf_); conky::register_disabled_data_source zxcv("zxcv", "BUILD_ZXCV"); diff --git a/src/data-source.hh b/src/data-source.hh index bd72c77a..9b55ef80 100644 --- a/src/data-source.hh +++ b/src/data-source.hh @@ -24,6 +24,7 @@ #ifndef DATA_SOURCE_HH #define DATA_SOURCE_HH +#include #include #include #include @@ -34,13 +35,6 @@ namespace conky { - /* - * Recieves a lua table on the stack and the name the object was registered with. It should - * pop the table after consuming it. The result should be pushed on the stack as lua userdata - * containing (a subclass of) data_source_base. - */ - typedef std::function data_source_factory; - /* * A base class for all data sources. * API consists of two functions: @@ -74,38 +68,69 @@ namespace conky { const T *source; public: - simple_numeric_source(const std::string &name_, const T *source_) + simple_numeric_source(lua::state *, const std::string &name_, const T *source_) : data_source_base(name_), source(source_) {} - static void factory(lua::state &l, const std::string &name, const T *source); - virtual double get_number() const { return *source; } }; + namespace priv { + const char data_source_metatable[] = "conky::data_source_metatable"; + + void do_register_data_source(const std::string &name, const lua::cpp_function &fn); + + class disabled_data_source: public simple_numeric_source { + public: + disabled_data_source(lua::state *l, const std::string &name, + const std::string &setting); + }; + + } + /* * Declaring an object of this type at global scope will register a data source with the give * name and factory function. */ + template class register_data_source { + template + static int factory(lua::state *l, const std::string &name, Args&&... args) + { + T *t = static_cast(l->newuserdata(sizeof(T))); + l->insert(1); + new(t) T(l, name, std::forward(args)...); + l->settop(1); + l->rawgetfield(lua::REGISTRYINDEX, priv::data_source_metatable); + l->setmetatable(-2); + return 1; + } + public: - register_data_source(const std::string &name, const data_source_factory &factory_func); + template + register_data_source(const std::string &name, Args&&... args) + { + priv::do_register_data_source( name, std::bind(&factory, + std::placeholders::_1, + name, + std::forward(args)... + )); + } }; /* * Use this to declare a data source that has been disabled during compilation. We can then * print a nice error message telling the used which setting to enable. */ - class register_disabled_data_source { + class register_disabled_data_source: public register_data_source { public: register_disabled_data_source(const std::string &name, const std::string &setting); }; - typedef std::unordered_map data_sources_t; + typedef std::unordered_map data_sources_t; - // returns the list of registered data sources - const data_sources_t& get_data_sources(); + void export_data_sources(lua::state &l); } #endif /* DATA_SOURCE_HH */ diff --git a/src/lua-config.cc b/src/lua-config.cc index c1b32a68..8b05b70e 100644 --- a/src/lua-config.cc +++ b/src/lua-config.cc @@ -28,87 +28,13 @@ #include "data-source.hh" namespace conky { - namespace { - const char data_source_metatable[] = "conky::data_source_metatable"; - - data_source_base& get_data_source(lua::state *l) - { - if(l->gettop() != 1) - throw std::runtime_error("Wrong number of parameters"); - - l->rawgetfield(lua::REGISTRYINDEX, data_source_metatable); - if(not l->getmetatable(-2) or not l->rawequal(-1, -2)) - throw std::runtime_error("Invalid parameter"); - - return *static_cast(l->touserdata(1)); - } - - int data_source_asnumber(lua::state *l) - { - double x = get_data_source(l).get_number(); - l->pushnumber(x); - return 1; - } - - int data_source_astext(lua::state *l) - { - std::string x = get_data_source(l).get_text(); - l->pushstring(x.c_str()); - return 1; - } - - int create_data_source(lua::state *l, const data_sources_t::value_type &v) - { - v.second(*l, v.first); - l->rawgetfield(lua::REGISTRYINDEX, data_source_metatable); - l->setmetatable(-2); - return 1; - } - - const char data_source__index[] = - "local table, key = ...;\n" - "if key == 'num' then\n" - " return conky.asnumber(table);\n" - "elseif key == 'text' then\n" - " return conky.astext(table);\n" - "else\n" - " print(string.format([[Invalid data source operation: '%s']], key));\n" - " return 0/0;\n" - "end\n"; - } - void export_symbols(lua::state &l) { lua::stack_sentry s(l); l.checkstack(3); - l.newmetatable(data_source_metatable); ++s; { - l.pushboolean(false); ++s; - l.rawsetfield(-2, "__metatable"); --s; - - l.pushdestructor>(); ++s; - l.rawsetfield(-2, "__gc"); --s; - - l.loadstring(data_source__index); ++s; - l.rawsetfield(-2, "__index"); --s; - } l.pop(); --s; - l.newtable(); ++s; { - l.newtable(); ++s; { - const data_sources_t &ds = get_data_sources(); - for(auto i = ds.begin(); i != ds.end(); ++i) { - l.pushfunction(std::bind(create_data_source, - std::placeholders::_1, std::cref(*i) - )); ++s; - l.rawsetfield(-2, i->first.c_str()); --s; - } - } l.rawsetfield(-2, "variables"); --s; - - l.pushfunction(data_source_asnumber); ++s; - l.rawsetfield(-2, "asnumber"); --s; - - l.pushfunction(data_source_astext); ++s; - l.rawsetfield(-2, "astext"); --s; + export_data_sources(l); } l.setglobal("conky"); --s; } }