www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Automatic method overriding in sub-classes

reply Tofu Ninja <emmons0 purdue.edu> writes:
I know this has basically no chance of ever actually being added 
because that is the way of all "I want feature X" threads, but I 
thought I would post this anyways because I seem to want it 
constantly.

So we have TemplateThisParameters methods which are cool but have 
some drawbacks.
They are templates so they are implicitly non-virtual and are 
called based on the type of the reference.
It would be very nice to be able to auto generate method 
overrides based on the this type with out having to drop a mixin 
in every sub class.
This would be very useful for CT-reflection.
I will call this new feature auto override methods for the rest 
of this post.

An auto override method would take the form of a template method 
with only a single type argument that is the type of the class or 
any of it's sub classes.
To differentiate it from TemplateThisParameters it could look 
like:

      returnType functionName(auto override this T)() {...}

When ever a class with an auto override method is sub-classed, 
the auto override method template is re-instantiated with the 
sub-class type and the method is overridden automatically.
Key point is that the method will still be virtual.


Here is an example contrasting an auto override method with a 
regular TemplateThisParameters method.


class A
{
      void foo(this T)() { writeln(T.stringof); }
      void bar(auto override this T)() { writeln(T.stringof); }
}

class B : A {}

void main()
{
      A a = new A();
      a.foo(); // prints "A"
      a.bar(); // prints "A"

      B b = new B();
      b.foo(); // prints "B"
      b.bar(); // prints "B"

      A c = new B();
      c.foo(); // prints "A"
      c.bar(); // prints "B" <-- main advantage, method is virtual
}



Possible uses:
-Automatic generation of nice toString.

-Auto-generate property panes. This is actually a use I need 
right now, to generate property panes for entity types in a game 
engine(unity style).

-Another time I needed it, I have a UI system that will 
automatically apply ui formatting based on the existence of 
"stylize properties" that can be read at compile time, but it 
required a virtual method to have to be redefined for each 
sub-class which simply reflected on the this type and checked for 
the existence of the "stylize properties".

-Check if a sub-class implements certain features

-Check if sub-class has a certain UDA
etc...


I find myself needing this a lot in my code, especially as I 
write a lot of CT-reflection.
So far the only way I have found to achieve this is to drop a 
string mixin in every subclass putting the override in.

Thoughts?
Any one else needed this before?
Any clever way to do this now that I have not thought of?

-Tofu
Oct 26 2015
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 10/26/2015 04:25 PM, Tofu Ninja wrote:

 class A
 {
       void foo(this T)() { writeln(T.stringof); }
       void bar(auto override this T)() { writeln(T.stringof); }
 }

 class B : A {}

 void main()
 {
       A a = new A();
       a.foo(); // prints "A"
       a.bar(); // prints "A"

       B b = new B();
       b.foo(); // prints "B"
       b.bar(); // prints "B"

       A c = new B();
       c.foo(); // prints "A"
       c.bar(); // prints "B" <-- main advantage, method is virtual
 }

I don't understand all uses of the request but typeid() returns a TypeInfo reference, which is about the actual type of an object. The following change produces the expected output in this case (except, it has the added module name): void bar() { writeln(typeid(this).name); } The output: A deneme.A B deneme.B A deneme.B Ali
Oct 26 2015
next sibling parent Tofu Ninja <emmons0 purdue.edu> writes:
On Tuesday, 27 October 2015 at 00:07:36 UTC, Ali Çehreli wrote:
 On 10/26/2015 04:25 PM, Tofu Ninja wrote:

 class A
 {
       void foo(this T)() { writeln(T.stringof); }
       void bar(auto override this T)() { writeln(T.stringof); }
 }

 class B : A {}

 void main()
 {
       A a = new A();
       a.foo(); // prints "A"
       a.bar(); // prints "A"

       B b = new B();
       b.foo(); // prints "B"
       b.bar(); // prints "B"

       A c = new B();
       c.foo(); // prints "A"
       c.bar(); // prints "B" <-- main advantage, method is 
 virtual
 }

I don't understand all uses of the request but typeid() returns a TypeInfo reference, which is about the actual type of an object. The following change produces the expected output in this case (except, it has the added module name): void bar() { writeln(typeid(this).name); } The output: A deneme.A B deneme.B A deneme.B Ali
For something as simple as the name, yea, type id works. But for something like iterating over UDA's or looking at __traits(allMembers) or something like that, type id is not what is needed.
Oct 26 2015
prev sibling parent reply Tofu Ninja <emmons0 purdue.edu> writes:
On Tuesday, 27 October 2015 at 00:07:36 UTC, Ali Çehreli wrote:
 I don't understand all uses of the request but typeid() returns 
 a TypeInfo reference, which is about the actual type of an 
 object. The following change produces the expected output in 
 this case (except, it has the added module name):

 void bar() { writeln(typeid(this).name); }

 The output:

 A
 deneme.A
 B
 deneme.B
 A
 deneme.B

 Ali
