Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to achieve "virtual template function" in C++

first off: I have read and I know now that a virtual template member function is not (yet?) possible in C++. A workaround would be to make the class a template and then use the template-argument also in the member-function.

But in the context of OOP, I find that the below example would not be very "natural" if the class was actually a template. Please note that the code is actually not working, but the gcc-4.3.4 reports: error: templates may not be ‘virtual’

#include <iostream> #include <vector>  class Animal {     public:         template< class AMOUNT >         virtual void eat( AMOUNT amount ) const {              std::cout << "I eat like a generic Animal." << std::endl;          }         virtual ~Animal() {          } };  class Wolf : public Animal {     public:         template< class AMOUNT >         void eat( AMOUNT amount) const {              std::cout << "I eat like a wolf!" << std::endl;          }         virtual ~Wolf() {          } };  class Fish : public Animal {     public:         template< class AMOUNT >         void eat( AMOUNT amount) const {              std::cout << "I eat like a fish!" << std::endl;          }         virtual ~Fish() {          } };  class GoldFish : public Fish {     public:         template< class AMOUNT >         void eat( AMOUNT amount) const {              std::cout << "I eat like a goldfish!" << std::endl;          }         virtual ~GoldFish() {          } };  class OtherAnimal : public Animal {         virtual ~OtherAnimal() {          } };  int main() {     std::vector<Animal*> animals;     animals.push_back(new Animal());     animals.push_back(new Wolf());     animals.push_back(new Fish());     animals.push_back(new GoldFish());     animals.push_back(new OtherAnimal());      for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) {         (*it)->eat();         delete *it;     }      return 0; } 

So creating a "Fish< Amount > foo" is kind of strange. However, it seems desirable to me to provide an arbitrary amount of food to eat for each animal.

Thus, I am searching a solution about how to achieve something like

Fish bar; bar.eat( SomeAmount food ); 

