diff --git a/docs/source/api/containers.rst b/docs/source/api/containers.rst index 36821a34..327e4de1 100644 --- a/docs/source/api/containers.rst +++ b/docs/source/api/containers.rst @@ -77,6 +77,7 @@ Based on the type pushed, a few additional functions are added as "member functi * ``my_container:add( key, value )`` or ``my_container:add( value )``: this will add to the end of the container, or if it is an associative or ordered container, simply put in an expected key-value pair into it. * ``my_contaner:insert( where, value )`` or ``my_contaner:insert( key, value )``: similar to add, but it only takes two arguments. In the case of ``std::vector`` and the like, the first argument is a ``where`` integer index. The second argument is the value. For associative containers, a key and value argument are expected. * ``my_container:find( value )``: This will call the underlying containers ``find`` function if it exists, or in case of associative containers, it will work just like an index call. This is meant to give a fast membership check for ``std::set`` and ``std::unordered_set`` containers. +* ``my_container:get( key )``: This function can return multiple values when the value type is a ``std::pair`` or ``std::tuple``, which is not the case for ``obj[key]``! This will call the underlying containers ``find`` function if it exists, index into a regular container, or in case of certain associative containers, it will work just like an index call. This is meant to give a fast membership check for ``std::set`` and ``std::unordered_set`` containers. .. _container-detection: diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index 3b7e13aa..4a721262 100644 --- a/single/sol/sol.hpp +++ b/single/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 2017-06-20 03:06:38.649640 UTC -// This header was generated with sol v2.17.5 (revision 5468ab8) +// Generated 2017-06-23 22:06:34.237732 UTC +// This header was generated with sol v2.17.5 (revision 6b34a15) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -829,6 +829,24 @@ namespace sol { static std::false_type test(...); }; + struct has_key_type_impl { + template, + typename V = typename U::key_type> + static std::true_type test(int); + + template + static std::false_type test(...); + }; + + struct has_mapped_type_impl { + template, + typename V = typename U::mapped_type> + static std::true_type test(int); + + template + static std::false_type test(...); + }; + struct has_key_value_pair_impl { template, typename V = typename U::value_type, @@ -868,6 +886,15 @@ namespace sol { template struct has_key_value_pair : decltype(meta_detail::has_key_value_pair_impl::test(0)) {}; + template + struct has_key_type : decltype(meta_detail::has_key_type_impl::test(0)) {}; + + template + struct has_mapped_type : decltype(meta_detail::has_mapped_type_impl::test(0)) {}; + + template + struct is_associative : meta::all, has_mapped_type> {}; + template using is_string_constructible = any, const char*>, std::is_same, char>, std::is_same, std::string>, std::is_same, std::initializer_list>>; @@ -3541,6 +3568,11 @@ namespace sol { return as_table_t(std::forward(container)); } + template + nested as_nested(T&& container) { + return as_nested(std::forward(container)); + } + struct this_state { lua_State* L; operator lua_State* () const { @@ -12520,7 +12552,7 @@ namespace sol { template struct container_usertype_metatable { - typedef meta::has_key_value_pair> is_associative; + typedef meta::is_associative>> is_associative; typedef meta::unqualified_t T; typedef typename T::iterator I; typedef std::conditional_t> KV; @@ -12555,6 +12587,25 @@ namespace sol { #endif // Safe getting with error } + static int delegate_call(lua_State* L) { + static std::unordered_map calls{ + { "add", &real_add_call }, + { "insert", &real_insert_call }, + { "clear", &real_clear_call }, + { "find", &real_find_call }, + { "get", &real_get_call } + }; + auto maybename = stack::check_get(L, 2); + if (maybename) { + auto& name = *maybename; + auto it = calls.find(name); + if (it != calls.cend()) { + return stack::push(L, it->second); + } + } + return stack::push(L, lua_nil); + } + static int real_index_call_associative(std::true_type, lua_State* L) { auto k = stack::check_get(L, 2); if (k) { @@ -12567,22 +12618,7 @@ namespace sol { } } 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); - } - else if (name == "find") { - return stack::push(L, &find_call); - } - } + return delegate_call(L); } return stack::push(L, lua_nil); } @@ -12602,22 +12638,7 @@ namespace sol { return stack::stack_detail::push_reference(L, *it); } 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); - } - else if (name == "find") { - return stack::push(L, &find_call); - } - } + return delegate_call(L); } return stack::push(L, lua_nil); @@ -12627,6 +12648,10 @@ namespace sol { return real_index_call_associative(is_associative(), L); } + static int real_get_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)"); } @@ -12635,12 +12660,12 @@ namespace sol { 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) { + static int real_new_index_call_fixed(std::true_type, lua_State* L) { auto& src = get_src(L); #ifdef SOL_CHECK_ARGUMENTS auto maybek = stack::check_get(L, 2); if (!maybek) { - return luaL_error(L, "sol: improper key of type %s to a %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); } K& k = *maybek; #else @@ -12658,6 +12683,33 @@ namespace sol { return 0; } + static int real_new_index_call_fixed(std::false_type, lua_State* L) { + auto& src = get_src(L); +#ifdef SOL_CHECK_ARGUMENTS + auto maybek = stack::check_get(L, 2); + if (!maybek) { + return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + K& k = *maybek; +#else + K k = stack::get(L, 2); +#endif + using std::end; + auto it = detail::find(src, k); + if (it != end(src)) { + auto& v = *it; + v.second = stack::get(L, 3); + } + else { + return luaL_error(L, "sol: cannot insert key of type %s to into %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + return 0; + } + + static int real_new_index_call_const(std::true_type, std::true_type, lua_State* L) { + return real_new_index_call_fixed(std::integral_constant::value>(), L); + } + static int real_new_index_call_const(std::true_type, std::false_type, lua_State* L) { auto& src = get_src(L); #ifdef SOL_CHECK_ARGUMENTS @@ -12692,7 +12744,7 @@ namespace sol { } static int real_new_index_call(lua_State* L) { - return real_new_index_call_const(meta::neg, std::is_const, meta::neg>>>(), is_associative(), L); + return real_new_index_call_const(meta::neg, std::is_const, meta::neg>>>(), meta::all>(), L); } static int real_pairs_next_call_assoc(std::true_type, lua_State* L) { @@ -12896,6 +12948,10 @@ namespace sol { return detail::typed_static_trampoline(L); } + static int get_call(lua_State*L) { + return detail::typed_static_trampoline(L); + } + static int index_call(lua_State*L) { return detail::typed_static_trampoline(L); } @@ -12910,12 +12966,13 @@ namespace sol { template inline auto container_metatable() { typedef container_usertype_metatable> meta_cumt; - std::array reg = { { + std::array reg = { { { "__index", &meta_cumt::index_call }, { "__newindex", &meta_cumt::new_index_call }, { "__pairs", &meta_cumt::pairs_call }, { "__ipairs", &meta_cumt::pairs_call }, { "__len", &meta_cumt::length_call }, + { "get", &meta_cumt::get_call }, { "clear", &meta_cumt::clear_call }, { "insert", &meta_cumt::insert_call }, { "add", &meta_cumt::add_call }, diff --git a/sol/container_usertype_metatable.hpp b/sol/container_usertype_metatable.hpp index c0cd7c63..967cbd7e 100644 --- a/sol/container_usertype_metatable.hpp +++ b/sol/container_usertype_metatable.hpp @@ -23,6 +23,7 @@ #define SOL_CONTAINER_USERTYPE_HPP #include "stack.hpp" +#include namespace sol { @@ -108,7 +109,7 @@ namespace sol { template struct container_usertype_metatable { - typedef meta::has_key_value_pair> is_associative; + typedef meta::is_associative>> is_associative; typedef meta::unqualified_t T; typedef typename T::iterator I; typedef std::conditional_t> KV; @@ -143,6 +144,25 @@ namespace sol { #endif // Safe getting with error } + static int delegate_call(lua_State* L) { + static std::unordered_map calls{ + { "add", &real_add_call }, + { "insert", &real_insert_call }, + { "clear", &real_clear_call }, + { "find", &real_find_call }, + { "get", &real_get_call } + }; + auto maybename = stack::check_get(L, 2); + if (maybename) { + auto& name = *maybename; + auto it = calls.find(name); + if (it != calls.cend()) { + return stack::push(L, it->second); + } + } + return stack::push(L, lua_nil); + } + static int real_index_call_associative(std::true_type, lua_State* L) { auto k = stack::check_get(L, 2); if (k) { @@ -155,22 +175,7 @@ namespace sol { } } 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); - } - else if (name == "find") { - return stack::push(L, &find_call); - } - } + return delegate_call(L); } return stack::push(L, lua_nil); } @@ -190,22 +195,7 @@ namespace sol { return stack::stack_detail::push_reference(L, *it); } 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); - } - else if (name == "find") { - return stack::push(L, &find_call); - } - } + return delegate_call(L); } return stack::push(L, lua_nil); @@ -215,6 +205,10 @@ namespace sol { return real_index_call_associative(is_associative(), L); } + static int real_get_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)"); } @@ -223,12 +217,12 @@ namespace sol { 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) { + static int real_new_index_call_fixed(std::true_type, lua_State* L) { auto& src = get_src(L); #ifdef SOL_CHECK_ARGUMENTS auto maybek = stack::check_get(L, 2); if (!maybek) { - return luaL_error(L, "sol: improper key of type %s to a %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); } K& k = *maybek; #else @@ -246,6 +240,33 @@ namespace sol { return 0; } + static int real_new_index_call_fixed(std::false_type, lua_State* L) { + auto& src = get_src(L); +#ifdef SOL_CHECK_ARGUMENTS + auto maybek = stack::check_get(L, 2); + if (!maybek) { + return luaL_error(L, "sol: improper key of type %s for %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + K& k = *maybek; +#else + K k = stack::get(L, 2); +#endif + using std::end; + auto it = detail::find(src, k); + if (it != end(src)) { + auto& v = *it; + v.second = stack::get(L, 3); + } + else { + return luaL_error(L, "sol: cannot insert key of type %s to into %s", lua_typename(L, static_cast(type_of(L, 2))), detail::demangle().c_str()); + } + return 0; + } + + static int real_new_index_call_const(std::true_type, std::true_type, lua_State* L) { + return real_new_index_call_fixed(std::integral_constant::value>(), L); + } + static int real_new_index_call_const(std::true_type, std::false_type, lua_State* L) { auto& src = get_src(L); #ifdef SOL_CHECK_ARGUMENTS @@ -280,7 +301,7 @@ namespace sol { } static int real_new_index_call(lua_State* L) { - return real_new_index_call_const(meta::neg, std::is_const, meta::neg>>>(), is_associative(), L); + return real_new_index_call_const(meta::neg, std::is_const, meta::neg>>>(), meta::all>(), L); } static int real_pairs_next_call_assoc(std::true_type, lua_State* L) { @@ -484,6 +505,10 @@ namespace sol { return detail::typed_static_trampoline(L); } + static int get_call(lua_State*L) { + return detail::typed_static_trampoline(L); + } + static int index_call(lua_State*L) { return detail::typed_static_trampoline(L); } @@ -498,12 +523,13 @@ namespace sol { template inline auto container_metatable() { typedef container_usertype_metatable> meta_cumt; - std::array reg = { { + std::array reg = { { { "__index", &meta_cumt::index_call }, { "__newindex", &meta_cumt::new_index_call }, { "__pairs", &meta_cumt::pairs_call }, { "__ipairs", &meta_cumt::pairs_call }, { "__len", &meta_cumt::length_call }, + { "get", &meta_cumt::get_call }, { "clear", &meta_cumt::clear_call }, { "insert", &meta_cumt::insert_call }, { "add", &meta_cumt::add_call }, diff --git a/sol/traits.hpp b/sol/traits.hpp index 2c9eeff2..422ac4a4 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -276,6 +276,24 @@ namespace sol { static std::false_type test(...); }; + struct has_key_type_impl { + template, + typename V = typename U::key_type> + static std::true_type test(int); + + template + static std::false_type test(...); + }; + + struct has_mapped_type_impl { + template, + typename V = typename U::mapped_type> + static std::true_type test(int); + + template + static std::false_type test(...); + }; + struct has_key_value_pair_impl { template, typename V = typename U::value_type, @@ -315,6 +333,15 @@ namespace sol { template struct has_key_value_pair : decltype(meta_detail::has_key_value_pair_impl::test(0)) {}; + template + struct has_key_type : decltype(meta_detail::has_key_type_impl::test(0)) {}; + + template + struct has_mapped_type : decltype(meta_detail::has_mapped_type_impl::test(0)) {}; + + template + struct is_associative : meta::all, has_mapped_type> {}; + template using is_string_constructible = any, const char*>, std::is_same, char>, std::is_same, std::string>, std::is_same, std::initializer_list>>; diff --git a/sol/types.hpp b/sol/types.hpp index 7b01bfc6..139092a3 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -399,6 +399,11 @@ namespace sol { return as_table_t(std::forward(container)); } + template + nested as_nested(T&& container) { + return as_nested(std::forward(container)); + } + struct this_state { lua_State* L; operator lua_State* () const { diff --git a/test_containers.cpp b/test_containers.cpp index 990ec690..1b70d84b 100644 --- a/test_containers.cpp +++ b/test_containers.cpp @@ -11,16 +11,20 @@ #include #include -std::vector test_table_return_one() { - return{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; +auto test_table_return_one() { + return sol::as_table(std::vector{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); } -std::vector> test_table_return_two() { - return{ { "one", 1 },{ "two", 2 },{ "three", 3 } }; +auto test_table_return_two() { + return sol::as_table(std::vector>{ { "one", 1 },{ "two", 2 },{ "three", 3 } }); } -std::map test_table_return_three() { - return{ { "name", "Rapptz" },{ "friend", "ThePhD" },{ "project", "sol" } }; +auto test_table_return_three() { + return sol::as_table(std::map{ { "name", "Rapptz" },{ "friend", "ThePhD" },{ "project", "sol" } }); +} + +auto test_table_return_four() { + return sol::as_table(std::array, 4>{ { { "one", 1 },{ "two", 2 },{ "three", 3 },{ "four", 4 } } }); } TEST_CASE("containers/returns", "make sure that even references to vectors are being serialized as tables") { @@ -249,10 +253,12 @@ TEST_CASE("containers/arbitrary-creation", "userdata and tables should be usable lua.set_function("test_one", test_table_return_one); lua.set_function("test_two", test_table_return_two); lua.set_function("test_three", test_table_return_three); + lua.set_function("test_four", test_table_return_four); REQUIRE_NOTHROW(lua.script("a = test_one()")); REQUIRE_NOTHROW(lua.script("b = test_two()")); REQUIRE_NOTHROW(lua.script("c = test_three()")); + REQUIRE_NOTHROW(lua.script("d = test_four()")); REQUIRE_NOTHROW(lua.script("assert(#a == 10, 'error')")); REQUIRE_NOTHROW(lua.script("assert(a[3] == 3, 'error')")); @@ -260,10 +266,14 @@ TEST_CASE("containers/arbitrary-creation", "userdata and tables should be usable REQUIRE_NOTHROW(lua.script("assert(b.three == 3, 'error')")); REQUIRE_NOTHROW(lua.script("assert(c.name == 'Rapptz', 'error')")); REQUIRE_NOTHROW(lua.script("assert(c.project == 'sol', 'error')")); + REQUIRE_NOTHROW(lua.script("assert(d.one == 1, 'error')")); + REQUIRE_NOTHROW(lua.script("assert(d.three == 3, 'error')")); + REQUIRE_NOTHROW(lua.script("assert(d.four == 4, 'error')")); sol::table a = lua.get("a"); sol::table b = lua.get("b"); sol::table c = lua.get("c"); + sol::table d = lua["d"]; REQUIRE(a.size() == 10ULL); REQUIRE(a.get(3) == 3); @@ -271,6 +281,9 @@ TEST_CASE("containers/arbitrary-creation", "userdata and tables should be usable REQUIRE(b.get("three") == 3); REQUIRE(c.get("name") == "Rapptz"); REQUIRE(c.get("project") == "sol"); + REQUIRE(d.get("one") == 1); + REQUIRE(d.get("three") == 3); + REQUIRE(d.get("four") == 4); } TEST_CASE("containers/extra-functions", "make sure the manipulation functions are present and usable and working across various container types") { @@ -540,7 +553,7 @@ TEST_CASE("containers/to_args", "Test that the to_args abstractions works") { } -TEST_CASE("containers/ipairs-test", "ensure that abstractions roundtrip properly and push nils to stop pairs / ipairs") { +TEST_CASE("containers/ipairs-test", "ensure that abstractions roundtrip properly") { struct thing { int x = 20; }; @@ -752,3 +765,59 @@ end )lua"); #endif } + +TEST_CASE("containers/pairs", "test how well pairs work with the underlying system") { + sol::state lua; + + lua.open_libraries(sol::lib::base); + + std::vector> a{ { "one", 1 },{ "two", 2 },{ "three", 3 },{ "four", 4 },{ "five", 5 } }; + std::array, 5> b{ { { "one", 1 },{ "two", 2 },{ "three", 3 },{ "four", 4 },{ "five", 5 } } }; + //std::pair c[5]{ { "one", 1 },{ "two", 2 },{ "three", 3 },{ "four", 4 },{ "five", 5 } }; + //int d[5] = { 1, 2, 3, 4, 5 }; + + lua["a"] = std::ref(a); + lua["b"] = &b; + //lua["c"] = std::ref(c); + //lua["d"] = &d; + + lua.script("av1, av2 = a:get(1)"); + lua.script("bv1, bv2 = b:get(1)"); + //lua.script("cv1, cv2 = c:get(1)"); + //lua.script("dv1, dv2 = d:get(1)"); + + std::vector>& la = lua["a"]; + std::array, 5>& lb = lua["b"]; + //std::pair (&lc)[5] = lua["c"]; + //int (&lc)[5] = lua["d"]; + + std::pair& va = la[0]; + std::pair& vb = lb[0]; + //std::pair& vc = lc[0]; + //int vd = ld[0]; + std::string av1 = lua["av1"]; + int av2 = lua["av2"]; + std::string bv1 = lua["bv1"]; + int bv2 = lua["bv2"]; + //std::string cv1 = lua["cv1"]; + //int cv2 = lua["cv2"]; + //int dv1 = lua["dv1"]; + //sol::lua_nil_t dv2 = lua["dv2"]; + + REQUIRE(va.first == "one"); + REQUIRE(va.second == 1); + REQUIRE(vb.first == "one"); + REQUIRE(vb.second == 1); + //REQUIRE(vc.first == "one"); + //REQUIRE(vc.second == 1); + //REQUIRE(vd == 1); + + REQUIRE(av1 == "one"); + REQUIRE(av2 == 1); + REQUIRE(bv1 == "one"); + REQUIRE(bv2 == 1); + //REQUIRE(cv1 == "one"); + //REQUIRE(cv2 == 1); + //REQUIRE(dv1 == 1); + //REQUIRE(dv2 == sol::lua_nil); +} diff --git a/test_usertypes.cpp b/test_usertypes.cpp index 3092b613..0e687eb0 100644 --- a/test_usertypes.cpp +++ b/test_usertypes.cpp @@ -671,17 +671,17 @@ TEST_CASE("usertype/private-constructible", "Check to make sure special snowflak } TEST_CASE("usertype/const-pointer", "Make sure const pointers can be taken") { - struct A { int x = 201; }; - struct B { - int foo(const A* a) { return a->x; }; + struct A_x { int x = 201; }; + struct B_foo { + int foo(const A_x* a) { return a->x; }; }; sol::state lua; - lua.new_usertype("B", - "foo", &B::foo + lua.new_usertype("B", + "foo", &B_foo::foo ); - lua.set("a", A()); - lua.set("b", B()); + lua.set("a", A_x()); + lua.set("b", B_foo()); lua.script("x = b:foo(a)"); int x = lua["x"]; REQUIRE(x == 201);