Adam Tomeček
Adam Tomeček

Reputation: 209

Application crashes with objects created in loop using Luabind in C++

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

Answers (1)

Nicol Bolas
Nicol Bolas

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

Related Questions