solusipse solusipse - 4 months ago 8
C++ Question

std::vector with a template argument

I got a code like this:

template<class T> void Engine::extend( std::string name ) {
T *instance = new T( this );
this->newExtensions[name] = instance;
}


It's called in such way:

engine.extend<Menu>( "menu" );


It works perfectly, but I'd like to to create a new Instance of
T
later in my code (in main loop of my game), instead of doing that in
Engine::extend
method. Is it possible to keep an information about class to be instantiated in vector or any other data structure that will be shared across whole
Engine
class, so I can call it later in other method? Pseudo-code of what I'm trying to achieve:

template<class T> void Engine::extend( std::string name ) {
this->newExtensions[name] = T;
}

Answer

Do the types that you will create have a common base? As in C++ you cannot have collection (vector / map) of unrelated type, types should have a common base class for the following code to work. (Otherwise, you can use void*, but conversions will not be safe):

#include <iostream>
#include <map>

class Entity {
    public:
    virtual ~Entity(){}
};

class C1: public Entity
{
    public:
    void doSomething(){std::cout << "C1" << std::endl;}
};

class C2: public Entity
{
    public:
    void doSomething(){std::cout << "C2" << std::endl;}
};

class C3
{
    public:
    void doSomething(){std::cout << "C3" << std::endl;}
};



class CreatorBase {
    public:
        virtual ~CreatorBase () {};
        virtual Entity* create () = 0;  
};

template<typename T>
class Creator: public CreatorBase {
    public:
        Entity* create ()
        {
            return new T;
        }       
};

std::map<int, CreatorBase*> cmap;

Here, we have a base Entity class and C1 and C2 inherit from it. Next, there is template class Creator, which creates an object of the requested type.

They all inherit from CreatorBase, so that they can be stored in a map.

The creator returns Entity* and for type safety we can use dynamic cast when calling create(), to ensure that we are creating the right type.

If you need only an Entity pointer, you can use Entity* everywhere and dynamic cast is not needed.

int main(int argc, char *argv[])
{   
    cmap[1]=new Creator<C1>;
    cmap[2]=new Creator<C2>;
    //cmap[3]=new Creator<C3>;  //will not compile - C3 does not derive from Entity

    C1 *i1 = dynamic_cast<C1*> (cmap[1]->create());  
    C2 *i2 = dynamic_cast<C2*> (cmap[2]->create());
    C1 *i3 = dynamic_cast<C1*> (cmap[2]->create()); //bad dynamic cast: cmap[2] does not create C1

    if (!i1) {
        std::cout << "i1: Bad dynamic cast" << std::endl;
    } else {    
        i1->doSomething();
    }

    if (!i2) {
        std::cout << "i2: Bad dynamic cast" << std::endl;
    } else {    
        i2->doSomething();
    }   

     if (!i3) {
        std::cout << "i3: Bad dynamic cast" << std::endl;
    } else {
            i3->doSomething();
    }
}

Output:

C1
C2
i3: Bad dynamic cast