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

@ -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<upvalue>(L, I + 1));
return ref_base_call(L, stack::get<upvalue>(L, I + 1));
}
static int ref_call(lua_State* L) {

View File

@ -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
}
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 <typename T, typename Key, typename... Args>
inline int push_confirmed_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
T* pdatum = static_cast<T*>(lua_newuserdata(L, sizeof(T)));
template <typename T>
struct userdata_pusher {
template <typename Key, typename... Args>
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<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);
}
};
template <typename T>
struct userdata_pusher<T*> {
template <typename Key, typename... Args>
static void push (lua_State* L, Key&& metatablekey, Args&&... args) {
T** pdatum = static_cast<T**>(lua_newuserdata(L, sizeof(T*)));
std::allocator<T*> alloc{};
alloc.construct(pdatum, std::forward<Args>(args)...);
luaL_getmetatable(L, std::addressof(metatablekey[0]));
lua_setmetatable(L, -2);
}
};
template <typename T, typename Key, typename... Args>
inline int push_confirmed_userdata(lua_State* L, Key&& metatablekey, Args&&... args) {
userdata_pusher<T>{}.push(L, std::forward<Key>(metatablekey), std::forward<Args>(args)...);
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>
static U& get(lua_State* L, int index = -1) {
void* udata = lua_touserdata(L, index);
T* obj = static_cast<T*>(udata);
return *obj;
return getter<T&>{}.get(L, index);
}
};

View File

@ -37,19 +37,20 @@ class table_core : public reference {
friend class state;
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 {
lua_getglobal(lua_state( ), &key[0]);
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 {
stack::detail::lua_getglobali(lua_state(), &key[0]);
auto pp = stack::push_popper(*this);
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 {
auto pp = stack::push_popper(*this);
stack::push( lua_state( ), std::forward<Key>( key ) );
@ -57,6 +58,27 @@ class table_core : public reference {
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>
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 ) )... );
@ -67,26 +89,6 @@ class table_core : public reference {
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>
void tuple_set( indices<I...>, Pairs&& pairs ) {
using swallow = int[];
@ -225,19 +227,28 @@ private:
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 ) {
stack::push<function_sig_t<Sig...>>( lua_state( ), std::forward<Args>( args )... );
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 ) {
auto pp = stack::push_popper( *this );
int tabletarget = lua_gettop( lua_state( ) );
stack::push<function_sig_t<Sig...>>( lua_state( ), std::forward<Args>( args )... );
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

View File

@ -315,6 +315,9 @@ struct has_key_value_pair : decltype(has_key_value_pair_impl::test<T>(0)) {};
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>>>;
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>
auto unwrapper(T&& item) -> decltype(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);
int argcount = lua_gettop(L);
void* udata = lua_newuserdata(L, sizeof(T));
T* obj = static_cast<T*>(udata);
T** referencepointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));
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()...);
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<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{};
alloc.destroy(obj);
return 0;

View File

@ -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<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") {
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<sol::types<int>> derivedctor;
sol::constructors<sol::types<int>> derivedctor;
sol::usertype<Derived> 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<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") {
@ -1058,8 +1068,8 @@ 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<void*>(this);};
~x () {++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");
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);