// sol3 // The MIT License (MIT) // Copyright (c) 2013-2018 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 "table_iterator.hpp" #include "types.hpp" #include "object_base.hpp" #include "usertype.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"); } } // namespace detail 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 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>(), std::get<0>(std::forward(keys))), traverse_get_optional(meta::is_optional>(), std::get<1>(std::forward(keys))), traverse_get_optional(meta::is_optional>(), std::get(std::forward(keys)))...); } template decltype(auto) tuple_get(types, std::index_sequence, Keys&& keys) const { return traverse_get_optional(meta::is_optional>(), std::get(std::forward(keys))); } template void tuple_set(std::index_sequence, Pairs&& pairs) { auto pp = stack::push_pop < top_level && (is_global(std::forward(pairs)))...>::value) > (*this); void(detail::swallow{ (stack::set_field(base_t::lua_state(), std::get(std::forward(pairs)), std::get(std::forward(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_nil_t n) : base_t(n) { } 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>, meta::neg>>, is_lua_reference>> = meta::enabler> basic_table_core(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward(r)) { } template >> = meta::enabler> basic_table_core(detail::no_safety_tag, lua_State* L, T&& r) noexcept : base_t(L, std::forward(r)) { } public: typedef basic_table_iterator iterator; typedef iterator const_iterator; using base_t::lua_state; 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) : base_t(L, std::forward(r)) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES auto pp = stack::push_pop(*this); constructor_handler handler{}; stack::check(lua_state(), -1, handler); #endif // Safety } basic_table_core(lua_State* L, const new_table& nt) : base_t(L, -stack::push(L, nt)) { if (!is_stack_based>::value) { lua_pop(L, 1); } } basic_table_core(lua_State* L, int index = -1) : basic_table_core(detail::no_safety, L, index) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES 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) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES auto pp = stack::push_pop(*this); constructor_handler handler{}; stack::check(lua_state(), -1, handler); #endif // Safety } template , basic_table_core>>, meta::neg>, meta::neg>>, is_lua_reference>> = meta::enabler> basic_table_core(T&& r) noexcept : basic_table_core(detail::no_safety, std::forward(r)) { #if defined(SOL_SAFE_REFERENCES) && SOL_SAFE_REFERENCES if (!is_table>::value) { auto pp = stack::push_pop(*this); constructor_handler handler{}; stack::check(base_t::lua_state(), -1, handler); } #endif // Safety } basic_table_core(lua_nil_t r) noexcept : basic_table_core(detail::no_safety, r) { } 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 usertype new_usertype(Key&& key); template usertype new_usertype(Key&& key, automagic_enrollments enrollment); template , automagic_enrollments>>> usertype new_usertype(Key&& key, Arg&& arg, Args&&... args); 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(static_cast(items.size()), static_cast(0)); for (const auto& kvp : items) { target.set(kvp.first, kvp.second); } if constexpr (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 { if constexpr (std::is_invocable_v) { auto pp = stack::push_pop(*this); stack::push(base_t::lua_state(), lua_nil); while (lua_next(base_t::lua_state(), -2)) { Key key(base_t::lua_state(), -2); Value value(base_t::lua_state(), -1); auto pn = stack::pop_n(base_t::lua_state(), 1); fx(key, value); } } else { auto pp = stack::push_pop(*this); stack::push(base_t::lua_state(), lua_nil); while (lua_next(base_t::lua_state(), -2)) { Key key(base_t::lua_state(), -2); Value value(base_t::lua_state(), -1); auto pn = stack::pop_n(base_t::lua_state(), 1); std::pair keyvalue(key, value); fx(keyvalue); } } } 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 , overload_set>> = meta::enabler> void set_fx(types<>, Key&& key, Fx&& fx) { set(std::forward(key), std::forward(fx)); } template , overload_set>> = 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)...); } }; } // namespace sol #endif // SOL_TABLE_CORE_HPP