// sol2 // The MIT License (MIT) // Copyright (c) 2013-2022 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. #include "sol_test.hpp" #include "common_classes.hpp" #include #include #include #include #include #include #include #include #include #include #include inline namespace sol2_test_container_table { template struct as_table_callable { T* ptr; as_table_callable(T& ref_) : ptr(&ref_) { } auto operator()() const { return sol::as_table(*ptr); } }; } // namespace sol2_test_container_table TEST_CASE("containers/vector table roundtrip", "make sure vectors can be round-tripped") { sol::state lua; std::vector v { 1, 2, 3 }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/deque table roundtrip", "make sure deques can be round-tripped") { sol::state lua; std::deque v { 1, 2, 3 }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/array table roundtrip", "make sure arrays can be round-tripped") { sol::state lua; std::array v { { 1, 2, 3 } }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/list table roundtrip", "make sure lists can be round-tripped") { sol::state lua; std::list v { 1, 2, 3 }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/forward_list table roundtrip", "make sure forward_lists can be round-tripped") { sol::state lua; std::forward_list v { 1, 2, 3 }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/map table roundtrip", "make sure maps can be round-tripped") { sol::state lua; std::map v { { "a", 1 }, { "b", 2 }, { "c", 3 } }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/unordered_map table roundtrip", "make sure unordered_maps can be round-tripped") { sol::state lua; std::unordered_map v { { "a", 1 }, { "b", 2 }, { "c", 3 } }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/unordered_set table roundtrip", "make sure unordered_sets can be round-tripped") { sol::state lua; std::unordered_set v { 1, 2, 3 }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/set table roundtrip", "make sure sets can be round-tripped") { sol::state lua; std::set v { 1, 2, 3 }; lua.set_function("f", as_table_callable>(v)); auto result1 = lua.safe_script("x = f()", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::as_table_t> x = lua["x"]; bool areequal = x.value() == v; REQUIRE(areequal); } TEST_CASE("containers/table conversions (lvalue)", "test table conversions with as_table and nested, when not directly serializing a temporary / new value") { sol::state lua; auto f = []() { std::vector response_words; response_words.push_back("a"); response_words.push_back("b"); response_words.push_back("c"); return sol::as_table(response_words); }; auto g = []() { std::vector response_words; response_words.push_back("a"); response_words.push_back("b"); response_words.push_back("c"); return sol::as_nested(response_words); }; lua["f"] = std::ref(f); lua["g"] = std::ref(g); sol::safe_function sff = lua["f"]; sol::safe_function sfg = lua["g"]; sol::table tf = sff(); sol::table tg = sfg(); std::string af = tf[1]; std::string bf = tf[2]; std::string cf = tf[3]; std::string ag = tf[1]; std::string bg = tf[2]; std::string cg = tf[3]; REQUIRE(tf.size() == 3); REQUIRE(af == "a"); REQUIRE(bf == "b"); REQUIRE(cf == "c"); REQUIRE(tg.size() == 3); REQUIRE(ag == "a"); REQUIRE(bg == "b"); REQUIRE(cg == "c"); } TEST_CASE("containers/table conversions (std::ref)", "test table conversions with as_table and nested, when not directly serializing a temporary / new value") { sol::state lua; std::vector response_words; response_words.push_back("a"); response_words.push_back("b"); response_words.push_back("c"); auto f = [&response_words]() { return sol::as_table(std::ref(response_words)); }; auto g = [&response_words]() { return sol::as_nested(std::ref(response_words)); }; lua["f"] = std::ref(f); lua["g"] = std::ref(g); sol::safe_function sff = lua["f"]; sol::safe_function sfg = lua["g"]; sol::table tf = sff(); sol::table tg = sfg(); std::string af = tf[1]; std::string bf = tf[2]; std::string cf = tf[3]; std::string ag = tf[1]; std::string bg = tf[2]; std::string cg = tf[3]; REQUIRE(tf.size() == 3); REQUIRE(af == "a"); REQUIRE(bf == "b"); REQUIRE(cf == "c"); REQUIRE(tg.size() == 3); REQUIRE(ag == "a"); REQUIRE(bg == "b"); REQUIRE(cg == "c"); } TEST_CASE("containers/table conversion", "test table conversions with as_table and nested") { sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("bark", []() { return sol::as_nested(std::vector { "bark", "woof" }); }); lua.set_function("woof", []() { return sol::as_nested(std::vector { "bark", "woof" }); }); auto result1 = lua.safe_script("v1 = bark()", sol::script_pass_on_error); REQUIRE(result1.valid()); auto result2 = lua.safe_script("v2 = woof()", sol::script_pass_on_error); REQUIRE(result2.valid()); sol::as_table_t> as_table_strings = lua["v1"]; sol::nested> nested_strings = lua["v2"]; std::vector expected_values { "bark", "woof" }; REQUIRE(as_table_strings.value() == expected_values); REQUIRE(nested_strings.value() == expected_values); } TEST_CASE("containers/from table argument conversions", "test table conversions without as_table and nested for function args") { const std::vector expected_values { "bark", "woof" }; sol::state lua; lua.open_libraries(sol::lib::base); lua.set_function("f", [&](std::vector t) { return t == expected_values; }); auto result0 = lua.safe_script("t = { \"bark\", \"woof\" }"); REQUIRE(result0.valid()); auto result1 = lua.safe_script("assert(f(t))", sol::script_pass_on_error); REQUIRE(result1.valid()); sol::function f = lua["f"]; sol::table t = lua["t"]; bool passed = f(t); REQUIRE(passed); } TEST_CASE("containers/deeply nested", "make sure nested works for deeply-nested C++ containers and works as advertised") { typedef std::map info_t; typedef std::vector info_vector; class ModList { public: info_vector list; ModList() { list.push_back(info_t { { "a", "b" } }); } sol::nested getList() { return sol::nested(list); } }; sol::state lua; lua.open_libraries(sol::lib::base); lua.new_usertype("ModList", "getList", &ModList::getList); sol::string_view code = R"( mods = ModList.new() local modlist = mods:getList() print(modlist[1]) assert(type(modlist) == "table") assert(type(modlist[1]) == "table") )"; auto result1 = lua.safe_script(code, sol::script_pass_on_error); REQUIRE(result1.valid()); } TEST_CASE("containers/nested with optional", "optionals should not change the behavior of getting or setting types such as nested") { const std::vector color = { 1, 2, 3, 0 }; sol::state lua; sol::optional>> maybe_color_no = lua["color"]; lua["color"] = color; sol::optional>> maybe_color_yes = lua["color"]; REQUIRE_FALSE(maybe_color_no.has_value()); REQUIRE(maybe_color_yes.has_value()); std::vector& color_yes = maybe_color_yes.value().value(); REQUIRE(color == color_yes); }