// The MIT License (MIT) // Copyright (c) 2013-2017 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SOL_TABLE_CORE_HPP #define SOL_TABLE_CORE_HPP #include "proxy.hpp" #include "stack.hpp" #include "function_types.hpp" #include "usertype.hpp" #include "table_iterator.hpp" #include "types.hpp" #include "object_base.hpp" namespace sol { namespace detail { template struct clean { lua_State* L; clean(lua_State* luastate) : L(luastate) {} ~clean() { lua_pop(L, static_cast(n)); } }; struct ref_clean { lua_State* L; int& n; ref_clean(lua_State* luastate, int& n) : L(luastate), n(n) {} ~ref_clean() { lua_pop(L, static_cast(n)); } }; inline int fail_on_newindex(lua_State* L) { return luaL_error(L, "sol: cannot modify the elements of an enumeration table"); } } const new_table create = new_table{}; template class basic_table_core : public basic_object_base { typedef basic_object_base base_t; friend class state; friend class state_view; template using is_global = meta::all, meta::is_c_str...>; template void for_each(std::true_type, Fx&& fx) const { auto pp = stack::push_pop(*this); stack::push(base_t::lua_state(), lua_nil); while (lua_next(base_t::lua_state(), -2)) { sol::object key(base_t::lua_state(), -2); sol::object value(base_t::lua_state(), -1); std::pair keyvalue(key, value); auto pn = stack::pop_n(base_t::lua_state(), 1); fx(keyvalue); } } template void for_each(std::false_type, Fx&& fx) const { auto pp = stack::push_pop(*this); stack::push(base_t::lua_state(), lua_nil); while (lua_next(base_t::lua_state(), -2)) { sol::object key(base_t::lua_state(), -2); sol::object value(base_t::lua_state(), -1); auto pn = stack::pop_n(base_t::lua_state(), 1); fx(key, value); } } template auto tuple_get(types, std::index_sequence<0, 1, I...>, Keys&& keys) const -> decltype(stack::pop>(nullptr)) { typedef decltype(stack::pop>(nullptr)) Tup; return Tup( traverse_get_optional(meta::is_optional>(), detail::forward_get<0>(keys)), traverse_get_optional(meta::is_optional>(), detail::forward_get<1>(keys)), traverse_get_optional(meta::is_optional>(), detail::forward_get(keys))... ); } template decltype(auto) tuple_get(types, std::index_sequence, Keys&& keys) const { return traverse_get_optional(meta::is_optional>(), detail::forward_get(keys)); } template void tuple_set(std::index_sequence, Pairs&& pairs) { auto pp = stack::push_pop(pairs))...>::value)>(*this); void(detail::swallow{ (stack::set_field(base_t::lua_state(), detail::forward_get(pairs), detail::forward_get(pairs), lua_gettop(base_t::lua_state()) ), 0)... }); } template decltype(auto) traverse_get_deep(Key&& key) const { stack::get_field(base_t::lua_state(), std::forward(key)); return stack::get(base_t::lua_state()); } template decltype(auto) traverse_get_deep(Key&& key, Keys&&... keys) const { stack::get_field(base_t::lua_state(), std::forward(key)); return traverse_get_deep(std::forward(keys)...); } template decltype(auto) traverse_get_deep_optional(int& popcount, Key&& key) const { typedef decltype(stack::get(base_t::lua_state())) R; auto p = stack::probe_get_field(base_t::lua_state(), std::forward(key), lua_gettop(base_t::lua_state())); popcount += p.levels; if (!p.success) return R(nullopt); return stack::get(base_t::lua_state()); } template decltype(auto) traverse_get_deep_optional(int& popcount, Key&& key, Keys&&... keys) const { auto p = I > 0 ? stack::probe_get_field(base_t::lua_state(), std::forward(key), -1) : stack::probe_get_field(base_t::lua_state(), std::forward(key), lua_gettop(base_t::lua_state())); popcount += p.levels; if (!p.success) return T(nullopt); return traverse_get_deep_optional(popcount, std::forward(keys)...); } template decltype(auto) traverse_get_optional(std::false_type, Keys&&... keys) const { detail::clean c(base_t::lua_state()); return traverse_get_deep(std::forward(keys)...); } template decltype(auto) traverse_get_optional(std::true_type, Keys&&... keys) const { int popcount = 0; detail::ref_clean c(base_t::lua_state(), popcount); return traverse_get_deep_optional(popcount, std::forward(keys)...); } template void traverse_set_deep(Key&& key, Value&& value) const { stack::set_field(base_t::lua_state(), std::forward(key), std::forward(value)); } template void traverse_set_deep(Key&& key, Keys&&... keys) const { stack::get_field(base_t::lua_state(), std::forward(key)); traverse_set_deep(std::forward(keys)...); } basic_table_core(lua_State* L, detail::global_tag t) noexcept : base_t(L, t) { } protected: basic_table_core(detail::no_safety_tag, lua_State* L, int index) : base_t(L, index) {} basic_table_core(detail::no_safety_tag, lua_State* L, ref_index index) : base_t(L, index) {} template , basic_table_core>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_table_core(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward(r)) {} public: typedef basic_table_iterator iterator; typedef iterator const_iterator; basic_table_core() noexcept = default; basic_table_core(const basic_table_core&) = default; basic_table_core(basic_table_core&&) = default; basic_table_core& operator=(const basic_table_core&) = default; basic_table_core& operator=(basic_table_core&&) = default; basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {} basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {} template >>> = meta::enabler> basic_table_core(lua_State* L, T&& r) : basic_table_core(L, sol::ref_index(r.registry_index())) {} basic_table_core(lua_State* L, new_table nt) : base_t(L, (lua_createtable(L, nt.sequence_hint, nt.map_hint), -1)) { if (!std::is_base_of::value) { lua_pop(L, 1); } } basic_table_core(lua_State* L, int index = -1) : basic_table_core(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS constructor_handler handler{}; stack::check(L, index, handler); #endif // Safety } basic_table_core(lua_State* L, ref_index index) : basic_table_core(detail::no_safety, L, index) { #ifdef SOL_CHECK_ARGUMENTS auto pp = stack::push_pop(*this); constructor_handler handler{}; stack::check(L, -1, handler); #endif // Safety } template , basic_table_core>>, meta::neg>, std::is_base_of>> = meta::enabler> basic_table_core(T&& r) noexcept : basic_table_core(detail::no_safety, std::forward(r)) { #ifdef SOL_CHECK_ARGUMENTS if (!is_table>::value) { auto pp = stack::push_pop(*this); constructor_handler handler{}; stack::check(base_t::lua_state(), -1, handler); } #endif // Safety } iterator begin() const { return iterator(*this); } iterator end() const { return iterator(); } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } template decltype(auto) get(Keys&&... keys) const { static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match"); auto pp = stack::push_pop::value>(*this); return tuple_get(types(), std::make_index_sequence(), std::forward_as_tuple(std::forward(keys)...)); } template decltype(auto) get_or(Key&& key, T&& otherwise) const { typedef decltype(get("")) U; optional option = get>(std::forward(key)); if (option) { return static_cast(option.value()); } return static_cast(std::forward(otherwise)); } template decltype(auto) get_or(Key&& key, D&& otherwise) const { optional option = get>(std::forward(key)); if (option) { return static_cast(option.value()); } return static_cast(std::forward(otherwise)); } template decltype(auto) traverse_get(Keys&&... keys) const { auto pp = stack::push_pop::value>(*this); return traverse_get_optional(meta::is_optional>(), std::forward(keys)...); } template basic_table_core& traverse_set(Keys&&... keys) { auto pp = stack::push_pop::value>(*this); auto pn = stack::pop_n(base_t::lua_state(), static_cast(sizeof...(Keys)-2)); traverse_set_deep(std::forward(keys)...); return *this; } template basic_table_core& set(Args&&... args) { tuple_set(std::make_index_sequence(), std::forward_as_tuple(std::forward(args)...)); return *this; } template decltype(auto) raw_get(Keys&&... keys) const { static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match"); auto pp = stack::push_pop::value>(*this); return tuple_get(types(), std::make_index_sequence(), std::forward_as_tuple(std::forward(keys)...)); } template decltype(auto) raw_get_or(Key&& key, T&& otherwise) const { typedef decltype(raw_get("")) U; optional option = raw_get>(std::forward(key)); if (option) { return static_cast(option.value()); } return static_cast(std::forward(otherwise)); } template decltype(auto) raw_get_or(Key&& key, D&& otherwise) const { optional option = raw_get>(std::forward(key)); if (option) { return static_cast(option.value()); } return static_cast(std::forward(otherwise)); } template decltype(auto) traverse_raw_get(Keys&&... keys) const { auto pp = stack::push_pop::value>(*this); return traverse_get_optional(meta::is_optional>(), std::forward(keys)...); } template basic_table_core& traverse_raw_set(Keys&&... keys) { auto pp = stack::push_pop::value>(*this); auto pn = stack::pop_n(base_t::lua_state(), static_cast(sizeof...(Keys)-2)); traverse_set_deep(std::forward(keys)...); return *this; } template basic_table_core& raw_set(Args&&... args) { tuple_set(std::make_index_sequence(), std::forward_as_tuple(std::forward(args)...)); return *this; } template basic_table_core& set_usertype(usertype& user) { return set_usertype(usertype_traits::name(), user); } template basic_table_core& set_usertype(Key&& key, usertype& user) { return set(std::forward(key), user); } template basic_table_core& new_usertype(const std::string& name, Args&&... args) { usertype utype(std::forward(args)...); set_usertype(name, utype); return *this; } template basic_table_core& new_usertype(const std::string& name, Args&&... args) { constructors> ctor{}; return new_usertype(name, ctor, std::forward(args)...); } template basic_table_core& new_usertype(const std::string& name, constructors ctor, Args&&... args) { usertype utype(ctor, std::forward(args)...); set_usertype(name, utype); return *this; } template basic_table_core& new_simple_usertype(const std::string& name, Args&&... args) { simple_usertype utype(base_t::lua_state(), std::forward(args)...); set_usertype(name, utype); return *this; } template basic_table_core& new_simple_usertype(const std::string& name, Args&&... args) { constructors> ctor{}; return new_simple_usertype(name, ctor, std::forward(args)...); } template basic_table_core& new_simple_usertype(const std::string& name, constructors ctor, Args&&... args) { simple_usertype utype(base_t::lua_state(), ctor, std::forward(args)...); set_usertype(name, utype); return *this; } template simple_usertype create_simple_usertype(Args&&... args) { simple_usertype utype(base_t::lua_state(), std::forward(args)...); return utype; } template simple_usertype create_simple_usertype(Args&&... args) { constructors> ctor{}; return create_simple_usertype(ctor, std::forward(args)...); } template simple_usertype create_simple_usertype(constructors ctor, Args&&... args) { simple_usertype utype(base_t::lua_state(), ctor, std::forward(args)...); return utype; } template table new_enum(const string_view& name, Args&&... args) { table target = create_with(std::forward(args)...); if (read_only) { table x = create_with( meta_function::new_index, detail::fail_on_newindex, meta_function::index, target ); table shim = create_named(name, metatable_key, x); return shim; } else { set(name, target); return target; } } template table new_enum(const string_view& name, std::initializer_list> items) { table target = create(items.size(), 0); for (const auto& kvp : items) { target.set(kvp.first, kvp.second); } if (read_only) { table x = create_with( meta_function::new_index, detail::fail_on_newindex, meta_function::index, target ); table shim = create_named(name, metatable_key, x); return shim; } else { set(name, target); return target; } } template void for_each(Fx&& fx) const { typedef meta::is_invokable)> is_paired; for_each(is_paired(), std::forward(fx)); } size_t size() const { auto pp = stack::push_pop(*this); lua_len(base_t::lua_state(), -1); return stack::pop(base_t::lua_state()); } bool empty() const { return cbegin() == cend(); } template proxy operator[](T&& key) & { return proxy(*this, std::forward(key)); } template proxy operator[](T&& key) const & { return proxy(*this, std::forward(key)); } template proxy operator[](T&& key) && { return proxy(*this, std::forward(key)); } template basic_table_core& set_function(Key&& key, Args&&... args) { set_fx(types(), std::forward(key), std::forward(args)...); return *this; } template basic_table_core& set_function(Key&& key, Args&&... args) { set_fx(types<>(), std::forward(key), std::forward(args)...); return *this; } template basic_table_core& add(Args&&... args) { auto pp = stack::push_pop(*this); (void)detail::swallow{0, (stack::set_ref(base_t::lua_state(), std::forward(args)), 0)... }; return *this; } private: template> void set_fx(types, Key&& key, Fx&& fx) { set_resolved_function(std::forward(key), std::forward(fx)); } template>> = meta::enabler> void set_fx(types<>, Key&& key, Fx&& fx) { set(std::forward(key), std::forward(fx)); } template>> = meta::enabler> void set_fx(types<>, Key&& key, Fx&& fx, Args&&... args) { set(std::forward(key), as_function_reference(std::forward(fx), std::forward(args)...)); } template void set_resolved_function(Key&& key, Args&&... args) { set(std::forward(key), as_function_reference>(std::forward(args)...)); } public: static inline table create(lua_State* L, int narr = 0, int nrec = 0) { lua_createtable(L, narr, nrec); table result(L); lua_pop(L, 1); return result; } template static inline table create(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) { lua_createtable(L, narr, nrec); table result(L); result.set(std::forward(key), std::forward(value), std::forward(args)...); lua_pop(L, 1); return result; } template static inline table create_with(lua_State* L, Args&&... args) { static_assert(sizeof...(Args) % 2 == 0, "You must have an even number of arguments for a key, value ... list."); static const int narr = static_cast(meta::count_2_for_pack::value); return create(L, narr, static_cast((sizeof...(Args) / 2) - narr), std::forward(args)...); } table create(int narr = 0, int nrec = 0) { return create(base_t::lua_state(), narr, nrec); } template table create(int narr, int nrec, Key&& key, Value&& value, Args&&... args) { return create(base_t::lua_state(), narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); } template table create(Name&& name, int narr = 0, int nrec = 0) { table x = create(base_t::lua_state(), narr, nrec); this->set(std::forward(name), x); return x; } template table create(Name&& name, int narr, int nrec, Key&& key, Value&& value, Args&&... args) { table x = create(base_t::lua_state(), narr, nrec, std::forward(key), std::forward(value), std::forward(args)...); this->set(std::forward(name), x); return x; } template table create_with(Args&&... args) { return create_with(base_t::lua_state(), std::forward(args)...); } template table create_named(Name&& name, Args&&... args) { static const int narr = static_cast(meta::count_2_for_pack::value); return create(std::forward(name), narr, (sizeof...(Args) / 2) - narr, std::forward(args)...); } }; } // sol #endif // SOL_TABLE_CORE_HPP