Is it possible to repeat the Ruby method_missing method in Lua?

I am sure that in Lua you can use the specified meta-tags __index , __newindex and __call for rude Ruby method_missing replication. And I have a few:

 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 method missing!", ...) end end) test_print("I like me some method_missing abuse!") print(this_should_be_nil) 

My problem is this: although the syntax is similar, and I can of course use it to replicate functionality, it introduces an interrupt error. Each variable that you use in the context of the table to which you apply method_missing is never zero, since I must return an object that can be called to pass the buck potential call from the index function to the actual call.

i.e. After defining the global method method_missing as described above, an attempt to call the undefined method "test_print" is performed as expected, but the value of test_print is not equal to zero during indexing and other methods / variables that do not respond, for example this_should_be_nil not equal to zero.

So can this trap be avoided? Or can't the syntax be bent to support this modification without changing the language itself? I assume that the difficulty is how in Ruby, indexing and calling are similar, whereas in Lua they are different.

+6
source share
3 answers

You can avoid this problem by running nil .
Unfortunately, this can only be done from host code (i.e. C programs), and not from a Lua script.

Pascal Code:

 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; 

Output:

 true This method is under construction 
+3
source

You have well defined the problem: I do not know how it is possible to solve this problem in pure Lua.

EDIT: I was wrong, you can make nil callable. See Other Answers. This is still a bad IMO idea. The main use method_missing is proxy objects, and you can solve it differently. method_missing on Kernel (Ruby) / _G (Lua) is awful :)

What you can do is to process only some methods, for example, if you know that you expect methods starting with 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 method missing!", ...) end end) test_print("I like me some method_missing abuse!") print(this_should_be_nil) 

Now, maybe the question should be: why do you want to replicate method_missing , and can you avoid it? Even in Ruby, it is recommended that you avoid using method_missing and, if possible, prefer dynamic method generation.

+2
source

So, with a hint from @lhf, I dealt with a passive double (from what I can say) method_missing . As a result, I developed the following:

 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 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? 

Output:

 Oh my lord, it method missing! I like me some method_missing abuse! Oh my lord, it method missing! Do it again! nil method_missing magic! true true conditionally due to args 

This snippet allows you to use method_missing quite similar to how you can work in Ruby (but don't check the answer at the same time). This is similar to my original answer, except that it “passes the dollar” through nil metatable, which I thought I couldn’t do. (thanks for the tip!) But, as @greatwolf says, there is probably no reason to use a construct like this in Lua; the same dynamism can probably be achieved through clearer manipulations with metamethods.

+1
source

Source: https://habr.com/ru/post/957377/


All Articles