www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - template member function confusion

reply "Francois Chabot" <francois.chabot.dev gmail.com> writes:
Hello, I've been getting into the language recently, and ror the 
most part, it's going pretty smoothly.

I have finally run into the first major snag that's really making 
me scratch my head. Mind you, I can easily code around it, but 
I'd like to understand why it's not working:


Given (roughly) the following code:
class Binding
{
   ...
}

class Bar
{
   Binding[string] Bindings ;

   //cached function
   void foo( Foo target , const ref Matrix44 val ) { ... }
   void foo( Foo target , const ref Vec4 val ) { ... }
   //... more function...

   //convenience interface for non-critical code-paths
   void foo(T)( string target , const ref T val ) { foo( 
Bindings[target] , val ) ; }

}

DMD gives me the following:
Error: Bar.foo(T) conflicts with function Bar.foo at ...

Now, I can easily
A) Change the name of either one the functions (which yields a 
slightly less elegant interface)
B) Not use a template and put string versions of all the foos 
(which yields ugly code)
C) Make the binding-based interface a template and implement the 
functions through specialization (as they are unfortunately 
different enough to not be templatable). While maintaining the 
interface and conciseness, it feels like a hack to me.

I just can't wrap my head around why my current implementation 
cannot work. Any insight?
Apr 08 2012
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, April 08, 2012 22:51:51 Francois Chabot wrote:
 Hello, I've been getting into the language recently, and ror the
 most part, it's going pretty smoothly.
 
 I have finally run into the first major snag that's really making
 me scratch my head. Mind you, I can easily code around it, but
 I'd like to understand why it's not working:
 
 
 Given (roughly) the following code:
 class Binding
 {
    ...
 }
 
 class Bar
 {
    Binding[string] Bindings ;
 
    //cached function
    void foo( Foo target , const ref Matrix44 val ) { ... }
    void foo( Foo target , const ref Vec4 val ) { ... }
    //... more function...
 
    //convenience interface for non-critical code-paths
    void foo(T)( string target , const ref T val ) { foo(
 Bindings[target] , val ) ; }
 
 }
 
 DMD gives me the following:
 Error: Bar.foo(T) conflicts with function Bar.foo at ...
 
 Now, I can easily
 A) Change the name of either one the functions (which yields a
 slightly less elegant interface)
 B) Not use a template and put string versions of all the foos
 (which yields ugly code)
 C) Make the binding-based interface a template and implement the
 functions through specialization (as they are unfortunately
 different enough to not be templatable). While maintaining the
 interface and conciseness, it feels like a hack to me.
 
 I just can't wrap my head around why my current implementation
 cannot work. Any insight?

Two issues here. 1. You cannot currently overload a templated function with a non-templated function or vice versa: http://d.puremagic.com/issues/show_bug.cgi?id=1528 This is a bug which should work and should work at some point. The workaround is to templatize the non-templated functions with empty parens. e.g. void foo()(Foo target, const ref Matrix44 val) 2. Template functions are non-virtual and will _never_ be virtual. This probably isn't causing you any compilation issues, but it does mean that you must be careful with using templated functions in classes. It means that templatizing all of the foos will mean that none of them are virtual (though that's arguably better than making some of them virtual and some not, since that has its own set of issues). Regardless, if you have an API which relies on having a templated function be virtual, you're going to have to find a way to work around it, because templated functions _can't_ be virtual. - Jonathan M Davis
Apr 08 2012
prev sibling next sibling parent "Francois Chabot" <francois.chabot.dev gmail.com> writes:
 Two issues here.

 1. You cannot currently overload a templated function with a 
 non-templated
 function or vice versa: 
 http://d.puremagic.com/issues/show_bug.cgi?id=1528
 This is a bug which should work and should work at some point.

Thank you very much for the confirmation that I'm not crazy here.
 The workaround
 is to templatize the non-templated functions with empty parens. 
 e.g.

 void foo()(Foo target, const ref Matrix44 val)

I could see this working, but I'll steer clear of this in this particular case. "Dirtying" the preferred interface just for the sake of a convenience function is not exactly... nice...
 2. Template functions are non-virtual and will _never_ be 
 virtual. This probably isn't causing you any compilation 
 issues, but it does mean that you must be careful with using 
 templated functions in classes. It means that templatizing all 
 of the foos will mean that none of them are virtual (though 
 that's arguably better than making some of them virtual and 
 some not, since that has its own set of issues). Regardless, if 
 you have an API which relies on having a templated function be 
 virtual, you're going to have to find a way to work around it, 
 because templated functions _can't_ be virtual.

I was already aware of the non-virtualness of templates, and to tell the truth, I much prefer it this way. Maybe it's my C++ background showing here, but is this something people have been asking for? It sounds crazy to me. Once again, thanks for the help!
Apr 08 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, April 08, 2012 23:59:01 Francois Chabot wrote:
 I was already  aware of the non-virtualness of templates, and to
 tell the truth, I much prefer it this way. Maybe it's my C++
 background showing here, but is this something people have been
 asking for? It sounds crazy to me.

Of course, it's something that people have been asking for. It would be fantastic to be able to have templated functions which are virtual. And if you don't really understand how templates work or don't think it through enough, it seems crazy that they _wouldn't_ be virtual. But there are very practical reasons why doing so is more or less infeasible (it's certainly infeasible with how things currently work in D), and once it's explained how templates don't interact with virtual tables very well and all that, it becomes pretty obvious that there's no way that templates can be virtual. There's no question that there are people who want it though. And the fact that some stuff in D really needs to be templated (e.g. a lot of range-based stuff really only works well if it's templated, and you can't support multiple string types very well without templates) makes it so that the lack of virtual templates in classes can be frustrating at times. - Jonathan M Davis
Apr 08 2012
prev sibling parent "Stefan" <stefan schuerger.com> writes:
On Sunday, 8 April 2012 at 20:51:52 UTC, Francois Chabot wrote:
 class Bar
 {
   Binding[string] Bindings ;

   //cached function
   void foo( Foo target , const ref Matrix44 val ) { ... }
   void foo( Foo target , const ref Vec4 val ) { ... }
   //... more function...

   //convenience interface for non-critical code-paths
   void foo(T)( string target , const ref T val ) { foo( 
 Bindings[target] , val ) ; }

 }

 DMD gives me the following:
 Error: Bar.foo(T) conflicts with function Bar.foo at ...

 Now, I can easily
 A) Change the name of either one the functions (which yields a 
 slightly less elegant interface)
 B) Not use a template and put string versions of all the foos 
 (which yields ugly code)
 C) Make the binding-based interface a template and implement 
 the functions through specialization (as they are unfortunately 
 different enough to not be templatable). While maintaining the 
 interface and conciseness, it feels like a hack to me.

If you ask me, choose C - that's the most consistent solution. It's perfectly OK to have specialized templates. That's not a hack, it's a feature ;-) Regarding the virtualization discussion: You could still use method dispatcher pairs, such as // Specialization void foo(T: Matrix44)( Foo target , const ref Matrix44 val ) { myfoo(target , val); } void myfoo( Foo target , const ref Matrix44 val ) { /* real functionality here */ } // general "swiss knife" void foo(T)( string target , const ref T val ) { myfoo(target , val); } void myfoo( Foo target , const ref Variant val ) { /* real functionality here */ } Then the myfoo methods would be overloadable. If you want the "total hack", you could wrap the specialization pairs into mixins. This way, only the foo wrappers are visible to the human user, but the compiler also sees the overloadable, "ordinary" methods. Cheers, Stefan
Apr 08 2012