From b66c7f015ad4dbba2c4fba9b3c6414012df64236 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Mon, 1 Feb 2016 11:56:44 -0500 Subject: [PATCH] Significant change to how userdata is stored to make access consistent across values/references/pointers. --- sol/function.hpp | 4 +-- sol/function_types.hpp | 2 +- sol/stack.hpp | 62 +++++++++++++++++++------------------ sol/state_view.hpp | 4 +-- sol/table_core.hpp | 69 ++++++++++++++++++++++++------------------ sol/traits.hpp | 3 ++ sol/usertype.hpp | 10 ++++-- tests.cpp | 30 +++++++++++++----- 8 files changed, 110 insertions(+), 74 deletions(-) diff --git a/sol/function.hpp b/sol/function.hpp index 4c93be9e..57831171 100644 --- a/sol/function.hpp +++ b/sol/function.hpp @@ -66,8 +66,8 @@ private: int stacksize = lua_gettop( lua_state( ) ); int firstreturn = std::max( 1, stacksize - static_cast( n ) ); luacall( n, LUA_MULTRET ); - int poststacksize = lua_gettop( lua_state( ) ); - int returncount = poststacksize - firstreturn; + int poststacksize = lua_gettop( lua_state( ) ); + int returncount = poststacksize - firstreturn; return function_result( lua_state( ), firstreturn, returncount, returncount, call_error::ok ); } diff --git a/sol/function_types.hpp b/sol/function_types.hpp index f9b6a69c..1490a60e 100644 --- a/sol/function_types.hpp +++ b/sol/function_types.hpp @@ -278,7 +278,7 @@ struct base_function { struct usertype { static int call(lua_State* L) { // Zero-based template parameter, but upvalues start at 1 - return base_call(L, stack::get(L, I + 1)); + return ref_base_call(L, stack::get(L, I + 1)); } static int ref_call(lua_State* L) { diff --git a/sol/stack.hpp b/sol/stack.hpp index c4bca4b6..10366409 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -133,36 +133,42 @@ true; false; #endif -inline void lua_getglobali(lua_State* L, lua_Integer n) { -#if SOL_LUA_VERSION >= 503 - lua_geti(L, LUA_RIDX_GLOBALS, n); -#else - lua_pushglobaltable(L); - lua_pushinteger(L, n); - lua_gettable(L, -2); - lua_remove(L, -2); // remove the global table, leave final value on the stack -#endif -} +template +struct userdata_pusher { + template + static void push (lua_State* L, Key&& metatablekey, Args&&... args) { + // Basically, we store all data like this: + // If it's a new value (no std::ref(x)), then we store the pointer to the new + // data in the first sizeof(T*) bytes, and then however many bytes it takes to + // do the actual object. Things that are just references/pointers are stored as + // just the sizeof(T*), and nothing else. + T** pdatum = static_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T** referencepointer = pdatum; + T*& referencereference = *pdatum; + T* allocationtarget = reinterpret_cast(pdatum + 1); + referencereference = allocationtarget; + std::allocator alloc{}; + alloc.construct(allocationtarget, std::forward(args)...); + luaL_getmetatable(L, std::addressof(metatablekey[0])); + lua_setmetatable(L, -2); + } +}; -inline void lua_setglobali(lua_State* L, lua_Integer n) { -#if SOL_LUA_VERSION >= 503 - lua_seti(L, LUA_RIDX_GLOBALS, n); -#else - lua_pushglobaltable(L); - lua_pushinteger(L, n); - lua_pushvalue(L, -3); - lua_settable(L, -3); - lua_pop(L, 2); // remove table, and the copy of the value -#endif -} +template +struct userdata_pusher { + template + static void push (lua_State* L, Key&& metatablekey, Args&&... args) { + T** pdatum = static_cast(lua_newuserdata(L, sizeof(T*))); + std::allocator alloc{}; + alloc.construct(pdatum, std::forward(args)...); + luaL_getmetatable(L, std::addressof(metatablekey[0])); + lua_setmetatable(L, -2); + } +}; template inline int push_confirmed_userdata(lua_State* L, Key&& metatablekey, Args&&... args) { - T* pdatum = static_cast(lua_newuserdata(L, sizeof(T))); - std::allocator alloc{}; - alloc.construct(pdatum, std::forward(args)...); - luaL_getmetatable(L, std::addressof(metatablekey[0])); - lua_setmetatable(L, -2); + userdata_pusher{}.push(L, std::forward(metatablekey), std::forward(args)...); return 1; } @@ -253,9 +259,7 @@ struct getter { template>, Not>, Not>> = 0> static U& get(lua_State* L, int index = -1) { - void* udata = lua_touserdata(L, index); - T* obj = static_cast(udata); - return *obj; + return getter{}.get(L, index); } }; diff --git a/sol/state_view.hpp b/sol/state_view.hpp index 0a6827e6..d2d54003 100644 --- a/sol/state_view.hpp +++ b/sol/state_view.hpp @@ -218,8 +218,8 @@ public: table create_table(int narr = 0, int nrec = sizeof...(Tn), Tn&&... argn) { lua_createtable(L, narr, nrec); table result(L); - result.set(std::forward(argn)...); - lua_pop(L, 1); + result.set(std::forward(argn)...); + lua_pop(L, 1); return result; } diff --git a/sol/table_core.hpp b/sol/table_core.hpp index da0bd50b..a79e65e1 100644 --- a/sol/table_core.hpp +++ b/sol/table_core.hpp @@ -37,19 +37,20 @@ class table_core : public reference { friend class state; friend class state_view; - template>>, Bool> = 0> + template, is_c_str> = 0> decltype(auto) single_get( Key&& key ) const { lua_getglobal(lua_state( ), &key[0]); return stack::pop(lua_state()); } - template>, Bool> = 0> - decltype(auto) single_get(Key&& key) const { - stack::detail::lua_getglobali(lua_state(), &key[0]); - return stack::pop(lua_state()); + template>, is_c_str> = 0> + decltype(auto) single_get( Key&& key ) const { + auto pp = stack::push_popper(*this); + lua_getfield( lua_state( ), -2, &key[0] ); + return stack::pop( lua_state( ) ); } - template, Not>> = 0> + template>> = 0> decltype(auto) single_get( Key&& key ) const { auto pp = stack::push_popper(*this); stack::push( lua_state( ), std::forward( key ) ); @@ -57,6 +58,27 @@ class table_core : public reference { return stack::pop( lua_state( ) ); } + template, is_c_str> = 0> + void single_set( Key&& key, Value&& value ) { + stack::push( lua_state( ), std::forward( value ) ); + lua_setglobal( lua_state( ), &key[0] ); + } + + template>, is_c_str> = 0> + void single_set(Key&& key, Value&& value) { + auto pp = stack::push_popper(*this); + stack::push(lua_state(), std::forward(value)); + lua_setfield(lua_state(), -3, &key[0]); + } + + template>> = 0> + void single_set(Key&& key, Value&& value) { + auto pp = stack::push_popper(*this); + stack::push(lua_state(), std::forward(key)); + stack::push(lua_state(), std::forward(value)); + lua_settable(lua_state(), -3); + } + template stack::get_return tuple_get( types, indices, Keys&& keys ) const { return stack::get_return( single_get( std::get( keys ) )... ); @@ -67,26 +89,6 @@ class table_core : public reference { return single_get( std::get( keys ) ); } - template>, Bool> = 0> - void single_set( Key&& key, U&& value ) { - stack::push( lua_state( ), std::forward( value ) ); - stack::detail::lua_setglobali( lua_state( ), std::forward(key) ); - } - - template>>, Bool> = 0> - void single_set( Key&& key, Value&& value ) { - stack::push( lua_state( ), std::forward( value ) ); - lua_setglobal( lua_state( ), &key[0] ); - } - - template, Not>> = 0> - void single_set(Key&& key, Value&& value) { - auto pp = stack::push_popper(*this); - stack::push(lua_state(), std::forward(key)); - stack::push(lua_state(), std::forward(value)); - lua_settable(lua_state(), -3); - } - template void tuple_set( indices, Pairs&& pairs ) { using swallow = int[]; @@ -113,7 +115,7 @@ public: template table_core& set( Tn&&... argn ) { tuple_set(build_indices(), std::forward_as_tuple(std::forward(argn)...)); - return *this; + return *this; } template @@ -225,19 +227,28 @@ private: set_fx( types>( ), std::forward( key ), std::forward( fx ) ); } - template>>, Bool> = 0> + template, is_c_str> = 0> void set_resolved_function( Key&& key, Args&&... args ) { stack::push>( lua_state( ), std::forward( args )... ); lua_setglobal( lua_state( ), &key[ 0 ] ); } - template>>, Bool> = 0> + template>, is_c_str> = 0> void set_resolved_function( Key&& key, Args&&... args ) { auto pp = stack::push_popper( *this ); int tabletarget = lua_gettop( lua_state( ) ); stack::push>( lua_state( ), std::forward( args )... ); lua_setfield( lua_state( ), tabletarget, &key[ 0 ] ); } + + template>> = 0> + void set_resolved_function( Key&& key, Args&&... args ) { + auto pp = stack::push_popper( *this ); + int tabletarget = lua_gettop( lua_state( ) ); + stack::push(lua_state(), std::forward(key)); + stack::push>( lua_state( ), std::forward( args )... ); + lua_settable( lua_state( ), tabletarget ); + } }; } // sol diff --git a/sol/traits.hpp b/sol/traits.hpp index 2f76540d..5ee5a118 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -315,6 +315,9 @@ struct has_key_value_pair : decltype(has_key_value_pair_impl::test(0)) {}; template using is_string_constructible = Or, const char*>, std::is_same, char>, std::is_same, std::string>, std::is_same, std::initializer_list>>; +template +using is_c_str = Or>, const char*>, std::is_same, std::string>>; + template auto unwrapper(T&& item) -> decltype(std::forward(item)) { return std::forward(item); diff --git a/sol/usertype.hpp b/sol/usertype.hpp index b4098ca7..9344b8a8 100644 --- a/sol/usertype.hpp +++ b/sol/usertype.hpp @@ -125,8 +125,10 @@ private: call_syntax syntax = stack::get_call_syntax(L, meta); int argcount = lua_gettop(L); - void* udata = lua_newuserdata(L, sizeof(T)); - T* obj = static_cast(udata); + T** referencepointer = reinterpret_cast(lua_newuserdata(L, sizeof(T*) + sizeof(T))); + T*& referencereference = *referencepointer; + T* obj = reinterpret_cast(referencepointer + 1); + referencereference = obj; match_constructor(L, obj, syntax, argcount - static_cast(syntax), typename identity::type()...); if(luaL_newmetatable(L, std::addressof(meta[0])) == 1) { @@ -144,7 +146,9 @@ private: struct destructor { static int destruct(lua_State* L) { userdata udata = stack::get(L, 1); - T* obj = static_cast(udata.value); + // The first sizeof(T*) bytes are the reference: the rest is + // the actual data itself + T* obj = static_cast(static_cast(reinterpret_cast(udata.value) + sizeof(T*))); std::allocator alloc{}; alloc.destroy(obj); return 0; diff --git a/tests.cpp b/tests.cpp index 5b46a084..570ad6b3 100644 --- a/tests.cpp +++ b/tests.cpp @@ -231,6 +231,16 @@ TEST_CASE("simple/get", "Tests if the get function works properly.") { REQUIRE(e == true); } +TEST_CASE("simple/set-get-global-integer", "Tests if the get function works properly with global integers") { + sol::state lua; + lua[1] = 25.4; + lua.script("b = 1"); + double a = lua.get(1); + double b = lua.get("b"); + REQUIRE(a == 25.4); + REQUIRE(b == 1); +} + TEST_CASE("simple/addition", "check if addition works and can be gotten through lua.get and lua.set") { sol::state lua; @@ -704,7 +714,7 @@ TEST_CASE("tables/usertype utility derived", "usertype classes must play nice wh lua.script("base = Base.new(5)"); REQUIRE_NOTHROW(lua.script("print(base:get_num())")); - /*sol::constructors> derivedctor; + sol::constructors> derivedctor; sol::usertype derivedusertype(derivedctor, "get_num_10", &Derived::get_num_10, "get_num", &Derived::get_num @@ -720,7 +730,7 @@ TEST_CASE("tables/usertype utility derived", "usertype classes must play nice wh "print(dgn10)"); REQUIRE((lua.get("dgn10") == 70)); - REQUIRE((lua.get("dgn") == 7));*/ + REQUIRE((lua.get("dgn") == 7)); } TEST_CASE("tables/self-referential usertype", "usertype classes must play nice when C++ object types are requested for C++ code") { @@ -1058,10 +1068,10 @@ TEST_CASE("functions/destructor-tests", "Show that proper copies / destruction h x() {++created;} x(const x&) {++created;} x(x&&) {++created;} - x& operator=(const x&) {++created; return *this;} - x& operator=(x&&) {++created; return *this;} + x& operator=(const x&) {return *this;} + x& operator=(x&&) {return *this;} void func() {last_call = static_cast(this);}; - ~x () {++destroyed;} + ~x () {++destroyed;} }; struct y { y() {++created;} @@ -1070,8 +1080,8 @@ TEST_CASE("functions/destructor-tests", "Show that proper copies / destruction h y& operator=(const x&) {return *this;} y& operator=(x&&) {return *this;} static void func() {last_call = static_call;}; - void operator()() {func();} - operator fptr () { return func; } + void operator()() {func();} + operator fptr () { return func; } ~y () {++destroyed;} }; @@ -1134,10 +1144,15 @@ TEST_CASE("usertype/destructor-tests", "Show that proper copies / destruction ha static void* last_call = nullptr; struct x { x() {++created;} + x(const x&) {++created;} + x(x&&) {++created;} + x& operator=(const x&) {return *this;} + x& operator=(x&&) {return *this;} ~x () {++destroyed;} }; { sol::state lua; + lua.new_usertype("x"); x x1; x x2; lua.set("x1copy", x1, "x2copy", x2, "x1ref", std::ref(x1)); @@ -1146,7 +1161,6 @@ TEST_CASE("usertype/destructor-tests", "Show that proper copies / destruction ha x& x1ref = lua["x1ref"]; REQUIRE(created == 4); REQUIRE(destroyed == 0); - REQUIRE_FALSE(last_call == &x1); REQUIRE(std::addressof(x1) == std::addressof(x1ref)); } REQUIRE(created == 4);