virtual template function workaround

Hi,

I know that in C++ we cannot declare template member functions as virtual.
Thus the following declaration is illegal:

class Cillegal
{
public:
    template<class T> virtual void do_it(const T& t){} //or =0;
};

I am not sure I clearly see why this is not allowed.. It seems to be a
problem of creating the table for virtual functions, because such a
declaration could leads to 1000 virtual functions, if the function "do_it" is
called for 1000 different types. But this is known at compile time, so I
don't see the problem...

Anyway, here comes my question: there is some cases, where such a
construction could be very useful. Let say I want something like: (sorry for
that trivial code)

class CillegalA
{
public:
    template<class T> void do_it(const T& t)
    {
        std::cout<<"I am class A "<<T<<std::endl;
    }
};

class CillegalB
{
public:
    template<class T> void do_it(const T& t)
    {
        std::cout<<"I am class B "<<T<<std::endl;
    }
};

template<class T>
void print_it(Cillegal& ill,const T& t)
{
    ill.do_it(t);
}

int main()
{
    CillegalA A;
    print(A);
    CillegalB B;
    print(B);
}

How could I design my classes Cillegal, CillegalA, CillegalB in a legal way,
so that I can keep the print_it function and the main() as is or with only
slight modifications? Is there a way with template nested classes or someting
like that?

Thank you very much in advance,

Sylvain

mandawahAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

fsign21Commented:
You can define the second template parameter for you Cillegal-s

template<class T, class DoItClass>
void print_it(DoItClass& ill,const T& t)
{
   ill.do_it(t);
}
0
n_fortynineCommented:
Just curious though (my question does not relate to what you asked), does the compiler give errors for this code? Since (I think) using template functions from a non-template class requires the use of the ".template" construct, e.g.

struct A {
   template <typename T> void foo();
};

void f(A& a) {
   a.template foo<char>();
}

Did this standard get changed? (Otherwise it would have to be: template <typename T> void print(S& s) {
      s.template foo<T>();
    }
Just curious!)
0
nietodCommented:
>> But this is known at compile time, so I
>> don't see the problem...
It could be handled, but but would be a challenge for the compiler implimenters.  

>> How could I design my classes Cillegal, CillegalA, CillegalB in a legal way,
What's illegal in that code???
0
Ultimate Tool Kit for Technology Solution Provider

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy now.

mandawahAuthor Commented:
Thank you very much!
your solution solve the problem I posted, I didn't think about this elegant solution.

But in fact, I see now that my example was too simple and not completely defining the question. I would like to keep the usual inheritance relationship between my Cillegal* classes, and the "is a" property (a CillegalA object is a Cillegal). In other words I need run-time polymorphism for my Cillegal* and not only static-time.

But I thank you anyway, your answer gave me some idea.
Sylvain
0
mandawahAuthor Commented:
Sorry, I am a begginer with this forum. My last post was in reply to the comments of "fsign21"
I should have quote his post first, apologizes..
I repost it:

fsign21 wrote:
>You can define the second template parameter for you >Cillegal-s

>template<class T, class DoItClass>
>void print_it(DoItClass& ill,const T& t)
>{
>  ill.do_it(t);
>}

Thank you very much!
your solution solve the problem I posted, I didn't think about this elegant solution.

But in fact, I see now that my example was too simple and not completely defining the question. I would like to keep the usual inheritance relationship between my Cillegal* classes, and the "is a" property (a CillegalA object is a Cillegal). In other words I need run-time polymorphism for my Cillegal* and not only static-time.

But I thank you anyway, your answer gave me some idea.
Sylvain

0
mandawahAuthor Commented:
Sorry, I am a begginer with this forum. My last post was in reply to the comments of "fsign21"
I should have quote his post first, apologizes..
I repost it:

fsign21 wrote:
>You can define the second template parameter for you >Cillegal-s

>template<class T, class DoItClass>
>void print_it(DoItClass& ill,const T& t)
>{
>  ill.do_it(t);
>}

Thank you very much!
your solution solve the problem I posted, I didn't think about this elegant solution.

But in fact, I see now that my example was too simple and not completely defining the question. I would like to keep the usual inheritance relationship between my Cillegal* classes, and the "is a" property (a CillegalA object is a Cillegal). In other words I need run-time polymorphism for my Cillegal* and not only static-time.

But I thank you anyway, your answer gave me some idea.
Sylvain

0
mandawahAuthor Commented:
nietod wrote
>>> How could I design my classes Cillegal, CillegalA, CillegalB in a legal way,
>What's illegal in that code???

The class Cillegal is illegal because it declares a virtual member function that is templatized (or : it declares as virtual a template member function).

