Reworked the architecture of usertype functions to include a shim table between it and the actual metatables.

This means that usertype tables visible in the code and bound to the name specified with `new_usertype` and friends will be normal tables, and those tables delegate their responsibilities to the 3 backend tables of `sol.unique_usertype<T>`, `sol.T`, and `sol.T*`. This separation allows for a few caveats for updating and overriding members of `new_simple_usertype` tables to 'just work' without any serious hassle, and to have a single point that lets up decide how to perform updates properly. It also restores an optimization we had to turn off by ham-fisting whether ot not indexing was turned on the usertype metatables themselves.

o wow wow wow it's such a nice commit message look at how organized it is
PFFFFFFFFFF.
This commit is contained in:
ThePhD 2016-10-11 05:58:49 -04:00
parent 007fd19b8d
commit c2d219ea7d
6 changed files with 271 additions and 101 deletions

View File

@ -20,8 +20,8 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// This file was generated with a script. // This file was generated with a script.
// Generated 2016-10-06 00:53:22.017715 UTC // Generated 2016-10-11 09:41:28.136959 UTC
// This header was generated with sol v2.14.9 (revision 0ba4650) // This header was generated with sol v2.14.9 (revision 007fd19)
// https://github.com/ThePhD/sol2 // https://github.com/ThePhD/sol2
#ifndef SOL_SINGLE_INCLUDE_HPP #ifndef SOL_SINGLE_INCLUDE_HPP
@ -4524,6 +4524,7 @@ namespace sol {
static const std::string name; static const std::string name;
static const std::string qualified_name; static const std::string qualified_name;
static const std::string metatable; static const std::string metatable;
static const std::string user_metatable;
static const std::string user_gc_metatable; static const std::string user_gc_metatable;
static const std::string gc_table; static const std::string gc_table;
}; };
@ -4537,6 +4538,9 @@ namespace sol {
template<typename T> template<typename T>
const std::string usertype_traits<T>::metatable = std::string("sol.").append(detail::demangle<T>()); const std::string usertype_traits<T>::metatable = std::string("sol.").append(detail::demangle<T>());
template<typename T>
const std::string usertype_traits<T>::user_metatable = std::string("sol.").append(detail::demangle<T>()).append(".user");
template<typename T> template<typename T>
const std::string usertype_traits<T>::user_gc_metatable = std::string("sol.").append(detail::demangle<T>()).append(".user\xE2\x99\xBB"); const std::string usertype_traits<T>::user_gc_metatable = std::string("sol.").append(detail::demangle<T>()).append(".user\xE2\x99\xBB");
@ -7386,7 +7390,7 @@ namespace sol {
inline int construct(lua_State* L) { inline int construct(lua_State* L) {
static const auto& meta = usertype_traits<T>::metatable; static const auto& meta = usertype_traits<T>::metatable;
int argcount = lua_gettop(L); int argcount = lua_gettop(L);
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, meta, 1) : call_syntax::dot; call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, &usertype_traits<T>::user_metatable[0], 1) : call_syntax::dot;
argcount -= static_cast<int>(syntax); argcount -= static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T))); T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
@ -7624,7 +7628,7 @@ namespace sol {
static int call(lua_State* L, F&) { static int call(lua_State* L, F&) {
const auto& metakey = usertype_traits<T>::metatable; const auto& metakey = usertype_traits<T>::metatable;
int argcount = lua_gettop(L); int argcount = lua_gettop(L);
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, metakey, 1) : call_syntax::dot; call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, &usertype_traits<T>::user_metatable[0], 1) : call_syntax::dot;
argcount -= static_cast<int>(syntax); argcount -= static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T))); T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
@ -7679,7 +7683,7 @@ namespace sol {
}; };
static int call(lua_State* L, F& f) { static int call(lua_State* L, F& f) {
call_syntax syntax = stack::get_call_syntax(L, usertype_traits<T>::metatable); call_syntax syntax = stack::get_call_syntax(L, &usertype_traits<T>::user_metatable[0]);
int syntaxval = static_cast<int>(syntax); int syntaxval = static_cast<int>(syntax);
int argcount = lua_gettop(L) - syntaxval; int argcount = lua_gettop(L) - syntaxval;
return construct_match<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(onmatch(), L, argcount, 1 + syntaxval, f); return construct_match<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(onmatch(), L, argcount, 1 + syntaxval, f);
@ -10108,7 +10112,7 @@ namespace sol {
} }
// metatable on the metatable // metatable on the metatable
// for call constructor purposes and such // for call constructor purposes and such
lua_createtable(L, 0, 1); lua_createtable(L, 0, 3);
stack_reference metabehind(L, -1); stack_reference metabehind(L, -1);
if (um.callconstructfunc != nullptr) { if (um.callconstructfunc != nullptr) {
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index()); stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index());
@ -10121,9 +10125,26 @@ namespace sol {
metabehind.pop(); metabehind.pop();
// We want to just leave the table // We want to just leave the table
// in the registry only, otherwise we return it // in the registry only, otherwise we return it
if (i < 2) {
t.pop(); t.pop();
} }
// Now for the shim-table that actually gets assigned to the name
luaL_newmetatable(L, &usertype_traits<T>::user_metatable[0]);
stack_reference t(L, -1);
stack::push(L, make_light(um));
luaL_setfuncs(L, value_table.data(), 1);
{
lua_createtable(L, 0, 3);
stack_reference metabehind(L, -1);
if (um.callconstructfunc != nullptr) {
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index());
}
if (um.secondarymeta) {
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um)), metabehind.stack_index());
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um)), metabehind.stack_index());
}
stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop();
} }
return 1; return 1;
@ -10141,6 +10162,8 @@ namespace sol {
namespace sol { namespace sol {
namespace usertype_detail { namespace usertype_detail {
const lua_Integer toplevel_magic = static_cast<lua_Integer>(0x00000001);
struct variable_wrapper { struct variable_wrapper {
virtual int index(lua_State* L) = 0; virtual int index(lua_State* L) = 0;
virtual int new_index(lua_State* L) = 0; virtual int new_index(lua_State* L) = 0;
@ -10178,8 +10201,31 @@ namespace sol {
template <typename T> template <typename T>
inline int simple_metatable_newindex(lua_State* L) { inline int simple_metatable_newindex(lua_State* L) {
if (stack::stack_detail::check_metatable<T, false>(L, 1)) { int isnum = 0;
stack::set_field<false, true>(L, stack_reference(L, 2), stack_reference(L, 3), 1); lua_Integer magic = lua_tointegerx(L, lua_upvalueindex(4), &isnum);
if (isnum != 0 && magic == toplevel_magic) {
for (std::size_t i = 0; i < 3; lua_pop(L, 1), ++i) {
// Pointer types, AKA "references" from C++
const char* metakey = nullptr;
switch (i) {
case 0:
metakey = &usertype_traits<T*>::metatable[0];
break;
case 1:
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable[0];
break;
case 2:
default:
metakey = &usertype_traits<T>::metatable[0];
break;
}
luaL_getmetatable(L, metakey);
int tableindex = lua_gettop(L);
if (type_of(L, tableindex) == type::nil) {
continue;
}
stack::set_field<false, true>(L, stack_reference(L, 2), stack_reference(L, 3), tableindex);
}
lua_settop(L, 0); lua_settop(L, 0);
return 0; return 0;
} }
@ -10369,15 +10415,15 @@ namespace sol {
template <typename... Bases> template <typename... Bases>
void add(lua_State*, base_classes_tag, bases<Bases...>) { void add(lua_State*, base_classes_tag, bases<Bases...>) {
static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "size of function pointer is greater than sizeof(void*); cannot work on this platform"); static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "size of function pointer is greater than sizeof(void*); cannot work on this platform. Please file a bug report.");
if (sizeof...(Bases) < 1) { if (sizeof...(Bases) < 1) {
return; return;
} }
mustindex = true; mustindex = true;
(void)detail::swallow{ 0, ((detail::has_derived<Bases>::value = true), 0)... }; (void)detail::swallow{ 0, ((detail::has_derived<Bases>::value = true), 0)... };
static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report.");
static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report.");
baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check; baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check;
baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast; baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast;
indexbaseclasspropogation = usertype_detail::walk_all_bases<true, Bases...>; indexbaseclasspropogation = usertype_detail::walk_all_bases<true, Bases...>;
@ -10388,11 +10434,11 @@ namespace sol {
template<std::size_t... I, typename Tuple> template<std::size_t... I, typename Tuple>
simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args) simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args)
: callconstructfunc(nil), : callconstructfunc(nil),
indexfunc(&usertype_detail::indexing_fail<true>), newindexfunc(&usertype_detail::simple_metatable_newindex<T>), 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>), 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>), indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(&usertype_detail::walk_all_bases<false>),
baseclasscheck(nullptr), baseclasscast(nullptr), baseclasscheck(nullptr), baseclasscast(nullptr),
mustindex(true), secondarymeta(true) { mustindex(false), secondarymeta(false) {
(void)detail::swallow{ 0, (void)detail::swallow{ 0,
(add(L, detail::forward_get<I * 2>(args), detail::forward_get<I * 2 + 1>(args)),0)... (add(L, detail::forward_get<I * 2>(args), detail::forward_get<I * 2 + 1>(args)),0)...
}; };
@ -10466,6 +10512,40 @@ namespace sol {
bool hasequals = false; bool hasequals = false;
bool hasless = false; bool hasless = false;
bool haslessequals = false; bool haslessequals = false;
auto register_kvp = [&](std::size_t i, stack_reference& t, const std::string& first, object& second) {
if (first == name_of(meta_function::equal_to)) {
hasequals = true;
}
else if (first == name_of(meta_function::less_than)) {
hasless = true;
}
else if (first == name_of(meta_function::less_than_or_equal_to)) {
haslessequals = true;
}
else if (first == name_of(meta_function::index)) {
umx.indexfunc = second.template as<lua_CFunction>();
}
else if (first == name_of(meta_function::new_index)) {
umx.newindexfunc = second.template as<lua_CFunction>();
}
switch (i) {
case 0:
if (first == name_of(meta_function::garbage_collect)) {
return;
}
break;
case 1:
if (first == name_of(meta_function::garbage_collect)) {
stack::set_field(L, first, detail::unique_destruct<T>, t.stack_index());
return;
}
break;
case 2:
default:
break;
}
stack::set_field(L, first, second, t.stack_index());
};
for (std::size_t i = 0; i < 3; ++i) { for (std::size_t i = 0; i < 3; ++i) {
// Pointer types, AKA "references" from C++ // Pointer types, AKA "references" from C++
const char* metakey = nullptr; const char* metakey = nullptr;
@ -10486,38 +10566,7 @@ namespace sol {
for (auto& kvp : varmap.functions) { for (auto& kvp : varmap.functions) {
auto& first = std::get<0>(kvp); auto& first = std::get<0>(kvp);
auto& second = std::get<1>(kvp); auto& second = std::get<1>(kvp);
if (first == name_of(meta_function::equal_to)) { register_kvp(i, t, first, second);
hasequals = true;
}
else if (first == name_of(meta_function::less_than)) {
hasless = true;
}
else if (first == name_of(meta_function::less_than_or_equal_to)) {
haslessequals = true;
}
else if (first == name_of(meta_function::index)) {
umx.indexfunc = second.template as<lua_CFunction>();
}
else if (first == name_of(meta_function::new_index)) {
umx.newindexfunc = second.template as<lua_CFunction>();
}
switch (i) {
case 0:
if (first == name_of(meta_function::garbage_collect)) {
continue;
}
break;
case 1:
if (first == name_of(meta_function::garbage_collect)) {
stack::set_field(L, first, detail::unique_destruct<T>, t.stack_index());
continue;
}
break;
case 2:
default:
break;
}
stack::set_field(L, first, second, t.stack_index());
} }
luaL_Reg opregs[4]{}; luaL_Reg opregs[4]{};
int opregsindex = 0; int opregsindex = 0;
@ -10550,7 +10599,6 @@ namespace sol {
if (umx.mustindex) { if (umx.mustindex) {
// use indexing function // use indexing function
static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "The size of this data pointer is too small to fit the base class index propagation key: file a bug report.");
stack::set_field(L, meta_function::index, stack::set_field(L, meta_function::index,
make_closure(&usertype_detail::simple_index_call, make_closure(&usertype_detail::simple_index_call,
make_light(varmap), make_light(varmap),
@ -10570,7 +10618,7 @@ namespace sol {
} }
// metatable on the metatable // metatable on the metatable
// for call constructor purposes and such // for call constructor purposes and such
lua_createtable(L, 0, 1); lua_createtable(L, 0, 2 * static_cast<int>(umx.secondarymeta) + static_cast<int>(umx.callconstructfunc.valid()));
stack_reference metabehind(L, -1); stack_reference metabehind(L, -1);
if (umx.callconstructfunc.valid()) { if (umx.callconstructfunc.valid()) {
stack::set_field(L, sol::meta_function::call_function, umx.callconstructfunc, metabehind.stack_index()); stack::set_field(L, sol::meta_function::call_function, umx.callconstructfunc, metabehind.stack_index());
@ -10592,9 +10640,44 @@ namespace sol {
stack::set_field(L, metatable_key, metabehind, t.stack_index()); stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop(); metabehind.pop();
if (i < 2)
t.pop(); t.pop();
} }
// Now for the shim-table that actually gets pushed
luaL_newmetatable(L, &usertype_traits<T>::user_metatable[0]);
stack_reference t(L, -1);
for (auto& kvp : varmap.functions) {
auto& first = std::get<0>(kvp);
auto& second = std::get<1>(kvp);
register_kvp(2, t, first, second);
}
{
lua_createtable(L, 0, 2 + static_cast<int>(umx.callconstructfunc.valid()));
stack_reference metabehind(L, -1);
if (umx.callconstructfunc.valid()) {
stack::set_field(L, sol::meta_function::call_function, umx.callconstructfunc, metabehind.stack_index());
}
// use indexing function
stack::set_field(L, meta_function::index,
make_closure(&usertype_detail::simple_index_call,
make_light(varmap),
&usertype_detail::simple_index_call,
&usertype_detail::simple_metatable_newindex<T>,
usertype_detail::toplevel_magic
), metabehind.stack_index());
stack::set_field(L, meta_function::new_index,
make_closure(&usertype_detail::simple_new_index_call,
make_light(varmap),
&usertype_detail::simple_index_call,
&usertype_detail::simple_metatable_newindex<T>,
usertype_detail::toplevel_magic
), metabehind.stack_index());
stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop();
}
// Don't pop the table when we're done;
// return it
return 1; return 1;
} }
}; };

View File

@ -108,7 +108,7 @@ namespace sol {
inline int construct(lua_State* L) { inline int construct(lua_State* L) {
static const auto& meta = usertype_traits<T>::metatable; static const auto& meta = usertype_traits<T>::metatable;
int argcount = lua_gettop(L); int argcount = lua_gettop(L);
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, meta, 1) : call_syntax::dot; call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, &usertype_traits<T>::user_metatable[0], 1) : call_syntax::dot;
argcount -= static_cast<int>(syntax); argcount -= static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T))); T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
@ -346,7 +346,7 @@ namespace sol {
static int call(lua_State* L, F&) { static int call(lua_State* L, F&) {
const auto& metakey = usertype_traits<T>::metatable; const auto& metakey = usertype_traits<T>::metatable;
int argcount = lua_gettop(L); int argcount = lua_gettop(L);
call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, metakey, 1) : call_syntax::dot; call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, &usertype_traits<T>::user_metatable[0], 1) : call_syntax::dot;
argcount -= static_cast<int>(syntax); argcount -= static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T))); T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
@ -401,7 +401,7 @@ namespace sol {
}; };
static int call(lua_State* L, F& f) { static int call(lua_State* L, F& f) {
call_syntax syntax = stack::get_call_syntax(L, usertype_traits<T>::metatable); call_syntax syntax = stack::get_call_syntax(L, &usertype_traits<T>::user_metatable[0]);
int syntaxval = static_cast<int>(syntax); int syntaxval = static_cast<int>(syntax);
int argcount = lua_gettop(L) - syntaxval; int argcount = lua_gettop(L) - syntaxval;
return construct_match<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(onmatch(), L, argcount, 1 + syntaxval, f); return construct_match<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(onmatch(), L, argcount, 1 + syntaxval, f);

View File

@ -31,6 +31,8 @@
namespace sol { namespace sol {
namespace usertype_detail { namespace usertype_detail {
const lua_Integer toplevel_magic = static_cast<lua_Integer>(0x00000001);
struct variable_wrapper { struct variable_wrapper {
virtual int index(lua_State* L) = 0; virtual int index(lua_State* L) = 0;
virtual int new_index(lua_State* L) = 0; virtual int new_index(lua_State* L) = 0;
@ -68,8 +70,31 @@ namespace sol {
template <typename T> template <typename T>
inline int simple_metatable_newindex(lua_State* L) { inline int simple_metatable_newindex(lua_State* L) {
if (stack::stack_detail::check_metatable<T, false>(L, 1)) { int isnum = 0;
stack::set_field<false, true>(L, stack_reference(L, 2), stack_reference(L, 3), 1); lua_Integer magic = lua_tointegerx(L, lua_upvalueindex(4), &isnum);
if (isnum != 0 && magic == toplevel_magic) {
for (std::size_t i = 0; i < 3; lua_pop(L, 1), ++i) {
// Pointer types, AKA "references" from C++
const char* metakey = nullptr;
switch (i) {
case 0:
metakey = &usertype_traits<T*>::metatable[0];
break;
case 1:
metakey = &usertype_traits<detail::unique_usertype<T>>::metatable[0];
break;
case 2:
default:
metakey = &usertype_traits<T>::metatable[0];
break;
}
luaL_getmetatable(L, metakey);
int tableindex = lua_gettop(L);
if (type_of(L, tableindex) == type::nil) {
continue;
}
stack::set_field<false, true>(L, stack_reference(L, 2), stack_reference(L, 3), tableindex);
}
lua_settop(L, 0); lua_settop(L, 0);
return 0; return 0;
} }
@ -259,15 +284,15 @@ namespace sol {
template <typename... Bases> template <typename... Bases>
void add(lua_State*, base_classes_tag, bases<Bases...>) { void add(lua_State*, base_classes_tag, bases<Bases...>) {
static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "size of function pointer is greater than sizeof(void*); cannot work on this platform"); static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "size of function pointer is greater than sizeof(void*); cannot work on this platform. Please file a bug report.");
if (sizeof...(Bases) < 1) { if (sizeof...(Bases) < 1) {
return; return;
} }
mustindex = true; mustindex = true;
(void)detail::swallow{ 0, ((detail::has_derived<Bases>::value = true), 0)... }; (void)detail::swallow{ 0, ((detail::has_derived<Bases>::value = true), 0)... };
static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report.");
static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: file a bug report."); static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function), "The size of this data pointer is too small to fit the inheritance checking function: Please file a bug report.");
baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check; baseclasscheck = (void*)&detail::inheritance<T, Bases...>::type_check;
baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast; baseclasscast = (void*)&detail::inheritance<T, Bases...>::type_cast;
indexbaseclasspropogation = usertype_detail::walk_all_bases<true, Bases...>; indexbaseclasspropogation = usertype_detail::walk_all_bases<true, Bases...>;
@ -278,11 +303,11 @@ namespace sol {
template<std::size_t... I, typename Tuple> template<std::size_t... I, typename Tuple>
simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args) simple_usertype_metatable(usertype_detail::verified_tag, std::index_sequence<I...>, lua_State* L, Tuple&& args)
: callconstructfunc(nil), : callconstructfunc(nil),
indexfunc(&usertype_detail::indexing_fail<true>), newindexfunc(&usertype_detail::simple_metatable_newindex<T>), 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>), 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>), indexbaseclasspropogation(usertype_detail::walk_all_bases<true>), newindexbaseclasspropogation(&usertype_detail::walk_all_bases<false>),
baseclasscheck(nullptr), baseclasscast(nullptr), baseclasscheck(nullptr), baseclasscast(nullptr),
mustindex(true), secondarymeta(true) { mustindex(false), secondarymeta(false) {
(void)detail::swallow{ 0, (void)detail::swallow{ 0,
(add(L, detail::forward_get<I * 2>(args), detail::forward_get<I * 2 + 1>(args)),0)... (add(L, detail::forward_get<I * 2>(args), detail::forward_get<I * 2 + 1>(args)),0)...
}; };
@ -356,6 +381,40 @@ namespace sol {
bool hasequals = false; bool hasequals = false;
bool hasless = false; bool hasless = false;
bool haslessequals = false; bool haslessequals = false;
auto register_kvp = [&](std::size_t i, stack_reference& t, const std::string& first, object& second) {
if (first == name_of(meta_function::equal_to)) {
hasequals = true;
}
else if (first == name_of(meta_function::less_than)) {
hasless = true;
}
else if (first == name_of(meta_function::less_than_or_equal_to)) {
haslessequals = true;
}
else if (first == name_of(meta_function::index)) {
umx.indexfunc = second.template as<lua_CFunction>();
}
else if (first == name_of(meta_function::new_index)) {
umx.newindexfunc = second.template as<lua_CFunction>();
}
switch (i) {
case 0:
if (first == name_of(meta_function::garbage_collect)) {
return;
}
break;
case 1:
if (first == name_of(meta_function::garbage_collect)) {
stack::set_field(L, first, detail::unique_destruct<T>, t.stack_index());
return;
}
break;
case 2:
default:
break;
}
stack::set_field(L, first, second, t.stack_index());
};
for (std::size_t i = 0; i < 3; ++i) { for (std::size_t i = 0; i < 3; ++i) {
// Pointer types, AKA "references" from C++ // Pointer types, AKA "references" from C++
const char* metakey = nullptr; const char* metakey = nullptr;
@ -376,38 +435,7 @@ namespace sol {
for (auto& kvp : varmap.functions) { for (auto& kvp : varmap.functions) {
auto& first = std::get<0>(kvp); auto& first = std::get<0>(kvp);
auto& second = std::get<1>(kvp); auto& second = std::get<1>(kvp);
if (first == name_of(meta_function::equal_to)) { register_kvp(i, t, first, second);
hasequals = true;
}
else if (first == name_of(meta_function::less_than)) {
hasless = true;
}
else if (first == name_of(meta_function::less_than_or_equal_to)) {
haslessequals = true;
}
else if (first == name_of(meta_function::index)) {
umx.indexfunc = second.template as<lua_CFunction>();
}
else if (first == name_of(meta_function::new_index)) {
umx.newindexfunc = second.template as<lua_CFunction>();
}
switch (i) {
case 0:
if (first == name_of(meta_function::garbage_collect)) {
continue;
}
break;
case 1:
if (first == name_of(meta_function::garbage_collect)) {
stack::set_field(L, first, detail::unique_destruct<T>, t.stack_index());
continue;
}
break;
case 2:
default:
break;
}
stack::set_field(L, first, second, t.stack_index());
} }
luaL_Reg opregs[4]{}; luaL_Reg opregs[4]{};
int opregsindex = 0; int opregsindex = 0;
@ -440,7 +468,6 @@ namespace sol {
if (umx.mustindex) { if (umx.mustindex) {
// use indexing function // use indexing function
static_assert(sizeof(usertype_detail::base_walk) <= sizeof(void*), "The size of this data pointer is too small to fit the base class index propagation key: file a bug report.");
stack::set_field(L, meta_function::index, stack::set_field(L, meta_function::index,
make_closure(&usertype_detail::simple_index_call, make_closure(&usertype_detail::simple_index_call,
make_light(varmap), make_light(varmap),
@ -460,7 +487,7 @@ namespace sol {
} }
// metatable on the metatable // metatable on the metatable
// for call constructor purposes and such // for call constructor purposes and such
lua_createtable(L, 0, 1); lua_createtable(L, 0, 2 * static_cast<int>(umx.secondarymeta) + static_cast<int>(umx.callconstructfunc.valid()));
stack_reference metabehind(L, -1); stack_reference metabehind(L, -1);
if (umx.callconstructfunc.valid()) { if (umx.callconstructfunc.valid()) {
stack::set_field(L, sol::meta_function::call_function, umx.callconstructfunc, metabehind.stack_index()); stack::set_field(L, sol::meta_function::call_function, umx.callconstructfunc, metabehind.stack_index());
@ -482,9 +509,44 @@ namespace sol {
stack::set_field(L, metatable_key, metabehind, t.stack_index()); stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop(); metabehind.pop();
if (i < 2)
t.pop(); t.pop();
} }
// Now for the shim-table that actually gets pushed
luaL_newmetatable(L, &usertype_traits<T>::user_metatable[0]);
stack_reference t(L, -1);
for (auto& kvp : varmap.functions) {
auto& first = std::get<0>(kvp);
auto& second = std::get<1>(kvp);
register_kvp(2, t, first, second);
}
{
lua_createtable(L, 0, 2 + static_cast<int>(umx.callconstructfunc.valid()));
stack_reference metabehind(L, -1);
if (umx.callconstructfunc.valid()) {
stack::set_field(L, sol::meta_function::call_function, umx.callconstructfunc, metabehind.stack_index());
}
// use indexing function
stack::set_field(L, meta_function::index,
make_closure(&usertype_detail::simple_index_call,
make_light(varmap),
&usertype_detail::simple_index_call,
&usertype_detail::simple_metatable_newindex<T>,
usertype_detail::toplevel_magic
), metabehind.stack_index());
stack::set_field(L, meta_function::new_index,
make_closure(&usertype_detail::simple_new_index_call,
make_light(varmap),
&usertype_detail::simple_index_call,
&usertype_detail::simple_metatable_newindex<T>,
usertype_detail::toplevel_magic
), metabehind.stack_index());
stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop();
}
// Don't pop the table when we're done;
// return it
return 1; return 1;
} }
}; };

View File

@ -554,7 +554,7 @@ namespace sol {
} }
// metatable on the metatable // metatable on the metatable
// for call constructor purposes and such // for call constructor purposes and such
lua_createtable(L, 0, 1); lua_createtable(L, 0, 3);
stack_reference metabehind(L, -1); stack_reference metabehind(L, -1);
if (um.callconstructfunc != nullptr) { if (um.callconstructfunc != nullptr) {
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index()); stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index());
@ -567,9 +567,26 @@ namespace sol {
metabehind.pop(); metabehind.pop();
// We want to just leave the table // We want to just leave the table
// in the registry only, otherwise we return it // in the registry only, otherwise we return it
if (i < 2) {
t.pop(); t.pop();
} }
// Now for the shim-table that actually gets assigned to the name
luaL_newmetatable(L, &usertype_traits<T>::user_metatable[0]);
stack_reference t(L, -1);
stack::push(L, make_light(um));
luaL_setfuncs(L, value_table.data(), 1);
{
lua_createtable(L, 0, 3);
stack_reference metabehind(L, -1);
if (um.callconstructfunc != nullptr) {
stack::set_field(L, meta_function::call_function, make_closure(um.callconstructfunc, make_light(um)), metabehind.stack_index());
}
if (um.secondarymeta) {
stack::set_field(L, meta_function::index, make_closure(umt_t::index_call, make_light(um)), metabehind.stack_index());
stack::set_field(L, meta_function::new_index, make_closure(umt_t::new_index_call, make_light(um)), metabehind.stack_index());
}
stack::set_field(L, metatable_key, metabehind, t.stack_index());
metabehind.pop();
} }
return 1; return 1;

View File

@ -31,6 +31,7 @@ namespace sol {
static const std::string name; static const std::string name;
static const std::string qualified_name; static const std::string qualified_name;
static const std::string metatable; static const std::string metatable;
static const std::string user_metatable;
static const std::string user_gc_metatable; static const std::string user_gc_metatable;
static const std::string gc_table; static const std::string gc_table;
}; };
@ -44,6 +45,9 @@ namespace sol {
template<typename T> template<typename T>
const std::string usertype_traits<T>::metatable = std::string("sol.").append(detail::demangle<T>()); const std::string usertype_traits<T>::metatable = std::string("sol.").append(detail::demangle<T>());
template<typename T>
const std::string usertype_traits<T>::user_metatable = std::string("sol.").append(detail::demangle<T>()).append(".user");
template<typename T> template<typename T>
const std::string usertype_traits<T>::user_gc_metatable = std::string("sol.").append(detail::demangle<T>()).append(".user\xE2\x99\xBB"); const std::string usertype_traits<T>::user_gc_metatable = std::string("sol.").append(detail::demangle<T>()).append(".user\xE2\x99\xBB");

View File

@ -453,7 +453,11 @@ TEST_CASE("usertype/simple-table-append", "Ensure that appending to the meta tab
table["func"] = &A::func; table["func"] = &A::func;
A a; A a;
lua.set("a", &a); lua.set("a", &a);
lua.set("pa", &a);
lua.set("ua", std::make_unique<A>());
REQUIRE_NOTHROW( REQUIRE_NOTHROW(
lua.script("assert(a:func() == 5000)") lua.script("assert(a:func() == 5000)");
lua.script("assert(pa:func() == 5000)");
lua.script("assert(ua:func() == 5000)");
); );
} }