Here is another example void main() { A a = new A(); a.foo(); // prints nothing a.bar(); // prints nothing B b = new B(); a.foo(); // prints X b.bar(); // prints X A c = new B(); a.foo(); // prints nothing c.bar(); // prints X, <--- main advantage } enum uda; class A { void foo(this T)() { import std.traits : hasUDA; auto t = cast(T) this; foreach(s; __traits(allMembers, T)) { static if(hasUDA!(mixin("t." ~ s), uda)) { writeln(s); } } } void bar(auto override this T)() { auto t = cast(T) this; t.foo(); } } class B : A { uda int x; }
Oct 26 2015
parent Tofu Ninja <emmons0 purdue.edu> writes:
On Tuesday, 27 October 2015 at 00:27:46 UTC, Tofu Ninja wrote:
 ...
crap, copy paste error change the main to void main() { A a = new A(); a.foo(); // prints nothing a.bar(); // prints nothing B b = new B(); b.foo(); // prints X b.bar(); // prints X A c = new B(); c.foo(); // prints nothing c.bar(); // prints X, <--- main advantage }
Oct 26 2015
prev sibling next sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Monday, 26 October 2015 at 23:25:49 UTC, Tofu Ninja wrote:
 I know this has basically no chance of ever actually being 
 added because that is the way of all "I want feature X" 
 threads, but I thought I would post this anyways because I seem 
 to want it constantly.

 [...]
Stole the words right outta my mouth ;) http://forum.dlang.org/post/lnidwetvkpvrpecbpsvw forum.dlang.org +1 Bit
Oct 26 2015
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2015-10-27 00:25, Tofu Ninja wrote:
 I know this has basically no chance of ever actually being added because
 that is the way of all "I want feature X" threads, but I thought I would
 post this anyways because I seem to want it constantly.

 So we have TemplateThisParameters methods which are cool but have some
 drawbacks.
 They are templates so they are implicitly non-virtual and are called
 based on the type of the reference.
 It would be very nice to be able to auto generate method overrides based
 on the this type with out having to drop a mixin in every sub class.
 This would be very useful for CT-reflection.
 I will call this new feature auto override methods for the rest of this
 post.

 An auto override method would take the form of a template method with
 only a single type argument that is the type of the class or any of it's
 sub classes.
 To differentiate it from TemplateThisParameters it could look like:

       returnType functionName(auto override this T)() {...}

 When ever a class with an auto override method is sub-classed, the auto
 override method template is re-instantiated with the sub-class type and
 the method is overridden automatically.
 Key point is that the method will still be virtual.


 Here is an example contrasting an auto override method with a regular
 TemplateThisParameters method.


 class A
 {
       void foo(this T)() { writeln(T.stringof); }
       void bar(auto override this T)() { writeln(T.stringof); }
 }

 class B : A {}

 void main()
 {
       A a = new A();
       a.foo(); // prints "A"
       a.bar(); // prints "A"

       B b = new B();
       b.foo(); // prints "B"
       b.bar(); // prints "B"

       A c = new B();
       c.foo(); // prints "A"
       c.bar(); // prints "B" <-- main advantage, method is virtual
 }

I don't think this is possible. Think of code looking like this: // Imagine not having access to the source code "createA" A createA() { new B; } void inspectA(A a) { a.bar(); } How should the compiler know that when instantiating/calling "bar", T should be set to B? The compiler might not even know about B exists at all. Even if the compiler does have access to the complete source code it would, most likely, need to do a full program analyze to figure out the type of T, which is quite complicated. -- /Jacob Carlborg
Oct 27 2015
next sibling parent reply Tofu Ninja <emmons0 purdue.edu> writes:
On Tuesday, 27 October 2015 at 07:52:27 UTC, Jacob Carlborg wrote:
 [...]
 I don't think this is possible. Think of code looking like this:

 // Imagine not having access to the source code "createA"
 A createA()
 {
     new B;
 }

 void inspectA(A a)
 {
     a.bar();
 }

 How should the compiler know that when instantiating/calling 
 "bar", T should be set to B? The compiler might not even know 
 about B exists at all. Even if the compiler does have access to 
 the complete source code it would, most likely, need to do a 
 full program analyze to figure out the type of T, which is 
 quite complicated.
The method is instantiated when the subclass is defined so T would obviously be right on hand. Sub-classing a class with an auto override method would implicitly instantiate and override the method. Calling a.bar() would have no problems because bar would be virtual.
Oct 27 2015
parent Jacob Carlborg <doob me.com> writes:
On 2015-10-27 09:05, Tofu Ninja wrote:

 The method is instantiated when the subclass is defined so T would
 obviously be right on hand. Sub-classing a class with an auto override
 method would implicitly instantiate and override the method. Calling
 a.bar() would have no problems because bar would be virtual.
Hmm, interesting. -- /Jacob Carlborg
Oct 27 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 27 October 2015 at 07:52:27 UTC, Jacob Carlborg wrote:
 On 2015-10-27 00:25, Tofu Ninja wrote:
 I know this has basically no chance of ever actually being 
 added because
 that is the way of all "I want feature X" threads, but I 
 thought I would
 post this anyways because I seem to want it constantly.

 So we have TemplateThisParameters methods which are cool but 
 have some
 drawbacks.
 They are templates so they are implicitly non-virtual and are 
 called
 based on the type of the reference.
 It would be very nice to be able to auto generate method 
 overrides based
 on the this type with out having to drop a mixin in every sub 
 class.
 This would be very useful for CT-reflection.
 I will call this new feature auto override methods for the 
 rest of this
 post.

 An auto override method would take the form of a template 
 method with
 only a single type argument that is the type of the class or 
 any of it's
 sub classes.
 To differentiate it from TemplateThisParameters it could look 
 like:

       returnType functionName(auto override this T)() {...}

 When ever a class with an auto override method is sub-classed, 
 the auto
 override method template is re-instantiated with the sub-class 
 type and
 the method is overridden automatically.
 Key point is that the method will still be virtual.


 Here is an example contrasting an auto override method with a 
 regular
 TemplateThisParameters method.


 class A
 {
       void foo(this T)() { writeln(T.stringof); }
       void bar(auto override this T)() { writeln(T.stringof); }
 }

 class B : A {}

 void main()
 {
       A a = new A();
       a.foo(); // prints "A"
       a.bar(); // prints "A"

       B b = new B();
       b.foo(); // prints "B"
       b.bar(); // prints "B"

       A c = new B();
       c.foo(); // prints "A"
       c.bar(); // prints "B" <-- main advantage, method is 
 virtual
 }

I don't think this is possible. Think of code looking like this: // Imagine not having access to the source code "createA" A createA() { new B; } void inspectA(A a) { a.bar(); } How should the compiler know that when instantiating/calling "bar", T should be set to B? The compiler might not even know about B exists at all. Even if the compiler does have access to the complete source code it would, most likely, need to do a full program analyze to figure out the type of T, which is quite complicated.
It's not a template function. The auto override wouldn't be instantiated if/when it's called. It would always be instantiated automatically when the class was compiled. Then, if you were just using the class as an import, the compiler would know that the definition should exists somewhere(static lib, etc..). When an auto override function was called, it may or may not have the definition..it wouldn't matter, since it's like a virtual function. But, if someone tried to subclass a class which had even one auto override function with missing it's definition, then the effect would be the same as trying to subclass a sealed/final class. Bit
Oct 27 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 27 October 2015 at 14:21:12 UTC, bitwise wrote:
 On Tuesday, 27 October 2015 at 07:52:27 UTC, Jacob Carlborg 
 wrote:
 On 2015-10-27 00:25, Tofu Ninja wrote:
 [...]
I don't think this is possible. Think of code looking like this: // Imagine not having access to the source code "createA" A createA() { new B; } void inspectA(A a) { a.bar(); } How should the compiler know that when instantiating/calling "bar", T should be set to B? The compiler might not even know about B exists at all. Even if the compiler does have access to the complete source code it would, most likely, need to do a full program analyze to figure out the type of T, which is quite complicated.
It's not a template function. The auto override wouldn't be instantiated if/when it's called. It would always be instantiated automatically when the class was compiled. Then, if you were just using the class as an import, the compiler would know that the definition should exists somewhere(static lib, etc..). When an auto override function was called, it may or may not have the definition..it wouldn't matter, since it's like a virtual function. But, if someone tried to subclass a class which had even one auto override function with missing it's definition, then the effect would be the same as trying to subclass a sealed/final class. Bit
Going a bit further, I think you could override an auto override fun too manually as well. This would be helpful both for adding additional functionality, and as a fallback if the definition of the auto override function was not available. If someone sub classed a class in which they had manually overridden an auto override function, the compiler would fall back to using the base method, if available. Finally(pun intended), a user could manually override an auto override function and mark it as final to stop the automatic overriding. Bit
Oct 27 2015
parent reply Tofu Ninja <emmons0 purdue.edu> writes:
On Tuesday, 27 October 2015 at 14:47:03 UTC, bitwise wrote:
 [...]
 Going a bit further, I think you could override an auto 
 override fun too manually as well. This would be helpful both 
 for adding additional functionality, and as a fallback if the 
 definition of the auto override function was not available. If 
 someone sub classed a class in which they had manually 
 overridden an auto override function, the compiler would fall 
 back to using the base method, if available. Finally(pun 
 intended), a user could manually override an auto override 
 function and mark it as final to stop the automatic overriding.

    Bit
Is there ever a chance the auto override definition would not be available, I think the class chain all the way up to Object always needs to be available at compile time to compile a class, so I don't see any time an auto override definition would be missing. I do like the idea of manually overriding as well if needed, but with slightly different semantics than what you said. -Manually overriding an auto override with a regular method would simply work and would stop the automatic overriding from that class on. -Overriding an auto override with another auto override would cause the new auto override to be used from that class on. -Final could still be used to stop all overriding from that class on. You could even override a normal method with an auto override and have no problems.
Oct 27 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 27 October 2015 at 20:05:17 UTC, Tofu Ninja wrote:
 On Tuesday, 27 October 2015 at 14:47:03 UTC, bitwise wrote:
 [...]
Is there ever a chance the auto override definition would not be available, I think the class chain all the way up to Object always needs to be available at compile time to compile a class, so I don't see any time an auto override definition would be missing. I do like the idea of manually overriding as well if needed, but with slightly different semantics than what you said. -Manually overriding an auto override with a regular method would simply work and would stop the automatic overriding from that class on. -Overriding an auto override with another auto override would cause the new auto override to be used from that class on. -Final could still be used to stop all overriding from that class on. You could even override a normal method with an auto override and have no problems.
I just did a test for this. Using dmd's -H flag to generate header files, the bodies of member functions are removed. If you link a static library containing the compiled class though, everything will work, and you can still inherit the class. [test.d] module test; import std.stdio; class Test { void foo() { writeln("test"); } } [/test.d] [main.d] module main; import test; import std.stdio; class Derived : Test { override void foo() { writeln("derived"); } } void main() { Derived d = new Derived(); d.foo(); // prints "derived" } [/main.d] With the above two files, I did this: dmd test.d -lib -H rm test.d #make sure dmd can't find body of foo() dmd main.d test.a ./main The above will print "derived". I think dmd -H should definitely keep the definitions of auto override functions when generating headers, but in theory, someone could remove them, and things could still work. If someone received a static library and a D header file with missing auto override function, they wouldn't be able to inherit from the class, as if it were a sealed/final class. This person, however, could work around the problem by overriding the missing auto override function. I suppose this is a good reason to adopt the semantics you are recommending, where overriding an auto override function stops the automatic behaviour, or else someone would have to override missing auto override functions for every single level of inheritance after the missing auto override. I suppose that if someone wanted to resume the auto overrides, they could just override with another auto override, and call the base class implementation from their new override. With regards to the resuming of auto overriding though, I'm wondering if what I am proposing is possible in D. In C++, you can call any base method from anywhere in a class hierarchy. In D however, it doesn't seem to work. [C++ ] struct A { virtual void foo() { printf("A\n"); } }; struct B : public A { void foo() { printf("B\n"); } }; struct C : public B { void foo() { A::foo(); } }; int main() { C c; c.foo(); // prints "A" } [/C++] In D however, the following will cause a compiler error: "Error: identifier expected following '.', not 'super'" [D] class A { void foo() { writeln("A"); } } class B : A { override void foo() { writeln("B"); } } class C : B { override void foo() { super.super.foo(); } } void main() { C c = new C(); c.foo(); } [/D] Bit
Oct 27 2015
parent reply Tofu Ninja <emmons0 purdue.edu> writes:
On Tuesday, 27 October 2015 at 22:23:15 UTC, bitwise wrote:
 [...]
 I just did a test for this. Using dmd's -H flag to generate 
 header files, the bodies of member functions are removed. If 
 you link a static library containing the compiled class though, 
 everything will work, and you can still inherit the class.

 [test.d]
 module test;
 import std.stdio;
 class Test { void foo() { writeln("test"); } }
 [/test.d]

 [main.d]
 module main;
 import test;
 import std.stdio;
 class Derived : Test {
     override void foo() { writeln("derived"); }
 }
 void main() {
     Derived d = new Derived();
     d.foo(); // prints "derived"
 }
 [/main.d]

 With the above two files, I did this:

 dmd test.d -lib -H
 rm test.d   #make sure dmd can't find body of foo()
 dmd main.d test.a
 ./main

 The above will print "derived".

 I think dmd -H should definitely keep the definitions of auto 
 override functions when generating headers, but in theory,
-H also keeps the body of template functions so I would assume it would treat auto overrides the same.
 someone could remove them, and things could still work. If 
 someone received a static library and a D header file with 
 missing auto override function, they wouldn't be able to 
 inherit from the class, as if it were a sealed/final class. 
 This person, however, could work around the problem by 
 overriding the missing auto override function. I suppose this 
 is a good reason to adopt the semantics you are recommending, 
 where overriding an auto override function stops the automatic 
 behaviour, or else someone would have to override missing auto 
 override functions for every single level of inheritance after 
 the missing auto override. I suppose that if someone wanted to 
 resume the auto overrides, they could just override with 
 another auto override, and call the base class implementation 
 from their new override. With regards to the resuming of auto 
 overriding though, I'm wondering if what I am proposing is 
 possible in D. In C++, you can call any base method from 
 anywhere in a class hierarchy. In D however, it doesn't seem to 
 work.
I think you are correct, there is a scenario where one might want to provide a header to a class and not include the auto override body. An alternative solution could be that if you provide a header to a class and don't include the auto override body, then the auto override functionality is removed and the method is treated as a regular method from that point on(with the most recent version of the method being the one that is used). This would allow the class to still be inherited later on.
 [C++ ]
 struct A { virtual void foo() { printf("A\n"); } };
 struct B : public A { void foo() { printf("B\n"); } };
 struct C : public B { void foo() { A::foo(); } };
 int main() {
 	C c;
 	c.foo();   // prints "A"
 }
 [/C++]

 In D however, the following will cause a compiler error:
 "Error: identifier expected following '.', not 'super'"

 [D]
 class A { void foo() { writeln("A"); } }
 class B : A { override void foo() { writeln("B"); } }
 class C : B { override void foo() { super.super.foo(); } }
 void main() {
     C c = new C();
     c.foo();
 }
 [/D]

     Bit
A.foo() or B.foo() seems to work instead of super.super.foo() or super.foo()
Oct 27 2015
next sibling parent Idan Arye <GenericNPC gmail.com> writes:
On Tuesday, 27 October 2015 at 23:15:39 UTC, Tofu Ninja wrote:
 An alternative solution could be that if you provide a header 
 to a class and don't include the auto override body, then the 
 auto override functionality is removed and the method is 
 treated as a regular method from that point on(with the most 
 recent version of the method being the one that is used). This 
 would allow the class to still be inherited later on.
I think it's a bad idea. The usage or lack of usage of header files should not change the code behavior.
Oct 28 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 27 October 2015 at 23:15:39 UTC, Tofu Ninja wrote:
 On Tuesday, 27 October 2015 at 22:23:15 UTC, bitwise wrote:
 [...]
-H also keeps the body of template functions so I would assume it would treat auto overrides the same.
Well.. It's not going to just work. Dmd will surely have to be told what to do.
 [...]
I think you are correct, there is a scenario where one might want to provide a header to a class and not include the auto override body. An alternative solution could be that if you provide a header to a class and don't include the auto override body, then the auto override functionality is removed and the method is treated as a regular method from that point on(with the most recent version of the method being the one that is used). This would allow the class to still be inherited later on.
I have to disagree with this. A function's meaning should not change because it's definition is removed.
     [...]
A.foo() or B.foo() seems to work instead of super.super.foo() or super.foo()
Ah, ok. I'll take your word for it(on my phone). I'm wondering if it would be worth it to make a DIP for this. We could sort out the details and discuss the use and ab-use cases ;) I myself would be a bit reluctant to start on the DIP right this second though, as I don't currently have the knowledge/time to back it with a proof of concept implementation. Bit
Oct 28 2015
parent Tofu Ninja <emmons0 purdue.edu> writes:
On Wednesday, 28 October 2015 at 15:13:40 UTC, bitwise wrote:
 [...]
 I have to disagree with this. A function's meaning should not 
 change because it's definition is removed.
