www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Limited member function templates?

reply "Janice Caron" <caron800 googlemail.com> writes:
I get why member function templates would be a bad idea in general.
They'd be a nightmare to implement! But it occurs to me that if we
insist on some limitations, it might be quite feasible, within those
limitations. The limitation I have in mind is explicit instantiation.

Here's an example of what I'm thinking of.

    class A
    {
        string s;

        int f(T)(T t) { s = toUTF8(t); } /* template member function */

        alias f!(string) fs; /* explicit instantiation */
        alias f!(wstring) fw; /* explicit instantiation */
    }

The trick is that the instantiation must be done *within the class
definition*. We can still disallow anything not explicitly
instantiated inside the class definition, so

    A a;
    a.fs("hello"); /* OK - uses alias*/

    a.f!(string)("hello") /* OK - template is instantiated for string */

    string s;
    a.f(s); /* OK - type deduction */

    a.f!(dstring)("hello"d) /* Not OK - template not instantiated for dstring */

Thoughts?
Nov 09 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 I get why member function templates would be a bad idea in general.
 They'd be a nightmare to implement! But it occurs to me that if we
 insist on some limitations, it might be quite feasible, within those
 limitations. The limitation I have in mind is explicit instantiation.
 
 Here's an example of what I'm thinking of.
 
     class A
     {
         string s;
 
         int f(T)(T t) { s = toUTF8(t); } /* template member function */
 
         alias f!(string) fs; /* explicit instantiation */
         alias f!(wstring) fw; /* explicit instantiation */
     }
 
 The trick is that the instantiation must be done *within the class
 definition*. We can still disallow anything not explicitly
 instantiated inside the class definition, so
 
     A a;
     a.fs("hello"); /* OK - uses alias*/
 
     a.f!(string)("hello") /* OK - template is instantiated for string */
 
     string s;
     a.f(s); /* OK - type deduction */
 
     a.f!(dstring)("hello"d) /* Not OK - template not instantiated for dstring
*/
 
 Thoughts?

Member function templates have worked fine as long as I've been around. The only thing you can't have is _virtual_ member function templates. Is making them virtual what you are talking about? --bb
Nov 09 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Member function templates have worked fine as long as I've been around.

Really? The documentation at http://digitalmars.com/d/template.html says: <quote> Templates cannot be used to add non-static members or functions to classes. For example: class Foo { template TBar(T) { T xx; // Error int func(T) { ... } // Error static T yy; // Ok static int func(T t, int y) { ... } // Ok } } Templates cannot be declared inside functions. </quote>
   The only thing you can't have is _virtual_ member function templates.
   Is making them virtual what you are talking about?

All member functions in D are virtual (unless you explicitly use the "final" keyword). So obviously, yes.

Well, this works (dmd 1.023): ----------- import std.stdio; class Man { void member_function(T)(T arg) { writefln("Got me an x and it's value is %s", x); } int x = 23; } void main() { auto ima = new Man; ima.member_function!(int)(3); } -------------- Seems like the doc is wrong. --bb
Nov 10 2007
prev sibling next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Janice Caron wrote:
 On 11/10/07, Janice Caron <caron800 googlemail.com> wrote:
 On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
   The only thing you can't have is _virtual_ member function templates.
   Is making them virtual what you are talking about?

"final" keyword). So obviously, yes.

When we get the common calling convention whereby f(x) and x.f() are interchangable, then adding non-polymorphic template functions will be a piece of cake. Just declare as f(T)(X x, T t) { ... } and call as x.f(t). Maybe that will be enough. But yeah - I intended to mean "normal" functions. Polymorphic. Virtual.

I was just about to feature-request virtual templated member functions. I can't mock them, which is one of two major problems in my mocks library. You also can't have a templated method in an interface: interface Collection (T) { void addAll(U : T) (Collection!(U) toAdd); // fail } Which leads to annoyances: "why can't I add all my Bicycles to my Vehicles collection?"
Nov 10 2007
next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Christopher Wright wrote:
 Janice Caron wrote:
 On 11/10/07, Janice Caron <caron800 googlemail.com> wrote:
 On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
   The only thing you can't have is _virtual_ member function templates.
   Is making them virtual what you are talking about?

"final" keyword). So obviously, yes.

