# HG changeset patch # User David Demelier # Date 1399582548 -7200 # Node ID 8fc177bbc4a6c6c7b0bb9b0ae2242a4408402c3a # Parent 9324b9e0e7b75e1409e630d59a0fd968aa273c7d Update some code diff -r 9324b9e0e7b7 -r 8fc177bbc4a6 C++/DriverPostgres.cpp --- a/C++/DriverPostgres.cpp Thu May 08 18:57:47 2014 +0200 +++ b/C++/DriverPostgres.cpp Thu May 08 22:55:48 2014 +0200 @@ -16,28 +16,17 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include #include #include #include +#include + #include "DriverPostgres.h" namespace { -struct PGConnDeleter { - void operator()(PGconn *conn) - { - PQfinish(conn); - } -}; - -struct PGQueryDeleter { - void operator()(PGresult *result) - { - PQclear(result); - } -}; - using PostgresResult = std::shared_ptr; using PostgresConn = std::shared_ptr; @@ -225,7 +214,6 @@ if (info != nullptr && (status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK)) { - puts("SUCESS"); timestamp = atoi(PQgetvalue(info, 0, 0)); PQclear(info); } @@ -428,7 +416,7 @@ throw std::runtime_error(error); } - m_connection = std::shared_ptr(conn, PGConnDeleter()); + m_connection = std::shared_ptr(conn, PQfinish); } DriverRequest DriverPostgresImpl::prepare(const std::string &command) @@ -454,7 +442,7 @@ throw std::runtime_error(error); } - auto result = std::shared_ptr(info, PGQueryDeleter()); + auto result = std::shared_ptr(info, PQclear); auto impl = std::make_shared(m_connection, result); return DriverQuery(impl); diff -r 9324b9e0e7b7 -r 8fc177bbc4a6 C++/DriverPostgres.h --- a/C++/DriverPostgres.h Thu May 08 18:57:47 2014 +0200 +++ b/C++/DriverPostgres.h Thu May 08 22:55:48 2014 +0200 @@ -23,10 +23,6 @@ #ifndef _DRIVER_PG_H_ #define _DRIVER_PG_H_ -#include - -#include - #include "Driver.h" class DriverPostgres : public Driver { diff -r 9324b9e0e7b7 -r 8fc177bbc4a6 C++/Luae.cpp --- a/C++/Luae.cpp Thu May 08 18:57:47 2014 +0200 +++ b/C++/Luae.cpp Thu May 08 22:55:48 2014 +0200 @@ -1,7 +1,7 @@ /* * Luae.cpp -- Lua helpers and such * - * Copyright (c) 2013 David Demelier + * 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 @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + #include "Luae.h" /* -------------------------------------------------------- @@ -93,8 +95,6 @@ break; case LUA_TTABLE: { - LuaeValue k; - if (index < 0) -- index; @@ -262,10 +262,82 @@ 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, @@ -273,11 +345,7 @@ { LUAE_STACK_CHECKBEGIN(L); - lua_createtable(L, 0, def.size()); - for (auto p : def) { - lua_pushinteger(L, p.second); - lua_setfield(L, -2, p.first.c_str()); - } + create(L, def); if (index < 0) -- index; @@ -295,7 +363,7 @@ for (auto p : def) { if (value & p.second) { lua_pushinteger(L, p.second); - lua_setfield(L, -2, p.first.c_str()); + lua_rawseti(L, -2, p.second); } } @@ -307,18 +375,33 @@ int value = 0; LUAE_STACK_CHECKBEGIN(L); - luaL_checktype(L, index, LUA_TTABLE); + + if (lua_type(L, index) == LUA_TTABLE) { + if (index < 0) + -- index; - 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); + } + } - lua_pushnil(L); - while (lua_next(L, index)) { - if (lua_type(L, -1) == LUA_TNUMBER) - value |= lua_tointeger(L, -1); + LUAE_STACK_CHECKEQUALS(L); + + return value; +} - lua_pop(L, 1); - } +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); diff -r 9324b9e0e7b7 -r 8fc177bbc4a6 C++/Luae.h --- a/C++/Luae.h Thu May 08 18:57:47 2014 +0200 +++ b/C++/Luae.h Thu May 08 22:55:48 2014 +0200 @@ -1,7 +1,7 @@ /* * Luae.h -- Lua helpers and such * - * Copyright (c) 2013 David Demelier + * 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 @@ -16,8 +16,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifndef _LUA_H_ -#define _LUA_H_ +#ifndef _LUAE_H_ +#define _LUAE_H_ + +/** + * @file Luae.h + * @brief Lua C++ extended API + */ #include #include @@ -33,463 +38,63 @@ #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 -/* {{{ Luae */ - -/** - * @class Luae - * @brief Add lot of convenience for Lua - * - * This class adds lot of functions for Lua and C++. - */ -class Luae { -private: - /* - * Wrapper for dofile and dostring. - */ - static void doexecute(lua_State *L, int status); - -public: - /** - * @struct Convert - * @brief Push or get values - */ - template - struct Convert { - static const bool supported = false; - }; - - /** - * @struct Iterator - * @brief Wrap STL containers - */ - template - struct Iterator { - Type begin; - Type end; - Type current; - - Iterator(Type begin, Type end) - : begin(begin) - , end(end) - , current(begin) - { - } - }; - - /** - * Preload a library, it will be added to package.preload so the - * user can successfully call require "name". In order to work, you need - * to open luaopen_package and luaopen_base first. - * - * @param L the Lua state - * @param name the module name - * @param func the opening library - * @see require - */ - static void preload(lua_State *L, - const std::string &name, - lua_CFunction func); - - /** - * Get a global value from Lua. - * - * @param L the Lua state - * @param name the value name - */ - static inline void getglobal(lua_State *L, const std::string &name) - { - lua_getglobal(L, name.c_str()); - } - - /** - * Load all Lua libraries. - * - * @param L the Lua state - */ - static inline void openlibs(lua_State *L) - { - luaL_openlibs(L); - } - - /** - * Set a global. - * - * @param L the Lua state - * @param name the name - */ - static inline void setglobal(lua_State *L, const std::string &name) - { - lua_setglobal(L, name.c_str()); - } - - /** - * Push a function with an optional number of upvalues. - * - * @param L the Lua state - * @param func the function - * @param nup the number of up values - */ - static inline void pushfunction(lua_State *L, lua_CFunction func, int nup = 0) - { - lua_pushcclosure(L, func, nup); - } - - /** - * Move the top element at the given index. - * - * @param L the Lua state - * @param index the new index - */ - static inline void insert(lua_State *L, int index) - { - lua_insert(L, index); - } - - /** - * Replace the element at the given index by the one at the top. - * - * @param L the Lua state - * @param index the new index - */ - static inline void replace(lua_State *L, int index) - { - lua_replace(L, index); - } - - /** - * Remove the element at the given index. - * - * @param L the Lua state - * @param index the table index - */ - static inline void remove(lua_State *L, int index) - { - lua_remove(L, index); - } - - /** - * Wrapper around pcall, throw instead of returning an error code. - * - * @param L the Lua state - * @param np the number of parameters - * @param nr the number of return value - * @param error the message handler - */ - static inline void pcall(lua_State *L, int np, int nr, int error = 0) - { - if (lua_pcall(L, np, nr, error) != LUA_OK) { - auto error = lua_tostring(L, -1); - lua_pop(L, 1); - - throw std::runtime_error(error); - } - } - - /** - * Create a unique reference to the table at the given index. - * - * @param L the Lua state - * @param index the table index - * @return the reference - */ - static inline int ref(lua_State *L, int index) - { - return luaL_ref(L, index); - } - - /** - * Load a library just like it was loaded with require. - * - * @param L the Lua state - * @param name the module name - * @param func the function - * @param global store as global - */ - static void require(lua_State *L, - const std::string &name, - lua_CFunction func, - bool global); - - /** - * Load and execute a file. - * - * @param L the Lua state - * @param path the the path - */ - static inline void dofile(lua_State *L, const std::string &path) - { - doexecute(L, luaL_dofile(L, path.c_str())); - } - - static inline void dostring(lua_State *L, const std::string &data) - { - doexecute(L, luaL_dostring(L, data.c_str())); - } - - /** - * Get the type at the given index. - * - * @param L the Lua state - * @param index the value index - * @return the type - */ - static inline int type(lua_State *L, int index) - { - return lua_type(L, index); - } - - /** - * Push a value to Lua. - * - * @param L the Lua state - * @param value the value - */ - template - static void push(lua_State *L, const T &value) - { - static_assert(Convert::supported, "type not supported"); - - Convert::push(L, value); - } - - /** - * Overload for string literals. - * - * @param L the Lua state - * @param s the string - */ - template - static void push(lua_State *L, const char (&s)[N]) - { - push(L, s); - } - - /** - * Check the type at the given index. Calls luaL_error on bad - * type. - * - * @param L the Lua state - * @param index the the index - * @param type the type to check - */ - static inline void checktype(lua_State *L, int index, int type) - { - luaL_checktype(L, index, type); - } - - /** - * Get a value from Lua. The type are not checked - * - * @param L the Lua state - * @param index the value index - * @return the value - */ - template - static T get(lua_State *L, int index) - { - static_assert(Convert::supported, "type not supported"); - - return Convert::get(L, index); - } - - /** - * Get the value at the index, raise a Lua error on failures. - * - * @param L the Lua state - * @param index the value index - * @return the value - */ - template - static T check(lua_State *L, int index) - { - return Convert::check(L, index); - } - - /** - * 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) - { - using ValueType = typename Container::value_type; - using IteratorType = typename Container::const_iterator; - - LUAE_STACK_CHECKBEGIN(L); - - auto it = 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)); - - if (it->current == it->end) - return 0; - - Converter::push(L, *(it->current++)); - - return 1; - }, 1); - - LUAE_STACK_CHECKEND(L, -1); - - return 1; - } - - /** - * Convert a new placement made object, without testing if its a real - * object. - * - * @param L the Lua state - * @param idx the object index - * @return the converted object - */ - template - static T toType(lua_State *L, int idx) - { - return reinterpret_cast(lua_touserdata(L, idx)); - } -}; - -template <> -struct Luae::Convert { - static const bool supported = true; - - static void push(lua_State *L, const bool &value) - { - lua_pushboolean(L, value); - } - - static bool get(lua_State *L, int index) - { - return lua_toboolean(L, index); - } - - static bool check(lua_State *L, int index) - { - return lua_toboolean(L, index); - } -}; - -template <> -struct Luae::Convert { - static const bool supported = true; - - static void push(lua_State *L, const int &value) - { - lua_pushinteger(L, value); - } - - static int get(lua_State *L, int index) - { - return lua_tointeger(L, index); - } - - static int check(lua_State *L, int index) - { - return luaL_checkinteger(L, index); - } -}; - -template <> -struct Luae::Convert { - static const bool supported = true; - - static void push(lua_State *L, const double &value) - { - lua_pushnumber(L, value); - } - - static double get(lua_State *L, int index) - { - return lua_tonumber(L, index); - } - - static double check(lua_State *L, int index) - { - return luaL_checknumber(L, index); - } -}; - -template <> -struct Luae::Convert { - static const bool supported = true; - - static void push(lua_State *L, const std::string &value) - { - lua_pushlstring(L, value.c_str(), value.length()); - } - - static std::string get(lua_State *L, int index) - { - return lua_tostring(L, index); - } - - static std::string check(lua_State *L, int index) - { - return luaL_checkstring(L, index); - } -}; - -template <> -struct Luae::Convert { - static const bool supported = true; - - static void push(lua_State *L, const char *value) - { - lua_pushstring(L, value); - } - - static const char *get(lua_State *L, int index) - { - return lua_tostring(L, index); - } - - static const char *check(lua_State *L, int index) - { - return luaL_checkstring(L, index); - } -}; - -/* }}} */ - /* {{{ LuaeState */ /** @@ -515,13 +120,20 @@ void initRegistry(); public: - /* - * FieldRefs: The field stored into the registry to avoid - * recreation of shared objects. + /** + * 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; /** @@ -560,6 +172,148 @@ /* }}} */ +/* {{{ 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 */ /** @@ -606,160 +360,1519 @@ /* }}} */ -/* {{{ LuaeClass */ +/* {{{ Luae */ /** - * @class LuaeClass - * @brief Support for object oriented programming between C++ and Lua + * @class Luae + * @brief Add lot of convenience for Lua * - * This class provides functions for passing and retrieving objects from C++ and - * Lua. + * This class adds lot of functions for Lua and C++. */ -class LuaeClass { +class Luae { +private: + /* + * Wrapper for dofile and dostring. + */ + static void doexecute(lua_State *L, int status); + public: - using Methods = std::vector; + /** + * 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 + }; + + /** + * @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) + { + } + }; + + /** + * 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 + * + * The following example code allows the class Object to be pushed + * and get as Lua userdata. + * + * @code + * struct Object { }; + * + * template <> + * struct Luae::IsUserdata : std::true_type { + * static const char *MetatableName; + * }; + * + * const char *Luae::IsUserdata::MetatableName = "Object"; + * + * int l_push(lua_State *L) + * { + * Luae::push(L, Object()); + * } + * + * int l_get(lua_State *L) + * { + * Object *o = Luae::get(L, 1); + * } + * @endcode + * @note You don't need to add the pointer type to the get template parameter + * + * ## Custom object + * + * 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. + * + * @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; + * + * static void push(lua_State *L, const Point &p) + * { + * lua_createtable(L, 0, 0); + * lua_pushinteger(L, p.x); + * lua_setfield(L, -2, "x"); + * lua_pushinteger(L, p.y); + * lua_setfield(L, -2, "y"); + * } + * + * static Point get(lua_State *L, int index) + * { + * Point p; + * + * if (lua_type(L, index) == LUA_TTABLE) { + * lua_getfield(L, index, "x"); + * p.x = lua_tonumber(L, -1); + * lua_pop(L, 1); + * lua_getfield(L, index, "y"); + * p.y = lua_tonumber(L, -1); + * lua_pop(L, 1); + * } + * + * return p; + * } + * + * static Point check(lua_State *L, int index) + * { + * // Do your check + * + * return get(L, index); + * } + * }; + * + * int l_push(lua_State *L) + * { + * Luae::push(L, Point {1, 2}); + * } + * + * int l_get(lua_State *L) + * { + * Point p = Luae::get(L, 1); + * } + * @endcode + * @note Here you get a T and not a T * + */ + template + struct IsUserdata : std::false_type { }; + +private: + template + struct IsSharedUserdata : std::false_type { }; template - using Ptr = std::shared_ptr; - - struct Def { - std::string name; //! metatable name - Methods methods; //! methods - Methods metamethods; //! metamethods - const Def *parent; //! optional parent class + struct IsSharedUserdata> { + static const bool value = IsUserdata::value; }; - /* - * FieldName: The field stored in the object metatable about - * the object metatable name. +public: + /* ------------------------------------------------- + * Standard Lua API wrappers + * ------------------------------------------------- */ + + /** + * Calls a Lua function in non-protected mode. + * + * @param L the Lua state + * @param np the number of parameters + * @param nr the number of return values + */ + static inline void call(lua_State *L, int np = 0, int nr = 0) + { + lua_call(L, np, nr); + } + + /** + * Ensure that there are at least extra free stack slots in the stack. + * + * @param L the Lua state + * @param extra the extra data + * @return true if possible + */ + static inline int checkstack(lua_State *L, int extra) + { + return lua_checkstack(L, extra); + } + + /** + * Check the type at the given index. Calls luaL_error on bad + * type. + * + * @param L the Lua state + * @param index the the index + * @param type the type to check + */ + static inline void checktype(lua_State *L, int index, int type) + { + luaL_checktype(L, index, type); + } + + /** + * Compares two Lua values. + * + * Operation is one of: * - * FieldParents: The field that holds all parent classes. It is - * used to verify casts. + * * LUA_OPEQ, + * * LUA_OPLT, + * * LUA_OPLE + * + * @param L the Lua state + * @param index1 the first value + * @param index2 the second value + * @param op the operation + * @return true if index1 statisfies op compared to index2 + */ + static inline bool compare(lua_State *L, int index1, int index2, int op) + { + return lua_compare(L, index1, index2, op) == 1; + } + + /** + * Concatenate the n values at the top of the stack and pops them. + * Leaves the result at the top of the stack. + * + * @param L the Lua state + * @param n the number to concat */ - static const char *FieldName; - static const char *FieldParents; + static inline void concat(lua_State *L, int n) + { + lua_concat(L, n); + } + + /** + * Moves the element at the index from into the valid index to. + * + * @param L the Lua state + * @param from the from index + * @param to the destination index + */ + static inline void copy(lua_State *L, int from, int to) + { + lua_copy(L, from, to); + } + + /** + * Load and execute a file. + * + * @param L the Lua state + * @param path the the path + * @throw std::runtime_error on error + */ + static inline void dofile(lua_State *L, const std::string &path) + { + doexecute(L, luaL_dofile(L, path.c_str())); + } /** - * Initialize a new object. + * Load and execute a string. + * + * @param L the Lua state + * @param data the data + * @throw std::runtime_error on error + */ + static inline void dostring(lua_State *L, const std::string &data) + { + doexecute(L, luaL_dostring(L, data.c_str())); + } + + /** + * Generate an error with the string at the top of the stack. + * + * @param L the Lua state + * @return nothing + */ + static inline int error(lua_State *L) + { + return lua_error(L); + } + + /** + * Raises a Lua error, thus calling longjmp. + * + * @param L the Lua state + * @param fmt the format + * @param args the arguments + * @return nothing + */ + template + static inline int error(lua_State *L, const char *fmt, Args&&... args) + { + return luaL_error(L, fmt, std::forward(args)...); + } + + /** + * Controls the garbage collector. + * + * @param L the Lua state + * @param what the action + * @param data optional GC data + */ + static inline int gc(lua_State *L, int what, int data = 0) + { + return lua_gc(L, what, data); + } + + /** + * Get a field at the given index. + * + * @param L the Lua state + * @param idx the table index + * @param name the field name + */ + static inline void getfield(lua_State *L, int idx, const std::string &name) + { + lua_getfield(L, idx, name.c_str()); + } + + /** + * Get a global value from Lua. + * + * @param L the Lua state + * @param name the value name + */ + static inline void getglobal(lua_State *L, const std::string &name) + { + lua_getglobal(L, name.c_str()); + } + + /** + * Get the metatable of the value at the given index. Returns false + * if does not have a metatable and pushes nothing. + * + * @param L the Lua state + * @param index the value index + */ + static inline bool getmetatable(lua_State *L, int index) + { + return lua_getmetatable(L, index) == 1; + } + + /** + * Set the value at the given index. Top value is the value to assign + * key is just below the value. + * + * @param L the Lua state + * @param index the value index + */ + static inline void gettable(lua_State *L, int index) + { + lua_gettable(L, index); + } + + /** + * Get the current stack size. + * + * @param L the Lua state + * @return the stack size + */ + static inline int gettop(lua_State *L) + { + return lua_gettop(L); + } + + /** + * Pushes the Lua value associated with the userdata. * * @param L the Lua state - * @param def the definition + * @param index the value index + */ + static inline void getuservalue(lua_State *L, int index) + { + lua_getuservalue(L, index); + } + + /** + * Move the top element at the given index. + * + * @param L the Lua state + * @param index the new index + */ + static inline void insert(lua_State *L, int index) + { + lua_insert(L, index); + } + + /** + * Push the result of the operator '#' from the value at the given + * index. + * + * @param L the Lua state + * @param index the value index + */ + static inline void len(lua_State *L, int index) + { + lua_len(L, index); + } + + /** + * Create or get a metatable in the registry. + * + * @param L the Lua state + * @param name the metatable name */ - static void create(lua_State *L, const Def &def); + static inline void newmetatable(lua_State *L, const std::string &name) + { + luaL_newmetatable(L, name.c_str()); + } + + /** + * Create a new table and fill it with functions. + * + * @param L the Lua state + * @param functions the functions + */ + static inline void newlib(lua_State *L, const luaL_Reg *functions) + { + lua_createtable(L, 0, 0); + luaL_setfuncs(L, functions, 0); + } + + /** + * Create a new table and fill it with functions. + * + * @param L the Lua state + * @param functions the functions + */ + static inline void newlib(lua_State *L, const Reg &functions) + { + lua_createtable(L, 0, 0); + for (auto &p : functions) { + lua_pushcfunction(L, p.second); + lua_setfield(L, -2, p.first); + } + } + + /** + * Pops a key from the stack and pushes a key-value pair from the table + * at the given index. + * + * @param L the Lua state + * @param index the table index + * @return true if there are still elements + */ + static inline bool next(lua_State *L, int index) + { + return lua_next(L, index) == 1; + } /** - * 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: + * Load all Lua libraries. + * + * @param L the Lua state + */ + static inline void openlibs(lua_State *L) + { + luaL_openlibs(L); + } + + /** + * Wrapper around pcall, throw instead of returning an error code. + * + * @param L the Lua state + * @param np the number of parameters + * @param nr the number of return value + * @param error the message handler + */ + static inline void pcall(lua_State *L, int np, int nr, int error = 0) + { + if (lua_pcall(L, np, nr, error) != LUA_OK) { + auto error = lua_tostring(L, -1); + lua_pop(L, 1); + + throw std::runtime_error(error); + } + } + + /** + * Pop arguments from the stack. + * + * @param L the Lua state + * @param count the number of values to pop + */ + static inline void pop(lua_State *L, int count = 1) + { + lua_pop(L, count); + } + + /** + * Pushes a copy of the value at the given index. * - * 1. The user can use the userdata as table key - * 2. A performance gain thanks to less allocations + * @param L the Lua state + * @param index the value to copy + */ + static inline void pushvalue(lua_State *L, int index) + { + lua_pushvalue(L, index); + } + + /** + * Returns true if the values are primitively equal. + * + * @param L the Lua state + * @param index1 the first value + * @param index2 the second value + * @return true if they equals + */ + static inline bool rawequal(lua_State *L, int index1, int index2) + { + return lua_rawequal(L, index1, index2) == 1; + } + + /** + * Like gettable but with raw access. + * + * @param L the Lua state + * @param index the value index + */ + static inline void rawget(lua_State *L, int index) + { + lua_rawget(L, index); + } + + /** + * Like gettable but with raw access. + * + * @param L the Lua state + * @param index the value index + * @param n the nt + */ + static inline void rawget(lua_State *L, int index, int n) + { + lua_rawgeti(L, index, n); + } + + /** + * Like rawgeti but with pointer. + * + * @param L the Lua state + * @param index the value index + * @param p the pointer key + */ + static inline void rawget(lua_State *L, int index, const void *p) + { + lua_rawgetp(L, index, p); + } + + /** + * Get the value length. * * @param L the Lua state - * @param o the object to push - * @param name the object metatable name + * @param index the value index + * @return the raw length + */ + static inline int rawlen(lua_State *L, int index) + { + return lua_rawlen(L, index); + } + + /** + * Similar to settable but without raw assignment. + * + * @param L the Lua state + * @param index the value index + */ + static inline void rawset(lua_State *L, int index) + { + lua_rawset(L, index); + } + + /** + * Set the value at the top of stack to the ntn value at the value + * at the given index. + * + * @param L the Lua state + * @param index the value index + * @param n the nth index */ - template - static void pushShared(lua_State *L, Ptr o, const std::string &name) + static inline void rawset(lua_State *L, int index, int n) + { + lua_rawseti(L, index, n); + } + + /** + * Like rawseti with a void pointer as the key. + * + * @param L the Lua state + * @param index the value index + * @param ptr the pointer key + */ + static inline void rawset(lua_State *L, int index, const void *ptr) { - LUAE_STACK_CHECKBEGIN(L); + lua_rawsetp(L, index, ptr); + } + + /** + * Push a formatted string like lua_pushfstring. Warning, it is not type + * safe and you should for instance not pass std::String to %s. + * + * @param L the Lua state + * @param fmt the format + * @param args the arguments + * @return the formatted string + */ + template + static inline const char *pushfstring(lua_State *L, const char *fmt, Args&&... args) + { + return lua_pushfstring(L, fmt, std::forward(args)...); + } + + /** + * Push a function with an optional number of upvalues. + * + * @param L the Lua state + * @param func the function + * @param nup the number of up values + */ + static inline void pushfunction(lua_State *L, lua_CFunction func, int nup = 0) + { + lua_pushcclosure(L, func, nup); + } - lua_getfield(L, LUA_REGISTRYINDEX, LuaeState::FieldRefs); - assert(lua_type(L, -1) == LUA_TTABLE); + /** + * Create a unique reference to the table at the given index. + * + * @param L the Lua state + * @param index the table index + * @return the reference + */ + static inline int ref(lua_State *L, int index) + { + return luaL_ref(L, index); + } - lua_rawgetp(L, -1, o.get()); + /** + * Remove the element at the given index. + * + * @param L the Lua state + * @param index the table index + */ + static inline void remove(lua_State *L, int index) + { + lua_remove(L, index); + } + + /** + * Replace the element at the given index by the one at the top. + * + * @param L the Lua state + * @param index the new index + */ + static inline void replace(lua_State *L, int index) + { + lua_replace(L, index); + } - if (lua_type(L, -1) == LUA_TNIL) { - lua_pop(L, 1); + /** + * Set a field to the table at the given index. + * + * @param L the Lua state + * @param idx the table index + * @param name the field name + * @see set + */ + static inline void setfield(lua_State *L, int idx, const std::string &name) + { + lua_setfield(L, idx, name.c_str()); + } - new (L, name.c_str()) std::shared_ptr(o); - - lua_pushvalue(L, -1); - lua_rawsetp(L, -3, o.get()); + /** + * Set the functions to the table at the top of stack. + * + * @param L the Lua state + * @param functions the functions + * @param nup the number of upvalues + */ + static inline void setfuncs(lua_State *L, const luaL_Reg *functions, int nup = 0) + { + luaL_setfuncs(L, functions, nup); + } + + /** + * Set the functions to the table at the top of stack. + * + * @param L the Lua state + * @param functions the functions + * @param nup the number of upvalues + */ + static inline void setfuncs(lua_State *L, const Reg &functions, int nup = 0) + { + luaL_checkversion(L); + luaL_checkstack(L, nup, "too many upvalues"); + + for (auto &l : functions) { + for (int i = 0; i < nup; i++) + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l.second, nup); + lua_setfield(L, -(nup + 2), l.first); } - lua_replace(L, -2); + lua_pop(L, nup); + } + + /** + * Set a global. + * + * @param L the Lua state + * @param name the name + */ + static inline void setglobal(lua_State *L, const std::string &name) + { + lua_setglobal(L, name.c_str()); + } - LUAE_STACK_CHECKEND(L, -1); + /** + * Pops the table at the top of the stack and sets it as the metatable + * of the value at the given index. + * + * @param L the Lua state + * @param index the value index + */ + static inline void setmetatable(lua_State *L, int index) + { + lua_setmetatable(L, index); + } + + /** + * Does t[n] where n is the value at the top of the stack and the key + * just below the value. + * + * @param L the Lua state + * @param index the value index + */ + static inline void settable(lua_State *L, int index) + { + lua_settable(L, index); + } + + /** + * Set the stack size. + * + * @param L the Lua state + * @param index the index + */ + static inline void settop(lua_State *L, int index = 0) + { + lua_settop(L, index); } /** - * Get an object from Lua that was previously push with pushShared. + * Pops a table or nil from the top stack and set it as the new + * associated value to the userdata. + * + * @param L the Lua state + * @param index the userdata index + */ + static inline void setuservalue(lua_State *L, int index) + { + lua_setuservalue(L, index); + } + + /** + * Get the type at the given index. + * + * @param L the Lua state + * @param index the value index + * @return the type + */ + static inline int type(lua_State *L, int index) + { + return lua_type(L, index); + } + + /** + * Get the type name. + * + * @param L the Lua state + * @param type the type + * @return the name + * @see type + */ + static inline const char *typeName(lua_State *L, int type) + { + return lua_typename(L, type); + } + + /** + * Get the type name from a index. + * + * @param L the Lua state + * @param index the value index + * @return the name + */ + static inline const char *typeNamei(lua_State *L, int index) + { + return luaL_typename(L, index); + } + + /** + * Unref the value from the table. + * + * @param L the Lua state + * @param index the table index + * @param ref the reference + */ + static inline void unref(lua_State *L, int index, int ref) + { + luaL_unref(L, index, ref); + } + + /** + * Get the up value index. + * + * @param index the index + * @return the real index + */ + static inline int upvalueindex(int index) + { + return lua_upvalueindex(index); + } + + /* ------------------------------------------------- + * Extended API + * ------------------------------------------------- */ + + /** + * 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); + + /** + * 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_assert(Convert::hasPush, "type not supported"); + + Convert::push(L, 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, T>::type * = 0) + { + new (L, IsUserdata::MetatableName) T(value); + } + + /** + * TODO: DOC * * @param L the Lua state - * @param index the object index - * @param meta the object metatable name - * @return the object + * @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. + * + * @param L the Lua state + * @param s the string + */ + template + static void push(lua_State *L, const char (&s)[N]) + { + 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_assert(Convert::hasGet, "type not supported"); + + return Convert::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 Ptr getShared(lua_State *L, int index, const char *meta) + static T * + get(lua_State *L, + int index, + typename std::enable_if::value, T *>::type * = 0) + { + 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_assert(Convert::hasCheck, "type not supported"); + + return Convert::check(L, index); + } + + /** + * 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); + } + + /** + * 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) + { + return LuaeClass::getShared(L, index, IsUserdata::MetatableName); + } + + /** + * 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) + { + using ValueType = typename Container::value_type; + using IteratorType = typename Container::const_iterator; + LUAE_STACK_CHECKBEGIN(L); - luaL_checktype(L, index, LUA_TUSERDATA); - if (!luaL_getmetafield(L, index, FieldName)) - luaL_error(L, "invalid type"); + 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; + } + + /** + * Convert a new placement made object, without testing if its a real + * object. + * + * @param L the Lua state + * @param idx the object index + * @return the converted object + */ + template + static inline T toType(lua_State *L, int idx) + { + return reinterpret_cast(lua_touserdata(L, idx)); + } + + /** + * Check for a userdata from the stack but without checking if it's a real + * LuaeClass one. + * + * @param L the Lua state + * @param idx the index + * @param meta the metatable name + * @return the object + */ + template + static inline T toType(lua_State *L, int idx, const char *meta) + { + return reinterpret_cast(luaL_checkudata(L, idx, meta)); + } +}; + +/** + * @brief Overload for nil. + */ +template <> +struct Luae::Convert { + static const bool hasPush = true; //!< push supported + + /** + * Push nil. + * + * @param L the Lua state + */ + static void push(lua_State *L, const std::nullptr_t &) + { + lua_pushnil(L); + } +}; - // Get the class name - const char *name = lua_tostring(L, -1); - lua_pop(L, 1); +/** + * @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 + + /** + * Push the boolean value. + * + * @param L the Lua state + * @param value the value + */ + static void push(lua_State *L, const bool &value) + { + lua_pushboolean(L, value); + } + + /** + * Get a boolean. + * + * @param L the Lua state + * @param index the index + * @return a boolean + */ + static bool get(lua_State *L, int index) + { + return lua_toboolean(L, index); + } + + /** + * Check for a bool. + * + * @param L the Lua state + * @param index the index + */ + static bool check(lua_State *L, int index) + { + return lua_toboolean(L, index); + } +}; + +/** + * @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 + + /** + * Push the integer value. + * + * @param L the Lua state + * @param value the value + */ + static void push(lua_State *L, const int &value) + { + lua_pushinteger(L, value); + } + + /** + * Get a integer. + * + * @param L the Lua state + * @param index the index + * @return a boolean + */ + static int get(lua_State *L, int index) + { + return lua_tointeger(L, index); + } + + /** + * Check for an integer. + * + * @param L the Lua state + * @param index the index + */ + static int check(lua_State *L, int index) + { + return luaL_checkinteger(L, index); + } +}; - bool found(false); +/** + * @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 + + /** + * Push the integer value. + * + * @param L the Lua state + * @param value the value + */ + static void push(lua_State *L, const long &value) + { + lua_pushinteger(L, value); + } + + /** + * Get a integer. + * + * @param L the Lua state + * @param index the index + * @return a boolean + */ + static long get(lua_State *L, int index) + { + return lua_tointeger(L, index); + } + + /** + * Check for an integer. + * + * @param L the Lua state + * @param index the index + */ + static long check(lua_State *L, int index) + { + return luaL_checkinteger(L, index); + } +}; + +/** + * @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 + + /** + * Push the double value. + * + * @param L the Lua state + * @param value the value + */ + static void push(lua_State *L, const double &value) + { + lua_pushnumber(L, value); + } + + /** + * Get a double. + * + * @param L the Lua state + * @param index the index + * @return a boolean + */ + static double get(lua_State *L, int index) + { + return lua_tonumber(L, index); + } - if (std::string(name) == std::string(meta)) { - found = true; - } else { - if (!luaL_getmetafield(L, index, FieldParents)) - luaL_error(L, "invalid type"); + /** + * Check for a double. + * + * @param L the Lua state + * @param index the index + */ + static double check(lua_State *L, int index) + { + return luaL_checknumber(L, index); + } +}; + +/** + * @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 + + /** + * Push the string value. + * + * @param L the Lua state + * @param value the value + */ + static void push(lua_State *L, const std::string &value) + { + lua_pushlstring(L, value.c_str(), value.length()); + } + + /** + * Get a string. + * + * @param L the Lua state + * @param index the index + * @return a boolean + */ + static std::string get(lua_State *L, int index) + { + return lua_tostring(L, index); + } + + /** + * Check for a string. + * + * @param L the Lua state + * @param index the index + */ + static std::string check(lua_State *L, int index) + { + return luaL_checkstring(L, index); + } +}; + +/** + * @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 + + /** + * Push the string value. + * + * @param L the Lua state + * @param str the value + */ + static void push(lua_State *L, const std::u32string &str) + { + lua_createtable(L, str.size(), 0); + for (size_t i = 0; i < str.size(); ++i) { + lua_pushinteger(L, str[i]); + lua_rawseti(L, -2, i + 1); + } + } + + /** + * Get a string. + * + * @param L the Lua state + * @param index the index + * @return a boolean + */ + static std::u32string get(lua_State *L, int index) + { + std::u32string result; + + if (lua_type(L, index) == LUA_TNUMBER) { + result.push_back(lua_tonumber(L, index)); + } else if (lua_type(L, index) == LUA_TTABLE) { + if (index < 0) + -- index; 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; + while (lua_next(L, index)) { + if (lua_type(L, -1) == LUA_TNUMBER) + result.push_back(lua_tonumber(L, -1)); lua_pop(L, 1); } - - lua_pop(L, 1); } - if (!found) - luaL_error(L, "invalid cast from `%s' to `%s'", name, meta); + return result; + } + + /** + * Check for a string. + * + * @param L the Lua state + * @param index the index + */ + static std::u32string check(lua_State *L, int index) + { + if (lua_type(L, index) != LUA_TNUMBER && + lua_type(L, index) != LUA_TTABLE) + luaL_error(L, "expected table or number"); + // NOTREACHED + + return get(L, index); + } +}; - LUAE_STACK_CHECKEQUALS(L); - - return *static_cast *>(lua_touserdata(L, index)); +/** + * @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); + } } /** - * 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. + * Get a string list. * - * Also return 0 the __gc method can directly call - * return LuaeClass::deleteShared(...) + * @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 0 for convenience + * @return the list */ - template - static int deleteShared(lua_State *L, int index) + static std::vector check(lua_State *L, int index) { - LUAE_STACK_CHECKBEGIN(L); + luaL_checktype(L, index, LUA_TTABLE); + + return get(L, index); + } +}; - Luae::toType *>(L, index)->~shared_ptr(); +/** + * @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 - LUAE_STACK_CHECKEQUALS(L); + /** + * Push the string value. + * + * @param L the Lua state + * @param value the value + */ + static void push(lua_State *L, const char *value) + { + lua_pushstring(L, value); + } - return 0; + /** + * Get a string. + * + * @param L the Lua state + * @param index the index + * @return a boolean + */ + static const char *get(lua_State *L, int index) + { + return lua_tostring(L, index); + } + + /** + * Check for a string. + * + * @param L the Lua state + * @param index the index + */ + static const char *check(lua_State *L, int index) + { + return luaL_checkstring(L, index); } }; @@ -775,6 +1888,9 @@ */ class LuaeTable { public: + /** + * The map function for \ref read + */ using ReadFunction = std::function; /** @@ -782,7 +1898,7 @@ * * @param L the Lua state * @param nrec the optional number of entries as record - * @param nrec the optional number of entries as sequence + * @param narr the optional number of entries as sequence */ static void create(lua_State *L, int nrec = 0, int narr = 0); @@ -810,31 +1926,6 @@ } /** - * Set a field to the table at the given index. - * - * @param L the Lua state - * @param idx the table index - * @param name the field name - * @see set - */ - static inline void setfield(lua_State *L, int idx, const std::string &name) - { - lua_setfield(L, idx, name.c_str()); - } - - /** - * Get a field at the given index. - * - * @param L the Lua state - * @param idx the table index - * @param name the field name - */ - static inline void getfield(lua_State *L, int idx, const std::string &name) - { - lua_getfield(L, idx, name.c_str()); - } - - /** * Set a table field. Specialized for the same fields as get. * * @param L the Lua state @@ -849,7 +1940,7 @@ LUAE_STACK_CHECKBEGIN(L); Luae::push(L, value); - LuaeTable::setfield(L, (idx < 0) ? --idx : idx, name); + Luae::setfield(L, (idx < 0) ? --idx : idx, name); LUAE_STACK_CHECKEQUALS(L); } @@ -869,30 +1960,6 @@ } /** - * Set a class object to a table. - * - * @param L the Lua state - * @param index the table index - * @param name the field name - * @param meta the metatable name - * @param o the object - */ - template - static void setShared(lua_State *L, - int index, - const std::string &name, - const std::string &meta, - LuaeClass::Ptr o) - { - LUAE_STACK_CHECKBEGIN(L); - - LuaeClass::pushShared(L, o, meta); - LuaeTable::setfield(L, (index < 0) ? --index : index, name); - - LUAE_STACK_CHECKEQUALS(L); - } - - /** * Require a field from a table. * * @param L the Lua state @@ -975,25 +2042,47 @@ /** * The definition of the enumeration */ - using Def = std::unordered_map; + 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); /** - * Bind the enumeration as a table into an existing table. + * 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); + 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 @@ -1001,8 +2090,8 @@ 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. + * Get the enumeration from Lua. Returns 0 if the value at the index + * is not a table. * * This is used as the OR replacement. * @@ -1011,16 +2100,54 @@ * @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. + * + * @param size the object size + * @param L the Lua state + * @return the allocated data + */ void *operator new(size_t size, lua_State *L); +/** + * Push a Lua userdata with a metatable. + * + * @param size the object size + * @param L the Lua state + * @param metaname the metatable name + * @return the allocated data + */ void *operator new(size_t size, lua_State *L, const char *metaname); +/** + * Delete the Lua userdata. + * + * @param ptr the data + * @param L the Lua state + */ void operator delete(void *ptr, lua_State *L); +/** + * Delete the Lua userdata. + * + * @param ptr the data + * @param L the Lua state + * @param metaname the metatable name + */ void operator delete(void *ptr, lua_State *L, const char *metaname); -#endif // !_LUA_H_ +#endif // !_LUAE_H_ diff -r 9324b9e0e7b7 -r 8fc177bbc4a6 C++/Pack.h --- a/C++/Pack.h Thu May 08 18:57:47 2014 +0200 +++ b/C++/Pack.h Thu May 08 22:55:48 2014 +0200 @@ -58,7 +58,7 @@ static_assert(TypeInfo::supported, "unsupported type"); T ret = convert(value, endian); - out.write(reinterpret_cast(&ret), TypeInfo::size); + out.write(reinterpret_cast(&ret), TypeInfo::size); writeFile(out, endian, args...); } @@ -72,12 +72,15 @@ { static_assert(TypeInfo::supported, "unsupported type"); - in.read(reinterpret_cast(&value), TypeInfo::size); + in.read(reinterpret_cast(&value), TypeInfo::size); value = convert(value, endian); readFile(in, endian, args...); } public: + /** + * Host system endian mode. + */ static const Endian mode; /** diff -r 9324b9e0e7b7 -r 8fc177bbc4a6 CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Thu May 08 22:55:48 2014 +0200 @@ -0,0 +1,67 @@ +# +# CMakeLists.txt -- code building for common code +# +# 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. +# + +# +# This CMakeLists build system is primarily used for testing all +# modules. +# +cmake_minimum_required(VERSION 2.8.11) +project(code) + +set(CMAKE_MODULE_PATH ${code_SOURCE_DIR}/cmake) + +enable_testing() + +include_directories( + ${code_SOURCE_DIR}/C++ +) + +# Cppunit is the unit tester +find_package(Cppunit REQUIRED) + +function(define_test name sources) +# The executable + add_executable( + ${name} + ${sources} + ) + + target_include_directories(${name} PRIVATE ${CPPUNIT_INCLUDE_DIR}) + target_link_libraries(${name} ${CPPUNIT_LIBRARY}) + add_test(${name}-test ${name}) +endfunction() + +option(WITH_CONVERTER "Enable converter tests" On) +option(WITH_DIRECTORY "Enable directory tests" On) +option(WITH_DRIVER "Enable SQL drivers tests" On) +option(WITH_DYNLIB "Enable DynLib tests" On) +option(WITH_HASH "Enable hash functions tests" On) +option(WITH_LUAE "Enable Luae tests" On) +option(WITH_PACK "Enable pack functions" On) +option(WITH_PARSER "Enable parser tests" On) +option(WITH_SOCKET "Enable sockets tests" On) +option(WITH_UTF8 "Enable Utf8 functions tests" On) +option(WITH_XMLPARSER "Enable XML tests" On) + +if (WITH_DYNLIB) + add_subdirectory(C++/Tests/DynLib) +endif () + + + +