This becomes particularly useful when looking at the for-loop. One might like to feed a specific amount (FoodAmount) to all of the different animals (via eat() and bind1st() e.g.), it could not be done that easily, although I wound find this very inuitive (and thus to some extent "natural). While some might want to argue now that this is due to the "uniform"-character of a vector, I think/wish that it should be possible to achieve this and I really would like to know how, as this is puzzling me for quite some time now...

[EDIT]

To perhaps clarify the motivation behind my question, I want to program an Exporter-class and let different, more specialized classes derive from it. While the top-level Exporter-class is generally only for cosmetic/structural purpose, a GraphExporter-class is derived, that should again serve as a base-class for even more specialzed export. However, similar to the Animal-example, I would like to be able to define GraphExporter* even on specialized/derived classes (e.g. on SpecialGraphExplorer) but when calling "write( out_file )", it should call the appropriate member function for SpecialGraphExporter instead of GraphExporter::write( out_file).

Maybe this makes my situation and intentions clearer.

Best,

Shadow

like image 595
Shadow Avatar asked May 03 '11 15:05

Shadow


People also ask

Can template functions be virtual?

No, template member functions cannot be virtual.

Can you make templates in C?

You can't really get a high quality template work-alike in C with preprocessor macros; because, those macros expand only once, so at best you can get a data structure that can be retyped, but once processed is that type for the whole program.

What is the syntax of function template?

A function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.

What is a template and why do we use them C++?

Function templates. Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type. In C++ this can be achieved using template parameters.


2 Answers

After some thinking I recognized this as the classic multi-method requirement, i.e. a method that dispatches based on the runtime type of more than one parameter. Usual virtual functions are single dispatch in comparison (and they dispatch on the type of this only).

Refer to the following:

  • Andrei Alexandrescu has written (the seminal bits for C++?) on implementing multi-methods using generics in 'Modern C++ design'
    • Chapter 11: "Multimethods" - it implements basic multi-methods, making them logarithmic (using ordered typelists) and then going all the way to constant-time multi-methods. Quite powerful stuff !
  • A codeproject article that seems to have just such an implementation:
    • no use of type casts of any kind (dynamic, static, reinterpret, const or C-style)
    • no use of RTTI;
    • no use of preprocessor;
    • strong type safety;
    • separate compilation;
    • constant time of multimethod execution;
    • no dynamic memory allocation (via new or malloc) during multimethod call;
    • no use of nonstandard libraries;
    • only standard C++ features is used.
  • C++ Open Method Compiler, Peter Pirkelbauer, Yuriy Solodkyy, and Bjarne Stroustrup
  • The Loki Library has A MultipleDispatcher
  • Wikipedia has quite a nice simple write-up with examples on Multiple Dispatch in C++.

Here is the 'simple' approach from the wikipedia article for reference (the less simple approach scales better for larger number of derived types):

// Example using run time type comparison via dynamic_cast  struct Thing {     virtual void collideWith(Thing& other) = 0; }  struct Asteroid : Thing {     void collideWith(Thing& other) {         // dynamic_cast to a pointer type returns NULL if the cast fails         // (dynamic_cast to a reference type would throw an exception on failure)         if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {             // handle Asteroid-Asteroid collision         } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {             // handle Asteroid-Spaceship collision         } else {             // default collision handling here         }     } }  struct Spaceship : Thing {     void collideWith(Thing& other) {         if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {             // handle Spaceship-Asteroid collision         } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {             // handle Spaceship-Spaceship collision         } else {             // default collision handling here         }     } } 

like image 60
sehe Avatar answered Sep 20 '22 13:09

sehe


Obviously, virtual member function templates are not allowed and could not be realized even theoretically. To build a base class' virtual table, there needs to be a finite number of virtual function-pointer entries. A function template would admit an indefinite amount of "overloads" (i.e. instantiations).

Theoretically-speaking, a language (like C++) could allow virtual member function templates if it had some mechanism to specify the actual (finite) list of instantiations. C++ does have that mechanism (i.e. explicit template instantiations), so I guess it could be possible to do this in a newer C++ standard (although I have no idea what trouble it would entail for compiler vendors to implement this feature). But, that's just a theoretical discussion, in practice, this is simply not allowed. The fact remains, you have to make the number of virtual functions finite (no templates allowed).

Of course, that doesn't mean that class template cannot have virtual functions, nor does it mean that virtual functions cannot call function templates. So, there are many solutions in that vein (like the Visitor pattern or other schemes).

One solution that, I think, serves your purpose (although it is hard to comprehend) elegantly is the following (which is basically a visitor pattern):

#include <iostream> #include <vector>  struct Eater {    virtual void operator()(int amount) const = 0;   virtual void operator()(double amount) const = 0; };  template <typename EaterType> struct Eater_impl : Eater {   EaterType& data;   Eater_impl(EaterType& aData) : data(aData) { };   virtual void operator()(int amount) const { data.eat_impl(amount); };   virtual void operator()(double amount) const { data.eat_impl(amount); }; };  class Animal {   protected:     Animal(Eater& aEat) : eat(aEat) { };   public:     Eater& eat;     virtual ~Animal() { delete &eat; }; };  class Wolf : public Animal {   private:     template< class AMOUNT >     void eat_impl( AMOUNT amount) const {        std::cout << "I eat like a wolf!" << std::endl;      }    public:     friend struct Eater_impl<Wolf>;             Wolf() : Animal(*(new Eater_impl<Wolf>(*this))) { };     virtual ~Wolf() { }; };  class Fish : public Animal {   private:     template< class AMOUNT >     void eat_impl( AMOUNT amount) const {        std::cout << "I eat like a fish!" << std::endl;      }   public:     friend struct Eater_impl<Fish>;     Fish() : Animal(*(new Eater_impl<Fish>(*this))) { };     virtual ~Fish() { }; };  int main() {   std::vector<Animal*> animals;   animals.push_back(new Wolf());   animals.push_back(new Fish());    for (std::vector<Animal*>::const_iterator it = animals.begin(); it != animals.end(); ++it) {     (*it)->eat(int(0));     (*it)->eat(double(0.0));     delete *it;   };    return 0; }; 

The above is a neat solution because it allows you to define a finite number of overloads that you want in one place only (in the Eater_impl class template) and all you need in the derived class is a function template (and possibly additional overloads, for special cases). There is, of course, a bit of overhead, but I guess that a bit more thought could be put into it to reduce the overhead (additional reference storage and dynamic allocation of Eater_impl). I guess, the curiously recurring template pattern could probably be employed somehow to this end.

like image 23
Mikael Persson Avatar answered Sep 19 '22 13:09

Mikael Persson