Reputation: 23
I have a way to create a circle object and put the pointer into a vector before passing the pointer to Lua. I want to be able to call a destroy function that will remove the pointer from the vector and then set the circle object in lua to a nil value before deleting the pointer.
here's how the Lua state is set up
lua_pushlightuserdata(luaState, this);
lua_setglobal(luaState, "LUA_HOST");
lua_newtable(luaState);
int circleTblIdx = lua_gettop(luaState);
lua_pushvalue(luaState, circleTblIdx);
lua_setglobal(luaState, "Circle");
lua_pushcfunction(luaState, wrap_createCircle);
lua_setfield(luaState, -2, "new");
lua_pushcfunction(luaState, wrap_setCircleRadius);
lua_setfield(luaState, -2, "setRadius");
lua_pushcfunction(luaState, wrap_destroyCircle);
lua_setfield(luaState, -2, "destroy");
luaL_newmetatable(luaState, "CircleMetaTable");
lua_pushstring(luaState, "__index");
lua_pushvalue(luaState, circleTblIdx);
lua_settable(luaState, -3);
here's the functions
int wrap_createCircle(lua_State* luaState){
if (lua_gettop(luaState) != 1) return -1;
float radius = lua_tonumber(luaState, 1);
CircleShape* circle = (CircleShape*)lua_newuserdata(luaState, sizeof(CircleShape));
new (circle) CircleShape(radius);
luaL_getmetatable(luaState, "CircleMetaTable");
lua_setmetatable(luaState, -2);
//putting the circle into a list called draw objects
lua_getglobal(luaState, "LUA_HOST");
Render* host = static_cast<Render*>(lua_touserdata(luaState, -1));
host->drawObjects.push_back(circle);
lua_pop(luaState, 1);
return 1;
}
//this works fine, no need to change
int wrap_setCircleRadius(lua_State* luaState){
if (lua_gettop(luaState) != 2) return -1;
CircleShape* circle = static_cast<CircleShape*>(lua_touserdata(luaState, 1));
float radius = lua_tonumber(luaState, 2);
circle->setRadius(radius);
return 0;
}
int wrap_destroyCircle(lua_State* luaState){
if (lua_gettop(luaState) != 1) return -1;
CircleShape* circle = static_cast<CircleShape*>(lua_touserdata(luaState, 1));
// TODO: remove the pointer from Lua and make the circle into a nil value
// This removes the pointer from the list, it works
lua_getglobal(luaState, "LUA_HOST");
Render* host = static_cast<Render*>(lua_touserdata(luaState, -1));
for (int i=0; i<host->drawObjects.size(); i++){
Drawable* obj = host->drawObjects[i];
if ( (void*)obj == (void*)circle ){
host->drawObjects.erase(host->drawObjects.begin() + i);
break;
}
}
lua_pop(luaState, 1);
//TODO: delete circle pointer, currently can't delete as what I think is happening
// is lua is using the pointer, that's why when I delete it, it crashes
//delete circle;
return 0;
}
here's the lua code
local circle = Circle.new(100)
circle:setRadius(200)
print(circle) -- this will print the circle pointer
circle:destroy() -- destroy, this is where I want the circle to be set to nil
print(circle) -- this should print nil
I have tried setting the metatable to a nil value but this just makes the circle object from a CircleMetaTable type to a user data type, calling delete will still crash
here`s the code that I tried
int wrap_destroyCircle(lua_State* luaState){
if (lua_gettop(luaState) != 1) return -1;
CircleShape* circle = static_cast<CircleShape*>(lua_touserdata(luaState, 1));
lua_pushnil(luaState);
lua_setmetatable(luaState, 1); // setting to nil
lua_getglobal(luaState, "LUA_HOST");
Render* host = static_cast<Render*>(lua_touserdata(luaState, -1));
for (int i=0; i<host->drawObjects.size(); i++){
Drawable* obj = host->drawObjects[i];
if ( (void*)obj == (void*)circle ){
cout << "Delete: " << obj << endl;
host->drawObjects.erase(host->drawObjects.begin() + i);
break;
}
}
lua_pop(luaState, 1);
// delete circle;
return 0;
}
result:
local circle = Circle.new(100)
circle:setRadius(200)
print(circle) -- prints: CircleMetaTable: PointerToCircle
circle:destroy() -- destroy, this is where I want the circle to be set to nil
print(circle) -- prints: userData: PointerToCircle (I want this to print nil)
Upvotes: 1
Views: 65
Reputation: 11201
Unlike C++ which has manual memory management, Lua is a garbage-collected language; you typically don't release memory by manually "destroying" objects (*), you just let them go out of scope and don't reference them anymore, then Lua's garbage collection will eventually collect them. If you have userdata with associated data (say, a vector) you need to free, set the __gc
metamethod appropriately.
Now let's get back to your code:
local circle = Circle.new(100)
print(circle) -- prints: CircleMetaTable: PointerToCircle
circle:destroy() -- destroy, this is where I want the circle to be set to nil
print(circle) -- prints: userData: PointerToCircle (I want this to print nil)
This is not possible. It is not possible to set a reference to nil
from "under" the user (think about it: this would require Lua to have a "reverse lookup" of all places where there is a reference to this userdata - you might be storing the userdata in a million places after all! If there was some easy way to set it to nil
, Lua would have to change the values stored in a million places to nil
). You can however set some internal data in your userdata to mark the circle as "destroyed", and you can write your __index
metamethod / all your methods accordingly that they check for this and error on an invalid object (for example, Lua will mark closed files as closed, and any attempt to use them will error).
(*) resources like files or database connections are an exception here, as it may be important that they are released timely, so newer Lua versions offer the <close>
annotation to release variables as they go out of scope using the __close
metamethod; in older Lua versions you'd have to manually call :close()
.
Upvotes: 1