// Copyright satoren // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include #include "kaguya/config.hpp" #include "kaguya/error_handler.hpp" #include "kaguya/type.hpp" #include "kaguya/utility.hpp" #include "kaguya/detail/lua_ref_impl.hpp" #include "kaguya/detail/lua_variant_def.hpp" namespace kaguya { namespace util { template inline Result get_result_impl(lua_State *l, int startindex, types::typetag) { return lua_type_traits::get(l, startindex); } #if KAGUYA_USE_CPP11 inline standard::tuple<> get_result_tuple_impl(lua_State *, int, types::typetag >) { return standard::tuple<>(); } template inline standard::tuple get_result_tuple_impl(lua_State *l, int index, types::typetag >) { return standard::tuple_cat( standard::tuple(lua_type_traits::get(l, index)), get_result_tuple_impl(l, index + 1, types::typetag >())); } template inline standard::tuple get_result_impl(lua_State *l, int startindex, types::typetag > tag) { return get_result_tuple_impl(l, startindex, tag); } #else inline standard::tuple<> get_result_impl(lua_State *l, int startindex, types::typetag >) { KAGUYA_UNUSED(l); KAGUYA_UNUSED(startindex); return standard::tuple<>(); } #define KAGUYA_GET_DEF(N) \ lua_type_traits::get(l, N + startindex - 1) #define KAGUYA_GET_TUPLE_DEF(N) \ template \ inline standard::tuple get_result_impl( \ lua_State *l, int startindex, \ types::typetag >) { \ return standard::tuple( \ KAGUYA_PP_REPEAT_ARG(N, KAGUYA_GET_DEF)); \ } KAGUYA_PP_REPEAT_DEF(KAGUYA_FUNCTION_MAX_TUPLE_SIZE, KAGUYA_GET_TUPLE_DEF) #undef KAGUYA_GET_DEF #undef KAGUYA_GET_TUPLE_DEF #endif template inline Result get_result(lua_State *l, int startindex) { return get_result_impl(l, startindex, types::typetag()); } template <> inline void get_result(lua_State *, int) {} } /// @addtogroup Lua_reference_types /// @ingroup Lua_reference_types /// @brief Reference to any Lua data. class LuaRef : public Ref::RegistoryRef, public detail::LuaVariantImpl { private: static lua_State *toMainThread(lua_State *state) { #if LUA_VERSION_NUM >= 502 if (state) { lua_rawgeti(state, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); lua_State *mainthread = lua_tothread(state, -1); lua_pop(state, 1); if (mainthread) { return mainthread; } } #endif return state; } public: LuaRef(const Ref::RegistoryRef &src) : Ref::RegistoryRef(src) {} LuaRef(const LuaRef &src) : Ref::RegistoryRef(src) {} LuaRef &operator=(const LuaRef &src) { static_cast(*this) = src; return *this; } #if KAGUYA_USE_CPP11 LuaRef(LuaRef &&src) : Ref::RegistoryRef(std::move(src)) {} LuaRef &operator=(LuaRef &&src) throw() { swap(src); return *this; } LuaRef(RegistoryRef &&src) throw() : Ref::RegistoryRef(std::move(src)) {} template LuaRef(lua_State *state, T &&v, Ref::NoMainCheck) : Ref::RegistoryRef(state, std::move(v), Ref::NoMainCheck()) {} template LuaRef(lua_State *state, T &&v) : Ref::RegistoryRef(state, std::move(v)) {} #endif LuaRef() {} LuaRef(lua_State *state) : Ref::RegistoryRef(state) {} LuaRef(lua_State *state, StackTop, Ref::NoMainCheck) : Ref::RegistoryRef(state, StackTop(), Ref::NoMainCheck()) {} LuaRef(lua_State *state, StackTop) : Ref::RegistoryRef(state, StackTop()) {} template LuaRef(lua_State *state, const T &v, Ref::NoMainCheck) : Ref::RegistoryRef(state, v, Ref::NoMainCheck()) {} template LuaRef(lua_State *state, const T &v) : Ref::RegistoryRef(state, v) {} const void *native_pointer() const { util::ScopedSavedStack save(state()); push(state()); return lua_topointer(state(), -1); } static void putindent(std::ostream &os, int indent) { while (indent-- > 0) { os << " "; } } }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaRef template <> struct lua_type_traits { typedef LuaRef get_type; typedef const LuaRef &push_type; static bool checkType(lua_State *l, int index) { KAGUYA_UNUSED(l); KAGUYA_UNUSED(index); return true; } static bool strictCheckType(lua_State *l, int index) { KAGUYA_UNUSED(l); KAGUYA_UNUSED(index); return false; } static get_type get(lua_State *l, int index) { lua_pushvalue(l, index); return LuaRef(l, StackTop()); } static int push(lua_State *l, push_type v) { return v.push(l); } }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaRef template <> struct lua_type_traits : lua_type_traits {}; class LuaStackRef : public Ref::StackRef, public detail::LuaVariantImpl { public: LuaStackRef() : Ref::StackRef() {} LuaStackRef(lua_State *s, int index) : Ref::StackRef(s, index, false) {} LuaStackRef(lua_State *s, int index, bool popAtDestruct) : Ref::StackRef(s, index, popAtDestruct) {} #if KAGUYA_USE_CPP11 LuaStackRef(LuaStackRef &&src) : Ref::StackRef(std::move(src)) { src.pop_ = false; } LuaStackRef &operator=(LuaStackRef &&src) { if (this != &src) { Ref::StackRef::operator=(std::move(src)); src.pop_ = false; } return *this; } LuaStackRef(const LuaStackRef &src) = delete; #else LuaStackRef(const LuaStackRef &src) : Ref::StackRef(src) { src.pop_ = false; } LuaStackRef &operator=(const LuaStackRef &src) { if (this != &src) { Ref::StackRef::operator=(src); src.pop_ = false; } return *this; } #endif }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaStackRef template <> struct lua_type_traits { typedef LuaStackRef get_type; typedef const LuaStackRef &push_type; static bool checkType(lua_State *l, int index) { KAGUYA_UNUSED(l); KAGUYA_UNUSED(index); return true; } static bool strictCheckType(lua_State *l, int index) { KAGUYA_UNUSED(l); KAGUYA_UNUSED(index); return false; } static get_type get(lua_State *l, int index) { return LuaStackRef(l, index); } static int push(lua_State *l, push_type v) { return v.push(l); } }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaStackRef template <> struct lua_type_traits : lua_type_traits {}; /// @ingroup Lua_reference_types /// @brief Reference to Lua userdata. class LuaUserData : public Ref::RegistoryRef, public detail::LuaUserDataImpl, public detail::LuaTableOrUserDataImpl, public detail::LuaBasicTypeFunctions { void typecheck() { int t = type(); if (t != TYPE_USERDATA && t != TYPE_LIGHTUSERDATA && t != TYPE_NIL && t != TYPE_NONE) { except::typeMismatchError(state(), "not user data"); unref(); } } public: operator LuaRef() { push(state()); return LuaRef(state(), StackTop()); } LuaUserData(lua_State *state, StackTop) : Ref::RegistoryRef(state, StackTop()) { typecheck(); } template LuaUserData(lua_State *state, const TYPE &table) : Ref::RegistoryRef(state, table) { typecheck(); } explicit LuaUserData(lua_State *state) : Ref::RegistoryRef(state, NilValue()) { typecheck(); } LuaUserData() { typecheck(); } }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaUserData template <> struct lua_type_traits { typedef LuaUserData get_type; typedef LuaUserData push_type; static bool strictCheckType(lua_State *l, int index) { return lua_type(l, index) == LUA_TUSERDATA; } static bool checkType(lua_State *l, int index) { return lua_type(l, index) == LUA_TUSERDATA || lua_isnil(l, index); } static LuaUserData get(lua_State *l, int index) { lua_pushvalue(l, index); return LuaUserData(l, StackTop()); } static int push(lua_State *l, const LuaUserData &ref) { return ref.push(l); } }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaUserData template <> struct lua_type_traits : lua_type_traits {}; /// @ingroup Lua_reference_types /// @brief Reference to Lua table. class LuaTable : public Ref::RegistoryRef, public detail::LuaTableImpl, public detail::LuaTableOrUserDataImpl, public detail::LuaBasicTypeFunctions { void typecheck() { int t = type(); if (t != TYPE_TABLE && t != TYPE_NIL && t != TYPE_NONE) { except::typeMismatchError(state(), "not table"); unref(); } } public: operator LuaRef() { push(state()); return LuaRef(state(), StackTop()); } LuaTable(lua_State *state, StackTop) : Ref::RegistoryRef(state, StackTop()) { typecheck(); } LuaTable(lua_State *state, const NewTable &table) : Ref::RegistoryRef(state, table) { typecheck(); } explicit LuaTable(lua_State *state) : Ref::RegistoryRef(state, NewTable()) { typecheck(); } LuaTable() { typecheck(); } }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaTable template <> struct lua_type_traits { typedef LuaTable get_type; typedef LuaTable push_type; static bool strictCheckType(lua_State *l, int index) { return lua_istable(l, index); } static bool checkType(lua_State *l, int index) { return lua_istable(l, index) || lua_isnil(l, index); } static LuaTable get(lua_State *l, int index) { lua_pushvalue(l, index); return LuaTable(l, StackTop()); } static int push(lua_State *l, const LuaTable &ref) { return ref.push(l); } }; /// @ingroup lua_type_traits /// @brief lua_type_traits for LuaTable template <> struct lua_type_traits : lua_type_traits {}; /// @ingroup Lua_reference_types /// @brief Reference to Lua function. class LuaFunction : public Ref::RegistoryRef, public detail::LuaFunctionImpl, public detail::LuaBasicTypeFunctions { void typecheck() { int t = type(); if (t != TYPE_FUNCTION && t != TYPE_NIL && t != TYPE_NONE) { except::typeMismatchError(state(), "not function"); RegistoryRef::unref(); } } struct LuaLoadStreamWrapper { LuaLoadStreamWrapper(std::istream &stream) : preloaded_(false), stream_(stream) { buffer_.reserve(512); skipComment(); preloaded_ = !buffer_.empty(); } void skipComment() { // skip bom const char *bom = "\xEF\xBB\xBF"; const char *bomseq = bom; char c; while (stream_.get(c)) { if (c != *bomseq) // not bom sequence { buffer_.assign(bom, bomseq); buffer_.push_back(c); break; } bomseq++; if ('\0' == *bomseq) { return; } } // skip comment if (!buffer_.empty() && buffer_.front() == '#') { buffer_.clear(); std::string comment; std::getline(stream_, comment); } } static const char *getdata(lua_State *, void *ud, size_t *size) { LuaLoadStreamWrapper *loader = static_cast(ud); if (loader->preloaded_) { loader->preloaded_ = false; } else { loader->buffer_.clear(); } char c = 0; while (loader->buffer_.size() < loader->buffer_.capacity() && loader->stream_.get(c)) { loader->buffer_.push_back(c); } *size = loader->buffer_.size(); return loader->buffer_.empty() ? 0 : &loader->buffer_[0]; } private: bool preloaded_; std::vector buffer_; std::istream &stream_; }; public: /// @brief construct with state and function . /// @param state pointer to lua_State /// @param f execute function for lua thread. e.g. /// kaguya::function(function_ptr),kaguya::overload(function_ptr) template LuaFunction(lua_State *state, F f) : Ref::RegistoryRef(state, f) { typecheck(); } /// @brief construct with stack top value. /// @param state pointer to lua_State LuaFunction(lua_State *state, StackTop) : Ref::RegistoryRef(state, StackTop()) { typecheck(); } /// @brief construct with nil reference. LuaFunction() {} /// @brief load lua code . /// @param state pointer to lua_State /// @param luacode string static LuaFunction loadstring(lua_State *state, const std::string &luacode) { return loadstring(state, luacode.c_str()); } /// @brief load lua code . /// @param state pointer to lua_State /// @param luacode string static LuaFunction loadstring(lua_State *state, const char *luacode) { util::ScopedSavedStack save(state); int status = luaL_loadstring(state, luacode); if (status) { ErrorHandler::handle(status, state); lua_pushnil(state); } return LuaFunction(state, StackTop()); } /// @brief If there are no errors,compiled file as a Lua function and return. /// Otherwise send error message to error handler and return nil reference /// @param state pointer to lua_State /// @param file file path of lua script /// @return reference of lua function static LuaFunction loadfile(lua_State *state, const std::string &file) { return loadfile(state, file.c_str()); } /// @brief If there are no errors,compiled file as a Lua function and return. /// Otherwise send error message to error handler and return nil reference /// @param state pointer to lua_State /// @param file file path of lua script /// @return reference of lua function static LuaFunction loadfile(lua_State *state, const char *file) { util::ScopedSavedStack save(state); int status = luaL_loadfile(state, file); if (status) { ErrorHandler::handle(status, state); lua_pushnil(state); } return LuaFunction(state, StackTop()); } /// @brief If there are no errors,compiled stream as a Lua function and /// return. /// Otherwise send error message to error handler and return nil reference /// @param state pointer to lua_State /// @param stream stream of lua script data /// @param chunkname use for error message. /// @return reference of lua function static LuaStackRef loadstreamtostack(lua_State *state, std::istream &stream, const char *chunkname = 0) { LuaLoadStreamWrapper wrapper(stream); #if LUA_VERSION_NUM >= 502 int status = lua_load(state, &LuaLoadStreamWrapper::getdata, &wrapper, chunkname, 0); #else int status = lua_load(state, &LuaLoadStreamWrapper::getdata, &wrapper, chunkname); #endif if (status) { ErrorHandler::handle(status, state); lua_pushnil(state); } return LuaStackRef(state, -1, true); } /// @brief If there are no errors,compiled stream as a Lua function and /// return. /// Otherwise send error message to error handler and return nil reference /// @param state pointer to lua_State /// @param stream stream of lua script data /// @param chunkname use for error message. /// @return reference of lua function static LuaFunction loadstream(lua_State *state, std::istream &stream, const char *chunkname = 0) { util::ScopedSavedStack save(state); LuaLoadStreamWrapper wrapper(stream); #if LUA_VERSION_NUM >= 502 int status = lua_load(state, &LuaLoadStreamWrapper::getdata, &wrapper, chunkname, 0); #else int status = lua_load(state, &LuaLoadStreamWrapper::getdata, &wrapper, chunkname); #endif if (status) { ErrorHandler::handle(status, state); lua_pushnil(state); } return LuaFunction(state, StackTop()); } }; /// @ingroup Lua_reference_types /// @brief Reference to Lua thread(coroutine). class LuaThread : public Ref::RegistoryRef, public detail::LuaThreadImpl, public detail::LuaBasicTypeFunctions { void typecheck() { int t = type(); if (t != TYPE_THREAD && t != TYPE_NIL && t != TYPE_NONE) { except::typeMismatchError(state(), "not lua thread"); RegistoryRef::unref(); } } public: /// @brief construct with stack top value. LuaThread(lua_State *state, StackTop) : Ref::RegistoryRef(state, StackTop()) { typecheck(); } /// @brief construct with new thread. LuaThread(lua_State *state, const NewThread &t) : Ref::RegistoryRef(state, t) {} /// @brief construct with nil reference. LuaThread(lua_State *state) : Ref::RegistoryRef(state, NewThread()) {} /// @brief construct with nil reference. LuaThread() {} /// @brief get lua thread operator lua_State *() { return getthread(); } }; } #if KAGUYA_USE_CPP11 #else namespace std { template <> inline void swap(kaguya::LuaUserData &a, kaguya::LuaUserData &b) { a.swap(b); } template <> inline void swap(kaguya::LuaTable &a, kaguya::LuaTable &b) { a.swap(b); } template <> inline void swap(kaguya::LuaFunction &a, kaguya::LuaFunction &b) { a.swap(b); } template <> inline void swap(kaguya::LuaThread &a, kaguya::LuaThread &b) { a.swap(b); } template <> inline void swap(kaguya::LuaRef &a, kaguya::LuaRef &b) { a.swap(b); } } #endif