lua 之 全局变量

注:本篇文章从个人博客园移植而来

_G

lua的全局变量保存在一个常规的table中,这个table被称为全局环境,该table存储在名为 _G 的表中

for i, v in pairs(_G) do 
    print(i)
end 

--[[ 
-- 为了方便查看,进行了缩行
rawequal getmetatable bit32 load dofile pairs table package loadfile
io xpcall print type _G debug rawlen tostring error assert rawget
require math select next utf8 coroutine _VERSION arg string os rawset 
tonumber collectgarbage setmetatable pcall ipairs
]]

--[[
-- 整理了部分,方便大家学习Lua C相关
------------------ lbaselib.c中 ------------------
参考文件网址:http://www.lua.org/source/5.3/lbaselib.c.html
assert collectgarbage dofile error getmetatable ipairs loadfile
load next pairs pcall print rawequal rawget rawset select 
setmetatable tonumber tostring type xpcall _G _VERSION
------------------ loadlib.c中 ------------------
参考文件网址:http://www.lua.org/source/5.3/loadlib.c.html
require module
------------------ lualib.h 中 ------------------
参考文件网址:http://www.lua.org/source/5.3/lualib.h.html
coroutine table io os string math debug package               
------------------ lstrlib.h 中 ------------------
参考文件网址:http://www.lua.org/source/5.3/lstrlib.c.html
unpack
]]

全局变量不需要声明即可使用,然而打字出错的情况可能会导致程序出现难以发现的bug。

因此我们可以添加一个对全局表不存在健的访问,提高程序的健全性:

print(g_A)                 -- nil 可能会导致程序出现难以发现的bug


-- 推荐方式
setmetatable(_G, {
    -- 写入变量
    __newindex = function(_, n)
        error("attempt to write to undeclared variable " .. n, 2)
    end, 
    -- 读取变量
    __index = function(_, n)
        error("Error: attempt to read undeclared variable " .. n, 2)
    end,
})
print(g_A)                 -- Error:attempt to read undeclared variable g_A

Lua虚拟机

在Lua虚拟机中,用一个全局结构的global_State来管理多个lua_State。

在调用luaL_newstate时,创建一个全局的global_State和一个lua_State后,luaL_newstate会调用f_luaopen,然后f_luaopen调用init_registry来初始化注册表。

/*
** Create registry table and its predefined values 
** 在lstate.c中,参考代码网址:http://www.lua.org/source/5.1/lstate.c.html
*/
static void init_registry (lua_State *L, global_State *g) {
  TValue temp;
  /* 创建注册表,初始化注册表数组部分大小为LUA_RIDX_LAST */
  Table *registry = luaH_new(L);
  sethvalue(L, &g->l_registry, registry);
  luaH_resize(L, registry, LUA_RIDX_LAST, 0);
  /* 把这个注册表的数组部分的第一个元素赋值为主线程的状态机L
  这里所说的线程并非是os的线程,而是lua的状态机概念
  */
  /*即 registry[LUA_RIDX_MAINTHREAD] = L */
  setthvalue(L, &temp, L);  /* temp = L */
  luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &temp);
  // 把注册表的数组部分的第二个元素赋值为全局表
  /*即 registry[LUA_RIDX_GLOBALS] = table of globals */
  /* temp = new table (global table) */
  sethvalue(L, &temp, luaH_new(L));  
  luaH_setint(L, registry, LUA_RIDX_GLOBALS, &temp);
}

在创建注册表后,会将全局表注册到脚本中。

// 在linit.c中,参考代码网址:http://www.lua.org/source/5.1/linit.c.html
static const luaL_Reg loadedlibs[] = {
  {"_G", luaopen_base},
  {LUA_LOADLIBNAME, luaopen_package},
  {LUA_COLIBNAME, luaopen_coroutine},
  {LUA_TABLIBNAME, luaopen_table},
  {LUA_IOLIBNAME, luaopen_io},
  {LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_MATHLIBNAME, luaopen_math},
  {LUA_UTF8LIBNAME, luaopen_utf8},
  {LUA_DBLIBNAME, luaopen_debug},
#if defined(LUA_COMPAT_BITLIB)
  {LUA_BITLIBNAME, luaopen_bit32},
#endif
  {NULL, NULL}
};

以luaopen_base为例,会吧脚本用的函数注册到全局表,代码如下:

// 相关的函数表
static const luaL_Reg base_funcs[] = {
  {"assert", luaB_assert},
  {"collectgarbage", luaB_collectgarbage},
  {"dofile", luaB_dofile},
  {"error", luaB_error},
  {"getmetatable", luaB_getmetatable},
  {"ipairs", luaB_ipairs},
  {"loadfile", luaB_loadfile},
  {"load", luaB_load},
  /* 省略了部分代码*/ 
  {"_G", NULL},
  {"_VERSION", NULL},
  {NULL, NULL}
};

//
LUAMOD_API int luaopen_base (lua_State *L) {
  /* open lib into global table */
  lua_pushglobaltable(L);
  luaL_setfuncs(L, base_funcs, 0);
  /* set global _G */
  lua_pushvalue(L, -1);
  lua_setfield(L, -2, "_G");
  /* set global _VERSION */
  lua_pushliteral(L, LUA_VERSION);
  lua_setfield(L, -2, "_VERSION");
  return 1;
}

参考:深入理解Lua的全局变量_G以及源码实现

上一篇:yarn相关


下一篇:AI中pass架构设计优化