Рассмотрим небольшой тестовый скрипт Lua.
g1 = "Global 1"g2 = "Global 2"
function test ()
local l1
print(g1,g2,l1)
end
test()
Предположим, вы приостановили выполнение при печати (g1, g2, l1) и из C ++ получили все глобальные переменные с этим кодом 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
рутина)