0

I am trying to implement Heterogenous container, using pointers to non-template base class. While the derived class is a template.

Note: Derived class types are known at compile time. Note: the container size is fixed.

First attempt: is using a helper array to hold integer representation of the correct type. Its size equal to the container size. Yet I ended up with many if statements.

my problem is slightly similar to this thread yet I don't know to how to use std::type_index.

I am trying to avoid solving this using Boost::variant and run-time polymorphism.

My question: is there better way to handle casting from the base class to the derived class ?

Edit1 in my actual problem. the template class has 16 different types.

example:

template<typename Color, typename Smell, typename Shape, typename Origin>
class Fruit{};

Implementation:

class Plant
{ public: std::string sound = "I am jst a plant";};

template <typename T>
class Fruit : public Plant
{public: std::string sound = "I am jst a Fruit!";};

// list of types known at compile time.
struct Apple{ };           // types = 0
struct Orange{ };          // types = 1
struct Banana{ };          // types = 2

template <>
class Fruit<Apple> : public Plant
{public: std::string sound = "I am Apple";};

template <>
class Fruit<Orange> : public Plant
{public: std::string sound = "I am Orange";};

template <>
class Fruit<Banana> : public Plant
{public: std::string sound = "I am Banana";};


template <typename T>
void MakeSound(T fruit)
{
    std::cout << fruit->sound << std::endl;
}

int main() {

    Plant* Basket[5] = {nullptr};
    int types[5] = {0};

    Basket[0] = new Fruit<Apple>;
    types[0] = 0;

    Basket[1] = new Fruit<Orange>;
    types[1] = 1;

    Basket[2] = new Fruit<Orange>;
    types[2] = 1;

    Basket[3] = new Fruit<Apple>;
    types[3] = 0;

    Basket[4] = new Fruit<Apple>;
    types[4] = 0;


    for (int i = 0; i < 5; ++i)
    {
        if (types[i] == 0)
        {
            MakeSound(static_cast<Fruit<Apple> *>(Basket[i]));
        }
        else if (types[i] == 1)
        {
            MakeSound(static_cast<Fruit<Orange> *>(Basket[i]));
        }
        else
        {
            MakeSound(static_cast<Fruit<Banana> *>(Basket[i]));
        }

    }
}
Zingo
  • 600
  • 6
  • 23
  • 6
    What is the *actual* problem you're trying to solve? Why do you want a heterogeneous container? What will this solve that plain inheritance and virtual functions can't handle? This seems like an [XY problem](http://xyproblem.info/). – Some programmer dude Jul 19 '17 at 08:12
  • 2
    *"I am trying to avoid solving this using Boost::variant and run-time polymorphism."*. But it is the natural way. – Jarod42 Jul 19 '17 at 08:13
  • 1
    You *are* trying to use run-time polymorphism. A homegrown half-baked unmaintainable implementation of run-time polymorphism. – n. m. could be an AI Jul 19 '17 at 08:37

1 Answers1

1

I suggest the use of a virtual function to detect id of the type of the derived object; id of the type that I suggest registered in the template class parameter (as the sound) to avoid the need of specializations fo Fruits.

And please: you tagged C++11; so use smart pointers.

An example of what I mean

#include <string>
#include <vector>
#include <memory>
#include <iostream>

struct Plant
 { virtual std::size_t getTypeId () = 0; };

struct Apple
 { 
   static constexpr size_t  typeId { 0U };

   static std::string const & getSnd ()
    { static std::string sound { "I am Apple" }; return sound; }
 }; 

struct Orange
 { 
   static constexpr size_t  typeId { 1U };

   static std::string const & getSnd ()
    { static std::string sound { "I am Orange" }; return sound; }
 }; 

struct Banana
 { 
   static constexpr size_t  typeId { 2U };

   static std::string const & getSnd ()
    { static std::string sound { "I am Banana" }; return sound; }
 }; 

template <typename T>
struct Fruit : public Plant
 {
   virtual std::size_t getTypeId () override { return T::typeId; }

   static std::string const & getSnd () { return T::getSnd(); }
 };


template <typename T>
void MakeSound(T fruit)
 { std::cout << fruit->getSnd() << std::endl; }

int main()
 {
   std::vector<std::unique_ptr<Plant>> bask;

   bask.emplace_back(new Fruit<Apple>);
   bask.emplace_back(new Fruit<Orange>);
   bask.emplace_back(new Fruit<Orange>);
   bask.emplace_back(new Fruit<Apple>);
   bask.emplace_back(new Fruit<Apple>);
   bask.emplace_back(new Fruit<Banana>);

   for ( auto const & up : bask)
    {
      switch ( up->getTypeId() )
       {
         case 0U:
            MakeSound(static_cast<Fruit<Apple> *>(up.get()));
            break;
         case 1U:
            MakeSound(static_cast<Fruit<Orange> *>(up.get()));
            break;
         case 2U:
            MakeSound(static_cast<Fruit<Banana> *>(up.get()));
            break;
         default:
            break;
       }
    }
 }
max66
  • 65,235
  • 10
  • 71
  • 111