When we get the common calling convention whereby f(x) and x.f() are interchangable, then adding non-polymorphic template functions will be a piece of cake. Just declare as f(T)(X x, T t) { ... } and call as x.f(t). Maybe that will be enough. But yeah - I intended to mean "normal" functions. Polymorphic. Virtual.

I was just about to feature-request virtual templated member functions. I can't mock them, which is one of two major problems in my mocks library. You also can't have a templated method in an interface: interface Collection (T) { void addAll(U : T) (Collection!(U) toAdd); // fail } Which leads to annoyances: "why can't I add all my Bicycles to my Vehicles collection?"

Think about what you're asking for. An interface defines, essentially, a block of vtable addresses and what they mean. So, in a loose sense, interface Foo { void bar(); void baz(); } Says that for any class that implements Foo, there need to be a block of two vtable entries that point to the implementations of bar and baz. Let's try a different interface: interface Quxx { void zyzzy(T)(T); } How many vtable entries is that? There is no sane answer, because it depends on how the interface is used. And not just in one place: any module in the program could alter the number, order and meaning of the vtable block. If this got introduced, single-file compilation would be impossible: you'd need to parse the *entire* program first, and *then* work on generating output. You might say "well, just treat templated methods differently!" The problem then is how exactly *do* you implement them? In order for an interface to work, you need to be able to cast down to it from any implementation. This means the "template" has to be expressible as a run-time pointer or something. But templates are inherently compile-time beasts. Maybe you could return an array of pointers for each instantiation in the implementation, but you still wouldn't be able to guarantee whether a particular instantiation exists or not. If you're going to go down that road, you might as well just give up and go straight for dynamic dispatch, or just pass around Variants. It'll end up about the same. Trust me, there have been a few times I would have *killed* for templates in interfaces. I actually went and wrote my Variant type specifically because I couldn't. :P That said, I don't think this is a restriction that's going to be lifted any time soon. Standard disclaimer: I can always be wrong. Also, it's 1:36 AM right now, which increases those chances. :P -- Daniel
Nov 10 2007
prev sibling parent reply Yigal Chripun <yigal100 gmail.com> writes:
Christopher Wright wrote:
 Janice Caron wrote:
 On 11/10/07, Janice Caron <caron800 googlemail.com> wrote:
 On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 The only thing you can't have is _virtual_ member function
 templates. Is making them virtual what you are talking about?

the "final" keyword). So obviously, yes.

When we get the common calling convention whereby f(x) and x.f() are interchangable, then adding non-polymorphic template functions will be a piece of cake. Just declare as f(T)(X x, T t) { ... } and call as x.f(t). Maybe that will be enough. But yeah - I intended to mean "normal" functions. Polymorphic. Virtual.

