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

考虑以下 Lua 测试脚本。

g1 = "Global 1"
g2 = "Global 2"

function test ()
  local l1
  print(g1,g2,l1)
end

test()

假设你在执行 print(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?

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

点赞
用户107090
用户107090

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

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") 一次,并在循环中重复索引它。

2013-12-11 19:07:20
用户365929
用户365929

我的解决方案是在加载主要脚本之前,在全局环境中构建一个哈希表。当我需要获取用户定义的全局变量时,我仅显示哈希表中不存在的全局变量。这样,脚本可以在运行时不跟踪全局变量的情况下以全速运行。

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

// 存储全局名称的哈希表
std::set<unsigned int> Blacklist;

// 创建哈希表“黑名单”
void BlacklistSnapshot(lua_State *L) {

  lua_pushglobaltable(L);
  lua_pushnil(L);
  while (lua_next(L,-2) != 0) {                     // 弹出NIL,推送名称、值
    Blacklist.insert(HashName(lua_tostring(L,-2))); // 插入哈希表
    lua_pop(L,1);                                   // 移除值
  }
  lua_pop(L,1); // 移除全局表
}

// 仅显示用户定义的全局变量
void PrintGlobals(lua_State *L) {

  lua_pushglobaltable(L);
  lua_pushnil(L);
  while (lua_next(L,-2) != 0) { // 弹出NIL,推送名称、值

    // 检查全局变量是否存在于黑名单中
    if (Blacklist.find(HashName(lua_tostring(L,-2))) == Blacklist.end()) {
      // 不存在,打印它...
      PrintFormattedVariable(lua_type(L,-1),lua_tostring(L,-2));
    }
    lua_pop(L,1); // 移除值
  }
  lua_pop(L,1);   // 移除全局表
}

void RunScript(void) {

  // 创建新的Lua状态
  L = luaL_newstate();

  // 加载所有Lua库
  luaL_openlibs(L);

  // 创建协程
  CO = lua_newthread(L);

  BlacklistSnapshot(CO);

  // 加载和编译脚本
  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: ");    // 打印编译器错误
  }
}

函数HashName接受一个字符串,并将其哈希键作为unsigned int返回,请在此处使用任何哈希算法...

当您需要显示全局变量时,请调用PrintGlobals()(我从hook例程中调用它)

2013-12-12 01:29:48