How can I implement the new __iter function to my proxy table?

Strange, when I run the code on Demo - Luau, I get the tables as results:

local function proxify(tbl: {any})
	local proxy = newproxy(true)
	local meta = getmetatable(proxy)

	meta.__index = function(self, key)
		local idx = tbl[key]
		if idx and type(idx) == "table" then
			idx = proxify(idx)
		end
		return idx
	end

	meta.__newindex = function(self, key, newValue)
		if tbl[key] ~= newValue then
			tbl[key] = newValue
		end
	end
	
	meta.__iter = function()
		return next, tbl
	end

	return proxy
end

local tbl = proxify({
	Timer = 0,
	Name = "",
	Modes = {
		Easy = {},
		Normal = {},
		Hard = {},
		Insane = {}
	}
})

for key, value in tbl.Modes do
	print(key, value)
end

There might be an issue between the site and the Roblox engine, but I can’t see for sure until I get access to my computer again tomorrow

My fault! I had put the wrong table into the __iter function. But now I am facing the issue when attempting to use table.insert function on the tbl.

image

It looks like the tbl is returning userdata which table function can’t handle, how would I go about fixing this issue? I suppose it has to do with __index?

Thank you, I will look into your module later, but it sounds great already.

2 Likes

A temporary workaround I did was creating another table inside the proxify function and detecting when a key from that table is being indexed inside tbl and returning the value associated with the key:

local function proxify(tbl: {any})
	local proxy = newproxy(true)
	local meta = getmetatable(proxy)
	
	local internalMethods = {
        insert = function(key, value)
			table.insert(tbl, (key or (#tbl + 1)), value)
		end
	}

	meta.__index = function(self, key)
		local idx = tbl[key] or internalMethods[key]
		if idx and type(idx) == "table" then
			idx = proxify(idx)
		end
		return idx
	end

	meta.__newindex = function(self, key, newValue)
		if tbl[key] ~= newValue then
			tbl[key] = newValue
		end
	end
	
	meta.__iter = function()
		return next, tbl
	end

	return proxy
end

local tbl = proxify({
	Timer = 0,
	Name = "",
	Modes = {
		Easy = {},
		Normal = {},
		Hard = {},
		Insane = {}
	}
})

tbl.insert(nil, "test")
print(tbl[1]) --> Should print "test"

for key, value in tbl.Modes do
	print(key, value)
end

There’s probably a much more cleaner way of going about this

Is it possible to detect when table function is accessing the contents of the table?

Another thing is that in the proxy you can see another 2 functions:

  1. pmappend. Will append the main table and push the last main table:
local Proxy = require(script.Parent.Proxy)(getfenv());
--By sending the enviroment we have updated all the proxies

local myProxy = Proxy.new(CFrame.new());

print(-myProxy); --Prints the CFrame
Proxy.pmappend(myProxy, Color3.new());

print(-myProxy); --Prints the Color
  1. pappend. This appends new tables. So you can do:
local Proxy = require(script.Parent.Proxy)(getfenv());
--By sending the enviroment we have updated all the proxies

local myProxy = Proxy.new();

Proxy.pappend(myProxy, Color3.new());

print(myProxy.R); --Prints since we appended Color3

If I remember correctly, problem with getfenv is that it disables Lua optimizations and I would not want that to happen.

Source: Warnings for getfenv/setfenv that their use disables Luau optimisations

Sorry, I don’t I’m fully understanding, can you elaborate? Are you asking is there a way to dynamically connect the functions for the table global, to the proxy table? If so, I believe there is

I don’t believe so. What you could do is replace .insert and those functions and before inserting you could send a third argument

insert = function(t, v, i)
	if(type(t) == "userdata") then
		getmetatable(t).__newindex(t,i,v,true);
	else
		table.insert(t,v,i)
	end;
end;

That’s awesome but I personally would not use these kind of methods in production as it’s a hacky solution. I will probably go with adding .insert function as @HugeCoolboy2007 suggested, although I am probably gonna look for different solution to it as well because I want to avoid name collisions (eg. local tbl = proxify({ insert = "cool" }) ).

I could make a module like ptable (proxy table) to avoid overwriting global built-in functions.

local ptable = {}

function ptable.insert(tbl, value, index)
	getmetatable(tbl).__newindex(tbl, index, value, true)
end

return ptable

Also, could you explain what is going on with the .__newindex(...), what is that true for?

Fixed so that you don’t need to use getfenv
New example.rbxm (6.4 KB)

You just need to paste this when requiring

local Proxy = require(script.Parent.Proxy);

local getmetatable = Proxy.getmetatable;
local setmetatable = Proxy.setmetatable;
local ipairs = Proxy.ipairs;
local pairs = Proxy.pairs;
local rawget = Proxy.rawget;
local rawset = Proxy.rawset;
1 Like

It is the same you did.

same what?

I did what? Could you elaborate more on this?

I believe I solved the issue with name collision, I changed the __index meta method to this:

meta.__index = function(self, key)
		local idx = tbl[key] or internalMethods[key]
		local tableMethod = table[key]
		
		if idx and type(idx) == "table" then
			idx = proxify(idx)
		end
		
		return idx or (tableMethod and function(...)
			return tableMethod(tbl, ...)
		end) or nil
	end

If the key exists on the table first, then the key value from the will be returned, otherwise it will search the internalMethods table and do the same thing. If it doesn’t exist in either of the tables, then it will search the table global for the key and return a function that runs the built-in function from the global.

Code used:

Summary

For some, reason I can’t copy all of the code on mobile

tbl.insert("test") -- removed the "nil" argument
print(tbl[1])
for key, value in tbl.Modes do
	print(key, value)
end
3 Likes

So you could do this:

__newindex = function(t,k,v, isService)
	if(isService) then print("Being required by table!") end;
end
1 Like

You type fast! You are telling me that in mobile on 5min you could type all of that??

Yea, I’m just really used to typing :sweat_smile:

1 Like

Alright, one more question, how can I allow for printing my proxy table?

image

As currently it prints the memory address to it or whatever, and the __tostring method only accepts string as the return value.

That is a complicated one. What you will need to do is a custom print that loops through the table and creates a copy of a real table to print it. Any other method requires too much work, and it is just better to allow roblox to display the table.

1 Like

debug.getuservalue(userdata, n)
However that is unfortunately not native to Luau.

You can use native table values to track accesses/assignments to a table.
https://www.lua.org/pil/13.4.4.html

1 Like