Lua - 反射 - 获取对象上的函数/字段列表?
Posted
技术标签:
【中文标题】Lua - 反射 - 获取对象上的函数/字段列表?【英文标题】:Lua - Reflection - Get list of functions/fields on an object? 【发布时间】:2011-02-06 21:38:51 【问题描述】:我是 Lua 新手,在程序的 alpha 版本中将 Lua 作为脚本语言处理。开发人员没有响应,我需要获取一些 C++ 对象提供的函数列表,这些对象可以从 Lua 代码访问。
有什么简单的方法可以查看这些对象公开了哪些字段和函数?
【问题讨论】:
【参考方案1】:在 Lua 中,要查看对象的成员,可以使用:
for key,value in pairs(o) do
print("found member " .. key);
end
不幸的是,我不知道这是否适用于从 C++ 导入的对象。
【讨论】:
哇,这么简单。它工作100%,非常感谢!你不知道这有多大帮助。 :) 还有一件事——你还能得到函数的参数列表吗? 我不知道。我想知道在编译后的 c++ 上导出 dll 会发现什么。 我已经尝试过了,但是搜索函数名称甚至没有找到我知道存在的函数,不幸的是。 基于 DLL 的模块通常只会导出单个函数luaopen_modulename()
,该函数构造模块的表并将其返回给实现 require()
的机器。在模块中实现实际功能的代码通常不会导出,因为它只能在运行的 Lua 解释器状态的上下文中调用。【参考方案2】:
如果环境允许,查看导出的 C++ 对象的元表会有所帮助:
for key,value in pairs(getmetatable(o)) do
print(key, value)
end
【讨论】:
【参考方案3】:打印所有全局变量:
--globals.lua -- 显示所有全局变量 本地看到= 函数转储(t,i) 看到[t]=真 本地 s= 本地 n=0 对于 k 成对 (t) 做 n=n+1 s[n]=k 结尾 table.sort(s) 对于 ipairs(s) 中的 k,v 做 打印(一,五) v=t[v] 如果 type(v)=="table" 并且没有看到 [v] 那么 转储(v,i.."\t") 结尾 结尾 结尾 转储(_G,"")来源:http://www.lua.org/cgi-bin/demo
输出:
_G _版本 断言 位32 阿什夫 乐队 不 博尔 测试 bxor 提炼 旋转 左移 代替 旋转 右移 收集垃圾 协程 创建 是可屈服的 恢复 跑步 状态 裹 屈服 调试 钩子 获取信息 获取本地 可元化 获取价值 获取用户价值 塞勾 设置本地 设置元表 设置值 设置用户值 追溯 升值id 升值加入 倾倒 错误 可元化 io 写 配对 加载 数学 腹肌 阿科斯 阿辛 晒黑 atan2 细胞 因 科什 度 经验 地面 fmod 频率 巨大的 ldexp 日志 日志10 最大限度 最大整数 分钟 小整数 模型 圆周率 战俘 拉德 随机的 随机种子 罪 辛 平方 棕褐色 谭 整数 类型 终极 下一个 操作系统 钟 日期 差异时间 出口 设置语言环境 时间 对 电话呼叫 打印 原始相等 生吃 罗伦 原始集 选择 设置元表 细绳 字节 字符 倾倒 找 格式 匹配 gsub 连 降低 匹配 盒 包装尺寸 代表 逆转 子 打开包装 上 桌子 连接 插入 移动 盒 消除 种类 打开包装 编号 字符串 类型 utf8 字符 图表 代码点 代码 连 抵消 xpcall【讨论】:
【参考方案4】:与 Stinky 先生给出的答案大致相同,但信息更多。
我制作了下面的代码,最初是为了从 Web 服务器运行,但添加了在 lua for windows 上运行的功能
在网络服务器上,选项在查询字符串中传递 ?loadmodules=no&module=_G
程序形式的选项在命令行函数中传递 loadmodules no module _G
如果不带参数运行,pkgpath 中的所有模块都会被加载和解析
#!/usr/bin/lua
--------------------------------------------------------------------
--|Functs.lua load available modules parse tables give write to html|
--|Table Of Contents, modules, available functions, strings etc.. |
--------------------------------------------------------------------
-- CONFIGURE----------------------------------------------------------------------------------------
local sPkgPath = "/usr/lib/lua" --look here for modules to load in addition to the intrinsic ones
local sWpkgPath = "C:\\Program Files (x86)\\Lua\\5.1\\lua\\" --package path for windows
local sURLsearch = "http://pgl.yoyo.org/luai/i/" --for lua standard functions search this site
local iMaxStr = 1024 -- maximum characters in a string printed to HTML table
local sFileOut = "functs.html"
----------------------------------------------------------------------------------------------------
local tQuery = --key,val pairs of arguments
local sQuery = "" --string of arguments ex:'?modload=no&module=_G.math...'
local sResults = "" --Results of each step through
local sEnv = "web" --running on a web server?
----------------------------------------------------------------------------------------------------
----------------------------FUNCTIONS START----------------------------------------------
local function a2m_m2a(addr_member)
--turns members into addresses; addresses back into members
return addr_member
end
local function PrRes(sVal)
--cats results strings
sResults = sResults .. sVal
end
local function errorHandler( err )
PrRes(" ERROR:" .. err .. " ")
--print(debug.traceback())
end
local function putOutput(tData, iCt)
--keys are integer indices, values to iCt written, if iCt = nil whole table written
for k, v in ipairs(tData) do
if iCt == nil or k <= iCt then
io.write (v) --write to std out could be changed here, or as below we change stdout file
end
end
end
local function parse_url(s)
--http://www.flashair-developers.com/en/support/forum/#/discussion/880/getting-query-string-parameters-from-an-http-request-in-lua/
--splits on '=' and '&' puts argument string into named keys with value [Key1] = (val1)&[Key2] = (val2)
--ex: Key1=va1&Key2=val2
local ans = [0]=""
----FUNCTIONS for parse_url -----------------------------------------------------
local function decode(s)
s = s:gsub('+', ' ')
s = s:gsub('%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end)
return s
end
----END FUNCTIONS for parse_url --------------------------------------------------
if s == nil then return ans end
--s = s:match('%s+(.+)')
for k,v in s:gmatch('([^&=]+)=([^&=]*)&?' ) do
--2 capture groups all chars (not '&' or '=') '=' all chars (not '&' or '=') followed by '' or '&' or '?'
ans[ k ] = decode(v)
end
return ans
end
local function tableByName(tName)
--find the longest match possible to an actual table
--Name comes in as (table) tName.var so we can pass back out the name found PITA
--returns the table found (key and value)
local ld =
local sMatch = ""
local kMatch = nil
local vMatch = nil
----FUNCTIONS for tableByName -----------------------------------------------------
local function search4Str(n, k, v)
local sKey = tostring(k)
if string.find (n, sKey,1,true) then
if sKey:len() > sMatch:len() then sMatch = sKey kMatch = k vMatch = v end
--find the longest match we can
end
end
----END FUNCTIONS for tableByName -------------------------------------------------
if tName.val ~= nil and tName.val ~= "" then
for k, v in pairs(_G) do --_G check both since some tables are only in _G or package.loaded
search4Str(tName.val, k, v)
end
for k, v in pairs(package.loaded) do --package.loaded
search4Str(tName.val, k, v)
end
if not string.find (sMatch, "_G",1,true) then sMatch = "_G." .. sMatch end -- put the root _G in if not exist
if kMatch and vMatch then ld[kMatch] = vMatch tName.val = sMatch return ld end
end
tName.val = "_G"
return package.loaded --Not Found return default
end
local function get_common_branches(t, tRet)
--load t 'names(values)' into keys
--strip off long paths then iterate value if it exists
--local tRet=
local sBranch = ""
local tName
for k in pairs(t) do
tName=["val"]=k
tableByName(tName)
sBranch = tName.val
if tRet[sBranch] == nil then
tRet[sBranch] = 1 --first instance of this branch
else
tRet[sBranch] = tRet[sBranch] + 1
end
end
end
local function pairsByPairs (t, tkSorted)
--tkSorted should be an already sorted (i)table with t[keys] in the values
--https://www.lua.org/pil/19.3.html
--!!Note: table sort default function does not like numbers as [KEY]!!
--see *sortbyKeys*cmp_alphanum*
--for n in pairs(t) do table.insert(kSorted, n) end
--table.sort(kSorted, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if tkSorted[i] == nil then return nil
else return tkSorted[i], t[tkSorted[i]]
end
end
return iter
end
local function sortbyKeys(t, tkSorted)
--loads keys of (t) into values of tkSorted
--and then sorts them
--tkSorted has integer keys (see ipairs)
----FUNCTIONS for sortByKeys -------------
local cmp_alphanum = function (op1, op2)
local type1= type(op1)
local type2 = type(op2)
if type1 ~= type2 then
return type1 < type2
else
return op1 < op2
end
end
----END FUNCTIONS for sortByKeys ---------
for n in pairs(t) do table.insert(tkSorted, n) end
table.sort(tkSorted, cmp_alphanum)--table.sort(tkSorted)
end
local function load_modules(sPkgRoot, sWinPkgRoot)
--attempt to load all found modules
--Modules may depend on other modules
--Supresses print, os.exit, rawset
--Ignores *.luac
PrRes("Functions Suspended, ")
local orig =osexit = _G.os.exit, print = _G.print, rawset = _G.rawset --save original functions for later restoration
_G.rawset = function(t, i, v) --orig.print ("rawset!")
if _G[i] == v then
orig.rawset(t,i,"_G["..tostring(i).."] !DUP!") --Don't allow global table to be copied
else
orig.rawset(t,i,v)
end
end
_G.os.exit = function() error(999) end --don't exit whole program just this function
_G.print = function()end --don't print
local st = io.popen("find "..sPkgRoot.." -type f -iname '*.so' -o -type f -iname '*.lua'" .." 2> nul")
if not st:read(0) then --find didn't work try windows dir instead
st = io.popen("dir /b /s " .."\""..sWinPkgRoot.."\\*.lua\" " .. "\""..sWinPkgRoot.."\\*.so\" " ) --simple output, subdir
end
if st:read(0) then
for module in st:lines() do
if (module) then
if not string.find (module, ".luac", 1, true) then --don't load precompiled code
local ok, res = pcall(loadfile(module))--protected call
end
end
end
end
_G.os.exit = orig.osexit
_G.print = orig.print
_G.rawset = orig.rawset
PrRes("Functions Restored, ")
end
local function dtTag(sType)
--convert named type; 'number'.. to short type '[n]...'
--if '?' supplied print out datatype key; number = [n]...
local retType = "?"
local typ =
["nil"] = "nil",
["boolean"] = "b",
["number"] = "n",
["string"] = "s",
["userdata"] = "u",
["function"] = "f",
["thread"] = "thr",
["table"] = "t"
if sType == "?" then retType = "Datatypes: " end
for k,v in pairs(typ) do
if sType == k then
retType = v break
elseif (sType == "?") then
retType = retType .. " [" ..v.. "] = " .. k
end
end
return " [" ..retType.. "] "
end
local function dump_Tables(tBase,sFunc, tSeen, tRet)
--Based on: http://www.lua.org/cgi-bin/demo?globals
--Recurse through tBase tables copying all found Tables
local sSep=""
local ld=
if sFunc ~= "" then sSep = "." end
for k, v in pairs(tBase) do
k = tostring(k)
if k ~= "loaded" and type(v) == "table" and not tSeen[v] then
tSeen[v]=sFunc
tRet[sFunc..sSep .. k] = a2m_m2a(v) --place all keys into ld[i]=value
dump_Tables(v, sFunc .. sSep .. k, tSeen, tRet)
end
end
--print("tables dumped")
end
local function dump_Functions(tBase)
--Based on: http://www.lua.org/cgi-bin/demo?globals
--We already recursed through tBase copying all found tables
--we look up the table by name and then (ab)use a2m_m2a() to load the address
--after finding the table by address in tBase we will put the table address of tFuncs in its place
for k,v in pairs(tBase) do
local tTable = a2m_m2a(v)
local tFuncs =
--print(type(tTable))
for key, val in pairs(tTable) do
if key ~= "loaded" then
tFuncs[dtTag(type(val)) .. tostring(key) ]= val --put the name and value in our tFuncs table
end
end
tBase[k] = a2m_m2a(tFuncs) -- copy the address back to tBase
end
--print("functions dumped")
end
local function html_Table(tBase, tkSorted, sId, fHeader, sTitle, iCols, fCellCond, fCell, fFooter, fOut)
--[[Prints HTML <table>
tBase, the table of items you want in your table [key] contains the cell data
tkSorted, the key sorted values of tBase (tkSorted keys are (i) based (see: ipairs)
sID, ID of div tag
fHeader, function returning <DIV></DIV>
sTitle, title of the table
iCols, number of cells wide
fCellCond, if return (TRUE) cell is displayed, fCellCond(k, v, n, iCells, i)
fCell, function returning contents of cell
fFooter, function returning tags at the end of the </table>
fOut, function to print the table[integer]=HTML_DATA based output
--]]
local oTbl=
local i = 1
local strName=""
local iCells = 0
local n = 0 --counts columns
oTbl[i]=fHeader(sId,sTitle)
i = i + 1 oTbl[i] = "<table><tr><th colspan='"..iCols.."'>"..sTitle.."</th></tr>\r\n"
for k, v in pairsByPairs(tBase,tkSorted ) do
strName= tostring(k)
if fCellCond(k, v, n, iCells, i) then
if n == 0 then
i = i + 1 oTbl[i] = "\t<tr>\r\n"
end
n = n + 1 i = i + 1 iCells = iCells + 1
oTbl[i] = "\t\t"..fCell(strName, v, sTitle).."\r\n"
if n >= iCols then
n = 0
i = i + 1
oTbl[i] = "\t</tr>\r\n"
end
fOut(oTbl, i)
i = 0
end
end
if n ~= 0 then
i = i + 1 oTbl[i] = "\t</tr>\r\n"
end
i = i + 1 oTbl[i] = "</table>\r\n" .. fFooter(strName)
fOut(oTbl, i)
return iCells
end
local function html_function_tables(tBase, tkSortTbase, fOut, iCols)
--print a table of functions for every module in tBase
local strName=""
local iCt = 0
local tFuncs =
local tkSorted =
----FUNCTIONS for Funct Html-----------------------------------------------------
local function fCellTrue(k)
--return tostring(k) == strName
return true
end
local function fTableCell(strName, value, sTitle)
local sHref = ""
local sType = type(value)
local sPkg = string.match (sTitle, ".+%p(%a+%P)")
local sVal = ""
--strName = tostring(strName)
if string.len(strName) > iMaxStr then
strName=string.sub(strName,1 , iMaxStr).."....." --Truncate strings longer than iMaxStr
end
if sPkg ~= nil and string.find (";debug;package;string;coroutine;io;math;os;table;", ";"..sPkg..";") then
sHref = "<a href='"..sURLsearch .. string.sub(strName,6) .. "'>?</a>" --remove [f] from beginning
end
if nil ~= string.find (";string;number;userdata;boolean;", sType, 1, true) then
sVal = tostring(value)
if string.len(sVal) > iMaxStr then
sVal=string.sub(sVal,1 , iMaxStr).."....." --Truncate strings longer than iMaxStr
end
return "</tr><td colspan='"..iCols.."'>".. sHref ..strName.." : "..sVal.."</td><tr>"
else
return "<td><a>"..strName.."</a>".. sHref .. "</td>"
end
end
local function fPageAnchor(sId, strTitle)
local sHref = ""
local sAddr = ""
local sModload = tQuery.modload
if not sModload then sModload = "" end
local sStyle = "'style='display:block;text-decoration: none"
if os.getenv("SERVER_NAME") and os.getenv("SCRIPT_NAME") then
sAddr = "http://"..os.getenv("SERVER_NAME") .."/".. os.getenv("SCRIPT_NAME") .."?modload=".. sModload
sHref="<a href='"..sAddr.."&module="..strTitle..sStyle.."'>" .."Module: " .. strTitle.. "</a>"
else
sHref = "Module: " .. strTitle
end
return "<div id='"..sId.."'style='color:#0000FF'><h3>"..sHref.."</h3></div>\r\n"
end
local function fPageFooter(strName)
return "<p><a href='#toc'>^</a></p><BR /><BR />"
end
----END FUNCTIONS for Funct Html--------------------------------------------------
for key, val in pairsByPairs(tBase, tkSortTbase) do
strName=tostring(key)
tkSorted =
tFuncs = a2m_m2a(val)
sortbyKeys(tFuncs, tkSorted)
iCt = iCt + html_Table(tFuncs,tkSorted, strName, fPageAnchor, strName, iCols, fCellTrue, fTableCell, fPageFooter, fOut)
end
return iCt
end
local function html_toc_tables(tBase, tkSortTbase, fOut, iCols)
local iCt = 0
----FUNCTIONS for TOC Html-----------------------------------------------------
local function fTableCell(strName)
return "<td><a href='#" .. strName .. "'style='display:block;text-decoration: none'>" .. strName.. "</a></td>"
end
local function fCellTrue()
return true
end
local function fFooter()
return "<BR /><b>" .. dtTag("?") .. "</b><BR /><BR /><BR /><BR /><BR /><BR />"
end
local function fPageAnchor(sId, strTitle)
return "<div id='"..sId.."'><p></p></div>\r\n"
end
----END FUNCTIONS for TOC Html--------------------------------------------------
iCt = html_Table(tBase, tkSortTbase, "toc", fPageAnchor, "* Modules Found * Lua Ver. ".._VERSION, iCols, fCellTrue, fTableCell, fFooter, fOut)
return iCt
end
local function main (sPackage)
local tSeen=
local tcBase =
local tkSortCbase =
local tMods=
local tkSortMods =
local iCtF = 0
if not sPackage then sPackage = "_G" end
putOutput( --header for html document
[1] = "<!DOCTYPE html>\r\n<html><head>\r\n<style>\r\n",
[2] = "\ttable, th, td border: 1px solid black;\r\n",
[3] = "\ttable tr:nth-child(even) background-color: #C4C4C4;\r\n",
[4] = "\ttable tr:nth-child(odd) background-color:#EFEFEF;\r\n",
[5] = "</style>\r\n</head><body>\r\n"
)
PrRes("Dump Tables: ")
xpcall( function()dump_Tables(tableByName(["val"] = sPackage),"", tSeen, tMods) end , errorHandler )
tSeen = nil
PrRes("ok, ")
PrRes("Dump Functions: ")
xpcall( function()dump_Functions(tMods)end , errorHandler )
PrRes("ok, ")
PrRes("Common Branches: ")
get_common_branches(tMods, tcBase)
PrRes("ok, ")
PrRes("Sorting Branches: ")
sortbyKeys(tcBase, tkSortCbase)
sortbyKeys(tMods, tkSortMods)
PrRes("ok, ")
PrRes("Print TOC: ")
iCtF = html_toc_tables(tcBase, tkSortCbase, putOutput, 3)
tcBase= nil tkSortCbase= nil
PrRes(iCtF .." ok, ")
PrRes("Print Functions: ")
iCtF = html_function_tables(tMods, tkSortMods, putOutput, 6)
PrRes(iCtF .." ok, ")
end
----------------------------FUNCTIONS END--------------------------------------------------
if os.getenv("SERVER_NAME") == nil then
sEnv="other"
else --sEnv == web
--Send response header as soon as we load
io.write ("Status: 200 OK\r\nKeep-Alive: timeout=60\r\nContent-Type:text/html\r\nLast-Modified:Sun, 11 Jan 2099 01:01:99 GMT\r\n\r\n") -- end of response)
end
if sQuery == "" then --load arguments from query string if web; or arg[] if not
if sEnv == "web" then
sQuery = os.getenv("QUERY_STRING")
else --Load arguments from arg[] list arg[1]=arg[2]&arg[3]=arg[4]&..
for k,v in pairs(...) do
sQuery = sQuery .. v
if math.fmod (k, 2) == 0 then
sQuery = sQuery .. "&"
else
sQuery = sQuery .. "="
end
end
end
end
tQuery = parse_url(sQuery)
--[[print(sQuery)
for k,v in pairs(tQuery) do
print ("[", k ,"]=", v)
end]]
if sQuery ~= "modloadno" and tQuery.modload ~= "no" then
PrRes("Load Modules: ")
xpcall( function()load_modules(sPkgPath,sWpkgPath)end, errorHandler )
--load_modules(sPkgPath)
PrRes("ok, ")
end
PrRes("Main; ")
if sEnv ~= "web" then
print("Fileout: " .. sFileOut)
io.output(sFileOut)
end
local ok, res = pcall(main,tQuery.module)
if not ok then
if sEnv ~= "web" then
print("Status: 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\n" .. res .. "\r\n")
else
print("Error: " .. res)
end
end
PrRes("DONE")
io.write("<p> Query: " .. sQuery .. ";; " .. sResults .."</p>")
Sample Output
【讨论】:
以上是关于Lua - 反射 - 获取对象上的函数/字段列表?的主要内容,如果未能解决你的问题,请参考以下文章