Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different types assignment in switch state cases, inside template function

Tags:

c++

Apple.h

class Apple {
public:
    Apple(int);
    static int typeID;
private:
    int id_;
};

Apple.cpp

#include "Apple.h"
Apple::Apple(int pID) {
    id_ = pID;
}

Potato.h, Potato.cpp identical to Apple

Storage.h

#pragma once
#include "Apple.h"
#include "Potato.h"
#include <vector>
class Storage {
public:
    Storage();
    template<typename foodName> void store(foodName * object){
        (*getBasket<foodName>()).push_back(object);
    };
    template<typename foodName> int countSize(){
        return (*getBasket<foodName>()).size();
    };

private:
    std::vector<Apple*> applebasket_;
    std::vector<Potato*> potatobasket_;
    template <typename foodName> std::vector<foodName*> * getBasket(){
        std::vector<foodName*> * result;
        switch(foodName::typeID){
            case 0:
                result = &applebasket_;
                break;
            case 1:
                //result = &potatobasket_;
                break;
        }
        return result;
    } 
};

Storage.cpp

#include "Storage.h"
int Apple::typeID;
int Potato::typeID;
Storage::Storage() {
    Apple::typeID = 0;
    Potato::typeID =1;
}

main.cpp

#include "Storage.h"
#include <iostream>
int main() {
    Apple* apple;
    Potato* potato;
    Storage storage;
    int i;
    for(i = 0;i < 7;i++){
        apple = new Apple(i);
        storage.store<Apple>(apple);  
    }      
    std::cout<<storage.countSize<Apple>();
    return 0;
}

This code works and outputs right size of vector, but if case line in switch statement (inside Storage.h) is uncommented, compiler(g++) throws "error: cannot convert ‘std::vector < Potato*>* ' to 'std::vector< Apple*>* ' in assignment". It is like compiler trying out both cases anyway, and i cant find is it possible and how to avoid this. I need help with this and maybe some advice on a whole thing(one interface for containers of different types), I've started learning C++ recently and probably the way i try this to do here is a total mess.

like image 320
Человек Ящерица Avatar asked Jun 14 '12 14:06

Человек Ящерица


People also ask

What is the switch function in C++?

The switch statement in C++ is the best alternative to the lengthy if statements that are used to compare a variable to different integral values. It is a multi-way branch statement. The switch statement is the control statement that allows any value to change the control of the execution.

What can I use instead of a switch statement?

Luckily, JavaScript's object literals are a pretty good alternative for most switch statement use-cases I can think of. The idea is to define an object with a key for each case you would have in a switch statement. Then you can access its value directly using the expression you would pass to the switch statement.

Is there Switch case in C++?

The switch expression is evaluated once. The value of the expression is compared with the values of each case. If there is a match, the associated block of code is executed. The break and default keywords are optional, and will be described later in this chapter.


1 Answers

Your code doesn't compile because both case should compile which is not possible, as the type differs in both case.

One solution to this problem is to use overload instead of function template as (which means, there is NO need of typeID in your class!) :

std::vector<Apple*> * get_basket(Apple *)
{
   return &applebasket_;  //return pointer to the apple basket
}

std::vector<Potato*> * get_basket(Potato *)
{
   return &potatobasket_; //return pointer to the potate basket
}

And call it as:

template<typename FoodType> 
void store(FoodType * object)
{
    std::vector<FoodType> * basket = get_basket(static_cast<FoodType*>(0));
    basket->push_back(object);
}

The trick here is that you have two overloads, each takes one argument of different type, and so you use static_cast<FoodType*>(0) to help the compiler to pick the correct overload based on the type of the expression static_cast<FoodType*>(0) which would be either of type Apple* or Potato*.


@Gorpik said in the comment that both (this as well as other solution) are ugly, so here is another attempt to solve this problem.

Define a base_storage class template as:

template<typename FoodType>
class base_storage
{
    std::vector<FoodType*> m_storage;
    public:
        void store(FoodType *foodItem)
        {
            m_storage.push_back(foodItem);
        }
        size_t count() const
        {
            return m_storage.size();
        }
};

This base class stores food items of one type only, but in the question, we need to store food items of two types. So in order to do that, lets define another class Storage deriving from the above class template as:

class storage : private base_storage<Apple>, private base_storage<Potato>
{
    public:
        template<typename FoodType> 
        void store(FoodType * foodItem)
        {
            base_storage<FoodType>::store(foodItem);
        }
        template<typename FoodType> 
        size_t count() const
        {
            return base_storage<FoodType>::count();
        }
};

Note two points here:

  • The class storage doesn't have any member data now. It just forward the call to the base class which is chosen based on the type of template argument FoodType.
  • It derives privately from the base classes. So it is not is-a relationship.

See the online demo of this solution here : http://ideone.com/Ykjo5

The beauty of this solution is that if you want to make it work for three types of food, then all you need to derive it from three base classes as:

class storage : private base_storage<Apple>, 
                private base_storage<Potato>,
                private base_storage<Mango>   //added line!
{

     //same as before; no change at all !

};

Demo : http://ideone.com/lnMds

like image 198
Nawaz Avatar answered Sep 30 '22 16:09

Nawaz