能够在Lua中复制Ruby的method_missing方法吗?
我相当确定,在 Lua 中,您可以使用给定 metatable 的 __index,__newindex 和 __call 来大致复制 Ruby 的 method_missing。我有一些代码:
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
local metahack = {
__call = function(self, ...)
return func(selfs, name, ...)
end
}
return setmetatable({}, metahack)
end
setmetatable(selfs, meta)
end
_G:method_missing(function(self, name, ...)
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
end
end)
test_print("I like me some method_missing abuse!")
print(this_should_be_nil)
我的问题是:尽管语法相似,我肯定可以使用它来复制功能,但它会引入一个错误。您在应用 method_missing 的表的上下文中使用的每个变量都不为nil,因为我必须返回一个可调用的对象,以便将可能的调用从索引函数传递给实际调用。
例如:在上述定义了全局 method_missing 后,尝试调用未定义的方法 'test_print' 会按预期运行,但是索引时 test_print 的值是非 nil 的,并且未响应的其他方法/变量,例如 this_should_be_nil也是非 nil 的。
那么有可能避免这个陷阱吗?或者语法不能被扭曲以支持此修改,而不修改语言源代码本身?我想困难在于,在 Ruby 中,索引和调用是类似的,而在 Lua 中它们是不同的。
你可以通过将 nil 值作为可调用函数来避免这个问题。
不幸的是,这只能从主机代码(即 C 程序)中完成,而不能从 Lua 脚本中完成。
Pascal 代码:
function set_metatable_for_any_value_function(L: Plua_State): Integer; cdecl;
begin // set_metatable_for_any_value(any_value, mt)
lua_setmetatable(L, -2);
Result := 0;
end;
procedure Test_Proc;
var
L: Plua_State;
const
Script =
'set_metatable_for_any_value(nil, ' +
' { ' +
' __call = function() ' +
' print "This method is under construction" ' +
' end ' +
' } ' +
') ' +
'print(nonexisting_method == nil) ' +
'nonexisting_method() ';
begin
L := luaL_newstate;
luaL_openlibs(L);
lua_pushcfunction(L, lua_CFunction(@set_metatable_for_any_value_function));
lua_setglobal(L, 'set_metatable_for_any_value');
luaL_dostring(L, Script);
lua_close(L);
end;
输出:
true
This method is under construction
所以根据 @lhf 的提示,我已经成功实现了 method_missing 的一个可以接受的解决方案(就我所知)。最终,我开发出了以下代码:
local field = '__method__missing'
function method_missing(selfs, func)
local meta = getmetatable(selfs)
local f
if meta then
f = meta.__index
else
meta = {}
f = rawget
end
meta.__index = function(self, name)
local v = f(self, name)
if v then
return v
end
rawget(self, name)[field] = function(...)
return func(self, name, ...)
end
end
setmetatable(selfs, meta)
end
debug.setmetatable(nil, { __call = function(self, ...)
if self[field] then
return self[field](...)
end
return nil
end, __index = function(self, name)
if name~=field then error("attempt to index a nil value") end
return getmetatable(self)[field]
end, __newindex = function(self, name, value)
if name~=field then error("attempt to index a nil value") end
getmetatable(self)[field] = value
end} )
_G:method_missing(function(self, name, ...)
local args = {...}
if name=="test_print" then
print("Oh my lord, it's method missing!", ...)
return
elseif args[1] and string.find(name, args[1]) then --If the first argument is in the name called...
table.remove(args, 1)
return unpack(args)
end
end)
test_print("I like me some method_missing abuse!")
test_print("Do it again!")
print(test_print, "method_missing magic!")
print(this_should_be_nil == nil, this_should_be_nil() == nil)
print(conditional_runs("runs", "conditionally", "due to args"))
print(conditional_runs("While this does nothing!")) --Apparently this doesn't print 'nil'... why?
输出:
Oh my lord, it's method missing! I like me some method_missing abuse!
Oh my lord, it's method missing! Do it again!
nil method_missing magic!
true true
conditionally due to args
这段代码让你可以很类似地在 Lua 中使用 method_missing,就像在 Ruby 中一样(没有任何响应检查)。它类似于我的初始响应,但它通过 nil 的元表“传递了 Buck”,这是我以为我无法做的事情。 (感谢提示!)但正如 @greatwolf 所说,在 Lua 中可能没有理由使用这样的结构;相同的动态性可能可以通过更清晰的元方法操纵来实现。
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
你已经很好地找出了问题:就我所知,无法通过纯 Lua 解决这个问题。
编辑:我错了,你可以通过将
nil作为调用对象来解决这个问题。请参阅其他回答。但在我看来,这仍然是一个糟糕的想法。method_missing的主要用途是代理对象,你可以用其他方式解决这个问题。在Kernel(Ruby) /_G(Lua) 上使用method_missing是糟糕的 :)你 可以 做的是只处理 一些 方法,例如如果你知道希望以
test_开头的方法:local function is_handled(method_name) return method_name:sub(1,5) == "test_" end function method_missing(selfs, func) local meta = getmetatable(selfs) local f if meta then f = meta.__index else meta = {} f = rawget end meta.__index = function(self, name) local v = f(self, name) if v then return v end if is_handled(name) then local metahack = { __call = function(self, ...) return func(selfs, name, ...) end } return setmetatable({}, metahack) end end setmetatable(selfs, meta) end _G:method_missing(function(self, name, ...) if name=="test_print" then print("Oh my lord, it's method missing!", ...) end end) test_print("I like me some method_missing abuse!") print(this_should_be_nil)现在也许问题应该是:你为什么想复制
method_missing,你能避免它吗?即使在 Ruby 中,建议避免使用method_missing并在可能时选择动态方法生成。