- __index(self, key) - Used when indexing into a table to retrieve a value ex: tab["key"]
- __newindex(self, key, value) - Used when indexing into a table to assign to a value.
- __call(...) - Used when a function call operator is used on the type ex: tab()
- __add(left, right) - Used when adding together two objects using "+"
- __eq(left, right) - Used when checking if two variables are equal ex: a == b
- __set(left, right) - Used when setting one variable to another.
- __tostring() - Used when converting a variable to string form.
Meta tables for simple types
Due to this you must write additional logic inside of these functions which will determine how exactly these operations are performed. A useful tool in this is by using the type function which will return the basic type of a variable. Pay close attention to the "basic type" in that last sentence, lua is not capable of determining user-defined types you have created by using meta tables and will just return "table" (or "number" if it's a number.) What we will discuss doing later on and I recommend doing it as well is having a field inside of your meta tables with the name of the type for you to double check against.
Indexable userdata variables
Step 1: Store all your meta tables in a table
The more you know:
Unlike most variables in lua, tables are handled by reference instead of by value. What this means is that when you assign a variable to equal an existing table you aren't actually creating a new copy of the original table, just a reference to it. Think of shallow copying vs deep copying in C++. To make a real copy of a table you will have to make a helper function to do this for you. (Reference Manual)
Step 2: Make a SetupMetatable function
After we have a table to store any additional values we check to see if the key we are searching for is inside of that table, if it is then we return that value and go on our merry way. If it doesn't happen to be there then we return the value for that key from our meta table. We do this because any C++ functions that we have reflected will be stored inside of our meta table and we still want to be able to call them.
We've left an important piece out though which is the function for writing to an index, the __newindex function which is very straight forward since all we have to do is ensure the external table exists, and then write to it.
Step 3: Setup each meta table
At this point we have a function for setting up the meta tables for our C++ types however we now need to actually run that function on each type. We do this by declaring a simple function which iterates through each meta table inside of _R and running the SetupMetatable function on each table. We aren't done yet though, we also need to declare the VARDATA table inside of _R otherwise our __index and __newindex functions will cause errors!
If you end up using the _R table of your lua types as well (which is something I also do) then make sure to perform this step before you declare any of your lua types otherwise strange things will happen. The way I accomplish this is by initializing my lua environment, run my first lua script which defines these functions, create meta tables for each C++ type, setup all of their meta tables, then load the rest of my lua types. An alternative solution however is to also reference your C++ type meta tables in another table somewhere and just iterate through that.
Getting rid of the annoying GetComponent function
Step 1: Component list
Step 2: The __index function for objects
If you had forgotten to make oldindex a local variable or if it were a table entry then this wouldn't work.