?
Solved

virtual template function workaround

Posted on 2003-03-21
27
Medium Priority
?
4,561 Views
Last Modified: 2013-11-15
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

0
Comment
Question by:mandawah
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 6
  • 5
  • +5
27 Comments
 
LVL 1

Expert Comment

by:fsign21
ID: 8181104
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
 
LVL 4

Expert Comment

by:n_fortynine
ID: 8181274
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
 
LVL 22

Expert Comment

by:nietod
ID: 8181286
>> 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
Does Your Cloud Backup Use Blockchain Technology?

Blockchain technology has already revolutionized finance thanks to Bitcoin. Now it's disrupting other areas, including the realm of data protection. Learn how blockchain is now being used to authenticate backup files and keep them safe from hackers.

 

Author Comment

by:mandawah
ID: 8181352
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
 

Author Comment

by:mandawah
ID: 8181380
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
 

Author Comment

by:mandawah
ID: 8181404
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
 

Author Comment

by:mandawah
ID: 8181481
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
 
LVL 1

Expert Comment

by:ivec
ID: 8181604
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
 
LVL 22

Expert Comment

by:nietod
ID: 8181669
>> 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
 

Author Comment

by:mandawah
ID: 8182023
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
 
LVL 1

Accepted Solution

by:
fsign21 earned 100 total points
ID: 8182055
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
 
LVL 12

Expert Comment

by:Salte
ID: 8182267
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
 
LVL 22

Expert Comment

by:nietod
ID: 8182583
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
 
LVL 12

Expert Comment

by:Salte
ID: 8182797
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
 

Author Comment

by:mandawah
ID: 8183574
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
 
LVL 22

Expert Comment

by:nietod
ID: 8183664
>> 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
 

Author Comment

by:mandawah
ID: 8196050
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
 
LVL 12

Expert Comment

by:Salte
ID: 8196238
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
 

Author Comment

by:mandawah
ID: 8197407
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
 

Author Comment

by:mandawah
ID: 8197496
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
 
LVL 12

Assisted Solution

by:Salte
Salte earned 100 total points
ID: 8201554
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
 
LVL 22

Assisted Solution

by:nietod
nietod earned 100 total points
ID: 8202272
>> 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
 
LVL 12

Expert Comment

by:Salte
ID: 8202333
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
 
LVL 22

Expert Comment

by:nietod
ID: 8202468
>> 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
 
LVL 11

Expert Comment

by:bcladd
ID: 9543851
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
 

Expert Comment

by:saurabhramya
ID: 12179264
Hi,
I jsut curious to know that how you solve your problem that you have explained in the beginning.

Thanks
Saurabh
0

Featured Post

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Workplace bullying has increased with the use of email and social media. Retain evidence of this with email archiving to protect your employees.
The article will include the best Data Recovery Tools along with their Features, Capabilities, and their Download Links. Hope you’ll enjoy it and will choose the one as required by you.
This tutorial will walk an individual through the process of installing of Data Protection Manager on a server running Windows Server 2012 R2, including the prerequisites. Microsoft .Net 3.5 is required. To install this feature, go to Server Manager…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
Suggested Courses

764 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question