// sol3 // The MIT License (MIT) // Copyright (c) 2013-2019 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 "table_proxy.hpp" #include "stack.hpp" #include "function_types.hpp" #include "table_iterator.hpp" #include "types.hpp" #include "object.hpp" #include "usertype.hpp" #include "optional.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 { private: using base_t = basic_object; friend class state; friend class state_view; template friend class basic_usertype; template friend class basic_metatable; template decltype(auto) tuple_get(int table_index, Keys&&... keys) const { if constexpr (sizeof...(Ret) < 2) { return traverse_get_single_maybe_tuple(table_index, std::forward(keys)...); } else { using multi_ret = decltype(stack::pop>(nullptr)); return multi_ret(traverse_get_single_maybe_tuple(table_index, std::forward(keys))...); } } template decltype(auto) traverse_get_single_tuple(int table_index, std::index_sequence, Key&& key) const { return traverse_get_single(table_index, std::get(std::forward(key))...); } template decltype(auto) traverse_get_single_maybe_tuple(int table_index, Key&& key) const { if constexpr (meta::is_tuple_v>) { return traverse_get_single_tuple( table_index, std::make_index_sequence>>(), std::forward(key)); } else { return traverse_get_single(table_index, std::forward(key)); } } template decltype(auto) traverse_get_single(int table_index, Keys&&... keys) const { constexpr static bool global = top_level && (meta::count_for_to_pack_v<1, meta::is_c_str, meta::unqualified_t...> > 0); if constexpr (meta::is_optional_v>) { int popcount = 0; detail::ref_clean c(base_t::lua_state(), popcount); return traverse_get_deep_optional(popcount, table_index, std::forward(keys)...); } else { detail::clean...>> c(base_t::lua_state()); return traverse_get_deep(table_index, std::forward(keys)...); } } template void tuple_set(std::index_sequence, Pairs&& pairs) { constexpr static bool global = top_level && (meta::count_even_for_pack_v(std::forward(pairs)))>...> > 0); auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); lua_State* L = base_t::lua_state(); (void)table_index; (void)L; void(detail::swallow { (stack::set_field<(top_level), raw>( L, std::get(std::forward(pairs)), std::get(std::forward(pairs)), table_index), 0)... }); } template decltype(auto) traverse_get_deep(int table_index, Key&& key, Keys&&... keys) const { if constexpr (std::is_same_v, create_if_nil_t>) { (void)key; return traverse_get_deep(mode | detail::insert_mode::create_if_nil), T>( table_index, std::forward(keys)...); } else { lua_State* L = base_t::lua_state(); stack::get_field(L, std::forward(key), table_index); if constexpr (sizeof...(Keys) > 0) { if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) { type t = type_of(L, -1); if (t == type::lua_nil || t == type::none) { lua_pop(L, 1); stack::push(L, new_table(0, 0)); } } return traverse_get_deep(lua_gettop(L), std::forward(keys)...); } else { if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) { type t = type_of(L, -1); if ((t == type::lua_nil || t == type::none) && (is_table_like_v)) { lua_pop(L, 1); stack::push(L, new_table(0, 0)); } } return stack::get(L); } } } template decltype(auto) traverse_get_deep_optional(int& popcount, int table_index, Key&& key, Keys&&... keys) const { if constexpr (std::is_same_v, create_if_nil_t>) { constexpr detail::insert_mode new_mode = static_cast(mode | detail::insert_mode::create_if_nil); (void)key; return traverse_get_deep_optional(popcount, table_index, std::forward(keys)...); } else if constexpr (std::is_same_v, update_if_empty_t>) { constexpr detail::insert_mode new_mode = static_cast(mode | detail::insert_mode::update_if_empty); (void)key; return traverse_get_deep_optional(popcount, table_index, std::forward(keys)...); } else if constexpr (std::is_same_v, override_value_t>) { constexpr detail::insert_mode new_mode = static_cast(mode | detail::insert_mode::override_value); (void)key; return traverse_get_deep_optional(popcount, table_index, std::forward(keys)...); } else { if constexpr (sizeof...(Keys) > 0) { lua_State* L = base_t::lua_state(); auto p = stack::probe_get_field(L, std::forward(key), table_index); popcount += p.levels; if (!p.success) { if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) { lua_pop(L, 1); constexpr bool is_seq = meta::count_for_to_pack_v<1, std::is_integral, Keys...> > 0; stack::push(L, new_table(static_cast(is_seq), static_cast(!is_seq))); stack::set_field(L, std::forward(key), stack_reference(L, -1), table_index); } else { return T(nullopt); } } return traverse_get_deep_optional(popcount, lua_gettop(L), std::forward(keys)...); } else { using R = decltype(stack::get(nullptr)); using value_type = typename meta::unqualified_t::value_type; lua_State* L = base_t::lua_state(); auto p = stack::probe_get_field(L, key, table_index); popcount += p.levels; if (!p.success) { if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) { lua_pop(L, 1); stack::push(L, new_table(0, 0)); stack::set_field(L, std::forward(key), stack_reference(L, -1), table_index); if (stack::check(L, lua_gettop(L), no_panic)) { return stack::get(L); } } return R(nullopt); } return stack::get(L); } } } template void traverse_set_deep(int table_index, Key&& key, Keys&&... keys) const { using KeyU = meta::unqualified_t; if constexpr (std::is_same_v) { (void)key; traverse_set_deep(mode | detail::insert_mode::update_if_empty)>( table_index, std::forward(keys)...); } else if constexpr (std::is_same_v) { (void)key; traverse_set_deep(mode | detail::insert_mode::create_if_nil)>( table_index, std::forward(keys)...); } else if constexpr (std::is_same_v) { (void)key; traverse_set_deep(mode | detail::insert_mode::override_value)>( table_index, std::forward(keys)...); } else { lua_State* L = base_t::lua_state(); if constexpr (sizeof...(Keys) == 1) { if constexpr ((mode & detail::insert_mode::update_if_empty) == detail::insert_mode::update_if_empty) { auto p = stack::probe_get_field(L, key, table_index); lua_pop(L, p.levels); if (!p.success) { stack::set_field(L, std::forward(key), std::forward(keys)..., table_index); } } else { stack::set_field(L, std::forward(key), std::forward(keys)..., table_index); } } else { if constexpr (mode != detail::insert_mode::none) { stack::get_field(L, key, table_index); type vt = type_of(L, -1); if constexpr ((mode & detail::insert_mode::update_if_empty) == detail::insert_mode::update_if_empty || (mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) { if (vt == type::lua_nil || vt == type::none) { constexpr bool is_seq = meta::count_for_to_pack_v<1, std::is_integral, Keys...> > 0; lua_pop(L, 1); stack::push(L, new_table(static_cast(is_seq), static_cast(!is_seq))); stack::set_field(L, std::forward(key), stack_reference(L, -1), table_index); } } else { if (vt != type::table) { constexpr bool is_seq = meta::count_for_to_pack_v<1, std::is_integral, Keys...> > 0; lua_pop(L, 1); stack::push(L, new_table(static_cast(is_seq), static_cast(!is_seq))); stack::set_field(L, std::forward(key), stack_reference(L, -1), table_index); } } } else { stack::get_field(L, std::forward(key), table_index); } traverse_set_deep(lua_gettop(L), 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: using iterator = basic_table_iterator; using const_iterator = 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); int table_index = pp.index_of(*this); constructor_handler handler {}; stack::check(lua_state(), table_index, 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); int table_index = pp.index_of(*this); constructor_handler handler {}; stack::check(lua_state(), table_index, 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); int table_index = pp.index_of(*this); constructor_handler handler {}; stack::check(lua_state(), table_index, handler); } #endif // Safety } basic_table_core(lua_nil_t r) noexcept : basic_table_core(detail::no_safety, r) { } iterator begin() const { if (this->get_type() == type::table) { return iterator(*this); } return iterator(); } iterator end() const { return iterator(); } const_iterator cbegin() const { return begin(); } const_iterator cend() const { return end(); } void clear() { auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); stack::clear(lua_state(), table_index); } template decltype(auto) get(Keys&&... keys) const { static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match"); constexpr static bool global = meta::all, meta::is_c_str>...>::value; auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); return tuple_get(table_index, 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 { static_assert(sizeof...(Keys) > 0, "must pass at least 1 key to get"); constexpr static bool global = top_level && (meta::count_for_to_pack_v<1, meta::is_c_str, meta::unqualified_t...> > 0); auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); return traverse_get_single(table_index, std::forward(keys)...); } template basic_table_core& traverse_set(Keys&&... keys) { static_assert(sizeof...(Keys) > 1, "must pass at least 1 key and 1 value to set"); constexpr static bool global = top_level && (meta::count_when_for_to_pack_v...> > 0); auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); lua_State* L = base_t::lua_state(); auto pn = stack::pop_n(L, static_cast(sizeof...(Keys) - 2 - meta::count_for_pack_v...>)); traverse_set_deep(table_index, std::forward(keys)...); return *this; } template basic_table_core& set(Args&&... args) { if constexpr (sizeof...(Args) == 2) { traverse_set(std::forward(args)...); } else { 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"); constexpr static bool global = top_level && (meta::count_for_to_pack_v<1, meta::is_c_str, meta::unqualified_t...> > 0); auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); return tuple_get(table_index, 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 { constexpr static bool global = top_level && (meta::count_for_to_pack_v<1, meta::is_c_str, meta::unqualified_t...> > 0); auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); return traverse_get_single(table_index, std::forward(keys)...); } template basic_table_core& traverse_raw_set(Keys&&... keys) { constexpr static bool global = top_level && (meta::count_for_to_pack_v<1, meta::is_c_str, meta::unqualified_t...> > 0); auto pp = stack::push_pop(*this); lua_State* L = base_t::lua_state(); auto pn = stack::pop_n(L, static_cast(sizeof...(Keys) - 2 - meta::count_for_pack_v...>)); 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 { lua_State* L = base_t::lua_state(); if constexpr (std::is_invocable_v) { auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); stack::push(L, lua_nil); while (lua_next(L, table_index)) { Key key(L, -2); Value value(L, -1); auto pn = stack::pop_n(L, 1); fx(key, value); } } else { auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); stack::push(L, lua_nil); while (lua_next(L, table_index)) { Key key(L, -2); Value value(L, -1); auto pn = stack::pop_n(L, 1); std::pair keyvalue(key, value); fx(keyvalue); } } } size_t size() const { auto pp = stack::push_pop(*this); int table_index = pp.index_of(*this); lua_State* L = base_t::lua_state(); lua_len(L, table_index); return stack::pop(L); } bool empty() const { return cbegin() == cend(); } template auto operator[](T&& key) & { return table_proxy>(*this, std::forward(key)); } template auto operator[](T&& key) const& { return table_proxy>(*this, std::forward(key)); } template auto operator[](T&& key) && { return table_proxy>(std::move(*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); int table_index = pp.index_of(*this); lua_State* L = base_t::lua_state(); (void)detail::swallow { 0, (stack::set_ref(L, std::forward(args), table_index), 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."); constexpr int narr = static_cast(meta::count_odd_for_pack_v); 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_even_for_pack_v); return create(std::forward(name), narr, (sizeof...(Args) / 2) - narr, std::forward(args)...); } }; } // namespace sol #endif // SOL_TABLE_CORE_HPP