# HG changeset patch # User David Demelier # Date 1393190083 -3600 # Node ID b54c83ed71ca1d9da4b4642597af7d578fb46ccc # Parent 9f22ce5f1b393dad2323e6a943c746e26828a16a Improve Luae with static_assert and such diff -r 9f22ce5f1b39 -r b54c83ed71ca C++/Luae.cpp --- a/C++/Luae.cpp Sun Feb 16 12:39:03 2014 +0100 +++ b/C++/Luae.cpp Sun Feb 23 22:14:43 2014 +0100 @@ -153,134 +153,13 @@ * LuaeTable * -------------------------------------------------------- */ -template <> -bool LuaeTable::get(lua_State *L, int idx, const std::string &name) -{ - bool value = false; - - lua_getfield(L, idx, name.c_str()); - if (lua_type(L, -1) == LUA_TBOOLEAN) - value = lua_toboolean(L, -1) == 1; - lua_pop(L, 1); - - return value; -} - -template <> -double LuaeTable::get(lua_State *L, int idx, const std::string &name) -{ - double value = 0; - - lua_getfield(L, idx, name.c_str()); - if (lua_type(L, -1) == LUA_TNUMBER) - value = lua_tonumber(L, -1); - lua_pop(L, 1); - - return value; -} - -template <> -int LuaeTable::get(lua_State *L, int idx, const std::string &name) -{ - int value = 0; - - lua_getfield(L, idx, name.c_str()); - if (lua_type(L, -1) == LUA_TNUMBER) - value = lua_tointeger(L, -1); - lua_pop(L, 1); - - return value; -} - -template <> -std::string LuaeTable::get(lua_State *L, int idx, const std::string &name) -{ - std::string value; - - lua_getfield(L, idx, name.c_str()); - if (lua_type(L, -1) == LUA_TSTRING) - value = lua_tostring(L, -1); - lua_pop(L, 1); - - return value; -} - -template <> -void LuaeTable::set(lua_State *L, - int idx, - const std::string &name, - const bool &value) +void LuaeTable::create(lua_State *L, int nrec, int narr) { LUAE_STACK_CHECKBEGIN(L); - lua_pushboolean(L, value); - lua_setfield(L, (idx < 0) ? idx - 1 : idx, name.c_str()); - LUAE_STACK_CHECKEQUALS(L); -} - -template <> -void LuaeTable::set(lua_State *L, - int idx, - const std::string &name, - const double &value) -{ - LUAE_STACK_CHECKBEGIN(L); - lua_pushnumber(L, value); - lua_setfield(L, (idx < 0) ? idx - 1 : idx, name.c_str()); - LUAE_STACK_CHECKEQUALS(L); -} - -template <> -void LuaeTable::set(lua_State *L, - int idx, - const std::string &name, - const int &value) -{ - LUAE_STACK_CHECKBEGIN(L); - lua_pushinteger(L, value); - lua_setfield(L, (idx < 0) ? idx - 1 : idx, name.c_str()); - LUAE_STACK_CHECKEQUALS(L); -} -template <> -void LuaeTable::set(lua_State *L, - int idx, - const std::string &name, - const std::string &value) -{ - LUAE_STACK_CHECKBEGIN(L); - lua_pushlstring(L, value.c_str(), value.length()); - lua_setfield(L, (idx < 0) ? idx - 1 : idx, name.c_str()); - LUAE_STACK_CHECKEQUALS(L); -} + lua_createtable(L, nrec, narr); -/* - * Version for const char * - */ -template <> -void LuaeTable::set(lua_State *L, - int idx, - const std::string &name, - const char * const &value) -{ - LUAE_STACK_CHECKBEGIN(L); - lua_pushstring(L, value); - lua_setfield(L, (idx < 0) ? idx - 1 : idx, name.c_str()); - LUAE_STACK_CHECKEQUALS(L); -} - -/* - * Version for char * - */ -template <> -void LuaeTable::set(lua_State *L, - int idx, - const std::string &name, - char * const &value) -{ - LUAE_STACK_CHECKBEGIN(L); - lua_pushstring(L, value); - lua_setfield(L, (idx < 0) ? idx - 1 : idx, name.c_str()); - LUAE_STACK_CHECKEQUALS(L); + LUAE_STACK_CHECKEND(L, - 1); } int LuaeTable::type(lua_State *L, int idx, const std::string &name) @@ -450,6 +329,16 @@ * Luae * -------------------------------------------------------- */ +void Luae::doexecute(lua_State *L, int status) +{ + if (status != LUA_OK) { + auto error = lua_tostring(L, -1); + lua_pop(L, 1); + + throw std::runtime_error(error); + } +} + void Luae::preload(lua_State *L, const std::string &name, lua_CFunction func) { LUAE_STACK_CHECKBEGIN(L); diff -r 9f22ce5f1b39 -r b54c83ed71ca C++/Luae.h --- a/C++/Luae.h Sun Feb 16 12:39:03 2014 +0100 +++ b/C++/Luae.h Sun Feb 23 22:14:43 2014 +0100 @@ -23,12 +23,371 @@ #include #include #include +#include #include +#include #include #include #include +#if !defined(NDEBUG) + +#define LUAE_STACK_CHECKBEGIN(L) \ + int __topstack = lua_gettop((L)) + +#define LUAE_STACK_CHECKEQUALS(L) \ + assert(lua_gettop((L)) == __topstack) + +#define LUAE_STACK_CHECKEND(L, cond) \ + assert(lua_gettop((L)) cond == __topstack) + +#else + +#define LUAE_STACK_CHECKBEGIN(L) +#define LUAE_STACK_CHECKEQUALS(L) +#define LUAE_STACK_CHECKEND(L, cond) + +#endif + +/* {{{ Luae */ + +/** + * @class Luae + * @brief Add lot of convenience for Lua + * + * This class adds lot of functions for Lua and C++. + */ +class Luae { +private: + /* + * Wrapper for dofile and dostring. + */ + static void doexecute(lua_State *L, int status); + +public: + /** + * @struct Convert + * @brief Push or get values + */ + template + struct Convert { + static const bool supported = false; + }; + + /** + * Preload a library, it will be added to package.preload so the + * user can successfully call require "name". In order to work, you need + * to open luaopen_package and luaopen_base first. + * + * @param L the Lua state + * @param name the module name + * @param func the opening library + * @see require + */ + static void preload(lua_State *L, + const std::string &name, + lua_CFunction func); + + /** + * Get a global value from Lua. + * + * @param L the Lua state + * @param name the value name + */ + static inline void getglobal(lua_State *L, const std::string &name) + { + lua_getglobal(L, name.c_str()); + } + + /** + * Move the top element at the given index. + * + * @param L the Lua state + * @param index the new index + */ + static inline void insert(lua_State *L, int index) + { + lua_insert(L, index); + } + + /** + * Replace the element at the given index by the one at the top. + * + * @param L the Lua state + * @param index the new index + */ + static inline void replace(lua_State *L, int index) + { + lua_replace(L, index); + } + + /** + * Remove the element at the given index. + * + * @param L the Lua state + * @param index the table index + */ + static inline void remove(lua_State *L, int index) + { + lua_remove(L, index); + } + + /** + * Wrapper around pcall, throw instead of returning an error code. + * + * @param L the Lua state + * @param np the number of parameters + * @param nr the number of return value + * @param error the message handler + */ + static inline void pcall(lua_State *L, int np, int nr, int error = 0) + { + if (lua_pcall(L, np, nr, error) != LUA_OK) { + auto error = lua_tostring(L, -1); + lua_pop(L, 1); + + throw std::runtime_error(error); + } + } + + /** + * Create a unique reference to the table at the given index. + * + * @param L the Lua state + * @param index the table index + * @return the reference + */ + static inline int ref(lua_State *L, int index) + { + return luaL_ref(L, index); + } + + /** + * Load a library just like it was loaded with require. + * + * @param L the Lua state + * @param name the module name + * @param func the function + * @param global store as global + */ + static void require(lua_State *L, + const std::string &name, + lua_CFunction func, + bool global); + + /** + * Load and execute a file. + * + * @param L the Lua state + * @param path the the path + */ + static inline void dofile(lua_State *L, const std::string &path) + { + doexecute(L, luaL_dofile(L, path.c_str())); + } + + static inline void dostring(lua_State *L, const std::string &data) + { + doexecute(L, luaL_dostring(L, data.c_str())); + } + + /** + * Get the type at the given index. + * + * @param L the Lua state + * @param index the value index + * @return the type + */ + static inline int type(lua_State *L, int index) + { + return lua_type(L, index); + } + + /** + * Push a value to Lua. + * + * @param L the Lua state + * @param value the value + */ + template + static void push(lua_State *L, const T &value) + { + static_assert(Convert::supported, "type not supported"); + + Convert::push(L, value); + } + + /** + * Overload for string literals. + * + * @param L the Lua state + * @param s the string + */ + template + static void push(lua_State *L, const char (&s)[N]) + { + push(L, s); + } + + /** + * Check the type at the given index. Calls luaL_error on bad + * type. + * + * @param L the Lua state + * @param index the the index + * @param type the type to check + */ + static inline void checktype(lua_State *L, int index, int type) + { + luaL_checktype(L, index, type); + } + + /** + * Get a value from Lua. The type are not checked + * + * @param L the Lua state + * @param index the value index + * @return the value + */ + template + static T get(lua_State *L, int index) + { + static_assert(Convert::supported, "type not supported"); + + return Convert::get(L, index); + } + + /** + * Get the value at the index, raise a Lua error on failures. + * + * @param L the Lua state + * @param index the value index + * @return the value + */ + template + static T check(lua_State *L, int index) + { + return Convert::check(L, index); + } + + /** + * Convert a new placement made object, without testing if its a real + * object. + * + * @param L the Lua state + * @param idx the object index + * @return the converted object + */ + template + static T toType(lua_State *L, int idx) + { + return reinterpret_cast(lua_touserdata(L, idx)); + } +}; + +template <> +struct Luae::Convert { + static const bool supported = true; + + static void push(lua_State *L, const bool &value) + { + lua_pushboolean(L, value); + } + + static bool get(lua_State *L, int index) + { + return lua_toboolean(L, index); + } + + static bool check(lua_State *L, int index) + { + return lua_toboolean(L, index); + } +}; + +template <> +struct Luae::Convert { + static const bool supported = true; + + static void push(lua_State *L, const int &value) + { + lua_pushinteger(L, value); + } + + static int get(lua_State *L, int index) + { + return lua_tointeger(L, index); + } + + static int check(lua_State *L, int index) + { + return luaL_checkinteger(L, index); + } +}; + +template <> +struct Luae::Convert { + static const bool supported = true; + + static void push(lua_State *L, const double &value) + { + lua_pushnumber(L, value); + } + + static double get(lua_State *L, int index) + { + return lua_tonumber(L, index); + } + + static double check(lua_State *L, int index) + { + return luaL_checknumber(L, index); + } +}; + +template <> +struct Luae::Convert { + static const bool supported = true; + + static void push(lua_State *L, const std::string &value) + { + lua_pushlstring(L, value.c_str(), value.length()); + } + + static std::string get(lua_State *L, int index) + { + return lua_tostring(L, index); + } + + static std::string check(lua_State *L, int index) + { + return luaL_checkstring(L, index); + } +}; + +template <> +struct Luae::Convert { + static const bool supported = true; + + static void push(lua_State *L, const char *value) + { + lua_pushstring(L, value); + } + + static const char *get(lua_State *L, int index) + { + return lua_tostring(L, index); + } + + static const char *check(lua_State *L, int index) + { + return luaL_checkstring(L, index); + } +}; + +/* }}} */ + /* {{{ LuaeState */ /** @@ -145,6 +504,141 @@ /* }}} */ +/* {{{ LuaeClass */ + +/** + * @class LuaeClass + * @brief Support for object oriented programming between C++ and Lua + * + * This class provides functions for passing and retrieving objects from C++ and + * Lua. + */ +class LuaeClass { +public: + using Methods = std::vector; + + template + using Ptr = std::shared_ptr; + + struct Def { + std::string name; //! metatable name + Methods methods; //! methods + Methods metamethods; //! metamethods + const Def *parent; //! optional parent class + }; + + /* + * FieldName: The field stored in the object metatable about + * the object metatable name. + * + * FieldParents: The field that holds all parent classes. It is + * used to verify casts. + */ + static const char *FieldName; + static const char *FieldParents; + + /** + * Initialize a new object. + * + * @param L the Lua state + * @param def the definition + */ + static void create(lua_State *L, const Def &def); + + /** + * Push a shared object to Lua, it also push it to the "__refs" + * table with __mode = "v". That is if we need to push the object + * again we use the same reference so Lua get always the same + * userdata and gain the following benefits: + * + * 1. The user can use the userdata as table key + * 2. A performance gain thanks to less allocations + * + * @param L the Lua state + * @param o the object to push + * @param name the object metatable name + */ + template + static void pushShared(lua_State *L, Ptr o, const std::string &name) + { + LUAE_STACK_CHECKBEGIN(L); + + lua_getfield(L, LUA_REGISTRYINDEX, LuaeState::FieldRefs); + assert(lua_type(L, -1) == LUA_TTABLE); + + lua_rawgetp(L, -1, o.get()); + + if (lua_type(L, -1) == LUA_TNIL) { + lua_pop(L, 1); + + new (L, name.c_str()) std::shared_ptr(o); + + lua_pushvalue(L, -1); + lua_rawsetp(L, -3, o.get()); + } + + lua_replace(L, -2); + + LUAE_STACK_CHECKEND(L, -1); + } + + /** + * Get an object from Lua that was previously push with pushShared. + * + * @param L the Lua state + * @param index the object index + * @param meta the object metatable name + * @return the object + */ + template + static Ptr getShared(lua_State *L, int index, const char *meta) + { + LUAE_STACK_CHECKBEGIN(L); + + luaL_checktype(L, index, LUA_TUSERDATA); + if (!luaL_getmetafield(L, index, FieldName)) + luaL_error(L, "invalid type"); + + // Get the class name + const char *name = lua_tostring(L, -1); + lua_pop(L, 1); + + bool found(false); + + if (std::string(name) == std::string(meta)) { + found = true; + } else { + if (!luaL_getmetafield(L, index, FieldParents)) + luaL_error(L, "invalid type"); + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_type(L, -2) != LUA_TSTRING) { + lua_pop(L, 1); + continue; + } + + auto tn = lua_tostring(L, -1); + if (std::string(tn) == std::string(meta)) + found = true; + + lua_pop(L, 1); + } + + lua_pop(L, 1); + } + + if (!found) + luaL_error(L, "invalid cast from `%s' to `%s'", name, meta); + + LUAE_STACK_CHECKEQUALS(L); + + return *static_cast *>(lua_touserdata(L, index)); + } +}; + +/* }}} */ + /* {{{ LuaeTable */ /** @@ -158,6 +652,15 @@ using ReadFunction = std::function; /** + * Push a new table onto the stack. + * + * @param L the Lua state + * @param nrec the optional number of entries as record + * @param nrec the optional number of entries as sequence + */ + static void create(lua_State *L, int nrec = 0, int narr = 0); + + /** * Get a field of a specific type from a table. Specialized for the * types: int, double, bool and string. * @@ -167,7 +670,43 @@ * @return the converted type. */ template - static T get(lua_State *L, int idx, const std::string &name); + static T get(lua_State *L, int idx, const std::string &name) + { + LUAE_STACK_CHECKBEGIN(L); + + lua_getfield(L, idx, name.c_str()); + auto t = Luae::get(L, -1); + lua_pop(L, 1); + + LUAE_STACK_CHECKEQUALS(L); + + return t; + } + + /** + * Set a field to the table at the given index. + * + * @param L the Lua state + * @param idx the table index + * @param name the field name + * @see set + */ + static inline void setfield(lua_State *L, int idx, const std::string &name) + { + lua_setfield(L, idx, name.c_str()); + } + + /** + * Get a field at the given index. + * + * @param L the Lua state + * @param idx the table index + * @param name the field name + */ + static inline void getfield(lua_State *L, int idx, const std::string &name) + { + lua_getfield(L, idx, name.c_str()); + } /** * Set a table field. Specialized for the same fields as get. @@ -179,7 +718,15 @@ * @see get */ template - static void set(lua_State *L, int idx, const std::string &name, const T &value); + static void set(lua_State *L, int idx, const std::string &name, const T &value) + { + LUAE_STACK_CHECKBEGIN(L); + + Luae::push(L, value); + LuaeTable::setfield(L, (idx < 0) ? --idx : idx, name); + + LUAE_STACK_CHECKEQUALS(L); + } /** * Overload for string literals. @@ -196,6 +743,26 @@ } /** + * Set a class object to a table. + * + * @param L the Lua state + * @param index the table index + * @param name the field name + * @param meta the metatable name + * @param o the object + */ + template + static void setShared(lua_State *L, + int index, + const std::string &name, + const std::string &meta, + LuaeClass::Ptr o) + { + LuaeClass::pushShared(L, o, meta); + LuaeTable::setfield(L, (index < 0) ? --index : index, name); + } + + /** * Require a field from a table. * * @param L the Lua state @@ -259,128 +826,6 @@ /* }}} */ -/* {{{ LuaeClass */ - -/** - * @class LuaeClass - * @brief Support for object oriented programming between C++ and Lua - * - * This class provides functions for passing and retrieving objects from C++ and - * Lua. - */ -class LuaeClass { -public: - using Methods = std::vector; - - template - using Ptr = std::shared_ptr; - - struct Def { - std::string name; //! metatable name - Methods methods; //! methods - Methods metamethods; //! metamethods - const Def *parent; //! optional parent class - }; - - /* - * FieldName: The field stored in the object metatable about - * the object metatable name. - * - * FieldParents: The field that holds all parent classes. It is - * used to verify casts. - */ - static const char *FieldName; - static const char *FieldParents; - - /** - * Initialize a new object. - * - * @param L the Lua state - * @param def the definition - */ - static void create(lua_State *L, const Def &def); - - /** - * Push a shared object to Lua, it also push it to the "__refs" - * table with __mode = "v". That is if we need to push the object - * again we use the same reference so Lua get always the same - * userdata and gain the following benefits: - * - * 1. The user can use the userdata as table key - * 2. A performance gain thanks to less allocations - * - * @param L the Lua state - * @param o the object to push - * @param name the object metatable name - */ - template - static void push(lua_State *L, Ptr o, const std::string &name) - { - lua_getfield(L, LUA_REGISTRYINDEX, LuaeState::FieldRefs); - assert(lua_type(L, -1) == LUA_TTABLE); - - lua_rawgetp(L, -1, o.get()); - - if (lua_type(L, -1) == LUA_TNIL) { - lua_pop(L, 1); - - new (L, name.c_str()) std::shared_ptr(o); - - lua_pushvalue(L, -1); - lua_rawsetp(L, -3, o.get()); - } - - lua_replace(L, -2); - } - - /** - * Get an object from Lua that was previously push with pushShared. - * - * @param L the Lua state - * @param index the object index - * @param meta the object metatable name - * @return the object - */ - template - static Ptr get(lua_State *L, int index, const char *meta) - { - luaL_checktype(L, index, LUA_TUSERDATA); - if (!luaL_getmetafield(L, index, FieldName)) - luaL_error(L, "invalid type"); - - // Get the class name - const char *name = lua_tostring(L, -1); - lua_pop(L, 1); - - bool found(false); - - if (std::string(name) == std::string(meta)) { - found = true; - } else { - if (!luaL_getmetafield(L, index, FieldParents)) - luaL_error(L, "invalid type"); - - LuaeTable::read(L, -1, [&] (lua_State *L, int, int tvalue) { - if (tvalue != LUA_TSTRING) - return; - - const char *tn = lua_tostring(L, -1); - if (std::string(tn) == std::string(meta)) - found = true; - }); - - lua_pop(L, 1); - } - - if (!found) - luaL_error(L, "invalid cast from `%s' to `%s'", name, meta); - - return *static_cast *>(lua_touserdata(L, index)); - } -}; - -/* }}} */ - /* {{{ LuaeEnum */ /** @@ -435,80 +880,6 @@ /* }}} */ -/* {{{ Luae */ - -/** - * @class Luae - * @brief Add lot of convenience for Lua - * - * This class adds lot of functions for Lua and C++. - */ -class Luae { -public: - /** - * Preload a library, it will be added to package.preload so the - * user can successfully call require "name". In order to work, you need - * to open luaopen_package and luaopen_base first. - * - * @param L the Lua state - * @param name the module name - * @param func the opening library - * @see require - */ - static void preload(lua_State *L, - const std::string &name, - lua_CFunction func); - - - /** - * Load a library just like it was loaded with require. - * - * @param L the Lua state - * @param name the module name - * @param func the function - * @param global store as global - */ - static void require(lua_State *L, - const std::string &name, - lua_CFunction func, - bool global); - - /** - * Convert a new placement made object, without testing if its a real - * object. - * - * @param L the Lua state - * @param idx the object index - * @return the converted object - */ - template - static T toType(lua_State *L, int idx) - { - return reinterpret_cast(lua_touserdata(L, idx)); - } -}; - -/* }}} */ - -#if !defined(NDEBUG) - -#define LUAE_STACK_CHECKBEGIN(L) \ - int __topstack = lua_gettop((L)) - -#define LUAE_STACK_CHECKEQUALS(L) \ - assert(lua_gettop((L)) == __topstack) - -#define LUAE_STACK_CHECKEND(L, cond) \ - assert(lua_gettop((L)) cond == __topstack) - -#else - -#define LUAE_STACK_CHECKBEGIN(L) -#define LUAE_STACK_CHECKEQUALS(L) -#define LUAE_STACK_CHECKEND(L, cond) - -#endif - void *operator new(size_t size, lua_State *L); void *operator new(size_t size, lua_State *L, const char *metaname);