digitalmars.D - Overloading/Inheritance issue
- Steve Schveighoffer (32/32) Aug 01 2007 Hi,
- Derek Parnell (21/22) Aug 01 2007 This is intentional and is due to D's simplistic lookup rules. Basically...
- Steve Schveighoffer (8/11) Aug 01 2007 OK, I sort of saw this in the language specification, but I wasn't sure ...
- Christopher Wright (2/4) Aug 01 2007
- Derek Parnell (15/19) Aug 01 2007 Oh don't get me wrong, I too think that the current rules are totally da...
- Aarti_pl (9/28) Aug 01 2007 ... and it's even not consistent with one of D design rules:
- Walter Bright (3/10) Aug 03 2007 It does behave, in this instance, just like C++. Steve is asking for the...
- Marcin Kuszczak (17/28) Aug 04 2007 Yes you are right. I found that I was wrong just after Regan post with o...
- Regan Heath (45/63) Aug 02 2007 You're not recalling Walter reasoning correctly. :)
- Aarti_pl (7/63) Aug 02 2007 ups... my argument from last post falled down into ruins...
- Derek Parnell (9/13) Aug 02 2007 Thanks Regan. I'd succeded in burning that discussion out of my mind :)
- Regan Heath (30/36) Aug 02 2007 I'm not sure it's as simple as 'better' or worse. I think it's another
- Steve Schveighoffer (5/10) Aug 02 2007 OK, I was totally under the impression that C++ was the way I was saying...
- Regan Heath (50/69) Aug 02 2007 It's truly odd that no-one seems to have a problem with it in C++. It's...
- Frits van Bommel (2/11) Aug 02 2007 C++ has "using Base::method;" to replace alias for this particular case.
- Sean Kelly (8/47) Aug 02 2007 One key difference between D and C++ in this regard is that class
- Bruno Medeiros (55/111) Aug 03 2007 Regan, good thing posting about that other older thread, I didn't know
- Derek Parnell (19/21) Aug 02 2007 LOL ... it is obvious that I don't do much OO programming isn't it.
- Walter Bright (3/6) Aug 03 2007 All on one page:
- Walter Bright (4/5) Aug 03 2007 compile.
- Walter Bright (19/22) Aug 03 2007 Essentially, D's behavior here matches C++'s. Steve is asking for Java
- Bruno Medeiros (22/28) Aug 04 2007 I'm not sure if you've seen my other reply to this argument, but let be
- Walter Bright (7/18) Aug 04 2007 I appreciate that you can write tools to analyze it. I think they help a...
- Jeff Nowakowski (8/11) Aug 05 2007 I think optimizing your language for vi and the printer is a dead-end.
- Walter Bright (4/10) Aug 05 2007 Everyone uses different tools, and particular tools may not be available...
- Sean Kelly (5/17) Aug 05 2007 I use emacs at work. Sun Studio (the obvious alternative) is too slow
- Daniel Keep (7/19) Aug 05 2007 Cool; I'm hard core! Wait... does it still count if I'm using vim with
- Walter Bright (2/4) Aug 07 2007 A gui? I don't know, man :-)
- Derek Parnell (7/8) Aug 06 2007 And I think that some are even still chipping away at granite blocks :)
- Walter Bright (21/25) Aug 07 2007 I'm a command line interface curmudgeon. The reason is simple- it's more...
- Jascha Wetzel (16/44) Aug 07 2007 what you say assumes that GUIs imply mouse usage. i consider a GUI buggy...
- Bruno Medeiros (29/63) Aug 07 2007 You distort the argument somewhat, by mentioning ahead several tasks
- BCS (15/43) Aug 07 2007 If I ever manage an app development team, for the first GUI demonstratio...
- Walter Bright (9/14) Aug 07 2007 Here are some:
- Bruno Medeiros (31/47) Aug 09 2007 Maybe for rename refactoring. Most other refactoring operations need an
- Christopher Wright (15/66) Aug 10 2007 Are you saying that Visual Studio is better at refactoring C++ code than...
- Bruno Medeiros (7/29) Aug 11 2007 I didn't say do it in the CLI, I said do it in the CLI *with
- Ary Manzana (3/37) Aug 07 2007 What about automatic code refactoring?
- Bruno Medeiros (13/33) Aug 05 2007 Auditing, as in someone review someone else's code? Well, yes, in that
- Walter Bright (13/33) Aug 05 2007 Yes. This is done a lot in professional programming environments, and in...
- Derek Parnell (7/9) Aug 06 2007 BTW, I've tried to use the profiler a couple of times now, but the outpu...
- Sean Kelly (5/11) Aug 06 2007 I've used Quantify before and the UI it employs presents the information...
- Walter Bright (3/5) Aug 07 2007 Post a snippet of the output and point out what is unclear, and I'll try...
- Jascha Wetzel (12/14) Aug 07 2007 the sections that come first, before the list of functions sorted by
- Walter Bright (14/31) Aug 07 2007 I renamed the functions for simplicity.
- Jascha Wetzel (2/7) Aug 07 2007 thanks, this is very useful!
- Chris Nicholson-Sauls (5/40) Aug 07 2007 Thank you for explaining this. (And actually, it makes perfect sense on...
- Bill Baxter (17/31) Aug 09 2007 I'm just reposting this with a different subject to give it an easier to...
- Regan Heath (5/11) Aug 07 2007 I smell a potential project here, a gui front-end for dmd profiler
- Jascha Wetzel (4/8) Aug 07 2007 yep, i use -profile regularly but the only portion of the trace.log i
- Ary Manzana (3/23) Aug 05 2007 I think it's the opposite. In every workplace I've been, people can't
- Chris Nicholson-Sauls (39/63) Aug 05 2007 For most things I do just fine without an IDE. For all my work with D,
- BCS (2/18) Aug 05 2007 Unless I'm totally misreading you, that IS how it works.
- Chris Nicholson-Sauls (6/28) Aug 05 2007 Hmm. I just re-tested this after your reply, and yes it does indeed
- BCS (6/27) Aug 05 2007 In code review, you may have a point. In writing code? Now that is one t...
- Bill Baxter (8/28) Aug 05 2007 It might be neat to define an API for the compiler. I mean an api that
- Jascha Wetzel (4/10) Aug 07 2007 FWIW, i'm writing a library like that ATM.
- Charles D Hixson (7/32) Aug 04 2007 Well if you're going to make that kind of a requirement, why
- Chris Nicholson-Sauls (10/51) Aug 01 2007 I've never really 100% understood why this is the way that it is, but
- Kirk McDonald (8/75) Aug 01 2007 That's not a workaround, nor is this considered a problem. This is the
- Chris Nicholson-Sauls (6/79) Aug 01 2007 Intended, yes. Wanted? Not by all. (Which is why it keeps coming up
- Steve Schveighoffer (3/7) Aug 01 2007 It's sort of in the spec, but the example shows the two overrides differ...
- Walter Bright (3/6) Aug 03 2007 Yes, and it took some extra effort to make sure it worked that way! In
- Charles D Hixson (11/18) Aug 05 2007 But perhaps alias isn't the right tool? Someone below
- BCS (5/15) Aug 02 2007 And this suffers from the issue that a function name and a function are ...
- Steve Schveighoffer (7/30) Aug 02 2007 After thinking about it, my opinion is that C++ coders tend to avoid imp...
- Steve Schveighoffer (41/55) Aug 02 2007 Hm.. for some reason, I thought this would appear under the original thr...
- Regan Heath (4/6) Aug 03 2007 The web interface has ... 'issues' on occasion. I use Thunderbird
- Regan Heath (58/77) Aug 03 2007 But, that's exactly the problem.
- Steve Schveighoffer (8/48) Aug 03 2007 Oh, I understand what you are saying. However, undesirable behavior is ...
- Christopher Wright (13/30) Aug 04 2007 Which one saves more work? I think the common case is to want all
- Walter Bright (5/46) Aug 03 2007 I agree that this example is a problem. There's no way to detect it at
- Sean Kelly (5/52) Aug 03 2007 Certain C++ compilers have done something like this in the past, if I
- Walter Bright (5/14) Aug 03 2007 I didn't know that. This particular problem may not come up so often
- Sean Kelly (9/24) Aug 03 2007 I stumbled across the information while trying to diagnose a bizarre
- Bruno Medeiros (7/54) Aug 04 2007 There is a problem the moment the D subclass doesn't override all
- Walter Bright (2/11) Aug 04 2007 I'm not sure that's the right thing to do.
- James Dennett (18/30) Aug 04 2007 In general, overloading virtual functions in the first place
- Walter Bright (4/7) Aug 04 2007 I, for one, have had several severe and very hard to find bugs caused by...
- Bill Baxter (4/12) Aug 07 2007 Is there some way currently to force methods to be non-virtual? Does
- Sean Kelly (4/16) Aug 07 2007 Yes, that's what 'final' is for. In my D code, it's rare that I'll
- Derek Parnell (14/30) Aug 07 2007 I'm about to make a fool of myself again, but in my defence, I'm not a
- Bill Baxter (10/29) Aug 07 2007 So does that mean you can't do non-virtual overloads in D?
-
Walter Bright
(3/12)
Aug 07 2007
Good riddance
. - Steven Schveighoffer (31/39) Aug 08 2007 This is kind of cheating, but it does show a possibility where the lines...
Hi, I am wondering if the following behavior is intentional and if so, why. Given the code below: class X { public int foo() { return foo(0); } public int foo(int y) { return 2; } } class Y : X { public int foo(int y) { return 3; } } int main(char [][] argv) { Y y = new Y; y.foo(); //does not compile, says that the argument type doesn't match y.foo(1); X x = y; x.foo(); return 0; } How come the marked line above does not compile? Clearly there is no ambiguity that I want to call the base's foo, which in turn should call Y's foo(int) with an argument of 0. It's not that the method is not accessible, because I can clearly access it by casting to an X type (as I have done in the subsequent lines). If you interpret the code, I'm defining a default behavior for foo() with no arguments. A derived class which wants to keep the default behavior of foo() as calling foo(0), should only need to override foo(int). However, the compiler does not allow this. Why? Is there a workaround (besides implementing a stub function which calls super.foo())? Maybe there is a different method of defining in a base class one version of a function in terms of another? -Steve
Aug 01 2007
On Wed, 01 Aug 2007 16:47:12 -0400, Steve Schveighoffer wrote:I am wondering if the following behavior is intentional and if so, why.This is intentional and is due to D's simplistic lookup rules. Basically, D will look for a matching signature only in the class it self and not in any parent classes (its a little more complex than this but ...) To 'fix' this situation you need to explicitly identify methods from parent classes that you wish to access from objects of the child class. This is done using an alias statement. Add "alias X.foo foo;" to your class definition of Y. class Y : X { alias X.foo foo; // pulls in class X's foo name into this scope. public int foo(int y) { return 3; } } Now it will compile and run. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Aug 01 2007
Derek Parnell Wrote:This is intentional and is due to D's simplistic lookup rules. Basically, D will look for a matching signature only in the class it self and not in any parent classes (its a little more complex than this but ...)OK, I sort of saw this in the language specification, but I wasn't sure if it covered my case (in fact I tried an alias and it failed to compile, but I now see it was because I had a syntax error in my file. Trying it now works). So my concern here is that the given behavior does not gain anything, but just serves as an annoyance. Here is a scenario which I think could realistically happen: Let's use the same examples of X and Y. Let's say X, along with providing useful implementations of foo() and foo(int), provides another method bar(), which performs some other useful stuff. A coder thinks of some new way to do bar(), and so derives a class Y from X, which then provides the new implementation of bar(), but leaves X's implementation of foo() and foo(int) to the base class. People love the new Y class, and so start using it in all their projects. Later on, the coder who implemented Y, finds another way of implementing foo(int), but thinks foo() cannot be improved. So he releases a new version of Y which overrides only foo(int). Now code that calls foo() from a Y derivative all breaks because the coder added a foo(int) method. Everyone who used the Y derivative suddenly finds their code doesn't compile. This is very counterintuitive. Not only that, but I could easily see it slipping through unit tests. Who thinks to unit test a class by calling all the base class members they didn't override? My point basically is why should a coder be forced to declare an alias when realistically there is no reason they would NOT want to declare the alias? -Steve
Aug 01 2007
Steve Schveighoffer wrote:My point basically is why should a coder be forced to declare an alias when realistically there is no reason they would NOT want to declare the alias?You've got my vote.-Steve
Aug 01 2007
On Wed, 01 Aug 2007 17:37:58 -0400, Steve Schveighoffer wrote:This is very counterintuitive...My point basically is why should a coder be forced to declare an alias when realistically there is no reason they would NOT want to declare the alias?Oh don't get me wrong, I too think that the current rules are totally daft. I mean, why does one go to the trouble of deriving one class from another if its not to take advantage of members in the parent class? I vaguely recall Walter saying that he decided to do it this way because it is easy to implement this rule into the compiler and it makes the compiler run faster. (I am paraphrasing from memory, so I could be totally wrong). If this is so, then it strikes me that the language is this way in order to make life easier for compiler writers than for D coders, which is just not right in my POV. -- Derek (skype: derek.j.parnell) Melbourne, Australia 2/08/2007 2:14:09 PM
Aug 01 2007
Derek Parnell pisze:On Wed, 01 Aug 2007 17:37:58 -0400, Steve Schveighoffer wrote:... and it's even not consistent with one of D design rules: "If something looks similar as in C++, it should behave similar to C++." I think that breaking this rule is main source of confusion here. In my opinion it would be much better for D to implement inheritance in a way similar to other C-like languages. Regards Marcin Kuszczak Aarti_plThis is very counterintuitive...My point basically is why should a coder be forced to declare an alias when realistically there is no reason they would NOT want to declare the alias?Oh don't get me wrong, I too think that the current rules are totally daft. I mean, why does one go to the trouble of deriving one class from another if its not to take advantage of members in the parent class? I vaguely recall Walter saying that he decided to do it this way because it is easy to implement this rule into the compiler and it makes the compiler run faster. (I am paraphrasing from memory, so I could be totally wrong). If this is so, then it strikes me that the language is this way in order to make life easier for compiler writers than for D coders, which is just not right in my POV.
Aug 01 2007
Aarti_pl wrote:... and it's even not consistent with one of D design rules: "If something looks similar as in C++, it should behave similar to C++." I think that breaking this rule is main source of confusion here. In my opinion it would be much better for D to implement inheritance in a way similar to other C-like languages.It does behave, in this instance, just like C++. Steve is asking for the Java behavior.
Aug 03 2007
Walter Bright wrote:Aarti_pl wrote:Yes you are right. I found that I was wrong just after Regan post with old threads concerning this issue. There must be some other rule broken in this case, because I didn't know that in C++ behaviour is same. :-) Probably rule which is broken is just... programmer intuition... Although current behaviour is not very intuitive I can live with it (knowing that there are arguments against intuitive behaviour). I think that it would be good idea to document it better (including few arguments why it was chosen over Java style behaviour). -- Regards Marcin Kuszczak (Aarti_pl) ------------------------------------- Ask me why I believe in Jesus - http://zapytaj.dlajezusa.pl (en/pl) Doost (port of few Boost libraries) - http://www.dsource.org/projects/doost/ -------------------------------------... and it's even not consistent with one of D design rules: "If something looks similar as in C++, it should behave similar to C++." I think that breaking this rule is main source of confusion here. In my opinion it would be much better for D to implement inheritance in a way similar to other C-like languages.It does behave, in this instance, just like C++. Steve is asking for the Java behavior.
Aug 04 2007
Derek Parnell wrote:On Wed, 01 Aug 2007 17:37:58 -0400, Steve Schveighoffer wrote:You're not recalling Walter reasoning correctly. :) The reason it is done the way it is, the reason Walter gave, is that this is the same behaviour C++ has. He also explained why C++ has that behaviour, I've linked his original arguments at the end of this post. #include <stdio.h> class Base { public: void foo() { printf("Base::foo()\n"); } void foo(int i) { printf("Base::foo(i)\n"); } }; class Child : Base { public: void foo(int i) { printf("Child::foo(i)\n"); } }; void main() { Child c; c.foo(); } Some references to Walters original comments on the topic. Anyone who is interested should probably read this entire thread (though it is a monster) to get a really clear picture of the problems associated with this: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=6975 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7053 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7063 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7137 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7149 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7057 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7072 I could keep linking all day... these are all from the same thread so starting at the top and reading the whole thread should give you a fair idea of the issues. ReganThis is very counterintuitive...My point basically is why should a coder be forced to declare an alias when realistically there is no reason they would NOT want to declare the alias?Oh don't get me wrong, I too think that the current rules are totally daft. I mean, why does one go to the trouble of deriving one class from another if its not to take advantage of members in the parent class? I vaguely recall Walter saying that he decided to do it this way because it is easy to implement this rule into the compiler and it makes the compiler run faster. (I am paraphrasing from memory, so I could be totally wrong). If this is so, then it strikes me that the language is this way in order to make life easier for compiler writers than for D coders, which is just not right in my POV.
Aug 02 2007
Regan Heath pisze:#include <stdio.h> class Base { public: void foo() { printf("Base::foo()\n"); } void foo(int i) { printf("Base::foo(i)\n"); } }; class Child : Base { public: void foo(int i) { printf("Child::foo(i)\n"); } }; void main() { Child c; c.foo(); } Some references to Walters original comments on the topic. Anyone who is interested should probably read this entire thread (though it is a monster) to get a really clear picture of the problems associated with this: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma s.D&article_id=6975 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma s.D&article_id=7053 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma s.D&article_id=7063 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma s.D&article_id=7137 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma s.D&article_id=7149 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma s.D&article_id=7057 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalma s.D&article_id=7072 I could keep linking all day... these are all from the same thread so starting at the top and reading the whole thread should give you a fair idea of the issues. Reganups... my argument from last post falled down into ruins... I am little bit shocked that C++ does it same way as D... As I did not yet read all mentioned threads, currently I am not telling any word more :-) Regards Marcin Kuszczak (Aarti_pl)
Aug 02 2007
On Thu, 02 Aug 2007 09:32:31 +0100, Regan Heath wrote:You're not recalling Walter reasoning correctly. :)Thanks Regan. I'd succeded in burning that discussion out of my mind :)The reason it is done the way it is, the reason Walter gave, is that this is the same behaviour C++ has. He also explained why C++ has that behaviour, ...Ok, I re-read the threads again and still draw the same conclusion. Walter has got this one wrong. D should be better than C++, IMHO. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Aug 02 2007
Derek Parnell wrote:I'm not sure it's as simple as 'better' or worse. I think it's another case where D makes a tradeoff and as always happens when you make a tradeoff some people would have 'gone the other way'. Doing it the C++ way prevents the problems Walter describes but it requires you to manually 'alias' symbols into the derived class. Doing it the Java way (as many people seem to expect, more intuitive?) doesn't require you to 'alias' the symbols into the derived class but you are vulnerable to the problems Walter describes (I believe this was prooven in that old thread). So the tradeoff is safety/explicit/control (C++ way) vs brevity/intuitiveness (Java way). I would have thought that given your personal preference for being explicit in many cases you would prefer the C++ approach? (I'm not trying to paint you into a corner with this statement, you're perfectly entitled to have a different opinion for this case as no two cases are identical) Further thoughts.. (arguing both sides) Steve gave an example in this very thread where the C++ behaviour can cause errors for users/consumers of the classes so that would count as a penalty for the C++ case. Conversely you could argue that at least this isn't a silent error as the problems Walter describe are (from memory - pls correct me if I haven't remember it correctly). You could argue D with it's single inheritance and interfaces is closer to Java than C++. In a situation where you have multiple inheritance it's certainly more likely to be complex in which case the C++ approach makes it easier to understand and therefore use derived classes correctly. Conversely you could argue any inheritance is complex enough and justifies the C++ behaviour. ReganThe reason it is done the way it is, the reason Walter gave, is that this is the same behaviour C++ has. He also explained why C++ has that behaviour, ...Ok, I re-read the threads again and still draw the same conclusion. Walter has got this one wrong. D should be better than C++, IMHO.
Aug 02 2007
Regan Heath Wrote:Steve gave an example in this very thread where the C++ behaviour can cause errors for users/consumers of the classes so that would count as a penalty for the C++ case. Conversely you could argue that at least this isn't a silent error as the problems Walter describe are (from memory - pls correct me if I haven't remember it correctly).OK, I was totally under the impression that C++ was the way I was saying D should be. I was assuming that C++ got it right. More than I can't believe that C++ is not right, I'm surprised I never had problems with it... However, I think it should be changed. Not sure if it will, as it seems there is already a precedent. In my opinion, the base class should be examined if the derived class does not provide a suitable match. I can't see how this would cause too much of a performance hit, especially since the choice of the compiler at that point is to error out (current implementation) or continue searching (my proposition). If the derived class can provide a suitable method, then it should be no more performance hit than the current implementation. BTW, my example comes from a real world case... tango :) If you look at tango.io.selector.EPollSelector it only implements select(float). It is trying to rely on the AbstractSelector's select() to call the other version, but the author failed to include the alias statement. Note that this essentially makes it seem like EPollSelector DOES NOT implement the ISelector interface, since ISelector calls for a select() method. That is, unless you cast to an AbstractSelector or ISelector, which is my workaround. -Steve
Aug 02 2007
Steve Schveighoffer wrote:Regan Heath Wrote:I suspect most people think C++ does it the way you suggested.Steve gave an example in this very thread where the C++ behaviour can cause errors for users/consumers of the classes so that would count as a penalty for the C++ case. Conversely you could argue that at least this isn't a silent error as the problems Walter describe are (from memory - pls correct me if I haven't remember it correctly).OK, I was totally under the impression that C++ was the way I was saying D should be.I was assuming that C++ got it right. More than I can't believe that C++ is not right, I'm surprised I never had problems with it...It's truly odd that no-one seems to have a problem with it in C++. It's not as if C++ even has 'alias' so it cannot pull symbols in, you're left re-implementing in the derived class. Perhaps everyone just does that without thinking about it.However, I think it should be changed. Not sure if it will, as it seems there is already a precedent. In my opinion, the base class should be examined if the derived class does not provide a suitable match. I can't see how this would cause too much of a performance hitPerformance isn't the reason against the Java behaviour, here is Walters explaination: <quote Walter quoting Stroustrup> Stroustrup gives two examples (slightly modified here): --------------------------------- class X1 { void f(int); } // chain of derivations X(n) : X(n-1) class X9: X8 { void f(double); } void g(X9 p) { p.f(1); // X1.f or X9.f ? } ----------------------------------- His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it. The other example involves operator=(), but since D doesn't allow overloading operator=() instead I'll rewrite it as if it were a function that needs to alter a class state, and a derived class written later that 'caches' a computation on the derived state: class B { long x; void set(long i) { x = i; } void set(int i) { x = i; } long squareIt() { return x * x; } } class D : B { long square; void set(long i) { B.set(i); square = x * x; } long squareIt() { return square; } } Now, imagine B were a complex class with a lot of stuff in it, and our optimizing programmer missed the existence of set(int). Then, one has: long foo(B b) { b.set(3); return b.squareIt(); } and we have an obscure bug. </quote> So, as you can see it's not for performance reasons but rather to avoid obscure bugs which when they manifest do so silently. Regan
Aug 02 2007
Regan Heath wrote:Steve Schveighoffer wrote: > I was assuming that C++ got it right. More thanC++ has "using Base::method;" to replace alias for this particular case.I can't believe that C++ is not right, I'm surprised I never had problems with it...It's truly odd that no-one seems to have a problem with it in C++. It's not as if C++ even has 'alias' so it cannot pull symbols in, you're left re-implementing in the derived class. Perhaps everyone just does that without thinking about it.
Aug 02 2007
Regan Heath wrote:Steve Schveighoffer wrote:'using' serves this purpose in C++.Regan Heath Wrote:> I was assuming that C++ got it right. More thanI can't believe that C++ is not right, I'm surprised I never had problems with it...It's truly odd that no-one seems to have a problem with it in C++. It's not as if C++ even has 'alias' so it cannot pull symbols in, you're left re-implementing in the derived class. Perhaps everyone just does that without thinking about it.One key difference between D and C++ in this regard is that class methods in D are virtual by default, while in C++ they are not. One could argue that this changes the implications of the "is a" relationship in D. However, I do think the current rule is less error-prone, once understood. SeanHowever, I think it should be changed. Not sure if it will, as it seems there is already a precedent. In my opinion, the base class should be examined if the derived class does not provide a suitable match. I can't see how this would cause too much of a performance hitPerformance isn't the reason against the Java behaviour, here is Walters explaination: <quote Walter quoting Stroustrup> Stroustrup gives two examples (slightly modified here): --------------------------------- class X1 { void f(int); } // chain of derivations X(n) : X(n-1) class X9: X8 { void f(double); } void g(X9 p) { p.f(1); // X1.f or X9.f ? } ----------------------------------- His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it.
Aug 02 2007
Regan, good thing posting about that other older thread, I didn't know about it, and it was nice to see some actual arguments for the current behavior (even if I don't agree with them, but at least they can be argued), instead of the typical "it's the way it's done in C++"/"it's how C++ people want it done"... The first argument is this one: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=6975 (the one where with a series of cast, one can call a different method other than the "nearest" one in the method lineage hierarchy) Indeed that's a problematic behavior, but it's not a language design error, it's a compiler bug! There has to be a way to make that work. Wasn't this option even considered or discussed? Did Walter just go right through to changing the language? Regan Heath wrote:Steve Schveighoffer wrote: Performance isn't the reason against the Java behaviour, here is Walters explaination: <quote Walter quoting Stroustrup> Stroustrup gives two examples (slightly modified here): --------------------------------- class X1 { void f(int); } // chain of derivations X(n) : X(n-1) class X9: X8 { void f(double); } void g(X9 p) { p.f(1); // X1.f or X9.f ? } ----------------------------------- His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it.The second argument. This one is simply crap... Stroustrup argues "that one should not need to understand everything about a class heirarchy in order to derive from it". Well, maybe you don't have to understand *everything* but you surely will have to understand *a lot*, even if you aren't changing a lot of behavior in the subclass. This is simply something you can't (or shouldn't) escape from: if you are changing the implementation, even if just a bit, you have to understand the rest of implementation, or you risk breaking things, no mater how boring or worksome it might be. This is not the same as simply *using* a class, where you only have to understand it's interface/contract, and encapsulation shields you from the implementation. This is my rebuttal for the general case of this argument, but as for that particular example, it should also be noted, that with a decent IDE (in the likeness of JDT), the example is totally moot. You just place your mouse of the function call, and a text hover will show you the signature (and javadoc) of the function, thus immediately indicating which overload is being called, without having to manually search N levels of hierarchy to find which overload it is. Code completion will also allow listing all available overloads in an equally easy and convenient way. This is yet another case of people failing to understand how semantic code analysis tools (ie, IDE) can influence language productivity and design shortcomings. (Minor plug, these two features, especially the last one, are not too far from being implemented in the Descent project)The other example involves operator=(), but since D doesn't allow overloading operator=() instead I'll rewrite it as if it were a function that needs to alter a class state, and a derived class written later that 'caches' a computation on the derived state: class B { long x; void set(long i) { x = i; } void set(int i) { x = i; } long squareIt() { return x * x; } } class D : B { long square; void set(long i) { B.set(i); square = x * x; } long squareIt() { return square; } } Now, imagine B were a complex class with a lot of stuff in it, and our optimizing programmer missed the existence of set(int). Then, one has: long foo(B b) { b.set(3); return b.squareIt(); } and we have an obscure bug. </quote>The third and final argument. This one at least, I agree that it is a valid concern. However, this should be solved in another way, not with the current D behavior (which clearly breaks the is-a relationship, a fixture of OO semantics). One suggestion I agree with, is that if a subclass does not override *all* overloads of the same function name, then it is a compiler error. Then the above code would simply be an error, forcing the user to use an alias to bring down the upper class overloads, or write their own overloads. For example, I've personally written visitor classes that in D would *silently* break with this behavior, and it wouldn't be easy to spot it, as well as very error prone in the face of changes. I could post the examples but it likely wouldn't matter to Walter anyway. PS: Someone call to this thread those guys who think D design development is "community-driven"... ;) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 03 2007
On Thu, 02 Aug 2007 12:26:14 +0100, Regan Heath wrote:I would have thought that given your personal preference for being explicit in many cases you would prefer the C++ approach?LOL ... it is obvious that I don't do much OO programming isn't it. I turns out that I misunderstood the issue all this time. I did some playing around this morning and I now have a better knowledge base to work from. My first, and biggest mistake, was thinking that EVERY name in the parent class was hidden from the derived class unless aliased. Now I realize that its only the ones with the same names explicitly defined in the derived class are hidden. My second mistake was thinking that aliasing only worked for one level of derivation. In other words if C is derived from B, and B is derived from A, I thought that C couldn't get access to A methods unless they were aliased in B. I now know this is thinking is wrong. I guess over time I'll get to appreciate this :) -- Derek (skype: derek.j.parnell) Melbourne, Australia 3/08/2007 9:53:20 AM
Aug 02 2007
Regan Heath wrote:I could keep linking all day... these are all from the same thread so starting at the top and reading the whole thread should give you a fair idea of the issues.All on one page: http://www.digitalmars.com/d/archives/digitalmars/D/6928.html
Aug 03 2007
Steve Schveighoffer wrote:Everyone who used the Y derivative suddenly finds their code doesn'tcompile. This is actually good. Failing to compile means that a change in the class that might break things will not *silently* break things.
Aug 03 2007
Derek Parnell wrote:This is intentional and is due to D's simplistic lookup rules. Basically, D will look for a matching signature only in the class it self and not in any parent classes (its a little more complex than this but ...)Essentially, D's behavior here matches C++'s. Steve is asking for Java style behavior. There are good arguments for both styles, and risks with both. I prefer the C++ style because: 1) One can get the Java style behavior by using the alias declaration as outlined by Derek. 2) It avoids the problem of forgetting to override one of the base class overloads, which can be a hidden source of bugs. 3) A new overload can be added to the base class without screwing up the derived class. 4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match. 5) I tend to view overloads as a tool that should be used sparingly, not as a substitute for having to think up a new name. In that vein, having a complex set of overloads is an indication that something might be wrong with the design.
Aug 03 2007
Walter Bright wrote:4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it. This is a picture of one using a tool to list all possible overloads, without have to look at EVERY base class: http://web.ist.utl.pt/bruno.d.o.medeiros/dee/ide_overload_completion.png It shows you the signatures of all overloads (as well as the class that they belong to). This is a picture of one calling that function with a literal, and having the tool automatically list which overload is being called: http://web.ist.utl.pt/bruno.d.o.medeiros/dee/ide_overload_lookup.png The yellow popup shows the signature of the invoked overload, which we can see is the one with the long parameter. How easy! You don't even have to recall the specifics of the lookup rules of the language. (note that that popup appears just by placing the mouse cursor over "set", there is not even a need to invoke an explicit command) etc.. (which is *way* easier than doing it for C++) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 04 2007
Bruno Medeiros wrote:Walter Bright wrote:I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review). Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are.4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.
Aug 04 2007
Walter Bright wrote:Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are.I think optimizing your language for vi and the printer is a dead-end. The vast majority of the time a developer is going to be working on his code on a computer using some tool. If a tool lets you make use of a big productivity feature, then it makes sense to use it and officially endorse it as a basic requirement of the language. Even vi users use tools like grep and ctags. -Jeff
Aug 05 2007
Jeff Nowakowski wrote:I think optimizing your language for vi and the printer is a dead-end. The vast majority of the time a developer is going to be working on his code on a computer using some tool. If a tool lets you make use of a big productivity feature, then it makes sense to use it and officially endorse it as a basic requirement of the language. Even vi users use tools like grep and ctags.Everyone uses different tools, and particular tools may not be available on all platforms D is on. FWIW, a lot of hard core programmers still use vi.
Aug 05 2007
Walter Bright wrote:Jeff Nowakowski wrote:I use emacs at work. Sun Studio (the obvious alternative) is too slow and I don't care about any of the features it offers over a plain text editor combined with command-line tools (grep, etc). SeanI think optimizing your language for vi and the printer is a dead-end. The vast majority of the time a developer is going to be working on his code on a computer using some tool. If a tool lets you make use of a big productivity feature, then it makes sense to use it and officially endorse it as a basic requirement of the language. Even vi users use tools like grep and ctags.Everyone uses different tools, and particular tools may not be available on all platforms D is on. FWIW, a lot of hard core programmers still use vi.
Aug 05 2007
Walter Bright wrote:Jeff Nowakowski wrote:Cool; I'm hard core! Wait... does it still count if I'm using vim with a gui? :P Incidentally, I tried switching to Code::Blocks so I would have a nice gui to use with ddbg, but in the end I went back to using ddbg on the command line; compared to vim, Code::Blocks' editor is crap :) -- DanielI think optimizing your language for vi and the printer is a dead-end. The vast majority of the time a developer is going to be working on his code on a computer using some tool. If a tool lets you make use of a big productivity feature, then it makes sense to use it and officially endorse it as a basic requirement of the language. Even vi users use tools like grep and ctags.Everyone uses different tools, and particular tools may not be available on all platforms D is on. FWIW, a lot of hard core programmers still use vi.
Aug 05 2007
Daniel Keep wrote:Cool; I'm hard core! Wait... does it still count if I'm using vim with a gui? :PA gui? I don't know, man :-)
Aug 07 2007
On Sun, 05 Aug 2007 11:06:10 -0700, Walter Bright wrote:FWIW, a lot of hard core programmers still use vi.And I think that some are even still chipping away at granite blocks :) -- Derek Parnell Melbourne, Australia "Down with mediocrity!"
Aug 06 2007
Derek Parnell wrote:On Sun, 05 Aug 2007 11:06:10 -0700, Walter Bright wrote:I'm a command line interface curmudgeon. The reason is simple- it's more productive, since: 1) I can touch-type. There's no such thing as using a mouse without looking. Using a mouse is like being forced to type using only your left pinkie, and every program you use has a different keyboard layout. 2) I have muscle memory in my fingers, meaning a lot of complex commands can just spew out with no thought required. 3) Pipes are cool. Can't do the equivalent with a gui. 4) I routinely write simple batch files to automate whatever I'm doing at the moment. Can't do that with a gui. 5) Ever tried to do a series of repeated actions with a gui? Like, one by one, save all the emails in a folder to a text file? It's agonizing. With CLI, I'll just dude up a quick batch file using cut & paste, and it's done. 6) Most of the time I need a command, I've already done it, so I just type the first couple characters then F8, bam, I'm flying. Guis have their uses - mainly for programs you aren't familiar with. But once you are familiar, CLI is faster. P.S. I use microEmacs, not vi. Mainly because I have the source to it and just fix it to work like I want.FWIW, a lot of hard core programmers still use vi.And I think that some are even still chipping away at granite blocks :)
Aug 07 2007
Walter Bright wrote:I'm a command line interface curmudgeon. The reason is simple- it's more productive, since: 1) I can touch-type. There's no such thing as using a mouse without looking. Using a mouse is like being forced to type using only your left pinkie, and every program you use has a different keyboard layout. 2) I have muscle memory in my fingers, meaning a lot of complex commands can just spew out with no thought required. 3) Pipes are cool. Can't do the equivalent with a gui. 4) I routinely write simple batch files to automate whatever I'm doing at the moment. Can't do that with a gui. 5) Ever tried to do a series of repeated actions with a gui? Like, one by one, save all the emails in a folder to a text file? It's agonizing. With CLI, I'll just dude up a quick batch file using cut & paste, and it's done. 6) Most of the time I need a command, I've already done it, so I just type the first couple characters then F8, bam, I'm flying. Guis have their uses - mainly for programs you aren't familiar with. But once you are familiar, CLI is faster. P.S. I use microEmacs, not vi. Mainly because I have the source to it and just fix it to work like I want.what you say assumes that GUIs imply mouse usage. i consider a GUI buggy if it can't be fully and efficiently controlled with the keyboard only (as i hate using the mouse as well). i agree with all your points, but they can apply to GUIs as well. regarding the batch-file issue: an IDE should actually help you to write more effective batch files by providing you more environment variables (for project management for example) and assign shortcuts to them. developers of older editors appear not to adapt more recent features like "goto declaration", member auto completion, supporting the debugger for navigating at source level or refactoring stuff. i guess there is little doubt about the usefulness of these features. i'm using a pretty bare bones editor plus a command line myself, but i consider it a temporary solution. i'd like a bare bones editor with "goto declaration" so i dont have to mock around with search and bookmarks all day :)
Aug 07 2007
Walter Bright wrote:Derek Parnell wrote:You distort the argument somewhat, by mentioning ahead several tasks which are specific to the command line *shell*, whereas what was being discussed was programming and editing source code.On Sun, 05 Aug 2007 11:06:10 -0700, Walter Bright wrote:I'm a command line interface curmudgeon. The reason is simple- it's more productive, since:FWIW, a lot of hard core programmers still use vi.And I think that some are even still chipping away at granite blocks :)1) I can touch-type. There's no such thing as using a mouse without looking. Using a mouse is like being forced to type using only your left pinkie, and every program you use has a different keyboard layout.Good GUIs understand this, and understand that it's important to have several available shortcuts for common tasks. Eclipse in particular is designed to be able to work without using the mouse *at all*, and it actually has developers testing it's usability that way (ie, prohibited from using the mouse), to ensure it works well. Eclipse also has an Emacs keybindings setting for text editors.2) I have muscle memory in my fingers, meaning a lot of complex commands can just spew out with no thought required.Same as above.3) Pipes are cool. Can't do the equivalent with a gui.Shell-specific point.4) I routinely write simple batch files to automate whatever I'm doing at the moment. Can't do that with a gui. 5) Ever tried to do a series of repeated actions with a gui? Like, one by one, save all the emails in a folder to a text file? It's agonizing. With CLI, I'll just dude up a quick batch file using cut & paste, and it's done.When I have need for such a thing (a more complicated series of actions), I load up the CLI and do it there. That's why I have Cygwin installed in Windows, and why an Explorer context menu option that opens a shell on the selected folder. The remainder 95% of the time is spent using the GUI. But again this was a shell-specific point. When programming, I don't recall ever *having the need* to do a series of repeated actions in an IDE. Perhaps you can give an example? (The closest I recall is "refactoring" C++ code, but that's something where the CLI won't help you either)6) Most of the time I need a command, I've already done it, so I just type the first couple characters then F8, bam, I'm flying. Guis have their uses - mainly for programs you aren't familiar with. But once you are familiar, CLI is faster. P.S. I use microEmacs, not vi. Mainly because I have the source to it and just fix it to work like I want.GUI based programs intrinsically have more usability potential that text-based ones, just for that fact that you can have a text-based/command-line interface in a GUI (or alternate to it), but the reverse is not true. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 07 2007
Reply to Bruno,Walter Bright wrote:If I ever manage an app development team, for the first GUI demonstration I'm going to walk in with a pair of tin snips and cut off the mouse. A bit dramatic, but I will show how well the UI works from the keyboard.1) I can touch-type. There's no such thing as using a mouse without looking. Using a mouse is like being forced to type using only your left pinkie, and every program you use has a different keyboard layout.Good GUIs understand this, and understand that it's important to have several available shortcuts for common tasks. Eclipse in particular is designed to be able to work without using the mouse *at all*, and it actually has developers testing it's usability that way (ie, prohibited from using the mouse), to ensure it works well. Eclipse also has an Emacs keybindings setting for text editors.But should it be? How nice would it be to have the full UNIX tool kit available inside of find and replace (find this pattern, run it through this pipeline, replace with output). And why stop there? Just yesterday I was mucking around almost have killed for a semantically aware find and replace (find all errors where function X has no overload resolution, If adding a cast to Y for the args fixes it, do that) How about a more general functionality, find and replace with SQL queries? I could go on but just imagine.3) Pipes are cool. Can't do the equivalent with a gui.Shell-specific point.I generally don't even bother with a shell script, just use a pipeline ending in " | bash"5) Ever tried to do a series of repeated actions with a gui? Like, one by one, save all the emails in a folder to a text file? It's agonizing. With CLI, I'll just dude up a quick batch file using cut & paste, and it's done.When I have need for such a thing (a more complicated series of actions), I load up the CLI and do it there.When programming, I don't recall ever *having the need* to do a series of repeated actions in an IDE. Perhaps you can give an example?build a switch block from an enum where their will be one case per value
Aug 07 2007
Bruno Medeiros wrote:But again this was a shell-specific point. When programming, I don't recall ever *having the need* to do a series of repeated actions in an IDE. Perhaps you can give an example?Here are some: . global file renaming . running automated test suites . interfile search/replace across a subset of the project files . copying a subset of the project files into another directory . running the debugger with the same complex set of commands, over and over(The closest I recall is "refactoring" C++ code, but that's something where the CLI won't help you either)Sure it does, the CLI has very powerful text processing tools available to it.
Aug 07 2007
Walter Bright wrote:Maybe for rename refactoring. Most other refactoring operations need an actual semantic tool, you can't do it with CLI text processing tools. But that is a moot point: when you do such rename refactoring, most of the times the IDE built-in search-replace facilities are just as good as using the CLI, if not better. For example in Visual Studio C++ I find it more convenient to do search replace in the IDE, since it already knows which C++ files belong to the project and which don't. That's not saying there aren't cases where using the CLI is better, but in those cases (which should not be common), one can just fire up a shell, do it, and go back to the IDE. Walter Bright wrote:(The closest I recall is "refactoring" C++ code, but that's something where the CLI won't help you either)Sure it does, the CLI has very powerful text processing tools available to it.Bruno Medeiros wrote:Some of those actions are simple to perform (like running automated test suites, or running the debugger with the same complex set of commands) : they should consist simply of a command invocation, whether in a GUI, or in an CLI shell. The others may indeed be repetitive to perform, but the same argument as above holds: if they are easier to do in the CLI (which should be an uncommon case, if the IDE is good), then fire up the CLI, do it, and go back to the IDE. An interesting and ironic side story: When I diff the docs of DMD releases, I have some regexps that clear out some common but unsignificant changes. However, because these are multi-line regexps, I have to load up my text editor (EmEditor) and use the search/replace feature of the editor, because GNU's sed -e, or any other standard GNU tool that I know of, does not support multi-line regexps. At least as far as I could find, If I am wrong and anyone know sa way, let me know, so that I can put it in a script. :) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DBut again this was a shell-specific point. When programming, I don't recall ever *having the need* to do a series of repeated actions in an IDE. Perhaps you can give an example?Here are some: .. global file renaming .. running automated test suites .. interfile search/replace across a subset of the project files .. copying a subset of the project files into another directory .. running the debugger with the same complex set of commands, over and over
Aug 09 2007
Bruno Medeiros wrote:Walter Bright wrote:Yes, you can. It's just that tools for this are not currently written.Maybe for rename refactoring. Most other refactoring operations need an actual semantic tool, you can't do it with CLI text processing tools.(The closest I recall is "refactoring" C++ code, but that's something where the CLI won't help you either)Sure it does, the CLI has very powerful text processing tools available to it.But that is a moot point: when you do such rename refactoring, most of the times the IDE built-in search-replace facilities are just as good as using the CLI, if not better. For example in Visual Studio C++ I find it more convenient to do search replace in the IDE, since it already knows which C++ files belong to the project and which don't.Are you saying that Visual Studio is better at refactoring C++ code than sed? :boggles:That's not saying there aren't cases where using the CLI is better, but in those cases (which should not be common), one can just fire up a shell, do it, and go back to the IDE.They shouldn't be common because if they are, that functionality should be integrated into the IDE.Walter Bright wrote: > Bruno Medeiros wrote: >> But again this was a shell-specific point. When programming, I don't >> recall ever *having the need* to do a series of repeated actions in an >> IDE. Perhaps you can give an example? > > Here are some: > > .. global file renaming > .. running automated test suites > .. interfile search/replace across a subset of the project files > .. copying a subset of the project files into another directory > .. running the debugger with the same complex set of commands, over and > over > Some of those actions are simple to perform (like running automated test suites, or running the debugger with the same complex set of commands) : they should consist simply of a command invocation, whether in a GUI, or in an CLI shell.And for the rest, you need the command line. Now, there's a command line in Visual Studio....The others may indeed be repetitive to perform, but the same argument as above holds: if they are easier to do in the CLI (which should be an uncommon case, if the IDE is good), then fire up the CLI, do it, and go back to the IDE.True, if the IDE is any better than vim, for instance. (I'm looking directly at the XML editor in Visual Studio. It takes *ages* to load a file of a few kilobytes, and after that, it's slow. Now, if I could get vim to do syntax highlighting for aspx files, it'd be hands-down better.)An interesting and ironic side story: When I diff the docs of DMD releases, I have some regexps that clear out some common but unsignificant changes. However, because these are multi-line regexps, I have to load up my text editor (EmEditor) and use the search/replace feature of the editor, because GNU's sed -e, or any other standard GNU tool that I know of, does not support multi-line regexps. At least as far as I could find, If I am wrong and anyone know sa way, let me know, so that I can put it in a script. :)sed goes line-by-line. You could use vim...that does multiline searches. And you can give it command line arguments that describe vim commands to execute.
Aug 10 2007
Christopher Wright wrote:Bruno Medeiros wrote:I didn't say do it in the CLI, I said do it in the CLI *with (tradicional) text processing tools* (grep, sed, awk, etc.).Walter Bright wrote:Yes, you can. It's just that tools for this are not currently written.Maybe for rename refactoring. Most other refactoring operations need an actual semantic tool, you can't do it with CLI text processing tools.(The closest I recall is "refactoring" C++ code, but that's something where the CLI won't help you either)Sure it does, the CLI has very powerful text processing tools available to it.For the common rename-refactoring, yes. Are you saying it's not? :3 -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DBut that is a moot point: when you do such rename refactoring, most of the times the IDE built-in search-replace facilities are just as good as using the CLI, if not better. For example in Visual Studio C++ I find it more convenient to do search replace in the IDE, since it already knows which C++ files belong to the project and which don't.Are you saying that Visual Studio is better at refactoring C++ code than sed? :boggles:
Aug 11 2007
What about automatic code refactoring? Try renaming a method that is overloaded with batch files. Walter Bright escribió:Derek Parnell wrote:On Sun, 05 Aug 2007 11:06:10 -0700, Walter Bright wrote:I'm a command line interface curmudgeon. The reason is simple- it's more productive, since: 1) I can touch-type. There's no such thing as using a mouse without looking. Using a mouse is like being forced to type using only your left pinkie, and every program you use has a different keyboard layout. 2) I have muscle memory in my fingers, meaning a lot of complex commands can just spew out with no thought required. 3) Pipes are cool. Can't do the equivalent with a gui. 4) I routinely write simple batch files to automate whatever I'm doing at the moment. Can't do that with a gui. 5) Ever tried to do a series of repeated actions with a gui? Like, one by one, save all the emails in a folder to a text file? It's agonizing. With CLI, I'll just dude up a quick batch file using cut & paste, and it's done. 6) Most of the time I need a command, I've already done it, so I just type the first couple characters then F8, bam, I'm flying. Guis have their uses - mainly for programs you aren't familiar with. But once you are familiar, CLI is faster. P.S. I use microEmacs, not vi. Mainly because I have the source to it and just fix it to work like I want.FWIW, a lot of hard core programmers still use vi.And I think that some are even still chipping away at granite blocks :)
Aug 07 2007
Walter Bright wrote:Bruno Medeiros wrote:Auditing, as in someone review someone else's code? Well, yes, in that case that tool functionality wouldn't be that useful (although other functionalities might be), however, auditing is a very specific situation, as well has it has lots of other difficulties which aren't easy to handle.Walter Bright wrote:I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review).4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are."rarely used"?? Wait, what do you mean "add-on tools"? That functionality is part of JDT (the best (non-commercial) and most popular Java IDE), and similar functionality exists in other IDEs for other languages, so it's quite the opposite of rarely used. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Aug 05 2007
Bruno Medeiros wrote:Walter Bright wrote:Yes. This is done a lot in professional programming environments, and in particular in cases where high reliability/security is needed. C++ is a very expensive language to audit because of, for example, the nearly nonexistent modularity.I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review).Auditing, as in someone review someone else's code?Well, yes, in that case that tool functionality wouldn't be that useful (although other functionalities might be), however, auditing is a very specific situation, as well has it has lots of other difficulties which aren't easy to handle.I think that when a language is more auditable, it is more attractive for uses that demand high reliability or security. Auditability has definitely been a goal of D since the beginning. I've worked with programmers from the corporate Java world who have told me about auditability problems with Java that makes their life much more difficult.Let me put it another way. How many people use a profiler? coverage analyser? Those tools are built-in, are extremely useful, yet are still rarely used.Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are."rarely used"?? Wait, what do you mean "add-on tools"? That functionality is part of JDT (the best (non-commercial) and most popular Java IDE), and similar functionality exists in other IDEs for other languages, so it's quite the opposite of rarely used.
Aug 05 2007
On Sun, 05 Aug 2007 11:15:13 -0700, Walter Bright wrote:Let me put it another way. How many people use a profiler?BTW, I've tried to use the profiler a couple of times now, but the output is so obscure I can't work out what the hell it is trying to tell me. -- Derek Parnell Melbourne, Australia "Down with mediocrity!"
Aug 06 2007
Derek Parnell wrote:On Sun, 05 Aug 2007 11:15:13 -0700, Walter Bright wrote:I've used Quantify before and the UI it employs presents the information in an fairly clear manner. Not sure what there is on the non-commercial front though. SeanLet me put it another way. How many people use a profiler?BTW, I've tried to use the profiler a couple of times now, but the output is so obscure I can't work out what the hell it is trying to tell me.
Aug 06 2007
Derek Parnell wrote:BTW, I've tried to use the profiler a couple of times now, but the output is so obscure I can't work out what the hell it is trying to tell me.Post a snippet of the output and point out what is unclear, and I'll try to help.
Aug 07 2007
Walter Bright wrote:Post a snippet of the output and point out what is unclear, and I'll try to help.the sections that come first, before the list of functions sorted by "func time": ------------------ 1 _D5seatd6parser9GLRParser5parseMFKAxaPPS5seatd6parser10SyntaxTreeZb 1095 _D5seatd6parser11MainGrammar5parseMFKAxakkbZb12reduceBranchMFkZb 1856 _D5seatd6parser11MainGrammar5parseMFKAxakkbZb11shiftBranchMFkZb _D5seatd6parser11MainGrammar5parseMFKAxakkbZb 2952 122732995 3009972 the sections appear to list the functions that are directly called by the top most in the section. but i can't figure out what the numbers mean and why there are some entries with a single one on the left and some with 3 numbers on the right of the function name.
Aug 07 2007
Jascha Wetzel wrote:Walter Bright wrote:I renamed the functions for simplicity. D is called 2952 times. 1 of those calls comes from A. 1095 come from B. 1856 come from C. Note that 1+1095+1856 = 2952. (The A, B, C counts are called the "fan in".) The "fan out" is a list of counts of what D calls, and follows the line for D. The total number of timer ticks spent in D is 3009972, excluding whatever D calls. The total number of timer ticks in D, including whatever D calls, is 122732995. From this information you can not only determine where the time is being consumed, but *why*, as you know where the calls are coming from. You can do this to determine the runtime relationships between functions, which can be used to set the link order (and this is output by the profiler in the form of a .def file).Post a snippet of the output and point out what is unclear, and I'll try to help.the sections that come first, before the list of functions sorted by "func time": ------------------ 1 A 1095 B 1856 C D 2952 122732995 3009972 the sections appear to list the functions that are directly called by the top most in the section. but i can't figure out what the numbers mean and why there are some entries with a single one on the left and some with 3 numbers on the right of the function name.
Aug 07 2007
Walter Bright wrote:From this information you can not only determine where the time is being consumed, but *why*, as you know where the calls are coming from. You can do this to determine the runtime relationships between functions, which can be used to set the link order (and this is output by the profiler in the form of a .def file).thanks, this is very useful!
Aug 07 2007
Walter Bright wrote:Jascha Wetzel wrote:Thank you for explaining this. (And actually, it makes perfect sense once you /know/.) For those like me, knowing with a certainty what numbers are what, and what the layout of all that data means, should breathe new life into this nifty compiler feature. -- Chris Nicholson-SaulsWalter Bright wrote:I renamed the functions for simplicity. D is called 2952 times. 1 of those calls comes from A. 1095 come from B. 1856 come from C. Note that 1+1095+1856 = 2952. (The A, B, C counts are called the "fan in".) The "fan out" is a list of counts of what D calls, and follows the line for D. The total number of timer ticks spent in D is 3009972, excluding whatever D calls. The total number of timer ticks in D, including whatever D calls, is 122732995. From this information you can not only determine where the time is being consumed, but *why*, as you know where the calls are coming from. You can do this to determine the runtime relationships between functions, which can be used to set the link order (and this is output by the profiler in the form of a .def file).Post a snippet of the output and point out what is unclear, and I'll try to help.the sections that come first, before the list of functions sorted by "func time": ------------------ 1 A 1095 B 1856 C D 2952 122732995 3009972 the sections appear to list the functions that are directly called by the top most in the section. but i can't figure out what the numbers mean and why there are some entries with a single one on the left and some with 3 numbers on the right of the function name.
Aug 07 2007
I'm just reposting this with a different subject to give it an easier to find subject line. Walter Bright wrote: Jascha Wetzel wrote:Walter Bright wrote:I renamed the functions for simplicity. D is called 2952 times. 1 of those calls comes from A. 1095 come from B. 1856 come from C. Note that 1+1095+1856 = 2952. (The A, B, C counts are called the "fan in".) The "fan out" is a list of counts of what D calls, and follows the line for D. The total number of timer ticks spent in D is 3009972, excluding whatever D calls. The total number of timer ticks in D, including whatever D calls, is 122732995. From this information you can not only determine where the time is being consumed, but *why*, as you know where the calls are coming from. You can do this to determine the runtime relationships between functions, which can be used to set the link order (and this is output by the profiler in the form of a .def file).Post a snippet of the output and point out what is unclear, and I'll try to help.the sections that come first, before the list of functions sorted by "func time": ------------------ 1 A 1095 B 1856 C D 2952 122732995 3009972 the sections appear to list the functions that are directly called by the top most in the section. but i can't figure out what the numbers mean and why there are some entries with a single one on the left and some with 3 numbers on the right of the function name.
Aug 09 2007
Derek Parnell wrote:On Sun, 05 Aug 2007 11:15:13 -0700, Walter Bright wrote:I smell a potential project here, a gui front-end for dmd profiler output, hmm... Sounds like something I'd like to try. I have been meaning to have a good look at the current GUI libraries for D. ReganLet me put it another way. How many people use a profiler?BTW, I've tried to use the profiler a couple of times now, but the output is so obscure I can't work out what the hell it is trying to tell me.
Aug 07 2007
Derek Parnell wrote:yep, i use -profile regularly but the only portion of the trace.log i use is the one documented (#calls, tree time, func time, etc.). my guesses for the others have failed so far ;)Let me put it another way. How many people use a profiler?BTW, I've tried to use the profiler a couple of times now, but the output is so obscure I can't work out what the hell it is trying to tell me.
Aug 07 2007
Walter Bright escribió:Bruno Medeiros wrote:I think it's the opposite. In every workplace I've been, people can't live with a decent IDE.Walter Bright wrote:I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review). Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are.4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.
Aug 05 2007
Ary Manzana wrote:Walter Bright escribió:For most things I do just fine without an IDE. For all my work with D, PHP, ... well nearly everything, I use EditPlus which provides next to nothing beyond very basic keyword highlighting. Its enough. :) (Actually he added simplistic code folding in the last release... that I do like, but I digress.) For Ruby I use SciTE which is little more than a glorified notepad/edit with respectably elaborate highlighting. (I expressions within strings.) The only language I crave an IDE for... is Java. Just for dot-code-completion, and then only because the standard library so such huge. (Okay... so Ruby has a pretty extensive set as well, but its not so unapproachably big as Java's.) That being said... I do like to run tools to generate "meta-data" and other reports from code which I can use to review it. Even to the extent of sometimes coding my own. (Wrote a little thing to generate HTML files from a LambdaMOO database once. Made working on a MOO a little saner.) So I think the primary argument is still sound in favor of tools, although I'm slipping back toward neutrality on this matter of inheritance and overloading... turns out Walter is right about one important thing at the very least: it can definitely be argued, and argued well, in either direction. At least aliases are a quick and simple "fix". What we really need is a fix for the problem of casting to a Base class "forgetting" overrides. Maybe a change to how vtbls work? Okay, an example: class Base { int foo () { return 1; } } class Derived : Base { override int foo () { return 2; } } What I'm thinking, is that Derived's copy of Base's vtbl should have the pointer for .foo replaced with a pointer to its override. That way, when cast to Base the entry still points to Derived's method. Assuming the vtbl is prepared at compile-time, this shouldn't cause any runtime issues... should it? -- Chris Nicholson-SaulsBruno Medeiros wrote:I think it's the opposite. In every workplace I've been, people can't live with a decent IDE.Walter Bright wrote:I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review). Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are.4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.
Aug 05 2007
Reply to Chris Nicholson-Sauls,What we really need is a fix for the problem of casting to a Base class "forgetting" overrides. Maybe a change to how vtbls work? Okay, an example: class Base { int foo () { return 1; } } class Derived : Base { override int foo () { return 2; } } What I'm thinking, is that Derived's copy of Base's vtbl should have the pointer for .foo replaced with a pointer to its override. That way, when cast to Base the entry still points to Derived's method. Assuming the vtbl is prepared at compile-time, this shouldn't cause any runtime issues... should it?Unless I'm totally misreading you, that IS how it works.
Aug 05 2007
BCS wrote:Reply to Chris Nicholson-Sauls,Hmm. I just re-tested this after your reply, and yes it does indeed work. I could swear that it didn't use to, or at least that there was some case that caused Base's implementation to be ran. But since its apparently working right now... yay. :) -- Chris Nicholson-SaulsWhat we really need is a fix for the problem of casting to a Base class "forgetting" overrides. Maybe a change to how vtbls work? Okay, an example: class Base { int foo () { return 1; } } class Derived : Base { override int foo () { return 2; } } What I'm thinking, is that Derived's copy of Base's vtbl should have the pointer for .foo replaced with a pointer to its override. That way, when cast to Base the entry still points to Derived's method. Assuming the vtbl is prepared at compile-time, this shouldn't cause any runtime issues... should it?Unless I'm totally misreading you, that IS how it works.
Aug 05 2007
Reply to Walter,Bruno Medeiros wrote:In code review, you may have a point. In writing code? Now that is one thing Microsoft got RIGHT!! Visual Studio rocks in that regard. Now it they just had a feature that (automatically without me asking for it) gives me a list of cases where a code change resulted in a different overload resolution... It would make a nice feature in a diff tool as well.Walter Bright wrote:I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review). Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are.4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.
Aug 05 2007
Walter Bright wrote:Bruno Medeiros wrote:It might be neat to define an API for the compiler. I mean an api that would let you write programs that link to d-compiler.lib to do various things like get an AST etc. I know D syntax tries to be easy to parse, but why not just have the spec also specify an api for would-be D compilers. Giving a compiler a well defined API is not really something specific to D but it would make a lot of things easier. --bbWalter Bright wrote:I appreciate that you can write tools to analyze it. I think they help a lot in writing the code, but I don't think they are as helpful for auditing (code review). Another problem with them is they are not part of the D compiler itself, and in my experience add on tools rarely get used, no matter how useful they are.4) Having overloads spread across the inheritance hierarchy makes the source code resistant to visual audits. For any method call, you'll have to look at EVERY base class to see if it has an overload that is a better match.I'm not sure if you've seen my other reply to this argument, but let be more explicit, with pictures. Let's say you have such a big inheritance hierarchy, with overloads spread across it.
Aug 05 2007
Bill Baxter wrote:It might be neat to define an API for the compiler. I mean an api that would let you write programs that link to d-compiler.lib to do various things like get an AST etc. I know D syntax tries to be easy to parse, but why not just have the spec also specify an api for would-be D compilers. Giving a compiler a well defined API is not really something specific to D but it would make a lot of things easier.FWIW, i'm writing a library like that ATM. i'm testing it with various D code right now. it parses phobos, i'm going through tango now. dstress will be next.
Aug 07 2007
Derek Parnell wrote:On Wed, 01 Aug 2007 16:47:12 -0400, Steve Schveighoffer wrote:Well if you're going to make that kind of a requirement, why not allow multiple inheritance? It would seem to resolve all the problems with "ambiguity in multiple paths of implementation". (Also, it would be well if this were prominently documented. You did a very clear job, so copying that would be all that's necessary. File it under inheritance.)I am wondering if the following behavior is intentional and if so, why.This is intentional and is due to D's simplistic lookup rules. Basically, D will look for a matching signature only in the class it self and not in any parent classes (its a little more complex than this but ...) To 'fix' this situation you need to explicitly identify methods from parent classes that you wish to access from objects of the child class. This is done using an alias statement. Add "alias X.foo foo;" to your class definition of Y. class Y : X { alias X.foo foo; // pulls in class X's foo name into this scope. public int foo(int y) { return 3; } } Now it will compile and run.
Aug 04 2007
Steve Schveighoffer wrote:Hi, I am wondering if the following behavior is intentional and if so, why. Given the code below: class X { public int foo() { return foo(0); } public int foo(int y) { return 2; } } class Y : X { public int foo(int y) { return 3; } } int main(char [][] argv) { Y y = new Y; y.foo(); //does not compile, says that the argument type doesn't match y.foo(1); X x = y; x.foo(); return 0; } How come the marked line above does not compile? Clearly there is no ambiguity that I want to call the base's foo, which in turn should call Y's foo(int) with an argument of 0. It's not that the method is not accessible, because I can clearly access it by casting to an X type (as I have done in the subsequent lines). If you interpret the code, I'm defining a default behavior for foo() with no arguments. A derived class which wants to keep the default behavior of foo() as calling foo(0), should only need to override foo(int). However, the compiler does not allow this. Why? Is there a workaround (besides implementing a stub function which calls super.foo())? Maybe there is a different method of defining in a base class one version of a function in terms of another? -SteveI've never really 100% understood why this is the way that it is, but there /does/ exist a simple workaround. Add this line to Y: alias super.foo foo; Yep, alias the superclass's method into the current class's scope. The problem has something to do with the lookup rules. At the very least, I would think that declaring Y.foo with the 'override' attribute might give the wanted behavior, since surely a int(int) couldn't override a int(), yes? But no, last I checked, it doesn't work either. -- Chris Nicholson-Sauls
Aug 01 2007
Chris Nicholson-Sauls wrote:Steve Schveighoffer wrote:That's not a workaround, nor is this considered a problem. This is the intended behavior. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.orgHi, I am wondering if the following behavior is intentional and if so, why. Given the code below: class X { public int foo() { return foo(0); } public int foo(int y) { return 2; } } class Y : X { public int foo(int y) { return 3; } } int main(char [][] argv) { Y y = new Y; y.foo(); //does not compile, says that the argument type doesn't match y.foo(1); X x = y; x.foo(); return 0; } How come the marked line above does not compile? Clearly there is no ambiguity that I want to call the base's foo, which in turn should call Y's foo(int) with an argument of 0. It's not that the method is not accessible, because I can clearly access it by casting to an X type (as I have done in the subsequent lines). If you interpret the code, I'm defining a default behavior for foo() with no arguments. A derived class which wants to keep the default behavior of foo() as calling foo(0), should only need to override foo(int). However, the compiler does not allow this. Why? Is there a workaround (besides implementing a stub function which calls super.foo())? Maybe there is a different method of defining in a base class one version of a function in terms of another? -SteveI've never really 100% understood why this is the way that it is, but there /does/ exist a simple workaround. Add this line to Y: alias super.foo foo; Yep, alias the superclass's method into the current class's scope. The problem has something to do with the lookup rules. At the very least, I would think that declaring Y.foo with the 'override' attribute might give the wanted behavior, since surely a int(int) couldn't override a int(), yes? But no, last I checked, it doesn't work either. -- Chris Nicholson-Sauls
Aug 01 2007
Kirk McDonald wrote:Chris Nicholson-Sauls wrote:Intended, yes. Wanted? Not by all. (Which is why it keeps coming up again, and again, and again... ad nauseum.) I haven't personally scanned the specs to see if its at least now explicitly shown as the way to do this; if it isn't, it should be. -- Chris Nicholson-SaulsSteve Schveighoffer wrote:That's not a workaround, nor is this considered a problem. This is the intended behavior.Hi, I am wondering if the following behavior is intentional and if so, why. Given the code below: class X { public int foo() { return foo(0); } public int foo(int y) { return 2; } } class Y : X { public int foo(int y) { return 3; } } int main(char [][] argv) { Y y = new Y; y.foo(); //does not compile, says that the argument type doesn't match y.foo(1); X x = y; x.foo(); return 0; } How come the marked line above does not compile? Clearly there is no ambiguity that I want to call the base's foo, which in turn should call Y's foo(int) with an argument of 0. It's not that the method is not accessible, because I can clearly access it by casting to an X type (as I have done in the subsequent lines). If you interpret the code, I'm defining a default behavior for foo() with no arguments. A derived class which wants to keep the default behavior of foo() as calling foo(0), should only need to override foo(int). However, the compiler does not allow this. Why? Is there a workaround (besides implementing a stub function which calls super.foo())? Maybe there is a different method of defining in a base class one version of a function in terms of another? -SteveI've never really 100% understood why this is the way that it is, but there /does/ exist a simple workaround. Add this line to Y: alias super.foo foo; Yep, alias the superclass's method into the current class's scope. The problem has something to do with the lookup rules. At the very least, I would think that declaring Y.foo with the 'override' attribute might give the wanted behavior, since surely a int(int) couldn't override a int(), yes? But no, last I checked, it doesn't work either. -- Chris Nicholson-Sauls
Aug 01 2007
Chris Nicholson-Sauls Wrote:Intended, yes. Wanted? Not by all. (Which is why it keeps coming up again, and again, and again... ad nauseum.) I haven't personally scanned the specs to see if its at least now explicitly shown as the way to do this; if it isn't, it should be.It's sort of in the spec, but the example shows the two overrides differing by paramter types int and long. In this case, at least one can be implicitly casted to the other, but in my example, there is no doubt which method I intended to call. -Steve
Aug 01 2007
Kirk McDonald wrote:Yes, and it took some extra effort to make sure it worked that way! In other words, it is not accidental or incidental behavior.alias super.foo foo;That's not a workaround, nor is this considered a problem. This is the intended behavior.
Aug 03 2007
Walter Bright wrote:Kirk McDonald wrote:But perhaps alias isn't the right tool? Someone below suggested that this can cause problems when only one function out of several overloaded functions are desired. This can be coded around, but perhaps a more specific importation rule could be used. I can even see justification for co-opting import for this usage: import package.function(arglist) as localName; to be used within the class that is inheriting. Actually, I'd prefer that this be the verb inherit rather than import, but that would probably mean adding a new reserved word.Yes, and it took some extra effort to make sure it worked that way! In other words, it is not accidental or incidental behavior.alias super.foo foo;That's not a workaround, nor is this considered a problem. This is the intended behavior.
Aug 05 2007
Reply to Chris Nicholson-Sauls,I've never really 100% understood why this is the way that it is, but there /does/ exist a simple workaround. Add this line to Y: alias super.foo foo; Yep, alias the superclass's method into the current class's scope. The problem has something to do with the lookup rules. At the very least, I would think that declaring Y.foo with the 'override' attribute might give the wanted behavior, since surely a int(int) couldn't override a int(), yes? But no, last I checked, it doesn't work either.And this suffers from the issue that a function name and a function are not the same thing, a function name (which can be alised) refers to ALL function overloads (which can't be aliased). This is the cause of many bugs and hack-arounds. <coff> um... Walter?
Aug 02 2007
Regan Heath Wrote:It's truly odd that no-one seems to have a problem with it in C++. It's not as if C++ even has 'alias' so it cannot pull symbols in, you're left re-implementing in the derived class. Perhaps everyone just does that without thinking about it.After thinking about it, my opinion is that C++ coders tend to avoid implementing everything in objects using inheritance as opposed to languages which require using objects for everything (i.e. Java). This tends to have many coders putting their "methods" in the global namespace instead of in a method, putting everything in the same scope. and BTW, Walter in his previous posts rightly points out that the "using" keyword can be used inside a C++ class much like the "alias" keyword is used in a D class to achieve the same effect.My argument is not against this, as you will note that in these examples, both base and derived classes can match the call by some sort of implicit conversion. In the case where both the base class and the derived class match the call, I could care less if the derived class was called instead of the base class. Who can say what class documentation the coder was looking at when he wrote the call? Both options seem equally likely. I am proposing a change of behavior when the derived class CANNOT match the call. At that point the compiler errors out instead of examining the base classes. When the coder writes code where there is only one match, and that match is in a base class, it makes perfect sense that the coder explicitly wants to call the base class' method. I see no reason to force the coder to explicitly call for the base class' method when there is no alternative in the derived class, the code is unambiguous. I'd bet we'd see 0 obscure bugs if this change was made ;) As I've mentioned, I've already seen one obscure bug caused by the current implementation... -SteveHowever, I think it should be changed. Not sure if it will, as it seems there is already a precedent. In my opinion, the base class should be examined if the derived class does not provide a suitable match. I can't see how this would cause too much of a performance hitPerformance isn't the reason against the Java behaviour, here is Walters explaination: <quote Walter quoting Stroustrup> ... </quote> So, as you can see it's not for performance reasons but rather to avoid obscure bugs which when they manifest do so silently. Regan
Aug 02 2007
Steve Schveighoffer Wrote:Regan Heath Wrote:Hm.. for some reason, I thought this would appear under the original thread. In any case here is the missing quote from above: <quote Walter quoting Stroustrup> Stroustrup gives two examples (slightly modified here): --------------------------------- class X1 { void f(int); } // chain of derivations X(n) : X(n-1) class X9: X8 { void f(double); } void g(X9 p) { p.f(1); // X1.f or X9.f ? } ----------------------------------- His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it. The other example involves operator=(), but since D doesn't allow overloading operator=() instead I'll rewrite it as if it were a function that needs to alter a class state, and a derived class written later that 'caches' a computation on the derived state: class B { long x; void set(long i) { x = i; } void set(int i) { x = i; } long squareIt() { return x * x; } } class D : B { long square; void set(long i) { B.set(i); square = x * x; } long squareIt() { return square; } } Now, imagine B were a complex class with a lot of stuff in it, and our optimizing programmer missed the existence of set(int). Then, one has: long foo(B b) { b.set(3); return b.squareIt(); } and we have an obscure bug. </quote>Performance isn't the reason against the Java behaviour, here is Walters explaination: <quote Walter quoting Stroustrup> ... </quote> So, as you can see it's not for performance reasons but rather to avoid obscure bugs which when they manifest do so silently. Regan
Aug 02 2007
Steve Schveighoffer wrote:Hm.. for some reason, I thought this would appear under the original thread. In any case here is the missing quote from above:The web interface has ... 'issues' on occasion. I use Thunderbird myself. Opera is also good. Regan
Aug 03 2007
Steve Schveighoffer wrote:Regan Heath Wrote:But, that's exactly the problem. In fact example 2 shows that we currently have the undesirable and buggy behaviour in the current D implementation, eg. import std.stdio; class B { long x; void set(long i) { writefln("B::set(long)"); x = i; } void set(int i) { writefln("B::set(int)"); x = i; } long squareIt() { writefln("B::squareit()"); return x * x; } } class D : B { long square; void set(long i) { writefln("D::set(long)"); B.set(i); square = x * x; } long squareIt() { writefln("D::squareit()"); return square; } } long foo(B b) { b.set(3); return b.squareIt(); } void main() { writefln(foo(new D)); } Output: B::set(int) D::squareit() 0 In the above example the object is actually of type 'D' but the method is called from a reference to a 'B'. The result is a call to B::set(int), instead of D::set(long) and then D::squareit() which fails utterly. This is an 'obscure' bug because it is happening silently. It seems example 2 either no longer behaves as it did when it was first posted (D has changed) or it was never a correct example for the problem.So, as you can see it's not for performance reasons but rather to avoid obscure bugs which when they manifest do so silently.My argument is not against this, as you will note that in these examples, both base and derived classes can match the call by some sort of implicit conversion. In the case where both the base class and the derived class match the call, I could care less if the derived class was called instead of the base class.I am proposing a change of behavior when the derived class CANNOT match the call. At that point the compiler errors out instead of examining the base classes. When the coder writes code where there is only one match, and that match is in a base class, it makes perfect sense that the coder explicitly wants to call the base class' method. I see no reason to force the coder to explicitly call for the base class' method when there is no alternative in the derived class, the code is unambiguous.I see the distinction, and it is the case with your original example as you wanted it to call select() from the base class. Interestingly, if the derived class "EpollSelector" had to conform to the interface "ISelector" that the base class "AbstractSelector" implements then the author of "EpollSelector" would have noticed and added the 'alias' and this would never have come up. This change you suggest does violate D's current 'simple' lookup rules and go against the original reasoning of: "His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it." So, if you want to see this change you're going to have to post something convincing addressing this, probably with some real-life examples.I'd bet we'd see 0 obscure bugs if this change was made ;) As I've mentioned, I've already seen one obscure bug caused by the current implementation...Not to nit pick but is the bug you're referring to the problem you had with Tango's tango.io.selector.EPollSelector? If so I wouldn't say that bug was 'oscure' because presumably it gave you an error on compile, as opposed to silently failing on some customers system which is what happens in the two examples given by Walter (including example 2 above which fails in D today). Regan
Aug 03 2007
Regan Heath Wrote:Steve Schveighoffer wrote: > My argument is not against this, as you will note that in these > examples, both base and derived classes can match the call by some > sort of implicit conversion. In the case where both the base class > and the derived class match the call, I could care less if the > derived class was called instead of the base class. But, that's exactly the problem. In fact example 2 shows that we currently have the undesirable and buggy behaviour in the current D implementation, eg.Oh, I understand what you are saying. However, undesirable behavior is unavoidable no matter which way we go. It all depends on what you expect and what the user of the class expects. My thought is that you have to pick one way, and just tell people that's the way it is and they have to live with it. I'm speaking only of the case where both base and derived class can match the call exactly or through some implicit conversion. On second thought, you could pick a third option for behavior, and say that if the derived class has no explicit match, but has implicit matches, and the base class has explicit or implicit matches, the compiler should error out saying it doesn't know which one you intended. That would mean even less obscure bugs, forcing the user of the class to pick the one they want by explicitly casting the arguments or the object.I see the distinction, and it is the case with your original example as you wanted it to call select() from the base class. Interestingly, if the derived class "EpollSelector" had to conform to the interface "ISelector" that the base class "AbstractSelector" implements then the author of "EpollSelector" would have noticed and added the 'alias' and this would never have come up.One of the problems is that you would most likely re-use the unit test code for AbstractSelector, which means you would cast the EPollSelector instance to the AbstractSelector test code, hiding the actual problem. The only way to find this is for the author to write test code that specifically uses the derived class to call the base class' functions that he expects to still exist. I think this is an unnecessary requirement for finding this problem, especially when there is no reason to want it the other way.This change you suggest does violate D's current 'simple' lookup rules and go against the original reasoning of: "His argument is that one can easilly miss an overload of f() somewhere in a complex class heirarchy, and argues that one should not need to understand everything about a class heirarchy in order to derive from it."So by this argument, Walter is saying that the desired behavior of the author is to only make available the overloaded methods that the derived class overrides? I totally disagree. Why would an author want to do this? If it is to hide the other overloads, he has failed, because the user of the class can just cast to a base class in order to call the method they want. If the author desires to hide this from the user of the class, then his class can be used in ways he didn't intend. An author who overrides a class ignorant of all the functionality the class provides is IMHO a poor coder. Even with your example, with the "squareIt" bug, I would never accept code like that as correct. The argument above seems to be defending that type of behavior as desired. In my opinion, a class should behave the same no matter how you look at it, i.e. how you cast it.So, if you want to see this change you're going to have to post something convincing addressing this, probably with some real-life examples.I've posted one. Evidently others have had similar problems. I'm not sure I need any more examples, I think the fundamental argument against is flawed, at least in this specific case where the base class can be used but the derived class cannot. I'm also leaning towards my further argument that the compiler should error out if the desired behavior of the coder is not obvious.> I'd bet we'd see 0 obscure bugs if this change was made ;) As I've > mentioned, I've already seen one obscure bug caused by the current > implementation... Not to nit pick but is the bug you're referring to the problem you had with Tango's tango.io.selector.EPollSelector? If so I wouldn't say that bug was 'oscure' because presumably it gave you an error on compile, as opposed to silently failing on some customers system which is what happens in the two examples given by Walter (including example 2 above which fails in D today).It took me several hours of poking to figure out why the selector thing didn't compile, then several hours of writing test code to demonstrate the issue, researching whether this was desired behavior, and even then, I couldn't find any definite answer in the D spec, which led me to post this thread. I'd say that's a pretty obscure problem :) Not to mention that it's not MY bug. The author probably still doesn't know there is a problem, so from his point of view, the bug is still at large without him noticing. -Steve
Aug 03 2007
Steve Schveighoffer wrote:Regan Heath Wrote:Which one saves more work? I think the common case is to want all members of the base class in the derived class. Perhaps the problem is not having a way to hide methods from a base class? Except, as you point out, you can always cast to the base class and retrieve those methods. The Java version seems simpler to use and understand. Perhaps this is a place where compiler warnings are appropriate -- it's certainly not an error to override only one overload of a function, but it's a source of potential bugs no matter how you cut it. Though Walter is quite opposed to compiler warnings, and I appreciate the cleanness of DMD's output.Steve Schveighoffer wrote: > My argument is not against this, as you will note that in these > examples, both base and derived classes can match the call by some > sort of implicit conversion. In the case where both the base class > and the derived class match the call, I could care less if the > derived class was called instead of the base class. But, that's exactly the problem. In fact example 2 shows that we currently have the undesirable and buggy behaviour in the current D implementation, eg.Oh, I understand what you are saying. However, undesirable behavior is unavoidable no matter which way we go. It all depends on what you expect and what the user of the class expects. My thought is that you have to pick one way, and just tell people that's the way it is and they have to live with it. I'm speaking only of the case where both base and derived class can match the call exactly or through some implicit conversion.On second thought, you could pick a third option for behavior, and say that if the derived class has no explicit match, but has implicit matches, and the base class has explicit or implicit matches, the compiler should error out saying it doesn't know which one you intended. That would mean even less obscure bugs, forcing the user of the class to pick the one they want by explicitly casting the arguments or the object.True. But the original problem would still exist, unless D's behavior changed to Java's. -cbw
Aug 04 2007
Regan Heath wrote:In fact example 2 shows that we currently have the undesirable and buggy behaviour in the current D implementation, eg. import std.stdio; class B { long x; void set(long i) { writefln("B::set(long)"); x = i; } void set(int i) { writefln("B::set(int)"); x = i; } long squareIt() { writefln("B::squareit()"); return x * x; } } class D : B { long square; void set(long i) { writefln("D::set(long)"); B.set(i); square = x * x; } long squareIt() { writefln("D::squareit()"); return square; } } long foo(B b) { b.set(3); return b.squareIt(); } void main() { writefln(foo(new D)); } Output: B::set(int) D::squareit() 0 In the above example the object is actually of type 'D' but the method is called from a reference to a 'B'. The result is a call to B::set(int), instead of D::set(long) and then D::squareit() which fails utterly. This is an 'obscure' bug because it is happening silently.I agree that this example is a problem. There's no way to detect it at compile time, so it should throw a runtime exception. The way to accomplish that is to stuff D's vtbl[] entry for B.set(int) with a dummy function that throws the exception.
Aug 03 2007
Walter Bright wrote:Regan Heath wrote:Certain C++ compilers have done something like this in the past, if I remember correctly. I'd have to do some googling to remember the details, but I'm sure it was either the MS or Sun compiler. SeanIn fact example 2 shows that we currently have the undesirable and buggy behaviour in the current D implementation, eg. import std.stdio; class B { long x; void set(long i) { writefln("B::set(long)"); x = i; } void set(int i) { writefln("B::set(int)"); x = i; } long squareIt() { writefln("B::squareit()"); return x * x; } } class D : B { long square; void set(long i) { writefln("D::set(long)"); B.set(i); square = x * x; } long squareIt() { writefln("D::squareit()"); return square; } } long foo(B b) { b.set(3); return b.squareIt(); } void main() { writefln(foo(new D)); } Output: B::set(int) D::squareit() 0 In the above example the object is actually of type 'D' but the method is called from a reference to a 'B'. The result is a call to B::set(int), instead of D::set(long) and then D::squareit() which fails utterly. This is an 'obscure' bug because it is happening silently.I agree that this example is a problem. There's no way to detect it at compile time, so it should throw a runtime exception. The way to accomplish that is to stuff D's vtbl[] entry for B.set(int) with a dummy function that throws the exception.
Aug 03 2007
Sean Kelly wrote:Walter Bright wrote:I didn't know that. This particular problem may not come up so often with C++ because functions are not virtual by default - but not virtual by default produces its own set of very hard to find bugs that I've wasted many hours tracking down.I agree that this example is a problem. There's no way to detect it at compile time, so it should throw a runtime exception. The way to accomplish that is to stuff D's vtbl[] entry for B.set(int) with a dummy function that throws the exception.Certain C++ compilers have done something like this in the past, if I remember correctly. I'd have to do some googling to remember the details, but I'm sure it was either the MS or Sun compiler.
Aug 03 2007
Walter Bright wrote:Sean Kelly wrote:I stumbled across the information while trying to diagnose a bizarre linker error. It turned out that the function that couldn't be found was the dummy routine. And that reminds me which compiler it is--it's the MS one. Their explanation made it seem like the situation the routine was there for wasn't likely to ever actually happen and I regarded it as a curiosity at the time. It's kind of neat to see the idea resurface here though :-) SeanWalter Bright wrote:I didn't know that. This particular problem may not come up so often with C++ because functions are not virtual by default - but not virtual by default produces its own set of very hard to find bugs that I've wasted many hours tracking down.I agree that this example is a problem. There's no way to detect it at compile time, so it should throw a runtime exception. The way to accomplish that is to stuff D's vtbl[] entry for B.set(int) with a dummy function that throws the exception.Certain C++ compilers have done something like this in the past, if I remember correctly. I'd have to do some googling to remember the details, but I'm sure it was either the MS or Sun compiler.
Aug 03 2007
Walter Bright wrote:Regan Heath wrote:There is a problem the moment the D subclass doesn't override all overloads, so that can be detected at compile time and an error issued right there. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#DIn fact example 2 shows that we currently have the undesirable and buggy behaviour in the current D implementation, eg. import std.stdio; class B { long x; void set(long i) { writefln("B::set(long)"); x = i; } void set(int i) { writefln("B::set(int)"); x = i; } long squareIt() { writefln("B::squareit()"); return x * x; } } class D : B { long square; void set(long i) { writefln("D::set(long)"); B.set(i); square = x * x; } long squareIt() { writefln("D::squareit()"); return square; } } long foo(B b) { b.set(3); return b.squareIt(); } void main() { writefln(foo(new D)); } Output: B::set(int) D::squareit() 0 In the above example the object is actually of type 'D' but the method is called from a reference to a 'B'. The result is a call to B::set(int), instead of D::set(long) and then D::squareit() which fails utterly. This is an 'obscure' bug because it is happening silently.I agree that this example is a problem. There's no way to detect it at compile time, so it should throw a runtime exception. The way to accomplish that is to stuff D's vtbl[] entry for B.set(int) with a dummy function that throws the exception.
Aug 04 2007
Bruno Medeiros wrote:Walter Bright wrote:I'm not sure that's the right thing to do.I agree that this example is a problem. There's no way to detect it at compile time, so it should throw a runtime exception. The way to accomplish that is to stuff D's vtbl[] entry for B.set(int) with a dummy function that throws the exception.There is a problem the moment the D subclass doesn't override all overloads, so that can be detected at compile time and an error issued right there.
Aug 04 2007
Walter Bright wrote:Bruno Medeiros wrote:In general, overloading virtual functions in the first place is a mistake: in practice it means that in order for a derived class to implement consistent behavior it must override all of them. A hard error on overloading virtuals would be fair; the fix is to have non-virtual overloads dispatching to a single virtual which has the most general form, and provides a single point of customization for derived classes. This might not sit well with those who think that virtual is a good default though. I'm not one of them; I've never seen problems caused by non-virtual as default, but plenty caused by virtual as default (mostly relating to programmers not thinking deeply enough about what contracts mean for virtual functions, where it's necessary to specify both what the default implementation guarantees, and what derived class implementations must guarantee, including which other functions on the same object can legitimately be called from overrides). -- JamesWalter Bright wrote:I'm not sure that's the right thing to do.I agree that this example is a problem. There's no way to detect it at compile time, so it should throw a runtime exception. The way to accomplish that is to stuff D's vtbl[] entry for B.set(int) with a dummy function that throws the exception.There is a problem the moment the D subclass doesn't override all overloads, so that can be detected at compile time and an error issued right there.
Aug 04 2007
James Dennett wrote:This might not sit well with those who think that virtual is a good default though. I'm not one of them; I've never seen problems caused by non-virtual as default,I, for one, have had several severe and very hard to find bugs caused by non-virtual as the default. It's something that defies visual inspection looking for them.
Aug 04 2007
Walter Bright wrote:James Dennett wrote:Is there some way currently to force methods to be non-virtual? Does 'final' do that? --bbThis might not sit well with those who think that virtual is a good default though. I'm not one of them; I've never seen problems caused by non-virtual as default,I, for one, have had several severe and very hard to find bugs caused by non-virtual as the default. It's something that defies visual inspection looking for them.
Aug 07 2007
Bill Baxter wrote:Walter Bright wrote:Yes, that's what 'final' is for. In my D code, it's rare that I'll write a function that isn't final. SeanJames Dennett wrote:Is there some way currently to force methods to be non-virtual? Does 'final' do that?This might not sit well with those who think that virtual is a good default though. I'm not one of them; I've never seen problems caused by non-virtual as default,I, for one, have had several severe and very hard to find bugs caused by non-virtual as the default. It's something that defies visual inspection looking for them.
Aug 07 2007
On Tue, 07 Aug 2007 18:03:43 -0700, Sean Kelly wrote:Bill Baxter wrote:I'm about to make a fool of myself again, but in my defence, I'm not a frequent O-O practitioner. So can someone remind me ... "virtual" means that a member can possibly be overridden by a derived class? "pure virtual" means that a member *must* be overridden by a derived class? "final" means that a member can not be overridden by a derived class? And by default members are "virtual" in D. -- Derek (skype: derek.j.parnell) Melbourne, Australia 8/08/2007 11:19:00 AMWalter Bright wrote:Yes, that's what 'final' is for. In my D code, it's rare that I'll write a function that isn't final.James Dennett wrote:Is there some way currently to force methods to be non-virtual? Does 'final' do that?This might not sit well with those who think that virtual is a good default though. I'm not one of them; I've never seen problems caused by non-virtual as default,I, for one, have had several severe and very hard to find bugs caused by non-virtual as the default. It's something that defies visual inspection looking for them.
Aug 07 2007
Sean Kelly wrote:Bill Baxter wrote:So does that mean you can't do non-virtual overloads in D? I.e. have a situation where: x.foo() and (cast(Base)x).foo() call different member functions? If not, probably it's good riddance, but I do vaguely recall having a use for it once in C++. --bbWalter Bright wrote:Yes, that's what 'final' is for. In my D code, it's rare that I'll write a function that isn't final. SeanJames Dennett wrote:Is there some way currently to force methods to be non-virtual? Does 'final' do that?This might not sit well with those who think that virtual is a good default though. I'm not one of them; I've never seen problems caused by non-virtual as default,I, for one, have had several severe and very hard to find bugs caused by non-virtual as the default. It's something that defies visual inspection looking for them.
Aug 07 2007
Bill Baxter wrote:So does that mean you can't do non-virtual overloads in D?Right, you cannot.I.e. have a situation where: x.foo() and (cast(Base)x).foo() call different member functions? If not, probably it's good riddance, but I do vaguely recall having a use for it once in C++.Good riddance <g>.
Aug 07 2007
"Walter Bright" <newshound1 digitalmars.com> wrote in message news:f9bcvt$1vqb$1 digitalmars.com...Bill Baxter wrote:This is kind of cheating, but it does show a possibility where the lines of code you give do call 2 different functions :) Of course, it's completely off topic, but a fun exercise. import tango.io.Stdout; class Base { void foo(char[] x = "blah") { Stdout("in Base::foo").newline; } } class Derived : Base { void foo(int x = 5) { Stdout("in Derived::foo").newline; } } int main(char[][] args) { Derived x = new Derived; x.foo(); (cast(Base)x).foo(); return 0; } output: in Derived::foo in Base::foo -SteveSo does that mean you can't do non-virtual overloads in D?Right, you cannot.I.e. have a situation where: x.foo() and (cast(Base)x).foo() call different member functions?
Aug 08 2007