我正在尝试将Lua类对象推入堆栈。多个函数可以返回指向该对象的指针。
换句话说:我需要推送userdata值,同时仍然能够在它们上使用'==','〜='等,这样如果用户数据指针的C ++对象相同,那么它必须是相同的。
-- this should push the object onto the stack
local firstObject = GetClassObject();
firstObject:doSomething();
firstObject将由lua脚本存储,稍后在代码中我将需要再次执行此操作:
-- the c++ class pointer has not changed here
-- so I would like to push the same userdata pointer as in the first call...
local object = GetClassObject();
-- if I would not do this the following here would fail... :C
if object == firstObject then
...
我的Push函数应该基本上检查某个地方是否已经有相同的C ++类指针并且如果是这样推送相关的userdata指针(无论我怎么推它,对象应该以1:1的方式工作)
如果没有,它应该创建一个新的userdata(在堆栈上推送它)并将其内容设置为类对象。
这是我的代码:
template <typename T>
void Push( const T &tObject )
{
lua_State *L = GetLuaState();
// Here i need to check if such a C++ object (the same tObject)
// already exists!
//
// If so i want to push the associated userdata.
// Object didn't exist yet -> we need a new userdata
void *pUserData = lua_newuserdata( L, sizeof( tObject ) );
*reinterpret_cast<T*>( pUserData ) = tObject;
}
template <typename T>
void Push( const T &tObject, const char *pszTable )
{
Push( tObject );
lua_State *L = GetLuaState();
luaL_getmetatable( L, pszTable );
lua_setmetatable( L, -2 );
}
template <typename T>
T& Get( int nIndex )
{
T *pUserData = reinterpret_cast<T*>( lua_touserdata( GetLuaState(), nIndex ) );
if( pUserData == nullptr )
throw std::exception( "Invalid userdata!" );
return *pUserData;
}
template <typename T>
T& Get( int nIndex, const char *pszTable )
{
T *pUserData = reinterpret_cast<T*>( LuaToUData( nIndex, pszTable ) );
if( pUserData == nullptr )
throw std::exception( "Invalid userdata!" );
return *pUserData;
}
LuaToUData是一个自己的函数,我写的不是抛出lua错误:
void* LuaToUData( int nIndex, const char *pszTable )
{
void *pUserData = lua_touserdata( g_luaState, nIndex );
if( pUserData != nullptr )
{
if( lua_getmetatable( g_luaState, nIndex ) != 0 )
{
lua_getfield( g_luaState, LUA_REGISTRYINDEX, pszTable );
bool bEqual = ( lua_rawequal( g_luaState, -1, -2 ) == 1 );
lua_pop( g_luaState, 2 );
if( bEqual )
return pUserData;
}
}
return nullptr;
}
答案 0 :(得分:1)
是的,在Lua中,相同userdata 的任何两个实例都保证相同。但是,当您正在进行C ++类实例时,每个盒装实例都会被放入一个新的userdatum中,这意味着它们无法直接比较。
您需要做的是为您的对象定义__eq
元方法。它可能看起来像这样:
int l_compare_things(lua_State* l)
{
MyClass* a = reinterpret_cast<MyClass*>(lua_touserdata(L, 1));
MyClass* b = reinterpret_cast<MyClass*>(lua_touserdata(L, 2));
lua_pushboolean(L, (*a) == (*b));
return 1;
}
这假设MyClass
有某种operator==
覆盖。您可以将此函数设置为与您的MyClass
userdata项关联的元表中的__eq元方法。你似乎已经覆盖了metatable处理,所以我不会在这里打扰它。
现在,下一个问题:你将整个类实例装箱为lua完整的userdata项目。你可能不想一遍又一遍地推动同样的事情并耗尽所有可用的内存......如果你只是推动指针,这不是一个问题,但你不是那样做的。所以......你需要一些独特的方法来识别你的C ++类的每个实例。这是一个带字符串的例子:
class MyClass
{
private:
std::string _id;
public:
MyClass(const std::string& id) : _id(id) {}
const std::string& get_id() { return _id; }
// setters and operator= overrides not included.
};
void l_push_thing(lua_State* L, const MyClass& thing)
{
// try to get our instance by ID from the registry table:
lua_getfield(L, LUA_REGISTRYINDEX, thing.id());
// if so, return, leaving it at the top of the stack.
if (lua_isuserdata(L, -1))
return;
void *ud = lua_newuserdata(L, sizeof(MyClass));
*reinterpret_cast<MyClass*>(ud) = thing;
// set up the metatable, etc
// duplicate the userdata reference:
lua_pushvalue(L, -1);
// push our new userdata into the registry. pops the duplicate from the stack
lua_setfield(L, LUA_REGISTRYINDEX, thing.get_id());
}
(注意:我没有编译或测试过这个例子.E&amp; OE!)
这将使userdatum与堆栈顶部的某个特定MyClass
实例相关联。您需要采取自己的步骤来“取消注册”类实例;在这种情况下,注册表中存在对每个实例的硬引用,因此在销毁该引用之前,userdatum 将不会被垃圾回收。你可以考虑在这里使用weak / ephemeron表。
答案 1 :(得分:0)
这是弱表的工作原理吗?
void Push( const T &tObject )
{
std::ostringstream o;
o << tObject;
std::string sIdentifier = o.str();
const char *pszIdentifier = sIdentifier.c_str();
lua_State *L = GetLuaState();
luaL_getmetatable( L, "lua_userdata" );
if( !lua_istable( L, -1 ) )
{
// create new weak table
luaL_newmetatable( L, "lua_userdata" );
lua_pushstring( L, "v" );
lua_setfield( L, -2, "__mode" );
}
lua_getfield( L, -1, pszIdentifier );
if( lua_isuserdata( L, -1 ) == TRUE )
return lua_remove( L, -2 );
lua_pop( L, 1 ); // didnt exist yet - getfield is nil -> need to pop that
void *pUserData = lua_newuserdata( L, sizeof( UINT64 ) );
*reinterpret_cast<UINT64*>( pUserData ) = UINT64( tObject );
lua_pushvalue( L, -1 );
lua_setfield( L, -3, pszIdentifier );
lua_remove( L, -2 );
}