So I've been binding some C++ functions to Lua and all has been going well until I came across this problem.
local transform
transform = gfx3D.Transform(); -- Works fine (transform is a userdata)
transform.Position = Vector3(0, 0, 0) -- This seems to work fine (Position is a userdata)
transform.Position.z = -80 -- This doesn't work (z accessed with a __newindex call, position with an __index call)
LOG(tostring(transform.Position)) -- Prints X:0 Y:0 Z:0
Both transform and position are userdata but setting z does not work. I wondered why this was until I realised exactly the reason. This is the __index method for the transform userdata:
static int TransformGet(lua_State* L)
{
if (luaL_checkudata(L, 1, "Transform") == NULL)
{
luaL_typerror(L, 1, "Transform");
}
Transform* trans = (Transform*)lua_touserdata(L, 1);
const char* key = luaL_checkstring(L, 2);
if (strcmp(key, "Position") == 0)
{
Maths::Vec3f* returnValue;
returnValue = (Maths::Vec3f*)lua_newuserdata(L, sizeof(Maths::Vec3f)); // Creates a new vector for the return value, and so breaking the reference
luaL_getmetatable(L, "Vector3");
lua_setmetatable(L, -2);
*returnValue = trans->Position;
}
else if (strcmp(key, "Rotation") == 0)
{
Maths::Vec3f* returnValue;
returnValue = (Maths::Vec3f*)lua_newuserdata(L, sizeof(Maths::Vec3f));
luaL_getmetatable(L, "Vector3");
lua_setmetatable(L, -2);
*returnValue = trans->Rotation;
}
else if (strcmp(key, "Scale") == 0)
{
Maths::Vec3f* returnValue;
returnValue = (Maths::Vec3f*)lua_newuserdata(L, sizeof(Maths::Vec3f));
luaL_getmetatable(L, "Vector3");
lua_setmetatable(L, -2);
*returnValue = trans->Scale;
}
return 1;
}
As you can see when I access the Position element in the transform I create a new userdata and push it on the stack as the return value. But this breaks the reference to the original userdata, and so setting it's Z element has no effect on the original, and so it appears the assignment didn't work.
The logical answer is to return a reference to the existing data of the position vector. But it doesn't have it's own userdata as it's simply an element of the transform userdata. You can see the transform userdata being created here:
Transform* transform;
transform = (Transform*)lua_newuserdata(L, sizeof(Transform));
luaL_getmetatable(L, "Transform");
lua_setmetatable(L, -2);
*transform = Transform();
I am completely at a loss at what to do here. I somehow need to create a userdata that points to the same block of memory that the position element was kept in. I tried experimenting with lightuserdata as it's only a pointer, but that threw errors when I tried to access members of the lightuserdata, which seem to not exist in Lua's mind.
This seems like an issue other Lua devs must have come across so I'd like some help on dealing with it.
Thanks.