如何从C++中过滤掉Lua中用户定义的全局变量

How to filter out user defined globals in Lua from C++?

本文关键字:用户 定义 全局变量 Lua 过滤 C++      更新时间:2023-10-16

考虑一下这个小的Lua测试脚本。

g1 = "Global 1"
g2 = "Global 2"
function test ()
  local l1
  print(g1,g2,l1)
end
test()

假设您在打印时暂停执行(g1,g2,l1),并从C++获得所有全局变量,代码为:

lua_pushglobaltable(L);
lua_pushnil(L);
while (lua_next(L,-2) != 0) {
  const char* name = lua_tostring(L,-2);
  // How do I tell a user defined
  // global variable (now in name)
  // from all other environment variables?
  lua_pop(L,1);
}
lua_pop(L,1); // global table

当我获得全局条目的name时,我如何判断这是否是用户在脚本中定义的全局变量,如g1和g2?

由于用户可以自由编写脚本,我无法搜索特定的全局,我需要以某种方式将它们区分开来。

我看到了两种方法。在第一个例子中,在加载用户脚本之前,记录所有全局变量的名称:

local S={}
_G["system variables"]=S
for k in pairs(_G) do S[k]=true end

然后在C代码中,遍历全局变量,并只筛选那些名称在表"system variables"中的变量。使用lua_getglobal(L,"system variables")获取此表。

第二种方法是,在加载系统变量后跟踪全局变量的定义。您可以通过在加载用户脚本之前运行此脚本来设置:

local U={}
_G["user variables"]=U
local function trace(t,k,v)
    U[k]=true
    rawset(t,k,v)
end   
setmetatable(_G,{ __newindex = trace })

然后在C代码中,遍历全局变量,只筛选那些名称不在表"user variables"中的变量。使用lua_getglobal(L,"user variables")获取此表。

在这两种情况下,都不要将_G中的键转换为字符串:直接使用原始键为特殊表编制索引。

请注意,您可以在遍历之前只调用lua_getglobal(L,"system variables")lua_getglobal(L,"user variables")一次,并在循环中重复对其进行索引。

我的解决方案是在加载主脚本之前构建全局环境的哈希表。当我需要获得用户定义的全局变量时,我只显示哈希表中不存在的全局变量。通过这种方式,脚本可以全速运行,而无需在运行时跟踪全局变量。

我的解决方案示例(这是我实现的简短版本):

// The hash table storing global names
std::set<unsigned int> Blacklist;
// Create hash table "Blacklist"
void BlacklistSnapshot(lua_State *L) {
  lua_pushglobaltable(L);
  lua_pushnil(L);
  while (lua_next(L,-2) != 0) {                     // pop NIL, push name,value
    Blacklist.insert(HashName(lua_tostring(L,-2))); // insert to hash table
    lua_pop(L,1);                                   // remove value
  }
  lua_pop(L,1); // Remove global table
}

// Display user defined globals only
void PrintGlobals(lua_State *L) {
  lua_pushglobaltable(L);
  lua_pushnil(L);
  while (lua_next(L,-2) != 0) { // pop NIL, push name,value
    // Check if the global is present in our blacklist
    if (Blacklist.find(HashName(lua_tostring(L,-2))) == Blacklist.end()) {
      // Not present, print it...
      PrintFormattedVariable(lua_type(L,-1),lua_tostring(L,-2));
    }
    lua_pop(L,1); // remove value
  }
  lua_pop(L,1);   // remove global table
}

void RunScript(void) {
  // Create new Lua state
  L = luaL_newstate();
  // Load all Lua libraries
  luaL_openlibs(L);
  // Create co-routine
  CO = lua_newthread(L);
  BlacklistSnapshot(CO);
  // Load and compile script
  AnsiString script(Frame->Script_Edit->Text);
  if (luaL_loadbuffer(CO,script.c_str(),script.Length(),"Test") == LUA_OK) {
    lua_resume(CO,NULL,0);
  } else {
    cs_error(CO, "Compiler error: ");    // Print compiler error
  }
}

函数HashName获取一个字符串,并将其哈希键作为unsigned int返回,在这里使用您喜欢的任何哈希算法。。。

当您需要显示全局时,调用PrintGlobals()(我从hook例程中执行)