I suppose you are right.
     [...]
A.foo() or B.foo() seems to work instead of super.super.foo() or super.foo()
Ah, ok. I'll take your word for it(on my phone). I'm wondering if it would be worth it to make a DIP for this. We could sort out the details and discuss the use and ab-use cases ;) I myself would be a bit reluctant to start on the DIP right this second though, as I don't currently have the knowledge/time to back it with a proof of concept implementation. Bit
Yeah I was thinking about making a dip, but lots of dips get made. Without a proof of concept implementation, most don't even get considered. I have tried to look at dmd's source but have really not been able to grok it. I sort of wish there was an article written by Walter or some one that was a intro break down of dmd internals, maybe with some examples of adding features.
Oct 28 2015
prev sibling next sibling parent MrSmith <mrsmith33 yandex.ru> writes:
On Monday, 26 October 2015 at 23:25:49 UTC, Tofu Ninja wrote:
 I know this has basically no chance of ever actually being 
 added because that is the way of all "I want feature X" 
 threads, but I thought I would post this anyways because I seem 
 to want it constantly.
Hey, that is what I needed yesterday!
Oct 27 2015
prev sibling next sibling parent reply Shammah Chancellor <s s.com> writes:
On Monday, 26 October 2015 at 23:25:49 UTC, Tofu Ninja wrote:

 So we have TemplateThisParameters methods which are cool but 
 have some drawbacks.
 They are templates so they are implicitly non-virtual and are 
 called based on the type of the reference.
 It would be very nice to be able to auto generate method 
 overrides based on the this type with out having to drop a 
 mixin in every sub class.
 This would be very useful for CT-reflection.
 I will call this new feature auto override methods for the rest 
 of this post.
