Jump to content


Lua: Catching calls of non-existing functions.


  • You cannot reply to this topic
5 replies to this topic

#1 Jan

    Member

  • Members
  • PipPip
  • 51 posts

Posted 29 May 2006 - 01:10 PM

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.

#2 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 29 May 2006 - 04:00 PM

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".
reedbeta.com - developer blog, OpenGL demos, and other projects

#3 Jan

    Member

  • Members
  • PipPip
  • 51 posts

Posted 29 May 2006 - 05:03 PM

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.

#4 Reedbeta

    DevMaster Staff

  • Administrators
  • 4979 posts
  • LocationBellevue, WA

Posted 29 May 2006 - 07: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.
reedbeta.com - developer blog, OpenGL demos, and other projects

#5 Jan

    Member

  • Members
  • PipPip
  • 51 posts

Posted 29 May 2006 - 09: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.

#6 Jare

    Valued Member

  • Members
  • PipPipPip
  • 247 posts

Posted 30 May 2006 - 11: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.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users