Looping though objects.

Started by
5 comments, last by Glak 14 years, 1 month ago
I'm writing a GUI library for training purposes. There is a GUI_Master class with a container of GUI_Object (that is, any gui stuff: windows, buttons, etc). That GUI_Object holds a reference to it's GUI_Master (my_master). A function, GUI_Master::logic() is called every tick. That function runs a loop though all GUI_Objects calling their GUI_Object::logic() so they can perform their tasks. The problem is that if, for instance, I want to create a button that deletes another GUI_Object. So something like "my_master->destroyObject(obj_to_be_destroyed)". This line will be a runtime problem because it will mess with GUI_Master's loop. I've found 2 solutions, first is having a pointer vector in GUI_Master to hold the objects and not destroying anything, just setting the pointer to NULL when I want the object to be ignored by the loop, so, poof, it disappaers. It actually works allrigth but I fell like it's not the right approach. The second is passing the iterator to GUI_Object::logic() so if it happens to destroy something it can return the working iterator. The problem is that it makes the code more complicated and not intuitive. So I think maybe I have a concept flaw from the start, so I ask, what's the proper way of doing this?
Please point my english mistakes everytime you can.
Advertisement
Without seeing the code I can't say for sure, but the 'setting the pointer to NULL' part sounds a bit suspicious.

In any case though, I wouldn't try to remove items from the list while you're iterating over it in this case. What you could do instead would be to maintain an 'items to remove' list in the GUI_Master class. When an item does the following:
my_master->destroyObject(obj_to_be_destroyed)
Instead of removing the item right off, you would simply add a reference to the object to the 'items to remove' list. Then, once you've finished iterating over the item list, you can go through the 'items to remove' list, remove any items that it contains from the master list, and then clear the 'to remove' list.

There might be better ways you could set up your system overall, but as for the specific problem you mention, I think that keeping the 'removal' step separate from the update loop would be a good start.
WindScar is approaching the right pattern with his "items to delete" queue, but his idea doesn't generalize well.




In the more general case, there is a frequently used "deferred callback" pattern.


Create a deferred callback manager.

When you need to perform work later than now, use the deferred callback manager. Register your callback function (or function object, if you want to give it extra parameters like an object to delete) with that system.

When processing gets to the end of the update loop --- meaning you are outside all other processing --- call all the deferred callbacks.

You can queue up just about anything you need. Use it to delete objects that can't delete themselves, allocate objects that would interfere with mid-frame work, alter game-wide states, or do whatever processing you need after the main loop.

When done, empty the callback container.



It is a very useful design pattern.
Quote:Original post by frob
WindScar is approaching the right pattern with his "items to delete" queue, but his idea doesn't generalize well.
I assume you were referring to me? If so, I would say that it's not always necessary that a solution be general; if all the OP needs to do is remove items safely while iterating over the container, a generalized deferred callback system might be overkill.

The points you make are good ones of course, but I think the solution I proposed was fairly reasonable given the nature of the question.
Well actually I managed to solve it by simply creating a std::vector wrapper that can have it's elements deleted from inside loops without crashing.

jyk you are right, but knowing how the idea can be expanded is always welcome. Thank you both for the asnwers, BTW.
Please point my english mistakes everytime you can.
Quote:Original post by jyk
I assume you were referring to me?
Yes, sorry.

Quote:I think the solution I proposed was fairly reasonable given the nature of the question.

Very true.

If you only need it for the one task of object destruction, that works nicely.

I tend to see two kinds of needs: Either you need it a single time, in which case anything works. Or you need it more than once, in which case a generalized solution is preferred.
attempting to create a general solution isn't good for the inexperienced. I have an untold number of failed projects that have failed due to overgeneralization. I have never failed due to undergeneralization. The proper development cycle for beginners is to hack in new features until they work, then generalize. Not the other way around.

This topic is closed to new replies.

Advertisement