I am eying to write a generic load/save routine for arbitrary type data. I have in the same class a color, a vec4, a vec3 and a vec2 variable. I am Abe to persist them with JSON.
When I reconstruct these objects, I need to determine the type the variable came from. I’ve already thought about hacking the persistence with tags to reconstruct, but I still need to figure out what my class definition type is that I came from.
How can I programmatically get color vs vec4, for example? They are all classified as userdata. Another example question: Does userdata have a property to determine what is a vec4 vs a vec3?
Hello @aciolino. Some time ago i posted a big mesh saving utility. There is a convert() class in it, and in the convert class i have this function that returns the type of userdata. You can see the trick i have used below:
-- type(v):"nil", "number", "string", "boolean", "table", "function", "thread", "userdata".
function Convert:type(x)
local txt = type(x)
function typeCompare(x,ref)
-- nb: returns an error if x or ref have no metatable!
local i = getmetatable(x).__index
local j = getmetatable(ref).__index
return (i==j)
end
if txt == "userdata" then
if typeCompare(x,vec2()) then txt ="vec2"
elseif typeCompare(x,vec3()) then txt ="vec3"
elseif typeCompare(x,color()) then txt ="color"
elseif typeCompare(x,image(1,1)) then txt ="image"
elseif typeCompare(x,matrix()) then txt ="matrix"
elseif typeCompare(x,mesh()) then txt ="mesh"
end
end
if txt == "table" then
txt = "{"..self:type(x[1]).."}"
end
return txt
end
.@Jmv38 Quick style question for you. Why are you defining typeCompare every time the function Convert:type is called? Wouldn’t it make more sense to define it once outside this function and then call it from within? In fact, wouldn’t the following be a little more compact:
function Convert:type(x)
local txt = type(x)
local i = getmetatable(x).__index
local j = getmetatable(ref).__index
return (i==j)
end
if txt == "userdata" then
local mt = getmetatable(x).__index
if mt == getmetatable(vec2()).__index then txt ="vec2"
elseif mt == getmetatable(vec3()).__index then txt ="vec3"
elseif mt == getmetatable(color()).__index then txt ="color"
elseif mt == getmetatable(image()).__index then txt ="image"
elseif mt == getmetatable(matrix()).__index then txt ="matrix"
elseif mt == getmetatable(mesh()).__index then txt ="mesh"
end
elseif txt == "table" then
txt = "{"..self:type(x[1]).."}"
end
return txt
end
It’s certainly better form than me ‘forcing’ it the way I am. That said, it’s working either way, which was quite a surprise to me. I do like the code by @tnlogy.
Hello @andrew_Stacey and @tnlogy.
Function in function: it is just a way to hide the local function from the rest, there Are already too many visible function in my code.
If i look at both your code: metatable and __index appear at many locations. i am not familiar with these keywords, so they scared me. Then i used them only once, in an encapsulated location: this might not be the fastest code (but i dont need speed here) nor the most compact (but i favor debuggability against compactness). And it did the job for me, when i needed to… I always favor readability (according to my -low- level of programming skills) against optimization, except when speed is a key factor… Voilà.