Mercurial > code
changeset 193:258087829c66
Replace Luae
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 27 Nov 2013 11:34:02 +0100 |
parents | 1c2788f9f55f |
children | 9fc5f917872b |
files | C++/Luae.cpp C++/Luae.h Luae.cpp Luae.h |
diffstat | 4 files changed, 349 insertions(+), 998 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Luae.cpp Wed Nov 27 11:26:26 2013 +0100 +++ b/C++/Luae.cpp Wed Nov 27 11:34:02 2013 +0100 @@ -1,7 +1,7 @@ /* - * Luae.cpp -- Lua helpers and such + * Lua.cpp -- Lua helpers and such * - * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> + * Copyright (c) 2013 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,98 +18,116 @@ #include "Luae.h" -void LuaeClass::createClass(lua_State *L, const LuaObject &cls) +namespace irccd { + +LuaState::LuaState() { - LUA_STACK_CHECKBEGIN(L); + m_state = Ptr(luaL_newstate()); +} + +LuaState::LuaState(lua_State *L) +{ + m_state = Ptr(L); +} - // Already registered? - if (luaL_newmetatable(L, cls.name) == 0) - return; +LuaState::LuaState(LuaState &&state) +{ + m_state = std::move(state.m_state); +} - // Add it's "name" field - lua_pushstring(L, cls.name); - lua_setfield(L, -2, LUAE_CLASS_FIELDNAME); +LuaState &LuaState::operator=(LuaState &&state) +{ + m_state = std::move(state.m_state); - // Create a list of parent for a faster cast - lua_createtable(L, 0, 0); + return *this; +} + +LuaState::operator lua_State*() +{ + return m_state.get(); +} - int i = 0; - for (auto p = cls.parent; p != nullptr; p = p->parent) { - lua_pushstring(L, p->name); - lua_rawseti(L, -2, ++i); - } - lua_setfield(L, -2, LUAE_CLASS_FIELDPARENTS); +LuaValue LuaValue::copy(lua_State *L, int index) +{ + LuaValue v; + + v.type = lua_type(L, index); - // Metamethods - if (cls.metamethods.size() > 0) - luaL_setfuncs(L, cls.metamethods.data(), 0); + switch (v.type) { + case LUA_TBOOLEAN: + v.boolean = lua_toboolean(L, index); + break; + case LUA_TNUMBER: + v.number = lua_tonumber(L, index); + break; + case LUA_TSTRING: + v.str = lua_tostring(L, index); + break; + case LUA_TTABLE: + { + LuaValue k; - // Methods - lua_createtable(L, 0, 0); + if (index < 0) + -- index; - if (cls.methods.size() > 0) { - luaL_setfuncs(L, cls.methods.data(), 0); + lua_pushnil(L); + while (lua_next(L, index)) { + v.table.push_back(std::make_pair(copy(L, -2), copy(L, -1))); + lua_pop(L, 1); + } + + break; + } + default: + v.type = LUA_TNIL; + break; } - /* - * Add a metatable to this __index table so that if - * a method is not found it use the parent table recursively - */ - if (cls.parent) { - // 1. Get the parent - luaL_getmetatable(L, cls.parent->name); - assert(lua_type(L, -1) != LUA_TNIL); - - // 2. Create an anonymous metatable - lua_createtable(L, 1, 1); - - // 3. Get the __index field from this metatable - lua_getfield(L, -2, "__index"); - assert(lua_type(L, -1) != LUA_TNIL); - lua_setfield(L, -2, "__index"); - lua_setmetatable(L, -3); - lua_pop(L, 1); - } - - lua_setfield(L, -2, "__index"); - - // Pop that metatable - lua_pop(L, 1); - - LUA_STACK_CHECKEQUALS(L); + return v; } -/* -------------------------------------------------------- - * Luae public methods - * -------------------------------------------------------- */ - -void Luae::createEnum(lua_State *L, const LuaEnum *enumeration) +void LuaValue::push(lua_State *L, const LuaValue &value) { - lua_createtable(L, 0, 0); + switch (value.type) { + case LUA_TBOOLEAN: + lua_pushboolean(L, value.boolean); + break; + case LUA_TSTRING: + lua_pushlstring(L, value.str.c_str(), value.str.size()); + break; + case LUA_TNUMBER: + lua_pushnumber(L, value.number); + break; + case LUA_TTABLE: + { + lua_createtable(L, 0, 0); - for (int i = 0; enumeration[i].name != nullptr; ++i) { - lua_pushinteger(L, enumeration[i].value); - lua_setfield(L, -2, enumeration[i].name); + for (auto p : value.table) { + LuaValue::push(L, p.first); + LuaValue::push(L, p.second); + + lua_settable(L, -3); + } + break; + } + default: + lua_pushnil(L); + break; } } -void Luae::createLibrary(lua_State *L, const luaL_Reg *functions) +LuaValue::LuaValue() + : type(LUA_TNIL) { - LUA_STACK_CHECKBEGIN(L); - - lua_createtable(L, 0, 0); - luaL_setfuncs(L, functions, 0); - - LUA_STACK_CHECKEND(L, - 1); } template <> -bool Luae::getField(lua_State *L, int idx, const std::string & name) +bool Luae::getField(lua_State *L, int idx, const std::string &name) { bool value = false; lua_getfield(L, idx, name.c_str()); - if (lua_type(L, idx) == LUA_TBOOLEAN) + if (lua_type(L, -1) == LUA_TBOOLEAN) value = lua_toboolean(L, -1) == 1; lua_pop(L, 1); @@ -117,12 +135,12 @@ } template <> -double Luae::getField(lua_State *L, int idx, const std::string & name) +double Luae::getField(lua_State *L, int idx, const std::string &name) { double value = 0; lua_getfield(L, idx, name.c_str()); - if (lua_type(L, idx) == LUA_TNUMBER) + if (lua_type(L, -1) == LUA_TNUMBER) value = lua_tonumber(L, -1); lua_pop(L, 1); @@ -130,12 +148,12 @@ } template <> -int Luae::getField(lua_State *L, int idx, const std::string & name) +int Luae::getField(lua_State *L, int idx, const std::string &name) { int value = 0; lua_getfield(L, idx, name.c_str()); - if (lua_type(L, idx) == LUA_TNUMBER) + if (lua_type(L, -1) == LUA_TNUMBER) value = lua_tointeger(L, -1); lua_pop(L, 1); @@ -143,19 +161,34 @@ } template <> -std::string Luae::getField(lua_State *L, int idx, const std::string & name) +std::string Luae::getField(lua_State *L, int idx, const std::string &name) { std::string value; lua_getfield(L, idx, name.c_str()); - if (lua_type(L, idx) == LUA_TSTRING) + if (lua_type(L, -1) == LUA_TSTRING) value = lua_tostring(L, -1); lua_pop(L, 1); return value; } -void Luae::preload(lua_State *L, const std::string & name, lua_CFunction func) +int Luae::typeField(lua_State *L, int idx, const std::string &name) +{ + int type; + + LUA_STACK_CHECKBEGIN(L); + + lua_getfield(L, idx, name.c_str()); + type = lua_type(L, -1); + lua_pop(L, 1); + + LUA_STACK_CHECKEQUALS(L); + + return type; +} + +void Luae::preload(lua_State *L, const std::string &name, lua_CFunction func) { LUA_STACK_CHECKBEGIN(L); @@ -170,6 +203,8 @@ void Luae::readTable(lua_State *L, int idx, ReadFunction func) { + LUA_STACK_CHECKBEGIN(L); + lua_pushnil(L); if (idx < 0) @@ -180,10 +215,10 @@ lua_pop(L, 1); } - lua_pop(L, 1); + LUA_STACK_CHECKEQUALS(L); } -int Luae::referenceField(lua_State *L, int idx, int type, const std::string & name) +int Luae::referenceField(lua_State *L, int idx, int type, const std::string &name) { int ref = LUA_REFNIL; @@ -199,7 +234,7 @@ return ref; } -void Luae::require(lua_State *L, const std::string & name, lua_CFunction func, bool global) +void Luae::require(lua_State *L, const std::string &name, lua_CFunction func, bool global) { LUA_STACK_CHECKBEGIN(L); @@ -209,6 +244,18 @@ LUA_STACK_CHECKEQUALS(L); } +void Luae::initRegistry(lua_State *L) +{ + lua_createtable(L, 0, 0); + lua_createtable(L, 0, 1); + lua_pushstring(L, "v"); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); + lua_setfield(L, LUA_REGISTRYINDEX, "refs"); +} + +} // !irccd + void * operator new(size_t size, lua_State *L) { return lua_newuserdata(L, size);
--- a/C++/Luae.h Wed Nov 27 11:26:26 2013 +0100 +++ b/C++/Luae.h Wed Nov 27 11:34:02 2013 +0100 @@ -1,7 +1,7 @@ /* - * Luae.h -- Lua helpers and such + * Lua.h -- Lua helpers and such * - * Copyright (c) 2011, 2012, 2013 David Demelier <markand@malikania.fr> + * Copyright (c) 2013 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,226 +16,129 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _LUAE_H_ -#define _LUAE_H_ - -/** - * @file Luae.h - * @brief Lua extended API - * @author David Demelier - * - * Object oriented abstraction, common functions and such. - */ +#ifndef _LUA_H_ +#define _LUA_H_ #include <cassert> -#include <cstring> -#include <exception> #include <functional> #include <memory> -#include <sstream> -#include <stdexcept> #include <string> -#include <unordered_map> -#include <utility> #include <vector> #include <lua.hpp> -#define LUAE_CLASS_FIELDNAME "__name" -#define LUAE_CLASS_FIELDPARENTS "__parents" - -#if !defined(NDEBUG) - -#define LUA_STACK_CHECKBEGIN(L) \ - int __topstack = lua_gettop((L)) - -#define LUA_STACK_CHECKEQUALS(L) \ - assert(lua_gettop((L)) == __topstack) - -#define LUA_STACK_CHECKEND(L, cond) \ - assert(lua_gettop((L)) cond == __topstack) - -#else - -#define LUA_STACK_CHECKBEGIN(L) -#define LUA_STACK_CHECKEQUALS(L) -#define LUA_STACK_CHECKEND(L, cond) - -#endif +namespace irccd { /** - * @enum LuaEnum - * @brief A class to bind C enums + * @class LuaState + * @brief Wrapper for lua_State * - * Used as a static array, binds C enum with the function Luae::createEnum(). - */ -struct LuaEnum { - const char *name; - int value; -}; - -/** - * @struct LuaDeleter - * @brief Deletor for lua_State - * - * Just delete the Lua state at the end of ownership. + * This class automatically create a new Lua state and add implicit + * cast operator plus RAII destruction. */ -struct LuaDeleter { - void operator()(lua_State *L) { - lua_close(L); - } -}; +class LuaState { +private: + struct Deleter { + void operator()(lua_State *L) + { + lua_close(L); + } + }; + + using Ptr = std::unique_ptr<lua_State, Deleter>; + + Ptr m_state; + +public: + LuaState(const LuaState &) = delete; + LuaState &operator=(const LuaState &) = delete; + + /** + * Default constructor. Create a new state. + */ + LuaState(); -/** - * @struct LuaObject - * @brief Lua class binding definition - * - * Define a Lua class (single inheritance only). - */ -struct LuaObject { - const LuaObject *parent; - const char *name; - std::vector<luaL_Reg> methods; - std::vector<luaL_Reg> metamethods; + /** + * Use the already created state. + * + * @param L the state to use + */ + LuaState(lua_State *L); + + /** + * Move constructor. + * + * @param state the Lua state to move + */ + LuaState(LuaState &&state); + + /** + * Move assignment operator. + * + * @param state the Lua state to move + */ + LuaState &operator=(LuaState &&state); + + /** + * Implicit cast operator for convenient usage to C Lua API. + * + * @return the state as lua_State * + */ + operator lua_State*(); }; /** - * A lua_State owner with its own deleter. + * @class LuaValue + * @brief A fake variant for Lua values + * + * This class is primarly used for copying Lua values without checking + * the types, useful to pass data. */ -typedef std::unique_ptr<lua_State, LuaDeleter> LuaState; +class LuaValue { +private: + union { + lua_Number number; + bool boolean; + }; -/** - * @class Luae - * @brief Functions to initialize object oriented Lua classes - * - * Provide multiple inheritance and such. - */ -class LuaeClass { + int type; + std::string str; + std::vector<std::pair<LuaValue, LuaValue>> table; + public: /** - * @param L the Lua state - * @param name the class name - */ - static void createClass(lua_State *L, const LuaObject &cls); - - /** - * Push a object to Lua, the object must have been declared with - * createClass before. - * - * @param L the Lua state - * @param cls the class description - * @param object the object to push - * @see createClass - */ - template <class T> - static void pushObject(lua_State *L, - const LuaObject &cls, - std::shared_ptr<T> object) - { - new (L, cls.name) std::shared_ptr<T>(object); - } - - /** - * Get an object from Lua. The object must have been declared with - * createClass before. + * Dump a value at the specific index. * * @param L the Lua state - * @param cls the class description - * @param index the object index - * @return the value or throw a luaL_error + * @param index the value + * @return a tree of values */ - template <class T> - static std::shared_ptr<T> getObject(lua_State *L, const LuaObject &cls, int index) - { - LUA_STACK_CHECKBEGIN(L); - - const char *name; - bool found = false; - - /* - * Get the metafield __name to check if it's a correct object. - */ - luaL_checktype(L, index, LUA_TUSERDATA); - if (!luaL_getmetafield(L, index, LUAE_CLASS_FIELDNAME)) - luaL_error(L, "invalid object"); - // NOT REACHED - - name = lua_tostring(L, -1); - lua_pop(L, 1); - - /* - * Check if that cast is allowed. - */ - found = strcmp(name, cls.name) == 0; - if (!luaL_getmetafield(L, index, LUAE_CLASS_FIELDPARENTS)) - luaL_error(L, "invalid object"); - // NOT REACHED - - lua_pushnil(L); - while (lua_next(L, -2) && !found) { - auto t = lua_tostring(L, -1); - found = strcmp(cls.name, t) == 0; - lua_pop(L, 1); - } - lua_pop(L, 1); - - if (!found) - luaL_error(L, "invalid cast from %s to %s", name, cls.name); - // NOT REACHED - - return *static_cast<const std::shared_ptr<T> *>(lua_topointer(L, index)); - } + static LuaValue copy(lua_State *L, int index); /** - * Delete an object from Lua. The object must have been declared with - * createClass before. - * - * This function is an helper for your __gc metamethods. + * Push a value to a state. * * @param L the Lua state - * @param cls the class description - * @param index the object index - * @return 0 + * @param value the value to push */ - template <class T> - static int deleteObject(lua_State *L, const LuaObject &cls, int index) - { - std::shared_ptr<T> *ptr = static_cast<std::shared_ptr<T> *>(luaL_checkudata(L, index, cls.name)); + static void push(lua_State *L, const LuaValue &value); - ptr->~shared_ptr<T>(); - - return 0; - } + /** + * Default constructor (type nil) + */ + LuaValue(); }; /** * @class Luae - * @brief Lua extended API + * @brief Add lot of convenience for Lua * - * Provide some useful functions and multiple inheritance abstraction. + * This class adds lot of functions for Lua and C++. */ -class Luae { -public: - typedef std::function<void(lua_State *L, int tkey, int tvalue)> ReadFunction; - +class Luae +{ public: - /** - * Bind an enumeration as a table into Lua. This function pushes a new table - * mapped by the keys to their value. - * - * @param L the Lua state - * @param enumeration the values to bind - */ - static void createEnum(lua_State *L, const LuaEnum *enumeration); - - /** - * Create a library table and pushes it onto the stack. - * - * @param L the Lua state - * @param functions the functions - */ - static void createLibrary(lua_State *L, - const luaL_Reg *functions); + using ReadFunction = std::function<void(lua_State *L, int tkey, int tvalue)>; /** * Get a field of a specific type from a table. Specialized for the @@ -247,7 +150,39 @@ * @return the converted type. */ template <typename T> - static T getField(lua_State *L, int idx, const std::string & name); + static T getField(lua_State *L, int idx, const std::string &name); + + /** + * Require a field from a table. + * + * @param L the Lua state + * @param idx the table index + * @param name the field name + * @return the value or call luaL_error + */ + template <typename T> + static T requireField(lua_State *L, int idx, const std::string &name) + { + lua_getfield(L, idx, name.c_str()); + + if (lua_type(L, -1) == LUA_TNIL) + luaL_error(L, "missing field `%s'", name.c_str()); + // NOT REACHED + + lua_pop(L, 1); + + return getField<T>(L, idx, name); + } + + /** + * Check a table field. + * + * @param L the Lua state + * @param idx the table index + * @param name the field name + * @return the type + */ + static int typeField(lua_State *L, int idx, const std::string &name); /** * Read a table, the function func is called for each element in the @@ -274,7 +209,7 @@ * @see require */ static void preload(lua_State *L, - const std::string & name, + const std::string &name, lua_CFunction func); /** @@ -290,7 +225,7 @@ static int referenceField(lua_State *L, int idx, int type, - const std::string & name); + const std::string &name); /** * Load a library just like it was loaded with require. @@ -301,111 +236,123 @@ * @param global store as global */ static void require(lua_State *L, - const std::string & name, + const std::string &name, lua_CFunction func, bool global); /** - * Convert a new-placement created type to the requested template. + * Initialize the registry for shared objects. + * + * @param L the Lua state + */ + static void initRegistry(lua_State *L); + + /** + * 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 <typename T> + static void pushShared(lua_State *L, + std::shared_ptr<T> o, + const std::string &name) + { + lua_getfield(L, LUA_REGISTRYINDEX, "refs"); + 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<T>(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 <typename T> + static std::shared_ptr<T> getShared(lua_State *L, int index, const char *meta) + { + using Ptr = std::shared_ptr<T>; + + Ptr *ptr = static_cast<Ptr *>(luaL_checkudata(L, index, meta)); + + return *ptr; + } + + /** + * Convert a new placement made object, without testing if its a real + * object. * * @param L the Lua state * @param idx the object index - * @param metaname which metatable - * @return the type + * @return the converted object + */ + template<class T> + static T toType(lua_State *L, int idx) + { + return reinterpret_cast<T>(lua_touserdata(L, idx)); + } + + /** + * Convert a class created with new placement. + * + * @param L the Lua state + * @param idx the value index + * @param metaname the metatable name + * @return the converted object */ template <typename T> static T toType(lua_State *L, int idx, const char *metaname) { return reinterpret_cast<T>(luaL_checkudata(L, idx, metaname)); } - - /** - * Get a table field from a specific allowed range. - * - * @param L the Lua state - * @param idx the value index - * @param name the field name - * @param min the minimum - * @param max the maximum - * @return the converted value - * @throw NotValidScript on failure - */ - template <typename T> - static T getRanged(lua_State *L, - int idx, - const char *name, - int min, - int max, - int def) - { - LUA_STACK_CHECKBEGIN(L); - - T ret; - - lua_getfield(L, idx, name); - if (lua_type(L, -1) == LUA_TNUMBER) { - int i = lua_tonumber(L, -1); - - ret = (i < min || i > max) ? def : static_cast<T>(i); - } else { - ret = def; - } - - lua_pop(L, 1); - - LUA_STACK_CHECKEQUALS(L); - - return ret; - } - - template <typename T> - static T requireRanged(lua_State *L, - int idx, - const char *name, - int min, - int max) - { - LUA_STACK_CHECKBEGIN(L); - - lua_getfield(L, idx, name); - if (lua_type(L, -1) != LUA_TNUMBER) { - std::ostringstream oss; - lua_pop(L, 1); - - oss << "field `" << name; - oss << "': is not a number"; - - throw std::invalid_argument(oss.str()); - } - - int i = lua_tonumber(L, -1); - - lua_pop(L, 1); - if (i < min || i > max) { - std::ostringstream oss; - - oss << "field `" << name; - oss << "': is out of range [" << min; - oss << ", " << max << "] expected "; - oss << " got: " << i; - - throw std::invalid_argument(oss.str()); - } - - LUA_STACK_CHECKEQUALS(L); - - return static_cast<T>(i); - } }; -void * operator new(size_t size, lua_State *L); +#if !defined(NDEBUG) + +#define LUA_STACK_CHECKBEGIN(L) \ + int __topstack = lua_gettop((L)) -void * operator new(size_t size, lua_State *L, const char *metaname); +#define LUA_STACK_CHECKEQUALS(L) \ + assert(lua_gettop((L)) == __topstack) + +#define LUA_STACK_CHECKEND(L, cond) \ + assert(lua_gettop((L)) cond == __topstack) -/* - * Delete operators are just present to avoid some warnings, Lua does garbage - * collection so these functions just do nothing. - */ +#else + +#define LUA_STACK_CHECKBEGIN(L) +#define LUA_STACK_CHECKEQUALS(L) +#define LUA_STACK_CHECKEND(L, cond) + +#endif + +} // !irccd + +void *operator new(size_t size, lua_State *L); + +void *operator new(size_t size, lua_State *L, const char *metaname); void operator delete(void *ptr, lua_State *L);
--- a/Luae.cpp Wed Nov 27 11:26:26 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,282 +0,0 @@ -/* - * Lua.cpp -- Lua helpers and such - * - * Copyright (c) 2013 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "Luae.h" - -namespace irccd { - -LuaState::LuaState() -{ - m_state = Ptr(luaL_newstate()); -} - -LuaState::LuaState(lua_State *L) -{ - m_state = Ptr(L); -} - -LuaState::LuaState(LuaState &&state) -{ - m_state = std::move(state.m_state); -} - -LuaState &LuaState::operator=(LuaState &&state) -{ - m_state = std::move(state.m_state); - - return *this; -} - -LuaState::operator lua_State*() -{ - return m_state.get(); -} - -LuaValue LuaValue::copy(lua_State *L, int index) -{ - LuaValue v; - - v.type = lua_type(L, index); - - switch (v.type) { - case LUA_TBOOLEAN: - v.boolean = lua_toboolean(L, index); - break; - case LUA_TNUMBER: - v.number = lua_tonumber(L, index); - break; - case LUA_TSTRING: - v.str = lua_tostring(L, index); - break; - case LUA_TTABLE: - { - LuaValue k; - - if (index < 0) - -- index; - - lua_pushnil(L); - while (lua_next(L, index)) { - v.table.push_back(std::make_pair(copy(L, -2), copy(L, -1))); - lua_pop(L, 1); - } - - break; - } - default: - v.type = LUA_TNIL; - break; - } - - return v; -} - -void LuaValue::push(lua_State *L, const LuaValue &value) -{ - switch (value.type) { - case LUA_TBOOLEAN: - lua_pushboolean(L, value.boolean); - break; - case LUA_TSTRING: - lua_pushlstring(L, value.str.c_str(), value.str.size()); - break; - case LUA_TNUMBER: - lua_pushnumber(L, value.number); - break; - case LUA_TTABLE: - { - lua_createtable(L, 0, 0); - - for (auto p : value.table) { - LuaValue::push(L, p.first); - LuaValue::push(L, p.second); - - lua_settable(L, -3); - } - break; - } - default: - lua_pushnil(L); - break; - } -} - -LuaValue::LuaValue() - : type(LUA_TNIL) -{ -} - -template <> -bool Luae::getField(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 Luae::getField(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 Luae::getField(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 Luae::getField(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; -} - -int Luae::typeField(lua_State *L, int idx, const std::string &name) -{ - int type; - - LUA_STACK_CHECKBEGIN(L); - - lua_getfield(L, idx, name.c_str()); - type = lua_type(L, -1); - lua_pop(L, 1); - - LUA_STACK_CHECKEQUALS(L); - - return type; -} - -void Luae::preload(lua_State *L, const std::string &name, lua_CFunction func) -{ - LUA_STACK_CHECKBEGIN(L); - - lua_getglobal(L, "package"); - lua_getfield(L, -1, "preload"); - lua_pushcfunction(L, func); - lua_setfield(L, -2, name.c_str()); - lua_pop(L, 2); - - LUA_STACK_CHECKEQUALS(L); -} - -void Luae::readTable(lua_State *L, int idx, ReadFunction func) -{ - LUA_STACK_CHECKBEGIN(L); - - lua_pushnil(L); - - if (idx < 0) - --idx; - - while (lua_next(L, idx)) { - func(L, lua_type(L, -2), lua_type(L, -1)); - lua_pop(L, 1); - } - - LUA_STACK_CHECKEQUALS(L); -} - -int Luae::referenceField(lua_State *L, int idx, int type, const std::string &name) -{ - int ref = LUA_REFNIL; - - lua_getfield(L, idx, name.c_str()); - - if (lua_type(L, -1) == type) { - lua_pushvalue(L, -1); - ref = luaL_ref(L, LUA_REGISTRYINDEX); - } - - lua_pop(L, 1); - - return ref; -} - -void Luae::require(lua_State *L, const std::string &name, lua_CFunction func, bool global) -{ - LUA_STACK_CHECKBEGIN(L); - - luaL_requiref(L, name.c_str(), func, global); - lua_pop(L, 1); - - LUA_STACK_CHECKEQUALS(L); -} - -void Luae::initRegistry(lua_State *L) -{ - lua_createtable(L, 0, 0); - lua_createtable(L, 0, 1); - lua_pushstring(L, "v"); - lua_setfield(L, -2, "__mode"); - lua_setmetatable(L, -2); - lua_setfield(L, LUA_REGISTRYINDEX, "refs"); -} - -} // !irccd - -void * operator new(size_t size, lua_State *L) -{ - return lua_newuserdata(L, size); -} - -void * operator new(size_t size, lua_State *L, const char *metaname) -{ - void *object; - - object = lua_newuserdata(L, size); - luaL_setmetatable(L, metaname); - - return object; -} - -void operator delete(void *, lua_State *) -{ -} - -void operator delete(void *, lua_State *L, const char *) -{ - lua_pushnil(L); - lua_setmetatable(L, -2); -}
--- a/Luae.h Wed Nov 27 11:26:26 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,361 +0,0 @@ -/* - * Lua.h -- Lua helpers and such - * - * Copyright (c) 2013 David Demelier <markand@malikania.fr> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _LUA_H_ -#define _LUA_H_ - -#include <cassert> -#include <functional> -#include <memory> -#include <string> -#include <vector> - -#include <lua.hpp> - -namespace irccd { - -/** - * @class LuaState - * @brief Wrapper for lua_State - * - * This class automatically create a new Lua state and add implicit - * cast operator plus RAII destruction. - */ -class LuaState { -private: - struct Deleter { - void operator()(lua_State *L) - { - lua_close(L); - } - }; - - using Ptr = std::unique_ptr<lua_State, Deleter>; - - Ptr m_state; - -public: - LuaState(const LuaState &) = delete; - LuaState &operator=(const LuaState &) = delete; - - /** - * Default constructor. Create a new state. - */ - LuaState(); - - /** - * Use the already created state. - * - * @param L the state to use - */ - LuaState(lua_State *L); - - /** - * Move constructor. - * - * @param state the Lua state to move - */ - LuaState(LuaState &&state); - - /** - * Move assignment operator. - * - * @param state the Lua state to move - */ - LuaState &operator=(LuaState &&state); - - /** - * Implicit cast operator for convenient usage to C Lua API. - * - * @return the state as lua_State * - */ - operator lua_State*(); -}; - -/** - * @class LuaValue - * @brief A fake variant for Lua values - * - * This class is primarly used for copying Lua values without checking - * the types, useful to pass data. - */ -class LuaValue { -private: - union { - lua_Number number; - bool boolean; - }; - - int type; - std::string str; - std::vector<std::pair<LuaValue, LuaValue>> table; - -public: - /** - * Dump a value at the specific index. - * - * @param L the Lua state - * @param index the value - * @return a tree of values - */ - static LuaValue copy(lua_State *L, int index); - - /** - * Push a value to a state. - * - * @param L the Lua state - * @param value the value to push - */ - static void push(lua_State *L, const LuaValue &value); - - /** - * Default constructor (type nil) - */ - LuaValue(); -}; - -/** - * @class Luae - * @brief Add lot of convenience for Lua - * - * This class adds lot of functions for Lua and C++. - */ -class Luae -{ -public: - using ReadFunction = std::function<void(lua_State *L, int tkey, int tvalue)>; - - /** - * Get a field of a specific type from a table. Specialized for the - * types: int, double, bool and string. - * - * @param L the Lua state - * @param idx the table index - * @param name the field name - * @return the converted type. - */ - template <typename T> - static T getField(lua_State *L, int idx, const std::string &name); - - /** - * Require a field from a table. - * - * @param L the Lua state - * @param idx the table index - * @param name the field name - * @return the value or call luaL_error - */ - template <typename T> - static T requireField(lua_State *L, int idx, const std::string &name) - { - lua_getfield(L, idx, name.c_str()); - - if (lua_type(L, -1) == LUA_TNIL) - luaL_error(L, "missing field `%s'", name.c_str()); - // NOT REACHED - - lua_pop(L, 1); - - return getField<T>(L, idx, name); - } - - /** - * Check a table field. - * - * @param L the Lua state - * @param idx the table index - * @param name the field name - * @return the type - */ - static int typeField(lua_State *L, int idx, const std::string &name); - - /** - * Read a table, the function func is called for each element in the - * table. Parameter tkey is the Lua type of the key, parameter tvalue is - * the Lua type of the value. The key is available at index -2 and the - * value at index -1. - * - * <strong>Do not pop anything within the function.</strong> - * - * @param L the Lua state - * @param idx the table index - * @param func the function to call - */ - static void readTable(lua_State *L, int idx, ReadFunction func); - - /** - * 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); - - /** - * Reference a field from a table at the index. The reference is created in - * the registry only if type matches. - * - * @param L the Lua state - * @param idx the table index - * @param type the type requested - * @param name the field name - * @return the reference or LUA_REFNIL on problem - */ - static int referenceField(lua_State *L, - int idx, - int type, - const std::string &name); - - /** - * 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); - - /** - * Initialize the registry for shared objects. - * - * @param L the Lua state - */ - static void initRegistry(lua_State *L); - - /** - * 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 <typename T> - static void pushShared(lua_State *L, - std::shared_ptr<T> o, - const std::string &name) - { - lua_getfield(L, LUA_REGISTRYINDEX, "refs"); - 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<T>(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 <typename T> - static std::shared_ptr<T> getShared(lua_State *L, int index, const char *meta) - { - using Ptr = std::shared_ptr<T>; - - Ptr *ptr = static_cast<Ptr *>(luaL_checkudata(L, index, meta)); - - return *ptr; - } - - /** - * 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<class T> - static T toType(lua_State *L, int idx) - { - return reinterpret_cast<T>(lua_touserdata(L, idx)); - } - - /** - * Convert a class created with new placement. - * - * @param L the Lua state - * @param idx the value index - * @param metaname the metatable name - * @return the converted object - */ - template <typename T> - static T toType(lua_State *L, int idx, const char *metaname) - { - return reinterpret_cast<T>(luaL_checkudata(L, idx, metaname)); - } -}; - -#if !defined(NDEBUG) - -#define LUA_STACK_CHECKBEGIN(L) \ - int __topstack = lua_gettop((L)) - -#define LUA_STACK_CHECKEQUALS(L) \ - assert(lua_gettop((L)) == __topstack) - -#define LUA_STACK_CHECKEND(L, cond) \ - assert(lua_gettop((L)) cond == __topstack) - -#else - -#define LUA_STACK_CHECKBEGIN(L) -#define LUA_STACK_CHECKEQUALS(L) -#define LUA_STACK_CHECKEND(L, cond) - -#endif - -} // !irccd - -void *operator new(size_t size, lua_State *L); - -void *operator new(size_t size, lua_State *L, const char *metaname); - -void operator delete(void *ptr, lua_State *L); - -void operator delete(void *ptr, lua_State *L, const char *metaname); - -#endif // !_LUA_H_