问题
Consider this small Lua test script.
g1 = "Global 1"
g2 = "Global 2"
function test ()
local l1
print(g1,g2,l1)
end
test()
Assume you pause the execution at print(g1,g2,l1) and from C++ get all the global variables with this C code:
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
When I get the name
of a global entry, how can I tell if this is a global variable defined by the user in the script, like g1 and g2?
Since the user can freely write the script, I can't search for a specific global, I need to tell them apart somehow.
回答1:
I see two ways. In the first, you record the names of all global variables before loading user scripts:
local S={}
_G["system variables"]=S
for k in pairs(_G) do S[k]=true end
Then in your C code, you traverse globals variables and filter only those whose name is in the table "system variables"
. Use lua_getglobal(L,"system variables")
to get this table.
In the second way, you track the definition of global variables after the system ones have been loaded. You set this up by running this script before loading user scripts:
local U={}
_G["user variables"]=U
local function trace(t,k,v)
U[k]=true
rawset(t,k,v)
end
setmetatable(_G,{ __newindex = trace })
Then in your C code, you traverse globals variables and filter only those whose name is not in the table "user variables"
. Use lua_getglobal(L,"user variables")
to get this table.
In both cases, do not convert keys in _G
to strings: indexed the special tables directly with the original keys.
Note that you can call lua_getglobal(L,"system variables")
or lua_getglobal(L,"user variables")
just once, before the traversal, and index it repeatedly inside the loop.
回答2:
My solution was to build a hash table of the global environment before I loaded the main script. When I need to get the user defined globals I only display globals not present in the hash table. In this way the script can run at full speed without keeping track of globals in in runtime.
Example of my solution (this is the short version of my implementation):
// 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
}
}
The function HashName
takes a string and returns the hash key for it as an unsigned int
, use whatever Hash algorithm you like here...
When you need to display the globals, call PrintGlobals()
(I do it from a hook
routine)
来源:https://stackoverflow.com/questions/20527659/how-to-filter-out-user-defined-globals-in-lua-from-c