This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
KingofGamesYami's profile picture

Messing With Metatables

Started by KingofGamesYami, 28 December 2015 - 01:47 PM
KingofGamesYami #1
Posted 28 December 2015 - 02:47 PM
Spoiler

local function new( object )
	local mt = {}
	for k, v in pairs( getmetatable( object ) ) do
		mt[ k ] = v
	end
	mt.inherits[ #mt.inherits + 1 ] = object.class()
	mt.super = object
	return setmetatable( {}, mt )
end

local Object = setmetatable( {}, {
	__call = function( t, ... )
		local tArgs = { ... }
		if type( tArgs[ 1 ] ) == "table" and tArgs[ 1 ].name then
			local n = new( t )
			local mt = getmetatable( n )
			mt.inherits[ #mt.inherits + 1 ] = tArgs[ 1 ].name
			for k, v in pairs( tArgs[ 1 ] ) do
				n[ k ] = v
			end
			return n
		end
		return new( t )
	end,

	inherits = {"Object"},

	__super = {},

	__index = function( t, k )
		return t[ k ] or getmetatable( t ).__super[ k ]
	end,

	__newindex = function( t, k, v )
		if type( v ) == "function" then
			local env = getfenv( v )
			env.this, env.super = t, getmetatable( t ).__super
		end
		rawset( t, k, v )
	end,
})

Object.class = function()
	local inherits = getmetatable( this ).inherits
	return inherits[ #inherits ]
end

Object.extends = function( s )
	local inherits = getmetatable( this ).inherits
	for k, v in pairs( inherits ) do
		if v == s then
			return true
		end
	end
	return false
end

Window = Object{
	name = "Window",
}

For some reason, calling Object.class() after declaring Window returns "Window" - obviously I don't want that to happen. In addition, calling Window.class() is doing something weird, I get java.lang.arrayIndexOutofBoundsException - it's got to be going in circles somehow.
Creator #2
Posted 28 December 2015 - 02:53 PM
At a first glance, you don't declare "this" as a table anywhere.

That is the only thing I can find.
Edited on 28 December 2015 - 01:51 PM
SquidDev #3
Posted 28 December 2015 - 02:56 PM

 __index = function( t, k )
    return t[ k ] or getmetatable( t ).__super[ k ]
end,

This will result in an infinite loop as you are calling t[k] which will call __index.
KingofGamesYami #4
Posted 28 December 2015 - 03:09 PM
Good catch SquidDev.


	__index = function( t, k )
		return rawget( t, k ) or getmetatable( t ).__super[ k ]
	end,

That eliminates the ArrayIndexOutOfBounds problem, but the Object class is still becoming "Window".

Oh, and I get an error on line 6:

	mt.inherits[ #mt.inherits + 1 ] = object.class()

Which indicates object.class is nil (specifically, attempt to call nil).

@Creator: I do declare this. Env fun:


	__newindex = function( t, k, v )
		if type( v ) == "function" then
			local env = getfenv( v )
			env.this, env.super = t, getmetatable( t ).__super
		end
		rawset( t, k, v )
	end,
Edited on 28 December 2015 - 02:19 PM
Creator #5
Posted 28 December 2015 - 03:42 PM
How did I oversee this? I looked at the code for like 5 minutes.
KingofGamesYami #6
Posted 28 December 2015 - 04:56 PM
I fixed Object.class() returning "Window", but now Window.class() returns "Object".

Spoiler

local function new( object )
	local mt = {}
	for k, v in pairs( getmetatable( object ) ) do
		mt[ k ] = v
	end
	mt.inherits = { object.class(), unpack( mt.inherits ) }
	mt.__super = object
	return setmetatable( {}, mt )
end

local Object = setmetatable( {}, {
	__call = function( t, ... )
		local tArgs = { ... }
		if type( tArgs[ 1 ] ) == "table" and tArgs[ 1 ].name then
			local n = new( t )
			local mt = getmetatable( n )
			mt.inherits = { tArgs[ 1 ].name, unpack( mt.inherits ) }
			--mt.inherits[ #mt.inherits + 1 ] = tArgs[ 1 ].name
			for k, v in pairs( tArgs[ 1 ] ) do
				n[ k ] = v
			end
			return n
		end
		return new( t )
	end,

	inherits = { "Object" },

	__super = {},

	__index = function( t, k )
		return rawget( t, k ) or getmetatable( t ).__super[ k ]
	end,

	__newindex = function( t, k, v )
		if type( v ) == "function" then
			local env = getfenv( v )
			env.this, env.super = t, getmetatable( t ).__super
		end
		rawset( t, k, v )
	end,
})

Object.name = "Object"

Object.class = function()
	local inherits = getmetatable( this ).inherits
	return inherits[ 1 ]
end

Object.extends = function( s )
	local inherits = getmetatable( this ).inherits
	for k, v in pairs( inherits ) do
		if v == s then
			return true
		end
	end
	return false
end

Window = Object {
	name = "Window",
}

local w = Window();

print( Object.class() )
print( Window.class() )
KingofGamesYami #7
Posted 28 December 2015 - 05:10 PM
Oh goody, it's a problem with function environments. Now to handle this in the most hackish way possible…


Edit: I now broke everything by trying to move the environment stuff into __index. It really should work though… :/

Edit #2: I think I fixed it, but now Object thinks it extends it's own subclasses. *Screams*

Spoiler
local function new( object )
	local mt = {}
	for k, v in pairs( getmetatable( object ) ) do
		mt[ k ] = v
	end
	mt.inherits = { object.class(), unpack( mt.inherits ) }
	mt.__super = object
	mt.realValues = {}
	return setmetatable( {}, mt )
end

local Object = setmetatable( {}, {
	__call = function( t, ... )
		local tArgs = { ... }
		if type( tArgs[ 1 ] ) == "table" and tArgs[ 1 ].name then
			local n = new( t )
			local mt = getmetatable( n )
			mt.inherits[ 1 ] = tArgs[ 1 ].name
			for k, v in pairs( tArgs[ 1 ] ) do
				n[ k ] = v
			end
			return n
		end
		return new( t )
	end,

	inherits = { "Object" },

	realValues = {},

	__super = {},

	__index = function( t, k )
		local result = rawget( getmetatable( t ).realValues, k ) or getmetatable( t ).__super[ k ]
		if type( result ) == "function" then
			local env = getfenv( v )
			env.this, env.super = t, getmetatable( t ).__super
		end
		return result
	end,

	__newindex = function( t, k, v )
		getmetatable( t ).realValues[ k ] = v
	end,
})

Object.Object = function() end

Object.name = "Object"

Object.class = function()
	local inherits = getmetatable( this ).inherits
	return inherits[ 1 ]
end

Object.extends = function( s )
	local inherits = getmetatable( this ).inherits
	for k, v in pairs( inherits ) do
		if v == s then
			return true
		end
	end
	return false
end

Window = Object {
	name = "Window",
	identify = function()
		print( "A Window" )
	end,
}

local w = Window();

print( Object.extends( Window.class() ) )

Edit #3: code is now here: http://pastebin.com/reEf4fVk

How the heck does this work????

print( Object.extends( "Window" ) )
-->false
print( Window.class() )
-->Window
print( Object.extends( Window.class() ) )
-->true


Edit #4: Everything has been resolved now. I'd post the solutions, but I fixed so many problems that my fixes had to be fixed.
Edited on 29 December 2015 - 01:03 AM
Exerro #8
Posted 29 December 2015 - 10:56 PM
Just so you know, __index is only invoked if you try to access a key in a table that doesn't exist, so…


function mt.__index( t, k )
    return rawget( t, k ) or x -- x will always be returned
end
KingofGamesYami #9
Posted 29 December 2015 - 11:02 PM
Yeah, I know. That's why there are no keys in my table :)/>

Edit: It might be worth mentioning everything other than the pastebin is before I fixed everything. If you were looking at the code in the OP, that's understandable.
Edited on 29 December 2015 - 10:04 PM