Re: metatable __call function does not operate as documented

163 views
Skip to first unread message
Message has been deleted

Sean Conner

unread,
May 9, 2024, 1:01:03 AM5/9/24
to lu...@googlegroups.com
It was thus said that the Great Lawrence Emke once stated:
> Here is the documentation:
>
> - *__call: *The call operation func(args). This event happens when Lua
> tries to call a non-function value (that is, func is not a function).
> The metamethod is looked up in func. If present, the metamethod is
> called with func as its first argument, followed by the arguments of the
> original call (args). All results of the call are the results of the
> operation. This is the only metamethod that allows multiple results.

Yes. The text could be made a bit clearer that func isn't a function but
some value that has a metatable (like a table or a userdata) with the __call
metamethod in it.

mt = { __call = function(...) print(...) end }
foo = setmetatable({},mt)
bar = setmetatable({},mt)

print(foo) foo(1,2,3)
print(bar) bar(9,8,7)

When I run the above, I get:

table: 0x9682800
table: 0x9682800 1 2 3
table: 0x9682758
table: 0x9682758 9 8 7

> I wrote a constructor "function obj.new(arg1) .... end
> and I used the standard __call function definition:
>
> setmetatable(obj, { __call = function(cls, ...)
> return cls.new(...)
> end
> })
>
> When I run the statement obj.new("this is a test") it works correctly
> If I run it as a call to the object rather than a function in the table
> (e.g ' local abc = obj("this is a test")', the "new" function is called;
> However, the constructor's argument is passed data type of "table".
> This causes the constructor to operate incorrectly.
>
> The fact is lua issues a function call to the __call function that is
> different than what it is suppose to be.
>
> I want to write a general purpose metatable value for the __call function
> value, so the constructor receives the correct parameters.

What are the "correct parameters"? Perhaps if you posted the code you
have, we could help more.

-spc

bil til

unread,
May 9, 2024, 5:16:35 AM5/9/24
to lu...@googlegroups.com
Am Do., 9. Mai 2024 um 02:51 Uhr schrieb Lawrence Emke
<lawren...@gmail.com>:
> Is there a way to determine how the lua function that calls the _call function is passing its data? Maybe a conversion function could be written to correct this incompatibility.
>

... but you know the function type from Lua baselib I assume? (
"type(cls)" would give you the variable type - you could check this
type in your __call function - see e. g. chapter "Types and Values" in
Roberto's book "Programming in Lua", or description of type function
in reference manual).
Message has been deleted

Roberto Ierusalimschy

unread,
May 9, 2024, 10:06:30 AM5/9/24
to lu...@googlegroups.com
> > Here is the documentation:
> >
> > - *__call: *The call operation func(args). This event happens when Lua
> > tries to call a non-function value (that is, func is not a function).
> > The metamethod is looked up in func. If present, the metamethod is
> > called with func as its first argument, followed by the arguments of the
> > original call (args). All results of the call are the results of the
> > operation. This is the only metamethod that allows multiple results.
>
> Yes. The text could be made a bit clearer that func isn't a function but
> some value that has a metatable (like a table or a userdata) with the __call
> metamethod in it.

"that is, func is not a function" is not clear enough?

-- Roberto
Message has been deleted

Sean Conner

unread,
May 9, 2024, 5:59:28 PM5/9/24
to lu...@googlegroups.com
It was thus said that the Great Roberto Ierusalimschy once stated:
I had to read that carefully myself. Perhaps if it was fojb---


- *__call: *The call operation fobj(args). This event happens when
Lua tries to call a non-function value (that is, fobj is not a
function). The metamethod is looked up in fobj. If present, the
metamethod is called with fobj as its first argument, followed by
the arguments of the original call (args). All results of the call
are the results of the operation. This is the only metamethod that
allows multiple results.

To me, it helps to separate that func (the orginal name) isn't a
FUNCtion---it's a visual separator or reminder.

-spc

Message has been deleted

Denn...@protonmail.com

unread,
May 10, 2024, 12:48:29 AM5/10/24
to lu...@googlegroups.com
First, the vararg expression (`...`) is not a table and is never
implicitly converted to a table. It is a series of data similar to
multiple returns of a function[1].

It's hard to tell what the issue is since there's not enough of your
actual code to see what's going wrong. However, if it's true that
you're receiving only a second table with the arguments, then your Lua
implementation is non-standard and you should try to read the
documentation of your specific implementation, if you can.

Have you tried testing this behavior in a clean environment, such as
just running the following code?

Table = {}
Metatable = { _call = function(self, ...)
print(...)
end }

Table = setmetatable(Table, Metatable)

Table("test") -- Should print "test"

If you believe your Lua implementation is standard, then you may be
doing something wrong with your parameters and arguments. If you don't
mind, please send snippets of the `Class.new` function definition
(just the name and parameters) as well as the `__call` metamethod in
its entirety. This would help get to the bottom of the issue much
more directly.

Regardless, I encourage you to keep going at this, as it's a fun
exercise. I hope you can figure out a solution soon.

[1]: Lua 5.4 Reference Manual 3.4.11, https://www.lua.org/manual/5.4/manual.html#3.4.11
See also, "Vararg The Second Class Citizen", http://lua-users.org/wiki/VarargTheSecondClassCitizen
Additionally, PIL online is Lua 5.0, which differs in that
varargs *are* stored in a table. Disregard this: it is not real.

bil til

unread,
May 10, 2024, 4:56:50 AM5/10/24
to lu...@googlegroups.com
Am Fr., 10. Mai 2024 um 04:21 Uhr schrieb Lawrence Emke
<lawren...@gmail.com>:
> The hope is this will make implementing "classes" in lua, simpler. The name of the module that I am developing is "Class".

Old man's wisdom: ... unfortunately "making implemenations (or even
life) simpler" is often a VERY complicated/ambitious task. Often only
at the end of several weeks or even months work you will see the
beauty of simple solutions and somehow come back to the roots - this
might be an interesting experience somehow but also a quite "time
consuming life loop" :) :( ... .