This kind of magic is detrimental to being able to reason about the code you are writing. If libraries do this for example, there's too much spooky action-at-a-distance occurring. Having explicit mixins is the appropriate way to handle this.
Oct 28 2015
next sibling parent Tofu Ninja <emmons0 purdue.edu> writes:
On Wednesday, 28 October 2015 at 20:04:44 UTC, Shammah Chancellor 
wrote:
 This kind of magic is detrimental to being able to reason about 
 the code you are writing.  If libraries do this for example, 
 there's too much spooky action-at-a-distance occurring.  Having 
 explicit mixins is the appropriate way to handle this.
Care to give an example?
Oct 28 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Wednesday, 28 October 2015 at 20:04:44 UTC, Shammah Chancellor 
wrote:
 On Monday, 26 October 2015 at 23:25:49 UTC, Tofu Ninja wrote:

 So we have TemplateThisParameters methods which are cool but 
 have some drawbacks.
 They are templates so they are implicitly non-virtual and are 
 called based on the type of the reference.
 It would be very nice to be able to auto generate method 
 overrides based on the this type with out having to drop a 
 mixin in every sub class.
 This would be very useful for CT-reflection.
 I will call this new feature auto override methods for the 
 rest of this post.
This kind of magic is detrimental to being able to reason about the code you are writing. If libraries do this for example, there's too much spooky action-at-a-distance occurring. Having explicit mixins is the appropriate way to handle this.
There is nothing spooky going on here. The declaration would be clearly annotated with auto override, or whatever keyword was chosen. If you were inheriting from a class, and didn't like its auto overrides, you could manually override the functions in your own class to stop it. There are several discussion on these boards about using druntime's RTInfo(T) achieve similar goals to what(I think) we're trying to do here. This feature we're talking about nicely dodges the issues presented by RTInfo(T), because it's effects are localized to a single class hierarchy, instead of trying to affect all classes universally. I'm thinking there may be a better solution somewhere in between though. Having the compiler turn something with template parameters into a virtual function is a bit odd, and I don't think it will be obvious to everyone. I think the following would suit my needs, and may be a better solution: class Base { static this(this This)() { } } The compiler would instantiate that static constructor for every sub class. From there, any other relevant templates could be instantiated, and at runtime, this static ctor could stash any relevant information in a collection under the key 'typeid(This)'. Finally, 'Base' could implement a function that looks up the class's info by typeid(this). I believe this solution is a great deal simpler, and less invasive. Bit
Oct 28 2015
parent reply Tofu Ninja <emmons0 purdue.edu> writes:
On Wednesday, 28 October 2015 at 21:48:36 UTC, bitwise wrote:
 There is nothing spooky going on here. The declaration would be 
 clearly annotated with auto override, or whatever keyword was 
 chosen. If you were inheriting from a class, and didn't like 
 its auto overrides, you could manually override the functions 
 in your own class to stop it.

 There are several discussion on these boards about using 
 druntime's RTInfo(T) achieve similar goals to what(I think) 
 we're trying to do here. This feature we're talking about 
 nicely dodges the issues presented by RTInfo(T), because it's 
 effects are localized to a single class hierarchy, instead of 
 trying to affect all classes universally. I'm thinking there 
 may be a better solution somewhere in between though.

 Having the compiler turn something with template parameters 
 into a virtual function is a bit odd, and I don't think it will 
 be obvious to everyone. I think the following would suit my 
 needs, and may be a better solution:

 class Base {
     static this(this This)() { }
 }

 The compiler would instantiate that static constructor for 
 every sub class. From there, any other relevant templates could 
 be instantiated, and at runtime, this static ctor could stash 
 any relevant information in a collection under the key 
 'typeid(This)'. Finally, 'Base' could implement a function that 
 looks up the class's info by typeid(this).

 I believe this solution is a great deal simpler, and less 
 invasive.

     Bit
