views:

165

answers:

3

I have a class that uses several policies that are templated. It is called Dish in the following example. I store many of these Dishes in a vector (using a pointer to simple base class), but then I'd like to extract and use them. But I don't know their exact types.

Here is the code; it's a bit long, but really simple:

#include <iostream>
#include <vector>

struct DishBase {
  int id;
  DishBase(int i) : id(i) {}
};

std::ostream& operator<<(std::ostream& out, const DishBase& d) {
  out << d.id;
  return out;
}

// Policy-based class:
template<class Appetizer, class Main, class Dessert>
class Dish : public DishBase {
  Appetizer appetizer_;
  Main main_;
  Dessert dessert_;
public:
  Dish(int id) : DishBase(id) {}
  const Appetizer& get_appetizer() { return appetizer_; }
  const Main& get_main() { return main_; }
  const Dessert& get_dessert() { return dessert_; }
};

struct Storage {
  typedef DishBase* value_type;
  typedef std::vector<value_type> Container;
  typedef Container::const_iterator const_iterator;
  Container container;
  Storage() {
    container.push_back(new Dish<int,double,float>(0));
    container.push_back(new Dish<double,int,double>(1));
    container.push_back(new Dish<int,int,int>(2));
  }
  ~Storage() {
    // delete objects
  }
  const_iterator begin() { return container.begin(); }
  const_iterator end() { return container.end(); }  
};

int main() {
  Storage s;
  for(Storage::const_iterator it = s.begin(); it != s.end(); ++it){
    std::cout << **it << std::endl;
    std::cout << "Dessert: " << *it->get_dessert() << std::endl; // ??
  }
  return 0;
}

The tricky part is here, in the main() function:

    std::cout << "Dessert: " << *it->get_dessert() << std::endl; // ??

How can I access the dessert? I don't even know the Dessert type (it is templated), let alone the complete type of the object that I'm getting from the storage.

This is just a toy example, but I think my code reduces to this. I'd just like to pass those Dish classes around, and different parts of the code will access different parts of it (in the example: its appetizer, main dish, or dessert).

A: 

You will need to have appropriate member functions for querying (in this case an overload for the concrete Dessert type). The policies should expose a way of discovery. Here's a short example:

#include <iostream>
using namespace std;

struct TA { virtual string foo() { return "TA::foo\n"; } };
struct DTA  : TA { virtual string foo() { return "DTA::foo\n"; } };

template <class T>
struct C {
    T t;
};

template <class T>
ostream& operator <<(ostream& o, C<T> c) {
    o << c.t.foo();
    return o;
}

int main(int argc, char* argv[]) 
{
    C<DTA> c;
    cout << c;
}
dirkgently
Do I need a base class for every policy, then? If so, I could get rid of policy templates altogether and just use abstract base classes instead. But I thought I'd better use templates for efficiency ...
The example leads me to think in terms of aggregation alongwith -- as you suggest in your comment -- inheritance.
dirkgently
@dehmann: No, you don't need a base class. You just need that all classes has the same method signature. I think you are looking for compile time polymorphism.
coelhudo
A: 

My understanding is that policy-based template classes are not very container friendly. I just opt for plain old polymorphism for this kind of things. I'd be interested in a solution though.

EDIT: It's perhaps not by coincidence that I cannot find any usage of stl containers throughout Alexandrescu's "Modern C++ Desing" book.

EDIT2: More details on the friction between polymorphism and genericity can be found here http://www.artima.com/cppsource/type_erasure.html. You container can perhaps be made of boost::any objects?

baol
+1  A: 

What you have is not exactly policy-based design IMO... if it were, your class should've actually implemented (i.e. extended) the policies.

Now, back to your question/example. In your container, you store a "DishBase*". Right? From that point on, you loose any compile-time information wrt the actual type of the objects in the collection. So, I'm afraid what you try to do is provably impossible.

What you could do, is use an actual policy-based design, eg.

template<class Appetizer, class Main, class Dessert>
class Dish : public DishBase, Appetizer, Main, Dessert {
}

Then, you could simply use dynamic_cast to check at runtime that you can convert your object to any concrete Appetizer/Dessert/Main.

But from your description, I get the impression that you actually need abstract base classes (i.e. abstract base classes may be the design that makes sense for you, and not policies).

Virgil