lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

     I hope this message finds you well.

     I am developing a lua <-> C++ binding (like the several other that exist), and I've made significant headway into getting a very performant and easy to use wrapper. My final challenge in increasing the performance comes from trying to make basic "variable" get/sets on userdata made by C code work.

     The Question: Is there a way to get the original object that a lookup was performed on in a cascading series of `__index` queries? Right now, lua hands you the current object in the `__index` cascade that lookup failed on and the key name it is trying to find. This is all well and good and based on the specification, but my problem is that I want what the original lookup failed on (e.g. the userdata that started it all, in my case).

This tree might help explain what my dilemma is (pseudo code):

myuserdata -- userdata -- metatable -- table -- second metatable -- function that performs variable lookup

When I do
myuserdata.x -- cascades down to second function, but the provided "lookup failed" argument is, the table, not myuserdata. How do I get to 'userdata' ?

     Details: I used to have it working by overriding the `__index` metamethod of the userdata and then connecting a C function to it. The C function had a list of all the functions and variables for that type, and dispatched accordingly. Because the `__index` lookup failed on the userdata's metatable (I only had 1 metatable on the userdata), I would get the userdata as the "lookup failed on this" object. Calling my C function to get either a variable (instance.a) or a function (instance:bark) had worked because I could establish the proper context with the userdata and go forward. Unfortunately, this approach was about ~2x slow thanks to having to route requests through the C function, taking a significant performance hit to not just looking up member variables but calling member functions from lua that showed heavily in all benchmarks.

     I made things faster by changing the userdata's metatable to a table instead of a C function. The table had all of the non-metamethods that I wanted to provide for the userdata I had created in C. This worked, but I still needed something to hold the variables that would get found eventually by an `__index` call. I did not mind if variable getting/setting was slow, as most people using the framework use member functions and expect those to be performant.

     So, I made a metatable and then put all of the variables in a C function that I linked on that metatable's `__index` method. I think set this secondary, variable-lookup metatable to the I then linked this  The problem is now that I get the table as the "lookup failed on this" object, meaning I cannot get the userdata and appropriately substitute the "get/set" syntax style.

     NOTE: I noticed that in Lua 5.1, 5.2, and 5.3, the Lua C API "leaves" the original userdata below the bottom of the stack (in my specific case, 2 stack values underneath it). The lua API doesn't stop me from digging past API-specified beginning of the stack, so a dirty hack I'm compelled to do is to simply do a stack get at -4 (to result in -2 and get the userdata), but I'm obviously relying on Undefined Behavior if I'm doing that to work, even if it's present in all of the versions of the API I care about.