// sol2 // 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_CONTAINER_TRAITS_HPP #define SOL_CONTAINER_TRAITS_HPP #include "traits.hpp" #include "stack.hpp" #include namespace sol { template struct container_traits; template struct as_container_t { T source; as_container_t(T value) : source(std::move(value)) { } operator std::add_rvalue_reference_t() { return std::move(source); } operator std::add_lvalue_reference_t>() const { return source; } }; template struct as_container_t { std::reference_wrapper source; as_container_t(T& value) : source(value) { } operator T&() { return source; } }; template auto as_container(T&& value) { return as_container_t(std::forward(value)); } namespace container_detail { template struct has_clear_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::clear)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_empty_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::empty)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_erase_after_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(std::declval().erase_after(std::declval>()))*); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_find_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(std::declval().find(std::declval>()))*); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_find_test::value>> { private: typedef std::array one; typedef std::array two; template static one test(decltype(std::declval().find(std::declval>()))*); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_erase_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(std::declval().erase(std::declval()))*); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_find_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::find)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_insert_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::insert)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_erase_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::erase)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_index_set_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::index_set)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_index_get_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::index_get)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_set_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::set)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_get_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::get)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_at_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::at)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_pairs_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::pairs)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_ipairs_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::ipairs)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_next_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::next)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_add_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::add)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; template struct has_traits_size_test { private: typedef std::array one; typedef std::array two; template static one test(decltype(&C::size)); template static two test(...); public: static const bool value = sizeof(test(0)) == sizeof(char); }; 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_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_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; } struct error_result { int results; const char* fmt_; std::array args; error_result() : results(0), fmt_(nullptr) { } error_result(int results) : results(results), fmt_(nullptr) { } error_result(const char* fmt_, const char* msg) : results(0), fmt_(fmt_) { args[0] = msg; } }; inline int handle_errors(lua_State* L, const error_result& er) { if (er.fmt_ == nullptr) { return er.results; } return luaL_error(L, er.fmt_, er.args[0], er.args[1], er.args[2], er.args[3]); } template struct container_traits_default { private: typedef std::remove_pointer_t> T; public: typedef lua_nil_t iterator; 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 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 iterator 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 container_traits_default>, meta::has_value_type>>, meta::has_iterator>>>::value>> { private: typedef std::remove_pointer_t>> T; private: typedef container_traits deferred_traits; typedef meta::is_associative is_associative; typedef meta::is_lookup is_lookup; typedef meta::is_matched_lookup is_matched_lookup; typedef typename T::iterator iterator; typedef typename T::value_type value_type; typedef std::conditional_t, std::conditional_t > > KV; typedef typename KV::first_type K; typedef typename KV::second_type V; typedef std::conditional_t next_K; typedef decltype(*std::declval()) iterator_return; typedef std::conditional_t, std::conditional_t > captured_type; typedef typename meta::iterator_tag::type iterator_category; typedef std::is_same is_input_iterator; typedef std::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 { T& source; iterator it; std::size_t i; iter(T& source, iterator it) : source(source), it(std::move(it)), i(0) { } }; static auto& get_src(lua_State* L) { #if defined(SOL_SAFE_USERTYPE) && 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 error_result at_category(std::input_iterator_tag, lua_State* L, T& self, std::ptrdiff_t pos) { pos += deferred_traits::index_adjustment(L, self); if (pos < 0) { return stack::push(L, lua_nil); } auto it = deferred_traits::begin(L, self); auto e = deferred_traits::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 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_traits::index_adjustment(L, self); if (pos < 0 || pos >= len) { return stack::push(L, lua_nil); } auto it = std::next(deferred_traits::begin(L, self), pos); return get_associative(is_associative(), L, it); } static error_result at_start(lua_State* L, T& self, std::ptrdiff_t pos) { return at_category(iterator_category(), L, self, pos); } static error_result get_associative(std::true_type, lua_State* L, iterator& it) { auto& v = *it; return stack::stack_detail::push_reference(L, detail::deref_non_pointer(v.second)); } static error_result get_associative(std::false_type, lua_State* L, iterator& it) { return stack::stack_detail::push_reference(L, detail::deref_non_pointer(*it)); } static error_result get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) { key += deferred_traits::index_adjustment(L, self); if (key < 0) { return stack::push(L, lua_nil); } auto it = deferred_traits::begin(L, self); auto e = deferred_traits::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 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 += deferred_traits::index_adjustment(L, self); if (key < 0 || key >= len) { return stack::push(L, lua_nil); } auto it = std::next(deferred_traits::begin(L, self), key); return get_associative(is_associative(), L, it); } static error_result get_it(std::true_type, lua_State* L, T& self, K& key) { return get_category(iterator_category(), L, self, key); } static 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_traits::end(L, self); auto it = std::find_if(deferred_traits::begin(L, self), e, std::ref(fx)); if (it == e) { return stack::push(L, lua_nil); } return get_associative(is_associative(), L, it); } static error_result get_comparative(std::false_type, lua_State*, T&, K&) { return 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 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 error_result set_associative(std::true_type, iterator& it, stack_object value) { auto& v = *it; v.second = value.as(); return {}; } static error_result set_associative(std::false_type, iterator& it, stack_object value) { auto& v = *it; v = value.as(); return {}; } static 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 error_result set_writable(std::false_type, lua_State*, T&, iterator&, stack_object) { return error_result("cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle().data()); } static 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 += deferred_traits::index_adjustment(L, self); auto e = deferred_traits::end(L, self); auto it = deferred_traits::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 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 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(); if (key <= 0) { return error_result("sol: out of bounds (too small) for set on '%s'", detail::demangle().c_str()); } key += deferred_traits::index_adjustment(L, self); 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 error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle().c_str()); } auto it = std::next(deferred_traits::begin(L, self), key); return set_writable(is_writable(), L, self, it, std::move(value)); } static 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 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_traits::end(L, self); auto it = std::find_if(deferred_traits::begin(L, self), e, std::ref(fx)); if (it == e) { return {}; } return set_writable(is_writable(), L, self, it, std::move(value)); } static error_result set_comparative(std::false_type, lua_State*, T&, stack_object, stack_object) { return 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()); } static error_result set_associative_insert(std::true_type, lua_State*, T& self, iterator& it, K& key, stack_object value) { self.insert(it, value_type(key, value.as())); return {}; } static error_result set_associative_insert(std::false_type, lua_State*, T& self, iterator& it, K& key, stack_object) { self.insert(it, key); return {}; } static 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_traits::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 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 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 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)); } static error_result find_has_associative_lookup(std::true_type, lua_State* L, T& self) { decltype(auto) key = stack::unqualified_get(L, 2); auto it = self.find(key); if (it == deferred_traits::end(L, self)) { return stack::push(L, lua_nil); } return get_associative(is_associative(), L, it); } static error_result find_has_associative_lookup(std::false_type, lua_State* L, T& self) { decltype(auto) value = stack::unqualified_get(L, 2); auto it = self.find(value); if (it == deferred_traits::end(L, self)) { return stack::push(L, lua_nil); } return get_associative(is_associative(), L, it); } static error_result find_has(std::true_type, lua_State* L, T& self) { return find_has_associative_lookup(meta::any(), L, self); } static error_result find_associative_lookup(std::true_type, lua_State* L, iterator& it, std::size_t) { return get_associative(is_associative(), L, it); } static error_result find_associative_lookup(std::false_type, lua_State* L, iterator&, std::size_t index) { return stack::push(L, index); } static error_result find_comparative(std::false_type, lua_State*, T&) { return error_result("cannot call 'find' on '%s': there is no 'find' function and the value_type is not equality comparable", detail::demangle().c_str()); } static error_result find_comparative(std::true_type, lua_State* L, T& self) { decltype(auto) value = stack::unqualified_get(L, 2); auto it = deferred_traits::begin(L, self); auto e = deferred_traits::end(L, self); std::size_t index = 1; for (;; ++it, ++index) { if (it == e) { return stack::push(L, lua_nil); } if (value == get_value(is_associative(), *it)) { break; } } return find_associative_lookup(meta::any(), L, it, index); } static error_result find_has(std::false_type, lua_State* L, T& self) { return find_comparative(meta::supports_op_equal(), L, self); } static error_result add_insert_after(std::false_type, lua_State* L, T& self, stack_object value, iterator&) { return add_insert_after(std::false_type(), L, self, value); } static error_result add_insert_after(std::false_type, lua_State*, T&, stack_object) { return error_result("cannot call 'add' on type '%s': no suitable insert/push_back C++ functions", detail::demangle().data()); } static error_result add_insert_after(std::true_type, lua_State*, T& self, stack_object value, iterator& pos) { self.insert_after(pos, value.as()); return {}; } static error_result add_insert_after(std::true_type, lua_State* L, T& self, stack_object value) { auto backit = self.before_begin(); { auto e = deferred_traits::end(L, self); for (auto it = deferred_traits::begin(L, self); it != e; ++backit, ++it) { } } return add_insert_after(std::true_type(), L, self, value, backit); } static error_result add_insert(std::true_type, lua_State*, T& self, stack_object value, iterator& pos) { self.insert(pos, value.as()); return {}; } static error_result add_insert(std::true_type, lua_State* L, T& self, stack_object value) { auto pos = deferred_traits::end(L, self); return add_insert(std::true_type(), L, self, value, pos); } static error_result add_insert(std::false_type, lua_State* L, T& self, stack_object value, iterator& pos) { return add_insert_after(meta::has_insert_after(), L, self, std::move(value), pos); } static 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)); } static error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value, iterator&) { self.push_back(value.as()); return {}; } static error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value) { self.push_back(value.as()); return {}; } static error_result add_push_back(std::false_type, lua_State* L, T& self, stack_object value, iterator& pos) { return add_insert(meta::has_insert(), L, self, value, pos); } static error_result add_push_back(std::false_type, lua_State* L, T& self, stack_object value) { return add_insert(meta::has_insert(), L, self, value); } static error_result add_associative(std::true_type, lua_State* L, T& self, stack_object key, iterator& pos) { self.insert(pos, value_type(key.as(), stack::unqualified_get(L, 3))); return {}; } static error_result add_associative(std::true_type, lua_State* L, T& self, stack_object key) { auto pos = deferred_traits::end(L, self); return add_associative(std::true_type(), L, self, std::move(key), pos); } static error_result add_associative(std::false_type, lua_State* L, T& self, stack_object value, iterator& pos) { return add_push_back(meta::has_push_back(), L, self, value, pos); } static 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); } static error_result add_copyable(std::true_type, lua_State* L, T& self, stack_object value, iterator& pos) { return add_associative(is_associative(), L, self, std::move(value), pos); } static error_result add_copyable(std::true_type, lua_State* L, T& self, stack_object value) { return add_associative(is_associative(), L, self, value); } static error_result add_copyable(std::false_type, lua_State* L, T& self, stack_object value, iterator&) { return add_copyable(std::false_type(), L, self, std::move(value)); } static error_result add_copyable(std::false_type, lua_State*, T&, stack_object) { return error_result("cannot call 'add' on '%s': value_type is non-copyable", detail::demangle().data()); } static 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 error_result insert_lookup(std::false_type, lua_State* L, T& self, stack_object where, stack_object value) { auto it = deferred_traits::begin(L, self); auto key = where.as(); key += deferred_traits::index_adjustment(L, self); std::advance(it, key); self.insert(it, value.as()); return {}; } static 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 += deferred_traits::index_adjustment(L, self); auto e = deferred_traits::end(L, self); for (auto it = deferred_traits::begin(L, self); key > 0; ++backit, ++it, --key) { if (backit == e) { return error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle().c_str()); } } } self.insert_after(backit, value.as()); return {}; } static error_result insert_after_has(std::false_type, lua_State*, T&, stack_object, stack_object) { return error_result("cannot call 'insert' on '%s': no suitable or similar functionality detected on this container", detail::demangle().data()); } static 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 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 error_result insert_copyable(std::true_type, lua_State* L, T& self, stack_object key, stack_object value) { return insert_has(meta::has_insert(), L, self, std::move(key), std::move(value)); } static error_result insert_copyable(std::false_type, lua_State*, T&, stack_object, stack_object) { return error_result("cannot call 'insert' on '%s': value_type is non-copyable", detail::demangle().data()); } static error_result erase_integral(std::true_type, lua_State* L, T& self, K& key) { auto it = deferred_traits::begin(L, self); key += deferred_traits::index_adjustment(L, self); std::advance(it, key); self.erase(it); return {}; } static 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_traits::end(L, self); auto it = std::find_if(deferred_traits::begin(L, self), e, std::ref(fx)); if (it == e) { return {}; } self.erase(it); return {}; } static error_result erase_associative_lookup(std::true_type, lua_State*, T& self, const K& key) { self.erase(key); return {}; } static 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 error_result erase_after_has(std::true_type, lua_State* L, T& self, K& key) { auto backit = self.before_begin(); { key += deferred_traits::index_adjustment(L, self); auto e = deferred_traits::end(L, self); for (auto it = deferred_traits::begin(L, self); key > 0; ++backit, ++it, --key) { if (backit == e) { return error_result("sol: out of bounds for erase on '%s'", detail::demangle().c_str()); } } } self.erase_after(backit); return {}; } static error_result erase_after_has(std::false_type, lua_State*, T&, const K&) { return error_result("sol: cannot call erase on '%s'", detail::demangle().c_str()); } static error_result erase_has(std::true_type, lua_State* L, T& self, K& key) { return erase_associative_lookup(meta::any(), L, self, key); } static error_result erase_has(std::false_type, lua_State* L, T& self, K& key) { return erase_after_has(has_erase_after(), L, self, key); } static auto size_has(std::false_type, lua_State* L, T& self) { return std::distance(deferred_traits::begin(L, self), deferred_traits::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_traits::begin(L, self) == deferred_traits::end(L, self); } static error_result get_start(lua_State* L, T& self, K& key) { return get_it(is_linear_integral(), L, self, key); } static 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 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 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& source = i.source; auto& it = i.it; if (it == deferred_traits::end(L, source)) { return 0; } int p; if (ip) { ++i.i; p = stack::push_reference(L, i.i); } else { p = stack::push_reference(L, it->first); } p += stack::stack_detail::push_reference(L, detail::deref_non_pointer(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& source = i.source; auto& it = i.it; next_K k = stack::unqualified_get(L, 2); if (it == deferred_traits::end(L, source)) { return 0; } int p; p = stack::push_reference(L, k + 1); p += stack::stack_detail::push_reference(L, detail::deref_non_pointer(*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, src, deferred_traits::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, src, deferred_traits::begin(L, src)); stack::push(L, 0); return 3; } public: static int at(lua_State* L) { auto& self = get_src(L); error_result er; { std::ptrdiff_t pos = stack::unqualified_get(L); er = at_start(L, self, pos); } return handle_errors(L, er); } static int get(lua_State* L) { auto& self = get_src(L); 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 (type_of(L, 3) == type::lua_nil) { return erase(L); } auto& self = get_src(L); 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); 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); 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); error_result er = find_has(has_find(), L, self); return handle_errors(L, er); } static iterator begin(lua_State*, T& self) { using std::begin; return begin(self); } static iterator end(lua_State*, T& self) { 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); 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&) { #if defined(SOL_CONTAINERS_START_INDEX) return static_cast((SOL_CONTAINERS_START) == 0 ? 0 : -(SOL_CONTAINERS_START)); #else return static_cast(-1); #endif } 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 container_traits_default>>::value>> { private: typedef std::remove_pointer_t> T; typedef container_traits deferred_traits; public: typedef std::remove_extent_t value_type; typedef value_type* iterator; private: struct iter { T& source; iterator it; iter(T& source, iterator it) : source(source), it(std::move(it)) { } }; static auto& get_src(lua_State* L) { auto p = stack::unqualified_check_get(L, 1); #if defined(SOL_SAFE_USERTYPE) && 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) { const auto& v = self[idx]; if (v == value) { return stack::push(L, idx + 1); } } 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& source = i.source; auto& it = i.it; std::size_t k = stack::unqualified_get(L, 2); if (it == deferred_traits::end(L, source)) { return 0; } int p; p = stack::push_reference(L, k + 1); p += stack::push_reference(L, detail::deref_non_pointer(*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_traits::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_non_pointer(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_traits::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 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, src, deferred_traits::begin(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&) { #if defined(SOL_CONTAINERS_START_INDEX) return (SOL_CONTAINERS_START) == 0 ? 0 : -(SOL_CONTAINERS_START); #else return -1; #endif } static iterator begin(lua_State*, T& self) { return std::addressof(self[0]); } static iterator end(lua_State*, T& self) { return std::addressof(self[0]) + std::extent::value; } }; template struct container_traits_default> : container_traits_default {}; } // namespace container_detail template struct container_traits : container_detail::container_traits_default {}; } // namespace sol #endif // SOL_CONTAINER_TRAITS_HPP