PDA

View Full Version : Lua: Catching calls of non-existing functions.


Jan
05-29-2006, 06:10 AM
Hi

I hope someone here can help me, because the "official" Lua-forum is so inactive, that posting there is pointless.

I do "OOP" (if one can call it that) with Lua. Ie:


a =
{
member = 20
}

function a:foo (sender, value)
print (value)

sender:bar (value * 2)
end


Suppose, that "a" wants to call "bar" on "sender", if it exists. However, if "bar" does not exist, the function call should be ignored and NOT RAISE AN ERROR.

I tried setting the "__call" metamethod for every "class" to handle the situation that "sender.bar" is a nil-value. However, my "__call" metamethod seems never to be called itself. Instead my application tells me, through "lua_pcall", that an error occured.

I tried something like this (for every class):

self.__call = function (func, ...)
if (type (func) == "function") then
func (...)
else
print ("catched call to a non-existing function")
end
end


Any ideas, how to do this?

Thanks,
Jan.

Reedbeta
05-29-2006, 09:00 AM
The __call method has to go in the object's metatable, not in the table itself. You can use the setmetatable() function to do this, or (I think) just assign the metatable to the member "__metatable".

Jan
05-29-2006, 10:03 AM
Yes, this was only a code-snippet, i also do:

setmetatable (object, self)

Well, i also set the __index metamethod to handle single-inheritance and that works just fine, so i assumed i do it the right way, but it still doesn't work.

Jan.

Reedbeta
05-29-2006, 12:02 PM
Okay, after a little investigation, I found out that the __call metamethod isn't for resolving calls to members. It only resolves calls to the table itself (analogy would be "operator()" in C++, I suppose). So if "myTable" has a __call, then you can do

myTable(1); // invokes __call

but not

myTable:someFunc(1); // does not invoke __call

The second expression first invokes __index to look up the key "someFunc", then attempts to call the result of that lookup. If it's not a function, or a table with a __call metamethod, the call fails. You also can't call a nil value, which is somewhat surprising to me. You might just have to build this error-ignoring behavior into the C framework that you're calling Lua from. Or your __index could return a table with a single do-nothing __call metamethod whenever the lookup fails...but that seems ugly to me.

Jan
05-29-2006, 02:12 PM
Hm, actually i don't think that the __index idea is ugly. I mean, in the end, it is only a work-around for an already ugly case - the case that a non-existing function is called. However, i think it is less ugly and less error-prone, than to force the user (who might not know much about programming) to do this:


function a:foo (Sender)
if (Sender) then
if (Sender.bar) then
Sender:bar (42)
end
end
end


I think it is enough of a burden to be forced to do:



function a:foo (Sender)
if (Sender) then
Sender:bar (42)
end
end



I will investigate this idea and post my experience when i'm done.

Thanks,
Jan.

Jare
05-30-2006, 04:50 PM
As long as your non-existing-method idea only supports calling functions that can return nil, it's a fine fix.

If you create your isntances from a class by doing something like

instance = setmetatable({}, { __index = class }) -- empty object, take defaults from class

Then this would make any member not explicitly defined in the class to be an empty function:

function SetClassDefaultMethod(class)
setmetatable(class, { __index = function() end })
end

If you want to support inheritance where getmetatable(class).__index points to the class' parent, then you'll have to walk the chain of metatables until you find the root:

function SetClassDefaultMethod(class)
while getmetatable(class) ~= nil and type(getmetatable(class).__index) == "table" do
class = getmetatable(class).__index
end
setmetatable(class, { __index = function() end })
end

Written off the top of my head but something along those lines should work.