some small fixes for usertypes, still need a few more drastic improvements...

This commit is contained in:
ThePhD 2018-10-15 21:53:16 -04:00
parent 50f92be1c7
commit fa07e54e08
No known key found for this signature in database
GPG Key ID: 1509DB1C0F702BFA
4 changed files with 42 additions and 994 deletions

View File

@ -203,13 +203,13 @@ namespace sol {
void insert_default_registrations(IFx&& ifx, Fx&& fx) { void insert_default_registrations(IFx&& ifx, Fx&& fx) {
if constexpr (is_automagical<T>::value) { if constexpr (is_automagical<T>::value) {
if (fx(meta_function::less_than)) { if (fx(meta_function::less_than)) {
if constexpr (meta::supports_op_equal<T>::value) { if constexpr (meta::supports_op_less<T>::value) {
lua_CFunction f = &comparsion_operator_wrap<T, std::less<>>; lua_CFunction f = &comparsion_operator_wrap<T, std::less<>>;
ifx(meta_function::less_than, f); ifx(meta_function::less_than, f);
} }
} }
if (fx(meta_function::less_than_or_equal_to)) { if (fx(meta_function::less_than_or_equal_to)) {
if constexpr (meta::supports_op_equal<T>::value) { if constexpr (meta::supports_op_less_equal<T>::value) {
lua_CFunction f = &comparsion_operator_wrap<T, std::less_equal<>>; lua_CFunction f = &comparsion_operator_wrap<T, std::less_equal<>>;
ifx(meta_function::less_than_or_equal_to, f); ifx(meta_function::less_than_or_equal_to, f);
} }

View File

@ -126,14 +126,24 @@ namespace u_detail {
stack::get_field(L, stack_reference(L, raw_index(2)), metatarget); stack::get_field(L, stack_reference(L, raw_index(2)), metatarget);
return 1; return 1;
} }
// With runtime extensibility, we can't hard-error things. They have to return nil, like regular table types // With runtime extensibility, we can't
// hard-error things. They have to
// return nil, like regular table types
return stack::push(L, lua_nil); return stack::push(L, lua_nil);
} }
inline int index_target_fail(lua_State* L, void*) {
return index_fail(L);
}
inline int new_index_fail(lua_State* L) { inline int new_index_fail(lua_State* L) {
return luaL_error(L, "sol: cannot set (new_index) into this object: no defined new_index operation on usertype"); return luaL_error(L, "sol: cannot set (new_index) into this object: no defined new_index operation on usertype");
} }
inline int new_index_target_fail(lua_State* L, void*) {
return new_index_fail(L);
}
struct usertype_storage_base { struct usertype_storage_base {
public: public:
std::vector<std::unique_ptr<binding_base>> storage; std::vector<std::unique_ptr<binding_base>> storage;
@ -149,13 +159,19 @@ namespace u_detail {
reference gc_names_table; reference gc_names_table;
reference metametatable; reference metametatable;
std::bitset<64> properties; std::bitset<64> properties;
lua_CFunction base_index; index_call_storage base_index;
lua_CFunction base_new_index; index_call_storage base_new_index;
bool is_using_index; bool is_using_index;
bool is_using_new_index; bool is_using_new_index;
usertype_storage_base(lua_State* L) usertype_storage_base(lua_State* L)
: storage(), string_keys(), auxiliary_keys(), value_index_table(), reference_index_table(), unique_index_table(), const_reference_index_table(), type_table(make_reference(L, create)), gc_names_table(make_reference(L, create)), metametatable(make_reference(L, create)), properties(), base_index(index_fail), base_new_index(new_index_fail), is_using_index(false), is_using_new_index(false) { : storage(), string_keys(), auxiliary_keys(), value_index_table(), reference_index_table(), unique_index_table(), const_reference_index_table(), type_table(make_reference(L, create)), gc_names_table(make_reference(L, create)), metametatable(make_reference(L, create)), properties(), base_index(), base_new_index(), is_using_index(false), is_using_new_index(false) {
base_index.binding_data = nullptr;
base_index.index = index_target_fail;
base_index.new_index = index_target_fail;
base_new_index.binding_data = nullptr;
base_new_index.index = new_index_target_fail;
base_new_index.new_index = new_index_target_fail;
} }
void clear() { void clear() {
@ -244,7 +260,7 @@ namespace u_detail {
return index_fail(L); return index_fail(L);
} }
else { else {
return self.base_index(L); return self.base_index.index(L, self.base_index.binding_data);
} }
} }
@ -300,7 +316,7 @@ namespace u_detail {
return 0; return 0;
} }
else { else {
return self.base_new_index(L); return self.base_new_index.new_index(L, self.base_new_index.binding_data);
} }
} }
@ -438,7 +454,7 @@ namespace u_detail {
ics.new_index = &b.index_call_with_<false, is_var_bind::value>; ics.new_index = &b.index_call_with_<false, is_var_bind::value>;
// need to swap everything to use fast indexing here // need to swap everything to use fast indexing here
auto fet = [&](lua_State* L, submetatable submetatable_type, reference& fast_index_table) { auto fet = [&](lua_State* L, submetatable submetatable_type, reference& fast_index_table) {
if (submetatable_type == submetatable::named && no_use_named) { if (submetatable_type == submetatable::named && (no_use_named || is_index || is_new_index)) {
// do not override __call or // do not override __call or
// other specific meta functions on named metatable: // other specific meta functions on named metatable:
// we need that for call construction // we need that for call construction
@ -458,6 +474,12 @@ namespace u_detail {
} }
t.pop(); t.pop();
}; };
if (is_index) {
this->base_index = ics;
}
if (is_new_index) {
this->base_new_index = ics;
}
this->for_each_table(L, fet); this->for_each_table(L, fet);
this->string_keys.insert_or_assign(std::move(s), std::move(ics)); this->string_keys.insert_or_assign(std::move(s), std::move(ics));
} }

View File

@ -1,977 +0,0 @@
// sol3
// The MIT License (MIT)
// Copyright (c) 2013-2018 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 "test_sol.hpp"
#include <catch.hpp>
#include <iostream>
#include <list>
#include <memory>
#include <mutex>
TEST_CASE("simple_usertype/usertypes", "Ensure that simple usertypes properly work here") {
struct marker {
bool value = false;
};
struct bark {
int var = 50;
marker mark;
void fun() {
var = 51;
}
int get() const {
return var;
}
int set(int x) {
var = x;
return var;
}
std::string special() const {
return mark.value ? "woof" : "pantpant";
}
const marker& the_marker() const {
return mark;
}
};
sol::state lua;
lua.new_simple_usertype<bark>("bark",
"fun", &bark::fun,
"get", &bark::get,
"var", sol::as_function(&bark::var),
"the_marker", sol::as_function(&bark::the_marker),
"x", sol::overload(&bark::get),
"y", sol::overload(&bark::set),
"z", sol::overload(&bark::get, &bark::set));
lua.safe_script("b = bark.new()");
bark& b = lua["b"];
lua.safe_script("b:fun()");
int var = b.var;
REQUIRE(var == 51);
lua.safe_script("b:var(20)");
lua.safe_script("v = b:var()");
int v = lua["v"];
REQUIRE(v == 20);
REQUIRE(b.var == 20);
lua.safe_script("m = b:the_marker()");
marker& m = lua["m"];
REQUIRE_FALSE(b.mark.value);
REQUIRE_FALSE(m.value);
m.value = true;
REQUIRE(&b.mark == &m);
REQUIRE(b.mark.value);
sol::table barktable = lua["bark"];
barktable["special"] = &bark::special;
lua.safe_script("s = b:special()");
std::string s = lua["s"];
REQUIRE(s == "woof");
lua.safe_script("b:y(24)");
lua.safe_script("x = b:x()");
int x = lua["x"];
REQUIRE(x == 24);
lua.safe_script("z = b:z(b:z() + 5)");
int z = lua["z"];
REQUIRE(z == 29);
}
TEST_CASE("simple_usertype/usertype constructors", "Ensure that calls with specific arguments work") {
struct marker {
bool value = false;
};
struct bark {
int var = 50;
marker mark;
bark() {
}
bark(int v)
: var(v) {
}
void fun() {
var = 51;
}
int get() const {
return var;
}
int set(int x) {
var = x;
return var;
}
std::string special() const {
return mark.value ? "woof" : "pantpant";
}
const marker& the_marker() const {
return mark;
}
};
sol::state lua;
lua.new_simple_usertype<bark>("bark",
sol::constructors<sol::types<>, sol::types<int>>(),
"fun", sol::protect(&bark::fun),
"get", &bark::get,
"var", sol::as_function(&bark::var),
"the_marker", &bark::the_marker,
"x", sol::overload(&bark::get),
"y", sol::overload(&bark::set),
"z", sol::overload(&bark::get, &bark::set));
lua.safe_script("bx = bark.new(760)");
bark& bx = lua["bx"];
REQUIRE(bx.var == 760);
lua.safe_script("b = bark.new()");
bark& b = lua["b"];
lua.safe_script("b:fun()");
int var = b.var;
REQUIRE(var == 51);
lua.safe_script("b:var(20)");
lua.safe_script("v = b:var()");
int v = lua["v"];
REQUIRE(v == 20);
lua.safe_script("m = b:the_marker()");
marker& m = lua["m"];
REQUIRE_FALSE(b.mark.value);
REQUIRE_FALSE(m.value);
m.value = true;
REQUIRE(&b.mark == &m);
REQUIRE(b.mark.value);
sol::table barktable = lua["bark"];
barktable["special"] = &bark::special;
lua.safe_script("s = b:special()");
std::string s = lua["s"];
REQUIRE(s == "woof");
lua.safe_script("b:y(24)");
lua.safe_script("x = b:x()");
int x = lua["x"];
REQUIRE(x == 24);
lua.safe_script("z = b:z(b:z() + 5)");
int z = lua["z"];
REQUIRE(z == 29);
}
TEST_CASE("simple_usertype/vars", "simple usertype vars can bind various values (no ref)") {
int muh_variable = 10;
int through_variable = 25;
sol::state lua;
lua.open_libraries();
struct test {};
lua.new_simple_usertype<test>("test",
"straight", sol::var(2),
"global", sol::var(muh_variable),
"global2", sol::var(through_variable),
"global3", sol::var(std::ref(through_variable)));
through_variable = 20;
lua.safe_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("simple_usertype/variable-control", "test to see if usertypes respond to inheritance and variable controls") {
class A {
public:
virtual void a() {
throw std::runtime_error("entered base pure virtual implementation");
};
};
class B : public A {
public:
virtual void a() override {
}
};
class sA {
public:
virtual void a() {
throw std::runtime_error("entered base pure virtual implementation");
};
};
class sB : public sA {
public:
virtual void a() override {
}
};
struct sV {
int a = 10;
int b = 20;
int get_b() const {
return b + 2;
}
void set_b(int value) {
b = value;
}
};
struct sW : sV {};
sol::state lua;
lua.open_libraries();
lua.new_usertype<A>("A", "a", &A::a);
lua.new_usertype<B>("B", sol::base_classes, sol::bases<A>());
lua.new_simple_usertype<sA>("sA", "a", &sA::a);
lua.new_simple_usertype<sB>("sB", sol::base_classes, sol::bases<sA>());
lua.new_simple_usertype<sV>("sV", "a", &sV::a, "b", &sV::b, "pb", sol::property(&sV::get_b, &sV::set_b));
lua.new_simple_usertype<sW>("sW", sol::base_classes, sol::bases<sV>());
B b;
lua.set("b", &b);
lua.safe_script("b:a()");
sB sb;
lua.set("sb", &sb);
lua.safe_script("sb:a()");
sV sv;
lua.set("sv", &sv);
lua.safe_script("print(sv.b)assert(sv.b == 20)");
sW sw;
lua.set("sw", &sw);
lua.safe_script("print(sw.a)assert(sw.a == 10)");
lua.safe_script("print(sw.b)assert(sw.b == 20)");
lua.safe_script("print(sw.pb)assert(sw.pb == 22)");
lua.safe_script("sw.a = 11");
lua.safe_script("sw.b = 21");
lua.safe_script("print(sw.a)assert(sw.a == 11)");
lua.safe_script("print(sw.b)assert(sw.b == 21)");
lua.safe_script("print(sw.pb)assert(sw.pb == 23)");
lua.safe_script("sw.pb = 25");
lua.safe_script("print(sw.b)assert(sw.b == 25)");
lua.safe_script("print(sw.pb)assert(sw.pb == 27)");
}
TEST_CASE("simple_usertype/factory constructor overloads", "simple usertypes should invoke the proper factories") {
class A {
public:
virtual void a() {
throw std::runtime_error("entered base pure virtual implementation");
};
};
class B : public A {
public:
int bvar = 24;
virtual void a() override {
}
void f() {
}
};
sol::state lua;
lua.open_libraries();
sol::constructors<sol::types<>, sol::types<const B&>> c;
lua.new_simple_usertype<B>("B",
sol::call_constructor, c,
"new", sol::factories([]() {
return B();
}),
"new2", sol::initializers([](B& mem) { new (&mem) B(); }, [](B& mem, int v) {
new(&mem)B(); mem.bvar = v; }),
"f", sol::as_function(&B::bvar),
"g", sol::overload([](B&) { return 2; }, [](B&, int v) { return v; }));
lua.safe_script("b = B()");
lua.safe_script("b2 = B.new()");
lua.safe_script("b3 = B.new2()");
lua.safe_script("b4 = B.new2(11)");
lua.safe_script("x = b:f()");
lua.safe_script("x2 = b2:f()");
lua.safe_script("x3 = b3:f()");
lua.safe_script("x4 = b4:f()");
int x = lua["x"];
int x2 = lua["x2"];
int x3 = lua["x3"];
int x4 = lua["x4"];
REQUIRE(x == 24);
REQUIRE(x2 == 24);
REQUIRE(x3 == 24);
REQUIRE(x4 == 11);
lua.safe_script("y = b:g()");
lua.safe_script("y2 = b2:g(3)");
lua.safe_script("y3 = b3:g()");
lua.safe_script("y4 = b4:g(3)");
int y = lua["y"];
int y2 = lua["y2"];
int y3 = lua["y3"];
int y4 = lua["y4"];
REQUIRE(y == 2);
REQUIRE(y2 == 3);
REQUIRE(y3 == 2);
REQUIRE(y4 == 3);
}
TEST_CASE("simple_usertype/runtime append", "allow extra functions to be appended at runtime directly to the metatable itself") {
class A {
};
class B : public A {
};
sol::state lua;
lua.new_simple_usertype<A>("A");
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.safe_script("x = b.method()");
lua.safe_script("y = b:method()");
int x = lua["x"];
int y = lua["y"];
REQUIRE(x == 200);
REQUIRE(y == 200);
lua.safe_script("z = b.method2(b)");
lua.safe_script("w = b:method2()");
int z = lua["z"];
int w = lua["w"];
REQUIRE(z == 100);
REQUIRE(w == 100);
}
TEST_CASE("simple_usertype/table append", "Ensure that appending to the meta table also affects the internal function table for pointers as well") {
struct A {
int func() {
return 5000;
}
};
sol::state lua;
lua.open_libraries();
lua.new_simple_usertype<A>("A");
sol::table table = lua["A"];
table["func"] = &A::func;
A a;
lua.set("a", &a);
lua.set("pa", &a);
lua.set("ua", std::make_unique<A>());
REQUIRE_NOTHROW([&] {
lua.safe_script("assert(a:func() == 5000)");
lua.safe_script("assert(pa:func() == 5000)");
lua.safe_script("assert(ua:func() == 5000)");
}());
}
TEST_CASE("simple_usertype/class call propogation", "make sure methods and variables from base classes work properly in SAFE_USERTYPE mode") {
class A {
public:
int var = 200;
int thing() const {
return 123;
}
};
class B : public A {
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<B>("B",
sol::default_constructor,
"thing", &B::thing,
"var", &B::var);
lua.safe_script(R"(
b = B.new()
print(b.var)
b:thing()
)");
}
TEST_CASE("simple_usertype/call constructor", "ensure that all kinds of call-based constructors can be serialized") {
struct thing {};
struct v_test {
};
struct f_test {
int i;
f_test(int i)
: i(i) {
}
};
struct i_test {
int i;
i_test(int i)
: i(i) {
}
};
struct r_test {
int i;
r_test(int i)
: i(i) {
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
auto f = sol::factories([]() { return f_test(30); });
lua.new_simple_usertype<f_test>("f_test",
sol::call_constructor, sol::factories([]() {
return f_test(20);
}),
"new", f);
lua.new_simple_usertype<i_test>("i_test",
sol::call_constructor, sol::initializers([](i_test& obj) {
new (&obj) i_test(21);
}));
lua.new_simple_usertype<r_test>("r_test",
sol::call_constructor, [](sol::table) {
return r_test(22);
});
lua.safe_script("a = f_test()");
lua.safe_script("b = i_test()");
lua.safe_script("c = r_test()");
lua.safe_script("d = f_test.new()");
f_test& a = lua["a"];
f_test& d = lua["d"];
i_test& b = lua["b"];
r_test& c = lua["c"];
REQUIRE(a.i == 20);
REQUIRE(b.i == 21);
REQUIRE(c.i == 22);
REQUIRE(d.i == 30);
auto vfactories = sol::factories(
[](const sol::table& tbl) {
for (auto v : tbl) {
REQUIRE(v.second.valid());
REQUIRE(v.second.is<thing>());
}
return v_test();
});
lua.new_simple_usertype<v_test>("v_test",
sol::meta_function::construct, vfactories,
sol::call_constructor, vfactories);
lua.new_simple_usertype<thing>("thing");
lua.safe_script("things = {thing.new(), thing.new()}");
SECTION("new") {
{
auto result = lua.safe_script("a = v_test.new(things)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
SECTION("call_constructor") {
{
auto result = lua.safe_script("b = v_test(things)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
}
TEST_CASE("simple_usertype/no_constructor", "make sure simple usertype errors when no-constructor types are called") {
struct thing {};
SECTION("new no_constructor") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
sol::meta_function::construct, sol::no_constructor);
auto result = lua.safe_script("a = thing.new()", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
SECTION("call no_constructor") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
sol::call_constructor, sol::no_constructor);
auto result = lua.safe_script("a = thing()", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
}
TEST_CASE("simple_usertype/missing key", "make sure a missing key returns nil") {
struct thing {};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing");
{
auto result = lua.safe_script("print(thing.missingKey)", sol::script_pass_on_error);
REQUIRE(result.valid());
}
}
TEST_CASE("simple_usertype/runtime extensibility", "Check if usertypes are runtime extensible") {
struct thing {
int v = 20;
int func(int a) {
return a;
}
};
int val = 0;
SECTION("just functions") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
"func", &thing::func);
lua.safe_script(R"(
t = thing.new()
)");
{
auto result = lua.safe_script(R"(
t.runtime_func = function (a)
return a + 50
end
)",
sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
{
auto result = lua.safe_script(R"(
function t:runtime_func(a)
return a + 52
end
)",
sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
lua.safe_script("val = t:func(2)");
val = lua["val"];
REQUIRE(val == 2);
REQUIRE_NOTHROW([&lua]() {
lua.safe_script(R"(
function thing:runtime_func(a)
return a + 1
end
)");
}());
lua.safe_script("val = t:runtime_func(2)");
val = lua["val"];
REQUIRE(val == 3);
}
SECTION("with variable") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<thing>("thing",
"func", &thing::func,
"v", &thing::v);
lua.safe_script(R"(
t = thing.new()
)");
{
auto result = lua.safe_script(R"(
t.runtime_func = function (a)
return a + 50
end
)",
sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
{
auto result = lua.safe_script(R"(
function t:runtime_func(a)
return a + 52
end
)", sol::script_pass_on_error);
REQUIRE_FALSE(result.valid());
}
lua.safe_script("val = t:func(2)");
val = lua["val"];
REQUIRE(val == 2);
REQUIRE_NOTHROW([&lua]() {
lua.safe_script(R"(
function thing:runtime_func(a)
return a + 1
end
)");
}());
lua.safe_script("val = t:runtime_func(2)");
val = lua["val"];
REQUIRE(val == 3);
}
}
TEST_CASE("simple_usertype/runtime replacement", "ensure that functions can be properly replaced at runtime for non-indexed things") {
struct heart_base_t {};
struct heart_t : heart_base_t {
void func() {
}
};
SECTION("plain") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<heart_t>("a");
REQUIRE_NOTHROW([&lua]() {
lua.safe_script("obj = a.new()");
lua.safe_script("function a:heartbeat () print('arf') return 1 end");
lua.safe_script("v1 = obj:heartbeat()");
lua.safe_script("function a:heartbeat () print('bark') return 2 end");
lua.safe_script("v2 = obj:heartbeat()");
lua.safe_script("a.heartbeat = function(self) print('woof') return 3 end");
lua.safe_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<heart_t>("a",
sol::base_classes, sol::bases<heart_base_t>());
REQUIRE_NOTHROW([&lua]() {
lua.safe_script("obj = a.new()");
lua.safe_script("function a:heartbeat () print('arf') return 1 end");
lua.safe_script("v1 = obj:heartbeat()");
lua.safe_script("function a:heartbeat () print('bark') return 2 end");
lua.safe_script("v2 = obj:heartbeat()");
lua.safe_script("a.heartbeat = function(self) print('woof') return 3 end");
lua.safe_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<heart_t>("a",
"func", &heart_t::func,
sol::base_classes, sol::bases<heart_base_t>());
REQUIRE_NOTHROW([&lua]() {
lua.safe_script("obj = a.new()");
lua.safe_script("function a:heartbeat () print('arf') return 1 end");
lua.safe_script("v1 = obj:heartbeat()");
lua.safe_script("function a:heartbeat () print('bark') return 2 end");
lua.safe_script("v2 = obj:heartbeat()");
lua.safe_script("a.heartbeat = function(self) print('woof') return 3 end");
lua.safe_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/runtime additions with newindex", "ensure that additions when new_index is overriden don't hit the specified new_index function") {
class newindex_object {};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<newindex_object>("object",
sol::meta_function::new_index, [](newindex_object& o, sol::object key, sol::object value) {
return;
});
lua["object"]["test"] = [](newindex_object& o) {
std::cout << "test" << std::endl;
return 446;
};
auto result1 = lua.safe_script("o = object.new()", sol::script_pass_on_error);
REQUIRE(result1.valid());
auto result2 = lua.safe_script("assert(o:test() == 446)", sol::script_pass_on_error);
REQUIRE(result2.valid());
}
TEST_CASE("simple_usertype/meta key retrievals", "allow for special meta keys (__index, __newindex) to trigger methods even if overwritten directly") {
SECTION("dynamically") {
static int writes = 0;
static std::string keys[4] = {};
static int values[4] = {};
struct d_sample {
void foo(std::string k, int v) {
keys[writes] = k;
values[writes] = v;
++writes;
}
};
sol::state lua;
lua.new_simple_usertype<d_sample>("sample");
sol::table s = lua["sample"]["new"]();
s[sol::metatable_key][sol::meta_function::new_index] = &d_sample::foo;
lua["var"] = s;
lua.safe_script("var = sample.new()");
lua.safe_script("var.key = 2");
lua.safe_script("var.__newindex = 4");
lua.safe_script("var.__index = 3");
lua.safe_script("var.__call = 1");
REQUIRE(values[0] == 2);
REQUIRE(values[1] == 4);
REQUIRE(values[2] == 3);
REQUIRE(values[3] == 1);
REQUIRE(keys[0] == "key");
REQUIRE(keys[1] == "__newindex");
REQUIRE(keys[2] == "__index");
REQUIRE(keys[3] == "__call");
}
SECTION("statically") {
static int writes = 0;
static std::string keys[4] = {};
static int values[4] = {};
struct sample {
void foo(std::string k, int v) {
keys[writes] = k;
values[writes] = v;
++writes;
}
};
sol::state lua;
lua.new_simple_usertype<sample>("sample", sol::meta_function::new_index, &sample::foo);
lua.safe_script("var = sample.new()");
lua.safe_script("var.key = 2");
lua.safe_script("var.__newindex = 4");
lua.safe_script("var.__index = 3");
lua.safe_script("var.__call = 1");
REQUIRE(values[0] == 2);
REQUIRE(values[1] == 4);
REQUIRE(values[2] == 3);
REQUIRE(values[3] == 1);
REQUIRE(keys[0] == "key");
REQUIRE(keys[1] == "__newindex");
REQUIRE(keys[2] == "__index");
REQUIRE(keys[3] == "__call");
}
}
TEST_CASE("simple_usertype/static properties", "allow for static functions to get and set things as a property") {
static int b = 50;
struct test_t {
static double s_func() {
return b + 0.5;
}
static void g_func(int v) {
b = v;
}
std::size_t func() {
return 24;
}
};
test_t manager;
sol::state lua;
lua.new_simple_usertype<test_t>("test",
"f", std::function<std::size_t()>(std::bind(std::mem_fn(&test_t::func), &manager)),
"g", sol::property(&test_t::s_func, &test_t::g_func));
lua.safe_script("v1 = test.f()");
lua.safe_script("v2 = test.g");
lua.safe_script("test.g = 60");
lua.safe_script("v2a = test.g");
int v1 = lua["v1"];
REQUIRE(v1 == 24);
double v2 = lua["v2"];
REQUIRE(v2 == 50.5);
double v2a = lua["v2a"];
REQUIRE(v2a == 60.5);
}
TEST_CASE("simple_usertype/indexing", "make sure simple usertypes can be indexed/new_indexed properly without breaking") {
static int val = 0;
class indexing_test {
public:
indexing_test() {
val = 0;
}
sol::object getter(const std::string& name, sol::this_state _s) {
REQUIRE(name == "a");
return sol::make_object(_s, 2);
}
void setter(std::string name, sol::object n) {
REQUIRE(name == "unknown");
val = n.as<int>();
}
int hi() {
std::cout << "hi" << std::endl;
return 25;
}
};
SECTION("no runtime additions") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<indexing_test>("test",
sol::meta_function::index, &indexing_test::getter,
sol::meta_function::new_index, &indexing_test::setter);
lua.safe_script(R"(
local t = test.new()
v = t.a
print(v)
t.unknown = 50
)");
int v = lua["v"];
REQUIRE(v == 2);
REQUIRE(val == 50);
}
SECTION("runtime additions") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<indexing_test>("test",
sol::meta_function::index, &indexing_test::getter,
sol::meta_function::new_index, &indexing_test::setter);
lua["test"]["hi"] = [](indexing_test& _self) -> int { return _self.hi(); };
lua.safe_script(R"(
local t = test.new()
v = t.a;
print(v)
t.unknown = 50
u = t:hi()
)");
int v = lua["v"];
int u = lua["u"];
REQUIRE(v == 2);
REQUIRE(u == 25);
REQUIRE(val == 50);
}
}
TEST_CASE("simple_usertype/basic type information", "check that we can query some basic type information") {
struct my_thing {};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_simple_usertype<my_thing>("my_thing");
lua.safe_script("obj = my_thing.new()");
lua.safe_script("assert(my_thing.__type.is(obj))");
lua.safe_script("assert(not my_thing.__type.is(1))");
lua.safe_script("assert(not my_thing.__type.is(\"not a thing\"))");
lua.safe_script("print(my_thing.__type.name)");
lua.safe_script("assert(obj.__type.is(obj))");
lua.safe_script("assert(not obj.__type.is(1))");
lua.safe_script("assert(not obj.__type.is(\"not a thing\"))");
lua.safe_script("print(obj.__type.name)");
lua.safe_script("assert(getmetatable(my_thing).__type.is(obj))");
lua.safe_script("assert(not getmetatable(my_thing).__type.is(1))");
lua.safe_script("assert(not getmetatable(my_thing).__type.is(\"not a thing\"))");
lua.safe_script("print(getmetatable(my_thing).__type.name)");
lua.safe_script("assert(getmetatable(obj).__type.is(obj))");
lua.safe_script("assert(not getmetatable(obj).__type.is(1))");
lua.safe_script("assert(not getmetatable(obj).__type.is(\"not a thing\"))");
lua.safe_script("print(getmetatable(obj).__type.name)");
}

View File

@ -403,8 +403,11 @@ TEST_CASE("feature/indexing overrides", "make sure index functions can be overri
sol::state lua; sol::state lua;
lua.open_libraries(sol::lib::base); lua.open_libraries(sol::lib::base);
lua.new_usertype<PropertySet>("PropertySet", sol::meta_function::new_index, &PropertySet::set_property_lua, sol::meta_function::index, &PropertySet::get_property_lua); sol::usertype<PropertySet> utps = lua.new_usertype<PropertySet>("PropertySet");
lua.new_usertype<DynamicObject>("DynamicObject", "props", sol::property(&DynamicObject::get_dynamic_props)); utps[sol::meta_function::new_index] = &PropertySet::set_property_lua;
utps[sol::meta_function::index] = &PropertySet::get_property_lua;
sol::usertype<DynamicObject> utdo = lua.new_usertype<DynamicObject>("DynamicObject");
utdo["props"] = sol::property(&DynamicObject::get_dynamic_props);
lua.safe_script(R"__( lua.safe_script(R"__(
obj = DynamicObject:new() obj = DynamicObject:new()