我有一个程序,用户可以使用Lua命令创建框架,例如:
frm=Frame.new()
上述命令向用户显示一个帧。在幕后,C ++包装器如下:
Frame* Frame_new(lua_State* L)
{
int nargs=lua_gettop(L);
Frame* wb=0;
if(nargs==0){
//Omitted
wb=mainfrm->GetFrame();
lua_pushlightuserdata(L,(void*)(wb));
int key=luaL_ref(L, LUA_REGISTRYINDEX);
wb->SetLuaRegistryKey(key);
}
return wb;
}
由于向用户显示了框架,用户只需单击操作系统提供的关闭按钮即可关闭框架。这会生成一个close事件,其处理方式如下:
void Frm::OnClose(wxCloseEvent& evt)
{
//Omitted for brevity
int LuaRegistryKey=GetFrame()->GetLuaRegistryKey();
lua_rawgeti(glbLuaState,LUA_REGISTRYINDEX,LuaRegistryKey);//userdata
Frame* wb1=(Frame*)lua_touserdata(glbLuaState,-1); //userdata
lua_pop(glbLuaState,1); //
lua_getglobal(glbLuaState,"_G"); //table
lua_pushnil(glbLuaState); //table key
while (lua_next(glbLuaState,-2)) {//table key value
const char* name = lua_tostring(glbLuaState,-2);//table
if(lua_type(glbLuaState,-1)==LUA_TUSERDATA){
Frame* wb2=(Frame*)lua_touserdata(glbLuaState,-1);
if(wb2==m_Frame){ //this part doesnt work
lua_pushnumber(glbLuaState,0);
lua_setglobal(glbLuaState,name);
lua_pop(glbLuaState,1);
break;
}
}
lua_pop(glbLuaState,1); //table key
} //table
lua_pop(glbLuaState,1); //
if(m_Frame==wb1) {delete m_Frame; m_Frame=0; wb1=0;}
if(wb1) {delete wb1; wb1=0;}
luaL_unref(glbLuaState,LUA_REGISTRYINDEX,LuaRegistryKey );
}
现在的目标是当用户关闭框架时,frm=Frame.new()
创建的变量应该为nil,这样用户就无法调用其中一个方法,例如frm:size()
会导致程序崩溃。
在上面用于处理close事件的C ++代码中,wb1
和当前帧具有相同的内存地址。 现在我的理解我需要做的就是在全局表中搜索userdata类型Frame
并比较内存地址,以便我知道我选择了正确的帧然后将其设置为零。
但是,Frame* wb2=(Frame*)lua_touserdata(glbLuaState,-1);
会返回与wb1
完全不同的地址,因此我无法知道我指的是哪种类型的变量。
据我所知wb2
有一个不同的内存地址可能是由于3种情况:
1)frm
是一个完整的用户数据
2)frm在全局lua表中,因此有一个不同的地址(虽然这对我来说没有意义,因为我在C ++中推送了Frame的地址)。
3)我完全以错误的方式思考,或者看不到简单的事情。
答案 0 :(得分:1)
现在根据我的理解,我需要做的就是在全局表中搜索userdata类型的Frame并比较内存地址,以便我知道我选择了正确的帧然后将其设置为nil。
你的理解是错误的。
首先,您没有将userdata返回给Lua。您返回 light userdata。那不一样。轻型用户数据的lua_type
为LUA_TLIGHTUSERDATA
。
其次,即使你修复了这个问题,你也不会遍历里面的全局表。所以这么简单就会让你感到困惑:
global_var = {}
global_var.frame = Frame.new()
Lua代码应该能够将数据存储在任何需要的地方。如果它想在表格中存储一些用户数据,你可以说不是吗?
第三,即使你遍历每个可全局访问的表递归(带有无限循环保护),也不会停止这个:
local frm = Frame.new()
function GlobalFunc(...)
frm:Stuff();
end
由于Lua具有适当的词法范围,GlobalFunc
将在内部存储对frm
本地的引用。由于frm
是local
变量,因此只能通过遍历全局变量来获取它。
一般来说,如果你给Lua一个值,它现在拥有该值。它可以做任何想做的事情,一般认为违反这个合同是不礼貌的。
虽然这不是不可能的。处理它的方法是使用实际 userdata而不是light userdata。每个常规userdata都是一个对象,一个完整的内存分配。在该分配中,您将存储Frame
指针。当需要销毁Frame
时,您所要做的就是将userdata中的Frame
指针设置为NULL。
从概念上讲,它在C ++中是这样的:
struct FramePtr
{
Frame *ptr;
};
Lua将绕过FramePtr
的单个分配。因此,如果您将该分配的FramePtr
设置为NULL,则每个人都会看到它。没有遍历全局表或某些东西。
当然,从Frame
访问FramePtr
需要额外的间接。但是,通过使用完整的userdata而不是light userdata,您可以还为其附加一个适当的元表(轻的userdata不会获得每个对象的元表;每个轻的userdata都共享相同的元表)。