How to get if table is called with '.' or ':' at __index metamethod

for example:

-- in module
local BasicSound = {}
BasicSound .__index = function(t,i)
	if BasicSound [i] then return BasicSound[i] end --if BasicSound has required index, return its
	return t.object[i] --if it doesn't have, return actual object's
end

function module:Pause()
self.Pause = true
end

function module.new(SoundObject:Sound)
	local Sound = setmetatable({Volume = 15, Echo = true},BasicSound)
	Sound.Object = SoundObject
	return Sound
end
--in script
local Sound = module.new(workspace.Sound)
Sound.Name = "Sound1" --> Perfect
Sound:Pause() -->Perfect
Sound:Play() --> Expected ':' not '.' calling member function Play

I can’t know how to solve this :frowning:
Could you help me?

I am not entirely sure if you can do that. When you call a function using :, you’re basically passing over the index of the metatable you declared earlier. Then the __index method searches through BasicSound it seems for the function (as with regular .__index = BasicSound behavior. The issue I’m seeing here is that the sound instance’s methods dont have the same index or behavior as your regular functions, since their functions are called with the sound instance’s index and not your “BasicSound” index. If you want this to work you’d have to somehow compare the names of the function called and search for it in the sound methods, or just have a function called :Play() that just calls the sound :Play method.

1 Like

I’ve asked this before and __index only covers ‘.’ indexes

The way I got around it was by putting the function in the custom module

In order to use table:thing() you must use __index but have __index return a function. I’m bad at object oriented programming, but here’s what you would use to make this call:

local tabe = {}

local playFunction=function()
warn(“Got called”)
–(do your stuff here)
end

local meta = setmetatable(tabe,{
__index=function(tab,ind)
if ind==“Play” then
return playFunction
end
end
})

tabe:Play()

Here’s why you have to do that, too: When you call a function from within a table, the first thing that table:action() does is find that function in the table. Then, once it has the function, it attempts to call it, hence, why returning a function works, and why you get “Expected ‘:’ not ‘.’ calling member function Play” when using returning a simple value rather than a function.

if these were plain old non meta tables, you would essentially be doing this:
Your previous code: a={test=1} a.test() → error
This: a={test=function() return 1 end} a.test() → returns 1

local BasicSound = {}
BasicSound.__index = BasicSound
-- your constructors here

local newsound = BasicSound.new()
newsound:Play() -- perfect
newsound:Stop() -- perfect
print(newsound.Volume) -- perfect

Don’t ask why, your brain will make it make sense eventually

Atleast that’s how I learned it

local object = {}
local meta = {}
function meta.__index(_)
    return function(parameter)
        if object == parameter then
            -- it's called with a ':'
            print(':')
        else
            -- it's called with a '.'
            print('.')
        end
    end
end
setmetatable(object, meta)
object:method() -- ':'
object.method(object) -- ':'
object.function()

It won’t work when the first parameter is the table itself, but OOP in Lua is very funky so it won’t cause any problems.

By the way, the code you included doesn’t work because you can’t call a method on an instance with the first parameter being a value that isn’t the instance. You will need to make a proxy function that calls the method on the instance when it’s called. The code below will work.

local BasicSound = {}
local function proxy(object, name)
     return function(_, ...)
          object[name](object, ...)
     end
end
BasicSound .__index = function(t,i)
	if BasicSound [i] then return BasicSound[i] end --if BasicSound has required index, return its
	return proxy(t.object, i) --if it doesn't have, return actual object's
end
1 Like