www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Overloading/Inheritance issue

reply Steve Schveighoffer <schveiguy yahoo.com> writes:
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
next sibling parent reply Derek Parnell <derek psych.ward> writes:
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
next sibling parent reply Steve Schveighoffer <schveiguy yahoo.com> writes:
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
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
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
prev sibling next sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
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
next sibling parent reply Aarti_pl <aarti interia.pl> writes:
Derek Parnell pisze:
 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.
... 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_pl
Aug 01 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent Marcin Kuszczak <aarti interia.pl> writes:
Walter Bright wrote:

 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.
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/ -------------------------------------
Aug 04 2007
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Derek Parnell wrote:
 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.
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. Regan
Aug 02 2007
next sibling parent Aarti_pl <aarti interia.pl> writes:
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.
 
 Regan
ups... 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
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
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
parent reply Regan Heath <regan netmail.co.nz> writes:
Derek Parnell wrote:
 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.
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. Regan
Aug 02 2007
next sibling parent reply Steve Schveighoffer <schveiguy yahoo.com> writes:
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
parent reply Regan Heath <regan netmail.co.nz> writes:
Steve Schveighoffer wrote:
 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 suspect most people think C++ does it the way you suggested.
 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
 hit
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 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
next sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Regan Heath wrote:
 Steve Schveighoffer wrote:
  > 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.
C++ has "using Base::method;" to replace alias for this particular case.
Aug 02 2007
prev sibling next sibling parent Sean Kelly <sean f4.ca> writes:
Regan Heath wrote:
 Steve Schveighoffer wrote:
 Regan Heath Wrote:
> 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.
'using' serves this purpose in C++.
 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
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.
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. Sean
Aug 02 2007
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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
prev sibling parent Derek Parnell <derek nomail.afraid.org> writes:
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
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
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
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Steve Schveighoffer wrote:
 Everyone who used the Y derivative suddenly finds their code doesn't 
compile. 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
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruno Medeiros wrote:
 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.
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.
Aug 04 2007
next sibling parent reply Jeff Nowakowski <jeff dilacero.org> writes:
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
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
next sibling parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 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.
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). Sean
Aug 05 2007
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Walter Bright wrote:
 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.
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 :) -- Daniel
Aug 05 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Daniel Keep wrote:
 Cool; I'm hard core!  Wait... does it still count if I'm using vim with
 a gui? :P
A gui? I don't know, man :-)
Aug 07 2007
prev sibling parent reply Derek Parnell <derek psyc.ward> writes:
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
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 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 :)
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.
Aug 07 2007
next sibling parent Jascha Wetzel <"[firstname]" mainia.de> writes:
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
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Derek Parnell wrote:
 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 :)
I'm a command line interface curmudgeon. The reason is simple- it's more productive, since:
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.
 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
next sibling parent BCS <ao pathlink.com> writes:
Reply to Bruno,

 Walter Bright wrote:
 
 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.
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.
 3) Pipes are cool. Can't do the equivalent with a gui.
 
Shell-specific point.
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.
 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.
I generally don't even bother with a shell script, just use a pipeline ending in " | bash"
 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
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
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.
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:
 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. 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#D
Aug 09 2007
parent reply Christopher Wright <dhasenan gmail.com> writes:
Bruno Medeiros wrote:
 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.
Maybe for rename refactoring. Most other refactoring operations need an actual semantic tool, you can't do it with CLI text processing tools.
Yes, you can. It's just that tools for this are not currently written.
 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
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Christopher Wright wrote:
 Bruno Medeiros wrote:
 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.
Maybe for rename refactoring. Most other refactoring operations need an actual semantic tool, you can't do it with CLI text processing tools.
Yes, you can. It's just that tools for this are not currently written.
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.).
 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:
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#D
Aug 11 2007
prev sibling parent Ary Manzana <ary esperanto.org.ar> writes:
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:
 FWIW, a lot of hard core programmers still use vi.
