我很确定在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
的表的上下文中使用的每个变量永远不会为零,因为我必须返回一个可以被调用的对象,以便pass the buck
可能来自index函数实际调用。
即。在定义了如上所述的全局method_missing之后,尝试调用未定义的方法'test_print'按预期运行,但是在索引时test_print的值是非零的,以及未响应的其他方法/变量,如this_should_be_nil
非零。
那么有可能避免这个陷阱吗?或者,如果不修改语言源本身,语法是否可以支持这种修改?我认为在Ruby中,索引和调用是如何类似的,而在Lua中它们是截然不同的,这就产生了困难。
答案 0 :(得分:3)
您可以通过使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
答案 1 :(得分:2)
你已经很好地发现了问题:据我所知,它不是可以用纯粹的Lua来解决这个问题。
编辑:我错了,你可以让nil
可以调用。看到其他答案。 IMO仍然是一个坏主意。 method_missing
的主要用例是代理对象,您可以通过其他方式解决这个问题。 method_missing
(Ruby)/ Kernel
(Lua)上的_G
非常糟糕:)
你可以做的只是处理一些方法,例如,如果你知道你希望方法以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
并更喜欢动态方法生成。
答案 2 :(得分:1)
因此,根据@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
此代码段允许您使用method_missing
非常类似于Ruby中的方式(尽管没有任何响应检查)。它类似于我最初的反应,除了它“通过nil的metatable”,我认为我做不到的事情。 (感谢小费!)但正如@greatwolf所说,可能没有理由在Lua中使用这样的结构;通过更清晰的元方法操作可以实现相同的动力。