Significant change to how userdata is stored to make access consistent across values/references/pointers.

This commit is contained in:
ThePhD 2016-02-01 11:56:44 -05:00
parent c97b3f2b81
commit b66c7f015a
8 changed files with 110 additions and 74 deletions

View File

@ -66,8 +66,8 @@ private:
int stacksize = lua_gettop( lua_state( ) ); int stacksize = lua_gettop( lua_state( ) );
int firstreturn = std::max( 1, stacksize - static_cast<int>( n ) ); int firstreturn = std::max( 1, stacksize - static_cast<int>( n ) );
luacall( n, LUA_MULTRET ); luacall( n, LUA_MULTRET );
int poststacksize = lua_gettop( lua_state( ) ); int poststacksize = lua_gettop( lua_state( ) );
int returncount = poststacksize - firstreturn; int returncount = poststacksize - firstreturn;
return function_result( lua_state( ), firstreturn, returncount, returncount, call_error::ok ); return function_result( lua_state( ), firstreturn, returncount, returncount, call_error::ok );
} }

View File

@ -278,7 +278,7 @@ struct base_function {
struct usertype { struct usertype {
static int call(lua_State* L) { static int call(lua_State* L) {
// Zero-based template parameter, but upvalues start at 1 // Zero-based template parameter, but upvalues start at 1
return base_call(L, stack::get<upvalue>(L, I + 1)); return ref_base_call(L, stack::get<upvalue>(L, I + 1));
} }
static int ref_call(lua_State* L) { static int ref_call(lua_State* L) {

View File

@ -133,36 +133,42 @@ true;
false; false;
#endif #endif
inline void lua_getglobali(lua_State* L, lua_Integer n) { template <typename T>
#if SOL_LUA_VERSION >= 503 struct userdata_pusher {
lua_geti(L, LUA_RIDX_GLOBALS, n); template <typename Key, typename... Args>
#else static void push (lua_State* L, Key&& metatablekey, Args&&... args) {
lua_pushglobaltable(L); // Basically, we store all data like this:
lua_pushinteger(L, n); // If it's a new value (no std::ref(x)), then we store the pointer to the new
lua_gettable(L, -2); // data in the first sizeof(T*) bytes, and then however many bytes it takes to
lua_remove(L, -2); // remove the global table, leave final value on the stack // do the actual object. Things that are just references/pointers are stored as
#endif // just the sizeof(T*), and nothing else.
} T** pdatum = static_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T** referencepointer = pdatum;
T*& referencereference = *pdatum;
T* allocationtarget = reinterpret_cast<T*>(pdatum + 1);
referencereference = allocationtarget;
std::allocator<T> alloc{};
alloc.construct(allocationtarget, std::forward<Args>(args)...);
luaL_getmetatable(L, std::addressof(metatablekey[0]));
lua_setmetatable(L, -2);
}
};
inline void lua_setglobali(lua_State* L, lua_Integer n) { template <typename T>
#if SOL_LUA_VERSION >= 503 struct userdata_pusher<T*> {
lua_seti(L, LUA_RIDX_GLOBALS, n); template <typename Key, typename... Args>
#else static void push (lua_State* L, Key&& metatablekey, Args&&... args) {
lua_pushglobaltable(L); T** pdatum = static_cast<T**>(lua_newuserdata(L, sizeof(T*)));
lua_pushinteger(L, n); std::allocator<T*> alloc{};
lua_pushvalue(L, -3); alloc.construct(pdatum, std::forward<Args>(args)...);
lua_settable(L, -3); luaL_getmetatable(L, std::addressof(metatablekey[0]));
lua_pop(L, 2); // remove table, and the copy of the value lua_setmetatable(L, -2);
#endif }
} };
template <typename T, typename Key, typename... Args> template <typename T, typename Key, typename... Args>
inline int push_confirmed_userdata(lua_State* L, Key&& metatablekey, Args&&... args) { inline int push_confirmed_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
T* pdatum = static_cast<T*>(lua_newuserdata(L, sizeof(T))); userdata_pusher<T>{}.push(L, std::forward<Key>(metatablekey), std::forward<Args>(args)...);
std::allocator<T> alloc{};
alloc.construct(pdatum, std::forward<Args>(args)...);
luaL_getmetatable(L, std::addressof(metatablekey[0]));
lua_setmetatable(L, -2);
return 1; return 1;
} }
@ -253,9 +259,7 @@ struct getter {
template<typename U = T, EnableIf<Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0> template<typename U = T, EnableIf<Not<std::is_base_of<reference, U>>, Not<std::is_integral<U>>, Not<std::is_floating_point<U>>> = 0>
static U& get(lua_State* L, int index = -1) { static U& get(lua_State* L, int index = -1) {
void* udata = lua_touserdata(L, index); return getter<T&>{}.get(L, index);
T* obj = static_cast<T*>(udata);
return *obj;
} }
}; };

