Playing with metatables

So recently I learned about the function

and I was trying to figure out what I could do with it. WELL I was looking through Lua documentation about how to work with userdata and I learned quite a bit! Without further adieu, here’s a short script that showcases some cool stuff I figured out how to make with some of the recources available on the internet!

–purpose: ceate an object which holds data that cannot be accessed by another unless the know the password.
–aka: a hidden table with one or more values which are not accessable without explicitly knowing the index.
local function account( password, money, frozen )

local money = tonumber(  money )
local password = tostring( password )
if not money or not password then return end

frozen = tobool( frozen )

local obj = newproxy( true ) --create a userdata object with a blank metatable

local mt = getmetatable( obj )
mt.__metatable = 42 --hide the metatable so that it can't be accessed outside of this scope

function mt:__newindex( key, value )--we'll store data in the metatable and let the userdata object act as a buffer
	mt[key] = value

obj[password] = money --now that the metatable allows the userdata to know what to do when it gets a newindex event, we won't error!
obj.frozen = frozen

function mt:__index( key ) --the __index event happens whenever you try to access the key of a table, eg. obj[password]

	if key == "frozen" then --always tell the user if this object is frozen
		return mt.frozen
	elseif mt.frozen then --if they're asking for any key other than the frozen key while the account is actually frozen, return nil
		return nil
		return mt[key] --if everything's good, just give them the value

function mt:__unm() --this function is called when the - operator is run on it, eg. -obj
	return account( password, mt[password], not mt.frozen )--return an object that is the same, except invert our frozen value

--this isn't really a "newindex" function, more of a "changeindex"
function mt:__newindex( key, value )

	if mt[key] and key ~= "frozen" then-- only accept changes to existing keys, and don't let them change the frozen key this way
		mt[key] = value

mt.__call = mt.__index -- so obj( password ) works just like obj.password or obj[password]

return obj


local obj = new( “mypassword”, 100000000 ) --holy moly im rich!
print( obj.frozen ) --defaults to not frozen
obj.frozen = true --try to change it manually
print( obj.frozen ) --can’t
obj = -obj --cinvert it through our unary minus operator
print( obj.frozen ) --worked!

print( obj( “im guessing at passwords here” ) ) --nil
print( obj[“look at all these ways I can guess passwords!”] ) --nil
print( obj.wow_metatables_are_cool ) --nil

print( obj( “mypassword” ) ) --ok I guess I’ll prove that it does actually work.
–oops it’s frozen!
obj = -obj --gotta unfreeze it through our unfreeze operator :smiley:
print( obj( “mypassword” ) ) --now it works!

for k,v in pairs( obj ) do --screw guessing, I’ll just print every key and value to see the password!
print( k, v )
end --hint: it doesn’t work!

–FINE. I’ll just get the metatable and loop through that!
for k,v in pairs( getmetatable(obj ) ) do
print( k, v )
end --hint:still doesn’t work :smiley:

Obviously this has a couple cool applications. Basically this allows you to make private variables that truly cannot be access except through a get/set function, which has obvious upsides. I’m sure that programmers more skilled than me can/will/have found better applications for this kind of implementation in Lua. I just wanted to share a cool little bit of GLua that I haven’t really known about before, thanks for reading! :smiley:

I’d suggest using a table as your object, rather than userdata from newproxy. If you’re super-intent on hiding the values you can always use __index and __newindex metamethods on a metatable of that instead.

(Tables can have metatables too, not just userdata)

The only reason I can see to use newproxy’s output rather than a table would be for certain metamethods that tables don’t support like __len.

Sure! The main goal was to make userdata that was only interact-able with through methods that the coder explicitly describes, as to prevent people from circumventing some sort of security measures or hidden implementation, or whatever you’d like.

Also just to try and find a use for userdata.

edit: One of the problems that I see when using a table instead of userdata is you could simply do what I show in my little script:
for k,v in pairs( obj ) do
print( k, v )
which would basically involve removing the __index metamethod, something I don’t really wanna do :confused:

userdata is generally used to implement some form of control over C objects (maybe that is a bad word to use) in to Lua, so if you’re super interested in it you should go write some C modules.

I’ll do that next then!

To clarify, userdata is just garbagecollected memory that you can ask Lua to allocate.