digitalmars.D.learn - hiding a class property behind a method
- luka8088 (20/20) Feb 22 2014 It seems to me that the following code should be illegal, but I am
- simendsjo (3/19) Feb 22 2014 Just an addition. The following line shows the problem:
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (8/27) Feb 22 2014 The code uses the two objects through the A interface and x() is a
- Maxim Fomin (33/39) Feb 22 2014 Spec is silent on this, so this is indeed a question.
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (15/36) Feb 22 2014 I did not mean D's feature 'interface'. The code explicitly specifies
- simendsjo (28/31) Feb 22 2014 The problem is that hiding a name *is* a problem. When you are hiding a
- Nynn007 (11/14) Feb 22 2014 The book "Programming in D" (r651) says in chapter "57.7 Using
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (7/20) Feb 22 2014 I obviously agree with that as well. :)
- Francesco Cattoglio (6/11) Feb 22 2014 [snip]
- simendsjo (11/19) Feb 22 2014 The problem isn't about optional parenthesis or properties. It's the
- francesco cattoglio (4/11) Feb 22 2014 You are right. I thought that if we had forced parenthesis, the
- luka8088 (19/25) Feb 23 2014 That is exactly what I wanted to point out! I reduced the example code
- luka8088 (4/6) Feb 23 2014 Just one note. I know how this works currently in D and how it works in
It seems to me that the following code should be illegal, but I am uncertain of it so I am posting here for a confirmation before I post it on bug tracker: http://dpaste.dzfl.pl/dae728734cc6 import std.stdio; class A { string x () { return "A"; }; } class B : A { override string x () { return "B"; }; } class C : A { string x = "C"; // should this be illegal? } void main () { A o1 = new B(); writeln(o1.x); // B A o2 = new C(); writeln(o2.x); // A }
Feb 22 2014
On 02/22/2014 06:21 PM, luka8088 wrote:import std.stdio; class A { string x () { return "A"; }; } class B : A { override string x () { return "B"; }; } class C : A { string x = "C"; // should this be illegal? } void main () { A o1 = new B(); writeln(o1.x); // B A o2 = new C(); writeln(o2.x); // A }Just an addition. The following line shows the problem: writeln((cast(C)o2).x); // C
Feb 22 2014
On 02/22/2014 09:21 AM, luka8088 wrote:> It seems to me that the following code should be illegal, but I amuncertain of it so I am posting here for a confirmation before I post it on bug tracker: http://dpaste.dzfl.pl/dae728734cc6 import std.stdio; class A { string x () { return "A"; }; } class B : A { override string x () { return "B"; }; } class C : A { string x = "C"; // should this be illegal? } void main () { A o1 = new B(); writeln(o1.x); // B A o2 = new C(); writeln(o2.x); // A }The code uses the two objects through the A interface and x() is a virtual function on that interface. When the C interface is used then we get C.x, which happens to be hiding the x() function of the base class. It looks normal to me. Ali
Feb 22 2014
On Saturday, 22 February 2014 at 17:41:58 UTC, Ali Çehreli wrote:The code uses the two objects through the A interface and x() is a virtual function on that interface. When the C interface is used then we get C.x, which happens to be hiding the x() function of the base class. It looks normal to me. AliSpec is silent on this, so this is indeed a question. Actually A is not interface, so I don't understand why you mention it. And there is neither 'taking C interface' because static type is A, so A function is called, neither it hides function of the base class because it is base class function which is called. I don't understand you completely. AFAIK this feature exists for many years, at least 3, possibly roots to D1. What happens is follows: since there is no function, base class virtual is not replaced, so virtual table of C looks like A, so A member function is called. If example is modified, then import std.stdio; class A { //string x () { return "A"; }; string x = "A"; } class B : A { //override string x () { return "B"; }; string x = "B"; } class C : A { //string x = "C"; // should this be illegal? string x () { return "C"; } } void main () { A o1 = new B(); A o2 = new C(); writeln(o1.x); // A writeln(o2.x); //A } so it appears that data member have priority over function. Probably this should be filed as a spec or compiler bug.
Feb 22 2014
On 02/22/2014 10:00 AM, Maxim Fomin wrote:On Saturday, 22 February 2014 at 17:41:58 UTC, Ali Çehreli wrote:Sorry. I meant "If the C interface is used", not "When the".The code uses the two objects through the A interface and x() is a virtual function on that interface. When the C interface is used then we get C.x, which happens to be hiding the x() function of the base class.I did not mean D's feature 'interface'. The code explicitly specifies the objects as As, comitting to A's class interface. (As in, every used defined type defines an interface.)It looks normal to me. AliSpec is silent on this, so this is indeed a question. Actually A is not interface, so I don't understand why you mention it.And there is neither 'taking C interface' because static type is A, so A function is called, neither it hides function of the base class because it is base class function which is called. I don't understand you completely.I agree with all of that.since there is no function, base class virtual is not replaced, so virtual table of C looks like A, so A member function is called.Exactly. Otherwise, when faced with such a situation the compiler would have to synthesize a virtual function for C's virtual table. string x() { return member_x; }so it appears that data member have priority over function.It looks like name hiding, which I am familiar from C++. Name hiding does not differentiate between functions and variables. Ali
Feb 22 2014
On 02/22/2014 09:43 PM, Ali Çehreli wrote:It looks like name hiding, which I am familiar from C++. Name hiding does not differentiate between functions and variables. AliThe problem is that hiding a name *is* a problem. When you are hiding a name, then a class would no longer behave as you would expect. It breaks LSP in a pretty awful way, and suddenly the *type* of a symbol changes based on what superclass you happened to use for a reference. class A { void f() {} } class B : A { int f; } A b = new B(); writeln(typeof(b.f).stringof); // void() B veryB = cast(B)b; writeln(typeof(veryB.f).stringof); // int Now suddenly, it's very important to use B as the type for a reference. This is very dangerous behavior in my opinion, and I think I've only used it *once* in Now what if a superclass implements a symbol that you are using in a subclass? I say we force it to be explicit as we finally did with `override`, which is shows some of the same issues, although not nearly as dangerous and hidden. I think member hiding is nearly always a bug, and we should be very explicit about it.
Feb 22 2014
The code uses the two objects through the A interface and x() is a virtual function on that interface.[...]AliThe book "Programming in D" (r651) says in chapter "57.7 Using the subclass in place of the superclass", in the example about Clock and AlarmClock : void use(Clock clock) { ... } (sic) "In other words, although use() uses the object as a Clock, the actual object may be an inherited type that behaves in its own special way". Should'nt we understand that the first object is a B, the second object is a C and then should both behave like a B and a C, not like two A ?
Feb 22 2014
On 02/22/2014 12:06 PM, Nynn007 wrote:I agree. :)The code uses the two objects through the A interface and x() is a virtual function on that interface.[...]AliThe book "Programming in D" (r651) says in chapter "57.7 Using the subclass in place of the superclass", in the example about Clock and AlarmClock : void use(Clock clock) { ... } (sic) "In other words, although use() uses the object as a Clock, the actual object may be an inherited type that behaves in its own special way".I obviously agree with that as well. :)Should'nt we understand that the first object is a B, the second object is a C and then should both behave like a B and a C, not like two A ?You are correct. What I meant above is that the code uses the two object as two As, which involves the "interface" of A. The behaviors may be different. Ali
Feb 22 2014
On Saturday, 22 February 2014 at 17:21:50 UTC, luka8088 wrote:It seems to me that the following code should be illegal, but I am uncertain of it so I am posting here for a confirmation before I post it on bug tracker:[snip] Nice find. I guess we could add this to the list of "ugly code caused by calling functions without parenthesis. If parenthesis were not optional, I don't think that the code would behave in such a horrible way, right?
Feb 22 2014
On 02/22/2014 11:33 PM, Francesco Cattoglio wrote:On Saturday, 22 February 2014 at 17:21:50 UTC, luka8088 wrote:The problem isn't about optional parenthesis or properties. It's the fact that you can redefine a symbol to be something entierly different, and that this difference will only be seen if you are looking at the symbol through the "correct" type. you are hiding an existing symbol. D as a safe-by-default language should also require this.It seems to me that the following code should be illegal, but I am uncertain of it so I am posting here for a confirmation before I post it on bug tracker:[snip] Nice find. I guess we could add this to the list of "ugly code caused by calling functions without parenthesis. If parenthesis were not optional, I don't think that the code would behave in such a horrible way, right?
Feb 22 2014
On Saturday, 22 February 2014 at 22:42:24 UTC, simendsjo wrote:The problem isn't about optional parenthesis or properties. It's the fact that you can redefine a symbol to be something entierly different, and that this difference will only be seen if you are looking at the symbol through the "correct" type.You are right. I thought that if we had forced parenthesis, the compiler would at least be able to understand what symbol you were referring to, but this is actually not the case.
Feb 22 2014
On 22.2.2014. 23:43, simendsjo wrote:On 02/22/2014 11:33 PM, Francesco Cattoglio wrote: The problem isn't about optional parenthesis or properties. It's the fact that you can redefine a symbol to be something entierly different, and that this difference will only be seen if you are looking at the symbol through the "correct" type.That is exactly what I wanted to point out! I reduced the example code to make that point more obvious: import std.stdio; class A { string x = "A"; } class B : A { // should it be legal to hide a symbol and redefine it to a new type? int x = 2; } //static assert (is(typeof(A.x) == typeof(B.x))); //Error: static assert (is(string == int)) is false void main () { B o = new B(); writeln((cast (A)o).x); // A writeln((cast (B)o).x); // 2 } http://dpaste.dzfl.pl/6ae4ac5de1bc
Feb 23 2014
On 23.2.2014. 13:51, luka8088 wrote:That is exactly what I wanted to point out! I reduced the example code to make that point more obvious:Just one note. I know how this works currently in D and how it works in C++. What I am asking here is not how it works but is this a good idea as I just see it as a possible source of bugs without any benefit.
Feb 23 2014