// sol2 // The MIT License (MIT) // Copyright (c) 2013-2022 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_USERTYPE_CONTAINER_HPP #define SOL_USERTYPE_CONTAINER_HPP #include #include #include namespace sol { template struct usertype_container; namespace container_detail { template struct has_clear_test { private: template static meta::sfinae_yes_t test(decltype(&C::clear)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_empty_test { private: template static meta::sfinae_yes_t test(decltype(&C::empty)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_erase_after_test { private: template static meta::sfinae_yes_t test( decltype(std::declval().erase_after(std::declval>()))*); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_find_test { private: template static meta::sfinae_yes_t test(decltype(std::declval().find(std::declval>()))*); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_find_test::value>> { private: template static meta::sfinae_yes_t test(decltype(std::declval().find(std::declval>()))*); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_erase_test { private: template static meta::sfinae_yes_t test(decltype(std::declval().erase(std::declval()))*); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_erase_key_test { private: template static meta::sfinae_yes_t test(decltype(std::declval().erase(std::declval()))*); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_find_test { private: template static meta::sfinae_yes_t test(decltype(&C::find)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_index_of_test { private: template static meta::sfinae_yes_t test(decltype(&C::index_of)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_insert_test { private: template static meta::sfinae_yes_t test(decltype(&C::insert)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_erase_test { private: template static meta::sfinae_yes_t test(decltype(&C::erase)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_index_set_test { private: template static meta::sfinae_yes_t test(decltype(&C::index_set)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_index_get_test { private: template static meta::sfinae_yes_t test(decltype(&C::index_get)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_set_test { private: template static meta::sfinae_yes_t test(decltype(&C::set)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_get_test { private: template static meta::sfinae_yes_t test(decltype(&C::get)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_at_test { private: template static meta::sfinae_yes_t test(decltype(&C::at)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_pairs_test { private: template static meta::sfinae_yes_t test(decltype(&C::pairs)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_ipairs_test { private: template static meta::sfinae_yes_t test(decltype(&C::ipairs)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_next_test { private: template static meta::sfinae_yes_t test(decltype(&C::next)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_add_test { private: template static meta::sfinae_yes_t test(decltype(&C::add)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template struct has_traits_size_test { private: template static meta::sfinae_yes_t test(decltype(&C::size)); template static meta::sfinae_no_t test(...); public: static constexpr bool value = std::is_same_v(0)), meta::sfinae_yes_t>; }; template using has_clear = meta::boolean::value>; template using has_empty = meta::boolean::value>; template using has_find = meta::boolean::value>; template using has_erase = meta::boolean::value>; template using has_erase_key = meta::boolean::value>; template using has_erase_after = meta::boolean::value>; template using has_traits_get = meta::boolean::value>; template using has_traits_at = meta::boolean::value>; template using has_traits_set = meta::boolean::value>; template using has_traits_index_get = meta::boolean::value>; template using has_traits_index_set = meta::boolean::value>; template using has_traits_pairs = meta::boolean::value>; template using has_traits_ipairs = meta::boolean::value>; template using has_traits_next = meta::boolean::value>; template using has_traits_add = meta::boolean::value>; template using has_traits_size = meta::boolean::value>; template using has_traits_clear = has_clear; template using has_traits_empty = has_empty; template using has_traits_find = meta::boolean::value>; template using has_traits_index_of = meta::boolean::value>; template using has_traits_insert = meta::boolean::value>; template using has_traits_erase = meta::boolean::value>; template struct is_forced_container : is_container { }; template struct is_forced_container> : std::true_type { }; template struct container_decay { typedef T type; }; template struct container_decay> { typedef T type; }; template using container_decay_t = typename container_decay>::type; template decltype(auto) get_key(std::false_type, T&& t) { return std::forward(t); } template decltype(auto) get_key(std::true_type, T&& t) { return t.first; } template decltype(auto) get_value(std::false_type, T&& t) { return std::forward(t); } template decltype(auto) get_value(std::true_type, T&& t) { return t.second; } template struct usertype_container_default { private: typedef std::remove_pointer_t> T; public: typedef lua_nil_t iterator; typedef iterator sentinel; typedef lua_nil_t value_type; static int at(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'at(index)' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int get(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'get(key)' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int index_get(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'container[key]' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int set(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'set(key, value)' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int index_set(lua_State* L_) { return luaL_error( L_, "sol: cannot call 'container[key] = value' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int add(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'add' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int insert(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'insert' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int find(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'find' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int index_of(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'index_of' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int size(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int clear(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'clear' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int empty(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'empty' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int erase(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'erase' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int next(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'next' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int pairs(lua_State* L_) { return luaL_error(L_, "sol: cannot call '__pairs/pairs' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static int ipairs(lua_State* L_) { return luaL_error(L_, "sol: cannot call '__ipairs' on type '%s': it is not recognized as a container", detail::demangle().c_str()); } static iterator begin(lua_State* L_, T&) { luaL_error(L_, "sol: cannot call 'being' on type '%s': it is not recognized as a container", detail::demangle().c_str()); return lua_nil; } static sentinel end(lua_State* L_, T&) { luaL_error(L_, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle().c_str()); return lua_nil; } }; template struct usertype_container_default>, meta::has_value_type>>, meta::has_iterator>>>::value>> { private: using T = std::remove_pointer_t>>; private: using deferred_uc = usertype_container; using is_associative = meta::is_associative; using is_lookup = meta::is_lookup; using is_ordered = meta::is_ordered; using is_matched_lookup = meta::is_matched_lookup; using iterator = typename T::iterator; using sentinel = meta::sentinel_or_t; using value_type = typename T::value_type; typedef meta::conditional_t, meta::conditional_t>> KV; typedef typename KV::first_type K; typedef typename KV::second_type V; typedef meta::conditional_t next_K; typedef decltype(*std::declval()) iterator_return; typedef meta::conditional_t, meta::conditional_t> captured_type; typedef typename meta::iterator_tag::type iterator_category; typedef std::is_same is_input_iterator; typedef meta::conditional_t()))> push_type; typedef std::is_copy_assignable is_copyable; typedef meta::neg, std::is_const>, meta::neg>> is_writable; typedef meta::unqualified_t>()))> key_type; typedef meta::all, meta::neg>> is_linear_integral; struct iter : detail::ebco, detail::ebco { using it_base = detail::ebco; using sen_base = detail::ebco; main_reference keep_alive; std::size_t index; iter(lua_State* L_, int stack_index_, iterator it_, sentinel sen_) noexcept : it_base(std::move(it_)), sen_base(std::move(sen_)), keep_alive(L_, stack_index_), index(0) { } iterator& it() noexcept { return it_base::value(); } const iterator& it() const noexcept { return it_base::value(); } sentinel& sen() noexcept { return sen_base::value(); } const sentinel& sen() const noexcept { return sen_base::value(); } }; static auto& get_src(lua_State* L_) { #if SOL_IS_ON(SOL_SAFE_USERTYPE) auto p = stack::unqualified_check_get(L_, 1); if (!p) { luaL_error(L_, "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", detail::demangle().c_str()); } if (p.value() == nullptr) { luaL_error( L_, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type)", detail::demangle().c_str()); } return *p.value(); #else return stack::unqualified_get(L_, 1); #endif // Safe getting with error } static detail::error_result at_category(std::input_iterator_tag, lua_State* L_, T& self, std::ptrdiff_t pos) { pos += deferred_uc::index_adjustment(L_, self); if (pos < 0) { return stack::push(L_, lua_nil); } auto it = deferred_uc::begin(L_, self); auto e = deferred_uc::end(L_, self); if (it == e) { return stack::push(L_, lua_nil); } while (pos > 0) { --pos; ++it; if (it == e) { return stack::push(L_, lua_nil); } } return get_associative(is_associative(), L_, it); } static detail::error_result at_category(std::random_access_iterator_tag, lua_State* L_, T& self, std::ptrdiff_t pos) { std::ptrdiff_t len = static_cast(size_start(L_, self)); pos += deferred_uc::index_adjustment(L_, self); if (pos < 0 || pos >= len) { return stack::push(L_, lua_nil); } auto it = std::next(deferred_uc::begin(L_, self), pos); return get_associative(is_associative(), L_, it); } static detail::error_result at_start(lua_State* L_, T& self, std::ptrdiff_t pos) { return at_category(iterator_category(), L_, self, pos); } template static detail::error_result get_associative(std::true_type, lua_State* L_, Iter& it) { decltype(auto) v = *it; return stack::stack_detail::push_reference(L_, detail::deref_move_only(v.second)); } template static detail::error_result get_associative(std::false_type, lua_State* L_, Iter& it) { return stack::stack_detail::push_reference(L_, detail::deref_move_only(*it)); } static detail::error_result get_category(std::input_iterator_tag, lua_State* L_, T& self, K& key) { key = static_cast(key + deferred_uc::index_adjustment(L_, self)); if (key < 0) { return stack::push(L_, lua_nil); } auto it = deferred_uc::begin(L_, self); auto e = deferred_uc::end(L_, self); if (it == e) { return stack::push(L_, lua_nil); } while (key > 0) { --key; ++it; if (it == e) { return stack::push(L_, lua_nil); } } return get_associative(is_associative(), L_, it); } static detail::error_result get_category(std::random_access_iterator_tag, lua_State* L_, T& self, K& key) { std::ptrdiff_t len = static_cast(size_start(L_, self)); key = static_cast(static_cast(key) + deferred_uc::index_adjustment(L_, self)); if (key < 0 || key >= len) { return stack::push(L_, lua_nil); } auto it = std::next(deferred_uc::begin(L_, self), key); return get_associative(is_associative(), L_, it); } static detail::error_result get_it(std::true_type, lua_State* L_, T& self, K& key) { return get_category(iterator_category(), L_, self, key); } static detail::error_result get_comparative(std::true_type, lua_State* L_, T& self, K& key) { auto fx = [&](const value_type& r) -> bool { return key == get_key(is_associative(), r); }; auto e = deferred_uc::end(L_, self); auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx)); if (it == e) { return stack::push(L_, lua_nil); } return get_associative(is_associative(), L_, it); } static detail::error_result get_comparative(std::false_type, lua_State*, T&, K&) { return detail::error_result("cannot get this key on '%s': no suitable way to increment iterator and compare to key value '%s'", detail::demangle().data(), detail::demangle().data()); } static detail::error_result get_it(std::false_type, lua_State* L_, T& self, K& key) { return get_comparative(meta::supports_op_equal(), L_, self, key); } static detail::error_result set_associative(std::true_type, iterator& it, stack_object value) { decltype(auto) v = *it; v.second = value.as(); return {}; } static detail::error_result set_associative(std::false_type, iterator& it, stack_object value) { decltype(auto) v = *it; v = value.as(); return {}; } static detail::error_result set_writable(std::true_type, lua_State*, T&, iterator& it, stack_object value) { return set_associative(is_associative(), it, std::move(value)); } static detail::error_result set_writable(std::false_type, lua_State*, T&, iterator&, stack_object) { return detail::error_result( "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle().data()); } static detail::error_result set_category(std::input_iterator_tag, lua_State* L_, T& self, stack_object okey, stack_object value) { decltype(auto) key = okey.as(); key = static_cast(static_cast(key) + deferred_uc::index_adjustment(L_, self)); auto e = deferred_uc::end(L_, self); auto it = deferred_uc::begin(L_, self); auto backit = it; for (; key > 0 && it != e; --key, ++it) { backit = it; } if (it == e) { if (key == 0) { return add_copyable(is_copyable(), L_, self, std::move(value), meta::has_insert_after::value ? backit : it); } return detail::error_result("out of bounds (too big) for set on '%s'", detail::demangle().c_str()); } return set_writable(is_writable(), L_, self, it, std::move(value)); } static detail::error_result set_category(std::random_access_iterator_tag, lua_State* L_, T& self, stack_object okey, stack_object value) { decltype(auto) key = okey.as(); key = static_cast(static_cast(key) + deferred_uc::index_adjustment(L_, self)); if (key < 0) { return detail::error_result("sol: out of bounds (too small) for set on '%s'", detail::demangle().c_str()); } std::ptrdiff_t len = static_cast(size_start(L_, self)); if (key == len) { return add_copyable(is_copyable(), L_, self, std::move(value)); } else if (key >= len) { return detail::error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle().c_str()); } auto it = std::next(deferred_uc::begin(L_, self), key); return set_writable(is_writable(), L_, self, it, std::move(value)); } static detail::error_result set_comparative(std::true_type, lua_State* L_, T& self, stack_object okey, stack_object value) { decltype(auto) key = okey.as(); if (!is_writable::value) { return detail::error_result( "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle().data()); } auto fx = [&](const value_type& r) -> bool { return key == get_key(is_associative(), r); }; auto e = deferred_uc::end(L_, self); auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx)); if (it == e) { return {}; } return set_writable(is_writable(), L_, self, it, std::move(value)); } static detail::error_result set_comparative(std::false_type, lua_State*, T&, stack_object, stack_object) { return detail::error_result("cannot set this value on '%s': no suitable way to increment iterator or compare to '%s' key", detail::demangle().data(), detail::demangle().data()); } template static detail::error_result set_associative_insert(std::true_type, lua_State*, T& self, Iter& it, K& key, stack_object value) { if constexpr (meta::has_insert_with_iterator::value) { self.insert(it, value_type(key, value.as())); return {}; } else if constexpr (meta::has_insert::value) { self.insert(value_type(key, value.as())); return {}; } else { (void)self; (void)it; (void)key; return detail::error_result( "cannot call 'set' on '%s': there is no 'insert' function on this associative type", detail::demangle().c_str()); } } template static detail::error_result set_associative_insert(std::false_type, lua_State*, T& self, Iter& it, K& key, stack_object) { if constexpr (meta::has_insert_with_iterator::value) { self.insert(it, key); return {}; } else if constexpr (meta::has_insert::value) { self.insert(key); return {}; } else { (void)self; (void)it; (void)key; return detail::error_result( "cannot call 'set' on '%s': there is no 'insert' function on this non-associative type", detail::demangle().c_str()); } } static detail::error_result set_associative_find(std::true_type, lua_State* L_, T& self, stack_object okey, stack_object value) { decltype(auto) key = okey.as(); auto it = self.find(key); if (it == deferred_uc::end(L_, self)) { return set_associative_insert(is_associative(), L_, self, it, key, std::move(value)); } return set_writable(is_writable(), L_, self, it, std::move(value)); } static detail::error_result set_associative_find(std::false_type, lua_State* L_, T& self, stack_object key, stack_object value) { return set_comparative(meta::supports_op_equal(), L_, self, std::move(key), std::move(value)); } static detail::error_result set_it(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) { return set_category(iterator_category(), L_, self, std::move(key), std::move(value)); } static detail::error_result set_it(std::false_type, lua_State* L_, T& self, stack_object key, stack_object value) { return set_associative_find(meta::all, meta::any>(), L_, self, std::move(key), std::move(value)); } template static detail::error_result find_has_associative_lookup(std::true_type, lua_State* L_, T& self) { if constexpr (!is_ordered::value && idx_of) { (void)L_; (void)self; return detail::error_result("cannot perform an 'index_of': '%s's is not an ordered container", detail::demangle().data()); } else { decltype(auto) key = stack::unqualified_get(L_, 2); auto it = self.find(key); if (it == deferred_uc::end(L_, self)) { return stack::push(L_, lua_nil); } if constexpr (idx_of) { auto dist = std::distance(deferred_uc::begin(L_, self), it); dist -= deferred_uc::index_adjustment(L_, self); return stack::push(L_, dist); } else { return get_associative(is_associative(), L_, it); } } } template static detail::error_result find_has_associative_lookup(std::false_type, lua_State* L_, T& self) { if constexpr (!is_ordered::value && idx_of) { (void)L_; (void)self; return detail::error_result("cannot perform an 'index_of': '%s's is not an ordered container", detail::demangle().data()); } else { decltype(auto) value = stack::unqualified_get(L_, 2); auto it = self.find(value); if (it == deferred_uc::end(L_, self)) { return stack::push(L_, lua_nil); } if constexpr (idx_of) { auto dist = std::distance(deferred_uc::begin(L_, self), it); dist -= deferred_uc::index_adjustment(L_, self); return stack::push(L_, dist); } else { return get_associative(is_associative(), L_, it); } } } template static detail::error_result find_has(std::true_type, lua_State* L_, T& self) { return find_has_associative_lookup(meta::any(), L_, self); } template static detail::error_result find_associative_lookup(std::true_type, lua_State* L_, T&, Iter& it, std::size_t) { return get_associative(is_associative(), L_, it); } template static detail::error_result find_associative_lookup(std::false_type, lua_State* L_, T& self, Iter&, std::size_t idx) { idx = static_cast(static_cast(idx) - deferred_uc::index_adjustment(L_, self)); return stack::push(L_, idx); } template static detail::error_result find_comparative(std::false_type, lua_State*, T&) { return detail::error_result("cannot call 'find' on '%s': there is no 'find' function and the value_type is not equality comparable", detail::demangle().c_str()); } template static detail::error_result find_comparative(std::true_type, lua_State* L_, T& self) { decltype(auto) value = stack::unqualified_get(L_, 2); auto it = deferred_uc::begin(L_, self); auto e = deferred_uc::end(L_, self); std::size_t idx = 0; for (;; ++it, ++idx) { if (it == e) { return stack::push(L_, lua_nil); } if (value == get_value(is_associative(), *it)) { break; } } return find_associative_lookup(meta::all, meta::any>(), L_, self, it, idx); } template static detail::error_result find_has(std::false_type, lua_State* L_, T& self) { return find_comparative(meta::supports_op_equal(), L_, self); } template static detail::error_result add_insert_after(std::false_type, lua_State* L_, T& self, stack_object value, Iter&) { return add_insert_after(std::false_type(), L_, self, value); } static detail::error_result add_insert_after(std::false_type, lua_State*, T&, stack_object) { return detail::error_result("cannot call 'add' on type '%s': no suitable insert/push_back C++ functions", detail::demangle().data()); } template static detail::error_result add_insert_after(std::true_type, lua_State*, T& self, stack_object value, Iter& pos) { self.insert_after(pos, value.as()); return {}; } static detail::error_result add_insert_after(std::true_type, lua_State* L_, T& self, stack_object value) { auto backit = self.before_begin(); { auto e = deferred_uc::end(L_, self); for (auto it = deferred_uc::begin(L_, self); it != e; ++backit, ++it) { } } return add_insert_after(std::true_type(), L_, self, value, backit); } template static detail::error_result add_insert(std::true_type, lua_State*, T& self, stack_object value, Iter& pos) { self.insert(pos, value.as()); return {}; } static detail::error_result add_insert(std::true_type, lua_State* L_, T& self, stack_object value) { auto pos = deferred_uc::end(L_, self); return add_insert(std::true_type(), L_, self, value, pos); } template static detail::error_result add_insert(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) { return add_insert_after(meta::has_insert_after(), L_, self, std::move(value), pos); } static detail::error_result add_insert(std::false_type, lua_State* L_, T& self, stack_object value) { return add_insert_after(meta::has_insert_after(), L_, self, std::move(value)); } template static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value, Iter&) { self.push_back(value.as()); return {}; } static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value) { self.push_back(value.as()); return {}; } template static detail::error_result add_push_back(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) { return add_insert( std::integral_constant < bool, meta::has_insert::value || meta::has_insert_with_iterator::value > (), L_, self, value, pos); } static detail::error_result add_push_back(std::false_type, lua_State* L_, T& self, stack_object value) { return add_insert( std::integral_constant < bool, meta::has_insert::value || meta::has_insert_with_iterator::value > (), L_, self, value); } template static detail::error_result add_associative(std::true_type, lua_State* L_, T& self, stack_object key, Iter& pos) { if constexpr (meta::has_insert_with_iterator::value) { self.insert(pos, value_type(key.as(), stack::unqualified_get(L_, 3))); return {}; } else if constexpr (meta::has_insert::value) { self.insert(value_type(key.as(), stack::unqualified_get(L_, 3))); return {}; } else { (void)L_; (void)self; (void)key; (void)pos; return detail::error_result( "cannot call 'insert' on '%s': there is no 'insert' function on this associative type", detail::demangle().c_str()); } } static detail::error_result add_associative(std::true_type, lua_State* L_, T& self, stack_object key) { auto pos = deferred_uc::end(L_, self); return add_associative(std::true_type(), L_, self, std::move(key), pos); } template static detail::error_result add_associative(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) { return add_push_back(meta::has_push_back(), L_, self, value, pos); } static detail::error_result add_associative(std::false_type, lua_State* L_, T& self, stack_object value) { return add_push_back(meta::has_push_back(), L_, self, value); } template static detail::error_result add_copyable(std::true_type, lua_State* L_, T& self, stack_object value, Iter& pos) { return add_associative(is_associative(), L_, self, std::move(value), pos); } static detail::error_result add_copyable(std::true_type, lua_State* L_, T& self, stack_object value) { return add_associative(is_associative(), L_, self, value); } template static detail::error_result add_copyable(std::false_type, lua_State* L_, T& self, stack_object value, Iter&) { return add_copyable(std::false_type(), L_, self, std::move(value)); } static detail::error_result add_copyable(std::false_type, lua_State*, T&, stack_object) { return detail::error_result("cannot call 'add' on '%s': value_type is non-copyable", detail::demangle().data()); } static detail::error_result insert_lookup(std::true_type, lua_State* L_, T& self, stack_object, stack_object value) { // TODO: should we warn or error about someone calling insert on an ordered / lookup container with no associativity? return add_copyable(std::true_type(), L_, self, std::move(value)); } static detail::error_result insert_lookup(std::false_type, lua_State* L_, T& self, stack_object where, stack_object value) { auto it = deferred_uc::begin(L_, self); auto key = where.as(); key = static_cast(static_cast(key) + deferred_uc::index_adjustment(L_, self)); std::advance(it, key); self.insert(it, value.as()); return {}; } static detail::error_result insert_after_has(std::true_type, lua_State* L_, T& self, stack_object where, stack_object value) { auto key = where.as(); auto backit = self.before_begin(); { key = static_cast(static_cast(key) + deferred_uc::index_adjustment(L_, self)); auto e = deferred_uc::end(L_, self); for (auto it = deferred_uc::begin(L_, self); key > 0; ++backit, ++it, --key) { if (backit == e) { return detail::error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle().c_str()); } } } self.insert_after(backit, value.as()); return {}; } static detail::error_result insert_after_has(std::false_type, lua_State*, T&, stack_object, stack_object) { return detail::error_result( "cannot call 'insert' on '%s': no suitable or similar functionality detected on this container", detail::demangle().data()); } static detail::error_result insert_has(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) { return insert_lookup(meta::any(), L_, self, std::move(key), std::move(value)); } static detail::error_result insert_has(std::false_type, lua_State* L_, T& self, stack_object where, stack_object value) { return insert_after_has(meta::has_insert_after(), L_, self, where, value); } static detail::error_result insert_copyable(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) { return insert_has(std::integral_constant < bool, meta::has_insert::value || meta::has_insert_with_iterator::value > (), L_, self, std::move(key), std::move(value)); } static detail::error_result insert_copyable(std::false_type, lua_State*, T&, stack_object, stack_object) { return detail::error_result("cannot call 'insert' on '%s': value_type is non-copyable", detail::demangle().data()); } static detail::error_result erase_integral(std::true_type, lua_State* L_, T& self, K& key) { auto it = deferred_uc::begin(L_, self); key = (static_cast(key) + deferred_uc::index_adjustment(L_, self)); std::advance(it, key); self.erase(it); return {}; } static detail::error_result erase_integral(std::false_type, lua_State* L_, T& self, const K& key) { auto fx = [&](const value_type& r) -> bool { return key == r; }; auto e = deferred_uc::end(L_, self); auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx)); if (it == e) { return {}; } self.erase(it); return {}; } static detail::error_result erase_associative_lookup(std::true_type, lua_State*, T& self, const K& key) { self.erase(key); return {}; } static detail::error_result erase_associative_lookup(std::false_type, lua_State* L_, T& self, K& key) { return erase_integral(std::is_integral(), L_, self, key); } static detail::error_result erase_after_has(std::true_type, lua_State* L_, T& self, K& key) { auto backit = self.before_begin(); { key = static_cast(static_cast(key) + deferred_uc::index_adjustment(L_, self)); auto e = deferred_uc::end(L_, self); for (auto it = deferred_uc::begin(L_, self); key > 0; ++backit, ++it, --key) { if (backit == e) { return detail::error_result("sol: out of bounds for erase on '%s'", detail::demangle().c_str()); } } } self.erase_after(backit); return {}; } static detail::error_result erase_after_has(std::false_type, lua_State*, T&, const K&) { return detail::error_result("sol: cannot call erase on '%s'", detail::demangle().c_str()); } static detail::error_result erase_key_has(std::true_type, lua_State* L_, T& self, K& key) { return erase_associative_lookup(meta::any(), L_, self, key); } static detail::error_result erase_key_has(std::false_type, lua_State* L_, T& self, K& key) { return erase_after_has(has_erase_after(), L_, self, key); } static detail::error_result erase_has(std::true_type, lua_State* L_, T& self, K& key) { return erase_associative_lookup(meta::any(), L_, self, key); } static detail::error_result erase_has(std::false_type, lua_State* L_, T& self, K& key) { return erase_key_has(has_erase_key(), L_, self, key); } static auto size_has(std::false_type, lua_State* L_, T& self) { return std::distance(deferred_uc::begin(L_, self), deferred_uc::end(L_, self)); } static auto size_has(std::true_type, lua_State*, T& self) { return self.size(); } static void clear_has(std::true_type, lua_State*, T& self) { self.clear(); } static void clear_has(std::false_type, lua_State* L_, T&) { luaL_error(L_, "sol: cannot call clear on '%s'", detail::demangle().c_str()); } static bool empty_has(std::true_type, lua_State*, T& self) { return self.empty(); } static bool empty_has(std::false_type, lua_State* L_, T& self) { return deferred_uc::begin(L_, self) == deferred_uc::end(L_, self); } static detail::error_result get_associative_find(std::true_type, lua_State* L_, T& self, K& key) { auto it = self.find(key); if (it == deferred_uc::end(L_, self)) { stack::push(L_, lua_nil); return {}; } return get_associative(std::true_type(), L_, it); } static detail::error_result get_associative_find(std::false_type, lua_State* L_, T& self, K& key) { return get_it(is_linear_integral(), L_, self, key); } static detail::error_result get_start(lua_State* L_, T& self, K& key) { return get_associative_find(std::integral_constant < bool, is_associative::value&& has_find::value > (), L_, self, key); } static detail::error_result set_start(lua_State* L_, T& self, stack_object key, stack_object value) { return set_it(is_linear_integral(), L_, self, std::move(key), std::move(value)); } static std::size_t size_start(lua_State* L_, T& self) { return static_cast(size_has(meta::has_size(), L_, self)); } static void clear_start(lua_State* L_, T& self) { clear_has(has_clear(), L_, self); } static bool empty_start(lua_State* L_, T& self) { return empty_has(has_empty(), L_, self); } static detail::error_result erase_start(lua_State* L_, T& self, K& key) { return erase_has(has_erase(), L_, self, key); } template static int next_associative(std::true_type, lua_State* L_) { iter& i = stack::unqualified_get>(L_, 1); auto& it = i.it; auto& end = i.end; if (it == end) { return stack::push(L_, lua_nil); } int p; if constexpr (ip) { ++i.index; p = stack::push_reference(L_, i.index); } else { p = stack::push_reference(L_, it->first); } p += stack::stack_detail::push_reference(L_, detail::deref_move_only(it->second)); std::advance(it, 1); return p; } template static int next_associative(std::false_type, lua_State* L_) { iter& i = stack::unqualified_get>(L_, 1); auto& it = i.it(); auto& end = i.sen(); next_K k = stack::unqualified_get(L_, 2); if (it == end) { return stack::push(L_, lua_nil); } int p; if constexpr (std::is_integral_v) { p = stack::push_reference(L_, k + 1); } else { p = stack::stack_detail::push_reference(L_, k + 1); } p += stack::stack_detail::push_reference(L_, detail::deref_move_only(*it)); std::advance(it, 1); return p; } template static int next_iter(lua_State* L_) { typedef meta::any>> is_assoc; return next_associative(is_assoc(), L_); } template static int pairs_associative(std::true_type, lua_State* L_) { auto& src = get_src(L_); stack::push(L_, next_iter); stack::push>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::begin(L_, src)); stack::push(L_, lua_nil); return 3; } template static int pairs_associative(std::false_type, lua_State* L_) { auto& src = get_src(L_); stack::push(L_, next_iter); stack::push>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::end(L_, src)); stack::push(L_, 0); return 3; } public: static int at(lua_State* L_) { auto& self = get_src(L_); detail::error_result er; { std::ptrdiff_t pos = stack::unqualified_get(L_, 2); er = at_start(L_, self, pos); } return handle_errors(L_, er); } static int get(lua_State* L_) { auto& self = get_src(L_); detail::error_result er; { decltype(auto) key = stack::unqualified_get(L_); er = get_start(L_, self, key); } return handle_errors(L_, er); } static int index_get(lua_State* L_) { return get(L_); } static int set(lua_State* L_) { stack_object value = stack_object(L_, raw_index(3)); if constexpr (is_linear_integral::value) { // for non-associative containers, // erasure only happens if it is the // last index in the container auto key = stack::get(L_, 2); auto self_size = deferred_uc::size(L_); if (key == static_cast(self_size)) { if (type_of(L_, 3) == type::lua_nil) { return erase(L_); } } } else { if (type_of(L_, 3) == type::lua_nil) { return erase(L_); } } auto& self = get_src(L_); detail::error_result er = set_start(L_, self, stack_object(L_, raw_index(2)), std::move(value)); return handle_errors(L_, er); } static int index_set(lua_State* L_) { return set(L_); } static int add(lua_State* L_) { auto& self = get_src(L_); detail::error_result er = add_copyable(is_copyable(), L_, self, stack_object(L_, raw_index(2))); return handle_errors(L_, er); } static int insert(lua_State* L_) { auto& self = get_src(L_); detail::error_result er = insert_copyable(is_copyable(), L_, self, stack_object(L_, raw_index(2)), stack_object(L_, raw_index(3))); return handle_errors(L_, er); } static int find(lua_State* L_) { auto& self = get_src(L_); detail::error_result er = find_has(has_find(), L_, self); return handle_errors(L_, er); } static int index_of(lua_State* L_) { auto& self = get_src(L_); detail::error_result er = find_has(has_find(), L_, self); return handle_errors(L_, er); } static iterator begin(lua_State*, T& self) { if constexpr (meta::has_begin_end_v) { return self.begin(); } else { using std::begin; return begin(self); } } static sentinel end(lua_State*, T& self) { if constexpr (meta::has_begin_end_v) { return self.end(); } else { using std::end; return end(self); } } static int size(lua_State* L_) { auto& self = get_src(L_); std::size_t r = size_start(L_, self); return stack::push(L_, r); } static int clear(lua_State* L_) { auto& self = get_src(L_); clear_start(L_, self); return 0; } static int erase(lua_State* L_) { auto& self = get_src(L_); detail::error_result er; { decltype(auto) key = stack::unqualified_get(L_, 2); er = erase_start(L_, self, key); } return handle_errors(L_, er); } static int empty(lua_State* L_) { auto& self = get_src(L_); return stack::push(L_, empty_start(L_, self)); } static std::ptrdiff_t index_adjustment(lua_State*, T&) { return static_cast((SOL_CONTAINER_START_INDEX_I_) == 0 ? 0 : -(SOL_CONTAINER_START_INDEX_I_)); } static int pairs(lua_State* L_) { typedef meta::any>> is_assoc; return pairs_associative(is_assoc(), L_); } static int ipairs(lua_State* L_) { typedef meta::any>> is_assoc; return pairs_associative(is_assoc(), L_); } static int next(lua_State* L_) { return stack::push(L_, next_iter); } }; template struct usertype_container_default>>::value>> { private: typedef std::remove_pointer_t> T; typedef usertype_container deferred_uc; public: typedef std::remove_extent_t value_type; typedef value_type* iterator; typedef iterator sentinel; private: struct iter : detail::ebco, detail::ebco { using it_base = detail::ebco; using sen_base = detail::ebco; reference keep_alive; iter(lua_State* L_, int stack_index_, iterator it_, sentinel sen_) noexcept : it_base(std::move(it_)), sen_base(std::move(sen_)), keep_alive(sol::main_thread(L_, L_), stack_index_) { } iterator& it() noexcept { return it_base::value(); } const iterator& it() const noexcept { return it_base::value(); } sentinel& sen() noexcept { return sen_base::value(); } const sentinel& sen() const noexcept { return sen_base::value(); } }; static auto& get_src(lua_State* L_) { auto p = stack::unqualified_check_get(L_, 1); #if SOL_IS_ON(SOL_SAFE_USERTYPE) if (!p) { luaL_error(L_, "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)", detail::demangle().c_str()); } if (p.value() == nullptr) { luaL_error( L_, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type)", detail::demangle().c_str()); } #endif // Safe getting with error return *p.value(); } static int find(std::true_type, lua_State* L_) { T& self = get_src(L_); decltype(auto) value = stack::unqualified_get(L_, 2); std::size_t N = std::extent::value; for (std::size_t idx = 0; idx < N; ++idx) { using v_t = std::add_const_t; v_t v = self[idx]; if (v == value) { idx = static_cast(static_cast(idx) - deferred_uc::index_adjustment(L_, self)); return stack::push(L_, idx); } } return stack::push(L_, lua_nil); } static int find(std::false_type, lua_State* L_) { return luaL_error(L_, "sol: cannot call 'find' on '%s': no supported comparison operator for the value type", detail::demangle().c_str()); } static int next_iter(lua_State* L_) { iter& i = stack::unqualified_get>(L_, 1); auto& it = i.it(); auto& end = i.sen(); std::size_t k = stack::unqualified_get(L_, 2); if (it == end) { return 0; } int p; p = stack::push(L_, k + 1); p += stack::push_reference(L_, detail::deref_move_only(*it)); std::advance(it, 1); return p; } public: static int clear(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'clear' on type '%s': cannot remove all items from a fixed array", detail::demangle().c_str()); } static int erase(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'erase' on type '%s': cannot remove an item from fixed arrays", detail::demangle().c_str()); } static int add(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'add' on type '%s': cannot add to fixed arrays", detail::demangle().c_str()); } static int insert(lua_State* L_) { return luaL_error(L_, "sol: cannot call 'insert' on type '%s': cannot insert new entries into fixed arrays", detail::demangle().c_str()); } static int at(lua_State* L_) { return get(L_); } static int get(lua_State* L_) { T& self = get_src(L_); std::ptrdiff_t idx = stack::unqualified_get(L_, 2); idx += deferred_uc::index_adjustment(L_, self); if (idx >= static_cast(std::extent::value) || idx < 0) { return stack::push(L_, lua_nil); } return stack::push_reference(L_, detail::deref_move_only(self[idx])); } static int index_get(lua_State* L_) { return get(L_); } static int set(lua_State* L_) { T& self = get_src(L_); std::ptrdiff_t idx = stack::unqualified_get(L_, 2); idx += deferred_uc::index_adjustment(L_, self); if (idx >= static_cast(std::extent::value)) { return luaL_error(L_, "sol: index out of bounds (too big) for set on '%s'", detail::demangle().c_str()); } if (idx < 0) { return luaL_error(L_, "sol: index out of bounds (too small) for set on '%s'", detail::demangle().c_str()); } self[idx] = stack::unqualified_get(L_, 3); return 0; } static int index_set(lua_State* L_) { return set(L_); } static int index_of(lua_State* L_) { return find(L_); } static int find(lua_State* L_) { return find(meta::supports_op_equal(), L_); } static int size(lua_State* L_) { return stack::push(L_, std::extent::value); } static int empty(lua_State* L_) { return stack::push(L_, std::extent::value > 0); } static int pairs(lua_State* L_) { auto& src = get_src(L_); stack::push(L_, next_iter); stack::push>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::end(L_, src)); stack::push(L_, 0); return 3; } static int ipairs(lua_State* L_) { return pairs(L_); } static int next(lua_State* L_) { return stack::push(L_, next_iter); } static std::ptrdiff_t index_adjustment(lua_State*, T&) { return (SOL_CONTAINER_START_INDEX_I_) == 0 ? 0 : -(SOL_CONTAINER_START_INDEX_I_); } static iterator begin(lua_State*, T& self) { return std::addressof(self[0]); } static sentinel end(lua_State*, T& self) { return std::addressof(self[0]) + std::extent::value; } }; template struct usertype_container_default> : usertype_container_default { }; } // namespace container_detail template struct usertype_container : container_detail::usertype_container_default { }; } // namespace sol #endif // SOL_USERTYPE_CONTAINER_HPP