// sol2 // The MIT License (MIT) // Copyright (c) 2013-2021 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 inline namespace sol2_test_usertype_unique { template > class checked_ptr { private: using pointer = Type*; pointer ptr; Deleter del; public: checked_ptr() noexcept : checked_ptr(nullptr) { } checked_ptr(std::nullptr_t) noexcept : ptr(nullptr), del() { } checked_ptr(pointer ptr_) noexcept : ptr(ptr_), del() { } pointer get() const noexcept { return ptr; } void reset() noexcept { if (ptr != nullptr) { del(ptr); ptr = nullptr; } } }; struct checked_class { int value = 4645; }; struct unique_user_Display { int value = 5; }; std::vector> unique_user_foo() { return { std::make_shared(), std::make_shared(), std::make_shared() }; } int unique_user_bar(std::shared_ptr item) { return item->value; } struct factory_test { private: factory_test() { a = true_a; } ~factory_test() { a = 0; } public: static int num_saved; static int num_killed; struct deleter { void operator()(factory_test* f) { f->~factory_test(); } }; static const int true_a; int a; static std::unique_ptr make() { return std::unique_ptr(new factory_test(), deleter()); } static void save(factory_test& f) { new (&f) factory_test(); ++num_saved; } static void kill(factory_test& f) { f.~factory_test(); ++num_killed; } }; int factory_test::num_saved = 0; int factory_test::num_killed = 0; const int factory_test::true_a = 156; void const_ptr_test(const std::shared_ptr ptr) { REQUIRE(*ptr.get() == 1); } } // namespace sol2_test_usertype_unique namespace sol { template <> struct unique_usertype_traits> { static checked_class* get(lua_State*, const checked_ptr& ptr) noexcept { return ptr.get(); } static bool is_null(lua_State*, const checked_ptr& ptr) noexcept { return ptr.get() == nullptr; } }; } // namespace sol void sol_lua_check_access(sol::types, lua_State* L, int index, sol::stack::record& tracking) { sol::optional&> maybe_checked = sol::stack::check_get&>(L, index, sol::no_panic, tracking); if (!maybe_checked.has_value()) { return; } checked_ptr& checked = *maybe_checked; if (checked.get() == nullptr) { // freak out in whatever way is appropriate, here throw std::runtime_error("You dun goofed"); } } TEST_CASE("usertype/unique-shared-ptr", "manage the conversion and use of unique and shared pointers ('unique usertypes')") { const int64_t unique_value = 0x7125679355635963; auto uniqueint = std::make_unique(unique_value); auto sharedint = std::make_shared(unique_value); long preusecount = sharedint.use_count(); { sol::state lua; lua.open_libraries(sol::lib::base); lua.set("uniqueint", std::move(uniqueint)); lua.set("sharedint", sharedint); std::unique_ptr& uniqueintref = lua["uniqueint"]; std::shared_ptr& sharedintref = lua["sharedint"]; int64_t* rawuniqueintref = lua["uniqueint"]; int64_t* rawsharedintref = lua["sharedint"]; int siusecount = sharedintref.use_count(); REQUIRE((uniqueintref.get() == rawuniqueintref && sharedintref.get() == rawsharedintref)); REQUIRE((uniqueintref != nullptr && sharedintref != nullptr && rawuniqueintref != nullptr && rawsharedintref != nullptr)); REQUIRE((unique_value == *uniqueintref.get() && unique_value == *sharedintref.get())); REQUIRE((unique_value == *rawuniqueintref && unique_value == *rawsharedintref)); REQUIRE(siusecount == sharedint.use_count()); std::shared_ptr moreref = sharedint; REQUIRE(unique_value == *moreref.get()); REQUIRE(moreref.use_count() == sharedint.use_count()); REQUIRE(moreref.use_count() == sharedintref.use_count()); } REQUIRE(preusecount == sharedint.use_count()); std::cout << "----- end of 2" << std::endl; } TEST_CASE("usertype/private-constructible", "Check to make sure special snowflake types from Enterprise thingamahjongs work properly.") { int numsaved = factory_test::num_saved; int numkilled = factory_test::num_killed; { sol::state lua; lua.open_libraries(sol::lib::base); lua.new_usertype( "factory_test", "new", sol::initializers(factory_test::save), "__gc", sol::destructor(factory_test::kill), "a", &factory_test::a); std::unique_ptr f = factory_test::make(); lua.set("true_a", factory_test::true_a, "f", f.get()); { auto result = lua.safe_script("assert(f.a == true_a)", sol::script_pass_on_error); REQUIRE(result.valid()); } auto code1 = "local fresh_f = factory_test:new()\n" "assert(fresh_f.a == true_a)\n"; auto result1 = lua.safe_script(code1, sol::script_pass_on_error); REQUIRE(result1.valid()); } int expectednumsaved = numsaved + 1; int expectednumkilled = numkilled + 1; REQUIRE(expectednumsaved == factory_test::num_saved); REQUIRE(expectednumkilled == factory_test::num_killed); std::cout << "----- end of 5" << std::endl; } TEST_CASE("usertype/unique_usertype-check", "make sure unique usertypes don't get pushed as references with function calls and the like") { class Entity { public: std::string GetName() { return "Charmander"; } }; sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::math, sol::lib::string, sol::lib::io); lua.new_usertype("Entity", "new", sol::no_constructor, "get_name", &Entity::GetName); lua.safe_script(R"( function my_func(entity) print("INSIDE LUA") print(entity:get_name()) end )"); sol::function my_func = lua["my_func"]; REQUIRE_NOTHROW([&] { auto ent = std::make_shared(); my_func(ent); Entity ent2; my_func(ent2); my_func(std::make_shared()); }()); } TEST_CASE("usertype/unique void pointers", "can compile shared_ptr types and not trip the compiler or sol2's internals") { sol::state lua; lua.set_function("f", [](std::shared_ptr d) { int* pi = static_cast(d.get()); REQUIRE(*pi == 567); }); std::shared_ptr s = std::make_shared(567); lua["s"] = std::move(s); auto result = lua.safe_script("f(s)", sol::script_pass_on_error); REQUIRE(result.valid()); } TEST_CASE("usertype/unique containers", "copyable unique usertypes in containers are just fine and do not deref/decay") { sol::state lua; lua.open_libraries(); lua["foo"] = unique_user_foo; lua["bar"] = unique_user_bar; sol::optional err0 = lua.safe_script("v3 = foo()"); REQUIRE_FALSE(err0.has_value()); sol::optional err1 = lua.safe_script("assert(bar(v3[1]) == 5)"); REQUIRE_FALSE(err1.has_value()); } TEST_CASE("usertype/unique_usertype checks", "Ensure that access to usertypes can be checked") { sol::state lua; lua["c"] = checked_ptr(new checked_class()); lua["c_nil"] = checked_ptr(); lua["f"] = [](checked_class cc) { std::cout << cc.value << std::endl; }; sol::optional maybe_error = lua.safe_script("f(c)", sol::script_pass_on_error); REQUIRE_FALSE(maybe_error.has_value()); checked_ptr& c = lua["c"]; c.reset(); sol::optional should_error = lua.safe_script("f(c)", sol::script_pass_on_error); REQUIRE(should_error.has_value()); } TEST_CASE("usertype/unique_usertype const", "make sure const-qualified values don't trip the wrong template specializations") { sol::state lua; lua.set_function("f", &const_ptr_test); sol::protected_function lua_f = lua["f"]; const std::shared_ptr ptr = std::make_shared(1); lua_f(ptr); }