# HG changeset patch # User David Demelier # Date 1399648345 -7200 # Node ID e01ee0c72c438a30ee7e8bb809c346ee4bed1a79 # Parent ca69910b140782807798238b9783bae1be7f3277 Luae: begin refactoring of Luae diff -r ca69910b1407 -r e01ee0c72c43 C++/Luae.cpp --- a/C++/Luae.cpp Fri May 09 09:15:52 2014 +0200 +++ b/C++/Luae.cpp Fri May 09 17:12:25 2014 +0200 @@ -20,398 +20,6 @@ #include "Luae.h" -/* -------------------------------------------------------- - * LuaeState - * -------------------------------------------------------- */ - -const char *LuaeState::FieldRefs = "__refs"; - -void LuaeState::initRegistry() -{ - if (LuaeTable::type(*this, LUA_REGISTRYINDEX, FieldRefs) == LUA_TNIL) { - lua_createtable(*this, 0, 0); - lua_createtable(*this, 0, 1); - lua_pushstring(*this, "v"); - lua_setfield(*this, -2, "__mode"); - lua_setmetatable(*this, -2); - lua_setfield(*this, LUA_REGISTRYINDEX, FieldRefs); - } -} - -LuaeState::LuaeState() -{ - m_state = Ptr(luaL_newstate()); - - initRegistry(); -} - -LuaeState::LuaeState(lua_State *L) -{ - m_state = Ptr(L); - - initRegistry(); -} - -LuaeState::LuaeState(LuaeState &&state) -{ - m_state = std::move(state.m_state); - - initRegistry(); -} - -LuaeState &LuaeState::operator=(LuaeState &&state) -{ - m_state = std::move(state.m_state); - - initRegistry(); - - return *this; -} - -LuaeState::operator lua_State*() -{ - return m_state.get(); -} - -/* -------------------------------------------------------- - * LuaeValue - * -------------------------------------------------------- */ - -LuaeValue LuaeValue::copy(lua_State *L, int index) -{ - LuaeValue 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: - { - 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 LuaeValue::push(lua_State *L, const LuaeValue &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) { - push(L, p.first); - push(L, p.second); - - lua_settable(L, -3); - } - break; - } - default: - lua_pushnil(L); - break; - } -} - -LuaeValue::LuaeValue() - : type(LUA_TNIL) -{ -} - -/* -------------------------------------------------------- - * LuaeTable - * -------------------------------------------------------- */ - -void LuaeTable::create(lua_State *L, int nrec, int narr) -{ - LUAE_STACK_CHECKBEGIN(L); - - lua_createtable(L, nrec, narr); - - LUAE_STACK_CHECKEND(L, - 1); -} - -int LuaeTable::type(lua_State *L, int idx, const std::string &name) -{ - int type; - - LUAE_STACK_CHECKBEGIN(L); - - lua_getfield(L, idx, name.c_str()); - type = lua_type(L, -1); - lua_pop(L, 1); - - LUAE_STACK_CHECKEQUALS(L); - - return type; -} - -void LuaeTable::read(lua_State *L, int idx, ReadFunction func) -{ - LUAE_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); - } - - LUAE_STACK_CHECKEQUALS(L); -} - -int LuaeTable::ref(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; -} - -/* -------------------------------------------------------- - * LuaeClass - * -------------------------------------------------------- */ - -const char *LuaeClass::FieldName = "__name"; -const char *LuaeClass::FieldParents = "__parents"; - -void LuaeClass::create(lua_State *L, const Def &def) -{ - LUAE_STACK_CHECKBEGIN(L); - luaL_newmetatable(L, def.name.c_str()); - - // Store the name of the class - lua_pushlstring(L, def.name.c_str(), def.name.length()); - lua_setfield(L, -2, FieldName); - - // Store the parents names - int i = 0; - - lua_createtable(L, 0, 0); - for (auto d(def.parent); d != nullptr; d = d->parent) { - lua_pushlstring(L, d->name.c_str(), d->name.length()); - lua_rawseti(L, -2, ++i); - } - lua_setfield(L, -2, FieldParents); - - // Metamethods - if (def.metamethods.size() > 0) { - for (auto m : def.metamethods) { - lua_pushcfunction(L, m.func); - lua_setfield(L, -2, m.name); - } - } - - // Methods - lua_createtable(L, 0, 0); - for (auto m : def.methods) { - lua_pushcfunction(L, m.func); - lua_setfield(L, -2, m.name); - } - - // Create the inheritance - if (def.parent != nullptr) { - luaL_newmetatable(L, def.parent->name.c_str()); - lua_setmetatable(L, -2); - } - lua_setfield(L, -2, "__index"); - - lua_pop(L, 1); - LUAE_STACK_CHECKEQUALS(L); -} - -void LuaeClass::testShared(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 cast"); - - // 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 cast"); - - 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); -} - -/* -------------------------------------------------------- - * LuaeEnum - * -------------------------------------------------------- */ - -void LuaeEnum::create(lua_State *L, const Def &def) -{ - LUAE_STACK_CHECKBEGIN(L); - - lua_createtable(L, 0, 0); - - for (auto p : def) { - lua_pushinteger(L, p.second); - lua_setfield(L, -2, p.first); - } - - LUAE_STACK_CHECKEND(L, - 1); -} - -void LuaeEnum::create(lua_State *L, const Def &def, int index) -{ - LUAE_STACK_CHECKBEGIN(L); - - if (index < 0) - -- index; - - for (auto p : def) { - lua_pushinteger(L, p.second); - lua_setfield(L, index, p.first); - } - - LUAE_STACK_CHECKEQUALS(L); -} - -void LuaeEnum::create(lua_State *L, - const Def &def, - int index, - const std::string &name) -{ - LUAE_STACK_CHECKBEGIN(L); - - create(L, def); - - if (index < 0) - -- index; - - lua_setfield(L, index, name.c_str()); - - LUAE_STACK_CHECKEQUALS(L); -} - -void LuaeEnum::push(lua_State *L, const Def &def, int value) -{ - LUAE_STACK_CHECKBEGIN(L); - lua_createtable(L, 0, 0); - - for (auto p : def) { - if (value & p.second) { - lua_pushinteger(L, p.second); - lua_rawseti(L, -2, p.second); - } - } - - LUAE_STACK_CHECKEND(L, - 1); -} - -int LuaeEnum::get(lua_State *L, int index) -{ - int value = 0; - - LUAE_STACK_CHECKBEGIN(L); - - if (lua_type(L, index) == LUA_TTABLE) { - if (index < 0) - -- index; - - lua_pushnil(L); - while (lua_next(L, index)) { - if (lua_type(L, -1) == LUA_TNUMBER) - value |= lua_tointeger(L, -1); - - lua_pop(L, 1); - } - } - - LUAE_STACK_CHECKEQUALS(L); - - return value; -} - -int LuaeEnum::require(lua_State *L, int index) -{ - int value = 0; - - LUAE_STACK_CHECKBEGIN(L); - - luaL_checktype(L, index, LUA_TTABLE); - value = LuaeEnum::get(L, index); - - LUAE_STACK_CHECKEQUALS(L); - - return value; -} - -/* -------------------------------------------------------- - * Luae - * -------------------------------------------------------- */ - void Luae::doexecute(lua_State *L, int status) { if (status != LUA_OK) { @@ -422,9 +30,37 @@ } } +void Luae::assertBegin(lua_State *L) +{ +#if !defined(NDEBUG) + lua_pushinteger(L, lua_gettop(L)); + lua_setfield(L, LUA_REGISTRYINDEX, FieldTop); +#endif +} + +void Luae::assertEnd(lua_State *L, int expected) +{ +#if !defined(NDEBUG) + lua_getfield(L, LUA_REGISTRYINDEX, FieldTop); + if (lua_type(L, -1) == LUA_TNIL) + luaL_error(L, "stack verification without top field, call assertBegin"); + // NOTREACHED + + auto before = lua_tointeger(L, -1); + lua_pop(L, 1); + + if (before != (gettop(L) - expected)) + luaL_error(L, "stack size error: expected %d, got %d", expected, gettop(L) - before); + // NOTREACHED + + lua_pushnil(L); + lua_setfield(L, LUA_REGISTRYINDEX, FieldTop); +#endif +} + void Luae::preload(lua_State *L, const std::string &name, lua_CFunction func) { - LUAE_STACK_CHECKBEGIN(L); + assertBegin(L); lua_getglobal(L, "package"); lua_getfield(L, -1, "preload"); @@ -432,17 +68,17 @@ lua_setfield(L, -2, name.c_str()); lua_pop(L, 2); - LUAE_STACK_CHECKEQUALS(L); + assertEnd(L, 0); } -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) { - LUAE_STACK_CHECKBEGIN(L); + assertBegin(L); - luaL_requiref(L, name.c_str(), func, global); + luaL_requiref(L, name.c_str(), func, false); lua_pop(L, 1); - LUAE_STACK_CHECKEQUALS(L); + assertEnd(L, 0); } void *operator new(size_t size, lua_State *L) diff -r ca69910b1407 -r e01ee0c72c43 C++/Luae.h --- a/C++/Luae.h Fri May 09 09:15:52 2014 +0200 +++ b/C++/Luae.h Fri May 09 17:12:25 2014 +0200 @@ -36,332 +36,6 @@ #include -#if !defined(NDEBUG) - -/** - * Store the current stack size. Should be called at the beginning of a - * function. - * - * @param L the Lua state - */ -#define LUAE_STACK_CHECKBEGIN(L) \ - int __topstack = lua_gettop((L)) - -/** - * Check if the current stack size match the beginning. LUAE_STACK_CHECKBEGIN - * must have been called. - * - * @param L the Lua state - */ -#define LUAE_STACK_CHECKEQUALS(L) \ - assert(lua_gettop((L)) == __topstack) - -/** - * Check if the current stack size match the condition. LUAE_STACK_CHECKBEGIN - * must have been called. - * - * @param L the Lua state - * @param cond the condition - */ -#define LUAE_STACK_CHECKEND(L, cond) \ - assert(lua_gettop((L)) cond == __topstack) - -#else - -/** - * Store the current stack size. Should be called at the beginning of a - * function. - * - * @param L the Lua state - */ -#define LUAE_STACK_CHECKBEGIN(L) - -/** - * Check if the current stack size match the beginning. LUAE_STACK_CHECKBEGIN - * must have been called. - * - * @param L the Lua state - */ -#define LUAE_STACK_CHECKEQUALS(L) - -/** - * Check if the current stack size match the condition. LUAE_STACK_CHECKBEGIN - * must have been called. - * - * @param L the Lua state - * @param cond the condition - */ -#define LUAE_STACK_CHECKEND(L, cond) - -#endif - -/* {{{ 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; - - Ptr m_state; - - void initRegistry(); - -public: - /** - * The field stored into the registry to avoid recreation of shared - * objects. - */ - static const char *FieldRefs; - - /** - * Deleted copy constructor. - */ - LuaeState(const LuaeState &) = delete; - - /** - * Deleted copy assignment. - */ - 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*(); -}; - -/* }}} */ - -/* {{{ 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: - /** - * Methods for a class. - */ - using Methods = std::vector; - - /** - * Smart pointer for Luae objects. - */ - template - using Ptr = std::shared_ptr; - - /** - * @struct Def - * @brief Definition of a class - */ - struct Def { - std::string name; //!< metatable name - Methods methods; //!< methods - Methods metamethods; //!< metamethods - const Def *parent; //!< optional parent class - }; - - /** - * The field stored in the object metatable about the object metatable - * name. - */ - static const char *FieldName; - - /** - * The field that holds all parent classes. It is used to verify casts. - */ - 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); - } - - /** - * Check if the object at index is suitable for cast to meta. Calls - * luaL_error if not. - * - * @param L the Lua state - * @param index the value index - * @param meta the object name - */ - static void testShared(lua_State *L, int index, const char *meta); - - /** - * 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) - { - testShared(L, index, meta); - - return *static_cast *>(lua_touserdata(L, index)); - } - - /** - * Delete the shared pointer at the given index. This function does - * not check if the type is valid for performance reason. And because - * it's usually called in __gc, there is no reason to check. - * - * Also return 0 the __gc method can directly call - * return LuaeClass::deleteShared(...) - * - * @param L the Lua state - * @param index the index - * @return 0 for convenience - */ - template - static int deleteShared(lua_State *L, int index) - { - LUAE_STACK_CHECKBEGIN(L); - - static_cast *>(lua_touserdata(L, index))->~shared_ptr(); - - LUAE_STACK_CHECKEQUALS(L); - - return 0; - } -}; - -/* }}} */ - -/* {{{ 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> 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(); -}; - -/* }}} */ - -/* {{{ Luae */ - /** * @class Luae * @brief Add lot of convenience for Lua @@ -370,6 +44,9 @@ */ class Luae { private: + static constexpr const char *FieldRefs = "__luae_refs"; + static constexpr const char *FieldTop = "__luae_topcheck"; + /* * Wrapper for dofile and dostring. */ @@ -379,51 +56,16 @@ /** * Map from string to function. */ - using Reg = std::unordered_map; - - /** - * @struct Convert - * @brief Push or get values - */ - template - struct Convert { - static const bool hasPush = false; //!< has push function - static const bool hasGet = false; //!< has get function - static const bool hasCheck = false; //!< has check function - }; + using Reg = std::unordered_map; /** - * @struct Iterator - * @brief Wrap STL containers - */ - template - struct Iterator { - Type begin; //!< the beginning - Type end; //!< the end - Type current; //!< the current index - - /** - * Construct the iterator. - * - * @param begin the beginning - * @param end the end - */ - Iterator(Type begin, Type end) - : begin(begin) - , end(end) - , current(begin) - { - } - }; - - /** + * ## Userdata object + * * Test if the object can be pushed as a userdata. If the object can * be pushed as a userdata, it must match the following requirements: * * - Copy constructible - * - IsUserdata overload must have const char *MetatableName - * - * ## Userdata object + * - TypeInfo overload must have const char *name and inherit TypeUserdata * * The following example code allows the class Object to be pushed * and get as Lua userdata. @@ -432,12 +74,10 @@ * struct Object { }; * * template <> - * struct Luae::IsUserdata : std::true_type { - * static const char *MetatableName; + * struct Luae::TypeInfo : Luae::TypeUserdata { + * static constexpr const char *name = "Object"; * }; * - * const char *Luae::IsUserdata::MetatableName = "Object"; - * * int l_push(lua_State *L) * { * Luae::push(L, Object()); @@ -448,6 +88,7 @@ * Object *o = Luae::get(L, 1); * } * @endcode + * * @note You don't need to add the pointer type to the get template parameter * * ## Custom object @@ -455,17 +96,15 @@ * This other example can be used to push custom objects but not as * userdata. You can use this to push and read tables for instance. * + * For this you must overload TypeInfo to the object and inherit to TypeCustom. + * * @code * struct Point { * int x, y; * }; * * template <> - * struct Luae::Convert { - * static const bool hasPush = true; - * static const bool hasGet = true; - * static const bool hasCheck = true; - * + * struct Luae::TypeInfo : Luae::TypeCustom { * static void push(lua_State *L, const Point &p) * { * lua_createtable(L, 0, 0); @@ -509,20 +148,66 @@ * Point p = Luae::get(L, 1); * } * @endcode + * * @note Here you get a T and not a T * */ template - struct IsUserdata : std::false_type { }; + struct TypeInfo { + static const bool isCustom = false; + static const bool isUserdata = false; + }; + + /** + * Helpers for custom types. It is already overloaded for all standard + * types : + * - int + * - bool + * - double + * - std::string + * - std::32string + */ + struct TypeCustom { + static const bool canGet = true; + static const bool canPush = true; + static const bool canCheck = true; + static const bool isCustom = true; + static const bool isUserdata = false; + }; + + /** + * Helper for userdata. It only require to set the name field + * as the metatable name. + */ + struct TypeUserdata { + static const bool isUserdata = true; + static const bool isCustom = false; + }; private: template - struct IsSharedUserdata : std::false_type { }; + struct IsCustom { + static const bool value = TypeInfo::isCustom; + }; + + template + struct IsUserdata { + static const bool value = TypeInfo::isUserdata; + }; + + template + struct IsSharedUserdata : std::false_type {}; template struct IsSharedUserdata> { - static const bool value = IsUserdata::value; + static const bool value = TypeInfo::isUserdata; }; + template + using EnableIf = typename std::enable_if::type; + + template + using SharedType = typename T::element_type; + public: /* ------------------------------------------------- * Standard Lua API wrappers @@ -610,6 +295,18 @@ } /** + * Create a table. + * + * @param L the Lua state + * @param nr the number of array record + * @param ns the number of sequence + */ + static inline void createtable(lua_State *L, int nr = 0, int ns = 0) + { + lua_createtable(L, nr, ns); + } + + /** * Load and execute a file. * * @param L the Lua state @@ -1190,6 +887,24 @@ * ------------------------------------------------- */ /** + * Call this prior to assertEnd. + * + * @param L the Lua state + */ + static void assertBegin(lua_State *L); + + /** + * Call this to check the stack size after asserBegin. The stack size + * is automatically adjusted to the last size so the expected argument + * can just be set to the real expected size just like if the stack size + * were 0. + * + * @param L the Lua state + * @param expected the stack size expected + */ + static void assertEnd(lua_State *L, int expected); + + /** * 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. @@ -1199,9 +914,7 @@ * @param func the opening library * @see require */ - static void preload(lua_State *L, - const std::string &name, - lua_CFunction func); + static void preload(lua_State *L, const std::string &name, lua_CFunction func); /** * Load a library just like it was loaded with require. @@ -1209,60 +922,71 @@ * @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); + static void require(lua_State *L, const std::string &name, lua_CFunction func); + + template + static int push(lua_State *L, + const T &value, + EnableIf::value, T> * = 0) + { + static_assert(TypeInfo::canPush, "type not supported"); + + TypeInfo::push(L, value); + + return 1; + } + + template + static int push(lua_State *L, + const T &value, + EnableIf::value, T> * = 0) + { + new (L, TypeInfo::name) T(value); + + return 1; + } - /** - * TODO: DOC - * - * @param L the Lua state - * @param value the value - */ template - static void push(lua_State *L, - const T &value, - typename std::enable_if::value, T>::type * = 0, - typename std::enable_if::value, T>::type * = 0) + static int push(lua_State *L, + const std::shared_ptr &value, + EnableIf>::value, T> * = 0) { - static_assert(Convert::hasPush, "type not supported"); + assertBegin(L); - Convert::push(L, value); + /* + * If already in the registry use the same shared_ptr to get the same + * pointer address within Lua. This let scripts using the object + * as a table key for instance. + */ + getfield(L, LUA_REGISTRYINDEX, FieldRefs); + if (type(L, -1) == LUA_TNIL) { + pop(L); + createtable(L, 0, 0); + createtable(L, 0, 0); + pushfstring(L, "v"); + setfield(L, -2, "__mode"); + setmetatable(L, -2); + pushvalue(L, -1); + setfield(L, LUA_REGISTRYINDEX, FieldRefs); + } + + rawget(L, -1, value.get()); + if (type(L, -1) == LUA_TNIL) { + pop(L); + new (L, TypeInfo::name) std::shared_ptr(value); + pushvalue(L, -1); + rawset(L, -3, value.get()); + } + + // Remove refs table + remove(L, -2); + + return 1; } /** - * TODO: DOC - * - * @param L the Lua state - * @param value the value - */ - template - static void push(lua_State *L, - const T &value, - typename std::enable_if::value, T>::type * = 0) - { - new (L, IsUserdata::MetatableName) T(value); - } - - /** - * TODO: DOC - * - * @param L the Lua state - * @param value the value - */ - template - static void push(lua_State *L, - const T &value, - typename std::enable_if::value>::type * = 0) - { - LuaeClass::pushShared(L, value, IsUserdata::MetatableName); - } - - /** - * Overload for string literals and arrays. + * Overload for string literals. * * @param L the Lua state * @param s the string @@ -1273,159 +997,50 @@ push(L, s); } - /** - * Get the data, the type is not checked. - * - * @param L the Lua state - * @param index the index - * @return a T value - */ template - static T - get(lua_State *L, - int index, - typename std::enable_if::value, T>::type * = 0, - typename std::enable_if::value, T>::type * = 0) + static EnableIf::value, T> + get(lua_State *L, int index) { - static_assert(Convert::hasGet, "type not supported"); + static_assert(TypeInfo::canGet, "type not supported"); - return Convert::get(L, index); + return TypeInfo::get(L, index); } - /** - * Get a userdata object. The type is not checked and just cast. - * - * @param L the Lua state - * @param index the index - * @return a T * value - */ template - static T * - get(lua_State *L, - int index, - typename std::enable_if::value, T *>::type * = 0) + static EnableIf::value, T *> + get(lua_State *L, int index) { return Luae::toType(L, index); } - - /** - * Get a shared pointer object created from LuaeClass. The type is not - * checked. - * - * @param L the Lua state - * @param index the index - * @return a std::shared_ptr<> - */ - template - static std::shared_ptr - get(lua_State *L, - int index, - typename std::enable_if::value, T>::type * = 0) - { - return LuaeClass::getShared(L, index); - } - - /** - * Check for a data. The type is checked. - * - * @param L the Lua state - * @param index the index - * @return a T value - */ + template - static T - check(lua_State *L, - int index, - typename std::enable_if::value, T>::type * = 0, - typename std::enable_if::value, T>::type * = 0) + static EnableIf>::value, std::shared_ptr> + get(lua_State *L, int index) { - static_assert(Convert::hasCheck, "type not supported"); - - return Convert::check(L, index); + return *Luae::toType *>(L, index); } + + template + static EnableIf::value, T> + check(lua_State *L, int index) + { + static_assert(TypeInfo::canCheck, "type not supported"); - /** - * Check for a userdata object. - * - * @param L the Lua state - * @param index the index - * @return a T * value - */ - template - static T * - check(lua_State *L, - int index, - typename std::enable_if::value, T *>::type * = 0) - { - return Luae::toType(L, index, IsUserdata::MetatableName); + return TypeInfo::check(L, index); } - /** - * Check for a shared pointer object created from LuaeClass. - * - * @param L the Lua state - * @param index the index - * @return a std::shared_ptr<> - */ - template - static std::shared_ptr - check(lua_State *L, - int index, - typename std::enable_if::value, T>::type * = 0) + template + static EnableIf::value, T *> + check(lua_State *L, int index) { - return LuaeClass::getShared(L, index, IsUserdata::MetatableName); + return Luae::toType(L, index, TypeInfo::name); } - /** - * Push an iterator function onto the stack. May be used by functions - * calls or directly with __pairs. - * - * The container must at least have: - * value_type, - * const_iterator - * - * The value of the container must also be pushable with Luae::Convert. - * - * @param L the Lua state - * @param container the container - * @return 1 - */ - template > - static int pushIterator(lua_State *L, const Container &container) + template + static EnableIf::value, T> + check(lua_State *L, int index) { - using ValueType = typename Container::value_type; - using IteratorType = typename Container::const_iterator; - - LUAE_STACK_CHECKBEGIN(L); - - new (L) Iterator(container.cbegin(), container.cend()); - - lua_createtable(L, 0, 0); - lua_pushcfunction(L, [] (lua_State *L) -> int { - toType *>(L, 1)->~Iterator(); - - return 0; - }); - lua_setfield(L, -2, "__gc"); - lua_setmetatable(L, -2); - - // Push the iterator function - lua_pushcclosure(L, [] (lua_State *L) -> int { - auto it = toType *>(L, lua_upvalueindex(1)); - auto top = lua_gettop(L); - - if (it->current == it->end) - return 0; - - Converter::push(L, *(it->current++)); - - return lua_gettop(L) - top; - }, 1); - - LUAE_STACK_CHECKEND(L, -1); - - return 1; + return *Luae::toType(L, index, TypeInfo>::name); } /** @@ -1462,8 +1077,9 @@ * @brief Overload for nil. */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported +struct Luae::TypeInfo : Luae::TypeCustom { + static const bool canGet = false; + static const bool canCheck = false; /** * Push nil. @@ -1480,11 +1096,7 @@ * @brief Overload for booleans. */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - +struct Luae::TypeInfo : Luae::TypeCustom { /** * Push the boolean value. * @@ -1524,11 +1136,7 @@ * @brief Overload for integers. */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - +struct Luae::TypeInfo : Luae::TypeCustom { /** * Push the integer value. * @@ -1568,11 +1176,7 @@ * @brief Overload for longs. */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - +struct Luae::TypeInfo : Luae::TypeCustom { /** * Push the integer value. * @@ -1612,11 +1216,7 @@ * @brief Overload for doubles. */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - +struct Luae::TypeInfo : Luae::TypeCustom { /** * Push the double value. * @@ -1656,11 +1256,7 @@ * @brief Overload for std::string. */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - +struct Luae::TypeInfo : Luae::TypeCustom { /** * Push the string value. * @@ -1700,11 +1296,7 @@ * @brief Overload for std::u32string. */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - +struct Luae::TypeInfo : Luae::TypeCustom { /** * Push the string value. * @@ -1767,80 +1359,10 @@ }; /** - * @brief Overload for string list - */ -template <> -struct Luae::Convert> { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - - /** - * Push a string list. - * - * @param L the Lua state - * @param value the value - */ - static void push(lua_State *L, const std::vector &value) - { - int i = 0; - - lua_createtable(L, value.size(), 0); - for (const auto &s : value) { - lua_pushlstring(L, s.c_str(), s.length()); - lua_rawseti(L, -2, ++i); - } - } - - /** - * Get a string list. - * - * @param L the Lua state - * @param index the index - * @return the list - */ - static std::vector get(lua_State *L, int index) - { - std::vector list; - - if (index < 0) - -- index; - - lua_pushnil(L); - while (lua_next(L, index)) { - if (lua_type(L, -1) == LUA_TSTRING) - list.push_back(lua_tostring(L, -1)); - - lua_pop(L, 1); - } - - return list; - } - - /** - * Check for a string list. - * - * @param L the Lua state - * @param index the index - * @return the list - */ - static std::vector check(lua_State *L, int index) - { - luaL_checktype(L, index, LUA_TTABLE); - - return get(L, index); - } -}; - -/** * @brief Overload for const char * */ template <> -struct Luae::Convert { - static const bool hasPush = true; //!< push supported - static const bool hasGet = true; //!< get supported - static const bool hasCheck = true; //!< check supported - +struct Luae::TypeInfo : Luae::TypeCustom { /** * Push the string value. * @@ -1876,244 +1398,6 @@ } }; -/* }}} */ - -/* {{{ LuaeTable */ - -/** - * @class LuaeTable - * @brief Some function for table manipulation - * - * Read, reference and get fields from tables. - */ -class LuaeTable { -public: - /** - * The map function for \ref read - */ - 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 narr 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. - * - * @param L the Lua state - * @param idx the table index - * @param name the field name - * @return the converted type. - */ - template - 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 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 - static void set(lua_State *L, int idx, const std::string &name, const T &value) - { - LUAE_STACK_CHECKBEGIN(L); - - Luae::push(L, value); - Luae::setfield(L, (idx < 0) ? --idx : idx, name); - - LUAE_STACK_CHECKEQUALS(L); - } - - /** - * Overload for string literals. - * - * @param L the Lua state - * @param idx the index - * @param name the field name - * @param s the string - */ - template - static void set(lua_State *L, int idx, const std::string &name, const char (&s)[N]) - { - set(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 - static T require(lua_State *L, int idx, const std::string &name) - { - LUAE_STACK_CHECKBEGIN(L); - - 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); - auto v = get(L, idx, name); - - LUAE_STACK_CHECKEQUALS(L); - - return v; - } - - /** - * 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. - * - * Do not pop anything within the function. - * - * @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); -}; - -/* }}} */ - -/* {{{ 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; - - /** - * Bind the enumeration and keep it at the top of the stack. - * - * @param L the Lua state - * @param def the definition - */ - static void create(lua_State *L, const Def &def); - - /** - * Set the enumeration values to an existing table. - * - * @param L the Lua state - * @param def the definition - * @param index the table index - */ - static void create(lua_State *L, const Def &def, int index); - - /** - * Create the enumeration table and set it to a table field. - * - * @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. - * - * The table is pushed in the following manner: - * - * t[i] = i - * t[j] = j - * - * Where i and j are enumeration values so user can check which flag is enabled by - * just testing the presence of the field. - * - * @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. Returns 0 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); - - /** - * Require a enumeration from Lua. Calls luaL_error if the value at the given index - * is not a table. - * - * @param L the Lua state - * @param index the value index - * @return the value - */ - static int require(lua_State *L, int index); -}; - -/* }}} */ - /** * Push a Lua userdata. * diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeClass.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeClass.cpp Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,104 @@ +/* + * LuaeClass.cpp -- Lua object management + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 "LuaeClass.h" + +const char *LuaeClass::FieldName = "__name"; +const char *LuaeClass::FieldParents = "__parents"; + +void LuaeClass::create(lua_State *L, const Def &def) +{ + luaL_newmetatable(L, def.name.c_str()); + + // Store the name of the class + lua_pushlstring(L, def.name.c_str(), def.name.length()); + lua_setfield(L, -2, FieldName); + + // Store the parents names + int i = 0; + + lua_createtable(L, 0, 0); + for (auto d(def.parent); d != nullptr; d = d->parent) { + lua_pushlstring(L, d->name.c_str(), d->name.length()); + lua_rawseti(L, -2, ++i); + } + lua_setfield(L, -2, FieldParents); + + // Metamethods + if (def.metamethods.size() > 0) { + for (auto m : def.metamethods) { + lua_pushcfunction(L, m.func); + lua_setfield(L, -2, m.name); + } + } + + // Methods + lua_createtable(L, 0, 0); + for (auto m : def.methods) { + lua_pushcfunction(L, m.func); + lua_setfield(L, -2, m.name); + } + + // Create the inheritance + if (def.parent != nullptr) { + luaL_newmetatable(L, def.parent->name.c_str()); + lua_setmetatable(L, -2); + } + lua_setfield(L, -2, "__index"); + + lua_pop(L, 1); +} + +void LuaeClass::testShared(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 cast"); + + // 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 cast"); + + 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); +} diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeClass.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeClass.h Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,154 @@ +/* + * LuaeClass.h -- Lua object management + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 _LUAE_CLASS_H_ +#define _LUAE_CLASS_H_ + +#include + +/** + * @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: + /** + * Methods for a class. + */ + using Methods = std::vector; + + /** + * Smart pointer for Luae objects. + */ + template + using Ptr = std::shared_ptr; + + /** + * @struct Def + * @brief Definition of a class + */ + struct Def { + std::string name; //!< metatable name + Methods methods; //!< methods + Methods metamethods; //!< metamethods + const Def *parent; //!< optional parent class + }; + + /** + * The field stored in the object metatable about the object metatable + * name. + */ + static const char *FieldName; + + /** + * The field that holds all parent classes. It is used to verify casts. + */ + 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) + { + 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); + } + + /** + * Check if the object at index is suitable for cast to meta. Calls + * luaL_error if not. + * + * @param L the Lua state + * @param index the value index + * @param meta the object name + */ + static void testShared(lua_State *L, int index, const char *meta); + + /** + * 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) + { + testShared(L, index, meta); + + return *static_cast *>(lua_touserdata(L, index)); + } + + /** + * Delete the shared pointer at the given index. This function does + * not check if the type is valid for performance reason. And because + * it's usually called in __gc, there is no reason to check. + * + * Also return 0 the __gc method can directly call + * return LuaeClass::deleteShared(...) + * + * @param L the Lua state + * @param index the index + * @return 0 for convenience + */ + template + static int deleteShared(lua_State *L, int index) + { + static_cast *>(lua_touserdata(L, index))->~shared_ptr(); + + return 0; + } +}; + +#endif // !_LUAE_CLASS_H_ diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeEnum.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeEnum.cpp Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,86 @@ + +#include "LuaeEnum.h" + +void LuaeEnum::create(lua_State *L, const Def &def) +{ + LUAE_STACK_CHECKBEGIN(L); + + lua_createtable(L, 0, 0); + + for (auto p : def) { + lua_pushinteger(L, p.second); + lua_setfield(L, -2, p.first); + } + + LUAE_STACK_CHECKEND(L, - 1); +} + +void LuaeEnum::create(lua_State *L, const Def &def, int index) +{ + LUAE_STACK_CHECKBEGIN(L); + + if (index < 0) + -- index; + + for (auto p : def) { + lua_pushinteger(L, p.second); + lua_setfield(L, index, p.first); + } + + LUAE_STACK_CHECKEQUALS(L); +} + +void LuaeEnum::create(lua_State *L, + const Def &def, + int index, + const std::string &name) +{ + LUAE_STACK_CHECKBEGIN(L); + + create(L, def); + + if (index < 0) + -- index; + + lua_setfield(L, index, name.c_str()); + + LUAE_STACK_CHECKEQUALS(L); +} + +void LuaeEnum::push(lua_State *L, const Def &def, int value) +{ + LUAE_STACK_CHECKBEGIN(L); + lua_createtable(L, 0, 0); + + for (auto p : def) { + if (value & p.second) { + lua_pushinteger(L, p.second); + lua_setfield(L, -2, p.first); + } + } + + LUAE_STACK_CHECKEND(L, - 1); +} + +int LuaeEnum::get(lua_State *L, int index) +{ + int value = 0; + + LUAE_STACK_CHECKBEGIN(L); + luaL_checktype(L, index, LUA_TTABLE); + + if (index < 0) + -- index; + + lua_pushnil(L); + while (lua_next(L, index)) { + if (lua_type(L, -1) == LUA_TNUMBER) + value |= lua_tointeger(L, -1); + + lua_pop(L, 1); + } + + LUAE_STACK_CHECKEQUALS(L); + + return value; +} diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeEnum.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeEnum.h Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,91 @@ +/* + * LuaeEnum.h -- Lua enumeration helpers + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 _LUAE_ENUM_H_ +#define _LUAE_ENUM_H_ + +#include + +/** + * @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; + + /** + * Bind the enumeration and keep it at the top of the stack. + * + * @param L the Lua state + * @param def the definition + */ + static void create(lua_State *L, const Def &def); + + /** + * Set the enumeration values to an existing table. + * + * @param L the Lua state + * @param def the definition + * @param index the table index + */ + static void create(lua_State *L, const Def &def, int index); + + /** + * Create the enumeration table and set it to a table field. + * + * @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); +}; + +#endif // !_LUAE_ENUM_H_ diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeState.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeState.cpp Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,28 @@ +#include "LuaeState.h" + +LuaeState::LuaeState() +{ + m_state = Ptr(luaL_newstate()); +} + +LuaeState::LuaeState(lua_State *L) +{ + m_state = Ptr(L); +} + +LuaeState::LuaeState(LuaeState &&state) +{ + m_state = std::move(state.m_state); +} + +LuaeState &LuaeState::operator=(LuaeState &&state) +{ + m_state = std::move(state.m_state); + + return *this; +} + +LuaeState::operator lua_State*() +{ + return m_state.get(); +} diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeState.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeState.h Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,91 @@ +/* + * LuaeState.h -- Lua RAII wrapper for lua_State + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 _LUAE_STATE_H_ +#define _LUAE_STATE_H_ + +#include + +#include + +/** + * @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; + + Ptr m_state; + +public: + /** + * Deleted copy constructor. + */ + LuaeState(const LuaeState &) = delete; + + /** + * Deleted copy assignment. + */ + 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*(); +}; + +#endif // !_LUAE_STATE_H_ diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeTable.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeTable.cpp Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,76 @@ +/* + * LuaeTable.cpp -- Lua table helpers + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 "LuaeTable.h" + +void LuaeTable::create(lua_State *L, int nrec, int narr) +{ + LUAE_STACK_CHECKBEGIN(L); + + lua_createtable(L, nrec, narr); + + LUAE_STACK_CHECKEND(L, - 1); +} + +int LuaeTable::type(lua_State *L, int idx, const std::string &name) +{ + int type; + + LUAE_STACK_CHECKBEGIN(L); + + lua_getfield(L, idx, name.c_str()); + type = lua_type(L, -1); + lua_pop(L, 1); + + LUAE_STACK_CHECKEQUALS(L); + + return type; +} + +void LuaeTable::read(lua_State *L, int idx, ReadFunction func) +{ + LUAE_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); + } + + LUAE_STACK_CHECKEQUALS(L); +} + +int LuaeTable::ref(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; +} diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeTable.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeTable.h Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,170 @@ +/* + * LuaeTable.h -- Lua table helpers + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 _LUAE_TABLE_H_ +#define _LUAE_TABLE_H_ + +#include + +/** + * @class LuaeTable + * @brief Some function for table manipulation + * + * Read, reference and get fields from tables. + */ +class LuaeTable { +public: + /** + * The map function for \ref read + */ + 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 narr 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. + * + * @param L the Lua state + * @param idx the table index + * @param name the field name + * @return the converted type. + */ + template + 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 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 + static void set(lua_State *L, int idx, const std::string &name, const T &value) + { + LUAE_STACK_CHECKBEGIN(L); + + Luae::push(L, value); + Luae::setfield(L, (idx < 0) ? --idx : idx, name); + + LUAE_STACK_CHECKEQUALS(L); + } + + /** + * Overload for string literals. + * + * @param L the Lua state + * @param idx the index + * @param name the field name + * @param s the string + */ + template + static void set(lua_State *L, int idx, const std::string &name, const char (&s)[N]) + { + set(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 + static T require(lua_State *L, int idx, const std::string &name) + { + LUAE_STACK_CHECKBEGIN(L); + + 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); + auto v = get(L, idx, name); + + LUAE_STACK_CHECKEQUALS(L); + + return v; + } + + /** + * 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. + * + * Do not pop anything within the function. + * + * @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); +}; + +#endif // !_LUAE_TABLE_H_ diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeVariant.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeVariant.cpp Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,92 @@ +/* + * LuaeVariant.cpp -- Lua variant helper + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 "LuaeVariant.h" + +LuaeVariant LuaeVariant::copy(lua_State *L, int index) +{ + LuaeVariant 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: + { + 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 LuaeVariant::push(lua_State *L, const LuaeVariant &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) { + push(L, p.first); + push(L, p.second); + + lua_settable(L, -3); + } + break; + } + default: + lua_pushnil(L); + break; + } +} + +LuaeVariant::LuaeVariant() + : type(LUA_TNIL) +{ +} + diff -r ca69910b1407 -r e01ee0c72c43 C++/LuaeVariant.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/LuaeVariant.h Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,66 @@ +/* + * LuaeVariant.h -- Lua variant helper + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 _LUAE_VARIANT_H_ +#define _LUAE_VARIANT_H_ + +#include + +/** + * @class LuaeVariant + * @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 LuaeVariant { +private: + union { + lua_Number number; + bool boolean; + }; + + int type; + std::string str; + std::vector> table; + +public: + /** + * Dump a value at the specific index. + * + * @param L the Lua state + * @param index the value + * @return a tree of values + */ + static LuaeVariant 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 LuaeVariant &value); + + /** + * Default constructor (type nil) + */ + LuaeVariant(); +}; + +#endif // !_LUAE_VARIANT_H_ diff -r ca69910b1407 -r e01ee0c72c43 C++/Tests/Luae/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Luae/CMakeLists.txt Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,41 @@ +# +# CMakeLists.txt -- tests for Luae classes +# +# Copyright (c) 2014 David Demelier +# +# 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. +# + +find_package(Lua52 REQUIRED) + +set( + LUAE_SOURCES + ${code_SOURCE_DIR}/C++/Luae.cpp + ${code_SOURCE_DIR}/C++/Luae.h + ${code_SOURCE_DIR}/C++/LuaeState.cpp + ${code_SOURCE_DIR}/C++/LuaeState.h + TestLuae.cpp + TestLuae.h +) + +define_test(luae "${LUAE_SOURCES}") +target_include_directories(luae PRIVATE ${LUA52_INCLUDE_DIR}) +target_link_libraries(luae ${LUA52_LIBRARIES}) + +add_custom_command( + TARGET luae + POST_BUILD + COMMENT "Copying Lua example files" + COMMAND + ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/scripts $ +) \ No newline at end of file diff -r ca69910b1407 -r e01ee0c72c43 C++/Tests/Luae/TestLuae.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Luae/TestLuae.cpp Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,418 @@ +/* + * TestLuae.cpp -- test the Luae class + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 + +#include +#include + +#include "TestLuae.h" + +/* -------------------------------------------------------- + * Custom type Point + * -------------------------------------------------------- */ + +struct Point { + int x; + int y; +}; + +template <> +struct Luae::TypeInfo : Luae::TypeCustom { + static void push(lua_State *L, const Point &point) + { + lua_createtable(L, 0, 0); + lua_pushinteger(L, point.x); + lua_setfield(L, -2, "x"); + lua_pushinteger(L, point.y); + lua_setfield(L, -2, "y"); + } + + static Point get(lua_State *L, int index) + { + Point p{0, 0}; + + if (lua_type(L, index) == LUA_TTABLE) { + lua_getfield(L, index, "x"); + p.x = luaL_optint(L, -1, 0); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + p.y = luaL_optint(L, -1, 0); + lua_pop(L, 1); + } + + return p; + } + + static Point check(lua_State *L, int index) + { + luaL_checktype(L, index, LUA_TTABLE); + + return get(L, index); + } +}; + +/* -------------------------------------------------------- + * Userdata type Object + * -------------------------------------------------------- */ + +struct Object { + std::string hello() + { + return "hello"; + } +}; + +template <> +struct Luae::TypeInfo : Luae::TypeUserdata { + static constexpr const char *name = "Object"; +}; + +int l_hello(lua_State *L) +{ + return Luae::push(L, Luae::check(L, 1)->hello()); +} + +const Luae::Reg methods { + { "hello", l_hello } +}; + +/* -------------------------------------------------------- + * Userdata as shared_ptr Widget + * -------------------------------------------------------- */ + +class Widget { +private: + int m_width; + int m_height; + std::string m_name; + +public: + Widget(int width, int height, const std::string &name) + : m_width(width) + , m_height(height) + , m_name(name) + { + } + + int width() const + { + return m_width; + } + + int height() const + { + return m_height; + } + + const std::string &name() const + { + return m_name; + } +}; + +using WidgetPtr = std::shared_ptr; + +template <> +struct Luae::TypeInfo : Luae::TypeUserdata { + static constexpr const char *name = "Widget"; +}; + +int l_width(lua_State *L) +{ + return Luae::push(L, Luae::check(L, 1)->width()); +} + +int l_height(lua_State *L) +{ + return Luae::push(L, Luae::check(L, 1)->height()); +} + +int l_name(lua_State *L) +{ + return Luae::push(L, Luae::check(L, 1)->name()); +} + +const Luae::Reg widgetMethods { + { "width", l_width }, + { "height", l_height }, + { "name", l_name } +}; + +/* -------------------------------------------------------- + * Push function tests + * -------------------------------------------------------- */ + +void TestLuae::pushPoint() +{ + LuaeState L; + Luae::openlibs(L); + + try { + Luae::dofile(L, "push.lua"); + Luae::getglobal(L, "pushPoint"); + Luae::push(L, Point{10, 20}); + Luae::pcall(L, 1, 0); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::pushObject() +{ + LuaeState L; + Luae::openlibs(L); + + // Object metatable + Luae::newmetatable(L, "Object"); + Luae::newlib(L, methods); + Luae::setfield(L, -2, "__index"); + Luae::pop(L); + + try { + Luae::dofile(L, "push.lua"); + Luae::getglobal(L, "pushObject"); + Luae::push(L, Object()); + Luae::pcall(L, 1, 0); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::pushShared() +{ + LuaeState L; + Luae::openlibs(L); + + // Widget metatable + Luae::newmetatable(L, "Widget"); + Luae::newlib(L, widgetMethods); + Luae::setfield(L, -2, "__index"); + Luae::pop(L); + + try { + Luae::dofile(L, "push.lua"); + Luae::getglobal(L, "pushShared"); + Luae::push(L, std::make_shared(100, 200, "Button")); + Luae::pcall(L, 1, 0); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::pushSharedTwice() +{ + LuaeState L; + Luae::openlibs(L); + + // Widget metatable + Luae::newmetatable(L, "Widget"); + Luae::newlib(L, widgetMethods); + Luae::setfield(L, -2, "__index"); + Luae::pop(L); + + try { + auto widget = std::make_shared(640, 480, "Screen"); + + Luae::dofile(L, "push.lua"); + Luae::getglobal(L, "pushSharedTwice"); + Luae::push(L, widget); + Luae::push(L, widget); + Luae::pcall(L, 2, 0); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +/* -------------------------------------------------------- + * Other functions + * -------------------------------------------------------- */ + +void TestLuae::preload() +{ + LuaeState L; + Luae::openlibs(L); + + Luae::preload(L, "luae", [] (lua_State *L) -> int { + lua_createtable(L, 0, 0); + lua_pushstring(L, "1.0"); + lua_setfield(L, -2, "version"); + lua_pushstring(L, "Luae"); + lua_setfield(L, -2, "name"); + + return 1; + }); + + try { + Luae::dofile(L, "loading.lua"); + Luae::getglobal(L, "test"); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::require() +{ + LuaeState L; + Luae::openlibs(L); + + Luae::require(L, "luae", [] (lua_State *L) -> int { + lua_createtable(L, 0, 0); + lua_pushstring(L, "1.0"); + lua_setfield(L, -2, "version"); + lua_pushstring(L, "Luae"); + lua_setfield(L, -2, "name"); + + return 1; + }); + + try { + Luae::dofile(L, "loading.lua"); + Luae::getglobal(L, "test"); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +/* -------------------------------------------------------- + * Standard types push / get / check + * -------------------------------------------------------- */ + +void TestLuae::testbool() +{ + LuaeState L; + Luae::openlibs(L); + + try { + Luae::dofile(L, "standard.lua"); + Luae::getglobal(L, "testbool"); + Luae::push(L, true); + Luae::pcall(L, 1, 1); + CPPUNIT_ASSERT_EQUAL(Luae::check(L, 1), false); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::testdouble() +{ + LuaeState L; + Luae::openlibs(L); + + try { + Luae::dofile(L, "standard.lua"); + Luae::getglobal(L, "testdouble"); + Luae::push(L, 10.0); + Luae::pcall(L, 1, 1); + CPPUNIT_ASSERT_EQUAL(Luae::check(L, 1), -10.0); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::testint() +{ + LuaeState L; + Luae::openlibs(L); + + try { + Luae::dofile(L, "standard.lua"); + Luae::getglobal(L, "testint"); + Luae::push(L, 123); + Luae::pcall(L, 1, 1); + CPPUNIT_ASSERT_EQUAL(Luae::check(L, 1), -123); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::testlong() +{ + LuaeState L; + Luae::openlibs(L); + + try { + Luae::dofile(L, "standard.lua"); + Luae::getglobal(L, "testlong"); + Luae::push(L, 9999L); + Luae::pcall(L, 1, 1); + CPPUNIT_ASSERT_EQUAL(Luae::check(L, 1), -9999L); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::testnil() +{ + LuaeState L; + Luae::openlibs(L); + + try { + Luae::dofile(L, "standard.lua"); + Luae::getglobal(L, "testnil"); + Luae::push(L, nullptr); + Luae::pcall(L, 1, 1); + CPPUNIT_ASSERT_EQUAL(Luae::type(L, -1), LUA_TNIL); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::teststring() +{ + LuaeState L; + Luae::openlibs(L); + + try { + Luae::dofile(L, "standard.lua"); + Luae::getglobal(L, "teststring"); + Luae::push(L, "Hello"); + Luae::pcall(L, 1, 1); + CPPUNIT_ASSERT_EQUAL(Luae::check(L, 1), std::string("olleH")); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +void TestLuae::testustring() +{ + LuaeState L; + Luae::openlibs(L); + + try { + std::u32string arg{'H', 'e', 'l', 'l', 'o'}; + std::u32string expected{'o', 'l', 'l', 'e', 'H'}; + + Luae::dofile(L, "standard.lua"); + Luae::getglobal(L, "testustring"); + Luae::push(L, arg); + Luae::pcall(L, 1, 1); + CPPUNIT_ASSERT_MESSAGE("u32string failure", Luae::check(L, 1) == expected); + } catch (const std::runtime_error &error) { + CPPUNIT_ASSERT_MESSAGE("unexpected exception: " + std::string(error.what()), false); + } +} + +int main() +{ + CppUnit::TextTestRunner runnerText; + + runnerText.addTest(TestLuae::suite()); + + return runnerText.run("", false) == 1 ? 0 : 1; +} \ No newline at end of file diff -r ca69910b1407 -r e01ee0c72c43 C++/Tests/Luae/TestLuae.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Luae/TestLuae.h Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,59 @@ +/* + * TestLuae.h -- test the Luae class + * + * Copyright (c) 2013, 2014 David Demelier + * + * 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 _TEST_LUAE_H_ +#define _TEST_LUAE_H_ + +#include +#include + +class TestLuae : public CppUnit::TestFixture { +private: + CPPUNIT_TEST_SUITE(TestLuae); + CPPUNIT_TEST(pushPoint); + CPPUNIT_TEST(pushObject); + CPPUNIT_TEST(pushShared); + CPPUNIT_TEST(pushSharedTwice); + CPPUNIT_TEST(preload); + CPPUNIT_TEST(require); + CPPUNIT_TEST(testnil); + CPPUNIT_TEST(testbool); + CPPUNIT_TEST(testint); + CPPUNIT_TEST(testlong); + CPPUNIT_TEST(testdouble); + CPPUNIT_TEST(teststring); + CPPUNIT_TEST(testustring); + CPPUNIT_TEST_SUITE_END(); + +public: + void pushPoint(); + void pushObject(); + void pushShared(); + void pushSharedTwice(); + void preload(); + void require(); + void testnil(); + void testbool(); + void testint(); + void testlong(); + void testdouble(); + void teststring(); + void testustring(); +}; + +#endif // !_TEST_LUAE_H_ \ No newline at end of file diff -r ca69910b1407 -r e01ee0c72c43 C++/Tests/Luae/scripts/loading.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Luae/scripts/loading.lua Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,10 @@ +-- +-- loading.lua -- preload and require functions +-- + +function test() + local luae = require "luae" + + assert(luae.version == "1.0") + assert(luae.name == "Luae") +end \ No newline at end of file diff -r ca69910b1407 -r e01ee0c72c43 C++/Tests/Luae/scripts/push.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Luae/scripts/push.lua Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,22 @@ +-- +-- push.lua -- test for push functions +-- + +function pushPoint(point) + assert(point.x == 10) + assert(point.y == 20) +end + +function pushObject(object) + assert(object:hello() == "hello") +end + +function pushShared(widget) + assert(widget:width() == 100) + assert(widget:height() == 200) + assert(widget:name() == "Button") +end + +function pushSharedTwice(o1, o2) + assert(o1 == o2) +end diff -r ca69910b1407 -r e01ee0c72c43 C++/Tests/Luae/scripts/standard.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/Tests/Luae/scripts/standard.lua Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,55 @@ +-- +-- standard.lua -- standard types push / get / check +-- + +function testbool(v) + assert(v == true) + + return false +end + +function testdouble(v) + assert(v == 10.0) + + return -10.0 +end + +function testint(v) + assert(v == 123) + + return -123 +end + +function testlong(v) + assert(v == 9999) + + return -9999 +end + +function testnil(v) + assert(type(v) == "nil") + + return nil +end + +function teststring(v) + assert(v == "Hello") + + return "olleH" +end + +function testustring(v) + assert(v[1] == string.byte('H')) + assert(v[2] == string.byte('e')) + assert(v[3] == string.byte('l')) + assert(v[4] == string.byte('l')) + assert(v[5] == string.byte('o')) + + return { + string.byte('o'), + string.byte('l'), + string.byte('l'), + string.byte('e'), + string.byte('H') + } +end \ No newline at end of file diff -r ca69910b1407 -r e01ee0c72c43 CMakeLists.txt --- a/CMakeLists.txt Fri May 09 09:15:52 2014 +0200 +++ b/CMakeLists.txt Fri May 09 17:12:25 2014 +0200 @@ -75,10 +75,14 @@ add_subdirectory(C++/Tests/Hash) endif () +if (WITH_LUAE) + add_subdirectory(C++/Tests/Luae) +endif () + if (WITH_PACK) add_subdirectory(C++/Tests/Pack) endif () if (WITH_PARSER) add_subdirectory(C++/Tests/Parser) -endif () +endif () \ No newline at end of file diff -r ca69910b1407 -r e01ee0c72c43 cmake/FindLua52.cmake --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmake/FindLua52.cmake Fri May 09 17:12:25 2014 +0200 @@ -0,0 +1,80 @@ +# Locate Lua library +# This module defines +# LUA52_FOUND, if false, do not try to link to Lua +# LUA52_LIBRARIES +# LUA52_INCLUDE_DIR, where to find lua.h +# LUA52_VERSION_STRING, the version of Lua found (since CMake 2.8.8) +# +# Note that the expected include convention is +# #include "lua.h" +# and not +# #include +# This is because, the lua location is not standardized and may exist +# in locations other than lua/ + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +find_path(LUA52_INCLUDE_DIR lua.h + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES include/lua52 include/lua5.2 include/lua include + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +find_library(LUA52_LIBRARY + NAMES lua52 lua5.2 lua-5.2 lua + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES lib64 lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw + /opt/local + /opt/csw + /opt +) + +if (LUA52_LIBRARY) + # include the math library for Unix + if (UNIX AND NOT APPLE) + FIND_LIBRARY(LUA52_MATH_LIBRARY m) + SET(LUA52_LIBRARIES "${LUA52_LIBRARY};${LUA52_MATH_LIBRARY}" CACHE STRING "Lua Libraries") + # For Windows and Mac, don't need to explicitly include the math library + else (UNIX AND NOT APPLE) + SET(LUA52_LIBRARIES "${LUA52_LIBRARY}" CACHE STRING "Lua Libraries") + endif (UNIX AND NOT APPLE) +endif () + +if (LUA52_INCLUDE_DIR AND EXISTS "${LUA52_INCLUDE_DIR}/lua.h") + file(STRINGS "${LUA52_INCLUDE_DIR}/lua.h" lua52_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") + + string(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA52_VERSION_STRING "${lua52_version_str}") + unset(lua52_version_str) +endif() + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUA52_FOUND to TRUE if +# all listed variables are TRUE +find_package_handle_standard_args(Lua52 + REQUIRED_VARS LUA52_LIBRARIES LUA52_INCLUDE_DIR + VERSION_VAR LUA52_VERSION_STRING) + +mark_as_advanced(LUA52_INCLUDE_DIR LUA52_LIBRARIES LUA52_LIBRARY LUA52_MATH_LIBRARY)