www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - template this and traits getOverloads issue.

reply BBasile <b2.temp gmx.com> writes:
Background:
===========

http://stackoverflow.com/questions/33764540/warning-about-overriden-methods-when-using-a-mixin

Why this horrible trick has to be used:
=======================================

cast this as (T) in a function template that's been mixed in an 
ancestor is not always usable, unless I miss something:

----
import std.stdio;

mixin template Bug()
{
     import std.traits;
     void bug(T)()
     {
         foreach(member; __traits(allMembers, T))
             foreach(overload; __traits(getOverloads, T, member))
         {
                 auto dg = &overload;
                 writeln(member);
         }
     }
}

class A
{
     mixin Bug;
     this(){bug!A;}
     void foo(){}
}

class B: A
{
     void bar(){}
     void bar(uint a){}
     this(){bug!B;}
}

void main(){
     new A;
     new B;
}
----


a.d(11,27): Error: this for bar needs to be type B not type a.A
a.d(11,27): Error: this for bar needs to be type B not type a.A
a.d(11,27): Error: this for this needs to be type B not type a.A
everything that can be done to avoid the compilations errors will also prevent "bar" to be written in the output (because a B will never be analyzed). The "only" fix I see is like in the stack overflow answer: statically check if the mixin methods are already there and remix the mixin in each descendant, so that the getOverloads traits works on the right 'this'. What do you think ? is it a bug ?
Nov 20 2015
next sibling parent reply Alex Parrill <initrd.gz gmail.com> writes:
On Friday, 20 November 2015 at 14:01:13 UTC, BBasile wrote:
 Background:
 ===========

 http://stackoverflow.com/questions/33764540/warning-about-overriden-methods-when-using-a-mixin

 Why this horrible trick has to be used:
 =======================================

 cast this as (T) in a function template that's been mixed in an 
 ancestor is not always usable, unless I miss something:

 ----
 import std.stdio;

 mixin template Bug()
 {
     import std.traits;
     void bug(T)()
     {
         foreach(member; __traits(allMembers, T))
             foreach(overload; __traits(getOverloads, T, member))
         {
                 auto dg = &overload;
                 writeln(member);
         }
     }
 }

 class A
 {
     mixin Bug;
     this(){bug!A;}
     void foo(){}
 }

 class B: A
 {
     void bar(){}
     void bar(uint a){}
     this(){bug!B;}
 }

 void main(){
     new A;
     new B;
 }
 ----


a.d(11,27): Error: this for bar needs to be type B not type a.A
a.d(11,27): Error: this for bar needs to be type B not type a.A
a.d(11,27): Error: this for this needs to be type B not type a.A
everything that can be done to avoid the compilations errors will also prevent "bar" to be written in the output (because a B will never be analyzed). The "only" fix I see is like in the stack overflow answer: statically check if the mixin methods are already there and remix the mixin in each descendant, so that the getOverloads traits works on the right 'this'. What do you think ? is it a bug ?
Templates are not virtual, which is why you might be running into issues here; bug thinks it's being called on an `A` object. But you don't need a template for this case; mixin templates have access to `this`: --- import std.stdio; mixin template Bug() { import std.traits; override void bug() { foreach(member; __traits(allMembers, typeof(this))) foreach(overload; __traits(getOverloads, typeof(this), member)) { auto dg = &overload; writeln(member); } } } // Interface to allow for mixin template to use `override` without worrying if // `bug` is the first implementation or not. interface IF { void bug(); } class A : IF { mixin Bug; this(){bug();} void foo(){} } class B: A { mixin Bug; // need mixin on B now to override `bug` for the new class void bar(){} void bar(uint a){} } void main(){ new A; new B; }
Nov 20 2015
next sibling parent BBasile <b2.temp gmx.com> writes:
On Friday, 20 November 2015 at 14:18:00 UTC, Alex Parrill wrote:

If the mixin has to be used on class and on struct, I cant use an 
interface. In this case override will create an error and go back 
to the solution on SO: statically check if things are already 
there.

 Templates are not virtual, which is why you might be running 
 into issues here; bug thinks it's being called on an `A` object.
This is the origin of the problem, I totally forgot this limitation.
Nov 20 2015
prev sibling next sibling parent reply Alex Parrill <initrd.gz gmail.com> writes:
Alternatively, you can use a static method and pass in the 
instance.

Note that `new B` will print A's members twice, because A's 
constructor is always called and `__traits(allMembers, B)` 
includes A's members.

---

import std.stdio;

mixin template Bug()
{
     import std.traits;
     static void bug(T)(T t)
     {
         writeln(">", T.stringof);
         foreach(member; __traits(allMembers, T))
             foreach(overload; __traits(getOverloads, T, member))
         {
                 auto dg = &overload;
                 writeln(member);
         }
     }
}

class A
{
     mixin Bug;
     this(){bug!A(this);}
     void foo(){}
}

class B: A
{
     this(){bug!B(this);}
     void bar(){}
     void bar(uint a){}
}

void main(){
     new A;
     new B;
}
Nov 20 2015
parent BBasile <b2.temp gmx.com> writes:
On Friday, 20 November 2015 at 14:39:29 UTC, Alex Parrill wrote:
 Alternatively, you can use a static method and pass in the 
 instance.
