Lua tricks
NOTE: This is not a Lua tutorial or reference, just a place to record something interesting when I read Lua source code, which is version 5.4.3
Lua global stuff
print function
- print will first call
__tostringmetamethod in value’s metatable if it exists.
local t = {}
local mt = {
__tostring = function()
return "willmafh's table"
end
}
setmetatable(t, mt)
print(t) -- will print `willmafh's table`
- number, string, boolean, and nil will print their values as normal.
- for other type values, it will try to get
__namefirst, if it’s not a string, then Lua default type name will be used.
local t = {}
local mt = {
__name = "willmafh's table"
}
setmetatable(t, mt)
print(t) -- `willmafh's table: 0x5f3167d3acf0`
local t = {}
local mt = {
-- here __name should be a string, otherwise it's meaningless
__name = function() return "willmafh's table" end
}
setmetatable(t, mt)
print(t) -- `table: 0x5f3167d3acf0`
Lua global table
-- _G is Lua's global table
print(_G)
-- following can recursively get the global table
print(_G["_G"])
print(_G._G)
Lua version
print(_G._VERSION)
warn function
- should use control message
"@on"to turn on warning message emitting first warn(msg1, msg2, ...)function can concat multiple string message- then should use control message
"@off"to turn off warn function - there are only two control messages,
"@on"/"@off", other messages start with ‘@’ are normal messages
warn("@on")
warn("hello world") -- Lua warning: hello world
warn("hello", " from willmafh") -- Lua warning: hello from willmafh
warn("@off")
warn("hello world") -- won't log this warning message
setmetatable function
setmetatablewill return its first argument, that is the table itself, it won’t create a new table
local t = {}
local mt = {}
local rt = setmetatable(t, mt)
print(t, rt) -- here t and rt are the same value
- if the object’s metatable has
__metatablefield, it means the metatable is protected, and we can’t use setmetatable again to change it
local mt = {
__metatable = "the metatable is protected",
__tostring = function()
return "willmafh's table"
end
}
local t = setmetatable({}, mt)
setmetatable(t, {}) -- error throw here
nilmetatable argument will clear table’s original metatable if there is any
local mt = {
__tostring = function()
return "willmafh's table from __tostring"
end
}
local t = setmetatable({}, mt)
print(getmetatable(t)) -- table: 0x603284328ef0
setmetatable(t, nil)
print(getmetatable(t)) -- nil
getmetatable function
- if the object’s metatable has
__metatable, then returns its value, it can be any type. otherwise returns the metatable itself
local mt = {
__metatable = "the metatable is protected", -- can be any type value
__tostring = function()
return "willmafh's table"
end
}
local t = setmetatable({}, mt)
print(getmetatable(t)) -- __metatable value is printed
require function
requirefunction will first searchregistry["_LOADED"]table, and if there is a required module, then it return directlypackagetable is the upvalue ofrequirefunction
local registry = debug.getregistry()
--[[
string table
table: 0x5efa18719710
table: 0x5efa18719710
--]]
local name, upvalue = debug.getupvalue(require, 1)
print(type(name), type(upvalue))
print(upvalue)
print(_G["package"])
registry table
- the
firstelement in registry table array is the main Lua thread, and thesecondelement in registry table array is the global table_G
local registry = debug.getregistry()
-- thread: 0x5886909e72a8
print(registry[1]) -- main thread
--[[
table: 0x5886909e7c50
table: 0x5886909e7c50
--]]
print(registry[2]) -- global table _G
print(_G)
- all
requireloaded modules, including lua file module and c libs module, are kept inregistry["_LOADED"]table.
require "lfs"
require "cjson.safe"
local registry = debug.getregistry()
for k, v in pairs(registry["_LOADED"]) do
print(k, v)
end
--[[
_G table: 0x6461ef84bc50
os table: 0x5786711edbb0
...
lfs table: 0x6461ef8526f0
cjson.safe table: 0x6461ef855a80
...
--]]
- all loaded c libs will be kept in
registry["_CLIBS"]table and each lib will be kept in two ways, one is key value way, the other is an array element
local lfs = require "lfs"
local cjson = require "cjson.safe"
local registry = debug.getregistry()
local clibs = registry["_CLIBS"]
--[[
1 userdata: 0x567153cf7fe0
2 userdata: 0x567153cfa700
/usr/local/lib/lua/5.4/lfs.so userdata: 0x567153cf7fe0
/usr/local/lib/lua/5.4/cjson.so userdata: 0x567153cfa700
--]]
for k, v in pairs(clibs) do
print(k, v)
end
--[[
1 userdata: 0x567153cf7fe0
2 userdata: 0x567153cfa700
--]]
for i, v in ipairs(clibs) do
print(i, v)
end
registry["_PRELOAD"]is normally an empty table, but it can be used to store loaders for lua modules, and whenrequirefunction load a new module, it will first search this table, so you can use it to do something interesting
package.preload["devtools"] = function()
return { editor = "vim", os = "linux" }
end
local devtools = require "devtools"
print(devtools.editor, devtools.os)
environment variables
LUA_NOENV
- if
luais used with-Eoption, thenLUA_NOENVwill be set true in registry
#!/usr/bin/lua5.4 -E
local registry = debug.getregistry()
print(registry["LUA_NOENV"]) -- true, since -E is used
LUA_PATH_5_4/LUA_PATH, LUA_CPATH_5_4/LUA_CPATH
;;in envsLUA_PATH_5_4/LUA_PATHmeans to insert default value from macroLUA_PATH_DEFAULTtopackage.path‘s final value;;in envsLUA_CPATH_5_4/LUA_CPATHmeans to insert default value from macroLUA_CPATH_DEFAULTtopackage.cpath‘s final value- when lua is initialized, function luaopen_package will handle all these details
--[[ /usr/local/share/lua/5.4/?.lua;/usr/local/share/lua/5.4/?/init.lua;/usr/local/lib/lua/5.4/?.lua;/usr/local/lib/lua/5.4/?/init.lua;./?.lua;./?/init.lua
--]]
print(package.path)
-- /usr/local/lib/lua/5.4/?.so;/usr/local/lib/lua/5.4/loadall.so;./?.so
print(package.cpath)
package table
package.config
- a string describing some compile-time configurations for packages, please refer to lua manual for details
print(package.config)
package.loaded
package.loadedis a reference toregistry["_LOADED"]
local registry = debug.getregistry()
--[[
table: 0x5c638d059a60
table: 0x5c638d059a60
--]]
print(registry["_LOADED"])
print(package.loaded)
package.preload
package.preloadis a reference toregistry["_PRELOAD"]
local registry = debug.getregistry()
--[[
table: 0x64a359eb7be0
table: 0x64a359eb7be0
--]]
print(registry["_PRELOAD"])
print(package.preload)
package.searchers
- a table contains four searching functions (currently), which is used by
requiremethod to search modules, and they are searched in the following order- preload table
- lua module, searching
package.pathone by one - c module, searching
package.cpathone by one - c root module, still searching
package.cpath, but build name by stripping components after the first dot ‘.’
- using
require "cjson.safe"as an example to illustrate the differences between searching c and searching c root
-- searching c: '/usr/local/lib/lua/5.4/`cjson/safe.so`', will try to find safe.so under cjson directory
-- searching c root: '/usr/local/lib/lua/5.4/`cjson.so`', will try to find cjson.so
local cjson = require "cjson.safe"
- both of searching c and c root will finally look for function beginning with prefix
luaopen_, such as ‘luaopen_cjson’ or ‘luaopen_cjson_safe’
package.searchpath
- searching name in the given path, which is a template like
package.path- dot ‘.’ in name will be replaced by ‘/‘
- ‘?’ in path will be replaced by dot replaced name
local name = "foo.a"
local path = "./?.lua;./?.lc;/usr/local/?/init.lua"
-- so the search order will be: './foo/a.lua', './foo/a.lc', '/usr/local/foo/a/init.lua'
package.searchpath(name, path)
package.loadlib
- it
does notperform any path searching anddoes notautomatically adds extensions likerequire, so libname arg must be the complete path - if funcname is “*” , then only loads and exports all symbols in the lib. otherwise looks for funcname and return it as a lua c function
- libs loaded by this function will be added to
registry["_CLIBS"]table
local cjson_path = "/usr/local/lib/lua/5.4/cjson.so"
print(package.loadlib(cjson_path, "*")) -- true
-- error here, since "json_encode" is not exported by cjson.so, but hey this is just an example
local json_encode = package.loadlib(cjson_path, "json_encode")
-- using function following...