Caduchon
Caduchon

Reputation: 5201

Virtual template design

In order to work with a simplified example, let's consider some animals eating some items in a food list. The food list has a lot of different iterators for different situations.

class contains_fish
{
  public:
    bool operator () (const Food& food) const;
};
class is_vegetarian
{
  public:
    bool operator () (const Food& food) const;
};
class FoodList
{
  private:
    std::vector<Food> foodItems;
  public:
    typedef std::vector<Food>::iterator iterator;
    typedef std::vector<Food>::const_iterator const_iterator;
    typedef std::vector<Food>::reverse_iterator reverse_iterator;
    typedef std::vector<Food>::const_reverse_iterator const_reverse_iterator;
    typedef boost::filter_iterator<contains_fish,FoodList::iterator> fish_iterator;
    typedef boost::filter_iterator<contains_fish,FoodList::const_iterator> fish_const_iterator;
    typedef boost::filter_iterator<contains_fish,FoodList::reverse_iterator> fish_reverse_iterator;
    typedef boost::filter_iterator<contains_fish,FoodList::const_reverse_iterator> fish_const_reverse_iterator;
    typedef boost::filter_iterator<is_vegetarian,FoodList::iterator> vegetarian_iterator;
    typedef boost::filter_iterator<is_vegetarian,FoodList::const_iterator> vegetarian_const_iterator;
    typedef boost::filter_iterator<is_vegetarian,FoodList::reverse_iterator> vegetarian_reverse_iterator;
    typedef boost::filter_iterator<is_vegetarian,FoodList::const_reverse_iterator> vegetarian_const_reverse_iterator;
    //...
    //... with corresponding begin/end functions :
    FoodList::iterator begin() { return this->foodItems.begin(); }
    FoodList::const_iterator begin() const { return this->foodItems.begin(); }
    //...
    FoodList::vegetarian_const_reverse_iterator begin_vegetarian_const_reverse() const { return boost::make_filter_iterator<is_vegetarian>(this->foodItems.rbegin(), this->foodItems.rend()); }
};

Now I want to give food (a virtual function) to each animal with iterators on the food list. Something like this code (not working due to virtual template function) :

class Animal
{
  public:
    virtual ~Animal() {}
    template <typename FoodListIterator>
    virtual void eat(FoodListIterator begin, FoodListIterator end) = 0;
};

class Dog : public Animal
{
  public:
    virtual ~Dog() {}
    template <typename FoodListIterator>
    virtual void eat(FoodListIterator begin, FoodListIterator end)
    {
      if(begin == end)
        std::cout << "Sad day ! Nothing for me..." << std::endl;
      else
      {
        std::cout << "I'm a dog and I'm going to eat :" << std::endl;
        for(FoodListIterator it = begin; it != end; ++it)
          std::cout << it->toString() << std::endl;
      }
    }
};

void give_fish(std::vector<Animal*>& animals, const FoodList& food_list)
{
  for(unsigned long int i = 0; i < animals.size(); ++i)
    animals[i]->eat(food_list.fish_begin(), food_list.fish_end());
}

I have too many different iterator to implement a virtual function for each signature.

How can I do that elegantly, without C++11 ? If it can help, I know the list of types eligible (the list of iterators described in FoodList).

Upvotes: 0

Views: 95

Answers (1)

Barry
Barry

Reputation: 302842

You could reorganize your interface such that each Animal is fed one single Food virtually and a range of Foods non-virtually:

class Animal
{
  public:
    virtual ~Animal() {}
    virtual void eat(Food& ) = 0; // or Food const&

    template <typename Iterator>
    void eat(Iterator begin, Iterator end) {
        for (; begin != end; ++begin) {
            eat(*begin);
        }
    }
};

Though if you really need the whole range, you could use something like boost::any_range:

class Animal
{
public:
    using FoodRange = boost::any_range<Food, boost::forward_traversal_tag,
                                       Food&, std::ptrdiff_t>;

    virtual ~Animal() {}
    virtual void eat(FoodRange ) = 0;

    template <typename Iterator>
    void eat(Iterator begin, Iterator end) {
        eat(FoodRange{begin, end});
    }
};    

Upvotes: 1

Related Questions