-1

I am making the engine for a game and I can't seem to solve the following problem.

So, I have a base component class from which all the different components are derived. A GameObject is basically a container for different components. The components are stored in a vector containing pointers to the base component class. Now I need the GameObject class to have a getComponent member function template that will return the component with the requested type from the vector.

To be more clear:

class Component 
{
        /..../
};

class RigidBody : Component
{
        /..../
};

class Animation : Component
{
        /..../
};

class GameObject
{
public:
        template <class T>
        T* getComponent();

        void addComponent(Component*);
private:
        std::vector<Component*> m_components;
};

/...../

GameObject test;
test.AddComponent(new RigidBody());
test.AddComponent(new Animation());
Animation * animation = test.getComponent<Animation>();

Or something among those lines.

For simplicity's sake say that the vector is guaranteed to have the component that we are looking for and that there are no components of the same type.

Since the pointers in the vector are of the base component type, how can I check if they originally were of the requested type? Thanks in advance!

Veritas
  • 2,150
  • 3
  • 16
  • 40
  • 2
    Change your design. Or rather, *think about* your design before you jump to an implementation. – Kerrek SB May 03 '14 at 12:04
  • 1
    ^Helpful comment. I would also appreciate it if those who downvoted commented on what's wrong with the question. – Veritas May 03 '14 at 12:06
  • 1
    If you're asking "How do i compare derived types", then you've found a flaw in your design. Ask yourself how your program's behaviour changes depending on those derived types. Whatever the reason for wanting to know about the type of a derived class, the answer most likely is 'there's a better way'. – Ben Cottrell May 03 '14 at 12:13
  • Storing the different components like this is commonly done in component-based entity systems. – Veritas May 03 '14 at 12:17
  • @Veritas _'is commonly done in component-based entity systems'_ Not without using abstract interfaces, if these are well designed! (I'm one of the downvoters BTW) – πάντα ῥεῖ May 03 '14 at 12:19
  • @Veritas That doesn't necessarily mean it's the best design for the specific problem you are trying to tackle though. Without knowing a little more about the actual problem it's hard to judge either way. Your question isn't actually describing the real problem, it's describing an attempt at a solution - If you step back a bit and describe the real problem you'll probably get some far more useful answers. – Ben Cottrell May 03 '14 at 12:22
  • The whole point of the component system is to change the behavior of the object according to the component that you add. Hard coding the components misses the point. The objects then get processed by specific systems that act on certain components. I need this function to address the correct components in the systems. Like I said if you have anything else in mind I am all ears. – Veritas May 03 '14 at 12:27
  • @πάνταῥεῖ You didn't really specify what's wrong with the question. An interface with no functions seems completely useless to me. Components only keep data, they don't contain any logic, the logic is handled by the systems. – Veritas May 03 '14 at 12:36
  • @Veritas _'Components only keep data, they don't contain any logic'_ Who tought you so? That's not necessarily true, but depends on a kind of a meta-view, what makes up an abstract interface. Also an interface is not inherently purposed to perform logical operations only. – πάντα ῥεῖ May 03 '14 at 12:48
  • When people talk about interfaces in c++ they usually refer to abstract-classes. I may have misunderstood you, my English isn't perfect. Having the components in the component-entity system only keep data while external systems do all the processing is covered in many articles, a few books and it is used in some game frameworks. – Veritas May 03 '14 at 12:54
  • Which books are telling you to do that? Polymorphism is useless for classes which only contain data and no behaviour. The purpose of polymorphism is to have behaviour in your derived classes, which is accessible via the base-class interface – Ben Cottrell May 03 '14 at 13:00
  • What I am doing is not object-oriented programming, it is data-driven programming. The only reason I use polymorphism is to have a generic component container that keeps the bags of data. – Veritas May 03 '14 at 13:02

2 Answers2

1

Assuming that Component has at least one virtual function (otherwise what's the point of inheriting from it, right?) you should be able to do what you need using Runtime Type Information (RTTI) and dynamic_cast, like this:

template <class T> T* getFirstComponent() {
    for (int i = 0 ; i != m_components.size() ; i++) {
        T *candidate = dynamic_cast<T*>(m_components[i]);
        if (candidate) {
            return candidate;
        }
    }
    return nullptr;
}

Recall that dynamic_cast<T*> would return a non-null value only when the cast has been successful. The code above goes through all pointers, and picks the first one for which dynamic_cast<T*> succeeds.

Important note: While this should do the trick at making your program do what you want, consider changing your design: rather than pulling out objects by type, give them virtual functions that would let you use them all in a uniform way. It is pointless to put objects of different classes into one container, only to pull them apart at some later time. RTTI should be used as the last resort, not as a mainstream tool, because it makes your program harder to understand.

Another valid approach would be to store the individual components separately, not in a single vector, and get the vector only when you need to treat the objects uniformly.

Less important note: if nullptr does not compile on your system, replace with return 0.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    That may be an answer on the most technical level, but the OP would be very ill-advised to pursue this approach. – Kerrek SB May 03 '14 at 12:07
  • @KerrekSB You said the design is bad in both my question and this answer. If you have any idea how to keep a container of bags of different data while having them being addressable by third-party systems, please tell me. – Veritas May 03 '14 at 12:45
  • @Veritas: Think about your big-picture control flow. It's unlikely that you will not know what kind of data you want to operate on; it is probably more appropriate to have a collection of homogeneous containers. – Kerrek SB May 03 '14 at 13:11
  • @KerrekSB read this if you are interested: http://www.richardlord.net/blog/what-is-an-entity-framework – Veritas May 03 '14 at 13:12
  • 1
    @Veritas: Note how in that example there is no need to know the most-derived types of the objects in question. That fact that you seem to need to know indicates a flaw in your design. – Kerrek SB May 03 '14 at 13:17
  • @Veritas: For a different kind of perspective on the advantage of homogeneous containers, you might enjoy the middle part of [this recent talk by Herb Sutter](http://channel9.msdn.com/Events/Build/2014/2-661). – Kerrek SB May 03 '14 at 13:18
  • He is mapping the components according to their type. How is that different than knowing the types in my case? Actionscript just has the advantage of making that easier. The closest I could get to that would be to map using typeid but that's even worse imo. – Veritas May 03 '14 at 13:45
  • @Veritas Using `typeid` is neither worse nor better than `dynamic_cast` - they are on the same level :-) – Sergey Kalinichenko May 03 '14 at 14:07
  • I was referring to using a map. In any case, how I see it is that the operators are in the language for a reason. It's not like I want the different components to have similar behavior and I am not using inheritance correctly. I simply need a general component container and a way to retrieve the needed components from it. All I see is downvotes without any alternatives just because the solution requires a downcast. – Veritas May 03 '14 at 14:29
0

There are occasions where a system would want to group derived types from a base class vector, for example, the optimisation of multithreading.

One system I cooked up uses polymorphism to create a user defined type to avoid typeid or derived_class, here is some pseudo code...

class BaseType {
public:
virtual int getType() = 0;
}

class ThisType : public BaseType {
public:
int getType() {return 1;};
}

class TypeMaster {
private:
std::vector<ThisType*> myObjects;
public:
void add(ThisType* bc){ myObjects.push_back(bc); };
}

std::map<int,TypeMaster*> masters;
std::vector<BaseType*> objects;

for(int i=0;i<objects.size();i++){
masters.find(objects[i].getType())->second.add(objects[i]);
}

You would have to do a bit of work to make a system but the rudements are there to convey the idea. This code processes an arbitary vector of base objects and appends them to the vector of its type master.

My example has a collection of execution pools with multiple instances of the type master meaning the type master cannot be polymorphed because in that scenario the object would not be able to move around execution pools.

Note the lack of use of typeid or derived class. For me, implementations using native types keeps it simple without importing bloating libraries or any unnecessary execution fuss. You could perform speed trials but I have always found simple native type implementations to be quite succinct.

Binkie Boo
  • 43
  • 7