View File

@ -218,8 +218,8 @@ public:
table create_table(int narr = 0, int nrec = sizeof...(Tn), Tn&&... argn) { table create_table(int narr = 0, int nrec = sizeof...(Tn), Tn&&... argn) {
lua_createtable(L, narr, nrec); lua_createtable(L, narr, nrec);
table result(L); table result(L);
result.set(std::forward<Tn>(argn)...); result.set(std::forward<Tn>(argn)...);
lua_pop(L, 1); lua_pop(L, 1);
return result; return result;
} }

View File

@ -37,19 +37,20 @@ class table_core : public reference {
friend class state; friend class state;
friend class state_view; friend class state_view;
template<typename T, typename Key, EnableIf<Not<std::is_integral<Unqualified<Key>>>, Bool<top_level>> = 0> template<typename T, typename Key, EnableIf<Bool<top_level>, is_c_str<Key>> = 0>
decltype(auto) single_get( Key&& key ) const { decltype(auto) single_get( Key&& key ) const {
lua_getglobal(lua_state( ), &key[0]); lua_getglobal(lua_state( ), &key[0]);
return stack::pop<T>(lua_state()); return stack::pop<T>(lua_state());
} }
template<typename T, typename Key, EnableIf<std::is_integral<Unqualified<Key>>, Bool<top_level>> = 0> template<typename T, typename Key, EnableIf<Not<Bool<top_level>>, is_c_str<Key>> = 0>
decltype(auto) single_get(Key&& key) const { decltype(auto) single_get( Key&& key ) const {
stack::detail::lua_getglobali(lua_state(), &key[0]); auto pp = stack::push_popper(*this);
return stack::pop<T>(lua_state()); lua_getfield( lua_state( ), -2, &key[0] );
return stack::pop<T>( lua_state( ) );
} }
template<typename T, typename Key, DisableIf<Bool<top_level>, Not<std::is_void<Key>>> = 0> template<typename T, typename Key, EnableIf<Not<is_c_str<Key>>> = 0>
decltype(auto) single_get( Key&& key ) const { decltype(auto) single_get( Key&& key ) const {
auto pp = stack::push_popper(*this); auto pp = stack::push_popper(*this);
stack::push( lua_state( ), std::forward<Key>( key ) ); stack::push( lua_state( ), std::forward<Key>( key ) );
@ -57,6 +58,27 @@ class table_core : public reference {
return stack::pop<T>( lua_state( ) ); return stack::pop<T>( lua_state( ) );
} }
template<typename Key, typename Value, EnableIf<Bool<top_level>, is_c_str<Key>> = 0>
void single_set( Key&& key, Value&& value ) {
stack::push( lua_state( ), std::forward<Value>( value ) );
lua_setglobal( lua_state( ), &key[0] );
}
template<typename Key, typename Value, EnableIf<Not<Bool<top_level>>, is_c_str<Key>> = 0>
void single_set(Key&& key, Value&& value) {
auto pp = stack::push_popper(*this);
stack::push(lua_state(), std::forward<Value>(value));
lua_setfield(lua_state(), -3, &key[0]);
}
template<typename Key, typename Value, EnableIf<Not<is_c_str<Key>>> = 0>
void single_set(Key&& key, Value&& value) {
auto pp = stack::push_popper(*this);
stack::push(lua_state(), std::forward<Key>(key));
stack::push(lua_state(), std::forward<Value>(value));
lua_settable(lua_state(), -3);
}
template<typename Keys, typename... Ret, std::size_t... I> template<typename Keys, typename... Ret, std::size_t... I>
stack::get_return<Ret...> tuple_get( types<Ret...>, indices<I...>, Keys&& keys ) const { stack::get_return<Ret...> tuple_get( types<Ret...>, indices<I...>, Keys&& keys ) const {
return stack::get_return<Ret...>( single_get<Ret>( std::get<I>( keys ) )... ); return stack::get_return<Ret...>( single_get<Ret>( std::get<I>( keys ) )... );
@ -67,26 +89,6 @@ class table_core : public reference {
return single_get<Ret>( std::get<I>( keys ) ); return single_get<Ret>( std::get<I>( keys ) );
} }
template<typename Key, typename U, EnableIf<std::is_integral<Unqualified<Key>>, Bool<top_level>> = 0>
void single_set( Key&& key, U&& value ) {
stack::push( lua_state( ), std::forward<Value>( value ) );
stack::detail::lua_setglobali( lua_state( ), std::forward<Key>(key) );
}
template<typename Key, typename Value, EnableIf<Not<std::is_integral<Unqualified<Key>>>, Bool<top_level>> = 0>
void single_set( Key&& key, Value&& value ) {
stack::push( lua_state( ), std::forward<Value>( value ) );
lua_setglobal( lua_state( ), &key[0] );
}
template<typename Key, typename Value, DisableIf<Bool<top_level>, Not<std::is_void<Key>>> = 0>
void single_set(Key&& key, Value&& value) {
auto pp = stack::push_popper(*this);
stack::push(lua_state(), std::forward<Key>(key));
stack::push(lua_state(), std::forward<Value>(value));
lua_settable(lua_state(), -3);
}
template<typename Pairs, std::size_t... I> template<typename Pairs, std::size_t... I>
void tuple_set( indices<I...>, Pairs&& pairs ) { void tuple_set( indices<I...>, Pairs&& pairs ) {
using swallow = int[]; using swallow = int[];
@ -113,7 +115,7 @@ public:
template<typename... Tn> template<typename... Tn>
table_core& set( Tn&&... argn ) { table_core& set( Tn&&... argn ) {
tuple_set(build_indices<sizeof...(Tn) / 2>(), std::forward_as_tuple(std::forward<Tn>(argn)...)); tuple_set(build_indices<sizeof...(Tn) / 2>(), std::forward_as_tuple(std::forward<Tn>(argn)...));
return *this; return *this;
} }
template<typename T> template<typename T>
@ -225,19 +227,28 @@ private:
set_fx( types<function_signature_t<Sig>>( ), std::forward<Key>( key ), std::forward<Fx>( fx ) ); set_fx( types<function_signature_t<Sig>>( ), std::forward<Key>( key ), std::forward<Fx>( fx ) );
} }
template<typename... Sig, typename... Args, typename Key, EnableIf<Not<std::is_arithmetic<Unqualified<Key>>>, Bool<top_level>> = 0> template<typename... Sig, typename... Args, typename Key, EnableIf<Bool<top_level>, is_c_str<Key>> = 0>
void set_resolved_function( Key&& key, Args&&... args ) { void set_resolved_function( Key&& key, Args&&... args ) {
stack::push<function_sig_t<Sig...>>( lua_state( ), std::forward<Args>( args )... ); stack::push<function_sig_t<Sig...>>( lua_state( ), std::forward<Args>( args )... );
lua_setglobal( lua_state( ), &key[ 0 ] ); lua_setglobal( lua_state( ), &key[ 0 ] );
} }
template<typename... Sig, typename... Args, typename Key, DisableIf<Not<std::is_arithmetic<Unqualified<Key>>>, Bool<top_level>> = 0> template<typename... Sig, typename... Args, typename Key, EnableIf<Not<Bool<top_level>>, is_c_str<Key>> = 0>
void set_resolved_function( Key&& key, Args&&... args ) { void set_resolved_function( Key&& key, Args&&... args ) {
auto pp = stack::push_popper( *this ); auto pp = stack::push_popper( *this );
int tabletarget = lua_gettop( lua_state( ) ); int tabletarget = lua_gettop( lua_state( ) );
stack::push<function_sig_t<Sig...>>( lua_state( ), std::forward<Args>( args )... ); stack::push<function_sig_t<Sig...>>( lua_state( ), std::forward<Args>( args )... );
lua_setfield( lua_state( ), tabletarget, &key[ 0 ] ); lua_setfield( lua_state( ), tabletarget, &key[ 0 ] );
} }
template<typename... Sig, typename... Args, typename Key, EnableIf<Not<is_c_str<Key>>> = 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>(key));
stack::push<function_sig_t<Sig...>>( lua_state( ), std::forward<Args>( args )... );
lua_settable( lua_state( ), tabletarget );
}
}; };
} // sol } // sol

