bweber
bweber

Reputation: 4082

C++: store/remember type of template class

Let's assume we have a class like the following:

class AbstractContainer{
    ...
};

template <typename T>
class Container : public AbstractContainer {
    T someFunction();
};

Now there is another class which has a member variable that shall store one of these containers. However, the template type shall not be fixed. Therefore, instead of declaring the member variable as of type Container, it's declared as of type AbstractContainer*, so a pointer at the base class. This pointer should be able to store all types of Container classes, regardless of the template parameter.

class Interface{
public:
    Interface();
    void doSth();
private:
    AbstractContainer* container;
};

Let's assume the container is constructed in the constructor of the Interface class like this:

Interface::Interface(){
    if (/* some condition */)
        this->container = new Container<int>(25);
    } else {
        this->container = new Container<float>(25);
    }
    //here I'd need to remember of which type container is: int or float
}

So here I'd need to somehow store of which type my container is (int or float). I know it at this point of my program and it's totally deterministic. I need to store it, because later I might have to cast my AbstractContainer* back to a Container<int>* or a Container<float>*, for example in another function:

void Interface::doSth(){
    //here I have to recall what the type was
    if(/* type was int */) {
        dynamic_cast<Container<int>&>(*(this->container)).someFunction();
    } else {
        dynamic_cast<Container<float>&>(*(this->container)).someFunction();
    }
}

I have thought about using an enum that contains values for all different supported types and saving the type in an additional member variable of that enum type. Then I'd have to make a switch statement that checks for all the different possibilities and performs the correct cast. However, I wondered if there might not be an easier way.

What I'd basically like to do, is store the type used in the constructor of Interface and then recall it in Interface::doSth.

EDIT: I have to make clear that the function someFunction depends on the template parameter T. So it is not an option to make it virtual.

Upvotes: 2

Views: 1448

Answers (5)

mustafagonul
mustafagonul

Reputation: 1209

class AbstactObject {
public:
  virtual ~AbstractObject() = 0;

  virtual AbstractObject &doSomething() = 0;
}

template<class T>
class Object : AbstactObject {
public:
  virtual ~Object();

  virtual Object<T> &doSomething();

  T &get();
private:
  T t;
}

class AbstractContainer {
public:
  virtual ~AbstractObject() = 0;

  virtual AbstractObject &doSomething();

private:
  AbstractObject *obj;
};

template<class T>
class Container : AbstactContainer {
public:
  virtual ~Container();

  virtual Object<T> &doSomething() {
    return obj->doSomething();
  };
}

class Interface {
public:
  Interface();
  void doSth();
private:
  AbstractContainer* container;
};


Interface::Interface() {
  if (/* some condition */)
    container = new Container<int>(25);
  } else {
    container = new Container<float>(25);
  }
}

void Interface::doSth() {
  auto obj = container->doSomething();
  auto val = obj.get();    
}

when you want to get the T value

auto obj = container->doSomething();
auto val = obj.get();

Please check: Covariant return type https://en.wikipedia.org/wiki/Covariant_return_type

Upvotes: 0

Jarod42
Jarod42

Reputation: 217448

You may do

void Interface::doSth(){
    if (auto* c_int = dynamic_cast<Container<int>*>(this->container)) {
        c_int->someFunction();
    } else if (auto* c_float = dynamic_cast<Container<float>*>(this->container)) {
        c_float->someFunction();
    }
}

But why not move someFunction() into the base class ? and use virtual method ?

Upvotes: 2

nwp
nwp

Reputation: 10001

Another alternative:

template <class T>
T doSth(Container<T> &container){
    return container.someFunction(); //assume someFunction returns a T
}

You don't need a base class, no inheritance, no casts.

Upvotes: 0

mustafagonul
mustafagonul

Reputation: 1209

May be your answer is Curiously recurring template pattern.

template<class T, template<class> class U>
class AbstractContainer
{
    void interface()
    {
        static_cast<U<T> *>(this)->implementation();
    }
};

template<class T>
class Container : public AbstractContainer<T, Container>
{
    void implementation()
    {

    }
};

Upvotes: 1

Donotalo
Donotalo

Reputation: 13025

Though Jarod42's answer is better, one way to do it is by using typeid:

void doSth()
{
    if (typeid(*container).hash_code() == typeid(Container<int>).hash_code())
    {
        cout << "int" << endl;
    }
    else if (typeid(*container).hash_code() == typeid(Container<float>).hash_code())
    {
        cout << "float" << endl;
    }
}

But you'll need at least a virtual function in the base class. You can make a virtual destructor, for example.

Upvotes: 1

Related Questions