Lua userdata与C ++的变量通信

时间:2016-08-23 10:56:38

标签: c++ lua

我有一个程序,用户可以使用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)我完全以错误的方式思考,或者看不到简单的事情。

1 个答案:

答案 0 :(得分:1)

  

现在根据我的理解,我需要做的就是在全局表中搜索userdata类型的Frame并比较内存地址,以便我知道我选择了正确的帧然后将其设置为nil。

你的理解是错误的。

首先,您没有将userdata返回给Lua。您返回 light userdata。那不一样。轻型用户数据的lua_typeLUA_TLIGHTUSERDATA

其次,即使你修复了这个问题,你也不会遍历里面的全局表。所以这么简单就会让你感到困惑:

global_var = {}
global_var.frame = Frame.new()

Lua代码应该能够将数据存储在任何需要的地方。如果它想在表格中存储一些用户数据,你可以说不是吗?

第三,即使你遍历每个可全局访问的表递归(带有无限循环保护),也不会停止这个

local frm = Frame.new()

function GlobalFunc(...)
    frm:Stuff();
end

由于Lua具有适当的词法范围,GlobalFunc将在内部存储对frm本地的引用。由于frmlocal变量,因此只能通过遍历全局变量来获取它。

一般来说,如果你给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都共享相同的元表)。