Any method to check when setmetatable is called on a table?

In short, I’m trying to find a method which is fired when setmetatable is called on a table, provides the metatable which was attempted to be set, then enables you to return the desired metatable (similar to UpdateAsync):

local myTable = {}
local myMetatable = {__someMagicalMathod = function(originalMetatable, metatableWhichWasAttemptedToBeSet)
	-- do stuff with metatableWhichWasAttemptedToBeSet
	return originalMetatable -- use the returned metatable (in this case, keep the original)
end}
setmetatable(myTable, myMetatable)

local newMetatable = {__index = function() end}
setmetatable(myTable, newMetatable) -- triggers __someMagicalMathod

Looking at the metatables wiki, there appears to be no metamethods to achieve this.

Any thoughts on how to this can be done? Cheers.

There is no metamethod for that. This seems like a really weird thing to be doing. What exactly are you trying to achieve with this?

If you are really intent on producing some special behavior from setmetatable, you can always just create a wrapper function for it along these lines:

local function setmetatableWrapped(t, mt)
	local old = getmetatable(t)
	setmetatable(t, mt)
	return old
end

(note that setmetatable normally returns the (non-meta) table, but here it is returning the old metatable).

Again - I am pretty sure there will be a nicer way to accomplish what you’re trying to do.

4 Likes

Is the __metatable method what you’re looking for or am I wrong, the wiki says it errors when you call setmetatable upon a table.

I guess another thing you can do is, for example using the __call metamethod, when a function is called, set its metatable, have a variable outside saying wether a metatable has been set before or not, and each time __call is triggered, check if it was set before.

Or simply, have a wrapper function as @sircfenner suggested, that sets the metatable, and has the checking with that variable that I mentioned.

local set = false
local function setmetatable2(t, mt)
     if not(set) hen 
         setmetatable(t, mt)
         return t
    else
         --do the stuff you wanna do
    else

I don’t think you have to detect it. Shouldn’t firing a function or something right after setmetatable is called on a table.

function stuff()
end

setmetatable(myTable, myMetatable)
stuff()

You could try doing something like this, not sure how great it’d work in practice.

local env = getfenv();
local oldSMT = env.setmetatable;

env.setmetatable = function(t, mt)
	local current = getmetatable(t);
	
	if (current and current.__metaupdate ~= nil) then
		local new = current.__metaupdate(current, mt);
		
		if (new ~= nil) then
			oldSMT(t, new);
			return;
		end
	end
	
	oldSMT(t, mt);
end

setfenv(1, env);
1 Like