www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Templates and virtual functions

reply dsimcha <dsimcha yahoo.com> writes:
Every once in a while, it comes up on this NG that a significant limitation of
templates is that they can't add virtual functions to classes.  Of course,
removing this limitation for the general case is impossible w/o completely
changing the compilation model in ways that are bad ideas for other reasons.
However, would it be reasonable to allow _specific instantiations_ of
templates to add virtual functions?  This might be a nice convenience feature.
 Below is an illustration.

class foo {
    T nothing(T)(T arg) {  // Non-virtual.
        return arg;
    }

    virtual nothing!(int);  // Add nothing!(int) to foo's vtable.
    virtual nothing!(float);  // Add nothing!(float) to foo's vtable.
}

class bar : foo {
    // float, int instantiations override those of foo.
    // Any others are non-virtual and don't override those of foo.
    T nothing(T)(T arg) {
        return 2 * arg;
    }
}

class baz : foo {
    int nothing(int arg) {  // overrides foo.nothing!(int)
        return 3 * arg;
    }

    float nothing(float arg) {  // overrides foo.nothing!(float)
        return 3 * arg;
    }
}

Using the virtual keyword, one could add specific instantiations of a template
to a class's vtable.  Then, these functions would automatically work just like
non-template virtual functions.  My guess (I'm not an expert on compiler
internals) is that this would be easy to implement, yet would help in a lot of
cases where only a few instantiations even make sense.  Of course, the
compiler would throw an error when two instantiations differed only by return
type, just as with non-template functions.
Jan 21 2009
next sibling parent BCS <ao pathlink.com> writes:
Reply to dsimcha,

 Every once in a while, it comes up on this NG that a significant
 limitation of templates is that they can't add virtual functions 
 to classes.  Of course, removing this limitation for the general
 case is impossible w/o completely changing the compilation
 model in ways that are bad ideas for other reasons.
 However, would it be reasonable to allow _specific instantiations_ of
 templates to add virtual functions?  This might be a nice convenience
 feature.
 Below is an illustration.

 class foo {
 T nothing(T)(T arg) {  // Non-virtual.
 return arg;
 }
 virtual nothing!(int);  // Add nothing!(int) to foo's vtable.
 virtual nothing!(float);  // Add nothing!(float) to foo's vtable.
 }

 class bar : foo {
 // float, int instantiations override those of foo.
 // Any others are non-virtual and don't override those of foo.
Having this distinction could be a problem because it might make it hard to tell if a function is or is not virtual and also would make tweaking the list in a base class risky.
 T nothing(T)(T arg) { return 2 * arg; }
 }
 class baz : foo {
 int nothing(int arg) {  /* overrides foo.nothing!(int) */ return 3 * arg; }
 float nothing(float arg) {  /* overrides foo.nothing!(float) */ return 
3 * arg; }
 }

 Using the virtual keyword, one could add specific instantiations of a
 template to a class's vtable.  Then, these functions would
 automatically work just like non-template virtual functions.  My guess
 (I'm not an expert on compiler internals) is that this would be easy
 to implement, yet would help in a lot of cases where only a few
 instantiations even make sense.  Of course, the compiler would throw
 an error when two instantiations differed only by return type, just as
 with non-template functions.
 
I think you are right in that it would be easy to implement because I think you could reduce it to an alias declaration. OTOH I think you would get most of what you are proposing with some sugar around alias.
Jan 21 2009
prev sibling next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
dsimcha wrote:
 Every once in a while, it comes up on this NG that a significant limitation of
 templates is that they can't add virtual functions to classes.  Of course,
 removing this limitation for the general case is impossible w/o completely
 changing the compilation model in ways that are bad ideas for other reasons.
 However, would it be reasonable to allow _specific instantiations_ of
 templates to add virtual functions?  This might be a nice convenience feature.
  Below is an illustration.
 
 class foo {
     T nothing(T)(T arg) {  // Non-virtual.
         return arg;
     }
 
     virtual nothing!(int);  // Add nothing!(int) to foo's vtable.
     virtual nothing!(float);  // Add nothing!(float) to foo's vtable.
 }
 
 class bar : foo {
     // float, int instantiations override those of foo.
     // Any others are non-virtual and don't override those of foo.
     T nothing(T)(T arg) {
         return 2 * arg;
     }
 }
 
 class baz : foo {
     int nothing(int arg) {  // overrides foo.nothing!(int)
         return 3 * arg;
     }
 
     float nothing(float arg) {  // overrides foo.nothing!(float)
         return 3 * arg;
     }
 }
 
 Using the virtual keyword, one could add specific instantiations of a template
 to a class's vtable.  Then, these functions would automatically work just like
 non-template virtual functions.  My guess (I'm not an expert on compiler
 internals) is that this would be easy to implement, yet would help in a lot of
 cases where only a few instantiations even make sense.  Of course, the
 compiler would throw an error when two instantiations differed only by return
 type, just as with non-template functions.
If you're asking what I think ytou're asking; it's already there: mixins class foo { T nothing(T)(T arg) { // Non-virtual. return arg; } mixin nothing!(int); // Add nothing!(int) to foo's vtable. mixin nothing!(float); // Add nothing!(float) to foo's vtable. } You can override, whatever with them. You can't use the template syntax to do an override, but... I'm not sure how much of a limitation this is.
Jan 21 2009
next sibling parent Jason House <jason.james.house gmail.com> writes:
Robert Fraser Wrote:

 dsimcha wrote:
 Every once in a while, it comes up on this NG that a significant limitation of
 templates is that they can't add virtual functions to classes.  Of course,
 removing this limitation for the general case is impossible w/o completely
 changing the compilation model in ways that are bad ideas for other reasons.
 However, would it be reasonable to allow _specific instantiations_ of
 templates to add virtual functions?  This might be a nice convenience feature.
  Below is an illustration.
 
 class foo {
     T nothing(T)(T arg) {  // Non-virtual.
         return arg;
     }
 
     virtual nothing!(int);  // Add nothing!(int) to foo's vtable.
     virtual nothing!(float);  // Add nothing!(float) to foo's vtable.
 }
 
 class bar : foo {
     // float, int instantiations override those of foo.
     // Any others are non-virtual and don't override those of foo.
     T nothing(T)(T arg) {
         return 2 * arg;
     }
 }
 
 class baz : foo {
     int nothing(int arg) {  // overrides foo.nothing!(int)
         return 3 * arg;
     }
 
     float nothing(float arg) {  // overrides foo.nothing!(float)
         return 3 * arg;
     }
 }
 
 Using the virtual keyword, one could add specific instantiations of a template
 to a class's vtable.  Then, these functions would automatically work just like
 non-template virtual functions.  My guess (I'm not an expert on compiler
 internals) is that this would be easy to implement, yet would help in a lot of
 cases where only a few instantiations even make sense.  Of course, the
 compiler would throw an error when two instantiations differed only by return
 type, just as with non-template functions.
If you're asking what I think ytou're asking; it's already there: mixins class foo { T nothing(T)(T arg) { // Non-virtual. return arg; } mixin nothing!(int); // Add nothing!(int) to foo's vtable. mixin nothing!(float); // Add nothing!(float) to foo's vtable. } You can override, whatever with them. You can't use the template syntax to do an override, but... I'm not sure how much of a limitation this is.
That feels like a loophole to me. What about using template syntax to call the function? I'm going to guess it'll bypass the vtable! this mix also created a lot of shadowing problems both when making calls and inheriting
Jan 21 2009
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Robert Fraser wrote:
 If you're asking what I think ytou're asking; it's already there: mixins
 
 class foo {
      T nothing(T)(T arg) {  // Non-virtual.
          return arg;
      }
 
      mixin nothing!(int);    // Add nothing!(int) to foo's vtable.
      mixin nothing!(float);  // Add nothing!(float) to foo's vtable.
  }
 
 You can override, whatever with them. You can't use the template syntax 
 to do an override, but... I'm not sure how much of a limitation this is.
Well, I'll be hornswoggled. I never thought of that!
Jan 21 2009
prev sibling next sibling parent Christopher Wright <dhasenan gmail.com> writes:
dsimcha wrote:
 Every once in a while, it comes up on this NG that a significant limitation of
 templates is that they can't add virtual functions to classes.  Of course,
 removing this limitation for the general case is impossible w/o completely
 changing the compilation model in ways that are bad ideas for other reasons.
 However, would it be reasonable to allow _specific instantiations_ of
 templates to add virtual functions?  This might be a nice convenience feature.
This is insufficient to be at all useful to me.
Jan 21 2009
prev sibling next sibling parent Jason House <jason.james.house gmail.com> writes:
dsimcha Wrote:

 Every once in a while, it comes up on this NG that a significant limitation of
 templates is that they can't add virtual functions to classes.  Of course,
 removing this limitation for the general case is impossible w/o completely
 changing the compilation model in ways that are bad ideas for other reasons.
 However, would it be reasonable to allow _specific instantiations_ of
 templates to add virtual functions?  This might be a nice convenience feature.
  Below is an illustration.
 
 class foo {
     T nothing(T)(T arg) {  // Non-virtual.
         return arg;
     }
 
     virtual nothing!(int);  // Add nothing!(int) to foo's vtable.
     virtual nothing!(float);  // Add nothing!(float) to foo's vtable.
 }
 
 class bar : foo {
     // float, int instantiations override those of foo.
     // Any others are non-virtual and don't override those of foo.
     T nothing(T)(T arg) {
         return 2 * arg;
     }
 }
When reading bar in isolation, the special virtual nature of nothing is lost. I'd hope bar would not compile and require some kind of alias to keep it clear to a person reading the code
 
 class baz : foo {
     int nothing(int arg) {  // overrides foo.nothing!(int)
         return 3 * arg;
     }
 
     float nothing(float arg) {  // overrides foo.nothing!(float)
         return 3 * arg;
     }
 }
 
 Using the virtual keyword, one could add specific instantiations of a template
 to a class's vtable.  Then, these functions would automatically work just like
 non-template virtual functions.  My guess (I'm not an expert on compiler
 internals) is that this would be easy to implement, yet would help in a lot of
 cases where only a few instantiations even make sense.  Of course, the
 compiler would throw an error when two instantiations differed only by return
 type, just as with non-template functions.
Jan 21 2009
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
dsimcha wrote:
 Every once in a while, it comes up on this NG that a significant limitation of
 templates is that they can't add virtual functions to classes.  Of course,
 removing this limitation for the general case is impossible w/o completely
 changing the compilation model in ways that are bad ideas for other reasons.
 However, would it be reasonable to allow _specific instantiations_ of
 templates to add virtual functions?  This might be a nice convenience feature.
Setting aside the technical issues for the moment, I'd like to go back to the notion that structs are for compile time polymorphism and classes are for runtime polymorphism. Template functions are clearly in the compile time camp, and if you need compile time polymorphism in a class, perhaps the design should be seriously looked at to see if that's justifiable. As to resolving the technical issue, put the instantiation of the template inside another virtual function: class foo { T nothing(T)(T arg) { // Non-virtual. return arg; } int virtual_nothing(int arg) { return nothing!(arg); } float virtual_nothing(float arg) { return nothing!(arg); } } The advantage of this is there is nothing new to learn.
Jan 21 2009
parent Aarti_pl <aarti interia.pl> writes:
Walter Bright pisze:
 Setting aside the technical issues for the moment, I'd like to go back 
 to the notion that structs are for compile time polymorphism and classes 
 are for runtime polymorphism. Template functions are clearly in the 
 compile time camp, and if you need compile time polymorphism in a class, 
 perhaps the design should be seriously looked at to see if that's 
 justifiable.
That's not always true. I have similar case in my serialization library (Doost project) where template function is called in class to get information about class properties/values. Then, to support serialization from base classes I need to make virtual call to get most derived class. My point here is that there are cases where virtual template functions would be needed in classes.
 As to resolving the technical issue, put the instantiation of the 
 template inside another virtual function:
 
 class foo {
     T nothing(T)(T arg) {  // Non-virtual.
         return arg;
     }
 
     int virtual_nothing(int arg)
     {
     return nothing!(arg);
     }
 
     float virtual_nothing(float arg)
     {
     return nothing!(arg);
     }
 }
 
 The advantage of this is there is nothing new to learn.
I was thinking about above design in my serialization library. As I already said it is needed for serialization of classes from base class reference (It's necessary to know most derived class to do proper serialization). Unfortunately such a design cause quite a big problem for users of such a library. Please let me explain below. I have following template in my libs: template Serializable() { void describeUdt(T)(T arch) { foreach(i, v; this.tupleof) arch.describe(this.tupleof[i], this.tupleof[i].stringof); } } Argument passed to function describeUdt is Archive, which is class, representing what type of output/input is used for serialization (JSon, Text, Binary etc....) To make user class serializable is is just enough to put it as a mixin into user class. class UserClassA { mixin Serializable; } And now, to support serialization from base class pointer I played with following design: class UserClassA { mixin Serializable; void transportUdt(Archive arch) { describeUdt!(Archive)(arch); } } class UserClassB : UserClassA { mixin Serializable; void transportUdt(Archive arch) { describeUdt!(Archive)(arch); } } The problem here is that: 1. User of library will have to know all types of Archive template class (which is privately created by Serializer class). There are a lot of different possible classes which can be produced from Archive template class based on input/output stream type and based on type of archive. They should be unknown for user. 2. It is necessary to put functions transportUdt for every different Archive type into serialized class. It must be done by hand by user, as I don't see a way to make it automatically... Do you see any nice solution for above problem? I was thinking about registering somehow different Archive types during their instantiations and then automatically generating necessary functions transportUdt, but it doesn't seem to be possible currently... I would be happy to know about a good solution for this... REF: http://www.dsource.org/projects/doost/browser/trunk/doost/util/serializer Best Regards Marcin Kuszczak (aarti_pl) www.zapytajmnie.com - my christian site
Jan 22 2009