Strong recommendation from my side:

FIRST look VERY carefully, how Lua source code itself implements such
"meta-classes". A nice "demo proposal" e. g. is the file object / io
class (liolib.c), this contains already many tricky features... .

Try to apply this "creation scheme" to your class system FIRST (in any
details ... the "real tricks" are often somehow "hidden" in very few
lines of code... you will only recognize when you try to duplicate
this for your own application).

... when you did this successfully, only then try to do "further fancy
things" / improve this code for your application targets... .
Message has been deleted

Denn...@protonmail.com

unread,
May 10, 2024, 12:08:44 PM5/10/24
to lu...@googlegroups.com
Thank you for your response.

In your metamethod, you're calling `cls:new(args)`. This is different
from doing `cls.new(args)` in a very small but significant way. The
first form passes the value on the left side of the `:` (the cls table
in your case) as the first argument of the function call, while the
latter does not. I'm not sure if you intend to pass that. I think you
mentioned something to this effect by looking for a value after the
first argument in your function call, but I'm guessing the setup
may have changed between then and now.

Sorry if it sounds like I'm sending you in circles. If anything I say
doesn't work, then you can try to break it down into more explicit
chunks,

-- In your metatable
__call = function(cls, args)
local cls_new_function = cls.new -- Get the Class.new function explicitly
cls_new_function(args) -- Call that function with only args
end

-- In your testing code
local class_metatable = getmetatable(class)
local class_call = class_metatable.__call -- Get the __call metamethod explicitly
class_call(class, "class_name")

This should help you see clearly what arguments are being passed to
your functions. You can also add varargs at the end of all your
functions and print the varargs to see if there's anything you're
somehow not picking up.

Best of luck,
- Denneisk
Reply all
Reply to author
Forward
Message has been deleted
0 new messages