OoOoOooh and the world exploooodes.

Added new examples to illustrate a few things people were wondering about
HEAVILY fixed up usertypes and inheritance. Again. Sigh. One day it'll all be correct.
This commit is contained in:
ThePhD 2016-09-17 22:15:46 -04:00
parent 8028628868
commit 330df79ab9
11 changed files with 470 additions and 88 deletions

View File

@ -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
-------------------------

47
examples/require.cpp Normal file
View File

@ -0,0 +1,47 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp>
#include <cassert>
#include <iostream>
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>("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<sol::wrap<callable_struct, callable_struct{}>> to make the call
// if it's a constexpr struct
lua.require("my_lib", sol::c_call<decltype(&open_mylib), &open_mylib>);
// 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;
}

35
examples/self_call.cpp Normal file
View File

@ -0,0 +1,35 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp>
#include <cassert>
#include <iostream>
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;
}

View File

@ -58,12 +58,12 @@ namespace sol {
const std::size_t id_for<T>::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 <typename Base, typename... Args>
static bool type_check_bases(types<Base, Args...>, std::size_t ti) {
return ti != id_for<Base>::value || type_check_bases(types<Args...>(), ti);
return ti == id_for<Base>::value || type_check_bases(types<Args...>(), ti);
}
static bool type_check(std::size_t ti) {
return ti != id_for<T>::value || type_check_bases(types<Bases...>(), ti);
return ti == id_for<T>::value || type_check_bases(types<Bases...>(), ti);
}
static void* type_cast_bases(types<>, T*, std::size_t) {

View File

@ -161,18 +161,30 @@ namespace sol {
bool mustindex;
bool secondarymeta;
template <typename N>
void insert(N&& n, object&& o) {
std::string key = usertype_detail::make_string(std::forward<N>(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 <typename N, typename F, meta::enable<meta::is_callable<meta::unwrap_unqualified_t<F>>> = meta::enabler>
void add_function(lua_State* L, N&& n, F&& f) {
registrations.emplace(usertype_detail::make_string(std::forward<N>(n)), make_object(L, as_function_reference(std::forward<F>(f))));
insert(std::forward<N>(n), make_object(L, as_function_reference(std::forward<F>(f))));
}
template <typename N, typename F, meta::disable<meta::is_callable<meta::unwrap_unqualified_t<F>>> = meta::enabler>
void add_function(lua_State* L, N&& n, F&& f) {
object o = make_object(L, std::forward<F>(f));
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
callconstructfunc = make_object(L, std::forward<F>(f));
callconstructfunc = std::move(o);
return;
}
registrations.emplace(usertype_detail::make_string(std::forward<N>(n)), make_object(L, std::forward<F>(f)));
insert(std::forward<N>(n), std::move(o));
}
template <typename N, typename F, meta::disable<is_variable_binding<meta::unqualified_t<F>>> = meta::enabler>
@ -182,9 +194,16 @@ namespace sol {
template <typename N, typename F, meta::enable<is_variable_binding<meta::unqualified_t<F>>> = meta::enabler>
void add(lua_State*, N&& n, F&& f) {
varmap.emplace(usertype_detail::make_string(std::forward<N>(n)), std::make_unique<usertype_detail::callable_binding<T, std::decay_t<F>>>(std::forward<F>(f)));
mustindex = true;
secondarymeta = true;
std::string key = usertype_detail::make_string(std::forward<N>(n));
auto o = std::make_unique<usertype_detail::callable_binding<T, std::decay_t<F>>>(std::forward<F>(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 <typename N, typename... Fxs>
@ -194,7 +213,7 @@ namespace sol {
callconstructfunc = std::move(o);
return;
}
registrations.emplace(usertype_detail::make_string(std::forward<N>(n)), std::move(o));
insert(std::forward<N>(n), std::move(o));
}
template <typename N, typename... Lists>
@ -204,7 +223,27 @@ namespace sol {
callconstructfunc = std::move(o);
return;
}
registrations.emplace(usertype_detail::make_string(std::forward<N>(n)), std::move(o));
insert(std::forward<N>(n), std::move(o));
}
template <typename N>
void add(lua_State* L, N&& n, destructor_wrapper<void> c) {
object o(L, in_place<detail::tagged<T, destructor_wrapper<void>>>, std::move(c));
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
callconstructfunc = std::move(o);
return;
}
insert(std::forward<N>(n), std::move(o));
}
template <typename N, typename Fx>
void add(lua_State* L, N&& n, destructor_wrapper<Fx> c) {
object o(L, in_place<detail::tagged<T, destructor_wrapper<Fx>>>, std::move(c));
if (std::is_same<meta::unqualified_t<N>, call_construction>::value) {
callconstructfunc = std::move(o);
return;
}
insert(std::forward<N>(n), std::move(o));
}
template <typename... Bases>
@ -224,20 +263,20 @@ namespace sol {
newindexbaseclasspropogation = usertype_detail::walk_all_bases<false, Bases...>;
}
private:
template<std::size_t... I, typename Tuple>
simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args)
: callconstructfunc(nil),
indexfunc(&usertype_detail::indexing_fail<true>), newindexfunc(&usertype_detail::indexing_fail<false>),
indexbase(&usertype_detail::simple_core_indexing_call<true>), newindexbase(&usertype_detail::simple_core_indexing_call<false>),
indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(&usertype_detail::walk_all_bases<false>),
baseclasscheck(nullptr), baseclasscast(nullptr),
mustindex(false), secondarymeta(false) {
: callconstructfunc(nil),
indexfunc(&usertype_detail::indexing_fail<true>), newindexfunc(&usertype_detail::indexing_fail<false>),
indexbase(&usertype_detail::simple_core_indexing_call<true>), newindexbase(&usertype_detail::simple_core_indexing_call<false>),
indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(&usertype_detail::walk_all_bases<false>),
baseclasscheck(nullptr), baseclasscast(nullptr),
mustindex(false), secondarymeta(false) {
(void)detail::swallow{ 0,
(add(L, detail::forward_get<I * 2>(args), detail::forward_get<I * 2 + 1>(args)),0)...
};
}
private:
template<typename... Args>
simple_usertype_metatable(lua_State* L, usertype_detail::verified_tag v, Args&&... args) : simple_usertype_metatable(v, std::make_index_sequence<sizeof...(Args) / 2>(), L, std::forward_as_tuple(std::forward<Args>(args)...)) {}

View File

@ -262,16 +262,18 @@ namespace sol {
if (lua_getmetatable(L, index) == 0) {
return true;
}
if (stack_detail::check_metatable<U>(L))
int metatableindex = lua_gettop(L);
if (stack_detail::check_metatable<U>(L, metatableindex))
return true;
if (stack_detail::check_metatable<U*>(L))
if (stack_detail::check_metatable<U*>(L, metatableindex))
return true;
if (stack_detail::check_metatable<detail::unique_usertype<U>>(L))
if (stack_detail::check_metatable<detail::unique_usertype<U>>(L, metatableindex))
return true;
bool success = false;
if (detail::has_derived<T>::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;

View File

@ -32,7 +32,7 @@ namespace sol {
template<typename T>
class usertype {
protected:
private:
std::unique_ptr<usertype_detail::registrar, detail::deleter> metatableregister;
template<typename... Args>
@ -58,6 +58,10 @@ namespace sol {
template<typename... Args>
usertype(simple_tag, lua_State* L, Args&&... args) : metatableregister(detail::make_unique_deleter<simple_usertype_metatable<T>, detail::deleter>(L, std::forward<Args>(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<typename T>
class simple_usertype : public usertype<T> {
protected:
private:
typedef usertype<T> base_t;
lua_State* state;
public:
template<typename... Args>
simple_usertype(lua_State* L, Args&&... args) : state(L), usertype(simple, L, std::forward<Args>(args)...) {}
template <typename N, typename F>
void add(N&& n, F&& f) {
auto meta = (simple_usertype_metatable<T>*) metatableregister.get();
simple_usertype(lua_State* L, Args&&... args) : base_t(simple, L, std::forward<Args>(args)...), state(L) {}
template <typename N, typename F>
void set(N&& n, F&& f) {
auto meta = static_cast<simple_usertype_metatable<T>*>(base_t::registrar_data());
meta->add(state, n, f);
}
};

View File

@ -107,13 +107,13 @@ namespace sol {
auto maybeaccessor = stack::get<optional<string_detail::string_shim>>(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 <bool is_index, typename Base>
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<Base>::metatable[0];

247
test_inheritance.cpp Normal file
View File

@ -0,0 +1,247 @@
#define SOL_CHECK_ARGUMENTS
#include <sol.hpp>
#include <catch.hpp>
#include <iostream>
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::a
);
lua.new_usertype<B>("B",
"b", &B::b
);
lua.new_usertype<C>("C",
"c", &C::c,
sol::base_classes, sol::bases<B, A>()
);
lua.new_usertype<D>("D",
"d", &D::d,
sol::base_classes, sol::bases<C, B, A>()
);
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<TestClass00> s_TestUsertype00(
sol::call_constructor, sol::constructors<sol::types<>>(),
"Thing", &TestClass00::Thing
);
lua.set_usertype("TestClass00", s_TestUsertype00);
sol::usertype<TestClass01> s_TestUsertype01(
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass00>(),
"a", &TestClass01::a
);
lua.set_usertype("TestClass01", s_TestUsertype01);
sol::usertype<TestClass02> s_TestUsertype02(
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass01, TestClass00>(),
"b", &TestClass02::b
);
lua.set_usertype("TestClass02", s_TestUsertype02);
sol::usertype<TestClass03> s_TestUsertype03(
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass02&>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass02, TestClass01, TestClass00>(),
"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<TestClass00> s_TestUsertype00( lua,
sol::call_constructor, sol::constructors<sol::types<>>(),
"Thing", &TestClass00::Thing
);
lua.set_usertype("TestClass00", s_TestUsertype00);
sol::simple_usertype<TestClass01> s_TestUsertype01( lua,
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass00>(),
"a", &TestClass01::a
);
lua.set_usertype("TestClass01", s_TestUsertype01);
sol::simple_usertype<TestClass02> s_TestUsertype02( lua,
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass01, TestClass00>(),
"b", &TestClass02::b
);
lua.set_usertype("TestClass02", s_TestUsertype02);
sol::simple_usertype<TestClass03> s_TestUsertype03( lua,
sol::call_constructor, sol::constructors<sol::types<>, sol::types<const TestClass02&>, sol::types<const TestClass01&>, sol::types<const TestClass00&>>(),
sol::base_classes, sol::bases<TestClass02, TestClass01, TestClass00>(),
"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);
}

View File

@ -222,21 +222,31 @@ TEST_CASE("usertype/simple-vars", "simple usertype vars can bind various values
lua.new_simple_usertype<test>("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>("A");
lua.new_simple_usertype<B>("B", sol::base_classes, sol::bases<A>()); // OFFTOP: It crashes here because there's no stuff registered in A usertype( is it an issue? )
lua.new_simple_usertype<B>("B", sol::base_classes, sol::bases<A>());
lua.set("b", std::make_unique<B>());
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);
}

View File

@ -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::a
);
lua.new_usertype<B>("B",
"b", &B::b
);
lua.new_usertype<C>("C",
"c", &C::c,
sol::base_classes, sol::bases<B, A>()
);
lua.new_usertype<D>("D",
"d", &D::d,
sol::base_classes, sol::bases<C, B, A>()
);
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: