Mercurial > code
view C++/Luae.h @ 202:99d0887395cc
Luae: Add set() function for LuaeTable
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sat, 04 Jan 2014 18:02:06 +0100 |
parents | 8ee4a34e166c |
children | b54c83ed71ca |
line wrap: on
line source
/* * Luae.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 <cstddef> #include <functional> #include <memory> #include <string> #include <unordered_map> #include <vector> #include <lua.hpp> /* {{{ LuaeState */ /** * @class LuaeState * @brief Wrapper for lua_State * * This class automatically create a new Lua state and add implicit * cast operator plus RAII destruction. */ class LuaeState { private: struct Deleter { void operator()(lua_State *L) { lua_close(L); } }; using Ptr = std::unique_ptr<lua_State, Deleter>; Ptr m_state; void initRegistry(); public: /* * FieldRefs: The field stored into the registry to avoid * recreation of shared objects. */ static const char *FieldRefs; LuaeState(const LuaeState &) = delete; LuaeState &operator=(const LuaeState &) = delete; /** * Default constructor. Create a new state. */ LuaeState(); /** * Use the already created state. * * @param L the state to use */ LuaeState(lua_State *L); /** * Move constructor. * * @param state the Lua state to move */ LuaeState(LuaeState &&state); /** * Move assignment operator. * * @param state the Lua state to move */ LuaeState &operator=(LuaeState &&state); /** * Implicit cast operator for convenient usage to C Lua API. * * @return the state as lua_State * */ operator lua_State*(); }; /* }}} */ /* {{{ LuaeValue */ /** * @class LuaeValue * @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 LuaeValue { private: union { lua_Number number; bool boolean; }; int type; std::string str; std::vector<std::pair<LuaeValue, LuaeValue>> table; public: /** * Dump a value at the specific index. * * @param L the Lua state * @param index the value * @return a tree of values */ static LuaeValue 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 LuaeValue &value); /** * Default constructor (type nil) */ LuaeValue(); }; /* }}} */ /* {{{ LuaeTable */ /** * @class LuaeTable * @brief Some function for table manipulation * * Read, reference and get fields from tables. */ class LuaeTable { 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 get(lua_State *L, int idx, const std::string &name); /** * Set a table field. Specialized for the same fields as get. * * @param L the Lua state * @param idx the index * @param name the field name * @param value the value * @see get */ template <typename T> static void set(lua_State *L, int idx, const std::string &name, const T &value); /** * Overload for string literals. * * @param L the Lua state * @param idx the index * @param name the field name * @param s the string */ template <size_t N> static void set(lua_State *L, int idx, const std::string &name, const char (&s)[N]) { set<const char *>(L, idx, name, s); } /** * 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 require(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 get<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 type(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 read(lua_State *L, int idx, ReadFunction 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 ref(lua_State *L, int idx, int type, const std::string &name); }; /* }}} */ /* {{{ 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<luaL_Reg>; template <typename T> using Ptr = std::shared_ptr<T>; 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 <typename T> static void push(lua_State *L, Ptr<T> 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<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 Ptr<T> 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<Ptr<T> *>(lua_touserdata(L, index)); } }; /* }}} */ /* {{{ LuaeEnum */ /** * @class LuaeEnum * @brief Binds and get enumeration * * Bind C/C++ enumeration as tables to Lua in the key-value form. Two * methods push and get are also available for enumeration flags. */ class LuaeEnum { public: /** * The definition of the enumeration */ using Def = std::unordered_map<std::string, int>; /** * Bind the enumeration as a table into an existing table. * * @param L the Lua state * @param def the definition * @param index the table index * @param name the field name to store the enumeration */ static void create(lua_State *L, const Def &def, int index, const std::string &name); /** * Push the value enumeration as a table to Lua. This is used * as the OR replacement. * * @param L the Lua state * @param def the definition * @param value the value */ static void push(lua_State *L, const Def &def, int value); /** * Get the enumeration from Lua. Raises an error if the * value at the index is not a table. * * This is used as the OR replacement. * * @param L the Lua state * @param index the value index * @return the value */ static int get(lua_State *L, int index); }; /* }}} */ /* {{{ 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<class T> static T toType(lua_State *L, int idx) { return reinterpret_cast<T>(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); void operator delete(void *ptr, lua_State *L); void operator delete(void *ptr, lua_State *L, const char *metaname); #endif // !_LUA_H_