This is illegal in C++ and my compiler (gcc 2.96) refuses to compile it, and says something like "a virtual function cannot be template".
0
ivecCommented:
Here's how I would do it:

// Here's a convenient & general utility function
//  You could also use boost::lexical_cast instead...
template<typename T>
std::string to_string(const T& t)
{
  std::ostringstream strm;
  strm << t;
  return strm.str();
}

class Cillegal
{
public:
  virtual void do_it(const std::string& msg)=0;

  template<class T> void do_it(const T& t)
  {
     this->do_it( to_string(t) ); // pass-on to virtual fun
  }
};

class CillegalA : public Cillegal
{
public:
  virtual void do_it(const std::string& msg)
  { std::cout<<"I am class A "<<msg<<std::endl; }
};

class CillegalB : public Cillegal
{
public:
  virtual void do_it(const std::string& msg)
  { std::cout<<"I am class B "<<msg<<std::endl; }
};
0
nietodCommented:
>> The class Cillegal is illegal because it declares a virtual member function
>> that is templatized (or : it declares as virtual a template member function).
Except it doesn't!  do_it() is not virtual.   And there is no reason shown in the code for it to be virtual.  i.e. even if it coudl be virtual, there is no advatange to it.  There is no inheritance so there is no opportnity for polymorphism.   And even if there was ihneritance in the example, there still would be no need for a virtual function, since the do_it() function is being invoked by a _template_ function based on the parameter type.  again no need for a virtual function there.  

In other words, there is no virutal function in question, and if there were, no contorion of the code I can image would have any need for the virutal function.
0
mandawahAuthor Commented:
nietod wrote:
"
>>> The class Cillegal (...)
>Except it doesn't!  do_it() is not virtual.   And there is no reason shown in the code for it to be virtual(...)
"

OK OK I see. It's all my fault. I made a "typo" when I wrote my example code. I should have written
class CillegalA : public Cillegal
(...) and
class CillegalB : public Cillegal

otherwise my question was confusing and quite meaningless. I am very sorry for the time you waste trying to answer my badly formed question.

Sylvain
0
fsign21Commented:
mandawah,
there is an elegant solution for such cases, known as Barton-Nackman trick.

I found a short presentation for this "trick", may be you can find a better resource (the best one is of course the book "Engineering and Scientific C++"
von John J. Barton, Lee R. Nackman,Addison-Wesley, 1994)

http://proj-clhep.web.cern.ch/proj-clhep/Workshop-2003/uBlas.ppt