View File

@ -315,6 +315,9 @@ struct has_key_value_pair : decltype(has_key_value_pair_impl::test<T>(0)) {};
template <typename T> template <typename T>
using is_string_constructible = Or<std::is_same<Unqualified<T>, const char*>, std::is_same<Unqualified<T>, char>, std::is_same<Unqualified<T>, std::string>, std::is_same<Unqualified<T>, std::initializer_list<char>>>; using is_string_constructible = Or<std::is_same<Unqualified<T>, const char*>, std::is_same<Unqualified<T>, char>, std::is_same<Unqualified<T>, std::string>, std::is_same<Unqualified<T>, std::initializer_list<char>>>;
template <typename T>
using is_c_str = Or<std::is_same<std::decay_t<Unqualified<T>>, const char*>, std::is_same<Unqualified<T>, std::string>>;
template<typename T> template<typename T>
auto unwrapper(T&& item) -> decltype(std::forward<T>(item)) { auto unwrapper(T&& item) -> decltype(std::forward<T>(item)) {
return std::forward<T>(item); return std::forward<T>(item);

View File

@ -125,8 +125,10 @@ private:
call_syntax syntax = stack::get_call_syntax(L, meta); call_syntax syntax = stack::get_call_syntax(L, meta);
int argcount = lua_gettop(L); int argcount = lua_gettop(L);
void* udata = lua_newuserdata(L, sizeof(T)); T** referencepointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
T* obj = static_cast<T*>(udata); T*& referencereference = *referencepointer;
T* obj = reinterpret_cast<T*>(referencepointer + 1);
referencereference = obj;
match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), typename identity<TTypes>::type()...); match_constructor(L, obj, syntax, argcount - static_cast<int>(syntax), typename identity<TTypes>::type()...);
if(luaL_newmetatable(L, std::addressof(meta[0])) == 1) { if(luaL_newmetatable(L, std::addressof(meta[0])) == 1) {
@ -144,7 +146,9 @@ private:
struct destructor { struct destructor {
static int destruct(lua_State* L) { static int destruct(lua_State* L) {
userdata udata = stack::get<userdata>(L, 1); userdata udata = stack::get<userdata>(L, 1);
T* obj = static_cast<T*>(udata.value); // The first sizeof(T*) bytes are the reference: the rest is
// the actual data itself
T* obj = static_cast<T*>(static_cast<void*>(reinterpret_cast<char*>(udata.value) + sizeof(T*)));
std::allocator<T> alloc{}; std::allocator<T> alloc{};
alloc.destroy(obj); alloc.destroy(obj);
return 0; return 0;

View File

@ -231,6 +231,16 @@ TEST_CASE("simple/get", "Tests if the get function works properly.") {
REQUIRE(e == true); 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<double>(1);
double b = lua.get<double>("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") { TEST_CASE("simple/addition", "check if addition works and can be gotten through lua.get and lua.set") {
sol::state lua; 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)"); lua.script("base = Base.new(5)");
REQUIRE_NOTHROW(lua.script("print(base:get_num())")); REQUIRE_NOTHROW(lua.script("print(base:get_num())"));
/*sol::constructors<sol::types<int>> derivedctor; sol::constructors<sol::types<int>> derivedctor;
sol::usertype<Derived> derivedusertype(derivedctor, sol::usertype<Derived> derivedusertype(derivedctor,
"get_num_10", &Derived::get_num_10, "get_num_10", &Derived::get_num_10,
"get_num", &Derived::get_num "get_num", &Derived::get_num
@ -720,7 +730,7 @@ TEST_CASE("tables/usertype utility derived", "usertype classes must play nice wh
"print(dgn10)"); "print(dgn10)");
REQUIRE((lua.get<int>("dgn10") == 70)); REQUIRE((lua.get<int>("dgn10") == 70));
REQUIRE((lua.get<int>("dgn") == 7));*/ REQUIRE((lua.get<int>("dgn") == 7));
} }
TEST_CASE("tables/self-referential usertype", "usertype classes must play nice when C++ object types are requested for C++ code") { 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() {++created;}
x(const x&) {++created;} x(const x&) {++created;}
x(x&&) {++created;} x(x&&) {++created;}
x& operator=(const x&) {++created; return *this;} x& operator=(const x&) {return *this;}
x& operator=(x&&) {++created; return *this;} x& operator=(x&&) {return *this;}
void func() {last_call = static_cast<void*>(this);}; void func() {last_call = static_cast<void*>(this);};
~x () {++destroyed;} ~x () {++destroyed;}
}; };
struct y { struct y {
y() {++created;} 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=(const x&) {return *this;}
y& operator=(x&&) {return *this;} y& operator=(x&&) {return *this;}
static void func() {last_call = static_call;}; static void func() {last_call = static_call;};
void operator()() {func();} void operator()() {func();}
operator fptr () { return func; } operator fptr () { return func; }
~y () {++destroyed;} ~y () {++destroyed;}
}; };
@ -1134,10 +1144,15 @@ TEST_CASE("usertype/destructor-tests", "Show that proper copies / destruction ha
static void* last_call = nullptr; static void* last_call = nullptr;
struct x { struct x {
x() {++created;} x() {++created;}
x(const x&) {++created;}
x(x&&) {++created;}
x& operator=(const x&) {return *this;}
x& operator=(x&&) {return *this;}
~x () {++destroyed;} ~x () {++destroyed;}
}; };
{ {
sol::state lua; sol::state lua;
lua.new_usertype<x>("x");
x x1; x x1;
x x2; x x2;
lua.set("x1copy", x1, "x2copy", x2, "x1ref", std::ref(x1)); 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"]; x& x1ref = lua["x1ref"];
REQUIRE(created == 4); REQUIRE(created == 4);
REQUIRE(destroyed == 0); REQUIRE(destroyed == 0);
REQUIRE_FALSE(last_call == &x1);
REQUIRE(std::addressof(x1) == std::addressof(x1ref)); REQUIRE(std::addressof(x1) == std::addressof(x1ref));
} }
REQUIRE(created == 4); REQUIRE(created == 4);