And I think that some are even still chipping away at granite blocks :)
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.
Aug 07 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Bruno Medeiros wrote:
 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.
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.
 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
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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).
Auditing, as in someone review someone else's code?
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.
 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.
 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.
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.
Aug 05 2007
parent reply Derek Parnell <derek psyc.ward> writes:
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
next sibling parent Sean Kelly <sean f4.ca> writes:
Derek Parnell wrote:
 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.
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. Sean
Aug 06 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent reply Jascha Wetzel <"[firstname]" mainia.de> writes:
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
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jascha Wetzel wrote:
 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 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.
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).
Aug 07 2007
next sibling parent Jascha Wetzel <"[firstname]" mainia.de> writes:
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
prev sibling next sibling parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Walter Bright wrote:
 Jascha Wetzel wrote:
 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 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.
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).
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-Sauls
Aug 07 2007
prev sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
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:
 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.
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).
Aug 09 2007
prev sibling next sibling parent Regan Heath <regan netmail.co.nz> writes:
Derek Parnell wrote:
 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.
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. Regan
Aug 07 2007
prev sibling parent Jascha Wetzel <"[firstname]" mainia.de> writes:
Derek Parnell 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.
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 ;)
Aug 07 2007
prev sibling next sibling parent reply Ary Manzana <ary esperanto.org.ar> writes:
Walter Bright escribió:
 Bruno Medeiros wrote:
 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.
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.
I think it's the opposite. In every workplace I've been, people can't live with a decent IDE.
Aug 05 2007
parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Ary Manzana wrote:
 Walter Bright escribió:
 Bruno Medeiros wrote:
 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.
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.
I think it's the opposite. In every workplace I've been, people can't live with a decent IDE.
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-Sauls
Aug 05 2007
parent reply BCS <ao pathlink.com> writes:
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
parent Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
BCS wrote:
 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.
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-Sauls
Aug 05 2007
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to Walter,

 Bruno Medeiros wrote:
 
 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.
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.
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.
Aug 05 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Bruno Medeiros wrote:
 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.
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.
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. --bb
Aug 05 2007
parent Jascha Wetzel <"[firstname]" mainia.de> writes:
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
prev sibling parent Charles D Hixson <charleshixsn earthlink.net> writes:
Derek Parnell wrote:
 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.
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.)
Aug 04 2007
prev sibling next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
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?
 
 -Steve
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. -- Chris Nicholson-Sauls
Aug 01 2007
next sibling parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Chris Nicholson-Sauls wrote:
 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?

 -Steve
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. -- Chris Nicholson-Sauls
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.org
Aug 01 2007
next sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Kirk McDonald wrote:
 Chris Nicholson-Sauls wrote:
 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?

 -Steve
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. -- Chris Nicholson-Sauls
That's not a workaround, nor is this considered a problem. This is the intended behavior.
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-Sauls
Aug 01 2007
parent Steve Schveighoffer <schveiguy yahoo.com> writes:
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
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Kirk McDonald wrote:
   alias super.foo foo;
That's not a workaround, nor is this considered a problem. This is the intended behavior.
Yes, and it took some extra effort to make sure it worked that way! In other words, it is not accidental or incidental behavior.
Aug 03 2007
parent Charles D Hixson <charleshixsn earthlink.net> writes:
Walter Bright wrote:
 Kirk McDonald wrote:
   alias super.foo foo;
That's not a workaround, nor is this considered a problem. This is the intended behavior.
Yes, and it took some extra effort to make sure it worked that way! In other words, it is not accidental or incidental behavior.
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.
Aug 05 2007
prev sibling parent BCS <ao pathlink.com> writes:
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
prev sibling parent reply Steve Schveighoffer <schveiguy yahoo.com> writes:
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.
 
 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
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
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... -Steve
Aug 02 2007
next sibling parent reply Steve Schveighoffer <schveiguy yahoo.com> writes:
Steve Schveighoffer Wrote:

 Regan Heath Wrote:
 
 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
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>
Aug 02 2007
parent Regan Heath <regan netmail.co.nz> writes:
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
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Steve Schveighoffer wrote:
 Regan Heath Wrote:
 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.
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.
 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
next sibling parent reply Steve Schveighoffer <schveiguy yahoo.com> writes:
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
parent Christopher Wright <dhasenan gmail.com> writes:
Steve Schveighoffer wrote:
 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.
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.
 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
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 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.
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. Sean
Aug 03 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 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.
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.
Aug 03 2007
parent Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 Walter Bright wrote:
 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.
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 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 :-) Sean
Aug 03 2007
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 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.
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#D
Aug 04 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruno Medeiros wrote:
 Walter Bright wrote:
 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.
I'm not sure that's the right thing to do.
Aug 04 2007
parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Bruno Medeiros wrote:
 Walter Bright wrote:
 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.
I'm not sure that's the right thing to do.
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). -- James
Aug 04 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 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.
Is there some way currently to force methods to be non-virtual? Does 'final' do that? --bb
Aug 07 2007
parent reply Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 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.
Is there some way currently to force methods to be non-virtual? Does 'final' do that?
Yes, that's what 'final' is for. In my D code, it's rare that I'll write a function that isn't final. Sean
Aug 07 2007
next sibling parent Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 07 Aug 2007 18:03:43 -0700, Sean Kelly wrote:

 Bill Baxter wrote:
 Walter Bright wrote:
 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.
Is there some way currently to force methods to be non-virtual? Does 'final' do that?
Yes, that's what 'final' is for. In my D code, it's rare that I'll write a function that isn't final.
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 AM
Aug 07 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Bill Baxter wrote:
 Walter Bright wrote:
 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.
Is there some way currently to force methods to be non-virtual? Does 'final' do that?
Yes, that's what 'final' is for. In my D code, it's rare that I'll write a function that isn't final. Sean
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++. --bb
Aug 07 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message 
news:f9bcvt$1vqb$1 digitalmars.com...
 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?
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 -Steve
Aug 08 2007