mirror of
https://github.com/ThePhD/sol2.git
synced 2024-03-22 13:10:44 +08:00
Significant change to how userdata is stored to make access consistent across values/references/pointers.
This commit is contained in:
parent
c97b3f2b81
commit
b66c7f015a
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
decltype(auto) single_get(Key&& key) const {
|
||||
stack::detail::lua_getglobali(lua_state(), &key[0]);
|
||||
return stack::pop<T>(lua_state());
|
||||
template<typename T, typename Key, EnableIf<Not<Bool<top_level>>, is_c_str<Key>> = 0>
|
||||
decltype(auto) single_get( Key&& key ) const {
|
||||
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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
24
tests.cpp
24
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<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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user