I could not think of a way to implement the auto override functionality with your proposal. But while thinking about it came up with another way that is similar and very close to being doable now. class Base { this(this T)(){ writeln(T.stringof); } } class Child : Base {} That fails to compile, but the error is in Child for not calling the parent constructor. Change it to: class Base { this(this T)(){ writeln(T.stringof); } } class Child : Base { this(){ super(); } } And it works and the Base this(this T) gets the right T depending on if you do new Base or new Child. With that you could easily implement the auto override functionality with a single function pointer in the base class. The only thing to make it perfect would be to have the compiler properly recognize this(this T)(){} and instantiate it for the Child class. I think this plus your static version could do a lot and be pretty useful while still being pretty simple.
Oct 28 2015
parent reply Tofu Ninja <emmons0 purdue.edu> writes:
On Thursday, 29 October 2015 at 00:11:06 UTC, Tofu Ninja wrote:
 [...]
Actually never mind, what I just said was basically auto override for this() so its not really any different. And it is kinda limited with some problems.
Oct 28 2015
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 29 October 2015 at 01:14:35 UTC, Tofu Ninja wrote:
 On Thursday, 29 October 2015 at 00:11:06 UTC, Tofu Ninja wrote:
 [...]
Actually never mind, what I just said was basically auto override for this() so its not really any different. And it is kinda limited with some problems.
My argument though, is that a virtual function isn't needed. I'm not sure if this is sufficient for your use cases, but if I could do the this, I would have everything I need: struct MyInfo { string name; } static MyInfo[string] stuff; class Base { static this(this This)() { stuff[fullyQualifiedName!This] = MyInfo(This.stringof); } } class Derived : Base { // static this called for Derived as well } void main(string[] args) { writeln(stuff["main.Base"].name); writeln(stuff["main.Derived"].name); } I think this would be a lot easier to understand than the virtual function approach. There would be no need to explain the complicated semantics of what happens when the definition is missing(can't inherit from the class) or how manually overriding these auto override functions stops the overriding, etc... template static this would simply be a static constructor that gets called for all subclasses, and nothing more. When I thought about the virtual function approach, my intention was to have the virtual function return information about the subclass for the purpose of serialization or inspection. To be honest, I can't think of any other functionality I would want to specialize in this way. Bit
Oct 28 2015
parent reply Daniel N <ufo orbiting.us> writes:
On Thursday, 29 October 2015 at 01:52:16 UTC, bitwise wrote:
 My argument though, is that a virtual function isn't needed. 
 I'm not sure if this is sufficient for your use cases, but if I 
 could do the this, I would have everything I need:

 struct MyInfo { string name; }
 static MyInfo[string] stuff;

 class Base {
     static this(this This)() {
         stuff[fullyQualifiedName!This] = MyInfo(This.stringof);
     }
 }

 class Derived : Base {
     // static this called for Derived as well
  }

 void main(string[] args)
 {
     writeln(stuff["main.Base"].name);
     writeln(stuff["main.Derived"].name);
 }
There's a famous idiom from the C++ world which solves this, google CRTP... ... or see the quick hack below. ('final' is optional, the interface is also optional...) interface IBase { string Name(); } class Base(T) : IBase { final string Name() { return typeof(this).stringof; } } class One : Base!One { } class Two : Base!Two { }
Oct 29 2015
next sibling parent Daniel N <ufo orbiting.us> writes:
On Thursday, 29 October 2015 at 08:27:04 UTC, Daniel N wrote:
 class Base(T) : IBase
 {
    final string Name()
    {
        return typeof(this).stringof;
    }
 }

 class One : Base!One { }
 class Two : Base!Two { }
Meh, sorry, ofcourse it should be: return typeof(cast(T)this).stringof;
Oct 29 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 29 October 2015 at 08:27:04 UTC, Daniel N wrote:
 On Thursday, 29 October 2015 at 01:52:16 UTC, bitwise wrote:
 [...]
There's a famous idiom from the C++ world which solves this, google CRTP... ... or see the quick hack below. ('final' is optional, the interface is also optional...) interface IBase { string Name(); } class Base(T) : IBase { final string Name() { return typeof(this).stringof; } } class One : Base!One { } class Two : Base!Two { }
Doesn't work with multiple levels of inheritance... Bit
Oct 29 2015
parent reply Daniel N <ufo orbiting.us> writes:
On Thursday, 29 October 2015 at 13:54:17 UTC, bitwise wrote:
 class One : Base!One { }
 class Two : Base!Two { }
Doesn't work with multiple levels of inheritance... Bit
Just sprinkle some D magic ontop.. voilà DRTP is born ;) template Dynamic(T) { static if(is(T == U!V, alias U, V)) alias Dynamic = Dynamic!V; else alias Dynamic = T; } interface IBase { string Name(); } class Base(T) : IBase { final string Name() { auto self = cast(Dynamic!T)this; return self.me; // Dynamic!T.stringof; } } class One(T...) : Base!One { string me = "1";} class Two(T...) : Base!Two { string me = "2";} class Six(T...) : One!Six { string me = "6";} class Ten(T...) : Six!Ten { string me = "a";} void main() { import std.stdio; IBase one = new One!(); IBase two = new Two!(); IBase six = new Six!(); IBase ten = new Ten!(); one.Name.writeln; two.Name.writeln; six.Name.writeln; ten.Name.writeln; }
Oct 29 2015
next sibling parent bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 29 October 2015 at 17:32:22 UTC, Daniel N wrote:
 On Thursday, 29 October 2015 at 13:54:17 UTC, bitwise wrote:
     [...]
Just sprinkle some D magic ontop.. voilà DRTP is born ;) template Dynamic(T) { static if(is(T == U!V, alias U, V)) alias Dynamic = Dynamic!V; else alias Dynamic = T; } [...]
No.
Oct 29 2015
prev sibling parent Tofu Ninja <emmons0 purdue.edu> writes:
On Thursday, 29 October 2015 at 17:32:22 UTC, Daniel N wrote:
 [...]
 class Base(T) : IBase
 [...]
I actually did this before, I can confidently say how damn ugly and all over bad it is. Please no.
Oct 29 2015
prev sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
On Thursday, 29 October 2015 at 01:14:35 UTC, Tofu Ninja wrote:
 On Thursday, 29 October 2015 at 00:11:06 UTC, Tofu Ninja wrote:
 [...]
Actually never mind, what I just said was basically auto override for this() so its not really any different. And it is kinda limited with some problems.
Sorry, I'm still not clear on whether or not template static this would work for you. Personally, I don't need dynamic dispatch or per-instance access. For me, as long as class info is generated per class and stored statically, that would suffice. I could always have a base method look up what it needs at runtime by type name or something. An example would be generating a serializer for a class in template static this to be used at runtime. Bit
Oct 30 2015
next sibling parent reply Tofu Ninja <emmons0 purdue.edu> writes:
On Friday, 30 October 2015 at 21:38:40 UTC, bitwise wrote:
 On Thursday, 29 October 2015 at 01:14:35 UTC, Tofu Ninja wrote:
 On Thursday, 29 October 2015 at 00:11:06 UTC, Tofu Ninja wrote:
 [...]
Actually never mind, what I just said was basically auto override for this() so its not really any different. And it is kinda limited with some problems.
Sorry, I'm still not clear on whether or not template static this would work for you. Personally, I don't need dynamic dispatch or per-instance access. For me, as long as class info is generated per class and stored statically, that would suffice. I could always have a base method look up what it needs at runtime by type name or something. An example would be generating a serializer for a class in template static this to be used at runtime. Bit
I could make it work the same way you did, but would rather a solution that did not involve a AA lookup.
Oct 30 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Saturday, 31 October 2015 at 02:39:19 UTC, Tofu Ninja wrote:
 On Friday, 30 October 2015 at 21:38:40 UTC, bitwise wrote:
 On Thursday, 29 October 2015 at 01:14:35 UTC, Tofu Ninja wrote:
 On Thursday, 29 October 2015 at 00:11:06 UTC, Tofu Ninja 
 wrote:
 [...]
Actually never mind, what I just said was basically auto override for this() so its not really any different. And it is kinda limited with some problems.
Sorry, I'm still not clear on whether or not template static this would work for you. Personally, I don't need dynamic dispatch or per-instance access. For me, as long as class info is generated per class and stored statically, that would suffice. I could always have a base method look up what it needs at runtime by type name or something. An example would be generating a serializer for a class in template static this to be used at runtime. Bit
I could make it work the same way you did, but would rather a solution that did not involve a AA lookup.
You're probably right. My main use case would be serialization which is inherently slow anyways, so I figured it wouldn't matter. I'm trying to think of how this proposal can be kept as simple as possible to maximize the chances of it being accepted, and I couldn't think of enough use cases to add value to the virtual function approach. The more I think about it though, the more uses I come up with: -automatic generation of toString() -returning class specific type/serialization info -automatic implementation of visitors!! I don't think I have to explain the value of being able to do this: void accept(auto this This)(Visitor v) { v.visit(this); } So I guess I'm back on the side of "auto override" functions. I would prefer a better syntax though. Maybe just "auto" like the example above instead of "auto override".... or maybe " synthesized" in front of the function like this: synthesized void accept(this This)(Visitor v) { v.visit(this); } Bit
Oct 31 2015
parent reply bitwise <bitwise.pvt gmail.com> writes:
On Saturday, 31 October 2015 at 16:38:58 UTC, bitwise wrote:
  synthesized void accept(this This)(Visitor v) {
     v.visit(this);
 }
I meant to remove "(this This)" as you don't need it if you can just do "typeof(this)" in the function body. synthesized void accept(Visitor v) { v.visit(this); } Bit
Oct 31 2015
parent Tofu Ninja <emmons0 purdue.edu> writes:
On Saturday, 31 October 2015 at 16:55:32 UTC, bitwise wrote:
 On Saturday, 31 October 2015 at 16:38:58 UTC, bitwise wrote:
  synthesized void accept(this This)(Visitor v) {
     v.visit(this);
 }
I meant to remove "(this This)" as you don't need it if you can just do "typeof(this)" in the function body. synthesized void accept(Visitor v) { v.visit(this); } Bit
I don't particularly care what the syntax would be. I just choose auto override because it seems to match the concept pretty well and we already have those key words.
Oct 31 2015
prev sibling parent Daniel N <ufo orbiting.us> writes:
On Friday, 30 October 2015 at 21:38:40 UTC, bitwise wrote:
 On Thursday, 29 October 2015 at 01:14:35 UTC, Tofu Ninja wrote:
 On Thursday, 29 October 2015 at 00:11:06 UTC, Tofu Ninja wrote:
 [...]
Actually never mind, what I just said was basically auto override for this() so its not really any different. And it is kinda limited with some problems.
It's not without issues, but changing it to 'this(this T = void)()' makes it work(for one level of inheritance). class Base { string str; string function() fun; this(this T = void)() { str = T.stringof; fun = function() { return T.stringof;}; } } class Child : Base {}
Oct 31 2015
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 10/27/2015 12:25 AM, Tofu Ninja wrote:
 Thoughts?
 Any one else needed this before?
Yes, similar proposals have come up multiple times. It is better if it is more general though. The super class should be able to mix arbitrary declarations into the subclass scope.
Oct 28 2015