Just a heads up, I have received little formal education on this type of design theory, so bear with me if I'm ignorant of some concepts. All of my reasoning comes from a background in C++. I am working on a component based system for use in a game engine, and this is the structure I have come up with.
There are components, which are just data.
There are nodes, which allow access to that data.
There are systems, which operate on nodes only.
There are Entities, which contain these nodes, components, systems, and other entities.
Pretty straightforward, but let's just focus on the components and nodes, which I have a pretty strict set of guidelines for.
a node can provide access to a collection of components within an entity
a node is dependent upon the existence of all its underlying components
a component can exist independent of any node pointing to it.
a system can only have access and act upon nodes
Now any of these nodes and components can be destroyed at any time. I've implemented this for nodes by using a set of intrusive lists to maintain a non-ownership method of iterating across nodes, and then they automatically remove themselves from the list upon their destruction. But now I have a question revolving about the components. On destruction of some component, all nodes must also be destroyed who were dependent upon that component. Normally, the simple fix to an object needing to be destroyed when another is destroyed is ownership, where you simply place the node within the component or dynamically destroy it within that components destructor, but nodes here can reference multiple different components. When an object has multiple owners, normally a ref counting solution like a smart pointer gives ownership to all those objects and is destroyed when all owners are destroyed, but that isn't the case this time. My big question, what do I do in terms of ownership when I have one object that can only exist when all its dependencies exist, and upon the destruction of any dependency, results in the destruction of that dependent object.
example:
Red are components needed for the existence of the second node
What it looks like after either component it depends on is destroyed
Obviously, there are multiple unclean solutions with weak pointers, manual deletions, and lots of checks for an objects existence, but like all issues, I'm wondering if this can be safely achieved by design alone. Again, if this is a very simple or well known concept, please just point me in the right direction.
#Jorgen G Valley - All of the objects are indeed owned by the entity, in that all objects are destroyed on destruction of the containing entity, but nodes, components, systems, and entities should be able to be added or removed at any time dynamically. Here is an example. Start with the world entity, which contains an entity which is one mesh and two vectors. The two vectors are updated independently, but let's say you want to parent them together, you would simply add a node, specify one vector as the parent, and any number of vectors as children. The addition of the node to the entity places it in a non-owning list, allowing the previously existant "Parent" system to iterate through all "Parent" nodes and perform functionality on each parent node. Unparenting the object involves just deleting the node, but then the vector and mesh should still exist. Let's say you want to destroy just that vector and hold onto the mesh for use in another model, than the destruction of that vector should also destroy the parent node, because it no longer references a valid vector.
Here are some visuals:
here is an example of the case above. here
now here is an example of removing the parent node. here
notice that the component stays around because it could be used in other nodes, like in this example, where the render node is using it. The destruction of the node closed the gap in the intrusive list used by the parent system, meaning the parent system only manages whatever other entities have parent nodes.
now here is an example of removing the vector component. here
In this case, all nodes dependent upon that vector must be removed as well, including the parent and render node. There destruction closes the gaps in those respective intrusive lists, and the systems continue on there way. Hopefully this helps illustrate the design i'm trying to achieve.
I think you are complicating things a bit more than you need. You say that nodes and components can be destroyed at any time. Is this really true?
In your text you describe the entity as the owner since you say it contains the components, nodes and systems.
My approach to this would be that a component would only be destroyed when the entity owning it is destroyed. This means that the node, component and system doesn't need to bother about destruction. It is done by the owning object, the entity.
EDIT: If you have situations where the component, node or system can be destroyed without the overlying entity to be destroyed I am intrigued to hear an example. It's a really interesting problem in itself. :)
Related
In the game I am designing, I have a derived class Bullet that inherits from the class Model. I have two vectors of shared_pointer for both of those classes. Every bullet is in the model's vector. Whenever a bullet reaches the end of its lifetime, it is then erased from the bullet's vector. Since that same object is in the model's vector, is there a quick way of erasing it as well? I can't use the index from the bullets vector because that index does not match its index in the model class. I tried creating a function that included delete this in the bullet class but that crashed the program and isn't recommended after some research. I thought about assigning each model an ID, getting the ID from the bullet before erasing it, and then erasing it in the model's vector based upon that ID. That to me doesn't sound like the best way of doing it. What are some alternate solutions?
Note: The vectors are defined as std::vector<std::shared_ptr<Model>> models. same goes for bullets
Edit: I first loop through the vector of bullets. If it is found that a bullet at the index is at the end of its lifespan, then the object at that index is erased (bullets.erase(index)). This can not be done in the models class, because the index is most likely different. I need the objects in both vectors to be erased for the actual object to go out of scope, and be destroyed, right? The reason there is a bullets vector and a models vector is that some operations should only be done on the bullets check lifespan for instance. Some operations should be done on all models including bullets, for instance, calculate velocity. I tried separating bullets from models, but that lead to redundant code that could have been achieved if all models were in one vector.
A solution to this would be to use some form of weak ownership. This is how entity component system usually work: You have a reference, call it a model or an entity doesn't matter. Make them all non-owner. If you insist on using std::shared_ptr, then you will have to use std::weak_ptr everywhere. Then, simply have one and only one place where you have all the std::shared_ptr. If you remove one of them, all the weak reference to it becomes invalid.
System that implements that won't usually use shared and weak pointer, but instead a pointer to the lifetime manager object (usually called entity manager or entity system) and also an identifier that is used to fetch the strong reference. That way, you keep having easy to use objects, and you get the benefit of having a single place to safely manage the lifetime of any game objects.
i've made a dynamic graph structure where both nodes and arcs are classes (i mean arcs are an actual instance in memory, they are not implied by an adjacency list of nodes to nodes).
Each node has a list of pointers to the arcs it's connected to.
Each arc has 2 pointers to the 2 nodes it's connecting.
Deleting a node calls delete for each of its arcs.
Each arc delete removes its pointer from the arcs lists in the 2 nodes it connects.
Simplified:
~node()
{
while(arcs_list.size())
{
delete arcs_list[arcs_list.size()-1];
}
}
~arc()
{
node_from.remove_arc(this);
node_to.remove_arc(this);
}
If i want to start using smart pointers here, how do i proceed?
Does each arc own 2 nodes, or do 2 nodes share an individual arc's ownership?
I was thinking about a shared_ptr, but shared pointer would only delete the arc when both nodes are deleted. If i delete only one node i would still have to explicitly delete all its arcs if i used shared_ptr. And that totally defeats the point of not using raw pointers in the first place.
Nodes can exist alone; each arc is owned by two nodes and it can only exist as long as these two nodes both exist.
Is there some other kind of smart pointer i should use to handle this?
Or is raw pointer just the plain simple way to go?
Does each arc own 2 nodes, or do 2 nodes share an individual arc's ownership?
You answered this question yourself:
Nodes can exist alone; each arc is owned by two nodes and it can only exist as long as these two nodes both exist.
When object A owns object B, then object A can exist after destroying B, but destroying A implies destroying B. Applied to your case, the two nodes share ownership of the arc.
Is there some other kind of smart pointer i should use to handle this? Or is raw pointer just the plain simple way to go?
Ah, yes. That is the real question. There is no pre-made smart pointer for this situation. However, I would not go with raw pointers in your node and/or arc classes. That would mean those classes would need to implement memory management on top of their primary purpose. (Much better to let each class do one thing well, then try to do many things and fail.) I see a few viable options.
1: Write your own smart pointer
Write a class that can encapsulate the necessary destruction logic. The node and/or arc classes would use your new class instead of standard smart pointers (and instead of raw pointers). Take some time to make sure your design decisions are solid. I'm guessing your new class would want a functional/callable of some sort to tell it how to remove itself from the lists it is in. Or maybe shift some data (like the pointers to the nodes) from the arc class to the new class.
I haven't worked out the details, but this would be a reasonable approach since the situation does not fit any of the standard smart pointers. The key point is to not put this logic directly in your node and arc classes.
2: Flag invalid arcs
If your program can stand not immediately releasing memory, you may be able to take a different approach to resolving an arc deletion. Instead of immediately removing an arc from its nodes' lists, simply flag the arc as no longer valid. When a node needs to access its arcs, it (or better yet, its list) would check each arc it accesses – if the arc is invalid, it can be removed from the list at that time. Once the node has been removed from both lists, the normal shared_ptr functionality will kick in to delete the arc object.
The usefulness of this approach decreases the less frequently a node iterates over its arcs. So there is a judgement call to be made.
How would an arc be flagged invalid? The naive approach would be to give it a boolean flag. Set the flag to false in the constructors, and to true when the arc should be considered deleted. Effective, but does require a new field. Can this be done without bloating the arc class? Well, presumably, each arc needs pointers to its nodes. Since the arc does not own its nodes, these are probably weak pointers. So one way to define an arc being invalid is to check if either weak pointer is expired(). (Note that the weak pointers could be manually reset() when the arc is being deleted directly, not via a node's deletion. So an expired weak pointer need not mean the associated node is gone, only that the arc no longer points to it.)
In the case where the arc class is sizeable, you might want to discard most of its memory immediately, leaving just a stub behind. You could add a level of indirection to accomplish this. Essentially, the nodes would share a pointer to a unique pointer, and the unique pointer would point to what you currently call your arc class. When the arc is deleted, the unique pointer is reset(), freeing most of the arc's memory. An arc is invalid when this unique pointer is null. (It looks like Davis Herring's answer is another way to get this effect with less memory overhead, if you can accept an object storing a shared_ptr to itself.)
3: Use Boost.Bimap
If you can use Boost, they have a container that looks like it would solve your problem: Boost.Bimap. But, you ask, didn't I already discount using an adjacency list? Yes, but this Bimap is more than just a way to associate nodes to each other. This container supports having additional information associated with each relation. That is, each relation in the Bimap would represent an arc and it would have an associated object with the arc's information. Seems to fit your situation well, and you would be able to let someone else worry about memory management (always a nice thing, provided you can trust that someone's abilities).
Since nodes can exist alone, they are owned by the graph (which might or might not be a single object), not the arcs (even as shared ownership). The ownership of an arc by its nodes is, as you observed, dual to the usual shared_ptr situation of either owner being sufficient to keep the object alive. You can nonetheless use shared_ptr and weak_ptr here (along with raw, non-owning pointers to the nodes):
struct Node;
struct Arc {
Node *a,*b;
private:
std::shared_ptr<Arc> skyhook{this};
public:
void free() {skyhook.reset();}
};
struct Node {
std::vector<std::weak_ptr<Arc>> arcs;
~Node() {
for(const auto &w : arcs)
if(const auto a=w.lock()) a->free();
}
};
Obviously other Node operations have to check for empty weak pointers and perhaps clean them out periodically.
Note that exception safety (including vs. bad_alloc in constructing the shared_ptr) requires more care in constructing an Arc.
I am designing a game engine in c++. I am currently working on categorizing the different entities in the game. My base class is SpriteObject that two classes MovableObject and FixedObject inherit from. Now if i for example create an instance of a MovableObject and want to add it to a Vector of Sprite and a Vector of MovableObject i just do:
Vector<Sprite*> sprites;
Vector<MovableObject*> movableObjects;
MovableObject* movingObject = new MovableObject();
sprites.push_back(movingObject);
movableObjects.push_back(movingObject);
But as the different categories and entities grow the code will get large (and it would get tiresome to add every entity to every vector that it belongs to). How do i automatically add an object to the vector that it belongs to when it is created?
EDIT 1: I think i just came up with a solution, what if i just make a global static class Entities that holds all the vector of entities in the scene. Every entity could have access to this class and when a entity is created it just adds a pointer version of itself to the corresponding vector(s) in that global class.
EDIT 2: But i forgot that my solution requires me to still manually add every entity to its matching vector. I just split the work among the different entities.
This is a nice problem.
I think that I would implement it like this: There will be an addToVector() method in Sprite class, and each derived class will override it to add itself to the corresponding vector.
I would suggest a different approach. But before I start I would like to note one thing with your current design.
I would hide the creation of those objects behind a facade. Call it a scene or whatever. Using new manually is bad from a couple of perspectives. First of all if you decide you want to change the scheme on how you allocate/construct your objects you have to change it everywhere in the code. If you have a lets say a factory like Scene you just change the implementation and the calls to scene->CreateObject<Sprite>() will remain the same everywhere else. This might get important once you start adding stuff like custom memory allocation schemes, object pools etc and at some point you will if you will start to grow your engine. Even if this is just an excercise and a for fun project we all want to do this like its actually done, right ;) ?
Now going back to the core - dont abuse inheritance.
MovableObject is not a Sprite. Static Object is not a sprite either. They are that, movable and static elements.
A sprite can be movable or static, so it has a behavior of a dynamic or static element.
Use composition instead. Make a Sprite accepting behavior, or better a list of behaviors. In fact the Sprite itself is just a behavior on a Game object too, it just controls the way it is presented to the user.
What if you had an object that can be attached multiple behaviors like the fact it is a dynamic one, it has a sprite presence on the scene and even more is a sound emitter!
If you add those behaviors to the object you have to create them first. They can, when constructed, decide to which list they should subscribe to.
This is all metaphors for actually a well known system, that is proven to work well and is actually used in most game engines nowadays. Its a Entity Component System.
You object with behaviors are Entities, Components are those Behaviors and each of them is controlled by one system that knows the component and knows how to update/handle them.
Objects in the scene are merely a set of components attached to them that act upon them.
I am having some issues designing the memory management for an Entity-Component system and am having some issues coming up with the detail of the design. Here is what I am trying to do (note that all of these classes except Entity are actually virtual, so will have many different specific implementations):
The Program class will have a container of Entity's. The Program will loop through the Entity's and call update on each of them. It will also have a few SubSystem's, which it will also update on each loop through.
Each Entity will contain two types of Component's. All of them will be owned by a unique_ptr inside the Entity since their lifetime is directly tied to the entity. One type, UpdateableComponent, will be updated when the Entity.update() method is called. The second type SubSystemComponent will be updated from within their respective SubSystem.
Now here are my two problems. The first is that some of the Component's will control the lifetime of their parent Entity. My current idea for this is that Component will be able to call a function parent.die() which would change an internal flag inside Entity. Then after Program finishes looping through its updates, it loops through a second time and removes each Entity which was marked for deletion during the last update. I don't know if this is an efficient or smart way to go about it, although it should avoid the problem of an Entity dieing while its Component's are still updating.
The second issue is that I am not sure how to reference SubSystemComponent's from within SubSystem. Since they are refered to by a unique_ptr from inside Entity, I can't use a shared_ptr or a weak_ptr, and a standard pointer would end up dangling when the Entity owning a component dies. I could switch to a shared_ptr inside the Entity for these, then use a weak_ptr in the SubSystem's, however I would prefer to not do this because the whole point is that Entity completely owns its Component's.
So 2 things:
Can my first idea be improved upon in a meaningful way?
Is there an easy way to implement a weak_ptr sort of functionality with unique_ptr, or should I just switch to shared_ptr and just make sure to not create more than one shared_ptr to the SubSystemComponent's
Can my first idea be improved upon in a meaningful way?
Hard to say without knowing more about the nature of the work being undertaken. For example, you haven't said anything about your use of threads, but it seems your design gives equal priority to all the possible updates by cycling through things in a set sequence. For some things where low latency is important, or there's some useful prioritorisation that would ideally be done, a looping sequence like that isn't good, while other times it's ideal.
There are other ways to coordinate the Component-driven removal of Entities from the Program:
return codes could bubble up to the loop over entities, triggering an erase from the container of Entities,
an Observer pattern or lambda/std::function could allow the Program to specify cleanup behaviour.
Is there an easy way to implement a weak_ptr sort of functionality with unique_ptr,
No.
or should I just switch to shared_ptr and just make sure to not create more than one shared_ptr to the SubSystemComponent's
It sounds like a reasonable fit. You could even wrap a shared_ptr in a non-copyable class to avoid accidental mistakes.
Alternatively - as for Entity destruction above - you could coordinate the linkage between SubSystem and SubSystemComponent using events, so the SubSystemComponent destructor calls back to the SubSystem. An Observer pattern is one way to do this, a SubSystemComponent-side std::function fed a lambda is even more flexible. Either way, the Subsystem removes the SubSystemComponent from its records.
I want to store pointers to one instance of an object in some (two or more) containers. I've met one problem in this idea: how I can handle removing of this object. Objects have rather stormy life (I am talking about game, but I think this situation is not so specific) and can be removed rather often. To my mind this problem is divided into two problems
1.
How should I signal to containers about deletion? In C# I used to create boolean property IsDead in stored objects, so each iteration of the main loop at first finds 'dead' objects and removes them. No circular reference and everything is rather clear :-) Is this technique correct?
2.
Even if I implement this technique in C++ I meet difficulty with calling destructors if this object is in some containers. Even if I create some kind of a field 'IsDead' and remove dead object from all lists, I had to free memory.
After reading some articles I have an idea that I should have one 'main' container with shared_ptr to all my objects, and other containers should store weak_ptr to them, so only main container checks object's status and others look only at shared_ptr. Are my intentions correct or is there another solution?
It sounds like you're looking for shared_ptr<T>.
http://msdn.microsoft.com/en-us/library/bb982026.aspx
This is a reference counted ptr in C++ that enables easy sharing of objects. The shared_ptr<T> can be freely handed out to several objects. As the shared_ptr instances are copied around and destucted the internal reference counter will be updated appropriately. When all references are removed the underlying data will be deleted.