Initially this solution looked awesome but when `bug()` is static, `&dg` returns some functions, not some delegates, which is a problem: this implies that I have to find the matching delegate type, set `.funcptr` to the value of the `dg` function, set `.ptr` to the value of 't'...well not so hard I guess. But then I have no way to detect when a function is really a function or something that might be delegate when instantiated: { auto dg = &overload; if (member == "bar") writeln(member, " ", typeof(dg).stringof); } --> bar void function() --> bar void function(uint a)
 Note that `new B` will print A's members twice, because A's 
 constructor is always called and `__traits(allMembers, B)` 
 includes A's members.
Not a problem, it was already the case anyway. But thx much for the attempt.
Nov 20 2015
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 20 November 2015 at 14:18:00 UTC, Alex Parrill wrote:
 But you don't need a template for this case; mixin templates 
 have access to `this`:
Indeed, this is a good answer too. The difference between this and the template thing I did is that yours is virtual so calling it through an interface will work even on subclasses. However, you must mix it into each sub class. Mine is a template that only needs to be in the base class/interface, but also need a `this` of the derived type to see the derived type. (That's why I ran `this.bug();` in the constructor of B btw) If you call it on an variable typed as the interface, it will only show interface members. IF i = new A(); i.bug(); // would only show interface members with mine A a = new A(); a.bug(); // will now show A's members too That's what the template this parameter does: the type of this at the *usage site* is passed as the parameter. With your solution, the type of this at the *mixin site* is available. I think your solution is generally better for stuff like serialization where you are passed an interface but need child members too. The template this param I used is nice for interface functions that need some kind of covariance; returning a type based on how it was used.
Nov 20 2015
parent BBasile <b2.temp gmx.com> writes:
On Friday, 20 November 2015 at 15:03:06 UTC, Adam D. Ruppe wrote:
 On Friday, 20 November 2015 at 14:18:00 UTC, Alex Parrill wrote:
 But you don't need a template for this case; mixin templates 
 have access to `this`:
Indeed, this is a good answer too. The difference between this and the template thing I did is that yours is virtual so calling it through an interface will work even on subclasses. However, you must mix it into each sub class. Mine is a template that only needs to be in the base class/interface, but also need a `this` of the derived type to see the derived type. (That's why I ran `this.bug();` in the constructor of B btw) If you call it on an variable typed as the interface, it will only show interface members. IF i = new A(); i.bug(); // would only show interface members with mine A a = new A(); a.bug(); // will now show A's members too That's what the template this parameter does: the type of this at the *usage site* is passed as the parameter. With your solution, the type of this at the *mixin site* is available. I think your solution is generally better for stuff like serialization where you are passed an interface but need child members too. The template this param I used is nice for interface functions that need some kind of covariance; returning a type based on how it was used.
I review what I said before. Actually it doesn't work that well. Using the two methods (yours or A.Parrill's one) the protected members in a class that's located in another module are not accessible, the relationship to the current class the traits code is ran into is totally lost and only public members are visible. when bug() delcaration is `static void bug(T)(T t)`, pointer to members are get using __traits(getMember, t, member) and in your solution using _this instead of t. So it's a more for a less.
Nov 20 2015
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 20 November 2015 at 14:01:13 UTC, BBasile wrote:
 everything that can be done to avoid the compilations errors 
 will also prevent "bar" to be written in the output (because a 
 B will never be analyzed). The "only" fix I see is like in the 
 stack overflow answer: statically check if the mixin methods 
 are already there and remix the mixin in each descendant, so 
 that the getOverloads traits works on the right 'this'.
Did you try using a template this parameter like I said in my comment? import std.stdio; mixin template Bug() { import std.traits; void bug(this T)() { T this_ = cast(T) this; foreach(member; __traits(allMembers, T)) foreach(idx, overload; __traits(getOverloads, T, member)) { auto dg = &(__traits(getOverloads, this_, member)[idx]); writeln(T.stringof, ".", member, " ", typeof(dg).stringof); } } } class A { mixin Bug; this(){bug;} void foo(){} } class B: A { void bar(){} void bar(uint a){} this(){this.bug;} } void main(){ new A; new B; } There's a couple quirks in there, but I think you'll find the output shows what you want to see.
Nov 20 2015
parent reply BBasile <b2.temp gmx.com> writes:
On Friday, 20 November 2015 at 14:49:28 UTC, Adam D. Ruppe wrote:
 On Friday, 20 November 2015 at 14:01:13 UTC, BBasile wrote:
 everything that can be done to avoid the compilations errors 
 will also prevent "bar" to be written in the output (because a 
 B will never be analyzed). The "only" fix I see is like in the 
 stack overflow answer: statically check if the mixin methods 
 are already there and remix the mixin in each descendant, so 
 that the getOverloads traits works on the right 'this'.
Did you try using a template this parameter like I said in my comment? foreach(idx, overload; __traits(getOverloads, T, member)) { auto dg = &(__traits(getOverloads, this_, member)[idx]);
Yes, using an index and a second call to getOverloads works, "finally". No need to remix. Thx. I hadn't understood what you meant on SO. One last question: is it possible to nest the calls to functions that take this kind of parameters ? mixin template Bug() { void bug0(this T)(){} void bug1(this T)(){} void allbugs(this T)(){this.bug0(); this.bug1();} } I've tried different parameters and templates and it never works.
Nov 20 2015
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 20 November 2015 at 15:43:00 UTC, BBasile wrote:
 One last question: is it possible to nest the calls to 
 functions that take this kind of parameters ?
auto this_ = cast(T) this; this_.bug0(); this_.bug1();
Nov 20 2015