diff --git a/docs/source/tutorial/all-the-things.rst b/docs/source/tutorial/all-the-things.rst index ad248a04..64aa8676 100644 --- a/docs/source/tutorial/all-the-things.rst +++ b/docs/source/tutorial/all-the-things.rst @@ -43,13 +43,21 @@ running lua code // load and execute from file lua.script_file("path/to/luascript.lua"); + // run a script, get the result + int value = lua.script("return 54"); + // value == 54 + // load file without execute - sol::load_result script1 = state.load_file("path/to/luascript.lua"); + sol::load_result script1 = lua.load_file("path/to/luascript.lua"); script1(); //execute // load string without execute - sol::load_result script2 = state.load("a = 'test'"); + sol::load_result script2 = lua.load("a = 'test'"); script2(); //execute + sol::load_result script3 = lua.load("return 24"); + int value2 = script3(); // execute, get return value + // value2 == 24 + set and get variables --------------------- @@ -347,6 +355,40 @@ The lua code to call these things is: Can use ``sol::readonly( &some_class::variable )`` to make a variable readonly and error if someone tries to write to it. +self call +--------- + +You can pass the 'self' argument through C++ to emulate 'member function' calls in Lua. + +.. code-block:: cpp + + sol::state lua; + + lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::table); + + // a small script using 'self' syntax + lua.script(R"( + some_table = { some_val = 100 } + + function some_table:add_to_some_val(value) + self.some_val = self.some_val + value + end + + function print_some_val() + print("some_table.some_val = " .. some_table.some_val) + end + )"); + + // do some printing + lua["print_some_val"](); + // 100 + + sol::table self = lua["some_table"]; + self["add_to_some_val"](self, 10); + lua["print_some_val"](); + + + multiple returns from lua ------------------------- diff --git a/examples/require.cpp b/examples/require.cpp new file mode 100644 index 00000000..a94765b9 --- /dev/null +++ b/examples/require.cpp @@ -0,0 +1,47 @@ +#define SOL_CHECK_ARGUMENTS +#include +#include + +#include + +struct some_class { + int bark = 2012; +}; + +sol::table open_mylib(sol::this_state s) { + sol::state_view lua(s); + + sol::table module = lua.create_table(); + module["func"] = []() { /* super cool function here */ }; + // register a class too + module.new_usertype("some_class", + "bark", &some_class::bark + ); + + return module; +} + +int main() { + std::cout << "=== require example ===" << std::endl; + + sol::state lua; + lua.open_libraries(sol::lib::package); + // sol::c_call takes functions at the template level + // and turns it into a lua_CFunction + // alternatively, one can use sol::c_call> to make the call + // if it's a constexpr struct + lua.require("my_lib", sol::c_call); + + // do ALL THE THINGS YOU LIKE + lua.script(R"( +s = my_lib.some_class.new() +s.bark = 20; +)"); + some_class& s = lua["s"]; + assert(s.bark == 20); + std::cout << "s.bark = " << s.bark << std::endl; + + std::cout << std::endl; + + return 0; +} \ No newline at end of file diff --git a/examples/self_call.cpp b/examples/self_call.cpp new file mode 100644 index 00000000..c64ded34 --- /dev/null +++ b/examples/self_call.cpp @@ -0,0 +1,35 @@ +#define SOL_CHECK_ARGUMENTS +#include +#include +#include + +int main() { + std::cout << "=== self_call example ===" << std::endl; + + sol::state lua; + + lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::table); + + // a small script using 'self' syntax + lua.script(R"( + some_table = { some_val = 100 } + + function some_table:add_to_some_val(value) + self.some_val = self.some_val + value + end + + function print_some_val() + print("some_table.some_val = " .. some_table.some_val) + end + )"); + + // do some printing + lua["print_some_val"](); + // 100 + + sol::table self = lua["some_table"]; + self["add_to_some_val"](self, 10); + lua["print_some_val"](); + + std::cout << std::endl; +} \ No newline at end of file diff --git a/sol/inheritance.hpp b/sol/inheritance.hpp index 3910ee92..f2583495 100644 --- a/sol/inheritance.hpp +++ b/sol/inheritance.hpp @@ -58,12 +58,12 @@ namespace sol { const std::size_t id_for::value = unique_id(); inline decltype(auto) base_class_check_key() { - static const auto& key = u8"♡o。.(✿ฺ。 ✿ฺ)"; + static const auto& key = "class_check"; return key; } inline decltype(auto) base_class_cast_key() { - static const auto& key = u8"(◕‿◕✿)"; + static const auto& key = "class_cast"; return key; } @@ -85,11 +85,11 @@ namespace sol { template static bool type_check_bases(types, std::size_t ti) { - return ti != id_for::value || type_check_bases(types(), ti); + return ti == id_for::value || type_check_bases(types(), ti); } static bool type_check(std::size_t ti) { - return ti != id_for::value || type_check_bases(types(), ti); + return ti == id_for::value || type_check_bases(types(), ti); } static void* type_cast_bases(types<>, T*, std::size_t) { diff --git a/sol/simple_usertype_metatable.hpp b/sol/simple_usertype_metatable.hpp index d5add85d..f43751da 100644 --- a/sol/simple_usertype_metatable.hpp +++ b/sol/simple_usertype_metatable.hpp @@ -161,18 +161,30 @@ namespace sol { bool mustindex; bool secondarymeta; + template + void insert(N&& n, object&& o) { + std::string key = usertype_detail::make_string(std::forward(n)); + auto hint = registrations.find(key); + if (hint == registrations.cend()) { + registrations.emplace_hint(hint, std::move(key), std::move(o)); + return; + } + hint->second = std::move(o); + } + template >> = meta::enabler> void add_function(lua_State* L, N&& n, F&& f) { - registrations.emplace(usertype_detail::make_string(std::forward(n)), make_object(L, as_function_reference(std::forward(f)))); + insert(std::forward(n), make_object(L, as_function_reference(std::forward(f)))); } template >> = meta::enabler> void add_function(lua_State* L, N&& n, F&& f) { + object o = make_object(L, std::forward(f)); if (std::is_same, call_construction>::value) { - callconstructfunc = make_object(L, std::forward(f)); + callconstructfunc = std::move(o); return; } - registrations.emplace(usertype_detail::make_string(std::forward(n)), make_object(L, std::forward(f))); + insert(std::forward(n), std::move(o)); } template >> = meta::enabler> @@ -182,9 +194,16 @@ namespace sol { template >> = meta::enabler> void add(lua_State*, N&& n, F&& f) { - varmap.emplace(usertype_detail::make_string(std::forward(n)), std::make_unique>>(std::forward(f))); mustindex = true; secondarymeta = true; + std::string key = usertype_detail::make_string(std::forward(n)); + auto o = std::make_unique>>(std::forward(f)); + auto hint = varmap.find(key); + if (hint == varmap.cend()) { + varmap.emplace_hint(hint, std::move(key), std::move(o)); + return; + } + hint->second = std::move(o); } template @@ -194,7 +213,7 @@ namespace sol { callconstructfunc = std::move(o); return; } - registrations.emplace(usertype_detail::make_string(std::forward(n)), std::move(o)); + insert(std::forward(n), std::move(o)); } template @@ -204,7 +223,27 @@ namespace sol { callconstructfunc = std::move(o); return; } - registrations.emplace(usertype_detail::make_string(std::forward(n)), std::move(o)); + insert(std::forward(n), std::move(o)); + } + + template + void add(lua_State* L, N&& n, destructor_wrapper c) { + object o(L, in_place>>, std::move(c)); + if (std::is_same, call_construction>::value) { + callconstructfunc = std::move(o); + return; + } + insert(std::forward(n), std::move(o)); + } + + template + void add(lua_State* L, N&& n, destructor_wrapper c) { + object o(L, in_place>>, std::move(c)); + if (std::is_same, call_construction>::value) { + callconstructfunc = std::move(o); + return; + } + insert(std::forward(n), std::move(o)); } template @@ -224,20 +263,20 @@ namespace sol { newindexbaseclasspropogation = usertype_detail::walk_all_bases; } + private: template simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence, lua_State* L, Tuple&& args) - : callconstructfunc(nil), - indexfunc(&usertype_detail::indexing_fail), newindexfunc(&usertype_detail::indexing_fail), - indexbase(&usertype_detail::simple_core_indexing_call), newindexbase(&usertype_detail::simple_core_indexing_call), - indexbaseclasspropogation(usertype_detail::walk_all_bases), newindexbaseclasspropogation(&usertype_detail::walk_all_bases), - baseclasscheck(nullptr), baseclasscast(nullptr), - mustindex(false), secondarymeta(false) { + : callconstructfunc(nil), + indexfunc(&usertype_detail::indexing_fail), newindexfunc(&usertype_detail::indexing_fail), + indexbase(&usertype_detail::simple_core_indexing_call), newindexbase(&usertype_detail::simple_core_indexing_call), + indexbaseclasspropogation(usertype_detail::walk_all_bases), newindexbaseclasspropogation(&usertype_detail::walk_all_bases), + baseclasscheck(nullptr), baseclasscast(nullptr), + mustindex(false), secondarymeta(false) { (void)detail::swallow{ 0, (add(L, detail::forward_get(args), detail::forward_get(args)),0)... }; } - private: template simple_usertype_metatable(lua_State* L, usertype_detail::verified_tag v, Args&&... args) : simple_usertype_metatable(v, std::make_index_sequence(), L, std::forward_as_tuple(std::forward(args)...)) {} diff --git a/sol/stack_check.hpp b/sol/stack_check.hpp index 0b2b277e..b4b977cc 100644 --- a/sol/stack_check.hpp +++ b/sol/stack_check.hpp @@ -262,16 +262,18 @@ namespace sol { if (lua_getmetatable(L, index) == 0) { return true; } - if (stack_detail::check_metatable(L)) + int metatableindex = lua_gettop(L); + if (stack_detail::check_metatable(L, metatableindex)) return true; - if (stack_detail::check_metatable(L)) + if (stack_detail::check_metatable(L, metatableindex)) return true; - if (stack_detail::check_metatable>(L)) + if (stack_detail::check_metatable>(L, metatableindex)) return true; bool success = false; if (detail::has_derived::value) { auto pn = stack::pop_n(L, 1); - lua_getfield(L, -1, &detail::base_class_check_key()[0]); + lua_pushstring(L, &detail::base_class_check_key()[0]); + lua_rawget(L, metatableindex); if (type_of(L, -1) != type::nil) { void* basecastdata = lua_touserdata(L, -1); detail::inheritance_check_function ic = (detail::inheritance_check_function)basecastdata; diff --git a/sol/usertype.hpp b/sol/usertype.hpp index c841184b..c4d9e905 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -32,7 +32,7 @@ namespace sol { template class usertype { - protected: + private: std::unique_ptr metatableregister; template @@ -58,6 +58,10 @@ namespace sol { template usertype(simple_tag, lua_State* L, Args&&... args) : metatableregister(detail::make_unique_deleter, detail::deleter>(L, std::forward(args)...)) {} + usertype_detail::registrar* registrar_data() { + return metatableregister.get(); + } + int push(lua_State* L) { return metatableregister->push_um(L); } @@ -65,16 +69,17 @@ namespace sol { template class simple_usertype : public usertype { - protected: + private: + typedef usertype base_t; lua_State* state; public: template - simple_usertype(lua_State* L, Args&&... args) : state(L), usertype(simple, L, std::forward(args)...) {} - - template - void add(N&& n, F&& f) { - auto meta = (simple_usertype_metatable*) metatableregister.get(); + simple_usertype(lua_State* L, Args&&... args) : base_t(simple, L, std::forward(args)...), state(L) {} + + template + void set(N&& n, F&& f) { + auto meta = static_cast*>(base_t::registrar_data()); meta->add(state, n, f); } }; diff --git a/sol/usertype_metatable.hpp b/sol/usertype_metatable.hpp index c978aa51..e26e3663 100644 --- a/sol/usertype_metatable.hpp +++ b/sol/usertype_metatable.hpp @@ -107,13 +107,13 @@ namespace sol { auto maybeaccessor = stack::get>(L, is_index ? -1 : -2); string_detail::string_shim accessor = maybeaccessor.value_or(string_detail::string_shim("(unknown)")); if (is_index) - return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data()); + return luaL_error(L, "sol: attempt to index (get) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str()); else - return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.data()); + return luaL_error(L, "sol: attempt to index (set) nil value \"%s\" on userdata (bad (misspelled?) key name or does not exist)", accessor.c_str()); } template - static void walk_single_base(lua_State* L, bool& found, int& ret, string_detail::string_shim& accessor) { + static void walk_single_base(lua_State* L, bool& found, int& ret, string_detail::string_shim&) { if (found) return; const char* metakey = &usertype_traits::metatable[0]; diff --git a/test_inheritance.cpp b/test_inheritance.cpp new file mode 100644 index 00000000..5afb363b --- /dev/null +++ b/test_inheritance.cpp @@ -0,0 +1,247 @@ +#define SOL_CHECK_ARGUMENTS + +#include +#include + +#include + +TEST_CASE("inheritance/basic", "test that metatables are properly inherited") { + struct A { + int a = 5; + }; + + struct B { + int b() { + return 10; + } + }; + + struct C : B, A { + double c = 2.4; + }; + + struct D : C { + bool d() const { + return true; + } + }; + + sol::state lua; + lua.new_usertype("A", + "a", &A::a + ); + lua.new_usertype("B", + "b", &B::b + ); + lua.new_usertype("C", + "c", &C::c, + sol::base_classes, sol::bases() + ); + lua.new_usertype("D", + "d", &D::d, + sol::base_classes, sol::bases() + ); + + lua.script("obj = D.new()"); + lua.script("d = obj:d()"); + bool d = lua["d"]; + lua.script("c = obj.c"); + double c = lua["c"]; + lua.script("b = obj:b()"); + int b = lua["b"]; + lua.script("a = obj.a"); + int a = lua["a"]; + + REQUIRE(d); + REQUIRE(c == 2.4); + REQUIRE(b == 10); + REQUIRE(a == 5); +} + +TEST_CASE("inheritance/multi-base", "test that multiple bases all work and overloading for constructors works with them") { + class TestClass00 { + public: + int Thing() const { return 123; } + }; + + class TestClass01 : public TestClass00 { + public: + TestClass01() : a(1) {} + TestClass01(const TestClass00& other) : a(other.Thing()) {} + + int a; + }; + + class TestClass02 : public TestClass01 { + public: + TestClass02() : b(2) {} + TestClass02(const TestClass01& other) : b(other.a) {} + TestClass02(const TestClass00& other) : b(other.Thing()) {} + + int b; + }; + + class TestClass03 : public TestClass02 { + public: + TestClass03() : c(2) {} + TestClass03(const TestClass02& other) : c(other.b) {} + TestClass03(const TestClass01& other) : c(other.a) {} + TestClass03(const TestClass00& other) : c(other.Thing()) {} + + int c; + }; + + sol::state lua; + + sol::usertype s_TestUsertype00( + sol::call_constructor, sol::constructors>(), + "Thing", &TestClass00::Thing + ); + + lua.set_usertype("TestClass00", s_TestUsertype00); + + sol::usertype s_TestUsertype01( + sol::call_constructor, sol::constructors, sol::types>(), + sol::base_classes, sol::bases(), + "a", &TestClass01::a + ); + + lua.set_usertype("TestClass01", s_TestUsertype01); + + sol::usertype s_TestUsertype02( + sol::call_constructor, sol::constructors, sol::types, sol::types>(), + sol::base_classes, sol::bases(), + "b", &TestClass02::b + ); + + lua.set_usertype("TestClass02", s_TestUsertype02); + + sol::usertype s_TestUsertype03( + sol::call_constructor, sol::constructors, sol::types, sol::types, sol::types>(), + sol::base_classes, sol::bases(), + "c", &TestClass03::c + ); + + lua.set_usertype("TestClass03", s_TestUsertype03); + + lua.script(R"( +tc0 = TestClass00() +)"); + + lua.script(R"( +tc2 = TestClass02(tc0) +)"); + + lua.script(R"( +tc1 = TestClass01() +)"); + + lua.script(R"( +tc3 = TestClass03(tc1) +)"); + + TestClass00& tc0 = lua["tc0"]; + TestClass01& tc1 = lua["tc1"]; + TestClass02& tc2 = lua["tc2"]; + TestClass03& tc3 = lua["tc3"]; + REQUIRE(tc1.a == 1); + REQUIRE(tc2.a == 1); + REQUIRE(tc2.b == 123); + REQUIRE(tc3.a == 1); + REQUIRE(tc3.b == 2); + REQUIRE(tc3.c == 1); +} + +TEST_CASE("inheritance/simple-multi-base", "test that multiple bases all work and overloading for constructors works with them") { + class TestClass00 { + public: + int Thing() const { return 123; } + }; + + class TestClass01 : public TestClass00 { + public: + TestClass01() : a(1) {} + TestClass01(const TestClass00& other) : a(other.Thing()) {} + + int a; + }; + + class TestClass02 : public TestClass01 { + public: + TestClass02() : b(2) {} + TestClass02(const TestClass01& other) : b(other.a) {} + TestClass02(const TestClass00& other) : b(other.Thing()) {} + + int b; + }; + + class TestClass03 : public TestClass02 { + public: + TestClass03() : c(2) {} + TestClass03(const TestClass02& other) : c(other.b) {} + TestClass03(const TestClass01& other) : c(other.a) {} + TestClass03(const TestClass00& other) : c(other.Thing()) {} + + int c; + }; + + sol::state lua; + + sol::simple_usertype s_TestUsertype00( lua, + sol::call_constructor, sol::constructors>(), + "Thing", &TestClass00::Thing + ); + + lua.set_usertype("TestClass00", s_TestUsertype00); + + sol::simple_usertype s_TestUsertype01( lua, + sol::call_constructor, sol::constructors, sol::types>(), + sol::base_classes, sol::bases(), + "a", &TestClass01::a + ); + + lua.set_usertype("TestClass01", s_TestUsertype01); + + sol::simple_usertype s_TestUsertype02( lua, + sol::call_constructor, sol::constructors, sol::types, sol::types>(), + sol::base_classes, sol::bases(), + "b", &TestClass02::b + ); + + lua.set_usertype("TestClass02", s_TestUsertype02); + + sol::simple_usertype s_TestUsertype03( lua, + sol::call_constructor, sol::constructors, sol::types, sol::types, sol::types>(), + sol::base_classes, sol::bases(), + "c", &TestClass03::c + ); + + lua.set_usertype("TestClass03", s_TestUsertype03); + + lua.script(R"( +tc0 = TestClass00() +)"); + + lua.script(R"( +tc2 = TestClass02(tc0) +)"); + + lua.script(R"( +tc1 = TestClass01() +)"); + + lua.script(R"( +tc3 = TestClass03(tc1) +)"); + + TestClass00& tc0 = lua["tc0"]; + TestClass01& tc1 = lua["tc1"]; + TestClass02& tc2 = lua["tc2"]; + TestClass03& tc3 = lua["tc3"]; + REQUIRE(tc1.a == 1); + REQUIRE(tc2.a == 1); + REQUIRE(tc2.b == 123); + REQUIRE(tc3.a == 1); + REQUIRE(tc3.b == 2); + REQUIRE(tc3.c == 1); +} diff --git a/test_simple_usertypes.cpp b/test_simple_usertypes.cpp index b5687a2e..36b8415f 100644 --- a/test_simple_usertypes.cpp +++ b/test_simple_usertypes.cpp @@ -222,21 +222,31 @@ TEST_CASE("usertype/simple-vars", "simple usertype vars can bind various values lua.new_simple_usertype("test", "straight", sol::var(2), "global", sol::var(muh_variable), - "global2", sol::var(through_variable) + "global2", sol::var(through_variable), + "global3", sol::var(std::ref(through_variable)) ); + + through_variable = 20; lua.script(R"( +print(test.straight) s = test.straight +print(test.global) g = test.global +print(test.global2) g2 = test.global2 +print(test.global3) +g3 = test.global3 )"); int s = lua["s"]; int g = lua["g"]; int g2 = lua["g2"]; + int g3 = lua["g3"]; REQUIRE(s == 2); REQUIRE(g == 10); REQUIRE(g2 == 25); + REQUIRE(g3 == 20); } TEST_CASE("usertypes/simple-variable-control", "test to see if usertypes respond to inheritance and variable controls") { @@ -377,9 +387,10 @@ TEST_CASE("usertype/simple-runtime-append", "allow extra functions to be appende sol::state lua; lua.new_simple_usertype("A"); - lua.new_simple_usertype("B", sol::base_classes, sol::bases()); // OFFTOP: It crashes here because there's no stuff registered in A usertype( is it an issue? ) + lua.new_simple_usertype("B", sol::base_classes, sol::bases()); lua.set("b", std::make_unique()); lua["A"]["method"] = []() { return 200; }; + lua["B"]["method2"] = [](B&) { return 100; }; lua.script("x = b.method()"); lua.script("y = b:method()"); @@ -387,4 +398,11 @@ TEST_CASE("usertype/simple-runtime-append", "allow extra functions to be appende int y = lua["y"]; REQUIRE(x == 200); REQUIRE(y == 200); + + lua.script("z = b.method2(b)"); + lua.script("w = b:method2()"); + int z = lua["z"]; + int w = lua["w"]; + REQUIRE(z == 100); + REQUIRE(w == 100); } diff --git a/test_usertypes.cpp b/test_usertypes.cpp index 1ca7b5ef..9dff3238 100644 --- a/test_usertypes.cpp +++ b/test_usertypes.cpp @@ -1332,59 +1332,6 @@ print(t.global) )")); } -TEST_CASE("usertype/inheritance", "test that metatables are properly inherited") { - struct A { - int a = 5; - }; - - struct B { - int b() { - return 10; - } - }; - - struct C : B, A { - double c = 2.4; - }; - - struct D : C { - bool d() const { - return true; - } - }; - - sol::state lua; - lua.new_usertype("A", - "a", &A::a - ); - lua.new_usertype("B", - "b", &B::b - ); - lua.new_usertype("C", - "c", &C::c, - sol::base_classes, sol::bases() - ); - lua.new_usertype("D", - "d", &D::d, - sol::base_classes, sol::bases() - ); - - lua.script("obj = D.new()"); - lua.script("d = obj:d()"); - bool d = lua["d"]; - lua.script("c = obj.c"); - double c = lua["c"]; - lua.script("b = obj:b()"); - int b = lua["b"]; - lua.script("a = obj.a"); - int a = lua["a"]; - - REQUIRE(d); - REQUIRE(c == 2.4); - REQUIRE(b == 10); - REQUIRE(a == 5); -} - 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: