this commit adds support for differentiating between multiple different unique_usertypes without actually having to store type information. It does not include the ability to override a unique_usertype label for a specific type, but that might not prove necessary if differentiation remains possible with this methodology

Complete fixes #422 and addresses #423
This commit is contained in:
ThePhD 2017-06-15 01:23:51 -04:00
parent 85620df63c
commit 0fb53335c4
6 changed files with 104 additions and 42 deletions

View File

@ -429,8 +429,16 @@ namespace sol {
return true;
}
int metatableindex = lua_gettop(L);
if (stack_detail::check_metatable<detail::unique_usertype<T>>(L, metatableindex))
return true;
if (stack_detail::check_metatable<detail::unique_usertype<T>>(L, metatableindex)) {
void* memory = lua_touserdata(L, 1);
T** pointerpointer = static_cast<T**>(memory);
detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(static_cast<void*>(pointerpointer + 1));
bool success = &detail::usertype_unique_alloc_destroy<T, X> == pdx;
if (!success) {
handler(L, index, type::userdata, indextype);
}
return success;
}
lua_pop(L, 1);
handler(L, index, type::userdata, indextype);
return false;

View File

@ -40,25 +40,26 @@ namespace sol {
template <typename T>
struct as_value_tag {};
using special_destruct_func = void(*)(void*);
template <typename T, typename Real>
inline void special_destruct(void* memory) {
T** pointerpointer = static_cast<T**>(memory);
special_destruct_func* dx = static_cast<special_destruct_func*>(static_cast<void*>(pointerpointer + 1));
Real* target = static_cast<Real*>(static_cast<void*>(dx + 1));
target->~Real();
}
using unique_destructor = void(*)(void*);
template <typename T>
inline int unique_destruct(lua_State* L) {
void* memory = lua_touserdata(L, 1);
T** pointerpointer = static_cast<T**>(memory);
special_destruct_func& dx = *static_cast<special_destruct_func*>(static_cast<void*>(pointerpointer + 1));
unique_destructor& dx = *static_cast<unique_destructor*>(static_cast<void*>(pointerpointer + 1));
(dx)(memory);
return 0;
}
template <typename T, typename Real>
inline void usertype_unique_alloc_destroy(void* memory) {
T** pointerpointer = static_cast<T**>(memory);
unique_destructor* dx = static_cast<unique_destructor*>(static_cast<void*>(pointerpointer + 1));
Real* target = static_cast<Real*>(static_cast<void*>(dx + 1));
std::allocator<Real> alloc;
alloc.destroy(target);
}
template <typename T>
inline int user_alloc_destroy(lua_State* L) {
void* rawdata = lua_touserdata(L, 1);

View File

@ -567,7 +567,7 @@ namespace sol {
static Real& get(lua_State* L, int index, record& tracking) {
tracking.use(1);
P** pref = static_cast<P**>(lua_touserdata(L, index));
detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1));
detail::unique_destructor* fx = static_cast<detail::unique_destructor*>(static_cast<void*>(pref + 1));
Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1));
return *mem;
}

View File

@ -153,10 +153,10 @@ namespace sol {
template <typename... Args>
static int push_deep(lua_State* L, Args&&... args) {
P** pref = static_cast<P**>(lua_newuserdata(L, sizeof(P*) + sizeof(detail::special_destruct_func) + sizeof(Real)));
detail::special_destruct_func* fx = static_cast<detail::special_destruct_func*>(static_cast<void*>(pref + 1));
P** pref = static_cast<P**>(lua_newuserdata(L, sizeof(P*) + sizeof(detail::unique_destructor) + sizeof(Real)));
detail::unique_destructor* fx = static_cast<detail::unique_destructor*>(static_cast<void*>(pref + 1));
Real* mem = static_cast<Real*>(static_cast<void*>(fx + 1));
*fx = detail::special_destruct<P, Real>;
*fx = detail::usertype_unique_alloc_destroy<P, Real>;
detail::default_construct::construct(mem, std::forward<Args>(args)...);
*pref = unique_usertype_traits<T>::get(*mem);
if (luaL_newmetatable(L, &usertype_traits<detail::unique_usertype<P>>::metatable()[0]) == 1) {

View File

@ -746,8 +746,8 @@ namespace sol {
bool hasdestructor = !value_table.empty() && to_string(meta_function::garbage_collect) == value_table[lastreg - 1].name;
if (hasdestructor) {
ref_table[lastreg - 1] = { nullptr, nullptr };
unique_table[lastreg - 1] = { value_table[lastreg - 1].name, detail::unique_destruct<T> };
}
unique_table[lastreg - 1] = { value_table[lastreg - 1].name, detail::unique_destruct<T> };
// Now use um
const bool& mustindex = umc.mustindex;

View File

@ -1137,35 +1137,88 @@ TEST_CASE("functions/set_function-already-wrapped", "setting a function returned
}
TEST_CASE("functions/unique-overloading", "make sure overloading can work with ptr vs. specifically asking for a unique usertype") {
sol::state lua;
struct test { int special_value = 17; };
auto print_up_test = [](std::unique_ptr<test>& x) {
REQUIRE(x->special_value == 17);
struct test {
int special_value = 17;
test() : special_value(17) {}
test(int special_value) : special_value(special_value) {}
};
auto print_up_test = [](std::unique_ptr<test>& x) {
REQUIRE(x->special_value == 21);
};
auto print_sp_test = [](std::shared_ptr<test>& x) {
REQUIRE(x->special_value == 44);
};
auto print_ptr_test = [](test* x) {
REQUIRE(x->special_value == 17);
};
auto print_ref_test = [](test& x) {
bool is_any = x.special_value == 17
|| x.special_value == 21
|| x. special_value == 44;
REQUIRE(is_any);
};
using f_t = void(test&);
f_t* fptr = print_ref_test;
std::unique_ptr<test> ut = std::make_unique<test>(17);
SECTION("working") {
sol::state lua;
lua.set_function("f", print_up_test);
lua.set_function("g", sol::overload(
std::ref(print_up_test),
print_ptr_test
));
lua.set_function("f", print_up_test);
lua.set_function("g", sol::overload(
std::move(print_sp_test),
print_up_test,
std::ref(print_ptr_test)
));
lua.set_function("h", std::ref(fptr));
lua["v1"] = std::make_unique<test>();
lua["v2"] = test{};
REQUIRE_NOTHROW([&]() {
lua.script("g(v1)");
}());
REQUIRE_NOTHROW([&]() {
lua.script("g(v2)");
}());
REQUIRE_NOTHROW([&]() {
lua.script("f(v1)");
}());
REQUIRE_THROWS([&]() {
lua.script("f(v2)");
}());
lua["v1"] = std::make_unique<test>(21);
lua["v2"] = std::make_shared<test>(44);
lua["v3"] = test(17);
lua["v4"] = ut.get();
REQUIRE_NOTHROW([&]() {
lua.script("f(v1)");
lua.script("g(v1)");
lua.script("g(v2)");
lua.script("g(v3)");
lua.script("g(v4)");
lua.script("h(v1)");
lua.script("h(v2)");
lua.script("h(v3)");
lua.script("h(v4)");
}());
};
// LuaJIT segfaults hard on some Linux machines
// and it breaks all the tests...
SECTION("throws-value") {
sol::state lua;
lua.set_function("f", print_up_test);
lua["v3"] = test(17);
REQUIRE_THROWS([&]() {
lua.script("f(v3)");
}());
};
SECTION("throws-shared_ptr") {
sol::state lua;
lua.set_function("f", print_up_test);
lua["v2"] = std::make_shared<test>(44);
REQUIRE_THROWS([&]() {
lua.script("f(v2)");
}());
};
SECTION("throws-ptr") {
sol::state lua;
lua.set_function("f", print_up_test);
lua["v4"] = ut.get();
REQUIRE_THROWS([&]() {
lua.script("f(v4)");
}());
};
}