digitalmars.D - Getting around the non-virtuality of templates
- Stewart Gordon (24/24) Mar 25 2012 I'm coming up against some interesting challenges while porting stuff in...
- James Miller (20/47) Mar 25 2012 ic
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (6/33) Mar 25 2012 I believe the officially vetted answer is to have the templated calls
- Steven Schveighoffer (31/55) Mar 26 2012 I have definitely had issues with this. In dcollections, I have version...
- Stewart Gordon (32/44) Mar 27 2012 I can't seem to get this to work at the moment:
- Artur Skawina (7/21) Mar 27 2012 class Base {
-
Stewart Gordon
(8/15)
Mar 27 2012
- Steven Schveighoffer (9/27) Mar 27 2012 One tip -- if you are doing method as above inside a class and not an
- Stewart Gordon (12/17) Mar 27 2012 Which would work if the function always returns this. But in the genera...
I'm coming up against some interesting challenges while porting stuff in my utility library to D2. Firstly, D2 uses opBinary and opOpAssign, rather than the operator-specific op* and op*Assign. While the latter still work, they aren't mentioned in the current D2 docs. Which would imply that they're on the way out; however, there's no mention at https://github.com/D-Programming-Language/d-programming-language.org/blob/master/deprecate.dd (See also http://d.puremagic.com/issues/show_bug.cgi?id=7779 ) Still, it seems clear that opBinary/opOpAssign is the D2 way of doing it. But it has the drawback that, because it's a template, it isn't virtual. One way about it is to keep the D1-style op functions and make opUnary/opBinary/opOpAssign call these. But is there a better way? The other isn't a D2-specific issue, though D2 increases the significance of it. I have a method with the signature Set opAnd(bool delegate(Element) dg) I would like to enable a user of the library to pass in a delegate whose parameter is any type to which Element is implicitly convertible. This could be the same type as Element with the top-level constancy changed (probably the main use case), or a type that is distinct beyond the constancy level. Turning it into a template Set opAnd(E)(bool delegate(E) dg) would address this, but prevent overriding with the appropriate code for each set implementation. Who else has been faced with this problem? Have you found a good way of dealing with it? Stewart.
Mar 25 2012
On 26 March 2012 11:36, Stewart Gordon <smjg_1998 yahoo.com> wrote:I'm coming up against some interesting challenges while porting stuff in =myutility library to D2. Firstly, D2 uses opBinary and opOpAssign, rather than the operator-specif=icop* and op*Assign. =C2=A0While the latter still work, they aren't mention=ed inthe current D2 docs. Which would imply that they're on the way out; howev=er,there's no mention at https://github.com/D-Programming-Language/d-programming-language.org/blob=/master/deprecate.dd(See also http://d.puremagic.com/issues/show_bug.cgi?id=3D7779 ) Still, it seems clear that opBinary/opOpAssign is the D2 way of doing it. =C2=A0But it has the drawback that, because it's a template, it isn't vir=tual.=C2=A0One way about it is to keep the D1-style op functions and make opUnary/opBinary/opOpAssign call these. =C2=A0But is there a better way? The other isn't a D2-specific issue, though D2 increases the significance=ofit. =C2=A0I have a method with the signature =C2=A0 =C2=A0Set opAnd(bool delegate(Element) dg) I would like to enable a user of the library to pass in a delegate whose parameter is any type to which Element is implicitly convertible. =C2=A0T=hiscould be the same type as Element with the top-level constancy changed (probably the main use case), or a type that is distinct beyond the constancy level. =C2=A0Turning it into a template =C2=A0 =C2=A0Set opAnd(E)(bool delegate(E) dg) would address this, but prevent overriding with the appropriate code for each set implementation. Who else has been faced with this problem? =C2=A0Have you found a good wa=y ofdealing with it? Stewart.Non-virtuality of templates is a general problem at the moment. The issues seem to be around how to handle inheritance of template arguments, and how to dispatch the functions based on a combination of template arguments and class hierarchy. This is a hard problem with no obvious answer. in terms of trying to work around it, perhaps compile-time reflection could help, I haven't encountered this before, but that's where I would start. -- James Miller
Mar 25 2012
On Mon, 26 Mar 2012 00:36:08 +0200, Stewart Gordon <smjg_1998 yahoo.com> wrote:I'm coming up against some interesting challenges while porting stuff in my utility library to D2. Firstly, D2 uses opBinary and opOpAssign, rather than the operator-specific op* and op*Assign. While the latter still work, they aren't mentioned in the current D2 docs. Which would imply that they're on the way out; however, there's no mention at https://github.com/D-Programming-Language/d-programming-language.org/blob/master/deprecate.dd (See also http://d.puremagic.com/issues/show_bug.cgi?id=7779 ) Still, it seems clear that opBinary/opOpAssign is the D2 way of doing it. But it has the drawback that, because it's a template, it isn't virtual. One way about it is to keep the D1-style op functions and make opUnary/opBinary/opOpAssign call these. But is there a better way? The other isn't a D2-specific issue, though D2 increases the significance of it. I have a method with the signature Set opAnd(bool delegate(Element) dg) I would like to enable a user of the library to pass in a delegate whose parameter is any type to which Element is implicitly convertible. This could be the same type as Element with the top-level constancy changed (probably the main use case), or a type that is distinct beyond the constancy level. Turning it into a template Set opAnd(E)(bool delegate(E) dg) would address this, but prevent overriding with the appropriate code for each set implementation. Who else has been faced with this problem? Have you found a good way of dealing with it? Stewart.I believe the officially vetted answer is to have the templated calls forward to virtual functions. Of course, if you need to do something specific before handing it there, you're out of luck.
Mar 25 2012
On Sun, 25 Mar 2012 18:36:08 -0400, Stewart Gordon <smjg_1998 yahoo.com> wrote:I'm coming up against some interesting challenges while porting stuff in my utility library to D2. Firstly, D2 uses opBinary and opOpAssign, rather than the operator-specific op* and op*Assign. While the latter still work, they aren't mentioned in the current D2 docs. Which would imply that they're on the way out; however, there's no mention at https://github.com/D-Programming-Language/d-programming-language.org/blob/master/deprecate.dd (See also http://d.puremagic.com/issues/show_bug.cgi?id=7779 ) Still, it seems clear that opBinary/opOpAssign is the D2 way of doing it. But it has the drawback that, because it's a template, it isn't virtual. One way about it is to keep the D1-style op functions and make opUnary/opBinary/opOpAssign call these. But is there a better way?I have definitely had issues with this. In dcollections, I have versions of opBinary commented out, because at the time of writing, templates weren't allowed in the D compiler. I filed this bug: http://d.puremagic.com/issues/show_bug.cgi?id=4174 Looks like it hasn't been closed yet... So for now, I use the undocumented old-style functions. One other thing that this "wrapper" method loses is covariance, which I use a lot in dcollections. I haven't filed a bug on it, but there is at least a workaround on this one -- the template can capture the type of "this" from the call site as a template parameter.The other isn't a D2-specific issue, though D2 increases the significance of it. I have a method with the signature Set opAnd(bool delegate(Element) dg) I would like to enable a user of the library to pass in a delegate whose parameter is any type to which Element is implicitly convertible. This could be the same type as Element with the top-level constancy changed (probably the main use case), or a type that is distinct beyond the constancy level. Turning it into a template Set opAnd(E)(bool delegate(E) dg) would address this, but prevent overriding with the appropriate code for each set implementation.What I would do is this (assuming template interfaces worked): Set opAnd(E)(bool delegate(E) dg) if(is(E == Element)) { // call protected virtual opAnd equivalent which takes delegate of Element } Set opAnd(E)(bool delegate(E) dg) if(!is(E == Element) && implicitlyConvertsTo!(E, Element)) { bool _dg(Element e) { return dg(e); } // call protected virtual opAnd equivalent with &_dg } Note, with proper delegate implicit conversions, you could probably get some better optimization (including delegates that only differ by const) by checking if the delegate implicitly converts instead of the element. -Steve
Mar 26 2012
On 26/03/2012 14:37, Steven Schveighoffer wrote: <snip>So for now, I use the undocumented old-style functions. One other thing that this "wrapper" method loses is covariance, which I use a lot in dcollections. I haven't filed a bug on it, but there is at least a workaround on this one -- the template can capture the type of "this" from the call site as a template parameter.I can't seem to get this to work at the moment: ---------- class Base { T method(T = typeof(this))() { return this; } } class Derived : Base {} void main() { Base b = new Base; Derived d = new Derived; auto bm = b.method(); auto dm = d.method(); b = d; auto dbm = b.method(); pragma(msg, typeof(bm)); pragma(msg, typeof(dm)); pragma(msg, typeof(dbm)); } ---------- C:\Users\Stewart\Documents\Programming\D\Tests>dmd typeof_this Base Base Base ---------- Or do you mean something else? <snip>Set opAnd(E)(bool delegate(E) dg) if(!is(E == Element) && implicitlyConvertsTo!(E, Element)) { bool _dg(Element e) { return dg(e); } // call protected virtual opAnd equivalent with &_dg }<snip> I was beginning to think along those lines myself. I'll try it and see how I get on. Stewart.
Mar 27 2012
On 03/27/12 17:12, Stewart Gordon wrote:On 26/03/2012 14:37, Steven Schveighoffer wrote: <snip>class Base { T method(this T)() { return cast(T)this; } } arturSo for now, I use the undocumented old-style functions. One other thing that this "wrapper" method loses is covariance, which I use a lot in dcollections. I haven't filed a bug on it, but there is at least a workaround on this one -- the template can capture the type of "this" from the call site as a template parameter.I can't seem to get this to work at the moment: ---------- class Base { T method(T = typeof(this))() { return this; } }
Mar 27 2012
On 27/03/2012 16:12, Stewart Gordon wrote:On 26/03/2012 14:37, Steven Schveighoffer wrote: <snip><snip> Just figured how to do it. T method(this T)() { return cast(T) this; } Talk about having not got into D2 programming before.... Stewart.So for now, I use the undocumented old-style functions. One other thing that this "wrapper" method loses is covariance, which I use a lot in dcollections. I haven't filed a bug on it, but there is at least a workaround on this one -- the template can capture the type of "this" from the call site as a template parameter.I can't seem to get this to work at the moment:
Mar 27 2012
On Tue, 27 Mar 2012 11:26:08 -0400, Stewart Gordon <smjg_1998 yahoo.com> wrote:On 27/03/2012 16:12, Stewart Gordon wrote:One tip -- if you are doing method as above inside a class and not an interface, you can use: cast(T)cast(void*)this; which should avoid the unnecessary dynamic cast. This will *not* work in an interface. I'd argue the compiler should be aware of the special type of T for doing casts... -SteveOn 26/03/2012 14:37, Steven Schveighoffer wrote: <snip><snip> Just figured how to do it. T method(this T)() { return cast(T) this; } Talk about having not got into D2 programming before....So for now, I use the undocumented old-style functions. One other thing that this "wrapper" method loses is covariance, which I use a lot in dcollections. I haven't filed a bug on it, but there is at least a workaround on this one -- the template can capture the type of "this" from the call site as a template parameter.I can't seem to get this to work at the moment:
Mar 27 2012
On 27/03/2012 16:46, Steven Schveighoffer wrote: <snip>One tip -- if you are doing method as above inside a class and not an interface, you can use: cast(T)cast(void*)this; which should avoid the unnecessary dynamic cast.Which would work if the function always returns this. But in the general case of what I'm using it for, it would call a virtual function that is meant to always return an object of the same class as this, but somebody could potentially create a subclass that breaks this rule. But I suppose I could try comparing the .classinfo in an out contract to make sure it doesn't happen....This will *not* work in an interface. I'd argue the compiler should be aware of the special type of T for doing casts...What's more, given the "this T" parameter it ought to automatically treat this as being of type T within the body of the function. Essentially, semantically analyse the function when instantiated as a method of T rather than as a method of the class in which it is actually placed. Stewart.
Mar 27 2012