// 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 "kaguya/config.hpp" #include "kaguya/error_handler.hpp" #include "kaguya/type.hpp" #include "kaguya/utility.hpp" namespace kaguya { class LuaRef; class LuaStackRef; class LuaTable; template class TableKeyReferenceProxy; class MemberFunctionBinder; namespace detail { struct table_proxy { #if KAGUYA_USE_CPP11 template static void set(lua_State *state, int table_index, KEY &&key, V &&value) { util::one_push(state, std::forward(key)); util::one_push(state, std::forward(value)); lua_settable(state, table_index); } template static void set(lua_State *state, int table_index, const char *key, V &&value) { util::one_push(state, std::forward(value)); lua_setfield(state, table_index, key); } template static void set(lua_State *state, int table_index, const std::string &key, V &&value) { set(state, table_index, key.c_str(), std::forward(value)); } template static void set(lua_State *state, int table_index, luaInt key, V &&value) { util::one_push(state, std::forward(value)); lua_seti(state, table_index, key); } template static void rawset(lua_State *state, int table_index, KEY &&key, V &&value) { util::one_push(state, std::forward(key)); util::one_push(state, std::forward(value)); lua_rawset(state, table_index); } template static void rawset(lua_State *state, int table_index, luaInt key, V &&value) { util::one_push(state, std::forward(value)); lua_rawseti(state, table_index, key); } #else template static void set(lua_State *state, int table_index, const KEY &key, const V &value) { util::one_push(state, key); util::one_push(state, value); lua_settable(state, table_index); } template static void set(lua_State *state, int table_index, const char *key, const V &value) { util::one_push(state, value); lua_setfield(state, table_index, key); } template static void set(lua_State *state, int table_index, const std::string &key, const V &value) { util::one_push(state, value); lua_setfield(state, table_index, key.c_str()); } template static void set(lua_State *state, int table_index, luaInt key, const V &value) { util::one_push(state, value); lua_seti(state, table_index, key); } template static void rawset(lua_State *state, int table_index, const KEY &key, const V &value) { util::one_push(state, key); util::one_push(state, value); lua_rawset(state, table_index); } template static void rawset(lua_State *state, int table_index, luaInt key, const V &value) { util::one_push(state, value); lua_rawseti(state, table_index, key); } #endif #if KAGUYA_USE_CPP11 template static void get(lua_State *state, int table_index, KEY &&key) { util::one_push(state, std::forward(key)); lua_gettable(state, table_index); } #endif template static void get(lua_State *state, int table_index, const KEY &key) { util::one_push(state, key); lua_gettable(state, table_index); } static void get(lua_State *state, int table_index, const char *key) { lua_getfield(state, table_index, key); } static void get(lua_State *state, int table_index, const std::string &key) { lua_getfield(state, table_index, key.c_str()); } static void get(lua_State *state, int table_index, luaInt key) { lua_geti(state, table_index, key); } #if KAGUYA_USE_CPP11 template static void rawget(lua_State *state, int table_index, KEY &&key) { util::one_push(state, std::forward(key)); lua_rawget(state, table_index); } #endif template static void rawget(lua_State *state, int table_index, const KEY &key) { util::one_push(state, key); lua_rawget(state, table_index); } static void rawget(lua_State *state, int table_index, luaInt key) { lua_rawgeti(state, table_index, key); } }; template class LuaTableOrUserDataImpl { private: lua_State *state_() const { return static_cast(this)->state(); } int pushStackIndex_(lua_State *state) const { return static_cast(this)->pushStackIndex(state); } int push_(lua_State *state) const { return static_cast(this)->push(state); } public: /// @brief set metatable /// @param table metatable bool setMetatable(const LuaTable &table); /// @brief get metatable LuaTable getMetatable() const; /// @brief table->*"function_name"() in c++ and table:function_name(); in lua /// is same /// @param function_name function_name in table MemberFunctionBinder operator->*(const char *function_name); /// @brief value = table[key]; /// @param key key of table /// @return reference of field value template typename lua_type_traits::get_type getField(const KEY &key) const { lua_State *state = state_(); typedef typename lua_type_traits::get_type get_type; if (!state) { except::typeMismatchError(state, "is nil"); return get_type(); } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); table_proxy::get(state, stackIndex, key); return lua_type_traits::get(state, -1); } /// @brief value = table[key]; /// @param key key of table /// @return reference of field value template LuaStackRef getField(const KEY &key) const; #if KAGUYA_USE_CPP11 /// @brief table[key] = value; template bool setField(K &&key, V &&value) { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return false; } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); table_proxy::set(state, stackIndex, std::forward(key), std::forward(value)); return true; } #else /// @brief table[key] = value; template bool setField(const K &key, const V &value) { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return false; } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); table_proxy::set(state, stackIndex, key, value); return true; } #endif /// @brief value = table[key]; /// @param key key of table /// @return reference of field value template LuaStackRef operator[](K key) const; /// @brief value = table[key];or table[key] = value; /// @param key key of table /// @return reference of field value template TableKeyReferenceProxy operator[](K key); }; template class LuaTableImpl { private: lua_State *state_() const { return static_cast(this)->state(); } int pushStackIndex_(lua_State *state) const { return static_cast(this)->pushStackIndex(state); } int push_(lua_State *state) const { return static_cast(this)->push(state); } template struct gettablekey { typedef K key_type; typedef void value_type; std::vector &v_; gettablekey(std::vector &v) : v_(v) {} void operator()(K key, const void *) { v_.push_back(key); } }; template struct gettablevalue { typedef void key_type; typedef V value_type; std::vector &v_; gettablevalue(std::vector &v) : v_(v) {} void operator()(const void *, V value) { v_.push_back(value); } }; template struct gettablemap { typedef K key_type; typedef V value_type; std::map &m_; gettablemap(std::map &m) : m_(m) {} void operator()(K key, V value) { m_[key] = value; } }; public: #if KAGUYA_USE_CPP11 /// @brief rawset(table,key,value) template bool setRawField(K &&key, V &&value) { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return false; } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); table_proxy::rawset(state, stackIndex, std::forward(key), std::forward(value)); return true; } #else /// @brief rawset(table,key,value) template bool setRawField(const K &key, const V &value) { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return false; } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); table_proxy::rawset(state, stackIndex, key, value); return true; } #endif /// @brief value = rawget(table,key); /// @param key key of table /// @return reference of field value template typename lua_type_traits::get_type getRawField(const KEY &key) const { lua_State *state = state_(); typedef typename lua_type_traits::get_type get_type; if (!state) { except::typeMismatchError(state, "is nil"); return get_type(); } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); table_proxy::rawget(state, stackIndex, key); return lua_type_traits::get(state, -1); } /// @brief value = rawget(table,key); /// @param key key of table /// @return reference of field value template LuaStackRef getRawField(const KEY &key) const; /// @brief foreach table fields template void foreach_table(Fun f) const { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return; } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); lua_pushnil(state); while (lua_next(state, stackIndex) != 0) { // backup key lua_pushvalue(state, -2); f(lua_type_traits::get(state, -1), lua_type_traits::get(state, -2)); lua_pop(state, 2); // pop key and value } } /// @brief foreach table fields template void foreach_table_breakable(Fun f) const { lua_State *state = state_(); if (!state) { except::typeMismatchError(state, "is nil"); return; } util::ScopedSavedStack save(state); int stackIndex = pushStackIndex_(state); lua_pushnil(state); while (lua_next(state, stackIndex) != 0) { lua_pushvalue(state, -2); // backup key bool cont = f(lua_type_traits::get(state, -1), lua_type_traits::get(state, -2)); lua_pop(state, 2); // pop key and value if (!cont) { break; } } } /// @brief If type is table or userdata, return keys. /// @return field keys template std::vector keys() const { std::vector res; util::ScopedSavedStack save(state_()); int stackIndex = pushStackIndex_(state_()); size_t size = lua_rawlen(state_(), stackIndex); res.reserve(size); foreach_table(gettablekey(res)); return res; } /// @brief If type is table or userdata, return keys. /// @return field keys template std::vector keys() const { return keys >(); } std::vector keys() const; /// @brief If type is table or userdata, return values. /// @return field value template std::vector values() const { std::vector res; util::ScopedSavedStack save(state_()); int stackIndex = pushStackIndex_(state_()); size_t size = lua_rawlen(state_(), stackIndex); res.reserve(size); foreach_table(gettablevalue(res)); return res; } /// @brief If type is table or userdata, return values. /// @return field value template std::vector values() const { return values >(); } std::vector values() const; /// @brief If type is table or userdata, return key value pair. /// @return key value pair template std::map map() const { std::map res; foreach_table(gettablemap(res)); return res; } /// @brief If type is table or userdata, return key value pair. /// @return key value pair template std::map map() const { return map > >(); } /// @brief If type is table or userdata, return key value pair. /// @return key value pair template std::map map() const { return map >(); } std::map map() const; }; template class LuaUserDataImpl { private: lua_State *state_() const { return static_cast(this)->state(); } int pushStackIndex_(lua_State *state) const { return static_cast(this)->pushStackIndex(state); } int push_(lua_State *state) const { return static_cast(this)->push(state); } public: /// @brief is type test template bool isType() const { lua_State *state = state_(); util::ScopedSavedStack save(state); return lua_type_traits::strictCheckType(state, pushStackIndex_(state)); } template bool isConvertible() const { lua_State *state = state_(); util::ScopedSavedStack save(state); return lua_type_traits::checkType(state, pushStackIndex_(state)); } template bool typeTest() const { return isType(); } template bool weakTypeTest() const { return isConvertible(); } template typename lua_type_traits::get_type get() const { lua_State *state = state_(); util::ScopedSavedStack save(state); return lua_type_traits::get(state, state ? pushStackIndex_(state) : 0); } template operator T() const { return get(); } }; } }