I was just about to feature-request virtual templated member functions. I can't mock them, which is one of two major problems in my mocks library. You also can't have a templated method in an interface: interface Collection (T) { void addAll(U : T) (Collection!(U) toAdd); // fail } Which leads to annoyances: "why can't I add all my Bicycles to my Vehicles collection?"

I may be mistaken, but isn't it enough to use the following: ---- interface Collection (T) { void addAll(T) (Collection!(T) toAdd); } ---- a bicycle should conform to a is-a relationship (a bicycle is a vehicle). so you can in fact add them all due to inheritance. or am I missing something here? templates are only useful at compile time. in run time we need to rely on polymorphism (inheritance). Maybe you could give a better example for the case when you need to have template (virtual) member functions? Yigal
Nov 10 2007
parent Nathan Reed <nathaniel.reed gmail.com> writes:
Yigal Chripun wrote:
 I may be mistaken, but isn't it enough to use the following:
 ----
 interface Collection (T) {
    void addAll(T) (Collection!(T) toAdd);
 }
 ----
 a bicycle should conform to a is-a relationship
 (a bicycle is a vehicle). so you can in fact add them all due to 
 inheritance. or am I missing something here?

Unfortunately that doesn't quite work. Even if a bicycle is-a vehicle, it doesn't follow that a collection of bicycles is-a collection of vehicles. Consider that you can add a car to a collection of vehicles, but it would be an error to add a car to a collection of bicycles. Thanks, Nathan Reed
Nov 11 2007
prev sibling parent Christian Kamm <kamm.incasoftware shift-at-left-and-remove-this.de> writes:
 <quote>
 Templates cannot be used to add non-static members or functions to
 classes. 

I think that's bug 566 from last year: http://d.puremagic.com/issues/show_bug.cgi?id=566
Nov 11 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Member function templates have worked fine as long as I've been around.

Really? The documentation at http://digitalmars.com/d/template.html says: <quote> Templates cannot be used to add non-static members or functions to classes. For example: class Foo { template TBar(T) { T xx; // Error int func(T) { ... } // Error static T yy; // Ok static int func(T t, int y) { ... } // Ok } } Templates cannot be declared inside functions. </quote>
   The only thing you can't have is _virtual_ member function templates.
   Is making them virtual what you are talking about?

All member functions in D are virtual (unless you explicitly use the "final" keyword). So obviously, yes.
Nov 09 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Janice Caron <caron800 googlemail.com> wrote:
 On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
   The only thing you can't have is _virtual_ member function templates.
   Is making them virtual what you are talking about?

All member functions in D are virtual (unless you explicitly use the "final" keyword). So obviously, yes.

When we get the common calling convention whereby f(x) and x.f() are interchangable, then adding non-polymorphic template functions will be a piece of cake. Just declare as f(T)(X x, T t) { ... } and call as x.f(t). Maybe that will be enough. But yeah - I intended to mean "normal" functions. Polymorphic. Virtual.
Nov 09 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Well, this works (dmd 1.023):

Wow! Well, now I'm confused. If you'd said it works in 2.X, I would have thought "Ooh - Walter's experimenting with a new feature" - but in 1.X? Definitely suggests a documentation change would be in order. (For that matter, all the docs relate to 2.X anyway)
Nov 10 2007
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 11/10/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Well, this works (dmd 1.023):

Wow! Well, now I'm confused. If you'd said it works in 2.X, I would have thought "Ooh - Walter's experimenting with a new feature" - but in 1.X?

Also works in 2.X :)
 Definitely suggests a documentation change would be in order.

In fact, the code identified in the document that is an "Error" actually compiles. The results I have found with testing are: - template members variables seem to act like static members of the class - template member functions seem to act like non-virtual member functions of the class (even with override). I think the docs probably should be changed. At least to say Unsupported instead of Error (although I'd much rather see it changed to reflect what actually works :) BTW, given that member templates are supported this way, you can kind of mimic what you want this way: class A { string s; // override in derived classes to alter implementation protected void _s(string t) { s = t;} int f(T)(T t) { _s = toUTF8(t); } // non-virtual function, cannot override } General rule would be: use templates to convert args to what actual members are stored as, use non-template functions to actually interact with members.
 (For that matter, all the docs relate to 2.X anyway)

Click on that D 1.0 link in the upper left, you can get the D 1.0 docs (should be the default IMHO since the default download is D 1.0) -Steve
Nov 10 2007
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2007-11-10 01:30:46 -0500, "Janice Caron" <caron800 googlemail.com> said:

     class A
     {
         string s;
 
         int f(T)(T t) { s = toUTF8(t); } /* template member function */
 
         alias f!(string) fs; /* explicit instantiation */
         alias f!(wstring) fw; /* explicit instantiation */
     }

Why don't you try `mixin` instead of `alias`? Then I think you can even override them in subclasses, meaning they're made virtual. They're difficult to overload though, at least in D 1.x without overload sets (haven't tried with D 2.0). -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 10 2007