This is an example from this presentation:
template class<T_leaf>
  class Matrix{
    public:
     T_leaf& assign_leaf(){
      return static_cast<T_leaf>(*this);}
     
     double operator () (int i, int j){   //delegate to leaf
      return assign_leaf()(i,j)

class symmetric_matric : public Matrix<symmetric_matrix>

The trick is that the base class takes a template parameter which is the type of the leaf class. This ensures that the complete type of an object is known at compile time. No need for virtual function dispatch
Methods can be selectively specialized in the leaf classes (default in the base, overridden when necessary)
Leaf classes can have methods which are unique to the leaf class.

I hope, this gives you some ideas.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
SalteCommented:
Not only is there any need for virtual template functions but it is actually very hard to implement for C++.

You say the functions are known at compile time.

Unfortunately with template functions they aren't.

Consider:

foo.h is an include file that defines a class hierarchy holding one class foo and it has a:

class foo {
public:
   template <class T> virtual void bar(const T & t);
};


Now consider f1.cpp is a file that includes this and invokes
the virtual function bar for type 'int'.

Fine, the compiler generates code and pretends that bar wasn't template but declared with type 'int' and insert it in virtual table.

Now, consider next file f2.cpp which also includes the file foo.h and invokes the virtual function bar for type 'double'.

Same story it insert the function into the virtual table for type 'double'.

Now you link those two together.

It see that the virtual tables for the class X has a mismatch in the two, it is as if the class was defined differently, one with a 'int' parameter bar and one with a 'double' parameter bar() virtual function. The linker has the following ways to handle this situation:

1. Panic, complain that X isn't properly defined.

This is clearly not an acceptable solution. If we were to allow for virtual template functions this solution isn't acceptable.

2. You can use one vtable index for both functions. This is also clearly not acceptable, it would mean that if you called bar(double) you might end up having the bar(int) code being the one actually called!

3. You can try to 'merge' the two. This means that the compiler cannot generate the indexes into the vtable for a virtual function, when it see a call to bar(int) it must surely call the correct function but as the virtual table isn't defined at compile time but at link time the compiler cannot know this. You could let it be a linker symbol but then the compiler cannot handle such a call as fast as it can if the symbols were plain constants known at compile time. Disallowing virtual templates allows the compiler to have full control of the vtable and its layout at compile time, you would lose that control if you were to allow for virtual template functions.

The strongest point against 3 though is that such merging isn't the job of a linker - this is something a compiler could have done but a linker isn't supposed to modify the compiler datastructures like that. This would require the linker to know about the compiler's internal data structures (vtable layout etc) and is simply unacceptable.

This also contradicts your statement that the template functions are known at compile time. That is exactly what they aren't. Yes, the template functions you call are known at compile time and so the compiler can generate those functions but there might be additional template functions called which isn't called from this .cpp file and which isn't generated at this compilation but is generated at other compilations. Since a virtual template function would imply that they should all fit inside the vtable layout this implies that the vtable would not be known at compile time - horror!

Alf
0
nietodCommented:
If a compiler can handle exported templates (and many can't yet), then it should be able to handle thes types of difficulty without too much additional trouble.  This are the sorts of problems that arise with exported templates. Handling exprted templates, however, has prooven to be difficult for many compilers...
0
SalteCommented:
nietod,

perhaps part of the reason why that proves difficult is some of the problems encountered in this problem as well. I mean it is exactly because of these problems (and others like them) that many compilers have problems with exported templates.

Alf
0
mandawahAuthor Commented:
In reply to Salte's first message:

Thank you very much for your answer. Now I better understand how a compiler handles the template and the virtual functions. I did not realize, even if I know it, that each .cpp file, so each instanciation of a template is compiled separately. Maybe a compiler-linker that can parse the whole source code of a program and remember each instanciation of a template function can then use this informations to create the correct vtable...
0
nietodCommented:
>> I mean it is exactly because of these problems (and others like them) that
>> many compilers have problems with exported templates.
definitely.

>> so each instanciation of a template is
>> compiled separately. Maybe a compiler-linker
>> that can parse the whole source code of a program
>> and remember each instanciation of a template function
>> can then use this informations to create the correct vtable...
This is opening a whole can of worms....

to put it suscinctly, and hopefully still accurately, in a non-exported template, a definition of a template is needed (or at least portions of the template that are instanciated) in each translation unit (basically a source code file) that uses the template.  Each translation unit will then contain a copy of the instanciated template.  

However, if the template is exported, then the definition appears in only one translation unit and a declaration (not definition) for it appears in each translation unit that uses the template.  In this case only a single copy of the each instanciation of the template will appear in the project.  However, this also raises many of the implimentation difficulties that salte suggested.
0
mandawahAuthor Commented:
n_fortynine wrote:

>Just curious though (my question does not relate to what >you asked), does the compiler give errors for this code?
>struct A {
>  template <typename T> void foo();
>};

>void f(A& a) {
>  a.template foo<char>();
>}

This code doesn't give an error with my compiler(Metrowerks CW 8 for win32). But the same code with
void f(A& a) {
  a.foo<char>();
}
doesn't give an error as well.... Both codes work as expected

0
SalteCommented:
template functions in classes should work well:

class X {

   ....

   template <class T>
   void foo(const T & t);

};

should work.

template <class T>
class X {


   ....

   template <class U>
   void foo(const U & u);
};

should work as well.

template <class T, class U>
void X<T>::foo(const U & u)
{
  ....
}

or you can write:

template <class T>
  template <class U>
    void X<T>::foo(const U & u)
    {
       .....
    }

Either way works.

Some compilers may not support it properly but that's a problem with those compilers, standard C++ is supposed to support it.

Alf
0
mandawahAuthor Commented:
in reply to Salte:

and what about the following code? Is it compliant with the standard? Metrowerks CW 8 accepts it, but gcc 2.96.2 (under red-hat) refuse it (parse error before ',' at the line "t.get_the_t().func<int,2>();").

#include <iostream>
using namespace std;

class H
{
public:
        template<class T,int i> void func();
};

template<>
void H::func<int,2>(){cout<<"My param is "<<"two"<<endl;}

template<class T>
class D
{
public:
        T get_the_t()
        {
                T t;
                return t;
        }
};

template<class T>
class M
{
public:
        void do_it()
        {
                T t;
                t.get_the_t().func<int,2>();
        }
};

Thanks in advance
0
mandawahAuthor Commented:
in reply to myself:

if I change the line

"t.get_the_t().func<int,2>();"

for

"t.get_the_t().template func<int,2>();"

everything is cool.

I was not aware of this construction before I read the post from n_fortynine. Thank you all for your posts, I've learned a lot.
0
SalteCommented:
Hmm...sounds odd because it shouldn't work...

The point is that the template function func is a member of the class H and nowhere do you say that T::func() is the same as H::func<int,2>().

The point is that T is a template argument and as such the func() may only be valid if that T is instantiated with a type that really have a function func<int,2>() as member.

If you do instantiate M with M< D<H> > then the above code would be proper but the compiler won't know that until you actually instantiate it.

For this reason writing the 'template' there after the dot tells the compiler that func is indeed a template function and so it will just require the template argument (D<H>) to have a type that return the proper type with such a member function rather than just refuse it.

So yes, the t.get_the_t().template func<int,2>() might work. I am not sure if this is part of standard C++ or if it is a GCC extension though, have to check a bit more before I can answer that.

However, it doesn't surprise me that t.get_the_t().func<int,2>() did not work, I would be surprised if it did. You might say it could just wait with such checking until it actually the that the parameter given to M is proper and so the type really have such a method but that can lead to the compiler accepting a lot of code that isn't correct and you won't get any compilation error until you compile a completely different function which instantiate the template. This is clearly undesirable since you really want to check as much as you can of the tmeplate as early as possible.

This is also the reason why you have the 'typename' keyword in C++.

template <class T>
class foo {
public:
   typedef typename T::bar bar_type;
   ...
};

This indicate that T is a class that must have a type bar defined in the class. We also give it the name foo::bar_type so that we can use bar_type to refer to the same type inside the class foo.

Just check out the STL, it is full of this kind of statements. Of course, you could in principle have simply said:

typedef T::bar bar_type;

and then when instantiating the type if it did happen to have a name T::bar and it was a type then everything's fine. However, using the keyword 'typename' you tell the compiler that you expect the type T to have a type T::bar defined is safer and is therefore required by the compiler.

Alf
0
nietodCommented:
>> If you do instantiate M with M< D<H> > then the above
>> code would be proper but the compiler won't know that
>> until you actually instantiate it.

>> You might say it could just wait with such checking until
>> it actually the that the parameter given to M is proper
>> and so the type really have such a method but that can
>> lead to the compiler accepting a lot of code that isn't correct
>> and you won't get any compilation error until you compile a
>> completely different function which instantiate the template.
Template code is not checked for type errors, until it is instanciated, this is a requirement.

14.3.6 If the use of a templateargument gives rise to an illformed construct in the instantiation of a template specialization, the program is illformed.

Note that is is int eh instanciation of the template, not in reading/parsing the definition.

>> This indicate that T is a class that must have a type bar defined in the class.
More precisely, the "class" (it coudl also be "typename:) in the template argument list indicates that T is a type.  The "typename" on the typedef line does not affect T's identinfty, but it does indicate that "bar" must be a type (as opposed to a member function, data member, etc) difined in T.  The fact that T is a type and that it has members, then forces T to be a class/struct.

>> is therefore required by the compiler.
Its not abolutely required.  It depends on the circumstances.  However there are many cases where the template will be parsed incorrectly because it considers a class member to be a data/function member and not a type member.  These errors can occur during the parsing of the template, before it is instanciated and interpreted "correctly", and prevent compillation.

14.6.2 A name used in a template declaration or definition and that is dependent on a mplateparameter
is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.
0
SalteCommented:
Nietod,

well, gcc for one complains if you write:

template <class T>
class X {
public:
   typedef T::bar bar_type;
};

It will insist that you put 'typename' in between 'typedef' and 'T::bar'.

And yes, even though you write 'class T' it doesn't have to be a class, it could be int etc however, int doesn't have int::bar defined and so the T::bar requires the T to be a class. The 'class T' doesn't. In essence you can see the word 'class' in 'class T' to mean 'type' so T can be int or double:

template <class T> void do_something();

do_something<int>();

should work without problems. Since 'typename' is also a keyword in C++ some people prefer to use 'typename' instead of class in template declarations:

template <typename T> ....;

They do mean the same thing in that position though.

Alf
0
nietodCommented:
>> It will insist that you put 'typename' in between 'typedef' and 'T::bar'.
According to 14.6.2, it must parse T::bar as if bar is not a typename, so it must be a data member of function member of the class.  Thus that statement is ill formed.  but there are cases where it can parse a statement referencing members of a class correctly without the typename "assistance".
0
bcladdCommented:
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

Split: fsign21, nietod, Salte

Please leave any comments here within the next seven days. Experts: Silence
means you don't care.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

-bcl (bcladd)
EE Cleanup Volunteer
0
saurabhramyaCommented:
Hi,
I jsut curious to know that how you solve your problem that you have explained in the beginning.

Thanks
Saurabh
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Storage Software

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.