From 5b3ca9343cd25b0f1d19faf3066e826ac6ea2213 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Sun, 13 Jan 2019 21:46:53 -0500 Subject: [PATCH] update single update forward declaration update container traits usage fix vector of pair as a usertype --- include/sol/call.hpp | 2 +- include/sol/container_traits.hpp | 1466 -------------- include/sol/error_handler.hpp | 18 +- include/sol/forward.hpp | 3 + include/sol/function.hpp | 53 +- include/sol/function_types.hpp | 16 +- include/sol/function_types_overloaded.hpp | 8 +- include/sol/function_types_stateless.hpp | 13 +- include/sol/protected_function.hpp | 35 +- include/sol/stack.hpp | 22 +- include/sol/stack_core.hpp | 147 +- include/sol/stack_get_unqualified.hpp | 273 +-- include/sol/state_view.hpp | 2 +- include/sol/types.hpp | 33 + include/sol/unicode.hpp | 29 +- include/sol/usertype.hpp | 13 +- include/sol/usertype_container.hpp | 1769 +++++++++++++---- include/sol/usertype_container_launch.hpp | 458 +++++ include/sol/usertype_core.hpp | 4 +- include/sol/usertype_storage.hpp | 2 +- single/include/sol/forward.hpp | 7 +- single/include/sol/sol.hpp | 1384 ++++++------- ...aits.cpp => usertype_container_launch.cpp} | 2 +- .../runtime_tests/source/container_shims.cpp | 58 +- tests/runtime_tests/source/functions.cpp | 58 +- 25 files changed, 3031 insertions(+), 2844 deletions(-) delete mode 100644 include/sol/container_traits.hpp create mode 100644 include/sol/usertype_container_launch.hpp rename tests/compile_tests/source/{container_traits.cpp => usertype_container_launch.cpp} (96%) diff --git a/include/sol/call.hpp b/include/sol/call.hpp index 27bc8d9a..839bacc5 100644 --- a/include/sol/call.hpp +++ b/include/sol/call.hpp @@ -63,7 +63,7 @@ namespace sol { template inline void handle_filter(returns_self_with, lua_State* L, int& pushed) { pushed = stack::push(L, raw_index(1)); - handle_filter(static_stack_dependencies<-1, In...>(), L, pushed); + handle_filter(static_stack_dependencies< -1, In...>(), L, pushed); } inline void handle_filter(const stack_dependencies& sdeps, lua_State* L, int&) { diff --git a/include/sol/container_traits.hpp b/include/sol/container_traits.hpp deleted file mode 100644 index 708493ce..00000000 --- a/include/sol/container_traits.hpp +++ /dev/null @@ -1,1466 +0,0 @@ -// sol3 - -// The MIT License (MIT) - -// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors - -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -#ifndef SOL_CONTAINER_TRAITS_HPP -#define SOL_CONTAINER_TRAITS_HPP - -#include "traits.hpp" -#include "stack.hpp" -#include "object.hpp" -#include "map.hpp" - -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: - 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_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_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 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: - using T = std::remove_pointer_t>>; - - private: - using deferred_traits = container_traits; - 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 value_type = typename T::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 detail::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 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_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 detail::error_result at_start(lua_State* L, T& self, std::ptrdiff_t pos) { - return at_category(iterator_category(), L, self, pos); - } - - static detail::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 detail::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 detail::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 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 += 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 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_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 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) { - auto& v = *it; - v.second = value.as(); - return {}; - } - - static detail::error_result set_associative(std::false_type, iterator& it, stack_object value) { - 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 += 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 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(); - if (key <= 0) { - return detail::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 detail::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 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_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 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()); - } - - static detail::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 detail::error_result set_associative_insert(std::false_type, lua_State*, T& self, iterator& it, K& key, stack_object) { - self.insert(it, key); - return {}; - } - - 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_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 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_traits::end(L, self)) { - return stack::push(L, lua_nil); - } - if constexpr (idx_of) { - auto dist = std::distance(deferred_traits::begin(L, self), it); - dist -= deferred_traits::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_traits::end(L, self)) { - return stack::push(L, lua_nil); - } - if constexpr (idx_of) { - auto dist = std::distance(deferred_traits::begin(L, self), it); - dist -= deferred_traits::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); - } - - static detail::error_result find_associative_lookup(std::true_type, lua_State* L, T&, iterator& it, std::size_t) { - return get_associative(is_associative(), L, it); - } - - static detail::error_result find_associative_lookup(std::false_type, lua_State* L, T& self, iterator&, std::size_t idx) { - idx -= deferred_traits::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_traits::begin(L, self); - auto e = deferred_traits::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); - } - - static detail::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 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()); - } - - static detail::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 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_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 detail::error_result add_insert(std::true_type, lua_State*, T& self, stack_object value, iterator& 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_traits::end(L, self); - return add_insert(std::true_type(), L, self, value, pos); - } - - static detail::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 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)); - } - - static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value, iterator&) { - 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 {}; - } - - static detail::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 detail::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 detail::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 detail::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 detail::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 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); - } - - static detail::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 detail::error_result add_copyable(std::true_type, lua_State* L, T& self, stack_object value) { - return add_associative(is_associative(), L, self, value); - } - - static detail::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 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_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 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 += 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 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(meta::has_insert(), 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_traits::begin(L, self); - key += deferred_traits::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_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 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 += 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 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_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_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 detail::error_result get_start(lua_State* L, T& self, K& key) { - return get_it(is_linear_integral(), 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 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& 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); - detail::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); - 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 (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) { - 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); - 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&) { -#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) { - idx -= deferred_traits::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& 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 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, 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 diff --git a/include/sol/error_handler.hpp b/include/sol/error_handler.hpp index 3a2d72f7..f72613e4 100644 --- a/include/sol/error_handler.hpp +++ b/include/sol/error_handler.hpp @@ -38,6 +38,15 @@ namespace sol { constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol2 userdata"; constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees"; constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment"; + constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call"; + + inline void accumulate_and_mark(const std::string& n, std::string& addendum, int& marker) { + if (marker > 0) { + addendum += ", "; + } + addendum += n; + ++marker; + } } inline std::string associated_type_name(lua_State* L, int index, type t) { @@ -120,14 +129,7 @@ namespace sol { addendum += detail::demangle(); addendum += "("; int marker = 0; - auto action = [&addendum, &marker](const std::string& n) { - if (marker > 0) { - addendum += ", "; - } - addendum += n; - ++marker; - }; - (void)detail::swallow{int(), (action(detail::demangle()), int())...}; + (void)detail::swallow{int(), (detail::accumulate_and_mark(detail::demangle(), addendum, marker), int())...}; addendum += ")')"; return type_panic_string(L, index, expected, actual, message.empty() ? addendum : message + " " + addendum); } diff --git a/include/sol/forward.hpp b/include/sol/forward.hpp index ca023e24..a5c7e05e 100644 --- a/include/sol/forward.hpp +++ b/include/sol/forward.hpp @@ -150,6 +150,9 @@ namespace sol { struct this_main_state; struct this_environment; + class state_view; + class state; + template struct as_table_t; template diff --git a/include/sol/function.hpp b/include/sol/function.hpp index b04da5fb..ee6d4b63 100644 --- a/include/sol/function.hpp +++ b/include/sol/function.hpp @@ -75,6 +75,34 @@ namespace sol { return *this; } + namespace detail { + template + struct std_shim { + unsafe_function lua_func_; + + std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) { + } + + template + meta::return_type_t operator()(Args&&... args) { + return lua_func_.call(std::forward(args)...); + } + }; + + template <> + struct std_shim { + unsafe_function lua_func_; + + std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) { + } + + template + void operator()(Args&&... args) { + lua_func_.call(std::forward(args)...); + } + }; + } // namespace detail + namespace stack { template struct unqualified_getter> { @@ -82,29 +110,12 @@ namespace sol { typedef typename fx_t::args_list args_lists; typedef meta::tuple_types return_types; - template - static std::function get_std_func(types, types, lua_State* L, int index) { - unsafe_function f(L, index); - auto fx = [ f = std::move(f) ](Args && ... args) -> meta::return_type_t { - return f.call(std::forward(args)...); - }; + template + static std::function get_std_func(types, lua_State* L, int index) { + detail::std_shim fx(unsafe_function(L, index)); return std::move(fx); } - template - static std::function get_std_func(types, types, lua_State* L, int index) { - unsafe_function f(L, index); - auto fx = [f = std::move(f)](FxArgs&&... args) -> void { - f(std::forward(args)...); - }; - return std::move(fx); - } - - template - static std::function get_std_func(types<>, types t, lua_State* L, int index) { - return get_std_func(types(), t, L, index); - } - static std::function get(lua_State* L, int index, record& tracking) { tracking.last = 1; tracking.used += 1; @@ -112,7 +123,7 @@ namespace sol { if (t == type::none || t == type::lua_nil) { return nullptr; } - return get_std_func(return_types(), args_lists(), L, index); + return get_std_func(return_types(), L, index); } }; } // namespace stack diff --git a/include/sol/function_types.hpp b/include/sol/function_types.hpp index 770f81a4..2b4bd093 100644 --- a/include/sol/function_types.hpp +++ b/include/sol/function_types.hpp @@ -88,7 +88,7 @@ namespace sol { int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::stack_detail::push_as_upvalues(L, memfxptr); - upvalues += stack::push(L, lightuserdata_value(static_cast(userptr))); + upvalues += stack::push(L, static_cast(userptr)); stack::push(L, c_closure(freefunc, upvalues)); } @@ -133,14 +133,14 @@ namespace sol { template static void select_reference_member_function(std::true_type, lua_State* L, Fx&& fx, T&& obj, Args&&... args) { - typedef std::decay_t dFx; + using dFx = std::decay_t; dFx memfxptr(std::forward(fx)); auto userptr = detail::ptr(std::forward(obj), std::forward(args)...); lua_CFunction freefunc = &function_detail::upvalue_member_function, meta::unqualified_t, is_yielding>::call; int upvalues = 0; upvalues += stack::push(L, nullptr); - upvalues += stack::stack_detail::push_as_upvalues(L, memfxptr); + upvalues += stack::push>(L, memfxptr); upvalues += stack::push(L, lightuserdata_value(static_cast(userptr))); stack::push(L, c_closure(freefunc, upvalues)); } @@ -158,22 +158,24 @@ namespace sol { template static void select_member_function(std::true_type, lua_State* L, Fx&& fx, function_detail::class_indicator) { - lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; + using dFx = std::decay_t; + lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; int upvalues = 0; upvalues += stack::push(L, nullptr); - upvalues += stack::stack_detail::push_as_upvalues(L, fx); + upvalues += stack::push>(L, fx); stack::push(L, c_closure(freefunc, upvalues)); } template static void select_member_function(std::true_type, lua_State* L, Fx&& fx) { + using dFx = std::decay_t; typedef typename meta::bind_traits>::object_type C; - lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; + lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; int upvalues = 0; upvalues += stack::push(L, nullptr); - upvalues += stack::stack_detail::push_as_upvalues(L, fx); + upvalues += stack::push>(L, fx); stack::push(L, c_closure(freefunc, upvalues)); } diff --git a/include/sol/function_types_overloaded.hpp b/include/sol/function_types_overloaded.hpp index 0c294a72..062aebc5 100644 --- a/include/sol/function_types_overloaded.hpp +++ b/include/sol/function_types_overloaded.hpp @@ -45,14 +45,14 @@ namespace function_detail { } template - int call(types, meta::index_value, types, types, lua_State* L, int, int) { - auto& func = std::get(overloads); + static int call(types, meta::index_value, types, types, lua_State* L, int, int, overload_list& ol) { + auto& func = std::get(ol); return call_detail::call_wrapped(L, func); } int operator()(lua_State* L) { - auto mfx = [&](auto&&... args) { return this->call(std::forward(args)...); }; - return call_detail::overload_match(mfx, L, 1 + start_skew); + auto mfx = [](auto&&... args) { return call(std::forward(args)...); }; + return call_detail::overload_match(mfx, L, 1 + start_skew, overloads); } }; } diff --git a/include/sol/function_types_stateless.hpp b/include/sol/function_types_stateless.hpp index 2be15860..cf3ea1b8 100644 --- a/include/sol/function_types_stateless.hpp +++ b/include/sol/function_types_stateless.hpp @@ -66,14 +66,12 @@ namespace function_detail { // idx n + 1: is the object's void pointer // We don't need to store the size, because the other side is templated // with the same member function pointer type - auto memberdata = stack::stack_detail::get_as_upvalues(L); - auto objdata = stack::stack_detail::get_as_upvalues(L, memberdata.second); - function_type& memfx = memberdata.first; - auto& item = *objdata.first; + function_type& memfx = stack::get>(L, upvalue_index(2)); + auto& item = *static_cast(stack::get(L, upvalue_index(3))); return call_detail::call_wrapped(L, memfx, item); } - static int call(lua_State* L) { + static int call(lua_State* L) noexcept(traits_type::is_noexcept) { int nr = detail::typed_static_trampoline(L); if (is_yielding) { return lua_yield(L, nr); @@ -113,7 +111,7 @@ namespace function_detail { } } - static int call(lua_State* L) { + static int call(lua_State* L) noexcept(traits_type::is_noexcept) { int nr = detail::typed_static_trampoline(L); if (is_yielding) { return lua_yield(L, nr); @@ -174,8 +172,7 @@ namespace function_detail { static int real_call(lua_State* L) noexcept(traits_type::is_noexcept) { // Layout: // idx 1...n: verbatim data of member variable pointer - auto memberdata = stack::stack_detail::get_as_upvalues(L); - function_type& memfx = memberdata.first; + function_type& memfx = stack::get>(L, upvalue_index(2)); return call_detail::call_wrapped(L, memfx); } diff --git a/include/sol/protected_function.hpp b/include/sol/protected_function.hpp index af393edd..8c705a87 100644 --- a/include/sol/protected_function.hpp +++ b/include/sol/protected_function.hpp @@ -34,6 +34,21 @@ namespace sol { + namespace detail { + template + inline void handle_protected_exception(lua_State* L, optional maybe_ex, const char* error, detail::protected_handler& h) { + h.stackindex = 0; + if (b) { + h.target.push(); + detail::call_exception_handler(L, maybe_ex, error); + lua_call(L, 1, 1); + } + else { + detail::call_exception_handler(L, maybe_ex, error); + } + } + } + template class basic_protected_function : public base_t { public: @@ -79,18 +94,6 @@ namespace sol { int returncount = 0; call_status code = call_status::ok; #if !defined(SOL_NO_EXCEPTIONS) || !SOL_NO_EXCEPTIONS - auto onexcept = [&](optional maybe_ex, const char* error) { - h.stackindex = 0; - if (b) { - h.target.push(); - detail::call_exception_handler(lua_state(), maybe_ex, error); - lua_call(lua_state(), 1, 1); - } - else { - detail::call_exception_handler(lua_state(), maybe_ex, error); - } - }; - (void)onexcept; #if (!defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) || !SOL_NO_EXCEPTIONS_SAFE_PROPAGATION) || (defined(SOL_LUAJIT) && SOL_LUAJIT) try { #endif // Safe Exception Propagation @@ -104,17 +107,17 @@ namespace sol { } // Handle C++ errors thrown from C++ functions bound inside of lua catch (const char* error) { - onexcept(optional(nullopt), error); + detail::handle_protected_exception(lua_state(), optional(nullopt), error, h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } catch (const std::string& error) { - onexcept(optional(nullopt), error.c_str()); + detail::handle_protected_exception(lua_state(), optional(nullopt), error.c_str(), h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } catch (const std::exception& error) { - onexcept(optional(error), error.what()); + detail::handle_protected_exception(lua_state(), optional(error), error.what(), h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } @@ -123,7 +126,7 @@ namespace sol { // but LuaJIT will swallow all C++ errors // if we don't at least catch std::exception ones catch (...) { - onexcept(optional(nullopt), "caught (...) unknown error during protected_function call"); + detail::handle_protected_exception(lua_state(), optional(nullopt), detail::protected_function_error, h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } diff --git a/include/sol/stack.hpp b/include/sol/stack.hpp index 7f1365ab..3207b156 100644 --- a/include/sol/stack.hpp +++ b/include/sol/stack.hpp @@ -117,17 +117,15 @@ namespace sol { return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); } - struct evaluator { - template - static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { - return std::forward(fx)(std::forward(args)...); - } + template + static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { + return std::forward(fx)(std::forward(args)...); + } - template - static decltype(auto) eval(types, std::index_sequence, lua_State* L, int start, record& tracking, Fx&& fx, FxArgs&&... fxargs) { - return eval(types(), std::index_sequence(), L, start, tracking, std::forward(fx), std::forward(fxargs)..., stack_detail::unchecked_get(L, start + tracking.used, tracking)); - } - }; + template + static decltype(auto) eval(types, std::index_sequence, lua_State* L, int start, record& tracking, Fx&& fx, FxArgs&&... fxargs) { + return eval(types(), std::index_sequence(), L, start, tracking, std::forward(fx), std::forward(fxargs)..., stack_detail::unchecked_get(L, start + tracking.used, tracking)); + } template ::value >> inline decltype(auto) call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { @@ -137,7 +135,7 @@ namespace sol { argument_handler> handler{}; multi_check(L, start, handler); record tracking{}; - return evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); + return eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } template @@ -148,7 +146,7 @@ namespace sol { argument_handler> handler{}; multi_check(L, start, handler); record tracking{}; - evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); + eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } } // namespace stack_detail diff --git a/include/sol/stack_core.hpp b/include/sol/stack_core.hpp index 1da19274..4abe06b0 100644 --- a/include/sol/stack_core.hpp +++ b/include/sol/stack_core.hpp @@ -84,15 +84,16 @@ namespace sol { return align(alignment, size, ptr, space, required_space); } + inline void align_one(std::size_t a, std::size_t s, void*& target_alignment) { + std::size_t space = (std::numeric_limits::max)(); + target_alignment = align(a, s, target_alignment, space); + target_alignment = static_cast(static_cast(target_alignment) + s); + } + template inline std::size_t aligned_space_for(void* alignment = nullptr) { char* start = static_cast(alignment); - auto specific_align = [&alignment](std::size_t a, std::size_t s) { - std::size_t space = (std::numeric_limits::max)(); - alignment = align(a, s, alignment, space); - alignment = static_cast(static_cast(alignment) + s); - }; - (void)detail::swallow{ int{}, (specific_align(std::alignment_of::value, sizeof(Args)), int{})... }; + (void)detail::swallow{ int{}, (align_one(std::alignment_of_v, sizeof(Args), alignment), int{})... }; return static_cast(alignment) - start; } @@ -236,6 +237,61 @@ namespace sol { return static_cast(adjusted); } + inline bool attempt_alloc(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t value_align, std::size_t value_size, + std::size_t allocated_size, void*& pointer_adjusted, void*& data_adjusted) { + void* adjusted = lua_newuserdata(L, allocated_size); + pointer_adjusted = align(ptr_align, ptr_size, adjusted, allocated_size); + if (pointer_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + // subtract size of what we're going to allocate there + allocated_size -= ptr_size; + adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); + data_adjusted = align(value_align, value_size, adjusted, allocated_size); + if (data_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + return true; + } + + inline bool attempt_alloc_unique(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t real_align, std::size_t real_size, + std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) { + void* adjusted = lua_newuserdata(L, allocated_size); + pointer_adjusted = align(ptr_align, ptr_size, adjusted, allocated_size); + if (pointer_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= ptr_size; + + adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); + dx_adjusted = align(std::alignment_of_v, sizeof(unique_destructor), adjusted, allocated_size); + if (dx_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= sizeof(unique_destructor); + + adjusted = static_cast(static_cast(dx_adjusted) + sizeof(unique_destructor)); + + id_adjusted = align(std::alignment_of_v, sizeof(unique_tag), adjusted, allocated_size); + if (id_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= sizeof(unique_tag); + + adjusted = static_cast(static_cast(id_adjusted) + sizeof(unique_tag)); + data_adjusted = align(real_align, real_size, adjusted, allocated_size); + if (data_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + return true; + } + template inline T* usertype_allocate(lua_State* L) { typedef std::integral_constant bool { - void* adjusted = lua_newuserdata(L, allocated_size); - pointer_adjusted = align(std::alignment_of::value, sizeof(T*), adjusted, allocated_size); - if (pointer_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - // subtract size of what we're going to allocate there - allocated_size -= sizeof(T*); - adjusted = static_cast(static_cast(pointer_adjusted) + sizeof(T*)); - data_adjusted = align(std::alignment_of::value, sizeof(T), adjusted, allocated_size); - if (data_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - return true; - }; - bool result = attempt_alloc(L, initial_size, pointer_adjusted, data_adjusted); + bool result = attempt_alloc(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, sizeof(T), initial_size, pointer_adjusted, data_adjusted); if (!result) { // we're likely to get something that fails to perform the proper allocation a second time, // so we use the suggested_new_size bump to help us out here pointer_adjusted = nullptr; data_adjusted = nullptr; - result = attempt_alloc(L, misaligned_size, pointer_adjusted, data_adjusted); + result = attempt_alloc(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, sizeof(T), misaligned_size, pointer_adjusted, data_adjusted); if (!result) { if (pointer_adjusted == nullptr) { luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); @@ -342,43 +381,16 @@ namespace sol { void* dx_adjusted; void* id_adjusted; void* data_adjusted; - auto attempt_alloc - = [](lua_State* L, std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) - -> bool { - void* adjusted = lua_newuserdata(L, allocated_size); - pointer_adjusted = align(std::alignment_of::value, sizeof(T*), adjusted, allocated_size); - if (pointer_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - allocated_size -= sizeof(T*); - - adjusted = static_cast(static_cast(pointer_adjusted) + sizeof(T*)); - dx_adjusted = align(std::alignment_of::value, sizeof(unique_destructor), adjusted, allocated_size); - if (dx_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - allocated_size -= sizeof(unique_destructor); - - adjusted = static_cast(static_cast(dx_adjusted) + sizeof(unique_destructor)); - - id_adjusted = align(std::alignment_of::value, sizeof(unique_tag), adjusted, allocated_size); - if (id_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - allocated_size -= sizeof(unique_tag); - - adjusted = static_cast(static_cast(id_adjusted) + sizeof(unique_tag)); - data_adjusted = align(std::alignment_of::value, sizeof(Real), adjusted, allocated_size); - if (data_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - return true; - }; - bool result = attempt_alloc(L, initial_size, pointer_adjusted, dx_adjusted, id_adjusted, data_adjusted); + bool result = attempt_alloc_unique(L, + std::alignment_of_v, + sizeof(T*), + std::alignment_of_v, + sizeof(Real), + initial_size, + pointer_adjusted, + dx_adjusted, + id_adjusted, + data_adjusted); if (!result) { // we're likely to get something that fails to perform the proper allocation a second time, // so we use the suggested_new_size bump to help us out here @@ -386,7 +398,16 @@ namespace sol { dx_adjusted = nullptr; id_adjusted = nullptr; data_adjusted = nullptr; - result = attempt_alloc(L, misaligned_size, pointer_adjusted, dx_adjusted, id_adjusted, data_adjusted); + result = attempt_alloc_unique(L, + std::alignment_of_v, + sizeof(T*), + std::alignment_of_v, + sizeof(Real), + misaligned_size, + pointer_adjusted, + dx_adjusted, + id_adjusted, + data_adjusted); if (!result) { if (pointer_adjusted == nullptr) { luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); diff --git a/include/sol/stack_get_unqualified.hpp b/include/sol/stack_get_unqualified.hpp index ba594fe8..044de34c 100644 --- a/include/sol/stack_get_unqualified.hpp +++ b/include/sol/stack_get_unqualified.hpp @@ -45,6 +45,77 @@ namespace sol { namespace stack { + namespace stack_detail { + template + struct count_code_units_utf { + std::size_t needed_size; + + count_code_units_utf() : needed_size(0) { + } + + void operator()(const unicode::encoded_result er) { + needed_size += er.code_units_size; + } + }; + + template + struct copy_code_units_utf { + Ch* target_; + + copy_code_units_utf(Ch* target) : target_(target) { + } + + void operator()(const unicode::encoded_result er) { + std::memcpy(target_, er.code_units.data(), er.code_units_size * sizeof(ErCh)); + target_ += er.code_units_size; + } + }; + + template + inline void convert(const char* strb, const char* stre, F&& f) { + char32_t cp = 0; + for (const char* strtarget = strb; strtarget < stre;) { + auto dr = unicode::utf8_to_code_point(strtarget, stre); + if (dr.error != unicode::error_code::ok) { + cp = unicode::unicode_detail::replacement; + ++strtarget; + } + else { + cp = dr.codepoint; + strtarget = dr.next; + } + if constexpr(std::is_same_v) { + auto er = unicode::code_point_to_utf32(cp); + f(er); + } + else { + auto er = unicode::code_point_to_utf16(cp); + f(er); + } + } + } + + template + inline S get_into(lua_State* L, int index, record& tracking) { + typedef typename S::value_type Ch; + tracking.use(1); + size_t len; + auto utf8p = lua_tolstring(L, index, &len); + if (len < 1) + return S(); + const char* strb = utf8p; + const char* stre = utf8p + len; + stack_detail::count_code_units_utf count_units; + convert(strb, stre, count_units); + S r(count_units.needed_size, static_cast(0)); + r.resize(count_units.needed_size); + Ch* target = &r[0]; + stack_detail::copy_code_units_utf copy_units(target); + convert(strb, stre, copy_units); + return r; + } + } + template struct userdata_getter { typedef stack_detail::strip_extensible_t T; @@ -99,7 +170,7 @@ namespace sol { namespace stack { template struct unqualified_getter> { - typedef meta::unqualified_t Tu; + using Tu = meta::unqualified_t; template static void push_back_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { @@ -113,8 +184,8 @@ namespace sol { namespace stack { template static void insert_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { - using std::end; - arr.insert(end(arr), stack::get(L, -lua_size::value)); + using std::cend; + arr.insert(cend(arr), stack::get(L, -lua_size::value)); } template @@ -131,11 +202,11 @@ namespace sol { namespace stack { } static T get(lua_State* L, int relindex, record& tracking) { - return get(meta::has_key_value_pair>(), L, relindex, tracking); + return get(meta::is_associative(), L, relindex, tracking); } static T get(std::false_type, lua_State* L, int relindex, record& tracking) { - typedef typename T::value_type V; + typedef typename Tu::value_type V; return get(types(), L, relindex, tracking); } @@ -143,15 +214,50 @@ namespace sol { namespace stack { static T get(types t, lua_State* L, int relindex, record& tracking) { tracking.use(1); + // the W4 flag is really great, + // so great that it can tell my for loops (2-nested) + // below never actually terminate without hitting a "return arr;" + // where the goto's are now so it would tell + // me that the return arr at the end of this function + // is W4XXX unreachable, + // which is fair until other compilers complain + // that there isn't a return and that based on + // SOME MAGICAL FORCE + // control flow falls off the end of a non-void function + // so it needs to be there for the compilers that are + // too flimsy to analyze the basic blocks... + // (I'm sure I should file a bug but those compilers are already + // in the wild; it doesn't matter if I fix them, + // someone else is still going to get some old-ass compiler + // and then bother me about the unclean build for the 30th + // time) + + // "Why not an IIFE?" + // Because additional lambdas / functions which serve as + // capture-all-and-then-invoke bloat binary sizes + // by an actually detectable amount + // (one user uses sol2 pretty heavily and 22 MB of binary size + // was saved by reducing reliance on lambdas in templates) + + // This would really be solved by having break N; + // be a real, proper thing... + // but instead, we have to use labels and gotos + // and earn the universal vitriol of the dogmatic + // programming community + + // all in all: W4 is great!~ + int index = lua_absindex(L, relindex); T arr; std::size_t idx = 0; #if SOL_LUA_VERSION >= 503 - // This method is HIGHLY performant over regular table iteration thanks to the Lua API changes in 5.3 + // This method is HIGHLY performant over regular table iteration + // thanks to the Lua API changes in 5.3 // Questionable in 5.4 for (lua_Integer i = 0;; i += lua_size::value) { if (max_size_check(meta::has_max_size(), arr, idx)) { - return arr; + // see above comment + goto done; } bool isnil = false; for (int vi = 0; vi < lua_size::value; ++vi) { @@ -181,7 +287,8 @@ namespace sol { namespace stack { #else lua_pop(L, (vi + 1)); #endif - return arr; + // see above comment + goto done; } } if (isnil) { @@ -199,7 +306,8 @@ namespace sol { namespace stack { // Zzzz slower but necessary thanks to the lower version API and missing functions qq for (lua_Integer i = 0;; i += lua_size::value, lua_pop(L, lua_size::value)) { if (idx >= arr.max_size()) { - return arr; + // see above comment + goto done; } #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 2, detail::not_enough_stack_space_generic); @@ -215,7 +323,8 @@ namespace sol { namespace stack { break; } lua_pop(L, (vi + 1)); - return arr; + // see above comment + goto done; } } if (isnil) @@ -224,11 +333,12 @@ namespace sol { namespace stack { ++idx; } #endif + done: return arr; } static T get(std::true_type, lua_State* L, int index, record& tracking) { - typedef typename T::value_type P; + typedef typename Tu::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; return get(types(), L, index, tracking); @@ -377,29 +487,26 @@ namespace sol { namespace stack { }; template - struct unqualified_getter, - std::enable_if_t< - meta::all, meta::neg>>>::value>> { + struct unqualified_getter, std::enable_if_t::value>> { + using Tu = meta::unqualified_t; + static T get(lua_State* L, int index, record& tracking) { - typedef typename T::value_type V; - unqualified_getter> g; - // VC++ has a bad warning here: shut it up - (void)g; - return g.get(types>(), L, index, tracking); - } - }; - - template - struct unqualified_getter, - std::enable_if_t, meta::has_key_value_pair>>::value>> { - static T get(lua_State* L, int index, record& tracking) { - typedef typename T::value_type P; - typedef typename P::first_type K; - typedef typename P::second_type V; - unqualified_getter> g; - // VC++ has a bad warning here: shut it up - (void)g; - return g.get(types>(), L, index, tracking); + if constexpr(meta::is_associative::value) { + typedef typename T::value_type P; + typedef typename P::first_type K; + typedef typename P::second_type V; + unqualified_getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } + else { + typedef typename T::value_type V; + unqualified_getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } } }; @@ -513,114 +620,24 @@ namespace sol { namespace stack { template struct unqualified_getter> { - typedef std::basic_string S; + using S = std::basic_string; static S get(lua_State* L, int index, record& tracking) { - typedef std::conditional_t Ch; - typedef typename std::allocator_traits::template rebind_alloc ChAl; - typedef std::char_traits ChTraits; - unqualified_getter> g; - (void)g; - return g.template get_into(L, index, tracking); + using Ch = std::conditional_t; + return stack_detail::get_into(L, index, tracking); } }; template struct unqualified_getter> { - template - static void convert(const char* strb, const char* stre, F&& f) { - char32_t cp = 0; - for (const char* strtarget = strb; strtarget < stre;) { - auto dr = unicode::utf8_to_code_point(strtarget, stre); - if (dr.error != unicode::error_code::ok) { - cp = unicode::unicode_detail::replacement; - ++strtarget; - } - else { - cp = dr.codepoint; - strtarget = dr.next; - } - auto er = unicode::code_point_to_utf16(cp); - f(er); - } - } - - template - static S get_into(lua_State* L, int index, record& tracking) { - typedef typename S::value_type Ch; - tracking.use(1); - size_t len; - auto utf8p = lua_tolstring(L, index, &len); - if (len < 1) - return S(); - std::size_t needed_size = 0; - const char* strb = utf8p; - const char* stre = utf8p + len; - auto count_units - = [&needed_size](const unicode::encoded_result er) { needed_size += er.code_units_size; }; - convert(strb, stre, count_units); - S r(needed_size, static_cast(0)); - r.resize(needed_size); - Ch* target = &r[0]; - auto copy_units = [&target](const unicode::encoded_result er) { - std::memcpy(target, er.code_units.data(), er.code_units_size * sizeof(Ch)); - target += er.code_units_size; - }; - convert(strb, stre, copy_units); - return r; - } - static std::basic_string get(lua_State* L, int index, record& tracking) { - return get_into>(L, index, tracking); + return stack_detail::get_into>(L, index, tracking); } }; template struct unqualified_getter> { - template - static void convert(const char* strb, const char* stre, F&& f) { - char32_t cp = 0; - for (const char* strtarget = strb; strtarget < stre;) { - auto dr = unicode::utf8_to_code_point(strtarget, stre); - if (dr.error != unicode::error_code::ok) { - cp = unicode::unicode_detail::replacement; - ++strtarget; - } - else { - cp = dr.codepoint; - strtarget = dr.next; - } - auto er = unicode::code_point_to_utf32(cp); - f(er); - } - } - - template - static S get_into(lua_State* L, int index, record& tracking) { - typedef typename S::value_type Ch; - tracking.use(1); - size_t len; - auto utf8p = lua_tolstring(L, index, &len); - if (len < 1) - return S(); - std::size_t needed_size = 0; - const char* strb = utf8p; - const char* stre = utf8p + len; - auto count_units - = [&needed_size](const unicode::encoded_result er) { needed_size += er.code_units_size; }; - convert(strb, stre, count_units); - S r(needed_size, static_cast(0)); - r.resize(needed_size); - Ch* target = &r[0]; - auto copy_units = [&target](const unicode::encoded_result er) { - std::memcpy(target, er.code_units.data(), er.code_units_size * sizeof(Ch)); - target += er.code_units_size; - }; - convert(strb, stre, copy_units); - return r; - } - static std::basic_string get(lua_State* L, int index, record& tracking) { - return get_into>(L, index, tracking); + return stack_detail::get_into>(L, index, tracking); } }; diff --git a/include/sol/state_view.hpp b/include/sol/state_view.hpp index be62ff1a..50f604dd 100644 --- a/include/sol/state_view.hpp +++ b/include/sol/state_view.hpp @@ -111,7 +111,7 @@ namespace sol { template void open_libraries(Args&&... args) { static_assert(meta::all_same::value, "all types must be libraries"); - if (sizeof...(args) == 0) { + if constexpr (sizeof...(args) == 0) { luaL_openlibs(L); return; } diff --git a/include/sol/types.hpp b/include/sol/types.hpp index 7db296a6..ed479c51 100644 --- a/include/sol/types.hpp +++ b/include/sol/types.hpp @@ -465,6 +465,39 @@ namespace sol { return nested>(std::forward(container)); } + 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)); + } + struct this_state { lua_State* L; diff --git a/include/sol/unicode.hpp b/include/sol/unicode.hpp index 7d997fd4..109aa59f 100644 --- a/include/sol/unicode.hpp +++ b/include/sol/unicode.hpp @@ -77,6 +77,19 @@ namespace sol { static constexpr auto continuation_mask = 0xC0u; static constexpr auto continuation_signature = 0x80u; + static constexpr bool is_invalid(unsigned char b) { + return b == 0xC0 || b == 0xC1 || b > 0xF4; + } + + static constexpr bool is_continuation(unsigned char b) { + return (b & unicode_detail::continuation_mask) == unicode_detail::continuation_signature; + } + + static constexpr bool is_overlong(char32_t u, std::size_t bytes) { + return u <= unicode_detail::last_1byte_value || (u <= unicode_detail::last_2byte_value && bytes > 2) + || (u <= unicode_detail::last_3byte_value && bytes > 3); + } + static constexpr int sequence_length(unsigned char b) { return (b & start_2byte_mask) == 0 ? 1 : (b & start_3byte_mask) != start_3byte_mask ? 2 @@ -193,12 +206,7 @@ namespace sol { return dr; } - auto is_invalid = [](unsigned char b) { return b == 0xC0 || b == 0xC1 || b > 0xF4; }; - auto is_continuation = [](unsigned char b) { - return (b & unicode_detail::continuation_mask) == unicode_detail::continuation_signature; - }; - - if (is_invalid(b0) || is_continuation(b0)) { + if (unicode_detail::is_invalid(b0) || unicode_detail::is_continuation(b0)) { dr.error = error_code::invalid_code_unit; dr.next = it; return dr; @@ -209,7 +217,7 @@ namespace sol { b[0] = b0; for (std::size_t i = 1; i < length; ++i) { b[i] = *it; - if (!is_continuation(b[i])) { + if (!unicode_detail::is_continuation(b[i])) { dr.error = error_code::invalid_code_unit; dr.next = it; return dr; @@ -230,12 +238,7 @@ namespace sol { break; } - auto is_overlong = [](char32_t u, std::size_t bytes) { - return u <= unicode_detail::last_1byte_value - || (u <= unicode_detail::last_2byte_value && bytes > 2) - || (u <= unicode_detail::last_3byte_value && bytes > 3); - }; - if (is_overlong(decoded, length)) { + if (unicode_detail::is_overlong(decoded, length)) { dr.error = error_code::overlong_sequence; return dr; } diff --git a/include/sol/usertype.hpp b/include/sol/usertype.hpp index 5b23570a..c53d97ac 100644 --- a/include/sol/usertype.hpp +++ b/include/sol/usertype.hpp @@ -25,7 +25,7 @@ #define SOL_USERTYPE_HPP #include "usertype_core.hpp" -#include "usertype_container.hpp" +#include "usertype_container_launch.hpp" #include "usertype_storage.hpp" #include "usertype_proxy.hpp" #include "metatable.hpp" @@ -47,9 +47,14 @@ namespace sol { void tuple_set(std::index_sequence, std::tuple&& args) { using args_tuple = std::tuple&&; optional&> maybe_uts = u_detail::maybe_get_usertype_storage(this->lua_state()); - if (maybe_uts) { - u_detail::usertype_storage& uts = *maybe_uts; - detail::swallow{ 0, (uts.set(this->lua_state(), std::get(std::forward(args)), std::get(std::forward(args))), 0)... }; + if constexpr(sizeof...(I) > 0) { + if (maybe_uts) { + u_detail::usertype_storage& uts = *maybe_uts; + detail::swallow{ 0, (uts.set(this->lua_state(), std::get(std::forward(args)), std::get(std::forward(args))), 0)... }; + } + } + else { + (void)args; } } diff --git a/include/sol/usertype_container.hpp b/include/sol/usertype_container.hpp index ea87a611..84e406f9 100644 --- a/include/sol/usertype_container.hpp +++ b/include/sol/usertype_container.hpp @@ -24,442 +24,1407 @@ #ifndef SOL_USERTYPE_CONTAINER_HPP #define SOL_USERTYPE_CONTAINER_HPP +#include "traits.hpp" #include "stack.hpp" -#include "container_traits.hpp" +#include "object.hpp" #include "map.hpp" namespace sol { - template - struct usertype_container { - typedef std::remove_pointer_t> T; - typedef container_traits traits; - typedef container_detail::container_traits_default default_traits; + template + struct usertype_container; - static inline int real_index_get_traits(std::true_type, lua_State* L) { - return traits::index_get(L); + 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_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_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); } - static inline int real_index_get_traits(std::false_type, lua_State* L) { - return default_traits::index_get(L); + template + decltype(auto) get_key(std::true_type, T&& t) { + return t.first; } - static inline int real_index_call(lua_State* L) { - typedef detail::unordered_map call_map; - static const call_map calls{ - { "at", &at_call }, - { "get", &real_get_call }, - { "set", &real_set_call }, - { "size", &real_length_call }, - { "add", &real_add_call }, - { "empty", &real_empty_call }, - { "insert", &real_insert_call }, - { "clear", &real_clear_call }, - { "find", &real_find_call }, - { "index_of", &real_index_of_call }, - { "erase", &real_erase_call }, - { "pairs", &pairs_call }, - { "next", &next_call }, + 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 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 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 value_type = typename T::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) { + } }; - auto maybenameview = stack::unqualified_check_get(L, 2); - if (maybenameview) { - const string_view& nameview = *maybenameview; -#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH - auto it = calls.find(nameview, string_view_hash(), std::equal_to()); + + 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 - std::string name(nameview.data(), nameview.size()); - auto it = calls.find(name); -#endif - if (it != calls.cend()) { - return stack::push(L, it->second); + 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); + } + + static detail::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 detail::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 detail::error_result get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) { + 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 += 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) { + auto& v = *it; + v.second = value.as(); + return {}; + } + + static detail::error_result set_associative(std::false_type, iterator& it, stack_object value) { + 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 += 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(); + if (key <= 0) { + return detail::error_result("sol: out of bounds (too small) for set on '%s'", detail::demangle().c_str()); + } + key += deferred_uc::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 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()); + } + + static detail::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 detail::error_result set_associative_insert(std::false_type, lua_State*, T& self, iterator& it, K& key, stack_object) { + self.insert(it, key); + return {}; + } + + 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); + } } } - return real_index_get_traits(container_detail::has_traits_index_get(), L); - } - static inline int real_at_traits(std::true_type, lua_State* L) { - return traits::at(L); - } - - static inline int real_at_traits(std::false_type, lua_State* L) { - return default_traits::at(L); - } - - static inline int real_at_call(lua_State* L) { - return real_at_traits(container_detail::has_traits_at(), L); - } - - static inline int real_get_traits(std::true_type, lua_State* L) { - return traits::get(L); - } - - static inline int real_get_traits(std::false_type, lua_State* L) { - return default_traits::get(L); - } - - static inline int real_get_call(lua_State* L) { - return real_get_traits(container_detail::has_traits_get(), L); - } - - static inline int real_set_traits(std::true_type, lua_State* L) { - return traits::set(L); - } - - static inline int real_set_traits(std::false_type, lua_State* L) { - return default_traits::set(L); - } - - static inline int real_set_call(lua_State* L) { - return real_set_traits(container_detail::has_traits_set(), L); - } - - static inline int real_index_set_traits(std::true_type, lua_State* L) { - return traits::index_set(L); - } - - static inline int real_index_set_traits(std::false_type, lua_State* L) { - return default_traits::index_set(L); - } - - static inline int real_new_index_call(lua_State* L) { - return real_index_set_traits(container_detail::has_traits_index_set(), L); - } - - static inline int real_pairs_traits(std::true_type, lua_State* L) { - return traits::pairs(L); - } - - static inline int real_pairs_traits(std::false_type, lua_State* L) { - return default_traits::pairs(L); - } - - static inline int real_pairs_call(lua_State* L) { - return real_pairs_traits(container_detail::has_traits_pairs(), L); - } - - static inline int real_ipairs_traits(std::true_type, lua_State* L) { - return traits::ipairs(L); - } - - static inline int real_ipairs_traits(std::false_type, lua_State* L) { - return default_traits::ipairs(L); - } - - static inline int real_ipairs_call(lua_State* L) { - return real_ipairs_traits(container_detail::has_traits_ipairs(), L); - } - - static inline int real_next_traits(std::true_type, lua_State* L) { - return traits::next(L); - } - - static inline int real_next_traits(std::false_type, lua_State* L) { - return default_traits::next(L); - } - - static inline int real_next_call(lua_State* L) { - return real_next_traits(container_detail::has_traits_next(), L); - } - - static inline int real_size_traits(std::true_type, lua_State* L) { - return traits::size(L); - } - - static inline int real_size_traits(std::false_type, lua_State* L) { - return default_traits::size(L); - } - - static inline int real_length_call(lua_State* L) { - return real_size_traits(container_detail::has_traits_size(), L); - } - - static inline int real_add_traits(std::true_type, lua_State* L) { - return traits::add(L); - } - - static inline int real_add_traits(std::false_type, lua_State* L) { - return default_traits::add(L); - } - - static inline int real_add_call(lua_State* L) { - return real_add_traits(container_detail::has_traits_add(), L); - } - - static inline int real_insert_traits(std::true_type, lua_State* L) { - return traits::insert(L); - } - - static inline int real_insert_traits(std::false_type, lua_State* L) { - return default_traits::insert(L); - } - - static inline int real_insert_call(lua_State* L) { - return real_insert_traits(container_detail::has_traits_insert(), L); - } - - static inline int real_clear_traits(std::true_type, lua_State* L) { - return traits::clear(L); - } - - static inline int real_clear_traits(std::false_type, lua_State* L) { - return default_traits::clear(L); - } - - static inline int real_clear_call(lua_State* L) { - return real_clear_traits(container_detail::has_traits_clear(), L); - } - - static inline int real_empty_traits(std::true_type, lua_State* L) { - return traits::empty(L); - } - - static inline int real_empty_traits(std::false_type, lua_State* L) { - return default_traits::empty(L); - } - - static inline int real_empty_call(lua_State* L) { - return real_empty_traits(container_detail::has_traits_empty(), L); - } - - static inline int real_erase_traits(std::true_type, lua_State* L) { - return traits::erase(L); - } - - static inline int real_erase_traits(std::false_type, lua_State* L) { - return default_traits::erase(L); - } - - static inline int real_erase_call(lua_State* L) { - return real_erase_traits(container_detail::has_traits_erase(), L); - } - - static inline int real_find_traits(std::true_type, lua_State* L) { - return traits::find(L); - } - - static inline int real_find_traits(std::false_type, lua_State* L) { - return default_traits::find(L); - } - - static inline int real_find_call(lua_State* L) { - return real_find_traits(container_detail::has_traits_find(), L); - } - - static inline int real_index_of_traits(std::true_type, lua_State* L) { - return traits::index_of(L); - } - - static inline int real_index_of_traits(std::false_type, lua_State* L) { - return default_traits::index_of(L); - } - - static inline int real_index_of_call(lua_State* L) { - return real_index_of_traits(container_detail::has_traits_index_of(), L); - } - - static inline int add_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int erase_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int insert_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int clear_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int empty_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int find_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int index_of_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int length_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int pairs_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int ipairs_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int next_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int at_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int get_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int set_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int index_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int new_index_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - }; - - namespace stack { - namespace stack_detail { - template - struct metatable_setup { - lua_State* L; - - metatable_setup(lua_State* L) - : L(L) { + 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()); } - - void operator()() { - typedef usertype_container>, - std::remove_pointer_t>> - meta_usertype_container; - static const char* metakey = is_shim ? &usertype_traits>>::metatable()[0] : &usertype_traits::metatable()[0]; - static const std::array reg = { { - // clang-format off - { "__pairs", &meta_usertype_container::pairs_call }, - { "__ipairs", &meta_usertype_container::ipairs_call }, - { "__len", &meta_usertype_container::length_call }, - { "__index", &meta_usertype_container::index_call }, - { "__newindex", &meta_usertype_container::new_index_call }, - { "pairs", &meta_usertype_container::pairs_call }, - { "next", &meta_usertype_container::next_call }, - { "at", &meta_usertype_container::at_call }, - { "get", &meta_usertype_container::get_call }, - { "set", &meta_usertype_container::set_call }, - { "size", &meta_usertype_container::length_call }, - { "empty", &meta_usertype_container::empty_call }, - { "clear", &meta_usertype_container::clear_call }, - { "insert", &meta_usertype_container::insert_call }, - { "add", &meta_usertype_container::add_call }, - { "find", &meta_usertype_container::find_call }, - { "index_of", &meta_usertype_container::index_of_call }, - { "erase", &meta_usertype_container::erase_call }, - std::is_pointer::value ? luaL_Reg{ nullptr, nullptr } : luaL_Reg{ "__gc", &detail::usertype_alloc_destruct }, - { nullptr, nullptr } - // clang-format on - } }; - - if (luaL_newmetatable(L, metakey) == 1) { - luaL_setfuncs(L, reg.data(), 0); + 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); } - lua_setmetatable(L, -2); + 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); + } + + static detail::error_result find_associative_lookup(std::true_type, lua_State* L, T&, iterator& it, std::size_t) { + return get_associative(is_associative(), L, it); + } + + static detail::error_result find_associative_lookup(std::false_type, lua_State* L, T& self, iterator&, std::size_t idx) { + 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); + } + + static detail::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 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()); + } + + static detail::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 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); + } + + static detail::error_result add_insert(std::true_type, lua_State*, T& self, stack_object value, iterator& 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); + } + + static detail::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 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)); + } + + static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value, iterator&) { + 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 {}; + } + + static detail::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 detail::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 detail::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 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); + } + + static detail::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 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); + } + + static detail::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 detail::error_result add_copyable(std::true_type, lua_State* L, T& self, stack_object value) { + return add_associative(is_associative(), L, self, value); + } + + static detail::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 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 += 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 += 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(meta::has_insert(), 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 += 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 += 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_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_after_has(has_erase_after(), 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_start(lua_State* L, T& self, K& key) { + return get_it(is_linear_integral(), 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 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& source = i.source; + auto& it = i.it; + if (it == deferred_uc::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_uc::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_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, src, deferred_uc::begin(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); + 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 (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) { + 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); + 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&) { +#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 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; + + private: + struct iter { + T& source; + iterator it; + + iter(T& source, iterator it) + : source(source), it(std::move(it)) { } }; - } // namespace stack_detail - template - struct unqualified_pusher> { - typedef meta::unqualified_t C; - - static int push_lvalue(std::true_type, lua_State* L, const C& cont) { - stack_detail::metatable_setup fx(L); - return stack::push>(L, detail::with_function_tag(), fx, detail::ptr(cont)); + 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 push_lvalue(std::false_type, lua_State* L, const C& cont) { - stack_detail::metatable_setup fx(L); - return stack::push>(L, detail::with_function_tag(), fx, cont); + 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) { + idx -= deferred_uc::index_adjustment(L, self); + return stack::push(L, idx); + } + } + return stack::push(L, lua_nil); } - static int push_rvalue(std::true_type, lua_State* L, C&& cont) { - stack_detail::metatable_setup fx(L); - return stack::push>(L, detail::with_function_tag(), fx, std::move(cont)); + 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 push_rvalue(std::false_type, lua_State* L, const C& cont) { - return push_lvalue(std::is_lvalue_reference(), L, cont); + 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_uc::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; } - static int push(lua_State* L, const as_container_t& as_cont) { - return push_lvalue(std::is_lvalue_reference(), L, as_cont.source); + 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 push(lua_State* L, as_container_t&& as_cont) { - return push_rvalue(meta::all, meta::neg>>(), L, std::forward(as_cont.source)); + 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_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_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, src, deferred_uc::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 unqualified_pusher> { - typedef std::add_pointer_t>> C; + template + struct usertype_container_default> : usertype_container_default {}; + } // namespace container_detail - static int push(lua_State* L, T* cont) { - stack_detail::metatable_setup fx(L); - return stack::push>(L, detail::with_function_tag(), fx, cont); - } - }; - - template - struct unqualified_pusher>, meta::neg>>>::value>> { - typedef meta::unqualified_t C; - - static int push(lua_State* L, const T& cont) { - stack_detail::metatable_setup fx(L); - return stack::push>(L, detail::with_function_tag(), fx, cont); - } - - static int push(lua_State* L, T&& cont) { - stack_detail::metatable_setup fx(L); - return stack::push>(L, detail::with_function_tag(), fx, std::move(cont)); - } - }; - - template - struct unqualified_pusher>, meta::neg>>>::value>> { - typedef std::add_pointer_t>> C; - - static int push(lua_State* L, T* cont) { - stack_detail::metatable_setup fx(L); - return stack::push>(L, detail::with_function_tag(), fx, cont); - } - }; - - template - struct unqualified_checker, type::userdata, C> { - template - static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { - return stack::check(L, index, std::forward(handler), tracking); - } - }; - - template - struct unqualified_getter> { - static decltype(auto) get(lua_State* L, int index, record& tracking) { - return stack::unqualified_get(L, index, tracking); - } - }; - - template - struct unqualified_getter*> { - static decltype(auto) get(lua_State* L, int index, record& tracking) { - return stack::unqualified_get(L, index, tracking); - } - }; - } // namespace stack + template + struct usertype_container : container_detail::usertype_container_default {}; } // namespace sol diff --git a/include/sol/usertype_container_launch.hpp b/include/sol/usertype_container_launch.hpp new file mode 100644 index 00000000..4f4482c6 --- /dev/null +++ b/include/sol/usertype_container_launch.hpp @@ -0,0 +1,458 @@ +// sol3 + +// The MIT License (MIT) + +// Copyright (c) 2013-2018 Rapptz, ThePhD and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_USERTYPE_CONTAINER_LAUNCH_HPP +#define SOL_USERTYPE_CONTAINER_LAUNCH_HPP + +#include "stack.hpp" +#include "usertype_container.hpp" +#include "map.hpp" + +namespace sol { + + namespace container_detail { + template + struct u_c_launch { + using T = std::remove_pointer_t>; + using uc = usertype_container; + using default_uc = usertype_container_default; + + static inline int real_index_get_traits(std::true_type, lua_State* L) { + return uc::index_get(L); + } + + static inline int real_index_get_traits(std::false_type, lua_State* L) { + return default_uc::index_get(L); + } + + static inline int real_index_call(lua_State* L) { + typedef detail::unordered_map call_map; + static const call_map calls{ + { "at", &real_at_call }, + { "get", &real_get_call }, + { "set", &real_set_call }, + { "size", &real_length_call }, + { "add", &real_add_call }, + { "empty", &real_empty_call }, + { "insert", &real_insert_call }, + { "clear", &real_clear_call }, + { "find", &real_find_call }, + { "index_of", &real_index_of_call }, + { "erase", &real_erase_call }, + { "pairs", &pairs_call }, + { "next", &next_call }, + }; + auto maybenameview = stack::unqualified_check_get(L, 2); + if (maybenameview) { + const string_view& name = *maybenameview; + auto it = calls.find(name); + if (it != calls.cend()) { + return stack::push(L, it->second); + } + } + return real_index_get_traits(container_detail::has_traits_index_get(), L); + } + + static inline int real_at_traits(std::true_type, lua_State* L) { + return uc::at(L); + } + + static inline int real_at_traits(std::false_type, lua_State* L) { + return default_uc::at(L); + } + + static inline int real_at_call(lua_State* L) { + return real_at_traits(container_detail::has_traits_at(), L); + } + + static inline int real_get_traits(std::true_type, lua_State* L) { + return uc::get(L); + } + + static inline int real_get_traits(std::false_type, lua_State* L) { + return default_uc::get(L); + } + + static inline int real_get_call(lua_State* L) { + return real_get_traits(container_detail::has_traits_get(), L); + } + + static inline int real_set_traits(std::true_type, lua_State* L) { + return uc::set(L); + } + + static inline int real_set_traits(std::false_type, lua_State* L) { + return default_uc::set(L); + } + + static inline int real_set_call(lua_State* L) { + return real_set_traits(container_detail::has_traits_set(), L); + } + + static inline int real_index_set_traits(std::true_type, lua_State* L) { + return uc::index_set(L); + } + + static inline int real_index_set_traits(std::false_type, lua_State* L) { + return default_uc::index_set(L); + } + + static inline int real_new_index_call(lua_State* L) { + return real_index_set_traits(container_detail::has_traits_index_set(), L); + } + + static inline int real_pairs_traits(std::true_type, lua_State* L) { + return uc::pairs(L); + } + + static inline int real_pairs_traits(std::false_type, lua_State* L) { + return default_uc::pairs(L); + } + + static inline int real_pairs_call(lua_State* L) { + return real_pairs_traits(container_detail::has_traits_pairs(), L); + } + + static inline int real_ipairs_traits(std::true_type, lua_State* L) { + return uc::ipairs(L); + } + + static inline int real_ipairs_traits(std::false_type, lua_State* L) { + return default_uc::ipairs(L); + } + + static inline int real_ipairs_call(lua_State* L) { + return real_ipairs_traits(container_detail::has_traits_ipairs(), L); + } + + static inline int real_next_traits(std::true_type, lua_State* L) { + return uc::next(L); + } + + static inline int real_next_traits(std::false_type, lua_State* L) { + return default_uc::next(L); + } + + static inline int real_next_call(lua_State* L) { + return real_next_traits(container_detail::has_traits_next(), L); + } + + static inline int real_size_traits(std::true_type, lua_State* L) { + return uc::size(L); + } + + static inline int real_size_traits(std::false_type, lua_State* L) { + return default_uc::size(L); + } + + static inline int real_length_call(lua_State* L) { + return real_size_traits(container_detail::has_traits_size(), L); + } + + static inline int real_add_traits(std::true_type, lua_State* L) { + return uc::add(L); + } + + static inline int real_add_traits(std::false_type, lua_State* L) { + return default_uc::add(L); + } + + static inline int real_add_call(lua_State* L) { + return real_add_traits(container_detail::has_traits_add(), L); + } + + static inline int real_insert_traits(std::true_type, lua_State* L) { + return uc::insert(L); + } + + static inline int real_insert_traits(std::false_type, lua_State* L) { + return default_uc::insert(L); + } + + static inline int real_insert_call(lua_State* L) { + return real_insert_traits(container_detail::has_traits_insert(), L); + } + + static inline int real_clear_traits(std::true_type, lua_State* L) { + return uc::clear(L); + } + + static inline int real_clear_traits(std::false_type, lua_State* L) { + return default_uc::clear(L); + } + + static inline int real_clear_call(lua_State* L) { + return real_clear_traits(container_detail::has_traits_clear(), L); + } + + static inline int real_empty_traits(std::true_type, lua_State* L) { + return uc::empty(L); + } + + static inline int real_empty_traits(std::false_type, lua_State* L) { + return default_uc::empty(L); + } + + static inline int real_empty_call(lua_State* L) { + return real_empty_traits(container_detail::has_traits_empty(), L); + } + + static inline int real_erase_traits(std::true_type, lua_State* L) { + return uc::erase(L); + } + + static inline int real_erase_traits(std::false_type, lua_State* L) { + return default_uc::erase(L); + } + + static inline int real_erase_call(lua_State* L) { + return real_erase_traits(container_detail::has_traits_erase(), L); + } + + static inline int real_find_traits(std::true_type, lua_State* L) { + return uc::find(L); + } + + static inline int real_find_traits(std::false_type, lua_State* L) { + return default_uc::find(L); + } + + static inline int real_find_call(lua_State* L) { + return real_find_traits(container_detail::has_traits_find(), L); + } + + static inline int real_index_of_call(lua_State* L) { + if constexpr(container_detail::has_traits_index_of()) { + return uc::index_of(L); + } + else { + return default_uc::index_of(L); + } + } + + static inline int add_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int erase_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int insert_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int clear_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int empty_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int find_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int index_of_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int length_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int pairs_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int ipairs_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int next_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int at_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int get_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int set_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int index_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + + static inline int new_index_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + }; + } // namespace container_detail + + namespace stack { + namespace stack_detail { + template + struct metatable_setup { + lua_State* L; + + metatable_setup(lua_State* L) + : L(L) { + } + + void operator()() { + using meta_usertype_container = container_detail::u_c_launch< + std::conditional_t>, std::remove_pointer_t>>; + static const char* metakey = is_shim ? &usertype_traits>>::metatable()[0] : &usertype_traits::metatable()[0]; + static const std::array reg = { { + // clang-format off + { "__pairs", &meta_usertype_container::pairs_call }, + { "__ipairs", &meta_usertype_container::ipairs_call }, + { "__len", &meta_usertype_container::length_call }, + { "__index", &meta_usertype_container::index_call }, + { "__newindex", &meta_usertype_container::new_index_call }, + { "pairs", &meta_usertype_container::pairs_call }, + { "next", &meta_usertype_container::next_call }, + { "at", &meta_usertype_container::at_call }, + { "get", &meta_usertype_container::get_call }, + { "set", &meta_usertype_container::set_call }, + { "size", &meta_usertype_container::length_call }, + { "empty", &meta_usertype_container::empty_call }, + { "clear", &meta_usertype_container::clear_call }, + { "insert", &meta_usertype_container::insert_call }, + { "add", &meta_usertype_container::add_call }, + { "find", &meta_usertype_container::find_call }, + { "index_of", &meta_usertype_container::index_of_call }, + { "erase", &meta_usertype_container::erase_call }, + std::is_pointer::value ? luaL_Reg{ nullptr, nullptr } : luaL_Reg{ "__gc", &detail::usertype_alloc_destruct }, + { nullptr, nullptr } + // clang-format on + } }; + + if (luaL_newmetatable(L, metakey) == 1) { + luaL_setfuncs(L, reg.data(), 0); + } + lua_setmetatable(L, -2); + } + }; + } // namespace stack_detail + + template + struct unqualified_pusher> { + typedef meta::unqualified_t C; + + static int push_lvalue(std::true_type, lua_State* L, const C& cont) { + stack_detail::metatable_setup fx(L); + return stack::push>(L, detail::with_function_tag(), fx, detail::ptr(cont)); + } + + static int push_lvalue(std::false_type, lua_State* L, const C& cont) { + stack_detail::metatable_setup fx(L); + return stack::push>(L, detail::with_function_tag(), fx, cont); + } + + static int push_rvalue(std::true_type, lua_State* L, C&& cont) { + stack_detail::metatable_setup fx(L); + return stack::push>(L, detail::with_function_tag(), fx, std::move(cont)); + } + + static int push_rvalue(std::false_type, lua_State* L, const C& cont) { + return push_lvalue(std::is_lvalue_reference(), L, cont); + } + + static int push(lua_State* L, const as_container_t& as_cont) { + return push_lvalue(std::is_lvalue_reference(), L, as_cont.source); + } + + static int push(lua_State* L, as_container_t&& as_cont) { + return push_rvalue(meta::all, meta::neg>>(), L, std::forward(as_cont.source)); + } + }; + + template + struct unqualified_pusher> { + typedef std::add_pointer_t>> C; + + static int push(lua_State* L, T* cont) { + stack_detail::metatable_setup fx(L); + return stack::push>(L, detail::with_function_tag(), fx, cont); + } + }; + + template + struct unqualified_pusher>, meta::neg>>>::value>> { + typedef meta::unqualified_t C; + + static int push(lua_State* L, const T& cont) { + stack_detail::metatable_setup fx(L); + return stack::push>(L, detail::with_function_tag(), fx, cont); + } + + static int push(lua_State* L, T&& cont) { + stack_detail::metatable_setup fx(L); + return stack::push>(L, detail::with_function_tag(), fx, std::move(cont)); + } + }; + + template + struct unqualified_pusher>, meta::neg>>>::value>> { + typedef std::add_pointer_t>> C; + + static int push(lua_State* L, T* cont) { + stack_detail::metatable_setup fx(L); + return stack::push>(L, detail::with_function_tag(), fx, cont); + } + }; + + template + struct unqualified_checker, type::userdata, C> { + template + static bool check(lua_State* L, int index, Handler&& handler, record& tracking) { + return stack::check(L, index, std::forward(handler), tracking); + } + }; + + template + struct unqualified_getter> { + static decltype(auto) get(lua_State* L, int index, record& tracking) { + return stack::unqualified_get(L, index, tracking); + } + }; + + template + struct unqualified_getter*> { + static decltype(auto) get(lua_State* L, int index, record& tracking) { + return stack::unqualified_get(L, index, tracking); + } + }; + } // namespace stack + +} // namespace sol + +#endif // SOL_USERTYPE_CONTAINER_LAUNCH_HPP diff --git a/include/sol/usertype_core.hpp b/include/sol/usertype_core.hpp index 27975ac4..818f5059 100644 --- a/include/sol/usertype_core.hpp +++ b/include/sol/usertype_core.hpp @@ -34,7 +34,7 @@ #include "deprecate.hpp" #include "object.hpp" #include "function_types.hpp" -#include "usertype_container.hpp" +#include "usertype_container_launch.hpp" #include #include @@ -141,7 +141,7 @@ namespace sol { ifx(meta_function::equal_to, f); } if (fx(meta_function::pairs)) { - ifx(meta_function::pairs, &usertype_container>::pairs_call); + ifx(meta_function::pairs, &container_detail::u_c_launch>::pairs_call); } if (fx(meta_function::length)) { if constexpr (meta::has_size::value || meta::has_size::value) { diff --git a/include/sol/usertype_storage.hpp b/include/sol/usertype_storage.hpp index 04ff5635..c0b78d86 100644 --- a/include/sol/usertype_storage.hpp +++ b/include/sol/usertype_storage.hpp @@ -942,7 +942,7 @@ namespace sol { namespace u_detail { stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast(&detail::inheritance::type_cast), t.stack_index()); auto prop_fx = detail::properties_enrollment_allowed(storage.properties, enrollments); - auto insert_fx = [&](meta_function mf, lua_CFunction reg) { + auto insert_fx = [&L, &t, &storage](meta_function mf, lua_CFunction reg) { stack::set_field(L, mf, reg, t.stack_index()); storage.properties[static_cast(mf)] = true; }; diff --git a/single/include/sol/forward.hpp b/single/include/sol/forward.hpp index a9bf8104..1ea564c8 100644 --- a/single/include/sol/forward.hpp +++ b/single/include/sol/forward.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2019-01-05 18:55:40.545875 UTC -// This header was generated with sol v2.20.6 (revision d9f973e) +// Generated 2019-01-14 02:43:17.627340 UTC +// This header was generated with sol v2.20.6 (revision 91faa7a) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_FORWARD_HPP @@ -366,6 +366,9 @@ namespace sol { struct this_main_state; struct this_environment; + class state_view; + class state; + template struct as_table_t; template diff --git a/single/include/sol/sol.hpp b/single/include/sol/sol.hpp index 25580220..cb9fd195 100644 --- a/single/include/sol/sol.hpp +++ b/single/include/sol/sol.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2019-01-05 18:55:40.295019 UTC -// This header was generated with sol v2.20.6 (revision d9f973e) +// Generated 2019-01-14 02:43:17.362011 UTC +// This header was generated with sol v2.20.6 (revision 91faa7a) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -394,6 +394,9 @@ namespace sol { struct this_main_state; struct this_environment; + class state_view; + class state; + template struct as_table_t; template @@ -6248,6 +6251,39 @@ namespace sol { return nested>(std::forward(container)); } + 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)); + } + struct this_state { lua_State* L; @@ -7609,6 +7645,15 @@ namespace sol { constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol2 userdata"; constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees"; constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment"; + constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call"; + + inline void accumulate_and_mark(const std::string& n, std::string& addendum, int& marker) { + if (marker > 0) { + addendum += ", "; + } + addendum += n; + ++marker; + } } inline std::string associated_type_name(lua_State* L, int index, type t) { @@ -7691,14 +7736,7 @@ namespace sol { addendum += detail::demangle(); addendum += "("; int marker = 0; - auto action = [&addendum, &marker](const std::string& n) { - if (marker > 0) { - addendum += ", "; - } - addendum += n; - ++marker; - }; - (void)detail::swallow{int(), (action(detail::demangle()), int())...}; + (void)detail::swallow{int(), (detail::accumulate_and_mark(detail::demangle(), addendum, marker), int())...}; addendum += ")')"; return type_panic_string(L, index, expected, actual, message.empty() ? addendum : message + " " + addendum); } @@ -8591,15 +8629,16 @@ namespace sol { return align(alignment, size, ptr, space, required_space); } + inline void align_one(std::size_t a, std::size_t s, void*& target_alignment) { + std::size_t space = (std::numeric_limits::max)(); + target_alignment = align(a, s, target_alignment, space); + target_alignment = static_cast(static_cast(target_alignment) + s); + } + template inline std::size_t aligned_space_for(void* alignment = nullptr) { char* start = static_cast(alignment); - auto specific_align = [&alignment](std::size_t a, std::size_t s) { - std::size_t space = (std::numeric_limits::max)(); - alignment = align(a, s, alignment, space); - alignment = static_cast(static_cast(alignment) + s); - }; - (void)detail::swallow{ int{}, (specific_align(std::alignment_of::value, sizeof(Args)), int{})... }; + (void)detail::swallow{ int{}, (align_one(std::alignment_of_v, sizeof(Args), alignment), int{})... }; return static_cast(alignment) - start; } @@ -8743,6 +8782,61 @@ namespace sol { return static_cast(adjusted); } + inline bool attempt_alloc(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t value_align, std::size_t value_size, + std::size_t allocated_size, void*& pointer_adjusted, void*& data_adjusted) { + void* adjusted = lua_newuserdata(L, allocated_size); + pointer_adjusted = align(ptr_align, ptr_size, adjusted, allocated_size); + if (pointer_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + // subtract size of what we're going to allocate there + allocated_size -= ptr_size; + adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); + data_adjusted = align(value_align, value_size, adjusted, allocated_size); + if (data_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + return true; + } + + inline bool attempt_alloc_unique(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t real_align, std::size_t real_size, + std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) { + void* adjusted = lua_newuserdata(L, allocated_size); + pointer_adjusted = align(ptr_align, ptr_size, adjusted, allocated_size); + if (pointer_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= ptr_size; + + adjusted = static_cast(static_cast(pointer_adjusted) + ptr_size); + dx_adjusted = align(std::alignment_of_v, sizeof(unique_destructor), adjusted, allocated_size); + if (dx_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= sizeof(unique_destructor); + + adjusted = static_cast(static_cast(dx_adjusted) + sizeof(unique_destructor)); + + id_adjusted = align(std::alignment_of_v, sizeof(unique_tag), adjusted, allocated_size); + if (id_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + allocated_size -= sizeof(unique_tag); + + adjusted = static_cast(static_cast(id_adjusted) + sizeof(unique_tag)); + data_adjusted = align(real_align, real_size, adjusted, allocated_size); + if (data_adjusted == nullptr) { + lua_pop(L, 1); + return false; + } + return true; + } + template inline T* usertype_allocate(lua_State* L) { typedef std::integral_constant bool { - void* adjusted = lua_newuserdata(L, allocated_size); - pointer_adjusted = align(std::alignment_of::value, sizeof(T*), adjusted, allocated_size); - if (pointer_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - // subtract size of what we're going to allocate there - allocated_size -= sizeof(T*); - adjusted = static_cast(static_cast(pointer_adjusted) + sizeof(T*)); - data_adjusted = align(std::alignment_of::value, sizeof(T), adjusted, allocated_size); - if (data_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - return true; - }; - bool result = attempt_alloc(L, initial_size, pointer_adjusted, data_adjusted); + bool result = attempt_alloc(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, sizeof(T), initial_size, pointer_adjusted, data_adjusted); if (!result) { // we're likely to get something that fails to perform the proper allocation a second time, // so we use the suggested_new_size bump to help us out here pointer_adjusted = nullptr; data_adjusted = nullptr; - result = attempt_alloc(L, misaligned_size, pointer_adjusted, data_adjusted); + result = attempt_alloc(L, std::alignment_of_v, sizeof(T*), std::alignment_of_v, sizeof(T), misaligned_size, pointer_adjusted, data_adjusted); if (!result) { if (pointer_adjusted == nullptr) { luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); @@ -8849,43 +8926,16 @@ namespace sol { void* dx_adjusted; void* id_adjusted; void* data_adjusted; - auto attempt_alloc - = [](lua_State* L, std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) - -> bool { - void* adjusted = lua_newuserdata(L, allocated_size); - pointer_adjusted = align(std::alignment_of::value, sizeof(T*), adjusted, allocated_size); - if (pointer_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - allocated_size -= sizeof(T*); - - adjusted = static_cast(static_cast(pointer_adjusted) + sizeof(T*)); - dx_adjusted = align(std::alignment_of::value, sizeof(unique_destructor), adjusted, allocated_size); - if (dx_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - allocated_size -= sizeof(unique_destructor); - - adjusted = static_cast(static_cast(dx_adjusted) + sizeof(unique_destructor)); - - id_adjusted = align(std::alignment_of::value, sizeof(unique_tag), adjusted, allocated_size); - if (id_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - allocated_size -= sizeof(unique_tag); - - adjusted = static_cast(static_cast(id_adjusted) + sizeof(unique_tag)); - data_adjusted = align(std::alignment_of::value, sizeof(Real), adjusted, allocated_size); - if (data_adjusted == nullptr) { - lua_pop(L, 1); - return false; - } - return true; - }; - bool result = attempt_alloc(L, initial_size, pointer_adjusted, dx_adjusted, id_adjusted, data_adjusted); + bool result = attempt_alloc_unique(L, + std::alignment_of_v, + sizeof(T*), + std::alignment_of_v, + sizeof(Real), + initial_size, + pointer_adjusted, + dx_adjusted, + id_adjusted, + data_adjusted); if (!result) { // we're likely to get something that fails to perform the proper allocation a second time, // so we use the suggested_new_size bump to help us out here @@ -8893,7 +8943,16 @@ namespace sol { dx_adjusted = nullptr; id_adjusted = nullptr; data_adjusted = nullptr; - result = attempt_alloc(L, misaligned_size, pointer_adjusted, dx_adjusted, id_adjusted, data_adjusted); + result = attempt_alloc_unique(L, + std::alignment_of_v, + sizeof(T*), + std::alignment_of_v, + sizeof(Real), + misaligned_size, + pointer_adjusted, + dx_adjusted, + id_adjusted, + data_adjusted); if (!result) { if (pointer_adjusted == nullptr) { luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle().c_str()); @@ -10707,6 +10766,19 @@ namespace sol { static constexpr auto continuation_mask = 0xC0u; static constexpr auto continuation_signature = 0x80u; + static constexpr bool is_invalid(unsigned char b) { + return b == 0xC0 || b == 0xC1 || b > 0xF4; + } + + static constexpr bool is_continuation(unsigned char b) { + return (b & unicode_detail::continuation_mask) == unicode_detail::continuation_signature; + } + + static constexpr bool is_overlong(char32_t u, std::size_t bytes) { + return u <= unicode_detail::last_1byte_value || (u <= unicode_detail::last_2byte_value && bytes > 2) + || (u <= unicode_detail::last_3byte_value && bytes > 3); + } + static constexpr int sequence_length(unsigned char b) { return (b & start_2byte_mask) == 0 ? 1 : (b & start_3byte_mask) != start_3byte_mask ? 2 @@ -10823,12 +10895,7 @@ namespace sol { return dr; } - auto is_invalid = [](unsigned char b) { return b == 0xC0 || b == 0xC1 || b > 0xF4; }; - auto is_continuation = [](unsigned char b) { - return (b & unicode_detail::continuation_mask) == unicode_detail::continuation_signature; - }; - - if (is_invalid(b0) || is_continuation(b0)) { + if (unicode_detail::is_invalid(b0) || unicode_detail::is_continuation(b0)) { dr.error = error_code::invalid_code_unit; dr.next = it; return dr; @@ -10839,7 +10906,7 @@ namespace sol { b[0] = b0; for (std::size_t i = 1; i < length; ++i) { b[i] = *it; - if (!is_continuation(b[i])) { + if (!unicode_detail::is_continuation(b[i])) { dr.error = error_code::invalid_code_unit; dr.next = it; return dr; @@ -10860,12 +10927,7 @@ namespace sol { break; } - auto is_overlong = [](char32_t u, std::size_t bytes) { - return u <= unicode_detail::last_1byte_value - || (u <= unicode_detail::last_2byte_value && bytes > 2) - || (u <= unicode_detail::last_3byte_value && bytes > 3); - }; - if (is_overlong(decoded, length)) { + if (unicode_detail::is_overlong(decoded, length)) { dr.error = error_code::overlong_sequence; return dr; } @@ -10944,6 +11006,77 @@ namespace sol { namespace sol { namespace stack { + namespace stack_detail { + template + struct count_code_units_utf { + std::size_t needed_size; + + count_code_units_utf() : needed_size(0) { + } + + void operator()(const unicode::encoded_result er) { + needed_size += er.code_units_size; + } + }; + + template + struct copy_code_units_utf { + Ch* target_; + + copy_code_units_utf(Ch* target) : target_(target) { + } + + void operator()(const unicode::encoded_result er) { + std::memcpy(target_, er.code_units.data(), er.code_units_size * sizeof(ErCh)); + target_ += er.code_units_size; + } + }; + + template + inline void convert(const char* strb, const char* stre, F&& f) { + char32_t cp = 0; + for (const char* strtarget = strb; strtarget < stre;) { + auto dr = unicode::utf8_to_code_point(strtarget, stre); + if (dr.error != unicode::error_code::ok) { + cp = unicode::unicode_detail::replacement; + ++strtarget; + } + else { + cp = dr.codepoint; + strtarget = dr.next; + } + if constexpr(std::is_same_v) { + auto er = unicode::code_point_to_utf32(cp); + f(er); + } + else { + auto er = unicode::code_point_to_utf16(cp); + f(er); + } + } + } + + template + inline S get_into(lua_State* L, int index, record& tracking) { + typedef typename S::value_type Ch; + tracking.use(1); + size_t len; + auto utf8p = lua_tolstring(L, index, &len); + if (len < 1) + return S(); + const char* strb = utf8p; + const char* stre = utf8p + len; + stack_detail::count_code_units_utf count_units; + convert(strb, stre, count_units); + S r(count_units.needed_size, static_cast(0)); + r.resize(count_units.needed_size); + Ch* target = &r[0]; + stack_detail::copy_code_units_utf copy_units(target); + convert(strb, stre, copy_units); + return r; + } + } + template struct userdata_getter { typedef stack_detail::strip_extensible_t T; @@ -10998,7 +11131,7 @@ namespace sol { namespace stack { template struct unqualified_getter> { - typedef meta::unqualified_t Tu; + using Tu = meta::unqualified_t; template static void push_back_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { @@ -11012,8 +11145,8 @@ namespace sol { namespace stack { template static void insert_at_end(std::true_type, types, lua_State* L, T& arr, std::size_t) { - using std::end; - arr.insert(end(arr), stack::get(L, -lua_size::value)); + using std::cend; + arr.insert(cend(arr), stack::get(L, -lua_size::value)); } template @@ -11030,11 +11163,11 @@ namespace sol { namespace stack { } static T get(lua_State* L, int relindex, record& tracking) { - return get(meta::has_key_value_pair>(), L, relindex, tracking); + return get(meta::is_associative(), L, relindex, tracking); } static T get(std::false_type, lua_State* L, int relindex, record& tracking) { - typedef typename T::value_type V; + typedef typename Tu::value_type V; return get(types(), L, relindex, tracking); } @@ -11042,15 +11175,50 @@ namespace sol { namespace stack { static T get(types t, lua_State* L, int relindex, record& tracking) { tracking.use(1); + // the W4 flag is really great, + // so great that it can tell my for loops (2-nested) + // below never actually terminate without hitting a "return arr;" + // where the goto's are now so it would tell + // me that the return arr at the end of this function + // is W4XXX unreachable, + // which is fair until other compilers complain + // that there isn't a return and that based on + // SOME MAGICAL FORCE + // control flow falls off the end of a non-void function + // so it needs to be there for the compilers that are + // too flimsy to analyze the basic blocks... + // (I'm sure I should file a bug but those compilers are already + // in the wild; it doesn't matter if I fix them, + // someone else is still going to get some old-ass compiler + // and then bother me about the unclean build for the 30th + // time) + + // "Why not an IIFE?" + // Because additional lambdas / functions which serve as + // capture-all-and-then-invoke bloat binary sizes + // by an actually detectable amount + // (one user uses sol2 pretty heavily and 22 MB of binary size + // was saved by reducing reliance on lambdas in templates) + + // This would really be solved by having break N; + // be a real, proper thing... + // but instead, we have to use labels and gotos + // and earn the universal vitriol of the dogmatic + // programming community + + // all in all: W4 is great!~ + int index = lua_absindex(L, relindex); T arr; std::size_t idx = 0; #if SOL_LUA_VERSION >= 503 - // This method is HIGHLY performant over regular table iteration thanks to the Lua API changes in 5.3 + // This method is HIGHLY performant over regular table iteration + // thanks to the Lua API changes in 5.3 // Questionable in 5.4 for (lua_Integer i = 0;; i += lua_size::value) { if (max_size_check(meta::has_max_size(), arr, idx)) { - return arr; + // see above comment + goto done; } bool isnil = false; for (int vi = 0; vi < lua_size::value; ++vi) { @@ -11080,7 +11248,8 @@ namespace sol { namespace stack { #else lua_pop(L, (vi + 1)); #endif - return arr; + // see above comment + goto done; } } if (isnil) { @@ -11098,7 +11267,8 @@ namespace sol { namespace stack { // Zzzz slower but necessary thanks to the lower version API and missing functions qq for (lua_Integer i = 0;; i += lua_size::value, lua_pop(L, lua_size::value)) { if (idx >= arr.max_size()) { - return arr; + // see above comment + goto done; } #if defined(SOL_SAFE_STACK_CHECK) && SOL_SAFE_STACK_CHECK luaL_checkstack(L, 2, detail::not_enough_stack_space_generic); @@ -11114,7 +11284,8 @@ namespace sol { namespace stack { break; } lua_pop(L, (vi + 1)); - return arr; + // see above comment + goto done; } } if (isnil) @@ -11123,11 +11294,12 @@ namespace sol { namespace stack { ++idx; } #endif + done: return arr; } static T get(std::true_type, lua_State* L, int index, record& tracking) { - typedef typename T::value_type P; + typedef typename Tu::value_type P; typedef typename P::first_type K; typedef typename P::second_type V; return get(types(), L, index, tracking); @@ -11276,29 +11448,26 @@ namespace sol { namespace stack { }; template - struct unqualified_getter, - std::enable_if_t< - meta::all, meta::neg>>>::value>> { + struct unqualified_getter, std::enable_if_t::value>> { + using Tu = meta::unqualified_t; + static T get(lua_State* L, int index, record& tracking) { - typedef typename T::value_type V; - unqualified_getter> g; - // VC++ has a bad warning here: shut it up - (void)g; - return g.get(types>(), L, index, tracking); - } - }; - - template - struct unqualified_getter, - std::enable_if_t, meta::has_key_value_pair>>::value>> { - static T get(lua_State* L, int index, record& tracking) { - typedef typename T::value_type P; - typedef typename P::first_type K; - typedef typename P::second_type V; - unqualified_getter> g; - // VC++ has a bad warning here: shut it up - (void)g; - return g.get(types>(), L, index, tracking); + if constexpr(meta::is_associative::value) { + typedef typename T::value_type P; + typedef typename P::first_type K; + typedef typename P::second_type V; + unqualified_getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } + else { + typedef typename T::value_type V; + unqualified_getter> g; + // VC++ has a bad warning here: shut it up + (void)g; + return g.get(types>(), L, index, tracking); + } } }; @@ -11412,114 +11581,24 @@ namespace sol { namespace stack { template struct unqualified_getter> { - typedef std::basic_string S; + using S = std::basic_string; static S get(lua_State* L, int index, record& tracking) { - typedef std::conditional_t Ch; - typedef typename std::allocator_traits::template rebind_alloc ChAl; - typedef std::char_traits ChTraits; - unqualified_getter> g; - (void)g; - return g.template get_into(L, index, tracking); + using Ch = std::conditional_t; + return stack_detail::get_into(L, index, tracking); } }; template struct unqualified_getter> { - template - static void convert(const char* strb, const char* stre, F&& f) { - char32_t cp = 0; - for (const char* strtarget = strb; strtarget < stre;) { - auto dr = unicode::utf8_to_code_point(strtarget, stre); - if (dr.error != unicode::error_code::ok) { - cp = unicode::unicode_detail::replacement; - ++strtarget; - } - else { - cp = dr.codepoint; - strtarget = dr.next; - } - auto er = unicode::code_point_to_utf16(cp); - f(er); - } - } - - template - static S get_into(lua_State* L, int index, record& tracking) { - typedef typename S::value_type Ch; - tracking.use(1); - size_t len; - auto utf8p = lua_tolstring(L, index, &len); - if (len < 1) - return S(); - std::size_t needed_size = 0; - const char* strb = utf8p; - const char* stre = utf8p + len; - auto count_units - = [&needed_size](const unicode::encoded_result er) { needed_size += er.code_units_size; }; - convert(strb, stre, count_units); - S r(needed_size, static_cast(0)); - r.resize(needed_size); - Ch* target = &r[0]; - auto copy_units = [&target](const unicode::encoded_result er) { - std::memcpy(target, er.code_units.data(), er.code_units_size * sizeof(Ch)); - target += er.code_units_size; - }; - convert(strb, stre, copy_units); - return r; - } - static std::basic_string get(lua_State* L, int index, record& tracking) { - return get_into>(L, index, tracking); + return stack_detail::get_into>(L, index, tracking); } }; template struct unqualified_getter> { - template - static void convert(const char* strb, const char* stre, F&& f) { - char32_t cp = 0; - for (const char* strtarget = strb; strtarget < stre;) { - auto dr = unicode::utf8_to_code_point(strtarget, stre); - if (dr.error != unicode::error_code::ok) { - cp = unicode::unicode_detail::replacement; - ++strtarget; - } - else { - cp = dr.codepoint; - strtarget = dr.next; - } - auto er = unicode::code_point_to_utf32(cp); - f(er); - } - } - - template - static S get_into(lua_State* L, int index, record& tracking) { - typedef typename S::value_type Ch; - tracking.use(1); - size_t len; - auto utf8p = lua_tolstring(L, index, &len); - if (len < 1) - return S(); - std::size_t needed_size = 0; - const char* strb = utf8p; - const char* stre = utf8p + len; - auto count_units - = [&needed_size](const unicode::encoded_result er) { needed_size += er.code_units_size; }; - convert(strb, stre, count_units); - S r(needed_size, static_cast(0)); - r.resize(needed_size); - Ch* target = &r[0]; - auto copy_units = [&target](const unicode::encoded_result er) { - std::memcpy(target, er.code_units.data(), er.code_units_size * sizeof(Ch)); - target += er.code_units_size; - }; - convert(strb, stre, copy_units); - return r; - } - static std::basic_string get(lua_State* L, int index, record& tracking) { - return get_into>(L, index, tracking); + return stack_detail::get_into>(L, index, tracking); } }; @@ -13754,17 +13833,15 @@ namespace sol { return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); } - struct evaluator { - template - static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { - return std::forward(fx)(std::forward(args)...); - } + template + static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) { + return std::forward(fx)(std::forward(args)...); + } - template - static decltype(auto) eval(types, std::index_sequence, lua_State* L, int start, record& tracking, Fx&& fx, FxArgs&&... fxargs) { - return eval(types(), std::index_sequence(), L, start, tracking, std::forward(fx), std::forward(fxargs)..., stack_detail::unchecked_get(L, start + tracking.used, tracking)); - } - }; + template + static decltype(auto) eval(types, std::index_sequence, lua_State* L, int start, record& tracking, Fx&& fx, FxArgs&&... fxargs) { + return eval(types(), std::index_sequence(), L, start, tracking, std::forward(fx), std::forward(fxargs)..., stack_detail::unchecked_get(L, start + tracking.used, tracking)); + } template ::value >> inline decltype(auto) call(types, types ta, std::index_sequence tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) { @@ -13774,7 +13851,7 @@ namespace sol { argument_handler> handler{}; multi_check(L, start, handler); record tracking{}; - return evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); + return eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } template @@ -13785,7 +13862,7 @@ namespace sol { argument_handler> handler{}; multi_check(L, start, handler); record tracking{}; - evaluator{}.eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); + eval(ta, tai, L, start, tracking, std::forward(fx), std::forward(args)...); } } // namespace stack_detail @@ -15254,7 +15331,7 @@ namespace sol { template inline void handle_filter(returns_self_with, lua_State* L, int& pushed) { pushed = stack::push(L, raw_index(1)); - handle_filter(static_stack_dependencies<-1, In...>(), L, pushed); + handle_filter(static_stack_dependencies< -1, In...>(), L, pushed); } inline void handle_filter(const stack_dependencies& sdeps, lua_State* L, int&) { @@ -16151,14 +16228,12 @@ namespace function_detail { // idx n + 1: is the object's void pointer // We don't need to store the size, because the other side is templated // with the same member function pointer type - auto memberdata = stack::stack_detail::get_as_upvalues(L); - auto objdata = stack::stack_detail::get_as_upvalues(L, memberdata.second); - function_type& memfx = memberdata.first; - auto& item = *objdata.first; + function_type& memfx = stack::get>(L, upvalue_index(2)); + auto& item = *static_cast(stack::get(L, upvalue_index(3))); return call_detail::call_wrapped(L, memfx, item); } - static int call(lua_State* L) { + static int call(lua_State* L) noexcept(traits_type::is_noexcept) { int nr = detail::typed_static_trampoline(L); if (is_yielding) { return lua_yield(L, nr); @@ -16198,7 +16273,7 @@ namespace function_detail { } } - static int call(lua_State* L) { + static int call(lua_State* L) noexcept(traits_type::is_noexcept) { int nr = detail::typed_static_trampoline(L); if (is_yielding) { return lua_yield(L, nr); @@ -16259,8 +16334,7 @@ namespace function_detail { static int real_call(lua_State* L) noexcept(traits_type::is_noexcept) { // Layout: // idx 1...n: verbatim data of member variable pointer - auto memberdata = stack::stack_detail::get_as_upvalues(L); - function_type& memfx = memberdata.first; + function_type& memfx = stack::get>(L, upvalue_index(2)); return call_detail::call_wrapped(L, memfx); } @@ -16481,14 +16555,14 @@ namespace function_detail { } template - int call(types, meta::index_value, types, types, lua_State* L, int, int) { - auto& func = std::get(overloads); + static int call(types, meta::index_value, types, types, lua_State* L, int, int, overload_list& ol) { + auto& func = std::get(ol); return call_detail::call_wrapped(L, func); } int operator()(lua_State* L) { - auto mfx = [&](auto&&... args) { return this->call(std::forward(args)...); }; - return call_detail::overload_match(mfx, L, 1 + start_skew); + auto mfx = [](auto&&... args) { return call(std::forward(args)...); }; + return call_detail::overload_match(mfx, L, 1 + start_skew, overloads); } }; } @@ -16701,7 +16775,7 @@ namespace sol { int upvalues = 0; upvalues += stack::push(L, nullptr); upvalues += stack::stack_detail::push_as_upvalues(L, memfxptr); - upvalues += stack::push(L, lightuserdata_value(static_cast(userptr))); + upvalues += stack::push(L, static_cast(userptr)); stack::push(L, c_closure(freefunc, upvalues)); } @@ -16746,14 +16820,14 @@ namespace sol { template static void select_reference_member_function(std::true_type, lua_State* L, Fx&& fx, T&& obj, Args&&... args) { - typedef std::decay_t dFx; + using dFx = std::decay_t; dFx memfxptr(std::forward(fx)); auto userptr = detail::ptr(std::forward(obj), std::forward(args)...); lua_CFunction freefunc = &function_detail::upvalue_member_function, meta::unqualified_t, is_yielding>::call; int upvalues = 0; upvalues += stack::push(L, nullptr); - upvalues += stack::stack_detail::push_as_upvalues(L, memfxptr); + upvalues += stack::push>(L, memfxptr); upvalues += stack::push(L, lightuserdata_value(static_cast(userptr))); stack::push(L, c_closure(freefunc, upvalues)); } @@ -16771,22 +16845,24 @@ namespace sol { template static void select_member_function(std::true_type, lua_State* L, Fx&& fx, function_detail::class_indicator) { - lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; + using dFx = std::decay_t; + lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; int upvalues = 0; upvalues += stack::push(L, nullptr); - upvalues += stack::stack_detail::push_as_upvalues(L, fx); + upvalues += stack::push>(L, fx); stack::push(L, c_closure(freefunc, upvalues)); } template static void select_member_function(std::true_type, lua_State* L, Fx&& fx) { + using dFx = std::decay_t; typedef typename meta::bind_traits>::object_type C; - lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; + lua_CFunction freefunc = &function_detail::upvalue_this_member_function::call; int upvalues = 0; upvalues += stack::push(L, nullptr); - upvalues += stack::stack_detail::push_as_upvalues(L, fx); + upvalues += stack::push>(L, fx); stack::push(L, c_closure(freefunc, upvalues)); } @@ -17349,6 +17425,21 @@ namespace sol { namespace sol { + namespace detail { + template + inline void handle_protected_exception(lua_State* L, optional maybe_ex, const char* error, detail::protected_handler& h) { + h.stackindex = 0; + if (b) { + h.target.push(); + detail::call_exception_handler(L, maybe_ex, error); + lua_call(L, 1, 1); + } + else { + detail::call_exception_handler(L, maybe_ex, error); + } + } + } + template class basic_protected_function : public base_t { public: @@ -17394,18 +17485,6 @@ namespace sol { int returncount = 0; call_status code = call_status::ok; #if !defined(SOL_NO_EXCEPTIONS) || !SOL_NO_EXCEPTIONS - auto onexcept = [&](optional maybe_ex, const char* error) { - h.stackindex = 0; - if (b) { - h.target.push(); - detail::call_exception_handler(lua_state(), maybe_ex, error); - lua_call(lua_state(), 1, 1); - } - else { - detail::call_exception_handler(lua_state(), maybe_ex, error); - } - }; - (void)onexcept; #if (!defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) || !SOL_NO_EXCEPTIONS_SAFE_PROPAGATION) || (defined(SOL_LUAJIT) && SOL_LUAJIT) try { #endif // Safe Exception Propagation @@ -17419,17 +17498,17 @@ namespace sol { } // Handle C++ errors thrown from C++ functions bound inside of lua catch (const char* error) { - onexcept(optional(nullopt), error); + detail::handle_protected_exception(lua_state(), optional(nullopt), error, h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } catch (const std::string& error) { - onexcept(optional(nullopt), error.c_str()); + detail::handle_protected_exception(lua_state(), optional(nullopt), error.c_str(), h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } catch (const std::exception& error) { - onexcept(optional(error), error.what()); + detail::handle_protected_exception(lua_state(), optional(error), error.what(), h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } @@ -17438,7 +17517,7 @@ namespace sol { // but LuaJIT will swallow all C++ errors // if we don't at least catch std::exception ones catch (...) { - onexcept(optional(nullopt), "caught (...) unknown error during protected_function call"); + detail::handle_protected_exception(lua_state(), optional(nullopt), detail::protected_function_error, h); firstreturn = lua_gettop(lua_state()); return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime); } @@ -17672,6 +17751,34 @@ namespace sol { return *this; } + namespace detail { + template + struct std_shim { + unsafe_function lua_func_; + + std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) { + } + + template + meta::return_type_t operator()(Args&&... args) { + return lua_func_.call(std::forward(args)...); + } + }; + + template <> + struct std_shim { + unsafe_function lua_func_; + + std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) { + } + + template + void operator()(Args&&... args) { + lua_func_.call(std::forward(args)...); + } + }; + } // namespace detail + namespace stack { template struct unqualified_getter> { @@ -17679,29 +17786,12 @@ namespace sol { typedef typename fx_t::args_list args_lists; typedef meta::tuple_types return_types; - template - static std::function get_std_func(types, types, lua_State* L, int index) { - unsafe_function f(L, index); - auto fx = [ f = std::move(f) ](Args && ... args) -> meta::return_type_t { - return f.call(std::forward(args)...); - }; + template + static std::function get_std_func(types, lua_State* L, int index) { + detail::std_shim fx(unsafe_function(L, index)); return std::move(fx); } - template - static std::function get_std_func(types, types, lua_State* L, int index) { - unsafe_function f(L, index); - auto fx = [f = std::move(f)](FxArgs&&... args) -> void { - f(std::forward(args)...); - }; - return std::move(fx); - } - - template - static std::function get_std_func(types<>, types t, lua_State* L, int index) { - return get_std_func(types(), t, L, index); - } - static std::function get(lua_State* L, int index, record& tracking) { tracking.last = 1; tracking.used += 1; @@ -17709,7 +17799,7 @@ namespace sol { if (t == type::none || t == type::lua_nil) { return nullptr; } - return get_std_func(return_types(), args_lists(), L, index); + return get_std_func(return_types(), L, index); } }; } // namespace stack @@ -17745,9 +17835,9 @@ namespace detail { // end of sol/deprecate.hpp -// beginning of sol/usertype_container.hpp +// beginning of sol/usertype_container_launch.hpp -// beginning of sol/container_traits.hpp +// beginning of sol/usertype_container.hpp // beginning of sol/map.hpp @@ -17777,42 +17867,7 @@ namespace detail { 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)); - } + struct usertype_container; namespace container_detail { @@ -18159,7 +18214,7 @@ namespace sol { } template - struct container_traits_default { + struct usertype_container_default { private: typedef std::remove_pointer_t> T; @@ -18239,12 +18294,12 @@ namespace sol { }; template - struct container_traits_default>, meta::has_value_type>>, meta::has_iterator>>>::value>> { + struct usertype_container_default>, meta::has_value_type>>, meta::has_iterator>>>::value>> { private: using T = std::remove_pointer_t>>; private: - using deferred_traits = container_traits; + using deferred_uc = usertype_container; using is_associative = meta::is_associative; using is_lookup = meta::is_lookup; using is_ordered = meta::is_ordered; @@ -18306,12 +18361,12 @@ namespace sol { } static detail::error_result at_category(std::input_iterator_tag, lua_State* L, T& self, std::ptrdiff_t pos) { - pos += deferred_traits::index_adjustment(L, self); + pos += deferred_uc::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); + auto it = deferred_uc::begin(L, self); + auto e = deferred_uc::end(L, self); if (it == e) { return stack::push(L, lua_nil); } @@ -18327,11 +18382,11 @@ namespace sol { 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_traits::index_adjustment(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_traits::begin(L, self), pos); + auto it = std::next(deferred_uc::begin(L, self), pos); return get_associative(is_associative(), L, it); } @@ -18349,12 +18404,12 @@ namespace sol { } static detail::error_result get_category(std::input_iterator_tag, lua_State* L, T& self, K& key) { - key += deferred_traits::index_adjustment(L, self); + key += deferred_uc::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); + auto it = deferred_uc::begin(L, self); + auto e = deferred_uc::end(L, self); if (it == e) { return stack::push(L, lua_nil); } @@ -18370,11 +18425,11 @@ namespace sol { 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 += deferred_traits::index_adjustment(L, self); + key += deferred_uc::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); + auto it = std::next(deferred_uc::begin(L, self), key); return get_associative(is_associative(), L, it); } @@ -18386,8 +18441,8 @@ namespace sol { 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)); + 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); } @@ -18424,9 +18479,9 @@ namespace sol { 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 += deferred_traits::index_adjustment(L, self); - auto e = deferred_traits::end(L, self); - auto it = deferred_traits::begin(L, self); + 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; @@ -18445,7 +18500,7 @@ namespace sol { if (key <= 0) { return detail::error_result("sol: out of bounds (too small) for set on '%s'", detail::demangle().c_str()); } - key += deferred_traits::index_adjustment(L, self); + key += deferred_uc::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)); @@ -18453,7 +18508,7 @@ namespace sol { 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_traits::begin(L, self), key); + auto it = std::next(deferred_uc::begin(L, self), key); return set_writable(is_writable(), L, self, it, std::move(value)); } @@ -18465,8 +18520,8 @@ namespace sol { 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)); + 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 {}; } @@ -18490,7 +18545,7 @@ namespace sol { 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_traits::end(L, self)) { + 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)); @@ -18518,12 +18573,12 @@ namespace sol { else { decltype(auto) key = stack::unqualified_get(L, 2); auto it = self.find(key); - if (it == deferred_traits::end(L, self)) { + if (it == deferred_uc::end(L, self)) { return stack::push(L, lua_nil); } if constexpr (idx_of) { - auto dist = std::distance(deferred_traits::begin(L, self), it); - dist -= deferred_traits::index_adjustment(L, self); + auto dist = std::distance(deferred_uc::begin(L, self), it); + dist -= deferred_uc::index_adjustment(L, self); return stack::push(L, dist); } else { @@ -18542,12 +18597,12 @@ namespace sol { else { decltype(auto) value = stack::unqualified_get(L, 2); auto it = self.find(value); - if (it == deferred_traits::end(L, self)) { + if (it == deferred_uc::end(L, self)) { return stack::push(L, lua_nil); } if constexpr (idx_of) { - auto dist = std::distance(deferred_traits::begin(L, self), it); - dist -= deferred_traits::index_adjustment(L, self); + auto dist = std::distance(deferred_uc::begin(L, self), it); + dist -= deferred_uc::index_adjustment(L, self); return stack::push(L, dist); } else { @@ -18566,7 +18621,7 @@ namespace sol { } static detail::error_result find_associative_lookup(std::false_type, lua_State* L, T& self, iterator&, std::size_t idx) { - idx -= deferred_traits::index_adjustment(L, self); + idx -= deferred_uc::index_adjustment(L, self); return stack::push(L, idx); } @@ -18578,8 +18633,8 @@ namespace sol { 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_traits::begin(L, self); - auto e = deferred_traits::end(L, self); + 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) { @@ -18613,8 +18668,8 @@ namespace sol { 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_traits::end(L, self); - for (auto it = deferred_traits::begin(L, self); it != e; ++backit, ++it) { + 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); @@ -18626,7 +18681,7 @@ namespace sol { } static detail::error_result add_insert(std::true_type, lua_State* L, T& self, stack_object value) { - auto pos = deferred_traits::end(L, self); + auto pos = deferred_uc::end(L, self); return add_insert(std::true_type(), L, self, value, pos); } @@ -18662,7 +18717,7 @@ namespace sol { } static detail::error_result add_associative(std::true_type, lua_State* L, T& self, stack_object key) { - auto pos = deferred_traits::end(L, self); + auto pos = deferred_uc::end(L, self); return add_associative(std::true_type(), L, self, std::move(key), pos); } @@ -18696,9 +18751,9 @@ namespace sol { } static detail::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 it = deferred_uc::begin(L, self); auto key = where.as(); - key += deferred_traits::index_adjustment(L, self); + key += deferred_uc::index_adjustment(L, self); std::advance(it, key); self.insert(it, value.as()); return {}; @@ -18708,9 +18763,9 @@ namespace sol { 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) { + 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()); } @@ -18741,8 +18796,8 @@ namespace sol { } static detail::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); + auto it = deferred_uc::begin(L, self); + key += deferred_uc::index_adjustment(L, self); std::advance(it, key); self.erase(it); @@ -18753,8 +18808,8 @@ namespace sol { 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)); + 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 {}; } @@ -18775,9 +18830,9 @@ namespace sol { static detail::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) { + 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()); } @@ -18800,7 +18855,7 @@ namespace sol { } 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)); + return std::distance(deferred_uc::begin(L, self), deferred_uc::end(L, self)); } static auto size_has(std::true_type, lua_State*, T& self) { @@ -18820,7 +18875,7 @@ namespace sol { } static bool empty_has(std::false_type, lua_State* L, T& self) { - return deferred_traits::begin(L, self) == deferred_traits::end(L, self); + return deferred_uc::begin(L, self) == deferred_uc::end(L, self); } static detail::error_result get_start(lua_State* L, T& self, K& key) { @@ -18852,7 +18907,7 @@ namespace sol { iter& i = stack::unqualified_get>(L, 1); auto& source = i.source; auto& it = i.it; - if (it == deferred_traits::end(L, source)) { + if (it == deferred_uc::end(L, source)) { return 0; } int p; @@ -18874,7 +18929,7 @@ namespace sol { auto& source = i.source; auto& it = i.it; next_K k = stack::unqualified_get(L, 2); - if (it == deferred_traits::end(L, source)) { + if (it == deferred_uc::end(L, source)) { return 0; } int p; @@ -18894,7 +18949,7 @@ namespace sol { 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, src, deferred_uc::begin(L, src)); stack::push(L, lua_nil); return 3; } @@ -18903,7 +18958,7 @@ namespace sol { 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, src, deferred_uc::begin(L, src)); stack::push(L, 0); return 3; } @@ -19032,10 +19087,10 @@ namespace sol { }; template - struct container_traits_default>>::value>> { + struct usertype_container_default>>::value>> { private: typedef std::remove_pointer_t> T; - typedef container_traits deferred_traits; + typedef usertype_container deferred_uc; public: typedef std::remove_extent_t value_type; @@ -19071,7 +19126,7 @@ namespace sol { for (std::size_t idx = 0; idx < N; ++idx) { const auto& v = self[idx]; if (v == value) { - idx -= deferred_traits::index_adjustment(L, self); + idx -= deferred_uc::index_adjustment(L, self); return stack::push(L, idx); } } @@ -19087,7 +19142,7 @@ namespace sol { auto& source = i.source; auto& it = i.it; std::size_t k = stack::unqualified_get(L, 2); - if (it == deferred_traits::end(L, source)) { + if (it == deferred_uc::end(L, source)) { return 0; } int p; @@ -19121,7 +19176,7 @@ namespace sol { 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); + idx += deferred_uc::index_adjustment(L, self); if (idx >= static_cast(std::extent::value) || idx < 0) { return stack::push(L, lua_nil); } @@ -19135,7 +19190,7 @@ namespace sol { 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); + 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()); } @@ -19169,7 +19224,7 @@ namespace sol { 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, src, deferred_uc::begin(L, src)); stack::push(L, 0); return 3; } @@ -19200,309 +19255,303 @@ namespace sol { }; template - struct container_traits_default> : container_traits_default {}; + struct usertype_container_default> : usertype_container_default {}; } // namespace container_detail template - struct container_traits : container_detail::container_traits_default {}; + struct usertype_container : container_detail::usertype_container_default {}; } // namespace sol -// end of sol/container_traits.hpp +// end of sol/usertype_container.hpp namespace sol { - template - struct usertype_container { - typedef std::remove_pointer_t> T; - typedef container_traits traits; - typedef container_detail::container_traits_default default_traits; + namespace container_detail { + template + struct u_c_launch { + using T = std::remove_pointer_t>; + using uc = usertype_container; + using default_uc = usertype_container_default; - static inline int real_index_get_traits(std::true_type, lua_State* L) { - return traits::index_get(L); - } + static inline int real_index_get_traits(std::true_type, lua_State* L) { + return uc::index_get(L); + } - static inline int real_index_get_traits(std::false_type, lua_State* L) { - return default_traits::index_get(L); - } + static inline int real_index_get_traits(std::false_type, lua_State* L) { + return default_uc::index_get(L); + } - static inline int real_index_call(lua_State* L) { - typedef detail::unordered_map call_map; - static const call_map calls{ - { "at", &at_call }, - { "get", &real_get_call }, - { "set", &real_set_call }, - { "size", &real_length_call }, - { "add", &real_add_call }, - { "empty", &real_empty_call }, - { "insert", &real_insert_call }, - { "clear", &real_clear_call }, - { "find", &real_find_call }, - { "index_of", &real_index_of_call }, - { "erase", &real_erase_call }, - { "pairs", &pairs_call }, - { "next", &next_call }, - }; - auto maybenameview = stack::unqualified_check_get(L, 2); - if (maybenameview) { - const string_view& nameview = *maybenameview; -#if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH - auto it = calls.find(nameview, string_view_hash(), std::equal_to()); -#else - std::string name(nameview.data(), nameview.size()); - auto it = calls.find(name); -#endif - if (it != calls.cend()) { - return stack::push(L, it->second); + static inline int real_index_call(lua_State* L) { + typedef detail::unordered_map call_map; + static const call_map calls{ + { "at", &real_at_call }, + { "get", &real_get_call }, + { "set", &real_set_call }, + { "size", &real_length_call }, + { "add", &real_add_call }, + { "empty", &real_empty_call }, + { "insert", &real_insert_call }, + { "clear", &real_clear_call }, + { "find", &real_find_call }, + { "index_of", &real_index_of_call }, + { "erase", &real_erase_call }, + { "pairs", &pairs_call }, + { "next", &next_call }, + }; + auto maybenameview = stack::unqualified_check_get(L, 2); + if (maybenameview) { + const string_view& name = *maybenameview; + auto it = calls.find(name); + if (it != calls.cend()) { + return stack::push(L, it->second); + } + } + return real_index_get_traits(container_detail::has_traits_index_get(), L); + } + + static inline int real_at_traits(std::true_type, lua_State* L) { + return uc::at(L); + } + + static inline int real_at_traits(std::false_type, lua_State* L) { + return default_uc::at(L); + } + + static inline int real_at_call(lua_State* L) { + return real_at_traits(container_detail::has_traits_at(), L); + } + + static inline int real_get_traits(std::true_type, lua_State* L) { + return uc::get(L); + } + + static inline int real_get_traits(std::false_type, lua_State* L) { + return default_uc::get(L); + } + + static inline int real_get_call(lua_State* L) { + return real_get_traits(container_detail::has_traits_get(), L); + } + + static inline int real_set_traits(std::true_type, lua_State* L) { + return uc::set(L); + } + + static inline int real_set_traits(std::false_type, lua_State* L) { + return default_uc::set(L); + } + + static inline int real_set_call(lua_State* L) { + return real_set_traits(container_detail::has_traits_set(), L); + } + + static inline int real_index_set_traits(std::true_type, lua_State* L) { + return uc::index_set(L); + } + + static inline int real_index_set_traits(std::false_type, lua_State* L) { + return default_uc::index_set(L); + } + + static inline int real_new_index_call(lua_State* L) { + return real_index_set_traits(container_detail::has_traits_index_set(), L); + } + + static inline int real_pairs_traits(std::true_type, lua_State* L) { + return uc::pairs(L); + } + + static inline int real_pairs_traits(std::false_type, lua_State* L) { + return default_uc::pairs(L); + } + + static inline int real_pairs_call(lua_State* L) { + return real_pairs_traits(container_detail::has_traits_pairs(), L); + } + + static inline int real_ipairs_traits(std::true_type, lua_State* L) { + return uc::ipairs(L); + } + + static inline int real_ipairs_traits(std::false_type, lua_State* L) { + return default_uc::ipairs(L); + } + + static inline int real_ipairs_call(lua_State* L) { + return real_ipairs_traits(container_detail::has_traits_ipairs(), L); + } + + static inline int real_next_traits(std::true_type, lua_State* L) { + return uc::next(L); + } + + static inline int real_next_traits(std::false_type, lua_State* L) { + return default_uc::next(L); + } + + static inline int real_next_call(lua_State* L) { + return real_next_traits(container_detail::has_traits_next(), L); + } + + static inline int real_size_traits(std::true_type, lua_State* L) { + return uc::size(L); + } + + static inline int real_size_traits(std::false_type, lua_State* L) { + return default_uc::size(L); + } + + static inline int real_length_call(lua_State* L) { + return real_size_traits(container_detail::has_traits_size(), L); + } + + static inline int real_add_traits(std::true_type, lua_State* L) { + return uc::add(L); + } + + static inline int real_add_traits(std::false_type, lua_State* L) { + return default_uc::add(L); + } + + static inline int real_add_call(lua_State* L) { + return real_add_traits(container_detail::has_traits_add(), L); + } + + static inline int real_insert_traits(std::true_type, lua_State* L) { + return uc::insert(L); + } + + static inline int real_insert_traits(std::false_type, lua_State* L) { + return default_uc::insert(L); + } + + static inline int real_insert_call(lua_State* L) { + return real_insert_traits(container_detail::has_traits_insert(), L); + } + + static inline int real_clear_traits(std::true_type, lua_State* L) { + return uc::clear(L); + } + + static inline int real_clear_traits(std::false_type, lua_State* L) { + return default_uc::clear(L); + } + + static inline int real_clear_call(lua_State* L) { + return real_clear_traits(container_detail::has_traits_clear(), L); + } + + static inline int real_empty_traits(std::true_type, lua_State* L) { + return uc::empty(L); + } + + static inline int real_empty_traits(std::false_type, lua_State* L) { + return default_uc::empty(L); + } + + static inline int real_empty_call(lua_State* L) { + return real_empty_traits(container_detail::has_traits_empty(), L); + } + + static inline int real_erase_traits(std::true_type, lua_State* L) { + return uc::erase(L); + } + + static inline int real_erase_traits(std::false_type, lua_State* L) { + return default_uc::erase(L); + } + + static inline int real_erase_call(lua_State* L) { + return real_erase_traits(container_detail::has_traits_erase(), L); + } + + static inline int real_find_traits(std::true_type, lua_State* L) { + return uc::find(L); + } + + static inline int real_find_traits(std::false_type, lua_State* L) { + return default_uc::find(L); + } + + static inline int real_find_call(lua_State* L) { + return real_find_traits(container_detail::has_traits_find(), L); + } + + static inline int real_index_of_call(lua_State* L) { + if constexpr(container_detail::has_traits_index_of()) { + return uc::index_of(L); + } + else { + return default_uc::index_of(L); } } - return real_index_get_traits(container_detail::has_traits_index_get(), L); - } - static inline int real_at_traits(std::true_type, lua_State* L) { - return traits::at(L); - } + static inline int add_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_at_traits(std::false_type, lua_State* L) { - return default_traits::at(L); - } + static inline int erase_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_at_call(lua_State* L) { - return real_at_traits(container_detail::has_traits_at(), L); - } + static inline int insert_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_get_traits(std::true_type, lua_State* L) { - return traits::get(L); - } + static inline int clear_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_get_traits(std::false_type, lua_State* L) { - return default_traits::get(L); - } + static inline int empty_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_get_call(lua_State* L) { - return real_get_traits(container_detail::has_traits_get(), L); - } + static inline int find_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_set_traits(std::true_type, lua_State* L) { - return traits::set(L); - } + static inline int index_of_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_set_traits(std::false_type, lua_State* L) { - return default_traits::set(L); - } + static inline int length_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_set_call(lua_State* L) { - return real_set_traits(container_detail::has_traits_set(), L); - } + static inline int pairs_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_index_set_traits(std::true_type, lua_State* L) { - return traits::index_set(L); - } + static inline int ipairs_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_index_set_traits(std::false_type, lua_State* L) { - return default_traits::index_set(L); - } + static inline int next_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_new_index_call(lua_State* L) { - return real_index_set_traits(container_detail::has_traits_index_set(), L); - } + static inline int at_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_pairs_traits(std::true_type, lua_State* L) { - return traits::pairs(L); - } + static inline int get_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_pairs_traits(std::false_type, lua_State* L) { - return default_traits::pairs(L); - } + static inline int set_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_pairs_call(lua_State* L) { - return real_pairs_traits(container_detail::has_traits_pairs(), L); - } + static inline int index_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } - static inline int real_ipairs_traits(std::true_type, lua_State* L) { - return traits::ipairs(L); - } - - static inline int real_ipairs_traits(std::false_type, lua_State* L) { - return default_traits::ipairs(L); - } - - static inline int real_ipairs_call(lua_State* L) { - return real_ipairs_traits(container_detail::has_traits_ipairs(), L); - } - - static inline int real_next_traits(std::true_type, lua_State* L) { - return traits::next(L); - } - - static inline int real_next_traits(std::false_type, lua_State* L) { - return default_traits::next(L); - } - - static inline int real_next_call(lua_State* L) { - return real_next_traits(container_detail::has_traits_next(), L); - } - - static inline int real_size_traits(std::true_type, lua_State* L) { - return traits::size(L); - } - - static inline int real_size_traits(std::false_type, lua_State* L) { - return default_traits::size(L); - } - - static inline int real_length_call(lua_State* L) { - return real_size_traits(container_detail::has_traits_size(), L); - } - - static inline int real_add_traits(std::true_type, lua_State* L) { - return traits::add(L); - } - - static inline int real_add_traits(std::false_type, lua_State* L) { - return default_traits::add(L); - } - - static inline int real_add_call(lua_State* L) { - return real_add_traits(container_detail::has_traits_add(), L); - } - - static inline int real_insert_traits(std::true_type, lua_State* L) { - return traits::insert(L); - } - - static inline int real_insert_traits(std::false_type, lua_State* L) { - return default_traits::insert(L); - } - - static inline int real_insert_call(lua_State* L) { - return real_insert_traits(container_detail::has_traits_insert(), L); - } - - static inline int real_clear_traits(std::true_type, lua_State* L) { - return traits::clear(L); - } - - static inline int real_clear_traits(std::false_type, lua_State* L) { - return default_traits::clear(L); - } - - static inline int real_clear_call(lua_State* L) { - return real_clear_traits(container_detail::has_traits_clear(), L); - } - - static inline int real_empty_traits(std::true_type, lua_State* L) { - return traits::empty(L); - } - - static inline int real_empty_traits(std::false_type, lua_State* L) { - return default_traits::empty(L); - } - - static inline int real_empty_call(lua_State* L) { - return real_empty_traits(container_detail::has_traits_empty(), L); - } - - static inline int real_erase_traits(std::true_type, lua_State* L) { - return traits::erase(L); - } - - static inline int real_erase_traits(std::false_type, lua_State* L) { - return default_traits::erase(L); - } - - static inline int real_erase_call(lua_State* L) { - return real_erase_traits(container_detail::has_traits_erase(), L); - } - - static inline int real_find_traits(std::true_type, lua_State* L) { - return traits::find(L); - } - - static inline int real_find_traits(std::false_type, lua_State* L) { - return default_traits::find(L); - } - - static inline int real_find_call(lua_State* L) { - return real_find_traits(container_detail::has_traits_find(), L); - } - - static inline int real_index_of_traits(std::true_type, lua_State* L) { - return traits::index_of(L); - } - - static inline int real_index_of_traits(std::false_type, lua_State* L) { - return default_traits::index_of(L); - } - - static inline int real_index_of_call(lua_State* L) { - return real_index_of_traits(container_detail::has_traits_index_of(), L); - } - - static inline int add_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int erase_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int insert_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int clear_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int empty_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int find_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int index_of_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int length_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int pairs_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int ipairs_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int next_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int at_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int get_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int set_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int index_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - - static inline int new_index_call(lua_State* L) { - return detail::typed_static_trampoline(L); - } - }; + static inline int new_index_call(lua_State* L) { + return detail::typed_static_trampoline(L); + } + }; + } // namespace container_detail namespace stack { namespace stack_detail { @@ -19515,10 +19564,8 @@ namespace sol { } void operator()() { - typedef usertype_container>, - std::remove_pointer_t>> - meta_usertype_container; + using meta_usertype_container = container_detail::u_c_launch< + std::conditional_t>, std::remove_pointer_t>>; static const char* metakey = is_shim ? &usertype_traits>>::metatable()[0] : &usertype_traits::metatable()[0]; static const std::array reg = { { // clang-format off @@ -19645,7 +19692,7 @@ namespace sol { } // namespace sol -// end of sol/usertype_container.hpp +// end of sol/usertype_container_launch.hpp namespace sol { namespace u_detail { @@ -19749,7 +19796,7 @@ namespace sol { ifx(meta_function::equal_to, f); } if (fx(meta_function::pairs)) { - ifx(meta_function::pairs, &usertype_container>::pairs_call); + ifx(meta_function::pairs, &container_detail::u_c_launch>::pairs_call); } if (fx(meta_function::length)) { if constexpr (meta::has_size::value || meta::has_size::value) { @@ -20728,7 +20775,7 @@ namespace sol { namespace u_detail { stack::set_field(L, detail::base_class_cast_key(), reinterpret_cast(&detail::inheritance::type_cast), t.stack_index()); auto prop_fx = detail::properties_enrollment_allowed(storage.properties, enrollments); - auto insert_fx = [&](meta_function mf, lua_CFunction reg) { + auto insert_fx = [&L, &t, &storage](meta_function mf, lua_CFunction reg) { stack::set_field(L, mf, reg, t.stack_index()); storage.properties[static_cast(mf)] = true; }; @@ -21805,9 +21852,14 @@ namespace sol { void tuple_set(std::index_sequence, std::tuple&& args) { using args_tuple = std::tuple&&; optional&> maybe_uts = u_detail::maybe_get_usertype_storage(this->lua_state()); - if (maybe_uts) { - u_detail::usertype_storage& uts = *maybe_uts; - detail::swallow{ 0, (uts.set(this->lua_state(), std::get(std::forward(args)), std::get(std::forward(args))), 0)... }; + if constexpr(sizeof...(I) > 0) { + if (maybe_uts) { + u_detail::usertype_storage& uts = *maybe_uts; + detail::swallow{ 0, (uts.set(this->lua_state(), std::get(std::forward(args)), std::get(std::forward(args))), 0)... }; + } + } + else { + (void)args; } } @@ -22465,7 +22517,7 @@ namespace sol { template void open_libraries(Args&&... args) { static_assert(meta::all_same::value, "all types must be libraries"); - if (sizeof...(args) == 0) { + if constexpr (sizeof...(args) == 0) { luaL_openlibs(L); return; } diff --git a/tests/compile_tests/source/container_traits.cpp b/tests/compile_tests/source/usertype_container_launch.cpp similarity index 96% rename from tests/compile_tests/source/container_traits.cpp rename to tests/compile_tests/source/usertype_container_launch.cpp index 4db25f14..747121dd 100644 --- a/tests/compile_tests/source/container_traits.cpp +++ b/tests/compile_tests/source/usertype_container_launch.cpp @@ -23,4 +23,4 @@ #include "sol_defines.hpp" -#include +#include diff --git a/tests/runtime_tests/source/container_shims.cpp b/tests/runtime_tests/source/container_shims.cpp index 53f6dbfc..bc3d0425 100644 --- a/tests/runtime_tests/source/container_shims.cpp +++ b/tests/runtime_tests/source/container_shims.cpp @@ -36,7 +36,6 @@ #include #include - class int_shim { public: int_shim() = default; @@ -143,7 +142,7 @@ namespace sol { struct is_container : std::true_type {}; template <> - struct container_traits { + struct usertype_container { static auto begin(lua_State*, my_vec& self) { return self.begin(); } @@ -157,6 +156,21 @@ namespace sol { } // namespace sol +struct order_suit { + std::vector> objs; + std::vector> objs2; + + order_suit(int pairs) { + objs.reserve(pairs); + objs2.reserve(pairs * 2); + for (int i = 0; i < pairs; ++i) { + objs.push_back({ i, i * 10 }); + objs2.push_back({ (i + pairs) * 2, (i * 2) * 50 }); + objs2.push_back({ ((i + pairs) * 2) + 1, (i * 2 + 1) * 50 }); + } + } +}; + TEST_CASE("containers/input iterators", "test shitty input iterators that are all kinds of B L E H") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::package); @@ -255,3 +269,43 @@ TEST_CASE("containers/containers of pointers", "containers of pointers shouldn't REQUIRE(*bp == 600); } } + +TEST_CASE("containers/pair container in usertypes", "make sure containers that use pairs in usertypes do not trigger compiler errors") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + auto orderSuit = lua.new_usertype("order_suit", sol::constructors()); +#define SET_PROP(__PROP__) orderSuit.set(#__PROP__, &order_suit::__PROP__) + SET_PROP(objs); + SET_PROP(objs2); +#undef SET_PROP + + auto result1 = lua.safe_script("osobj = order_suit.new(5)", sol::script_pass_on_error); + REQUIRE(result1.valid()); + auto result2 = lua.safe_script("pvec = osobj.objs", sol::script_pass_on_error); + REQUIRE(result2.valid()); + auto result3 = lua.safe_script("pvec2 = osobj.objs2", sol::script_pass_on_error); + REQUIRE(result3.valid()); + using vec_t = std::remove_reference_t().objs)>; + using vec2_t = std::remove_reference_t().objs2)>; + vec_t& pvec = lua["pvec"]; + vec2_t& pvec2 = lua["pvec2"]; + REQUIRE(pvec.size() == 5); + REQUIRE(pvec2.size() == 10); + + REQUIRE(pvec[0].first == 0); + REQUIRE(pvec[0].second == 0); + REQUIRE(pvec[1].first == 1); + REQUIRE(pvec[1].second == 10); + REQUIRE(pvec[2].first == 2); + REQUIRE(pvec[2].second == 20); + + REQUIRE(pvec2[0].first == 10); + REQUIRE(pvec2[0].second == 0); + REQUIRE(pvec2[1].first == 11); + REQUIRE(pvec2[1].second == 50); + REQUIRE(pvec2[2].first == 12); + REQUIRE(pvec2[2].second == 100); + REQUIRE(pvec2[3].first == 13); + REQUIRE(pvec2[3].second == 150); +} diff --git a/tests/runtime_tests/source/functions.cpp b/tests/runtime_tests/source/functions.cpp index 6c629179..5c2f3414 100644 --- a/tests/runtime_tests/source/functions.cpp +++ b/tests/runtime_tests/source/functions.cpp @@ -429,7 +429,7 @@ TEST_CASE("functions/function_result and protected_function_result", "Function r } #if !defined(SOL2_CI) && ((!defined(_M_IX86) || defined(_M_IA64)) || (defined(_WIN64)) || (defined(__LLP64__) || defined(__LP64__)) ) -TEST_CASE("functions/unsafe protected_function_result handlers", "This test will thrash the stack and allocations on weaker compilers (e.g., non 64-bit ones). Run with caution.") { +TEST_CASE("functions/safe protected_function_result handlers", "These tests will (hopefully) not destroy the stack since they are supposed to be mildly safe. Still, run with caution.") { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::debug); static const char unhandlederrormessage[] = "true error message"; @@ -449,23 +449,11 @@ TEST_CASE("functions/unsafe protected_function_result handlers", "This test will return handlederrormessage; }; lua.set_function("cpphandler", cpphandlerfx); - auto nontrampolinefx = [](lua_State*) -> int { - // this code shoots an exception - // through the C API, without the trampoline - // present. - // it is probably guaranteed to kill our code. - throw "x"; - }; - lua_CFunction c_nontrampolinefx = nontrampolinefx; - lua.set("nontrampoline", c_nontrampolinefx); - lua.set_function("bark", []() -> int { return 100; }); sol::function cpphandler = lua["cpphandler"]; sol::protected_function luadoom(lua["luadoom"]); - sol::protected_function nontrampoline = lua["nontrampoline"]; luadoom.error_handler = cpphandler; - nontrampoline.error_handler = cpphandler; - + bool present = true; { sol::protected_function_result result = luadoom(); @@ -479,12 +467,40 @@ TEST_CASE("functions/unsafe protected_function_result handlers", "This test will sol::error err = result; REQUIRE(err.what() == handlederrormessage_s); } +} + +TEST_CASE("functions/unsafe protected_function_result handlers", + "This test will thrash the stack and allocations on weaker compilers (e.g., non 64-bit ones). Run with caution.") { + sol::state lua; + lua.open_libraries(sol::lib::base, sol::lib::debug); + static const char handlederrormessage[] = "doodle"; + static const std::string handlederrormessage_s = handlederrormessage; + + auto cpphandlerfx = [](std::string x) { + INFO("c++ handler called with: " << x); + return handlederrormessage; + }; + lua.set_function("cpphandler", cpphandlerfx); + auto nontrampolinefx = [](lua_State*) -> int { + // this code shoots an exception + // through the C API, without the trampoline + // present. + // it is probably guaranteed to kill our code. + throw "x"; + }; + lua_CFunction c_nontrampolinefx = nontrampolinefx; + lua.set("nontrampoline", c_nontrampolinefx); + + sol::function cpphandler = lua["cpphandler"]; + sol::protected_function nontrampoline = lua["nontrampoline"]; + nontrampoline.error_handler = cpphandler; + { sol::protected_function_result result = nontrampoline(); REQUIRE_FALSE(result.valid()); sol::optional operr = result; sol::optional opvalue = result; - present = (bool)operr; + bool present = (bool)operr; REQUIRE(present); present = (bool)opvalue; REQUIRE_FALSE(present); @@ -492,7 +508,7 @@ TEST_CASE("functions/unsafe protected_function_result handlers", "This test will REQUIRE(err.what() == handlederrormessage_s); } } -#endif // This test will thrash the stack and allocations on weaker compilers +#endif // These tests will thrash the stack and allocations on weaker compilers TEST_CASE("functions/all kinds", "Register all kinds of functions, make sure they all compile and work") { sol::state lua; @@ -670,12 +686,16 @@ N = n(1, 2, 3) REQUIRE(N == 13); // Work that compiler, WORK IT! + test_2 test_2_instance; lua.set("o", &test_1::bark); lua.set("p", test_1::x_bark); lua.set("q", sol::c_call); lua.set("r", &test_2::a); lua.set("s", sol::readonly(&test_2::a)); lua.set_function("t", sol::readonly(&test_2::a), test_2()); + lua.set_function("t2", sol::readonly(&test_2::a), &test_2_instance); + lua.set_function("t3", sol::readonly(&test_2::a), std::ref(test_2_instance)); + lua.set_function("t4", sol::readonly(&test_2::a), std::cref(test_2_instance)); lua.set_function("u", &nested::i, nested()); lua.set("v", &nested::i); lua.set("nested", nested()); @@ -687,6 +707,12 @@ N = n(1, 2, 3) { auto result = lua.safe_script("t(2)", sol::script_pass_on_error); REQUIRE_FALSE(result.valid()); + auto result2 = lua.safe_script("t2(2)", sol::script_pass_on_error); + REQUIRE_FALSE(result2.valid()); + auto result3 = lua.safe_script("t3(2)", sol::script_pass_on_error); + REQUIRE_FALSE(result3.valid()); + auto result4 = lua.safe_script("t4(2)", sol::script_pass_on_error); + REQUIRE_FALSE(result4.valid()); } { auto result = lua.safe_script("u(inner)", sol::script_pass_on_error);