diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index dac37943..22c783c8 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-04-19 19:28:54.081352 UTC -// This header was generated with sol v2.17.1 (revision a1e3dab) +// Generated 2017-04-21 00:18:17.116630 UTC +// This header was generated with sol v2.17.1 (revision 3d5f80e) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -10493,15 +10493,50 @@ namespace sol { typedef int(*member_search)(lua_State*, void*, int); struct call_information { - member_search first; - member_search second; + member_search index; + member_search new_index; int runtime_target; - call_information(member_search first, member_search second) : call_information(first, second, -1) {} - call_information(member_search first, member_search second, int runtimetarget) : first(first), second(second), runtime_target(runtimetarget) {} + call_information(member_search index, member_search newindex) : call_information(index, newindex, -1) {} + call_information(member_search index, member_search newindex, int runtimetarget) : index(index), new_index(newindex), runtime_target(runtimetarget) {} }; typedef std::unordered_map mapping_t; + + struct variable_wrapper { + virtual int index(lua_State* L) = 0; + virtual int new_index(lua_State* L) = 0; + virtual ~variable_wrapper() {}; + }; + + template + struct callable_binding : variable_wrapper { + F fx; + + template + callable_binding(Arg&& arg) : fx(std::forward(arg)) {} + + virtual int index(lua_State* L) override { + return call_detail::call_wrapped(L, fx); + } + + virtual int new_index(lua_State* L) override { + return call_detail::call_wrapped(L, fx); + } + }; + + typedef std::unordered_map> variable_map; + typedef std::unordered_map function_map; + + struct simple_map { + const char* metakey; + variable_map variables; + function_map functions; + base_walk indexbaseclasspropogation; + base_walk newindexbaseclasspropogation; + + simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} + }; } struct usertype_metatable_core { @@ -10626,7 +10661,8 @@ namespace sol { inline int runtime_object_call(lua_State* L, void*, int runtimetarget) { usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); std::vector& runtime = umc.runtime; - return stack::push(L, runtime[runtimetarget]); + object& runtimeobj = runtime[runtimetarget]; + return stack::push(L, runtimeobj); } template @@ -10657,34 +10693,55 @@ namespace sol { } } + int runtime_new_index(lua_State* L, void*, int runtimetarget); + template inline int metatable_newindex(lua_State* L) { int isnum = 0; lua_Integer magic = lua_tointegerx(L, upvalue_index(4), &isnum); if (isnum != 0 && magic == toplevel_magic) { - auto non_simple = [&L]() { - if (is_simple) + auto non_indexable = [&L]() { + if (is_simple) { + simple_map& sm = stack::get>(L, upvalue_index(1)); + function_map& functions = sm.functions; + sol::optional maybeaccessor = stack::get>(L, 2); + if (!maybeaccessor) { + return; + } + std::string& accessor = maybeaccessor.value(); + auto preexistingit = functions.find(accessor); + if (preexistingit == functions.cend()) { + functions.emplace_hint(preexistingit, std::move(accessor), sol::object(L, 3)); + } + else { + preexistingit->second = sol::object(L, 3); + } return; + } usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); bool mustindex = umc.mustindex; if (!mustindex) return; - std::string accessor = stack::get(L, 2); + sol::optional maybeaccessor = stack::get>(L, 2); + if (!maybeaccessor) { + return; + } + std::string& accessor = maybeaccessor.value(); mapping_t& mapping = umc.mapping; std::vector& runtime = umc.runtime; int target = static_cast(runtime.size()); auto preexistingit = mapping.find(accessor); if (preexistingit == mapping.cend()) { runtime.emplace_back(L, 3); - mapping.emplace_hint(mapping.cend(), accessor, call_information(&runtime_object_call, &runtime_object_call, target)); + mapping.emplace_hint(mapping.cend(), accessor, call_information(&runtime_object_call, &runtime_new_index, target)); } else { target = preexistingit->second.runtime_target; runtime[target] = sol::object(L, 3); - preexistingit->second = call_information(&runtime_object_call, &runtime_object_call, target); + preexistingit->second = call_information(&runtime_object_call, &runtime_new_index, target); } }; - non_simple(); + non_indexable(); for (std::size_t i = 0; i < 4; lua_settop(L, 3), ++i) { const char* metakey = nullptr; switch (i) { @@ -10720,6 +10777,14 @@ namespace sol { } return indexing_fail(L); } + + inline int runtime_new_index(lua_State* L, void*, int runtimetarget) { + usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + std::vector& runtime = umc.runtime; + object& runtimeobj = runtime[runtimetarget]; + runtimeobj = object(L, 3); + return 0; + } template static void walk_single_base(lua_State* L, bool& found, int& ret, string_detail::string_shim&) { @@ -10876,6 +10941,7 @@ namespace sol { template void make_regs(regs_t&, int&, base_classes_tag, bases) { + static_assert(!meta::any_same::value, "base classes cannot list the original class as part of the bases"); if (sizeof...(Bases) < 1) { return; } @@ -10937,8 +11003,8 @@ namespace sol { hasequals(false), hasless(false), haslessequals(false) { std::initializer_list ilist{ { std::pair( usertype_detail::make_string(std::get(functions)), - usertype_detail::call_information(&usertype_metatable::real_find_call, - &usertype_metatable::real_find_call) + usertype_detail::call_information(&usertype_metatable::real_find_call, + &usertype_metatable::real_find_call) ) }... }; this->mapping.insert(ilist); @@ -10981,7 +11047,7 @@ namespace sol { auto memberit = f.mapping.find(name); if (memberit != f.mapping.cend()) { const usertype_detail::call_information& ci = memberit->second; - const usertype_detail::member_search& member = is_index ? ci.second : ci.first; + const usertype_detail::member_search& member = is_index ? ci.index: ci.new_index; return (member)(L, static_cast(&f), ci.runtime_target); } string_detail::string_shim accessor = name; @@ -11200,41 +11266,6 @@ namespace sol { namespace sol { namespace usertype_detail { - struct variable_wrapper { - virtual int index(lua_State* L) = 0; - virtual int new_index(lua_State* L) = 0; - virtual ~variable_wrapper() {}; - }; - - template - struct callable_binding : variable_wrapper { - F fx; - - template - callable_binding(Arg&& arg) : fx(std::forward(arg)) {} - - virtual int index(lua_State* L) override { - return call_detail::call_wrapped(L, fx); - } - - virtual int new_index(lua_State* L) override { - return call_detail::call_wrapped(L, fx); - } - }; - - typedef std::unordered_map> variable_map; - typedef std::unordered_map function_map; - - struct simple_map { - const char* metakey; - variable_map variables; - function_map functions; - base_walk indexbaseclasspropogation; - base_walk newindexbaseclasspropogation; - - simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} - }; - template inline int simple_core_indexing_call(lua_State* L) { simple_map& sm = toplevel ? stack::get>(L, upvalue_index(1)) : stack::pop>(L); @@ -11259,8 +11290,14 @@ namespace sol { } auto fit = functions.find(accessorkey); if (fit != functions.cend()) { - auto& func = (fit->second); - return stack::push(L, func); + sol::object& func = fit->second; + if (is_index) { + return stack::push(L, func); + } + else { + lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); + return indexingfunc(L); + } } // Check table storage first for a method that works luaL_getmetatable(L, sm.metakey); @@ -11440,6 +11477,7 @@ namespace sol { template void add(lua_State*, base_classes_tag, bases) { static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "size of function pointer is greater than sizeof(void*); cannot work on this platform. Please file a bug report."); + static_assert(!meta::any_same::value, "base classes cannot list the original class as part of the bases"); if (sizeof...(Bases) < 1) { return; } diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index d248f6a9..22c4740f 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -31,41 +31,6 @@ namespace sol { namespace usertype_detail { - struct variable_wrapper { - virtual int index(lua_State* L) = 0; - virtual int new_index(lua_State* L) = 0; - virtual ~variable_wrapper() {}; - }; - - template - struct callable_binding : variable_wrapper { - F fx; - - template - callable_binding(Arg&& arg) : fx(std::forward(arg)) {} - - virtual int index(lua_State* L) override { - return call_detail::call_wrapped(L, fx); - } - - virtual int new_index(lua_State* L) override { - return call_detail::call_wrapped(L, fx); - } - }; - - typedef std::unordered_map> variable_map; - typedef std::unordered_map function_map; - - struct simple_map { - const char* metakey; - variable_map variables; - function_map functions; - base_walk indexbaseclasspropogation; - base_walk newindexbaseclasspropogation; - - simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} - }; - template inline int simple_core_indexing_call(lua_State* L) { simple_map& sm = toplevel ? stack::get>(L, upvalue_index(1)) : stack::pop>(L); @@ -90,8 +55,14 @@ namespace sol { } auto fit = functions.find(accessorkey); if (fit != functions.cend()) { - auto& func = (fit->second); - return stack::push(L, func); + sol::object& func = fit->second; + if (is_index) { + return stack::push(L, func); + } + else { + lua_CFunction indexingfunc = is_index ? stack::get(L, upvalue_index(2)) : stack::get(L, upvalue_index(3)); + return indexingfunc(L); + } } // Check table storage first for a method that works luaL_getmetatable(L, sm.metakey); @@ -271,6 +242,7 @@ namespace sol { template void add(lua_State*, base_classes_tag, bases) { static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "size of function pointer is greater than sizeof(void*); cannot work on this platform. Please file a bug report."); + static_assert(!meta::any_same::value, "base classes cannot list the original class as part of the bases"); if (sizeof...(Bases) < 1) { return; } diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index edb92e2b..381c81bf 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -40,15 +40,50 @@ namespace sol { typedef int(*member_search)(lua_State*, void*, int); struct call_information { - member_search first; - member_search second; + member_search index; + member_search new_index; int runtime_target; - call_information(member_search first, member_search second) : call_information(first, second, -1) {} - call_information(member_search first, member_search second, int runtimetarget) : first(first), second(second), runtime_target(runtimetarget) {} + call_information(member_search index, member_search newindex) : call_information(index, newindex, -1) {} + call_information(member_search index, member_search newindex, int runtimetarget) : index(index), new_index(newindex), runtime_target(runtimetarget) {} }; typedef std::unordered_map mapping_t; + + struct variable_wrapper { + virtual int index(lua_State* L) = 0; + virtual int new_index(lua_State* L) = 0; + virtual ~variable_wrapper() {}; + }; + + template + struct callable_binding : variable_wrapper { + F fx; + + template + callable_binding(Arg&& arg) : fx(std::forward(arg)) {} + + virtual int index(lua_State* L) override { + return call_detail::call_wrapped(L, fx); + } + + virtual int new_index(lua_State* L) override { + return call_detail::call_wrapped(L, fx); + } + }; + + typedef std::unordered_map> variable_map; + typedef std::unordered_map function_map; + + struct simple_map { + const char* metakey; + variable_map variables; + function_map functions; + base_walk indexbaseclasspropogation; + base_walk newindexbaseclasspropogation; + + simple_map(const char* mkey, base_walk index, base_walk newindex, variable_map&& vars, function_map&& funcs) : metakey(mkey), variables(std::move(vars)), functions(std::move(funcs)), indexbaseclasspropogation(index), newindexbaseclasspropogation(newindex) {} + }; } struct usertype_metatable_core { @@ -173,7 +208,8 @@ namespace sol { inline int runtime_object_call(lua_State* L, void*, int runtimetarget) { usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); std::vector& runtime = umc.runtime; - return stack::push(L, runtime[runtimetarget]); + object& runtimeobj = runtime[runtimetarget]; + return stack::push(L, runtimeobj); } template @@ -204,34 +240,55 @@ namespace sol { } } + int runtime_new_index(lua_State* L, void*, int runtimetarget); + template inline int metatable_newindex(lua_State* L) { int isnum = 0; lua_Integer magic = lua_tointegerx(L, upvalue_index(4), &isnum); if (isnum != 0 && magic == toplevel_magic) { - auto non_simple = [&L]() { - if (is_simple) + auto non_indexable = [&L]() { + if (is_simple) { + simple_map& sm = stack::get>(L, upvalue_index(1)); + function_map& functions = sm.functions; + sol::optional maybeaccessor = stack::get>(L, 2); + if (!maybeaccessor) { + return; + } + std::string& accessor = maybeaccessor.value(); + auto preexistingit = functions.find(accessor); + if (preexistingit == functions.cend()) { + functions.emplace_hint(preexistingit, std::move(accessor), sol::object(L, 3)); + } + else { + preexistingit->second = sol::object(L, 3); + } return; + } usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); bool mustindex = umc.mustindex; if (!mustindex) return; - std::string accessor = stack::get(L, 2); + sol::optional maybeaccessor = stack::get>(L, 2); + if (!maybeaccessor) { + return; + } + std::string& accessor = maybeaccessor.value(); mapping_t& mapping = umc.mapping; std::vector& runtime = umc.runtime; int target = static_cast(runtime.size()); auto preexistingit = mapping.find(accessor); if (preexistingit == mapping.cend()) { runtime.emplace_back(L, 3); - mapping.emplace_hint(mapping.cend(), accessor, call_information(&runtime_object_call, &runtime_object_call, target)); + mapping.emplace_hint(mapping.cend(), accessor, call_information(&runtime_object_call, &runtime_new_index, target)); } else { target = preexistingit->second.runtime_target; runtime[target] = sol::object(L, 3); - preexistingit->second = call_information(&runtime_object_call, &runtime_object_call, target); + preexistingit->second = call_information(&runtime_object_call, &runtime_new_index, target); } }; - non_simple(); + non_indexable(); for (std::size_t i = 0; i < 4; lua_settop(L, 3), ++i) { const char* metakey = nullptr; switch (i) { @@ -267,6 +324,14 @@ namespace sol { } return indexing_fail(L); } + + inline int runtime_new_index(lua_State* L, void*, int runtimetarget) { + usertype_metatable_core& umc = stack::get>(L, upvalue_index(2)); + std::vector& runtime = umc.runtime; + object& runtimeobj = runtime[runtimetarget]; + runtimeobj = object(L, 3); + return 0; + } template static void walk_single_base(lua_State* L, bool& found, int& ret, string_detail::string_shim&) { @@ -423,6 +488,7 @@ namespace sol { template void make_regs(regs_t&, int&, base_classes_tag, bases) { + static_assert(!meta::any_same::value, "base classes cannot list the original class as part of the bases"); if (sizeof...(Bases) < 1) { return; } @@ -484,8 +550,8 @@ namespace sol { hasequals(false), hasless(false), haslessequals(false) { std::initializer_list ilist{ { std::pair( usertype_detail::make_string(std::get(functions)), - usertype_detail::call_information(&usertype_metatable::real_find_call, - &usertype_metatable::real_find_call) + usertype_detail::call_information(&usertype_metatable::real_find_call, + &usertype_metatable::real_find_call) ) }... }; this->mapping.insert(ilist); @@ -528,7 +594,7 @@ namespace sol { auto memberit = f.mapping.find(name); if (memberit != f.mapping.cend()) { const usertype_detail::call_information& ci = memberit->second; - const usertype_detail::member_search& member = is_index ? ci.second : ci.first; + const usertype_detail::member_search& member = is_index ? ci.index: ci.new_index; return (member)(L, static_cast(&f), ci.runtime_target); } string_detail::string_shim accessor = name; diff --git a/test_simple_usertypes.cpp b/test_simple_usertypes.cpp index 9b70dfa7..d08a3575 100644 --- a/test_simple_usertypes.cpp +++ b/test_simple_usertypes.cpp @@ -698,27 +698,81 @@ end } TEST_CASE("simple_usertype/runtime-replacement", "ensure that functions can be properly replaced at runtime for non-indexed things") { - struct heart_t {}; + struct heart_base_t {}; + struct heart_t : heart_base_t { + void func() {} + }; - sol::state lua; - lua.open_libraries(sol::lib::base); + SECTION("plain") { + sol::state lua; + lua.open_libraries(sol::lib::base); - lua.new_usertype("a"); - REQUIRE_NOTHROW([&lua]() { - lua.script("obj = a.new()"); - lua.script("function a:heartbeat () print('arf') return 1 end"); - lua.script("v1 = obj:heartbeat()"); - lua.script("function a:heartbeat () print('bark') return 2 end"); - lua.script("v2 = obj:heartbeat()"); - lua.script("a.heartbeat = function(self) print('woof') return 3 end"); - lua.script("v3 = obj:heartbeat()"); - }()); - int v1 = lua["v1"]; - int v2 = lua["v2"]; - int v3 = lua["v3"]; - REQUIRE(v1 == 1); - REQUIRE(v2 == 2); - REQUIRE(v3 == 3); + lua.new_simple_usertype("a"); + REQUIRE_NOTHROW([&lua]() { + lua.script("obj = a.new()"); + lua.script("function a:heartbeat () print('arf') return 1 end"); + lua.script("v1 = obj:heartbeat()"); + lua.script("function a:heartbeat () print('bark') return 2 end"); + lua.script("v2 = obj:heartbeat()"); + lua.script("a.heartbeat = function(self) print('woof') return 3 end"); + lua.script("v3 = obj:heartbeat()"); + }()); + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + REQUIRE(v1 == 1); + REQUIRE(v2 == 2); + REQUIRE(v3 == 3); + } + SECTION("variables") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_simple_usertype("a", + sol::base_classes, sol::bases() + ); + + REQUIRE_NOTHROW([&lua]() { + lua.script("obj = a.new()"); + lua.script("function a:heartbeat () print('arf') return 1 end"); + lua.script("v1 = obj:heartbeat()"); + lua.script("function a:heartbeat () print('bark') return 2 end"); + lua.script("v2 = obj:heartbeat()"); + lua.script("a.heartbeat = function(self) print('woof') return 3 end"); + lua.script("v3 = obj:heartbeat()"); + }()); + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + REQUIRE(v1 == 1); + REQUIRE(v2 == 2); + REQUIRE(v3 == 3); + } + SECTION("methods") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_simple_usertype("a", + "func", &heart_t::func, + sol::base_classes, sol::bases() + ); + + REQUIRE_NOTHROW([&lua]() { + lua.script("obj = a.new()"); + lua.script("function a:heartbeat () print('arf') return 1 end"); + lua.script("v1 = obj:heartbeat()"); + lua.script("function a:heartbeat () print('bark') return 2 end"); + lua.script("v2 = obj:heartbeat()"); + lua.script("a.heartbeat = function(self) print('woof') return 3 end"); + lua.script("v3 = obj:heartbeat()"); + }()); + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + REQUIRE(v1 == 1); + REQUIRE(v2 == 2); + REQUIRE(v3 == 3); + } } TEST_CASE("simple_usertype/meta-key-retrievals", "allow for special meta keys (__index, __newindex) to trigger methods even if overwritten directly") { diff --git a/test_usertypes.cpp b/test_usertypes.cpp index 67bdec40..f0ce1129 100644 --- a/test_usertypes.cpp +++ b/test_usertypes.cpp @@ -1623,27 +1623,81 @@ end } TEST_CASE("usertype/runtime-replacement", "ensure that functions can be properly replaced at runtime for non-indexed things") { - struct heart_t {}; + struct heart_base_t {}; + struct heart_t : heart_base_t { + void func() {} + }; - sol::state lua; - lua.open_libraries(sol::lib::base); + SECTION("plain") { + sol::state lua; + lua.open_libraries(sol::lib::base); - lua.new_usertype("a"); - REQUIRE_NOTHROW([&lua]() { - lua.script("obj = a.new()"); - lua.script("function a:heartbeat () print('arf') return 1 end"); - lua.script("v1 = obj:heartbeat()"); - lua.script("function a:heartbeat () print('bark') return 2 end"); - lua.script("v2 = obj:heartbeat()"); - lua.script("a.heartbeat = function(self) print('woof') return 3 end"); - lua.script("v3 = obj:heartbeat()"); - }()); - int v1 = lua["v1"]; - int v2 = lua["v2"]; - int v3 = lua["v3"]; - REQUIRE(v1 == 1); - REQUIRE(v2 == 2); - REQUIRE(v3 == 3); + lua.new_usertype("a"); + REQUIRE_NOTHROW([&lua]() { + lua.script("obj = a.new()"); + lua.script("function a:heartbeat () print('arf') return 1 end"); + lua.script("v1 = obj:heartbeat()"); + lua.script("function a:heartbeat () print('bark') return 2 end"); + lua.script("v2 = obj:heartbeat()"); + lua.script("a.heartbeat = function(self) print('woof') return 3 end"); + lua.script("v3 = obj:heartbeat()"); + }()); + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + REQUIRE(v1 == 1); + REQUIRE(v2 == 2); + REQUIRE(v3 == 3); + } + SECTION("variables") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("a", + sol::base_classes, sol::bases() + ); + + REQUIRE_NOTHROW([&lua]() { + lua.script("obj = a.new()"); + lua.script("function a:heartbeat () print('arf') return 1 end"); + lua.script("v1 = obj:heartbeat()"); + lua.script("function a:heartbeat () print('bark') return 2 end"); + lua.script("v2 = obj:heartbeat()"); + lua.script("a.heartbeat = function(self) print('woof') return 3 end"); + lua.script("v3 = obj:heartbeat()"); + }()); + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + REQUIRE(v1 == 1); + REQUIRE(v2 == 2); + REQUIRE(v3 == 3); + } + SECTION("methods") { + sol::state lua; + lua.open_libraries(sol::lib::base); + + lua.new_usertype("a", + "func", &heart_t::func, + sol::base_classes, sol::bases() + ); + + REQUIRE_NOTHROW([&lua]() { + lua.script("obj = a.new()"); + lua.script("function a:heartbeat () print('arf') return 1 end"); + lua.script("v1 = obj:heartbeat()"); + lua.script("function a:heartbeat () print('bark') return 2 end"); + lua.script("v2 = obj:heartbeat()"); + lua.script("a.heartbeat = function(self) print('woof') return 3 end"); + lua.script("v3 = obj:heartbeat()"); + }()); + int v1 = lua["v1"]; + int v2 = lua["v2"]; + int v3 = lua["v3"]; + REQUIRE(v1 == 1); + REQUIRE(v2 == 2); + REQUIRE(v3 == 3); + } } TEST_CASE("usertype/meta-key-retrievals", "allow for special meta keys (__index, __newindex) to trigger methods even if overwritten directly") {