digitalmars.D - Inheritance of things that are note instance methods
- Arafel (104/104) Aug 03 2017 Hi!
- Adam D. Ruppe (16/25) Aug 03 2017 virtual functions can be overridden. All others work as functions
- Arafel (25/37) Aug 03 2017 Well, here I agree, that's why I was suggesting the "templated this"
- Timon Gehr (2/4) Aug 03 2017 https://issues.dlang.org/show_bug.cgi?id=17713
Hi! TL;DR: Inheritance seems to be a mess out of instance methods. Some things work, some other don't or not as expected, and apparently it isn't documented either [1]. Does somebody know how it's even *supposed* to work? Making it a bit more intuitive would also be nice :-) Now, the long rant: ---- After a couple of threads in learn [2,3] related to how inheritance works in D out of the "usual" case of instance methods, I haven't been able to find in the spec [1] how things are supposed to be, and indeed I find the situation quite messy: * Static functions are "virtual", i.e. there is a lookup of parent classes if it's not found. This is not documented, and I didn't even think it would work: ``` class A { static void foo() { }; } class B : A { } void main() { B.foo(); // This works, surprisingly } ``` * However, there's no way for B.foo() to get its own class: ``` class A { static void foo() { import std.stdio; writeln(typeid(typeof(this))); } } class B : A { } void main() { B.foo(); // This prints "A", I'd like to print "B" } ``` This is a bit shocking, since the compiler must know for sure which class I'm from, and I can also make it explicit: ``` class A { static void foo(C)() { import std.stdio; writeln(typeid(C)); } } class B : A { } alias foo(C : A) = C.foo!C; void main() { foo!B(); // This does indeed print "B" } ``` I'd argue that since it's *already possible* (just inconvenient, unintuitive, and convoluted), "this" template parameters should be available everywhere, thus making something like this work out of the box: ``` class A { static void foo(this C)() { import std.stdio; writeln(typeid(typeof(this))); writeln(typeid(C)); } } class B : A { } void main() { B.foo(); // This should print "A" and then "B" } ``` I think this is how most people would expect it to work, *given that B.foo() already works*. The other option would be to totally disable looking up static functions of parent classes. * Finally, things get even more confusing with templates: a missing template will be looked up in "parent" classes, but a template that exists but is not instantiable won't: ``` class A { template foo(string s) if (s == "bar") { enum foo = 1; } enum bar = 1; } class B : A { template foo(string s) if (s == "baz") { enum foo = 2; } enum baz = 2; } void main() { import std.stdio; writeln(B.bar); // This works: bar is taken from A writeln(B.baz); // This also works, baz is directly in B //writeln(B.foo!"bar"); // This doesn't work: why? writeln(B.foo!"baz"); } ``` So all in all, I think it'd be nice to have some clear spec on what is supposed to work or not, and I also think that with just a couple of small changes things might greatly improve. A. [1]: https://dlang.org/spec/class.html [2]: https://forum.dlang.org/post/olng6h$1e1s$1 digitalmars.com [3]: https://forum.dlang.org/thread/axpqehyrvunzcjnrdccn forum.dlang.org
Aug 03 2017
On Thursday, 3 August 2017 at 12:06:02 UTC, Arafel wrote:Does somebody know how it's even *supposed* to work?virtual functions can be overridden. All others work as functions across shared namespaces. Overloads across namespaces need to be explicitly added to the overload set, see: http://dlang.org/hijack.html* Static functions are "virtual", i.e. there is a lookup of parent classes if it's not found. This is not documented, and I didn't even think it would work:It isn't virtual, more like an import. Same as member variables. idk where this is in the spec though...This is a bit shocking, since the compiler must know for sure which class I'm from, and I can also make it explicit:There's no place in the binary for it to pass it that information. With a non-static member, `this` is passed as a hidden function argument. With static, there explicitly are no hidden function arguments (it is compatible with outside function pointers). The template can do it since it creates several copies... and as such is NOT compatible with function pointers. The function pointer compatibility is fairly important.* Finally, things get even more confusing with templates: a missing template will be looked up in "parent" classes, but a template that exists but is not instantiable won't:This has to do with the overload rules, see the hijack article linked above.
Aug 03 2017
On 08/03/2017 02:28 PM, Adam D. Ruppe wrote:There's no place in the binary for it to pass it that information. With a non-static member, `this` is passed as a hidden function argument. With static, there explicitly are no hidden function arguments (it is compatible with outside function pointers). The template can do it since it creates several copies... and as such is NOT compatible with function pointers. The function pointer compatibility is fairly important.Well, here I agree, that's why I was suggesting the "templated this" approach. I think it'd fit nicely. It would be a template and thus create as many copies as needed, meaning that if you have: ยดยดยด class A { public: static C getInstance(this C)() { // Do some stuff here return new C(); } private: this() { } } class B : A { } ``` &A.foo and &B.foo would point (as reasonable and expected, on the other hand) to different functions, and they would of course return different types. It would also follow the principle of least astonishment.This has to do with the overload rules, see the hijack article linked above.Well, the article only talked about functions and such, I didn't see any reference to how it would apply to templates. Still, fair enough, it looks reasonable, since one can always explicitly dispatch to the parent class if needed... a bit cumbersome, but doable. Thanks for the explanation! A.
Aug 03 2017
On 03.08.2017 15:02, Arafel wrote:Well, here I agree, that's why I was suggesting the "templated this" approach. I think it'd fit nicely.https://issues.dlang.org/show_bug.cgi?id=17713
Aug 03 2017