diff --git a/sol/container_usertype_metatable.hpp b/sol/container_usertype_metatable.hpp index 2cbc97f0..0743067a 100644 --- a/sol/container_usertype_metatable.hpp +++ b/sol/container_usertype_metatable.hpp @@ -47,7 +47,33 @@ namespace sol { typedef std::array one; typedef std::array two; - template static one test(decltype(&C::push_back)); + template static one test(decltype(std::declval().push_back(std::declval>()))*); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_clear { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(&C::clear)); + template static two test(...); + + public: + static const bool value = sizeof(test(0)) == sizeof(char); + }; + + template + struct has_insert { + private: + typedef std::array one; + typedef std::array two; + + template static one test(decltype(std::declval().insert(std::declval>(), std::declval>()))*); template static two test(...); public: @@ -82,10 +108,12 @@ namespace sol { template struct container_usertype_metatable { + typedef meta::has_key_value_pair> is_associative; typedef meta::unqualified_t T; - typedef std::size_t K; - typedef typename T::value_type V; typedef typename T::iterator I; + typedef std::conditional_t> KV; + typedef typename KV::first_type K; + typedef typename KV::second_type V; typedef std::remove_reference_t())> IR; struct iter { @@ -107,7 +135,36 @@ namespace sol { #endif } - static int real_index_call(lua_State* L) { + static int real_index_call_associative(std::true_type, lua_State* L) { + auto k = stack::check_get(L, 2); + if (k) { + auto& src = get_src(L); + using std::end; + auto it = detail::find(src, *k); + if (it != end(src)) { + auto& v = *it; + return stack::push_reference(L, v.second); + } + } + else { + auto maybename = stack::check_get(L, 2); + if (maybename) { + auto& name = *maybename; + if (name == "add") { + return stack::push(L, &add_call); + } + else if (name == "insert") { + return stack::push(L, &insert_call); + } + else if (name == "clear") { + return stack::push(L, &clear_call); + } + } + } + return stack::push(L, nil); + } + + static int real_index_call_associative(std::false_type, lua_State* L) { auto& src = get_src(L); auto maybek = stack::check_get(L, 2); if (maybek) { @@ -143,12 +200,36 @@ namespace sol { return stack::push(L, nil); } - static int real_new_index_call_const(std::false_type, lua_State* L) { - luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + static int real_index_call(lua_State* L) { + return real_index_call_associative(is_associative(), L); + } + + static int real_new_index_call_const(std::false_type, std::false_type, lua_State* L) { + return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + } + + static int real_new_index_call_const(std::false_type, std::true_type, lua_State* L) { + return luaL_error(L, "sol: cannot write to a const value type or an immutable iterator (e.g., std::set)"); + } + + static int real_new_index_call_const(std::true_type, std::true_type, lua_State* L) { + auto& src = get_src(L); + auto k = stack::check_get(L, 2); + if (k) { + using std::end; + auto it = detail::find(src, *k); + if (it != end(src)) { + auto& v = *it; + v.second = stack::get(L, 3); + } + else { + src.insert(it, { std::move(*k), stack::get(L, 3) }); + } + } return 0; } - static int real_new_index_call_const(std::true_type, lua_State* L) { + static int real_new_index_call_const(std::true_type, std::false_type, lua_State* L) { auto& src = get_src(L); #ifdef SOL_SAFE_USERTYPE auto maybek = stack::check_get(L, 2); @@ -172,10 +253,32 @@ namespace sol { } static int real_new_index_call(lua_State* L) { - return real_new_index_call_const(meta::neg, std::is_const>>(), L); + return real_new_index_call_const(meta::neg, std::is_const>>(), is_associative(), L); } - static int real_pairs_next_call(lua_State* L) { + static int real_pairs_next_call_assoc(std::true_type, lua_State* L) { + using std::end; + iter& i = stack::get>(L, 1); + auto& source = i.source; + auto& it = i.it; + if (it == end(source)) { + return 0; + } + int p = stack::multi_push_reference(L, it->first, it->second); + std::advance(it, 1); + return p; + } + + static int real_pairs_call_assoc(std::true_type, lua_State* L) { + auto& src = get_src(L); + using std::begin; + stack::push(L, pairs_next_call); + stack::push>(L, src, begin(src)); + stack::push(L, 1); + return 3; + } + + static int real_pairs_next_call_assoc(std::false_type, lua_State* L) { using std::end; iter& i = stack::get>(L, 1); auto& source = i.source; @@ -189,7 +292,7 @@ namespace sol { return p; } - static int real_pairs_call(lua_State* L) { + static int real_pairs_call_assoc(std::false_type, lua_State* L) { auto& src = get_src(L); using std::begin; stack::push(L, pairs_next_call); @@ -198,40 +301,100 @@ namespace sol { return 3; } + static int real_pairs_next_call(lua_State* L) { + return real_pairs_next_call_assoc(is_associative(), L); + } + + static int real_pairs_call(lua_State* L) { + return real_pairs_call_assoc(is_associative(), L); + } + static int real_length_call(lua_State*L) { auto& src = get_src(L); return stack::push(L, src.size()); } + static int real_add_call_insert(std::true_type, lua_State*L, T& src, int boost = 0) { + using std::end; + src.insert(end(src), stack::get(L, 2 + boost)); + return 0; + } + + static int real_add_call_insert(std::false_type, lua_State*L, T&, int = 0) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); + } + static int real_add_call_push(std::true_type, lua_State*L, T& src, int boost = 0) { src.push_back(stack::get(L, 2 + boost)); return 0; } static int real_add_call_push(std::false_type, lua_State*L, T& src, int boost = 0) { - using std::end; - src.insert(end(src), stack::get(L, 2 + boost)); - return 0; + return real_add_call_insert(std::integral_constant::value>(), L, src, boost); } - static int real_add_call(lua_State*L) { + static int real_add_call_associative(std::true_type, lua_State* L) { + return real_insert_call(L); + } + + static int real_add_call_associative(std::false_type, lua_State* L) { auto& src = get_src(L); return real_add_call_push(std::integral_constant::value>(), L, src); } - static int real_insert_call(lua_State*L) { + static int real_add_call_capable(std::true_type, lua_State* L) { + return real_add_call_associative(is_associative(), L); + } + + static int real_add_call_capable(std::false_type, lua_State* L) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call add on type %s", s.c_str()); + } + + static int real_add_call(lua_State* L) { + return real_add_call_capable(std::integral_constant::value || detail::has_insert::value>(), L); + } + + static int real_insert_call_capable(std::false_type, std::false_type, lua_State*L) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call insert on type %s", s.c_str()); + } + + static int real_insert_call_capable(std::false_type, std::true_type, lua_State*L) { + return real_insert_call_capable(std::false_type(), std::false_type(), L); + } + + static int real_insert_call_capable(std::true_type, std::false_type, lua_State* L) { using std::begin; auto& src = get_src(L); src.insert(std::next(begin(src), stack::get(L, 2)), stack::get(L, 3)); return 0; } - static int real_clear_call(lua_State*L) { + static int real_insert_call_capable(std::true_type, std::true_type, lua_State* L) { + return real_new_index_call(L); + } + + static int real_insert_call(lua_State*L) { + return real_insert_call_capable(std::integral_constant::value>(), is_associative(), L); + } + + static int real_clear_call_capable(std::false_type, lua_State* L) { + static const std::string& s = detail::demangle(); + return luaL_error(L, "sol: cannot call clear on type %s", s.c_str()); + } + + static int real_clear_call_capable(std::true_type, lua_State* L) { auto& src = get_src(L); src.clear(); return 0; } + static int real_clear_call(lua_State*L) { + return real_clear_call_capable(std::integral_constant::value>(), L); + } + static int add_call(lua_State*L) { return detail::static_trampoline<(&real_add_call)>(L); } @@ -265,157 +428,6 @@ namespace sol { } }; - template - struct container_usertype_metatable>::value>> { - typedef meta::unqualified_t T; - typedef typename T::value_type KV; - typedef typename KV::first_type K; - typedef typename KV::second_type V; - typedef typename T::iterator I; - struct iter { - T& source; - I it; - - iter(T& source, I it) : source(source), it(std::move(it)) {} - }; - - static auto& get_src(lua_State* L) { -#ifdef SOL_SAFE_USERTYPE - auto p = stack::check_get(L, 1); - if (!p || p.value() == nullptr) { - luaL_error(L, "sol: 'self' argument is nil (pass 'self' as first argument or call on proper type)"); - } - return *p.value(); -#else - return stack::get(L, 1); -#endif - } - - static int real_index_call(lua_State* L) { - auto k = stack::check_get(L, 2); - if (k) { - auto& src = get_src(L); - using std::end; - auto it = detail::find(src, *k); - if (it != end(src)) { - auto& v = *it; - return stack::push_reference(L, v.second); - } - } - else { - auto maybename = stack::check_get(L, 2); - if (maybename) { - auto& name = *maybename; - if (name == "add") { - return stack::push(L, &add_call); - } - else if (name == "insert") { - return stack::push(L, &insert_call); - } - else if (name == "clear") { - return stack::push(L, &clear_call); - } - } - } - return stack::push(L, nil); - } - - static int real_new_index_call_const(std::false_type, lua_State* L) { - luaL_error(L, "sol: cannot write to a const value type"); - return 0; - } - - static int real_new_index_call_const(std::true_type, lua_State* L) { - auto& src = get_src(L); - auto k = stack::check_get(L, 2); - if (k) { - using std::end; - auto it = detail::find(src, *k); - if (it != end(src)) { - auto& v = *it; - v.second = stack::get(L, 3); - } - else { - src.insert(it, { std::move(*k), stack::get(L, 3) }); - } - } - return 0; - } - - static int real_new_index_call(lua_State* L) { - return real_new_index_call_const(meta::neg>(), L); - } - - static int real_pairs_next_call(lua_State* L) { - using std::end; - iter& i = stack::get>(L, 1); - auto& source = i.source; - auto& it = i.it; - if (it == end(source)) { - return 0; - } - int p = stack::multi_push_reference(L, it->first, it->second); - std::advance(it, 1); - return p; - } - - static int real_pairs_call(lua_State* L) { - auto& src = get_src(L); - using std::begin; - stack::push(L, pairs_next_call); - stack::push>(L, src, begin(src)); - stack::push(L, 1); - return 3; - } - - static int real_length_call(lua_State*L) { - auto& src = get_src(L); - return stack::push(L, src.size()); - } - - static int real_insert_call(lua_State*L) { - return real_new_index_call(L); - } - - static int real_clear_call(lua_State*L) { - auto& src = get_src(L); - src.clear(); - return 0; - } - - static int add_call(lua_State*L) { - return detail::static_trampoline<(&real_insert_call)>(L); - } - - static int insert_call(lua_State*L) { - return detail::static_trampoline<(&real_insert_call)>(L); - } - - static int clear_call(lua_State*L) { - return detail::static_trampoline<(&real_clear_call)>(L); - } - - static int length_call(lua_State*L) { - return detail::static_trampoline<(&real_length_call)>(L); - } - - static int pairs_next_call(lua_State*L) { - return detail::static_trampoline<(&real_pairs_next_call)>(L); - } - - static int pairs_call(lua_State*L) { - return detail::static_trampoline<(&real_pairs_call)>(L); - } - - static int index_call(lua_State*L) { - return detail::static_trampoline<(&real_index_call)>(L); - } - - static int new_index_call(lua_State*L) { - return detail::static_trampoline<(&real_new_index_call)>(L); - } - }; - namespace stack { namespace stack_detail { template diff --git a/sol/demangle.hpp b/sol/demangle.hpp index 90ee42ac..1c49da4f 100644 --- a/sol/demangle.hpp +++ b/sol/demangle.hpp @@ -142,13 +142,13 @@ namespace sol { } template - inline std::string demangle() { + inline const std::string& demangle() { static const std::string d = demangle_once(); return d; } template - inline std::string short_demangle() { + inline const std::string& short_demangle() { static const std::string d = short_demangle_once(); return d; } diff --git a/sol/traits.hpp b/sol/traits.hpp index 3e0fd9fe..d1d8fc63 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -45,6 +45,9 @@ namespace sol { template struct is_tuple> : std::true_type { }; + template + struct is_builtin_type : std::integral_constant::value || std::is_pointer::value || std::is_array::value> {}; + template struct unwrapped { typedef T type; diff --git a/sol/usertype_traits.hpp b/sol/usertype_traits.hpp index d1439ed0..a776a16f 100644 --- a/sol/usertype_traits.hpp +++ b/sol/usertype_traits.hpp @@ -29,11 +29,11 @@ namespace sol { template struct usertype_traits { static const std::string& name() { - static const std::string n = detail::short_demangle(); + static const std::string& n = detail::short_demangle(); return n; } static const std::string& qualified_name() { - static const std::string q_n = detail::demangle(); + static const std::string& q_n = detail::demangle(); return q_n; } static const std::string& metatable() { @@ -49,7 +49,7 @@ namespace sol { return u_g_m; } static const std::string& gc_table() { - static const std::string g_t = std::string("sol.").append(detail::demangle().append(".\xE2\x99\xBB")); + static const std::string g_t = std::string("sol.").append(detail::demangle()).append(".\xE2\x99\xBB"); return g_t; } };