// The MIT License (MIT) // Copyright (c) 2013-2017 Rapptz, ThePhD and contributors // Permission is hereby granted, free of charge, to any person obtaining a copy of // this software and associated documentation files (the "Software"), to deal in // the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #ifndef SOL_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_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_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 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_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_add = meta::boolean::value>; template using has_traits_size = meta::has_size; 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; } 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 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 pairs(lua_State* L) { return luaL_error(L, "sol: cannot call '__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 typename T::iterator iterator; typedef typename T::value_type value_type; typedef std::conditional_t, std::pair>> KV; typedef typename KV::first_type K; typedef typename KV::second_type V; typedef decltype(*std::declval()) iterator_return; typedef typename meta::iterator_tag::type iterator_category; typedef std::is_same is_input_iterator; typedef std::conditional_t, iterator_return>>()))> 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) { typedef std::remove_pointer_t> Tu; #ifdef SOL_SAFE_USERTYPE auto p = stack::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::get(L, 1); #endif // Safe getting with error } static int get_associative(std::true_type, lua_State* L, iterator& it) { auto& v = *it; return stack::stack_detail::push_reference(L, detail::deref(v.second)); } static int get_associative(std::false_type, lua_State* L, iterator& it) { return stack::stack_detail::push_reference(L, detail::deref(*it)); } static int get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) { if (key < 1) { return stack::push(L, lua_nil); } auto it = begin(L, self); auto e = end(L, self); if (it == e) { return stack::push(L, lua_nil); } while (key > 1) { --key; ++it; if (it == e) { return stack::push(L, lua_nil); } } return get_associative(is_associative(), L, it); } static int get_category(std::random_access_iterator_tag, lua_State* L, T& self, K& key) { std::ptrdiff_t len = static_cast(size_start(L, self)); if (key < 1 || key > len) { return stack::push(L, lua_nil); } --key; auto it = std::next(begin(L, self), key); return get_associative(is_associative(), L, it); } static int get_it(std::true_type, lua_State* L, T& self, K& key) { return get_category(iterator_category(), L, self, key); } static int 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 = end(L, self); auto it = std::find_if(begin(L, self), e, std::ref(fx)); if (it == e) { return stack::push(L, lua_nil); } return get_associative(is_associative(), L, it); } static int get_comparative(std::false_type, lua_State* L, T&, K&) { return luaL_error(L, "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 int get_it(std::false_type, lua_State* L, T& self, K& key) { return get_comparative(meta::supports_op_equal(), L, self, key); } static void set_associative(std::true_type, iterator& it, stack_object value) { auto& v = *it; v.second = value.as(); } static void set_associative(std::false_type, iterator& it, stack_object value) { auto& v = *it; v = value.as(); } static void set_writable(std::true_type, lua_State*, T&, iterator& it, stack_object value) { set_associative(is_associative(), it, std::move(value)); } static void set_writable(std::false_type, lua_State* L, T&, iterator&, stack_object) { luaL_error(L, "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle().data()); } static void set_category(std::input_iterator_tag, lua_State* L, T& self, stack_object okey, stack_object value) { decltype(auto) key = okey.as(); auto e = end(L, self); auto it = begin(L, self); auto backit = it; for (; key > 1 && it != e; --key, ++it) { backit = it; } if (it == e) { if (key == 1) { add_copyable(is_copyable(), L, self, std::move(value), meta::has_insert_after::value ? backit : it); return; } luaL_error(L, "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 void 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 < 1) { luaL_error(L, "sol: out of bounds (too small) for set on '%s'", detail::demangle().c_str()); return; } --key; std::ptrdiff_t len = static_cast(size_start(L, self)); if (key == len) { add_copyable(is_copyable(), L, self, std::move(value)); return; } else if (key > len) { luaL_error(L, "sol: out of bounds (too big) for set on '%s'", detail::demangle().c_str()); return; } auto it = std::next(begin(L, self), key); set_writable(is_writable(), L, self, it, std::move(value)); } static void 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) { luaL_error(L, "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle().data()); ; return; } auto fx = [&](const value_type& r) -> bool { return key == get_key(is_associative(), r); }; auto e = end(L, self); auto it = std::find_if(begin(L, self), e, std::ref(fx)); if (it == e) { return; } set_writable(is_writable(), L, self, it, std::move(value)); } static void set_comparative(std::false_type, lua_State* L, T&, stack_object, stack_object) { luaL_error(L, "cannot set this value on '%s': no suitable way to increment iterator or compare to '%s' key", detail::demangle().data(), detail::demangle().data()); } static void 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())); } static void set_associative_insert(std::false_type, lua_State*, T& self, iterator& it, K& key, stack_object) { self.insert(it, key); } static void 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 == end(L, self)) { set_associative_insert(is_associative(), L, self, it, key, std::move(value)); return; } set_writable(is_writable(), L, self, it, std::move(value)); } static void set_associative_find(std::false_type, lua_State* L, T& self, stack_object key, stack_object value) { set_comparative(meta::supports_op_equal(), L, self, std::move(key), std::move(value)); } static void set_it(std::true_type, lua_State* L, T& self, stack_object key, stack_object value) { set_category(iterator_category(), L, self, std::move(key), std::move(value)); } static void set_it(std::false_type, lua_State* L, T& self, stack_object key, stack_object value) { set_associative_find(meta::all, meta::any>(), L, self, std::move(key), std::move(value)); } static int find_has_associative_lookup(std::true_type, lua_State* L, T& self) { decltype(auto) key = stack::get(L, 2); auto it = self.find(key); if (it == end(L, self)) { return stack::push(L, lua_nil); } return get_associative(is_associative(), L, it); } static int find_has_associative_lookup(std::false_type, lua_State* L, T& self) { decltype(auto) value = stack::get(L, 2); auto it = self.find(value); if (it == end(L, self)) { return stack::push(L, lua_nil); } return get_associative(is_associative(), L, it); } static int find_has(std::true_type, lua_State* L, T& self) { return find_has_associative_lookup(meta::any(), L, self); } static int find_associative_lookup(std::true_type, lua_State* L, iterator& it, std::size_t) { return get_associative(is_associative(), L, it); } static int find_associative_lookup(std::false_type, lua_State* L, iterator&, std::size_t index) { return stack::push(L, index); } static int find_comparative(std::false_type, lua_State* L, T&) { return luaL_error(L, "cannot call 'find' on '%s': there is no 'find' function and the value_type is not equality comparable", detail::demangle().c_str()); } static int find_comparative(std::true_type, lua_State* L, T& self) { decltype(auto) value = stack::get(L, 2); auto it = begin(L, self); auto e = 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 int find_has(std::false_type, lua_State* L, T& self) { return find_comparative(meta::supports_op_equal(), L, self); } static void add_insert_after(std::false_type, lua_State* L, T& self, stack_object value, iterator&) { add_insert_after(std::false_type(), L, self, value); } static void add_insert_after(std::false_type, lua_State* L, T&, stack_object) { luaL_error(L, "cannot call 'add' on type '%s': no suitable insert/push_back C++ functions", detail::demangle().data()); } static void add_insert_after(std::true_type, lua_State*, T& self, stack_object value, iterator& at) { self.insert_after(at, value.as()); } static void add_insert_after(std::true_type, lua_State* L, T& self, stack_object value) { auto backit = self.before_begin(); { auto e = end(L, self); for (auto it = begin(L, self); it != e; ++backit, ++it) { } } return add_insert_after(std::true_type(), L, self, value, backit); } static void add_insert(std::true_type, lua_State*, T& self, stack_object value, iterator& at) { self.insert(at, value.as()); } static void add_insert(std::true_type, lua_State* L, T& self, stack_object value) { auto at = end(L, self); add_insert(std::true_type(), L, self, value, at); } static void add_insert(std::false_type, lua_State* L, T& self, stack_object value, iterator& at) { return add_insert_after(meta::has_insert_after(), L, self, std::move(value), at); } static void 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 void add_push_back(std::true_type, lua_State*, T& self, stack_object value, iterator&) { self.push_back(value.as()); } static void add_push_back(std::true_type, lua_State*, T& self, stack_object value) { self.push_back(value.as()); } static void add_push_back(std::false_type, lua_State* L, T& self, stack_object value, iterator& at) { add_insert(meta::has_insert(), L, self, value, at); } static void add_push_back(std::false_type, lua_State* L, T& self, stack_object value) { add_insert(meta::has_insert(), L, self, value); } static void add_associative(std::true_type, lua_State* L, T& self, stack_object key, iterator& at) { self.insert(at, value_type(key.as(), stack::get(L, 3))); } static void add_associative(std::true_type, lua_State* L, T& self, stack_object key) { auto at = end(L, self); add_associative(std::true_type(), L, self, std::move(key), at); } static void add_associative(std::false_type, lua_State* L, T& self, stack_object value, iterator& at) { add_push_back(meta::has_push_back(), L, self, value, at); } static void add_associative(std::false_type, lua_State* L, T& self, stack_object value) { add_push_back(meta::has_push_back(), L, self, value); } static void add_copyable(std::true_type, lua_State* L, T& self, stack_object value, iterator& at) { add_associative(is_associative(), L, self, std::move(value), at); } static void add_copyable(std::true_type, lua_State* L, T& self, stack_object value) { add_associative(is_associative(), L, self, value); } static void add_copyable(std::false_type, lua_State* L, T& self, stack_object value, iterator&) { add_copyable(std::false_type(), L, self, std::move(value)); } static void add_copyable(std::false_type, lua_State* L, T&, stack_object) { luaL_error(L, "cannot call 'add' on '%s': value_type is non-copyable", detail::demangle().data()); } static void 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? add_copyable(std::true_type(), L, self, std::move(value)); } static void insert_lookup(std::false_type, lua_State* L, T& self, stack_object where, stack_object value) { auto it = begin(L, self); auto key = where.as(); --key; std::advance(it, key); self.insert(it, value.as()); } static void 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; auto e = end(L, self); for (auto it = begin(L, self); key > 0; ++backit, ++it, --key) { if (backit == e) { luaL_error(L, "sol: out of bounds (too big) for set on '%s'", detail::demangle().c_str()); return; } } } self.insert_after(backit, value.as()); } static void insert_after_has(std::false_type, lua_State* L, T&, stack_object, stack_object) { luaL_error(L, "cannot call 'insert' on '%s': no suitable or similar functionality detected on this container", detail::demangle().data()); } static void insert_has(std::true_type, lua_State* L, T& self, stack_object key, stack_object value) { insert_lookup(meta::all(), L, self, std::move(key), std::move(value)); } static void insert_has(std::false_type, lua_State* L, T& self, stack_object where, stack_object value) { insert_after_has(meta::has_insert_after(), L, self, where, value); } static void insert_copyable(std::true_type, lua_State* L, T& self, stack_object key, stack_object value) { insert_has(meta::has_insert(), L, self, std::move(key), std::move(value)); } static void insert_copyable(std::false_type, lua_State* L, T&, stack_object, stack_object) { luaL_error(L, "cannot call 'insert' on '%s': value_type is non-copyable", detail::demangle().data()); } static void erase_integral(std::true_type, lua_State* L, T& self, K& key) { auto it = begin(L, self); --key; std::advance(it, key); self.erase(it); } static void 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 = end(L, self); auto it = std::find_if(begin(L, self), e, std::ref(fx)); if (it == e) { return; } self.erase(it); } static void erase_associative_lookup(std::true_type, lua_State*, T& self, const K& key) { self.erase(key); } static void erase_associative_lookup(std::false_type, lua_State* L, T& self, K& key) { erase_integral(std::is_integral(), L, self, key); } static void erase_after_has(std::true_type, lua_State* L, T& self, K& key) { auto backit = self.before_begin(); { --key; auto e = end(L, self); for (auto it = begin(L, self); key > 0; ++backit, ++it, --key) { if (backit == e) { luaL_error(L, "sol: out of bounds for erase on '%s'", detail::demangle().c_str()); return; } } } self.erase_after(backit); } static void erase_after_has(std::false_type, lua_State* L, T&, const K&) { luaL_error(L, "sol: cannot call erase on '%s'", detail::demangle().c_str()); } static void erase_has(std::true_type, lua_State* L, T& self, K& key) { erase_associative_lookup(meta::any(), L, self, key); } static void erase_has(std::false_type, lua_State* L, T& self, K& key) { 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 int get_start(lua_State* L, T& self, K& key) { return get_it(is_linear_integral(), L, self, key); } static void set_start(lua_State* L, T& self, stack_object key, stack_object value) { 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 void erase_start(lua_State* L, T& self, K& key) { erase_has(has_erase(), L, self, key); } template static int next_associative(std::true_type, lua_State* L) { iter& i = stack::get>(L, 1); auto& source = i.source; auto& it = i.it; if (it == deferred_traits::end(L, source)) { return 0; } int p; if (ip) { p = stack::push_reference(L, it->first); } else { ++i.i; p = stack::push_reference(L, i.i); } p += stack::stack_detail::push_reference(L, detail::deref(it->second)); std::advance(it, 1); return p; } template static int pairs_associative(std::true_type, lua_State* L) { auto& src = get_src(L); stack::push(L, next); stack::push>(L, src, deferred_traits::begin(L, src)); stack::push(L, lua_nil); return 3; } template static int next_associative(std::false_type, lua_State* L) { iter& i = stack::get>(L, 1); auto& source = i.source; auto& it = i.it; K k = stack::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(*it)); std::advance(it, 1); return p; } template static int pairs_associative(std::false_type, lua_State* L) { auto& src = get_src(L); stack::push(L, next); stack::push>(L, src, deferred_traits::begin(L, src)); stack::push(L, 0); return 3; } template static int next(lua_State* L) { return next_associative(is_associative(), L); } public: static int get(lua_State* L) { auto& self = get_src(L); decltype(auto) key = stack::get(L); return get_start(L, self, key); } 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); set_start(L, self, stack_object(L, raw_index(2)), std::move(value)); return 0; } static int index_set(lua_State* L) { return set(L); } static int add(lua_State* L) { auto& self = get_src(L); add_copyable(is_copyable(), L, self, stack_object(L, raw_index(2))); return 0; } static int insert(lua_State* L) { auto& self = get_src(L); insert_copyable(is_copyable(), L, self, stack_object(L, raw_index(2)), stack_object(L, raw_index(3))); return 0; } static int find(lua_State* L) { auto& self = get_src(L); return find_has(has_find(), L, self); } 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); decltype(auto) key = stack::get(L, 2); erase_start(L, self, key); return 0; } static int empty(lua_State* L) { auto& self = get_src(L); return stack::push(L, empty_start(L, self)); } static int pairs(lua_State* L) { return pairs_associative(is_associative(), L); } static int ipairs(lua_State* L) { return pairs_associative(is_associative(), L); } }; 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::check_get(L, 1); #ifdef SOL_SAFE_USERTYPE if (!p || p.value() == nullptr) { luaL_error(L, "sol: 'self' argument is nil or not of type '%s' (pass 'self' as first argument with ':' or call on proper 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::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(lua_State* L) { iter& i = stack::get>(L, 1); auto& source = i.source; auto& it = i.it; std::size_t k = stack::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(*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 get(lua_State* L) { T& self = get_src(L); std::ptrdiff_t idx = stack::get(L, 2); if (idx > static_cast(std::extent::value) || idx < 1) { return stack::push(L, lua_nil); } --idx; return stack::push_reference(L, detail::deref(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::get(L, 2); 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 < 1) { return luaL_error(L, "sol: index out of bounds (too small) for set on '%s'", detail::demangle().c_str()); } --idx; self[idx] = stack::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); 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 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