Problems with metatable

Hey there, I’m currently messing with metatables and datas, you can have a look at my constructor here:

function constructMetatbl(originalTbl, callback)
	local changedEvent, events 
	
	
	local proxy = setmetatable({}, {
		__index = function(self, key)
			return rawget(originalTbl, key)
		end,
		__newindex = function(self, key, v)
			if callback then
				callback(key, v)	
			end
			
			if changedEvent and events then
				changedEvent:Fire(key, v)
				
				if events[key] then
					for i, bindable in pairs(events[key]) do
						bindable:Fire(v)
					end	
				end				
			end
			
			return rawset(originalTbl, key, v)
		end,
	})
	
	for i, v in pairs(originalTbl) do
		if type(v) == "table" then
			rawset(proxy, i, constructMetatbl(v, callback))
		end
	end		
	
	changedEvent, events = addChangedEvent(proxy)
	
	return proxy	
end

My first question is is it possible to loop through a metatable ? Because when I loop the whole table it doesn’t print out anything because there’s technically nothing in a metatable.

Second of all,

   for i, v in pairs(originalTbl) do
		if type(v) == "table" then
			rawset(proxy, i, constructMetatbl(v, callback))
		end
	end

This recursion loops through the original table and construct another metatable if the value is a table, then I set that metatable to the prior table. However, doing so would restrain me from calling that particular key that was assigned to because it already exists, so the __index metamethod wouldn’t be called.

Lets say this is my metatable:

local metatable = { -- first metatable
    key = {
    -- another metatable, lets say it has a key named "a" with a value of "b"
    }
}

If I was to call metatable.key, it would print out nil, but if it was metatable.key.a it would print out "b".
I have tried altering the smaller metatable parent to the orignal table like so rawset(originalTbl, i, constructMetatbl(v, callback)). But this just seems to facilitate more errors, is there any workarounds to this problem ?

Any help is appreciated profusely!

1 Like

not sure, but in this part it should looks like

local proxy = setmetatable(originalTbl,{})

For first question, getmetatable(x) should return the metatable of table x. Can loop through what gets returned.

Wouldn’t that not work ? I mean like the __index and __newindex metamethods won’t be fired as the keys are already in there, which is why I needed a proxy table (Dummy table) to accomplish so.

Unsure if this would work or not as I’m returning a proxy table so doing getmetatable would just point to the metatable (I’m not setting the metatable to the orignal table)

What he’s saying is you can just store this getmetatable(x) into some local variable, and then use your recursion loop to loop through that new variable to do whatever you wanted to do with table x’s metatable.

But I didn’t set the original table to that metatable, so returning that original table and do getmetatable(orignalTable) would not return anything as there’s no metatables attached to it

Let me start by apologizing if I’m struggling to follow your first question.

When you say:

I’m not sure if you’ve misstated your thoughts in that sentence or not. Assuming the later, a metatable is just like any other table in Lua. What makes it special is the way it’s used (to modify behavior of another table). It’s still fundamentally a table. In other words, a metatable is a table, so it can have stuff in it and that stuff can be printed like any other table. If we agree on that, then I’ve probably missed the deeper question.

Just for clarity, it isn’t a metatable that is returned by setmetatable(); it returns whatever table you passed as the first arg. It attaches to that table a metatable that is just the table you passed in as the second arg. In my earlier comment, I was suggesting that you call getmetatable(x) with x = the thing returned from your constructor, the proxy table—because that’s a table with an assoc metatable you defined. The function getmetatable() does return a metatable, which in this case should contain the __index and __newindex you wrote, and it should be possible to loop over and print the contents of that table.

If that was all clear already, then I apologize; I’m way out of my depth because I don’t even understand what appears to be the easiest of your questions :grinning:

1 Like

Hey its totally okay, I wholeheartedly appreciate the effort you’ve put into this topic.

Looking back at my post I should have said that there’s nothing in a proxy table, not in a metatable, sorry in advance.

The main problem I’ve identified is that when you loop through a proxy table, it doesn’t return anything, so I would have to point to the original table. I don’t think there are any alternatives besides storing the orignalTbl in the metatable and retrieve it using getmetatable(x).orignalTbl, somewhat similar to what you’ve advised. (I would be more than happy for you to prove me wrong, no seriously :smiley: )

Let me clear up my 2nd question, it is closely connected to my first question since they are practically referring to the same aspect.

And solving question 1 also solves question 2 on itself with just 1 single line of code

Although I’m trying to find a more efficient way to do so, calling the getmetatable(x).orignalTbl in every script gets kinda hideous overtime lol

Thanks for your immense contribution!

It’s getting kinda exasperating, I think this explains itself:

if (tbl[key] or tbl.originalTbl[key]) or (templateTbl and (templateTbl[key] or templateTbl.originalTbl[key])) then

yeah…