// 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 "kaguya/config.hpp" #include "kaguya/lua_ref.hpp" #include "kaguya/push_any.hpp" #include "kaguya/detail/lua_ref_impl.hpp" #include "kaguya/detail/lua_table_def.hpp" namespace kaguya { class State; /** * This class is the type returned by members of non-const LuaRef(Table) when * directly accessing its elements. */ template class TableKeyReferenceProxy : public detail::LuaVariantImpl > { public: int pushStackIndex(lua_State *state) const { push(state); return lua_gettop(state); } lua_State *state() const { return state_; } friend class LuaRef; friend class State; //! this is not copy.same assign from referenced value. TableKeyReferenceProxy &operator=(const TableKeyReferenceProxy &src) { detail::table_proxy::set(state_, table_index_, key_, src); return *this; } //! assign from T template TableKeyReferenceProxy &operator=(const T &src) { detail::table_proxy::set(state_, table_index_, key_, src); return *this; } #if KAGUYA_USE_CPP11 template TableKeyReferenceProxy &operator=(T &&src) { detail::table_proxy::set(state_, table_index_, key_, std::forward(src)); return *this; } #endif bool isNilref() const { if (!state_) { return false; } util::ScopedSavedStack save(state_); push(state_); return lua_isnoneornil(state_, -1); } //! register class metatable to lua and set to table template void setClass(const UserdataMetatable ®) { set_class(reg); } //! set function template void setFunction(T f) { detail::table_proxy::set(state_, table_index_, key_, kaguya::function(f)); } int push(lua_State *state) const { int type = lua_type(state_, table_index_); if (type != LUA_TTABLE && type != LUA_TUSERDATA) { lua_pushnil(state); return 1; } detail::table_proxy::get(state_, table_index_, key_); if (state_ != state) { lua_xmove(state_, state, 1); } return 1; } int push() const { return push(state_); } int type() const { util::ScopedSavedStack save(state_); push(); return lua_type(state_, -1); } ~TableKeyReferenceProxy() { if (state_) { lua_settop(state_, stack_top_); } } ///!constructs the reference. Accessible only to kaguya::LuaRef itself TableKeyReferenceProxy(const TableKeyReferenceProxy &src) : state_(src.state_), stack_top_(src.stack_top_), table_index_(src.table_index_), key_(src.key_) { src.state_ = 0; } ///!constructs the reference. Accessible only to kaguya::LuaRef itself TableKeyReferenceProxy(lua_State *state, int table_index, KEY key, int revstacktop) : state_(state), stack_top_(revstacktop), table_index_(table_index), key_(key) {} private: template void set_class(const UserdataMetatable ®) { detail::table_proxy::set(state_, table_index_, key_, reg.createMatatable(state_)); } ///!constructs the reference. Accessible only to kaguya::LuaRef itself TableKeyReferenceProxy(lua_State *state, int table_index, const KEY &key, int revstacktop, const NoTypeCheck &) : state_(state), stack_top_(revstacktop), table_index_(table_index), key_(key) {} TableKeyReferenceProxy(const LuaTable &table, const KEY &key) : state_(table.state()), stack_top_(lua_gettop(state_)), key_(key) { util::one_push(state_, table); table_index_ = stack_top_ + 1; } TableKeyReferenceProxy(const LuaRef &table, const KEY &key) : state_(table.state()), stack_top_(lua_gettop(state_)), key_(key) { util::one_push(state_, table); table_index_ = stack_top_ + 1; int t = lua_type(state_, table_index_); if (t != LUA_TTABLE) { except::typeMismatchError(state_, lua_typename(state_, t) + std::string(" is not table")); } } mutable lua_State *state_; // mutable for RVO unsupported compiler int stack_top_; int table_index_; KEY key_; }; template inline std::ostream &operator<<(std::ostream &os, const TableKeyReferenceProxy &ref) { lua_State *state = ref.state(); util::ScopedSavedStack save(state); int stackIndex = ref.pushStackIndex(state); util::stackValueDump(os, state, stackIndex); return os; } namespace detail { template inline bool LuaFunctionImpl::setFunctionEnv(const LuaTable &env) { lua_State *state = state_(); if (!state) { return false; } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); int t = lua_type(state, stackIndex); if (t != LUA_TFUNCTION) { except::typeMismatchError(state, lua_typename(state, t) + std::string(" is not function")); return false; } env.push(state); #if LUA_VERSION_NUM >= 502 lua_setupvalue(state, stackIndex, 1); #else lua_setfenv(state, stackIndex); #endif return true; } template inline bool LuaFunctionImpl::setFunctionEnv(NewTable) { return setFunctionEnv(LuaTable(state_())); } template inline LuaTable LuaFunctionImpl::getFunctionEnv() const { lua_State *state = state_(); util::ScopedSavedStack save(state); if (!state) { except::typeMismatchError(state, "is nil"); return LuaTable(); } int stackIndex = pushStackIndex_(state); int t = lua_type(state, stackIndex); if (t != LUA_TFUNCTION) { except::typeMismatchError(state, lua_typename(state, t) + std::string(" is not function")); return LuaTable(); } #if LUA_VERSION_NUM >= 502 lua_getupvalue(state, stackIndex, 1); #else lua_getfenv(state, stackIndex); #endif return LuaTable(state, StackTop()); } template void LuaThreadImpl::setFunction(const LuaFunction &f) { lua_State *corstate = getthread(); if (corstate) { lua_settop(corstate, 0); f.push(corstate); } } template bool LuaTableOrUserDataImpl::setMetatable(const LuaTable &table) { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return false; } util::ScopedSavedStack save(state); int stackindex = pushStackIndex_(state); int t = lua_type(state, stackindex); if (t != LUA_TTABLE && t != LUA_TUSERDATA) { except::typeMismatchError(state, lua_typename(state, t) + std::string(" is not table")); return false; } table.push(); return lua_setmetatable(state, stackindex) != 0; } template LuaTable LuaTableOrUserDataImpl::getMetatable() const { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return LuaTable(); } util::ScopedSavedStack save(state); int stackindex = pushStackIndex_(state); int t = lua_type(state, stackindex); if (t != LUA_TTABLE && t != LUA_TUSERDATA) { except::typeMismatchError(state, lua_typename(state, t) + std::string(" is not table")); return LuaTable(); } if (!lua_getmetatable(state, stackindex)) { lua_pushnil(state); } return LuaTable(state, StackTop()); } template MemberFunctionBinder LuaTableOrUserDataImpl:: operator->*(const char *function_name) { push_(state_()); return MemberFunctionBinder(LuaRef(state_(), StackTop()), function_name); } template template LuaStackRef LuaTableOrUserDataImpl::getField(const KEY &key) const { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return LuaStackRef(); } push_(state); detail::table_proxy::get(state, lua_gettop(state), key); lua_remove(state, -2); // remove table return LuaStackRef(state, -1, true); } template template LuaStackRef LuaTableImpl::getRawField(const KEY &key) const { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return LuaStackRef(); } push_(state); detail::table_proxy::rawget(state, lua_gettop(state), key); lua_remove(state, -2); // remove table return LuaStackRef(state, -1, true); } template template LuaStackRef LuaTableOrUserDataImpl::operator[](KEY key) const { return getField(key); } template std::vector LuaTableImpl::values() const { return values(); } template std::vector LuaTableImpl::keys() const { return keys(); } template std::map LuaTableImpl::map() const { return map(); } template template TableKeyReferenceProxy LuaTableOrUserDataImpl::operator[](K key) { lua_State *state = state_(); int stack_top = lua_gettop(state); int stackindex = pushStackIndex_(state); return TableKeyReferenceProxy(state, stackindex, key, stack_top); } } /// @ingroup lua_type_traits /// @brief lua_type_traits for TableKeyReferenceProxy template struct lua_type_traits > { static int push(lua_State *l, const TableKeyReferenceProxy &ref) { return ref.push(l); } }; #if KAGUYA_USE_CPP11 /// @ingroup lua_type_traits /// @brief lua_type_traits for std::array template struct lua_type_traits > { typedef std::array get_type; typedef const std::array &push_type; static bool checkType(lua_State *l, int index) { if (lua_type(l, index) != LUA_TTABLE) { return false; } LuaStackRef table(l, index); if (table.size() != S) { return false; } // TODO bool valid = true; table.foreach_table_breakable( [&](const LuaStackRef &k, const LuaStackRef &v) { valid = valid && k.typeTest() && v.typeTest(); return valid; }); return valid; } static bool strictCheckType(lua_State *l, int index) { if (lua_type(l, index) != LUA_TTABLE) { return false; } LuaStackRef table(l, index); if (table.size() != S) { return false; } // TODO bool valid = true; table.foreach_table_breakable( [&](const LuaStackRef &k, const LuaStackRef &v) { valid = valid && k.typeTest() && v.typeTest(); return valid; }); return valid; } static get_type get(lua_State *l, int index) { if (lua_type(l, index) != LUA_TTABLE) { except::typeMismatchError(l, std::string("type mismatch")); return get_type(); } LuaStackRef t(l, index); if (t.size() != S) // TODO { except::typeMismatchError(l, std::string("type mismatch")); } get_type res; t.foreach_table([&](size_t k, const T &v) { if (k > 0 && k <= S) { res[k - 1] = v; } }); return res; } static int push(lua_State *l, push_type v) { lua_createtable(l, int(S), 0); for (size_t i = 0; i < S; ++i) { util::one_push(l, v[i]); lua_rawseti(l, -2, i + 1); } return 1; } }; #endif #ifndef KAGUYA_NO_STD_VECTOR_TO_TABLE /// @ingroup lua_type_traits /// @brief lua_type_traits for std::vector template struct lua_type_traits > { typedef std::vector get_type; typedef const std::vector &push_type; struct checkTypeForEach { checkTypeForEach(bool &valid) : valid_(valid) {} bool &valid_; bool operator()(const LuaStackRef &k, const LuaStackRef &v) { valid_ = k.typeTest() && v.weakTypeTest(); return valid_; } }; struct strictCheckTypeForEach { strictCheckTypeForEach(bool &valid) : valid_(valid) {} bool &valid_; bool operator()(const LuaStackRef &k, const LuaStackRef &v) { valid_ = k.typeTest() && v.typeTest(); return valid_; } }; static bool checkType(lua_State *l, int index) { LuaStackRef table(l, index); if (table.type() != LuaRef::TYPE_TABLE) { return false; } bool valid = true; table.foreach_table_breakable( checkTypeForEach(valid)); return valid; } static bool strictCheckType(lua_State *l, int index) { LuaStackRef table(l, index); if (table.type() != LuaRef::TYPE_TABLE) { return false; } bool valid = true; table.foreach_table_breakable( strictCheckTypeForEach(valid)); return valid; } static get_type get(lua_State *l, int index) { if (lua_type(l, index) != LUA_TTABLE) { except::typeMismatchError(l, std::string("type mismatch")); return get_type(); } return LuaStackRef(l, index).values(); } #if KAGUYA_USE_CPP11 typedef std::vector &&move_push_type; static int push(lua_State *l, move_push_type v) { lua_createtable(l, int(v.size()), 0); int count = 1; // array is 1 origin in Lua for (typename std::vector::iterator it = v.begin(); it != v.end(); ++it) { util::one_push(l, static_cast(*it)); lua_rawseti(l, -2, count++); } return 1; } #endif static int push(lua_State *l, push_type v) { lua_createtable(l, int(v.size()), 0); int count = 1; // array is 1 origin in Lua for (typename std::vector::const_iterator it = v.begin(); it != v.end(); ++it) { util::one_push(l, *it); lua_rawseti(l, -2, count++); } return 1; } }; #endif #ifndef KAGUYA_NO_STD_MAP_TO_TABLE /// @ingroup lua_type_traits /// @brief lua_type_traits for std::map template struct lua_type_traits > { typedef std::map get_type; typedef const std::map &push_type; struct checkTypeForEach { checkTypeForEach(bool &valid) : valid_(valid) {} bool &valid_; bool operator()(const LuaStackRef &k, const LuaStackRef &v) { valid_ = k.weakTypeTest() && v.weakTypeTest(); return valid_; } }; struct strictCheckTypeForEach { strictCheckTypeForEach(bool &valid) : valid_(valid) {} bool &valid_; bool operator()(const LuaStackRef &k, const LuaStackRef &v) { valid_ = k.typeTest() && v.typeTest(); return valid_; } }; static bool checkType(lua_State *l, int index) { LuaStackRef table(l, index); if (table.type() != LuaRef::TYPE_TABLE) { return false; } bool valid = true; table.foreach_table_breakable( checkTypeForEach(valid)); return valid; } static bool strictCheckType(lua_State *l, int index) { LuaStackRef table(l, index); if (table.type() != LuaRef::TYPE_TABLE) { return false; } bool valid = true; table.foreach_table_breakable( strictCheckTypeForEach(valid)); return valid; } static get_type get(lua_State *l, int index) { if (lua_type(l, index) != LUA_TTABLE) { except::typeMismatchError(l, std::string("type mismatch")); return get_type(); } return LuaStackRef(l, index).map(); } static int push(lua_State *l, push_type v) { lua_createtable(l, 0, int(v.size())); for (typename std::map::const_iterator it = v.begin(); it != v.end(); ++it) { util::one_push(l, it->first); util::one_push(l, it->second); lua_rawset(l, -3); } return 1; } }; #endif struct TableDataElement { typedef std::pair keyvalue_type; template TableDataElement(Value value) : keyvalue(keyvalue_type(AnyDataPusher(), value)) {} template TableDataElement(Key key, Value value) : keyvalue(keyvalue_type(key, value)) {} std::pair keyvalue; }; struct TableData { typedef std::pair data_type; #if KAGUYA_USE_CPP11 TableData(std::initializer_list list) : elements(list.begin(), list.end()) {} #endif template TableData(IT beg, IT end) : elements(beg, end) {} TableData() {} std::vector elements; }; /// @ingroup lua_type_traits /// @brief lua_type_traits for TableData template <> struct lua_type_traits { static int push(lua_State *l, const TableData &list) { lua_createtable(l, int(list.elements.size()), int(list.elements.size())); int count = 1; // array is 1 origin in Lua for (std::vector::const_iterator it = list.elements.begin(); it != list.elements.end(); ++it) { const TableDataElement &v = *it; if (v.keyvalue.first.empty()) { util::one_push(l, v.keyvalue.second); lua_rawseti(l, -2, count++); } else { util::one_push(l, v.keyvalue.first); util::one_push(l, v.keyvalue.second); lua_rawset(l, -3); } } return 1; } }; }