Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Help with Metamethods, __index not firing?

Hi,

I am unsure as to why the __index metamethod isn't firing. This is my first time working with metatables, so I might be doing something which is obviously wrong.

local SBD = {}

Unit = {}
Unit.__index = Unit

function Unit.new(inst)
    return setmetatable({
	unit = inst,
	active = false,
	mode = 0
		}, Unit)
end

for _,v in pairs(script.Parent:GetChildren()) do
	if v:IsA("BaseScript") then return end
	SBD[v] = Unit.new(v)
	SBD[v].__index = function(t, field)
		print("why isn't this firing?")
	end
end

print("ok got here")

print(SBD[script.Parent.Model].unit) --isn't this supposed to fire __index??

My output reads:

ok got here

Model

Thank you for your efforts.

1 answer

✓ Accepted answer

The __index metamethod is only invoked if the index you're searching for is nil. In this case, your Unit class returns a table with the following contents: unit, active, and mode. You later then try to index the object you created with Unit.new, for unit, which already exists, therefore the metamethod will not be invoked.

Also, when you're setting a new __index to your Unit object, you're storing that inside your normal table, not it's metatable. In other words, after your for loop, your Unit object looks like this:

local SBD = {
	[script] = {
		-- Unit properties after loop
		unit = script;
		active = false;
		mode = 0;
		__index = function: address
	}
}

Meanwhile, the metatable to that object still only reads {__index = Unit} So, even if you indexed your object for a key that doesn't exist, it would still print nil because it's not inside the Unit class. If you want to make it so you can invoke __index whenever your object is indexed, you'll have to store everything somewhere else and leave your table empty. After that, you can set __index to a function, do whatever you want, and return the value from the new table you're storing everything in. Example:

local SBD = {}

local Unit = {}
Unit.__index = Unit

local function NewUnit(instance)
	-- Create a reference to the object
	local object = {
		unit = instance;
		active = false;
		mode = 0;
	}

	-- Create new interface for the object
	-- (empty table, with __index that returns what's inside object)
	local interface = setmetatable({}, {
		__index = function(self, k) 
			print("__index invoked") -- What you want to happen
			return object[k] -- Return the index from object
		end;

		-- Set any new index request to object, not this proxy
		__newindex = function(self, k, v)
			object[k] = v -- Set new key/value to object instead
		end
	})

	-- May as well set this new index to SBD while we're at it
	SBD[instance] = interface

	-- Return interface
	return interface
end

local unit1 = NewUnit(Instance.new("Script"))

print(unit1.unit) -- > "__index invoked",  "Script"
print(unit1.x) -- > "__index invoked",  "nil"
print(select(2, next(SBD)).unit) -- > "__index invoked",  "Script"

Now no matter what, __index will be invoked because we'll always be indexing an empty table. Hope this helped, if you have any questions, just let me know.

1 comment

0

Thank you! This helped me so much

This discussion is preserved as it was. New answers, comments, and votes are closed.