Reputation: 209
I'm trying to use Lua with my game engine prototype but I'm stuck with odd error.
My target is to create X objects in loop with Lua and render them.
sprite = Sprite("icon.jpg", 300, 300, 0)
sprite2 = Sprite("icon.jpg", 100, 100, 0)
b1 = BoxObject(sprite)
b2 = BoxObject(sprite2)
sprite3 = Sprite("circle.png", 200, 100, 0)
sprite4 = Sprite("circle.png", 300, 100, 0)
b3 = CircleObject(sprite3)
b4 = CircleObject(sprite4)
n = Node()
n:AddChild(b1)
n:AddChild(b2)
n:AddChild(b3)
n:AddChild(b4)
for i = 0, 10, 1 do
x = math.random(700)
y = math.random(500)
n:AddChild(BoxObject(Sprite("icon.jpg", x, y, 0)))
end
for i = 0, 10, 1 do
x = math.random(700)
y = math.random(500)
local s = Sprite("circle.png", x, y, 0)
local o = CircleObject(s)
n:AddChild(BoxObject)
end
If I do it this way, code works without errors, but game crashes in random time from instant to few secs. If I use this code with only objects created in loops, not those created manually, game crashes instantly.
However if I write Lua code equivalent in C++, it runs without any problem.
for(int i = 0; i < 20; i++){
float x = rand() % 700;
float y = rand() % 500;
n->AddChild(new BoxObject(new Sprite("icon.jpg", x, y)));
}
for(int i = 0; i < 20; i++){
float x = rand() % 700;
float y = rand() % 500;
n->AddChild(new CircleObject(new Sprite("circle.png", x, y)));
}
This is my Lua binding
static void Lua(lua_State *lua){
luabind::module(lua)
[
luabind::class_<Node>("Node")
.def(luabind::constructor<>())
.def(luabind::constructor<float, float, float>())
.def("Render", &Node::Render)
.def("Move", &Node::Move)
.def("Rotate", &Node::Rotate)
.def("AddChild", &Node::AddChild)
.def("RotateAroundPoint", &Node::RotateAroundPoint)
];
}
Every method accepts and returns void except AddChild
virtual void AddChild(Node *child);
Sprite and Box aand Circle Objects are inherited from Node class.
Does any one have an idea what can cause this odd error? I would be glad for any help.
Upvotes: 1
Views: 777
Reputation: 473322
You have serious ownership problems.
In C/C++, a piece of code "owns" a pointer or other resource if that code or object takes on the responsibility for destroying it. It keeps it alive while it needs it, and it ensures that it is destroyed when it is finished using it.
Maintaining an understanding of who owns what is vital for any even mildly complex C or C++ program.
When Luabind creates a C++ object from Lua, then the Lua script owns that object. This means that Lua's garbage collector will decide when to delete it.
So this:
local s = Sprite("circle.png", x, y, 0)
Will create a Sprite
object who's lifetime is governed by the Lua state. When the Lua state no longer has any active references to it, then the Lua state will be free to delete it.
I don't know how your code works, but this line:
local o = CircleObject(s)
Suggests that CircleObject
's constructor is supposed to claim ownership over the object it is given (thus the CircleObject
decides when to delete it). If that's true, then you need to actually state that when you bind this constructor with Luabind.
If CircleObject
's constructor is not supposed to take ownership, then... well, quite frankly your design is suspect; long-term storage of a pointer that you don't own is just begging for something to get screwed up. But if it's really supposed to store a pointer that's owned by someone else, then you need to use Lua mechanisms to ensure that the Sprite
remains alive so long as CircleObject
does.
Similarly, the Node::AddChild
function looks like it's claiming ownership of whatever node it is given. This would be done like this:
.def("AddChild", &Node::AddChild, adopt(_1))
The _1
means the first parameter of the function. This means that the Node
claims ownership of the first parameter; if it's owned by Lua, this ownership chain is now broken, and C++ now owns the object.
Personally, I would say that this API is... not good for Lua. You seem to want all of the minor object ownership to be done in C++ code. So Lua should call C++ functions that set all of these minor objects up without Lua having to interfere. Lua should call a member function of a NodeManager
in order to get/create a Node
, then it should call a function that will create a Sprite
within that Node
.
Lua shouldn't have to deal with the minutiae of BoxObject
and such. It shouldn't have to directly create a Sprite
object and put it into a Node
.
Also, again personally, this API is not good for C++ either. If Sprite
needs to be stored in some kind of container, and that container needs to be stored in some kind of Node
, then it should not be possible to create a Sprite
without storing it in a container. This is what factory functions are for. I'm not even sure what BoxObject
and CircleObject
are for, since Sprite
seems to be doing all the work.
Upvotes: 1