Metatable Lookup Rules

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Metatable Lookup Rules

ThePhD
Almost a year ago, I presented at Lua Workshop and talked about sol2[1] and having a Zero-Overhead abstraction built in C++. In particular, one of the things I noted was that because of being forced to have to return desired properties and functions of a userdata using a `lua_CFunction` rather than hitting a pre-filled Lua lookup table, users binding variables and properties would take a 2x-4x performance hit at all times.

This was mostly because we not only had to prep a proper Lua C Function to return (e.g., upvalues as a way to communicate pointers to C structures after returning the wrapped function to Lua), but that we had to enter a C Function in the first place which presented a measurable overhead over having a metatable with the functions already laid out on the type.

At the end of my talk, I briefly mentioned that a possible solution would be including the "initial missed target" that began the cascading chain of `__index` or `__newindex` lookups as a new argument at the end of the current argument list __index and __newindex. That is (for this contrived example):

function new_index ( last_failed_lookup_object, key, value ) {
    ....
}

inner_mt = { __newindex = new_index }
setmetatable(inner_mt, inner_mt)
mt = { __newindex = inner_mt }

local t = {}
setmetatable(t, mt)

would be allowed to look like...

function new_index ( last_failed_lookup_object, key, lookup_starter ) {

}

Has any thought been given to this since then? Is this something I should hack on to the Lua 5.3 source and see what it takes? I was mostly interested in this solution as -- to my mind -- it is backwards-compatible with older code since passing an extra argument to an old-style "new_index" or "index" function would not disturb what's already present and how it works.

[1] - https://youtu.be/NAox5UsjbUM?t=27m43s
Reply | Threaded
Open this post in threaded view
|

Re: Metatable Lookup Rules

ThePhD
Apologies, I had sent that code while writing Typescript, and it isn't valid Lua code. A more proper example (with sample output):

-----------Code:------------------
function new_index ( last_failed_lookup_object, key, value )
    print("lookup- ", last_failed_lookup_object)
    print("key   - ", key)
    print("value - ", value)
end

inner_mt = { __newindex = new_index }
setmetatable(inner_mt, inner_mt)
mt = { __newindex = inner_mt }

local t = {}
setmetatable(t, mt)

print("t is  - ", t)
t.hello = "world"

-----------Output:----------------
t is  - table: 0x1c98090
lookup- table: 0x1c97fb0
key   - hello
value - world


Notice that the table passed for lookup is different from the table that caused the lookup. My idea stems from adding that last parameter "lookup_starter", that -- no matter how many metatables are chained through -- would always be "t".

On Sun, Sep 24, 2017 at 5:16 PM, ThePhD <[hidden email]> wrote:
Almost a year ago, I presented at Lua Workshop and talked about sol2[1] and having a Zero-Overhead abstraction built in C++. In particular, one of the things I noted was that because of being forced to have to return desired properties and functions of a userdata using a `lua_CFunction` rather than hitting a pre-filled Lua lookup table, users binding variables and properties would take a 2x-4x performance hit at all times.

This was mostly because we not only had to prep a proper Lua C Function to return (e.g., upvalues as a way to communicate pointers to C structures after returning the wrapped function to Lua), but that we had to enter a C Function in the first place which presented a measurable overhead over having a metatable with the functions already laid out on the type.

At the end of my talk, I briefly mentioned that a possible solution would be including the "initial missed target" that began the cascading chain of `__index` or `__newindex` lookups as a new argument at the end of the current argument list __index and __newindex. That is (for this contrived example):

function new_index ( last_failed_lookup_object, key, value ) {
    ....
}

inner_mt = { __newindex = new_index }
setmetatable(inner_mt, inner_mt)
mt = { __newindex = inner_mt }

local t = {}
setmetatable(t, mt)

would be allowed to look like...

function new_index ( last_failed_lookup_object, key, lookup_starter ) {

}

Has any thought been given to this since then? Is this something I should hack on to the Lua 5.3 source and see what it takes? I was mostly interested in this solution as -- to my mind -- it is backwards-compatible with older code since passing an extra argument to an old-style "new_index" or "index" function would not disturb what's already present and how it works.

[1] - https://youtu.be/NAox5UsjbUM?t=27m43s

Reply | Threaded
Open this post in threaded view
|

Re: Metatable Lookup Rules

Viacheslav Usov
In reply to this post by ThePhD
On Sun, Sep 24, 2017 at 11:16 PM, ThePhD <[hidden email]> wrote:

> At the end of my talk, I briefly mentioned that a possible solution would be including the "initial missed target" that began the cascading chain of `__index` or `__newindex` lookups as a new argument at the end of the current argument list __index and __newindex.

Let's say for a given table (T) we have an __index metamethod that is a table (M) that has itself an __index metamethod, which is a function (F). Suppose your request is satisfied and the function gets the original table as an additional argument. Then, unless I am mistaken, if F returns a non-nil value, both T and M will get a new key assigned with that value.

In certain cases, that might be undesirable. For example, we want to implement a "class" with constant generic "methods" (generic in the sense that they work whatever the content of T is, where T is one of the class, and have no upvalues bound to T-specific data) and variable T-specific "properties", then M should ideally only have those constant methods, and T should get "properties" from F without polluting M. How could we implement that efficiently?

Cheers,
V.

Reply | Threaded
Open this post in threaded view
|

Re: Metatable Lookup Rules

Javier Guerra Giraldez
On 25 September 2017 at 15:14, Viacheslav Usov <[hidden email]> wrote:
> as an additional argument. Then, unless I am mistaken, if F returns a
> non-nil value, both T and M will get a new key assigned with that value.

Lua won't modify either table by itself, it's up to the function F to
do it if you want.




--
Javier

Reply | Threaded
Open this post in threaded view
|

Re: Metatable Lookup Rules

Viacheslav Usov
On Mon, Sep 25, 2017 at 5:15 PM, Javier Guerra Giraldez <[hidden email]> wrote:

> Lua won't modify either table by itself, it's up to the function F to do it if you want.

Thanks for dispelling my misconception.

Then I would say the proposal by ThePhD would be really helpful in exposing "methods" and "properties" efficiently.

Cheers,
V.