|
Archives
D Programming
D
D.gnu
digitalmars.D
digitalmars.D.bugs
digitalmars.D.dtl
digitalmars.D.dwt
digitalmars.D.announce
digitalmars.D.learn
digitalmars.D.debugger
C/C++ Programming
c++
c++.announce
c++.atl
c++.beta
c++.chat
c++.command-line
c++.dos
c++.dos.16-bits
c++.dos.32-bits
c++.idde
c++.mfc
c++.rtl
c++.stl
c++.stl.hp
c++.stl.port
c++.stl.sgi
c++.stlsoft
c++.windows
c++.windows.16-bits
c++.windows.32-bits
c++.wxwindows
digitalmars.empire
digitalmars.DMDScript
electronics
|
digitalmars.D - Hijacking
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
I want to continue a bit in the Overloading/Inheritance thread which is
getting a little long, on a closely related issue which I find to be
important, but so rarely referred to that I had to come up with a name
for it - hijacking.
Hijacking is when code A depends on B, and then when seemingly unrelated
code C is modified, then A's behavior silently changes. Here's an
example from C++:
--A.h--
void foo(long i);
--B.c--
#include "A.h"
#include "C.h"
...
foo(3); // calls A's foo(long)
--------
Let's say A.h and C.h are developed by different people. In C.h, the
developer adds:
--C.h--
void foo(int i);
-----
This does something completely different from A.h's foo(long), because
they just happened to share the same name. Now, when B.c is recompiled,
it's call to foo is silently *hijacked* by C.h's foo.
Because of C++'s overloading rules and lack of modularity, there's no
way to programmatically defend against this. Instead, one has to rely on
coding conventions (such as using a unique package prefix name on all
symbols, like A_foo() and C_foo()).
So how does this relate to the overloading/inheritance issues? Consider:
----A.d----
class A
{
...
}
-----B.d----
import A;
class B : A
{
void foo(long);
}
...
void bar(B b)
{
b.foo(3); // calls B.foo(long)
}
----
Let's say A.d comes from some third party library. Now the developer of
A decides to add some functionality to class A, and adds the member
function foo(int):
----A.d---
class A
{
void foo(int);
}
------------
Now our hapless B programmer has his calls to B.foo(long) silently
hijacked to A.foo(int) (under Java rules). I don't see any reasonable
way for B to defend against this. Certainly, developer A doesn't have
any idea who is deriving from A (and that's the point of polymorphism) -
but should he be disallowed from adding *any* method names? And the
hapless B developer, he wrote class B years ago and no longer quite
remembers how it works, he just recompiles it and now it silently fails.
So, this is one case where I feel C++ got it right, and Java didn't.
(P.S. It's not a compiler implementation issue, nor is it a runtime
performance issue.)
↑ ↓ ← → Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread which is
getting a little long, on a closely related issue which I find to be
important, but so rarely referred to that I had to come up with a name
for it - hijacking.
Hijacking is when code A depends on B, and then when seemingly unrelated
code C is modified, then A's behavior silently changes. Here's an
example from C++:
--A.h--
void foo(long i);
--B.c--
#include "A.h"
#include "C.h"
...
foo(3); // calls A's foo(long)
--------
Let's say A.h and C.h are developed by different people. In C.h, the
developer adds:
--C.h--
void foo(int i);
-----
This does something completely different from A.h's foo(long), because
they just happened to share the same name. Now, when B.c is recompiled,
it's call to foo is silently *hijacked* by C.h's foo.
Because of C++'s overloading rules and lack of modularity, there's no
way to programmatically defend against this. Instead, one has to rely on
coding conventions (such as using a unique package prefix name on all
symbols, like A_foo() and C_foo()).
Thank the heavens for D's modules and FQN's. :) You've got that
situation covered.
So how does this relate to the overloading/inheritance issues? Consider:
----A.d----
class A
{
...
}
-----B.d----
import A;
class B : A
{
void foo(long);
}
...
void bar(B b)
{
b.foo(3); // calls B.foo(long)
}
----
Let's say A.d comes from some third party library. Now the developer of
A decides to add some functionality to class A, and adds the member
function foo(int):
----A.d---
class A
{
void foo(int);
}
------------
Now our hapless B programmer has his calls to B.foo(long) silently
hijacked to A.foo(int) (under Java rules). I don't see any reasonable
way for B to defend against this. Certainly, developer A doesn't have
any idea who is deriving from A (and that's the point of polymorphism) -
but should he be disallowed from adding *any* method names? And the
hapless B developer, he wrote class B years ago and no longer quite
remembers how it works, he just recompiles it and now it silently fails.
So, this is one case where I feel C++ got it right, and Java didn't.
(P.S. It's not a compiler implementation issue, nor is it a runtime
performance issue.)
Alright. I can't argue against that one little bit. I will mention for
completeness the option of using an L suffix to explicitly state you
want a long... but then there's no analog for explicit int/short/byte,
so the problem still exists in any case other than ambiguity against
long. (Yes there's cast()... but ew.)
-- Chris Nicholson-Sauls
↑ ↓ ← → kris <fu bar.org> writes:
Walter:
There's a related problem where a public method is added to a base-class
A (as in your example) but where the signature is *exactly* that of one
existing in derived class B. If A actually calls that new method
internally, "bad things"tm will almost certainly happen, since B never
intended to effectively override the newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by the
compiler. The request was first made two or three years back, and once
or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this related
type of hijacking and avoid such nasty surprises.
Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread which is
getting a little long, on a closely related issue which I find to be
important, but so rarely referred to that I had to come up with a name
for it - hijacking.
Hijacking is when code A depends on B, and then when seemingly unrelated
code C is modified, then A's behavior silently changes. Here's an
example from C++:
--A.h--
void foo(long i);
--B.c--
#include "A.h"
#include "C.h"
....
foo(3); // calls A's foo(long)
--------
Let's say A.h and C.h are developed by different people. In C.h, the
developer adds:
--C.h--
void foo(int i);
-----
This does something completely different from A.h's foo(long), because
they just happened to share the same name. Now, when B.c is recompiled,
it's call to foo is silently *hijacked* by C.h's foo.
Because of C++'s overloading rules and lack of modularity, there's no
way to programmatically defend against this. Instead, one has to rely on
coding conventions (such as using a unique package prefix name on all
symbols, like A_foo() and C_foo()).
So how does this relate to the overloading/inheritance issues? Consider:
----A.d----
class A
{
...
}
-----B.d----
import A;
class B : A
{
void foo(long);
}
....
void bar(B b)
{
b.foo(3); // calls B.foo(long)
}
----
Let's say A.d comes from some third party library. Now the developer of
A decides to add some functionality to class A, and adds the member
function foo(int):
----A.d---
class A
{
void foo(int);
}
------------
Now our hapless B programmer has his calls to B.foo(long) silently
hijacked to A.foo(int) (under Java rules). I don't see any reasonable
way for B to defend against this. Certainly, developer A doesn't have
any idea who is deriving from A (and that's the point of polymorphism) -
but should he be disallowed from adding *any* method names? And the
hapless B developer, he wrote class B years ago and no longer quite
remembers how it works, he just recompiles it and now it silently fails.
So, this is one case where I feel C++ got it right, and Java didn't.
(P.S. It's not a compiler implementation issue, nor is it a runtime
performance issue.)
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
kris wrote:
There's a related problem where a public method is added to a base-class
A (as in your example) but where the signature is *exactly* that of one
existing in derived class B. If A actually calls that new method
internally, "bad things"tm will almost certainly happen, since B never
intended to effectively override the newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by the
compiler. The request was first made two or three years back, and once
or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this related
type of hijacking and avoid such nasty surprises.
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Mitigating factors are private and final methods cannot be overridden.
↑ ↓ ← → Mike Capp <mike.capp gmail.com> writes:
Walter Bright Wrote:
[re:making 'override' mandatory]
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
C# has this rule, and based on 3-4 years of using it I'd say it's fine. No
annoyance in the general case; in fact it normally _saves_ typing since you can
just type "override" and ReSharper (a refactoring plugin for VS) offers
completions based on virtuals in the base class. And it's been a useful warning
of unintended overrides many a time.
The one case where the rule does feel annoying is when you have an interface,
an abstract base class implementing that interface with pure virtuals while
providing common functionality, and a concrete subclass. In D you'd probably
mixin the base stuff, so this case wouldn't arise.
↑ ↓ ← → "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Walter Bright" <newshound1 digitalmars.com> wrote in message
news:f95leh$hn4$1 digitalmars.com...
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override' keyword.
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
If anything's annoying, it's when that kind of hijacking happens (I've had
it bite me a few times). I _always_ try to put override on every method
that I override, as not only is it self-documenting, but it also traps this
kind of bug.
↑ ↓ ← → kris <fu bar.org> writes:
Walter Bright wrote:
kris wrote:
There's a related problem where a public method is added to a
base-class A (as in your example) but where the signature is *exactly*
that of one existing in derived class B. If A actually calls that new
method internally, "bad things"tm will almost certainly happen, since
B never intended to effectively override the newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by
the compiler. The request was first made two or three years back, and
once or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this related
type of hijacking and avoid such nasty surprises.
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Let's put it this way: I have a personal and quite open dislike for
exception specifications, yet I try very hard to ensure override is
added in all the right places in D, even though the compiler doesn't
enforce it.
In other words, I go to the trouble of adding override for the benefit
of manual auditing. The compiler ought to be helping me with that task?
- Kris
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
kris wrote:
In other words, I go to the trouble of adding override for the benefit
of manual auditing. The compiler ought to be helping me with that task?
I'll put it in as a warning in the next update, and we can try it out.
↑ ↓ ← → eao197 <eao197 intervale.ru> writes:
On Mon, 06 Aug 2007 03:11:14 +0400, Walter Bright
<newshound1 digitalmars.com> wrote:
kris wrote:
There's a related problem where a public method is added to a
base-class A (as in your example) but where the signature is *exactly*
that of one existing in derived class B. If A actually calls that new
method internally, "bad things"tm will almost certainly happen, since B
never intended to effectively override the newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by
the compiler. The request was first made two or three years back, and
once or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this related
type of hijacking and avoid such nasty surprises.
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Eiffel has same feature for more than 20 years and it really works :)
Each method which derived class wants to redefine must be declared in
special section:
class
BASE
feature
a is ... end
b is ... end
c is ... end
end
class
DERIVED
inherit
BASE
redefine
a, b
end
feature
a is ... end
b is ... end
end
It is error if 'c' is defined in DERIVED without specifying in 'redefine'
section.
In addition, Scala requires to use 'override' keyword and it also works
fine.
--
Regards,
Yauheni Akhotnikau
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
eao197 wrote:
Eiffel has same feature for more than 20 years and it really works :)
Eiffel has many good ideas, but we must be careful thinking of Eiffel as
an endorsement, as it failed to catch on. I think one reason it failed
is because of the syntax.
↑ ↓ ← → eao197 <eao197 intervale.ru> writes:
On Mon, 06 Aug 2007 21:30:43 +0400, Walter Bright =
<newshound1 digitalmars.com> wrote:
eao197 wrote:
Eiffel has same feature for more than 20 years and it really works :)=
Eiffel has many good ideas, but we must be careful thinking of Eiffel =
an endorsement, as it failed to catch on. I think one reason it failed=
is because of the syntax.
No, I don't think so. I spent last 1.5 month to study Eiffel. Eiffel =
syntax isn't a problem (moreover I think Eiffel/Ruby syntax is more =
attractive than old C curly braces tradition).
By main opinion the main Eiffel's problems are:
1. Only one paradim. Only OOP and only by following the Eiffel method.
2. Comand/query separation principle. For example, you can't do somethin=
g =
with object and get the result, as in C++/D:
auto sent_result =3D mail_box.send_message(msg);
In Eiffel send_message must be command, and mail_box must have a query t=
o =
retrive last send result:
local
send_result: SEND_RESULT
do
mail_box.send_message (msg)
send_result :=3D mail_box.last_send_result
...
end
3. Very different exception mechanism. For example see my question about=
=
resouce cleanup in the case of exception: =
http://tech.groups.yahoo.com/group/eiffel_software/message/11062
But 'DesignByContract' and inheritance (and various forms of inheritance=
=
conflicts resolving) are really great part of Eiffel.
So I vote for mandatory 'override' keyword in D too.
-- =
Regards,
Yauheni Akhotnikau
↑ ↓ ← → Charles D Hixson <charleshixsn earthlink.net> writes:
Walter Bright wrote:
eao197 wrote:
Eiffel has same feature for more than 20 years and it really works :)
Eiffel has many good ideas, but we must be careful thinking of Eiffel as
an endorsement, as it failed to catch on. I think one reason it failed
is because of the syntax.
the personality and resultant policies of the designer. It
took a long time for SmallEiffel to be released...and it's
policies aren't that friendly, either, even though it IS GPL
software.
OTOH, IMNSHO, Eiffel did make a few serious mistakes. One of
them was in forbidding two different functions (essentially
the same) to be distinguished by their argument list, but not
by their name. BAD decision.
So you're right when you say "be careful", but don't attribute
most of the failure of Eiffel to it's design. Other factors
predominated.
Note that D already had three branches DMD1, DMD2, and gdc.
And that they share a lot of the same code (which keeps
duplicate effort to a minimum).
If you want to copy organization policies, copy from Python,
but Eiffel had very many good ideas that a compiler language
could benefit from. (I'm not as convinced by modern versions
of Eiffel as I was by the earlier ones...but then I've
basically stopped following it, because it kept breaking all
of it's libraries with each new release. And it never did
become as easy to link to C as D already is. [Now if only
there was a decent way to manage C++ linkage.])
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
Charles D Hixson wrote:
So you're right when you say "be careful", but don't attribute most of
the failure of Eiffel to it's design. Other factors predominated.
For a complex thing like a language, it can be really hard to pin down
why it failed. Ask n different people, and you'll get n different
answers. What turned me off was the syntax. It just means we have to be
extra careful about adopting features from a failed language.
The further a feature is from mainstream convention in at least one
successful language, the more nervous I get about it.
↑ ↓ ← → Regan Heath <regan netmail.co.nz> writes:
Walter Bright wrote:
kris wrote:
There's a related problem where a public method is added to a
base-class A (as in your example) but where the signature is *exactly*
that of one existing in derived class B. If A actually calls that new
method internally, "bad things"tm will almost certainly happen, since
B never intended to effectively override the newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by
the compiler. The request was first made two or three years back, and
once or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this related
type of hijacking and avoid such nasty surprises.
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Mitigating factors are private and final methods cannot be overridden.
My vote was, and still is, for 'override' to be mandatory.
Regan
↑ ↓ ← → Don Clugston <dac nospam.com.au> writes:
Regan Heath wrote:
Walter Bright wrote:
kris wrote:
There's a related problem where a public method is added to a
base-class A (as in your example) but where the signature is
*exactly* that of one existing in derived class B. If A actually
calls that new method internally, "bad things"tm will almost
certainly happen, since B never intended to effectively override the
newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by
the compiler. The request was first made two or three years back, and
once or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this
related type of hijacking and avoid such nasty surprises.
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Mitigating factors are private and final methods cannot be overridden.
My vote was, and still is, for 'override' to be mandatory.
Regan
onerous than putting 'virtual' in front of every function in C++.
↑ ↓ ← → Sean Kelly <sean f4.ca> writes:
Don Clugston wrote:
Me too. Note that since you can use 'override { }' or 'override:', it's
less onerous than putting 'virtual' in front of every function in C++.
How does "override:" work with other properties used as labels? Is it
disabled when public/protected/private is next used in the same way?
Sean
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
How does "override:" work with other properties used as labels? Is it
disabled when public/protected/private is next used in the same way?
It adds to them.
↑ ↓ ← → Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
Sean Kelly wrote:
How does "override:" work with other properties used as labels? Is it
disabled when public/protected/private is next used in the same way?
It adds to them.
So is there anyway to disable "override:" once set?
Sean
↑ ↓ ← → Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Sean Kelly wrote:
Walter Bright wrote:
Sean Kelly wrote:
How does "override:" work with other properties used as labels? Is
it disabled when public/protected/private is next used in the same way?
It adds to them.
So is there anyway to disable "override:" once set?
Sean
Since there is no "not an override" keyword, I would wager there isn't.
(Unless for end of current scope, aye? But even then...)
I don't think we really need a non-override keyword, and I wouldn't want
'final' to be given that effect either ('final override' should be a
valid attribute), or any other existing attributes... so our options are:
#1 - Add some sort of "plain" specifier, or a way to un-set a 'foo:'
attribute. Maybe '!foo:' or similar.
#2 - Make a point of using 'override { ... }' instead.
...I think I prefer #2.
-- Chris Nicholson-Sauls
↑ ↓ ← → Sean Kelly <sean f4.ca> writes:
Chris Nicholson-Sauls wrote:
#2 - Make a point of using 'override { ... }' instead.
...I think I prefer #2.
Same here. Using "override:" doesn't seem feasible in all but the
simplest cases.
Sean
↑ ↓ ← → =?ISO-8859-1?Q?Julio_C=E9sar_Carrascal_Urquijo?= writes:
Chris Nicholson-Sauls wrote:
> I don't think we really need a non-override keyword, and I wouldn't
want
'final' to be given that effect either ('final override' should be a
valid attribute), or any other existing attributes... so our options are:
#1 - Add some sort of "plain" specifier, or a way to un-set a 'foo:'
attribute. Maybe '!foo:' or similar.
#2 - Make a point of using 'override { ... }' instead.
....I think I prefer #2.
-- Chris Nicholson-Sauls
#3 - Use default:
override:
void foo() {}
void bar() {}
default:
void baz() {}
But then again, maybe in this case we should just use override {} or
reorder methods.
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
Walter Bright wrote:
Sean Kelly wrote:
How does "override:" work with other properties used as labels? Is
it disabled when public/protected/private is next used in the same way?
It adds to them.
So is there anyway to disable "override:" once set?
No.
↑ ↓ ← → Frank Benoit <keinfarbton googlemail.com> writes:
Regan Heath schrieb:
My vote was, and still is, for 'override' to be mandatory.
Regan
As said in my posting
http://www.digitalmars.com/d/archives/digitalmars/D/aliasing_base_methods_49572.html#N49577
, i still second that also.
↑ ↓ ← → Robert Fraser <fraserofthenight gmail.com> writes:
Frank Benoit Wrote:
Regan Heath schrieb:
My vote was, and still is, for 'override' to be mandatory.
Regan
As said in my posting
http://www.digitalmars.com/d/archives/digitalmars/D/aliasing_base_methods_49572.html#N49577
, i still second that also.
votes++;
It's really not that hard and will catch a lot of errors. Plus, a good IDE can
insert that stuff automatically.
↑ ↓ ← → Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
I don't think the mainstream opinion in Java is that checked exceptions
are a good ideia.
--
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
Bruno Medeiros wrote:
Walter Bright wrote:
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
I don't think the mainstream opinion in Java is that checked exceptions
are a good ideia.
It's not a great analogy, because there are very good technical
arguments against checked exceptions. But I do remember prominent Java
people promoting checked exceptions as a great idea, while in their own
code they'd subvert them because they were just too annoying.
I also think that mainstream Java has evolved its understanding of
checked exceptions.
↑ ↓ ← → "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
Exception specification *is* a good idea. Although I do hate it - try to
remember what a specific method may throw when you write your code,
especially if the error is ambiguous like "Unhandled exception" it's really
irritating - but I hate unspecified exceptions even more, as the problem
then is to even remember to put in the necessary try-catch-finally blocks.
↑ ↓ ← → "Nick Sabalausky" <a a.a> writes:
"Rioshin an'Harthen" <rharth75 hotmail.com> wrote in message
news:f974u9$9k1$1 digitalmars.com...
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
Exception specification *is* a good idea. Although I do hate it - try to
remember what a specific method may throw when you write your code,
especially if the error is ambiguous like "Unhandled exception" it's
really irritating - but I hate unspecified exceptions even more, as the
problem then is to even remember to put in the necessary try-catch-finally
blocks.
Not to start a big big debate on it, but my own personal feeling on that
(after having used a fair amount of it) is that the specification of
exceptions belongs in generated documentation (whether javadoc-style or as
part of the IDE as with "some_function() -> Called By..."). I normally
prefer having to explicity specify things (yea, strong-typing fan here ;) ),
but personally, I find it overkill in this case. (Not to mention it gave me
flashbacks of writing C/C++ headers. j/k ;) ).
↑ ↓ ← → Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Nick Sabalausky wrote:
"Rioshin an'Harthen" <rharth75 hotmail.com> wrote in message
news:f974u9$9k1$1 digitalmars.com...
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
remember what a specific method may throw when you write your code,
especially if the error is ambiguous like "Unhandled exception" it's
really irritating - but I hate unspecified exceptions even more, as the
problem then is to even remember to put in the necessary try-catch-finally
blocks.
Not to start a big big debate on it, but my own personal feeling on that
(after having used a fair amount of it) is that the specification of
exceptions belongs in generated documentation (whether javadoc-style or as
part of the IDE as with "some_function() -> Called By..."). I normally
prefer having to explicity specify things (yea, strong-typing fan here ;) ),
but personally, I find it overkill in this case. (Not to mention it gave me
flashbacks of writing C/C++ headers. j/k ;) ).
I didn't mind it at first, in that the Java compiler would then let me
know if I'd missed any potential exceptions...
...but when I noticed I was just adding 'throws A,B,C' to a lot of my
methods, I decided it wasn't entirely a great thing afterall. What
would have been better, I think, would have been to make throws
specifications /optional/. A thing libraries should do, but which
applications should be allowed to quietly "forget" most of the time.
Java is the language of conventions, after all.
-- Chris Nicholson-Sauls
↑ ↓ ← → Robert Fraser <fraserofthenight gmail.com> writes:
Chris Nicholson-Sauls Wrote:
Nick Sabalausky wrote:
"Rioshin an'Harthen" <rharth75 hotmail.com> wrote in message
news:f974u9$9k1$1 digitalmars.com...
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
remember what a specific method may throw when you write your code,
especially if the error is ambiguous like "Unhandled exception" it's
really irritating - but I hate unspecified exceptions even more, as the
problem then is to even remember to put in the necessary try-catch-finally
blocks.
Not to start a big big debate on it, but my own personal feeling on that
(after having used a fair amount of it) is that the specification of
exceptions belongs in generated documentation (whether javadoc-style or as
part of the IDE as with "some_function() -> Called By..."). I normally
prefer having to explicity specify things (yea, strong-typing fan here ;) ),
but personally, I find it overkill in this case. (Not to mention it gave me
flashbacks of writing C/C++ headers. j/k ;) ).
I didn't mind it at first, in that the Java compiler would then let me
know if I'd missed any potential exceptions...
...but when I noticed I was just adding 'throws A,B,C' to a lot of my
methods, I decided it wasn't entirely a great thing afterall. What
would have been better, I think, would have been to make throws
specifications /optional/. A thing libraries should do, but which
applications should be allowed to quietly "forget" most of the time.
Java is the language of conventions, after all.
-- Chris Nicholson-Sauls
I'd like it to be required in the method where the exception is thrown for
checked exceptions, but not in any calling methods (they're just optional
there). But I'm not too passionate about the point... I can live with 'em
(they're a bit annoying, but I can see their advantages), and I can live
without 'em.
↑ ↓ ← → Bill Baxter <dnewsgroup billbaxter.com> writes:
Nick Sabalausky wrote:
"Rioshin an'Harthen" <rharth75 hotmail.com> wrote in message
news:f974u9$9k1$1 digitalmars.com...
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
remember what a specific method may throw when you write your code,
especially if the error is ambiguous like "Unhandled exception" it's
really irritating - but I hate unspecified exceptions even more, as the
problem then is to even remember to put in the necessary try-catch-finally
blocks.
Not to start a big big debate on it, but my own personal feeling on that
(after having used a fair amount of it) is that the specification of
exceptions belongs in generated documentation (whether javadoc-style or as
part of the IDE as with "some_function() -> Called By..."). I normally
prefer having to explicity specify things (yea, strong-typing fan here ;) ),
but personally, I find it overkill in this case. (Not to mention it gave me
flashbacks of writing C/C++ headers. j/k ;) ).
I agree with this 100%. With check exceptions it just becomes too
annoying and verbose. Without them, often it is too difficult to find
out what exceptions are possible for a function to throw.
So the right place seems to be an analysis / doc-generation tool / IDE.
--bb
↑ ↓ ← → Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bill Baxter wrote:
Nick Sabalausky wrote:
"Rioshin an'Harthen" <rharth75 hotmail.com> wrote in message
news:f974u9$9k1$1 digitalmars.com...
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
to remember what a specific method may throw when you write your
code, especially if the error is ambiguous like "Unhandled exception"
it's really irritating - but I hate unspecified exceptions even more,
as the problem then is to even remember to put in the necessary
try-catch-finally blocks.
Not to start a big big debate on it, but my own personal feeling on
that (after having used a fair amount of it) is that the specification
of exceptions belongs in generated documentation (whether
javadoc-style or as part of the IDE as with "some_function() -> Called
By..."). I normally prefer having to explicity specify things (yea,
strong-typing fan here ;) ), but personally, I find it overkill in
this case. (Not to mention it gave me flashbacks of writing C/C++
headers. j/k ;) ).
I agree with this 100%. With check exceptions it just becomes too
annoying and verbose. Without them, often it is too difficult to find
out what exceptions are possible for a function to throw.
So the right place seems to be an analysis / doc-generation tool / IDE.
--bb
Interesting thing I'd like to mention, again about how IDEs affect the
language design and influence it's advantages and disadvantages.
I too find checked exceptions annoying, but this is an example of a
language disadvantage has been practically nullified by the IDE (JDT).
First I took a slightly modified ExceptionAdapter class such as the one
from Bruce Eckel's article:
http://www.mindview.net/Etc/Discussions/CheckedExceptions
(basicly ExceptionAdapter is class that wraps a checked exception in an
unchecked exception, allowing it to be thrown unchecked)
So when you have something like this:
foo.doSomething(); // throws a normal exception
and Java will complain that an exception is thrown, you add this:
try {
foo.doSomething(); // throws a normal exception
} catch(FooException fe) {
ExceptionAdapter.unchecked(fe);
}
and there is no longer a compiler error (unchecked is a static method
that wraps the exception), without having needed to put throws clauses
in all method along the call hierarchy.
This is an interesting workaround, but it is still annoying to have to
write that try-catch code whenever an exception is thrown.
The second part is where JDT comes in. Whenever you have this:
foo.doSomething(); // throws a normal exception
the IDE compiler will show and highlight the error, and then you can
press Ctrl+1 on the error (Quick-Assist), showing a list of possible
fixes, one of them being "Surround with Try-Catch". If you select it,
JDT will automatically add the try-catch code, like this:
try {
foo.doSomething(); // throws a normal exception
} catch(FooException fe) {
// TODO: catch exception here
}
Note that the catch clause is automatically set to the thrown exception.
But furthermore you can change the template of code that is created with
this try-catch quick-fix, so that instead of the "// TODO:" line, you
put the "ExceptionAdapter.unchecked(...);" line. So now whenever this
quick fix is invoked, it will automatically generate all necessary code,
and you're now able to avoid the checked exception problem with just a
few keystrokes (Ctrl+1, Down, Enter). :)
As an added bonus, if some time you feel the need to rigorously specify
your code, you can then search for calls of the
ExceptionAdapter.unchecked method, to find points where you can turn
unchecked exceptions into checked ones.
--
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
↑ ↓ ← → eao197 <eao197 intervale.ru> writes:
On Wed, 08 Aug 2007 15:38:14 +0400, Bruno Medeiros
<brunodomedeiros+spam com.gmail> wrote:
Bill Baxter wrote:
Nick Sabalausky wrote:
"Rioshin an'Harthen" <rharth75 hotmail.com> wrote in message
news:f974u9$9k1$1 digitalmars.com...
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications where
everyone says it's a good idea but guiltily hate in secret <g>.
to remember what a specific method may throw when you write your
code, especially if the error is ambiguous like "Unhandled exception"
it's really irritating - but I hate unspecified exceptions even more,
as the problem then is to even remember to put in the necessary
try-catch-finally blocks.
Not to start a big big debate on it, but my own personal feeling on
that (after having used a fair amount of it) is that the specification
of exceptions belongs in generated documentation (whether
javadoc-style or as part of the IDE as with "some_function() -> Called
By..."). I normally prefer having to explicity specify things (yea,
strong-typing fan here ;) ), but personally, I find it overkill in
this case. (Not to mention it gave me flashbacks of writing C/C++
headers. j/k ;) ).
annoying and verbose. Without them, often it is too difficult to find
out what exceptions are possible for a function to throw.
So the right place seems to be an analysis / doc-generation tool / IDE.
--bb
Interesting thing I'd like to mention, again about how IDEs affect the
language design and influence it's advantages and disadvantages.
I too find checked exceptions annoying, but this is an example of a
language disadvantage has been practically nullified by the IDE (JDT).
First I took a slightly modified ExceptionAdapter class such as the one
from Bruce Eckel's article:
http://www.mindview.net/Etc/Discussions/CheckedExceptions
(basicly ExceptionAdapter is class that wraps a checked exception in an
unchecked exception, allowing it to be thrown unchecked)
So when you have something like this:
foo.doSomething(); // throws a normal exception
and Java will complain that an exception is thrown, you add this:
try {
foo.doSomething(); // throws a normal exception
} catch(FooException fe) {
ExceptionAdapter.unchecked(fe);
}
and there is no longer a compiler error (unchecked is a static method
that wraps the exception), without having needed to put throws clauses
in all method along the call hierarchy.
This is an interesting workaround, but it is still annoying to have to
write that try-catch code whenever an exception is thrown.
The second part is where JDT comes in. Whenever you have this:
foo.doSomething(); // throws a normal exception
the IDE compiler will show and highlight the error, and then you can
press Ctrl+1 on the error (Quick-Assist), showing a list of possible
fixes, one of them being "Surround with Try-Catch". If you select it,
JDT will automatically add the try-catch code, like this:
try {
foo.doSomething(); // throws a normal exception
} catch(FooException fe) {
// TODO: catch exception here
}
Note that the catch clause is automatically set to the thrown exception.
But furthermore you can change the template of code that is created with
this try-catch quick-fix, so that instead of the "// TODO:" line, you
put the "ExceptionAdapter.unchecked(...);" line. So now whenever this
quick fix is invoked, it will automatically generate all necessary code,
and you're now able to avoid the checked exception problem with just a
few keystrokes (Ctrl+1, Down, Enter). :)
As an added bonus, if some time you feel the need to rigorously specify
your code, you can then search for calls of the
ExceptionAdapter.unchecked method, to find points where you can turn
unchecked exceptions into checked ones.
I don't agree (almost complitely). Checked expession is not simply
annoying. Sometimes they lead to bad-style and erroneous code. For example
imagine you redefine some method:
class MySpecializedClass extends SomeGeneralClass
{
public void someMethod() throws DomainSpecificException { ... }
}
but you need to use temporary file in someMethod() implementation (or need
to run some external tool, or need to use some crypto library). And
File.createTempFile throws IllegalArgumentException or IOException or
SecurityException, but no one of them is in someMethod 'throws' clause.
In such case you have only two possibilities: wrap the exception which is
thrown by File.createTempFile into some another exception (it is good if
DomainSpecificException allows that, but in an usual case it don't) or
catch and hide the original exception. As I seen in the past many
developers prefered catch and hide exception. And this is not a good
approach I think.
So in my opinion it is better to write code which doesn't depend on
particular kind of exception. Assumption that any part of code could
throws any kind of exception lead to more reliable software (Erlang is an
evidence).
The exception safety in much more hard in C++ where there is manual memory
management and there isn't scope()-constructs like in D. So I hope that
writting exception safe code in D is much more easy, than in C++ or in
Java with checked exceptions.
However sometimes is good to know that some method doesn't throw exception
at all (it could be necessary for exception safety, like C++ convection
that swap() methods and destructors are exception free). So I suppose to
introduce 'nothrows' modifier as a sign that some routine is exception
free:
bool is_item_in_array( int[] array, int item ) nothrows { ... }
Compiler could checks nothrows-routines. It is an error if
nothrows-routine calls any routine without 'nothrows' modifier.
So it could be an crear advice to programmer: if it has some data which
must be protected from exception and he calls any routine without
'nothrows' modifier he must do some action to protect his data.
--
Regards,
Yauheni Akhotnikau
↑ ↓ ← → eao197 <eao197 intervale.ru> writes:
On Wed, 08 Aug 2007 17:05:07 +0400, eao197 <eao197 intervale.ru> wrote:
So in my opinion it is better to write code which doesn't depend on =
particular kind of exception. Assumption that any part of code could =
throws any kind of exception lead to more reliable software (Erlang is=
an evidence).
The exception safety in much more hard in C++ where there is manual =
memory management and there isn't scope()-constructs like in D. So I =
hope that writting exception safe code in D is much more easy, than in=
C++ or in Java with checked exceptions.
However sometimes is good to know that some method doesn't throw =
exception at all (it could be necessary for exception safety, like =
C++ convection that swap() methods and destructors are exception free)=
So I suppose to introduce 'nothrows' modifier as a sign that some =
routine is exception free:
bool is_item_in_array( int[] array, int item ) nothrows { ... }
Compiler could checks nothrows-routines. It is an error if =
nothrows-routine calls any routine without 'nothrows' modifier.
So it could be an crear advice to programmer: if it has some data whic=
must be protected from exception and he calls any routine without =
'nothrows' modifier he must do some action to protect his data.
Yet more ideas about 'nothrows' modifier (sorry for wasting your time).
Which routines need 'nothrows' specifications?
Obvious (for me) answer is: various forms of cleanup-routines. For =
example: File.close, Mutex.release, DbConnection.disconnect and so on. =
E.g. routines which will be used in destructors, scope-constructs and =
finally-blocks. So, compiler could checks all calls in those constructs =
=
and reports warning (or even errors) if any method without 'nothrows' is=
=
being called.
Even more. In some situation cleanup actions could be performed in =
catch-blocks. To ensure that no exception will be thrown during cleanup =
=
programmer could write:
nothrows {
... // some actions...
}
and compiler will check all calls in such block.
For example:
// Create new DB connection for user.
// And checks user right. If user has no right (has expiried password) t=
hen
// exception is thrown.
UserSession
open_user_section( string user_name, string user_password )
{
auto db_connection =3D establish_db_connection();
try
{
check_user_rights( db_connection, user_name, user_password );
... // Some other actions...
}
catch( Exception x )
{
nothrows
{
cleanup_db_connection( db_connection );
... // Some other cleanup actions...
}
log_session_creation_error( x );
throw;
}
return new UserSession( db_connection, user_name );
}
So compiler could check that no exception is allowed in cleanup actions.=
=
But new exception could be thrown in 'log_session_creation_error' method=
.
-- =
Regards,
Yauheni Akhotnikau
↑ ↓ ← → Christopher Wright <dhasenan gmail.com> writes:
eao197 wrote:
However sometimes is good to know that some method doesn't throw
exception at all (it could be necessary for exception safety, like C++
convection that swap() methods and destructors are exception free). So I
suppose to introduce 'nothrows' modifier as a sign that some routine is
exception free:
The compiler can support this. It'd have to add more metadata to
precompiled libraries, but that's okay. The ddocs could then
(optionally?) include information on what exceptions the function throws
directly and what other exceptions can be thrown on invocation.
So I don't see any reason for the programmer to have to manually do all
this.
-cbw
↑ ↓ ← → eao197 <eao197 intervale.ru> writes:
On Fri, 10 Aug 2007 06:03:10 +0400, Christopher Wright =
<dhasenan gmail.com> wrote:
eao197 wrote:
However sometimes is good to know that some method doesn't throw =
exception at all (it could be necessary for exception safety, like C+=
convection that swap() methods and destructors are exception free). S=
I suppose to introduce 'nothrows' modifier as a sign that some routin=
is exception free:
The compiler can support this. It'd have to add more metadata to =
precompiled libraries, but that's okay. The ddocs could then =
(optionally?) include information on what exceptions the function thro=
directly and what other exceptions can be thrown on invocation.
So I don't see any reason for the programmer to have to manually do al=
this.
There are one important use case where the compiler can't detect which =
exceptions are thrown by a method: polymorphism and late binding. Imagin=
e:
class EventHandler
{
abstract void handle_input();
abstract void handle_output();
abstract void handle_exception();
...
}
class SelectReactor : Reactor
{
void
reactor_event_loop()
{
while( true )
{
EventHandler[] input_ready_handlers =3D =
detect_input_ready_handlers();
foreach( h; input_ready_handlers )
h.handle_input();
...
}
}
}
During compilation of SelectReactor the compiler can't determine which =
exception could be thrown by descendants of EventHandler. So it is a =
programmer's task to specify exceptions in 'throws' clause (like in =
Java/Spec#) or declare that method doesn't throw exception at all in =
'nothrows' clause (like throw() in C++).
-- =
Regards,
Yauheni Akhotnikau
↑ ↓ ← → Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
eao197 wrote:
On Wed, 08 Aug 2007 15:38:14 +0400, Bruno Medeiros
<brunodomedeiros+spam com.gmail> wrote:
Bill Baxter wrote:
Nick Sabalausky wrote:
"Rioshin an'Harthen" <rharth75 hotmail.com> wrote in message
news:f974u9$9k1$1 digitalmars.com...
"Walter Bright" <newshound1 digitalmars.com> kirjoitti viestissä
news:f95leh$hn4$1 digitalmars.com...
It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
try to remember what a specific method may throw when you write
your code, especially if the error is ambiguous like "Unhandled
exception" it's really irritating - but I hate unspecified
exceptions even more, as the problem then is to even remember to
put in the necessary try-catch-finally blocks.
Not to start a big big debate on it, but my own personal feeling on
that (after having used a fair amount of it) is that the
specification of exceptions belongs in generated documentation
(whether javadoc-style or as part of the IDE as with
"some_function() -> Called By..."). I normally prefer having to
explicity specify things (yea, strong-typing fan here ;) ), but
personally, I find it overkill in this case. (Not to mention it gave
me flashbacks of writing C/C++ headers. j/k ;) ).
annoying and verbose. Without them, often it is too difficult to
find out what exceptions are possible for a function to throw.
So the right place seems to be an analysis / doc-generation tool / IDE.
--bb
Interesting thing I'd like to mention, again about how IDEs affect the
language design and influence it's advantages and disadvantages.
I too find checked exceptions annoying, but this is an example of a
language disadvantage has been practically nullified by the IDE (JDT).
First I took a slightly modified ExceptionAdapter class such as the
one from Bruce Eckel's article:
http://www.mindview.net/Etc/Discussions/CheckedExceptions
(basicly ExceptionAdapter is class that wraps a checked exception in
an unchecked exception, allowing it to be thrown unchecked)
So when you have something like this:
foo.doSomething(); // throws a normal exception
and Java will complain that an exception is thrown, you add this:
try {
foo.doSomething(); // throws a normal exception
} catch(FooException fe) {
ExceptionAdapter.unchecked(fe);
}
and there is no longer a compiler error (unchecked is a static method
that wraps the exception), without having needed to put throws clauses
in all method along the call hierarchy.
This is an interesting workaround, but it is still annoying to have to
write that try-catch code whenever an exception is thrown.
The second part is where JDT comes in. Whenever you have this:
foo.doSomething(); // throws a normal exception
the IDE compiler will show and highlight the error, and then you can
press Ctrl+1 on the error (Quick-Assist), showing a list of possible
fixes, one of them being "Surround with Try-Catch". If you select it,
JDT will automatically add the try-catch code, like this:
try {
foo.doSomething(); // throws a normal exception
} catch(FooException fe) {
// TODO: catch exception here
}
Note that the catch clause is automatically set to the thrown
exception. But furthermore you can change the template of code that is
created with this try-catch quick-fix, so that instead of the "//
TODO:" line, you put the "ExceptionAdapter.unchecked(...);" line. So
now whenever this quick fix is invoked, it will automatically generate
all necessary code, and you're now able to avoid the checked exception
problem with just a few keystrokes (Ctrl+1, Down, Enter). :)
As an added bonus, if some time you feel the need to rigorously
specify your code, you can then search for calls of the
ExceptionAdapter.unchecked method, to find points where you can turn
unchecked exceptions into checked ones.
I don't agree (almost complitely). Checked expession is not simply
annoying. Sometimes they lead to bad-style and erroneous code. For
example imagine you redefine some method:
class MySpecializedClass extends SomeGeneralClass
{
public void someMethod() throws DomainSpecificException { ... }
}
but you need to use temporary file in someMethod() implementation (or
need to run some external tool, or need to use some crypto library). And
File.createTempFile throws IllegalArgumentException or IOException or
SecurityException, but no one of them is in someMethod 'throws' clause.
In such case you have only two possibilities: wrap the exception which
is thrown by File.createTempFile into some another exception (it is good
if DomainSpecificException allows that, but in an usual case it don't)
or catch and hide the original exception. As I seen in the past many
developers prefered catch and hide exception. And this is not a good
approach I think.
So in my opinion it is better to write code which doesn't depend on
particular kind of exception. Assumption that any part of code could
throws any kind of exception lead to more reliable software (Erlang is
an evidence).
I don't see how that is disagreeing with what I said before. Note that
the above doesn't hide the exception (as in, make it disappear), it gets
re-thrown as an unchecked exception.
--
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
↑ ↓ ← → eao197 <eao197 intervale.ru> writes:
On Fri, 10 Aug 2007 14:15:47 +0400, Bruno Medeiros
<brunodomedeiros+spam com.gmail> wrote:
I don't see how that is disagreeing with what I said before. Note that
the above doesn't hide the exception (as in, make it disappear), it gets
re-thrown as an unchecked exception.
Umm... I'm sorry, I thought that 'ExceptionAdapter.unchecked(fe)' simply
hides exception.
But initaly I was disagree with that:
an example of a language disadvantage has been practically nullified by
the IDE (JDT).
Easy method of incapsulating exception into unchecked one doesn't repair
flaw in the language design.
--
Regards,
Yauheni Akhotnikau
↑ ↓ ← → Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
eao197 wrote:
On Fri, 10 Aug 2007 14:15:47 +0400, Bruno Medeiros
<brunodomedeiros+spam com.gmail> wrote:
I don't see how that is disagreeing with what I said before. Note that
the above doesn't hide the exception (as in, make it disappear), it
gets re-thrown as an unchecked exception.
Umm... I'm sorry, I thought that 'ExceptionAdapter.unchecked(fe)' simply
hides exception.
No, it doesn't do that. And indeed it would be bad if it did.
But initaly I was disagree with that:
an example of a language disadvantage has been practically nullified
by the IDE (JDT).
Easy method of incapsulating exception into unchecked one doesn't repair
flaw in the language design.
It fixes it well enough for me. :)
--
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
↑ ↓ ← → 0ffh <spam frankhirsch.net> writes:
Walter Bright wrote:
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Hmmmm... even I think that requiring the override keyword is okay,
and I am not exactly known to be a discipline and bondage fanatic. ^^
Regards, Frank
↑ ↓ ← → BCS <ao pathlink.com> writes:
Reply to Walter,
kris wrote:
There's a related problem where a public method is added to a
base-class A (as in your example) but where the signature is
*exactly* that of one existing in derived class B. If A actually
calls that new method internally, "bad things"tm will almost
certainly happen, since B never intended to effectively override the
newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by
the compiler. The request was first made two or three years back, and
once or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this
related type of hijacking and avoid such nasty surprises.
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Mitigating factors are private and final methods cannot be overridden.
How hard would it be to put a command line flag on the 2.0 version that would
turn it on?
↑ ↓ ← → 0ffh <spam frankhirsch.net> writes:
BCS wrote:
Reply to Walter,
How hard would it be to put a command line flag on the 2.0 version that
would turn it on?
Or rather, one to turn it off? ;-)
Regards, Frank
↑ ↓ ← → BCS <ao pathlink.com> writes:
Reply to 0ffh,
BCS wrote:
Reply to Walter,
How hard would it be to put a command line flag on the 2.0 version
that
would turn it on?
Or rather, one to turn it off? ;-)
Regards, Frank
either
↑ ↓ ← → Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
kris wrote:
There's a related problem where a public method is added to a
base-class A (as in your example) but where the signature is *exactly*
that of one existing in derived class B. If A actually calls that new
method internally, "bad things"tm will almost certainly happen, since
B never intended to effectively override the newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by
the compiler. The request was first made two or three years back, and
once or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this related
type of hijacking and avoid such nasty surprises.
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Mitigating factors are private and final methods cannot be overridden.
Although, I generally like the idea, I fear that requiring 'override'
will have side effects for mixins. The writer of the mixin has no way
of knowing if the methods will override something or not.
I'm thinking the only reasonable solution would be to allow mixins to go
on as they currently are, without enforcing the use of 'override'.
However, an accidental override that comes in via a mixin seems like a
very likely source of subtle bugs. And requiring a special exception to
the rules is not really desirable in the first place.
--bb
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
Although, I generally like the idea, I fear that requiring 'override'
will have side effects for mixins. The writer of the mixin has no way
of knowing if the methods will override something or not.
I didn't think of that.
But I'll put the check in as a warning, and we'll see if there are real
problems or not.
↑ ↓ ← → Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Walter Bright wrote:
Bill Baxter wrote:
Although, I generally like the idea, I fear that requiring 'override'
will have side effects for mixins. The writer of the mixin has no way
of knowing if the methods will override something or not.
I didn't think of that.
But I'll put the check in as a warning, and we'll see if there are real
problems or not.
There are probably on two likely scenarios for that:
#1 - The mixin and the base class happen to have symbol overlap.
#2 - The mixin was previously used by the base class.
In the case of #1, the resulting errors could actually be a boon, alerting the
user to a
potential (serious) hazard. On the other hand, if the overlap is not an issue,
or even on
purpose, the workaround is clumsy and kludgy:
class B {
int foo() {...}
}
template M {
int foo() {...}
int bar() {...}
}
class D : B {
mixin M _m;
alias _m.bar bar;
override int foo () { return _m.foo(); }
}
Chances are that a relative straightforward CheckOverrides() template or CTF
could be used
to auto-generate this. There is still, however, the issue of a superfluous
proxy method
being written, whether by the programmer or by CheckOverrides().
In the case of #2, there are two sub-scenarios.
In the first, the situation is an accident. Perhaps D was not previously
derived from B,
but is now as a result of refactoring, and the unnecessary mixin statement was
simply
missed. The compiler has caught a programmer error. Good Thing.
In the second, perhaps class B had overwritten some of the mixin's methods to
behave
differently, and class D intends to re-mix the default behavior (perhaps to
expose its own
changes). In this case perhaps one could wrap the mixin statement and
subsequent
overwrites in an 'override {...}' block and be good to go.
template M {
int foo() {...}
int bar() {...}
}
class B {
mixin M;
int foo() {...custom version...}
}
class D : B {
override {
mixin M;
int bar() {...custom version...}
}
}
It makes for nice grouping as well, even if it is a workaround.
Of course I might have missed some other case where this could happen.
-- Chris Nicholson-Sauls
↑ ↓ ← → Reiner Pope <some address.com> writes:
Bill Baxter wrote:
Walter Bright wrote:
kris wrote:
There's a related problem where a public method is added to a
base-class A (as in your example) but where the signature is
*exactly* that of one existing in derived class B. If A actually
calls that new method internally, "bad things"tm will almost
certainly happen, since B never intended to effectively override the
newly-added method in A.
This is a very hard problem to isolate yet can be easily remedied by
the compiler. The request was first made two or three years back, and
once or twice since then: you make the "override" keyword *required*.
When "override" is required, the compiler can easily trap this
related type of hijacking and avoid such nasty surprises.
That is a good point. The reason I haven't added it is because I'm not
sure how annoying it will be to have to always add the 'override'
keyword. It might be one of those things like exception specifications
where everyone says it's a good idea but guiltily hate in secret <g>.
Mitigating factors are private and final methods cannot be overridden.
Although, I generally like the idea, I fear that requiring 'override'
will have side effects for mixins. The writer of the mixin has no way
of knowing if the methods will override something or not.
I'm thinking the only reasonable solution would be to allow mixins to go
on as they currently are, without enforcing the use of 'override'.
However, an accidental override that comes in via a mixin seems like a
very likely source of subtle bugs. And requiring a special exception to
the rules is not really desirable in the first place.
--bb
Interesting point. I think, however, that an explicit solution which
re-enables using mixins as overriding functions is the best solution (as
you point out, the alternative is subtle bugs). Perhaps a new form of
alias, to add the override?
template Foo()
{
int foo() { return x*x; }
}
class Base
{
int foo() { return 5; }
}
class Derived : Base
{
int x = 5;
mixin Foo my_foo;
// specify that my_foo.foo overrides Base.foo
alias my_foo.foo Base.foo;
// or specify that my_foo.foo is an override
alias my_foo.foo override foo;
// (but obviously not both at once)
}
-- Reiner
PS. Notice how I've (not-so-)subtly worked in my suggestion from
http://www.digitalmars.com/d/archives/digitalmars/D/Implementing_required_methods_w
th_alias_56011.html
;-)
↑ ↓ ← → "Chris Miller" <chris dprogramming.com> writes:
This is a very hard problem to isolate yet can be easily remedied by the
compiler. The request was first made two or three years back, and once
or twice since then: you make the "override" keyword *required*.
I'm for this. I feel funny when I don't use "override".
↑ ↓ ← → Regan Heath <regan netmail.co.nz> writes:
This post looks like a prime candidate for an article page on the D
website (with "Const", "Memory Management", etc).
Regan
↑ ↓ ← → Derek Parnell <derek psyc.ward> writes:
On Sun, 05 Aug 2007 11:47:18 -0700, Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread which is
getting a little long, on a closely related issue which I find to be
important, but so rarely referred to that I had to come up with a name
for it - hijacking.
I'm just wondering what the NSA is making of this discussion :)
--
Derek Parnell
Melbourne, Australia
"Down with mediocrity!"
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
On Sun, 05 Aug 2007 11:47:18 -0700, Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread which is
getting a little long, on a closely related issue which I find to be
important, but so rarely referred to that I had to come up with a name
for it - hijacking.
I'm just wondering what the NSA is making of this discussion :)
Hopefully, they'll analyze the site, get excited about D, and use D for
their next generation software!
↑ ↓ ← → BCS <ao pathlink.com> writes:
Reply to Walter,
Derek Parnell wrote:
On Sun, 05 Aug 2007 11:47:18 -0700, Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread which
is getting a little long, on a closely related issue which I find to
be important, but so rarely referred to that I had to come up with a
name for it - hijacking.
for their next generation software!
Fat chance, most of D's advantages don't exist in under ~5000 LOC and anything
over that and the NSA won't touch it. Besides there isn't 20+ years of best
practices in how to right secure software in D (as this is in C)
Sorry
↑ ↓ ← → Sean Kelly <sean f4.ca> writes:
BCS wrote:
Reply to Walter,
Derek Parnell wrote:
On Sun, 05 Aug 2007 11:47:18 -0700, Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread which
is getting a little long, on a closely related issue which I find to
be important, but so rarely referred to that I had to come up with a
name for it - hijacking.
for their next generation software!
Fat chance, most of D's advantages don't exist in under ~5000 LOC and
anything over that and the NSA won't touch it. Besides there isn't 20+
years of best practices in how to right secure software in D (as this is
in C)
I'd think DBC would be a perk, if nothing else. Assuming the NSA
currently uses C, that is.
Sean
↑ ↓ ← → janderson <askme me.com> writes:
Sean Kelly wrote:
BCS wrote:
Reply to Walter,
Derek Parnell wrote:
On Sun, 05 Aug 2007 11:47:18 -0700, Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread which
is getting a little long, on a closely related issue which I find to
be important, but so rarely referred to that I had to come up with a
name for it - hijacking.
for their next generation software!
Fat chance, most of D's advantages don't exist in under ~5000 LOC and
anything over that and the NSA won't touch it. Besides there isn't 20+
years of best practices in how to right secure software in D (as this
is in C)
I'd think DBC would be a perk, if nothing else. Assuming the NSA
currently uses C, that is.
Sean
I thought NSA used ADA it was designed for such purposes.
-Joel
↑ ↓ ← → BCS <ao pathlink.com> writes:
Reply to janderson,
Sean Kelly wrote:
BCS wrote:
Reply to Walter,
Derek Parnell wrote:
On Sun, 05 Aug 2007 11:47:18 -0700, Walter Bright wrote:
I want to continue a bit in the Overloading/Inheritance thread
which is getting a little long, on a closely related issue which
I find to be important, but so rarely referred to that I had to
come up with a name for it - hijacking.
for their next generation software!
and anything over that and the NSA won't touch it. Besides there
isn't 20+ years of best practices in how to right secure software in
D (as this is in C)
currently uses C, that is.
Sean
-Joel
The only experience I have (yes I do have a tiny bit) is in C and is only
about 500 loc.
↑ ↓ ← → Sean Kelly <sean f4.ca> writes:
janderson wrote:
Sean Kelly wrote:
BCS wrote:
Fat chance, most of D's advantages don't exist in under ~5000 LOC and
anything over that and the NSA won't touch it. Besides there isn't
20+ years of best practices in how to right secure software in D (as
this is in C)
I'd think DBC would be a perk, if nothing else. Assuming the NSA
currently uses C, that is.
I thought NSA used ADA it was designed for such purposes.
By my understanding, Ada was largely intended for robust systems
control, while I would guess that much of the code written at the NSA is
for in-house problem solving and that the safety provided by Ada isn't
typically necessary. I think it's likely that the NSA most often uses
mainstream languages for the bulk of its work, tending towards
systems-oriented languages for their speed.
Sean
↑ ↓ ← → "Kristian Kilpi" <kjkilpi gmail.com> writes:
The old aliasing trick will still cause problems.
(The 'override' keyword won't solve the problem in the following case.)
class A {
void foo(int);
void foo(long);
}
class B : A {
alias A.foo foo;
override void foo(int);
}
void bar(B b) {
short val =3D 1;
b.foo(val); //calls 'foo(int)'
}
Now 'foo(short)' is added to 'A':
void bar(B b) {
short val =3D 1;
b.foo(val); //calls 'foo(short)'
}
Solutions:
(1) The aliasing trick will be banned (argh).
or
(2) One can overload final functions only (ermmm).
Or, to solve the hijacking problem once and for all:
(3) No implicit type casting with function calls.
Somehow (3) seems appealing (but I'm probably just too tired).
I go to sleep now... ;)
On Sun, 05 Aug 2007 21:47:18 +0300, Walter Bright =
<newshound1 digitalmars.com> wrote:
I want to continue a bit in the Overloading/Inheritance thread which i=
getting a little long, on a closely related issue which I find to be =
important, but so rarely referred to that I had to come up with a name=
for it - hijacking.
Hijacking is when code A depends on B, and then when seemingly unrelat=
code C is modified, then A's behavior silently changes. Here's an =
example from C++:
--A.h--
void foo(long i);
--B.c--
#include "A.h"
#include "C.h"
...
foo(3); // calls A's foo(long)
--------
Let's say A.h and C.h are developed by different people. In C.h, the =
developer adds:
--C.h--
void foo(int i);
-----
This does something completely different from A.h's foo(long), because=
they just happened to share the same name. Now, when B.c is recompiled=
it's call to foo is silently *hijacked* by C.h's foo.
Because of C++'s overloading rules and lack of modularity, there's no =
way to programmatically defend against this. Instead, one has to rely =
coding conventions (such as using a unique package prefix name on all =
symbols, like A_foo() and C_foo()).
So how does this relate to the overloading/inheritance issues? Conside=
----A.d----
class A
{
...
}
-----B.d----
import A;
class B : A
{
void foo(long);
}
...
void bar(B b)
{
b.foo(3); // calls B.foo(long)
}
----
Let's say A.d comes from some third party library. Now the developer o=
A decides to add some functionality to class A, and adds the member =
function foo(int):
----A.d---
class A
{
void foo(int);
}
------------
Now our hapless B programmer has his calls to B.foo(long) silently =
hijacked to A.foo(int) (under Java rules). I don't see any reasonable =
way for B to defend against this. Certainly, developer A doesn't have =
any idea who is deriving from A (and that's the point of polymorphism)=
but should he be disallowed from adding *any* method names? And the =
hapless B developer, he wrote class B years ago and no longer quite =
remembers how it works, he just recompiles it and now it silently fail=
So, this is one case where I feel C++ got it right, and Java didn't.
(P.S. It's not a compiler implementation issue, nor is it a runtime =
performance issue.)
↑ ↓ ← → Walter Bright <newshound1 digitalmars.com> writes:
Kristian Kilpi wrote:
The old aliasing trick will still cause problems.
(The 'override' keyword won't solve the problem in the following case.)
class A {
void foo(int);
void foo(long);
}
class B : A {
alias A.foo foo;
override void foo(int);
}
void bar(B b) {
short val = 1;
b.foo(val); //calls 'foo(int)'
}
Now 'foo(short)' is added to 'A':
void bar(B b) {
short val = 1;
b.foo(val); //calls 'foo(short)'
}
I don't think this is a problem, because the B author explicitly and
deliberately opened the door to this, therefore he is taking
responsibility for it.
↑ ↓ ← → Regan Heath <regan netmail.co.nz> writes:
Walter Bright wrote:
Kristian Kilpi wrote:
The old aliasing trick will still cause problems.
(The 'override' keyword won't solve the problem in the following case.)
class A {
void foo(int);
void foo(long);
}
class B : A {
alias A.foo foo;
override void foo(int);
}
void bar(B b) {
short val = 1;
b.foo(val); //calls 'foo(int)'
}
Now 'foo(short)' is added to 'A':
void bar(B b) {
short val = 1;
b.foo(val); //calls 'foo(short)'
}
I don't think this is a problem, because the B author explicitly and
deliberately opened the door to this, therefore he is taking
responsibility for it.
Yes and no.
At the time the author wrote "alias A.foo foo;" they were allowing void
foo(long), not void foo(short).
Yes, they still "opened the door to this" because D's current behaviour
is to pull all overloads past/present/future.
Perhaps being able to specify overloads exactly would in some cases be
of benefit, eg.
alias A.foo(long) foo;
Regan
↑ ↓ ← → Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Regan Heath wrote:
Walter Bright wrote:
Kristian Kilpi wrote:
The old aliasing trick will still cause problems.
(The 'override' keyword won't solve the problem in the following case.)
class A {
void foo(int);
void foo(long);
}
class B : A {
alias A.foo foo;
override void foo(int);
}
void bar(B b) {
short val = 1;
b.foo(val); //calls 'foo(int)'
}
Now 'foo(short)' is added to 'A':
void bar(B b) {
short val = 1;
b.foo(val); //calls 'foo(short)'
}
I don't think this is a problem, because the B author explicitly and
deliberately opened the door to this, therefore he is taking
responsibility for it.
Yes and no.
At the time the author wrote "alias A.foo foo;" they were allowing void
foo(long), not void foo(short).
Yes, they still "opened the door to this" because D's current behaviour
is to pull all overloads past/present/future.
Perhaps being able to specify overloads exactly would in some cases be
of benefit, eg.
alias A.foo(long) foo;
Regan
Honestly, we need some ability to specify more of a function/method's signature
in the
general case anyhow. (Return type isn't really important, since it can't be
different
without at least one parameter difference) This would help with the
super-aliasing case
above, /and/ with the address operator.
void foo (int);
void foo (char[]);
Currently:
&foo => *(foo(int))
Proposed:
&foo => *(foo(int)) or ambiguity error?
&foo(int) => *(foo(int))
&foo(char[]) => *(foo(char[]))
Currently:
alias foo bar; // all foo's
Proposed:
alias foo bar; // all foo's
alias foo(int) bar; // just this one
alias foo(char[]) bar; // ditto
The only thing that might get interesting is whether the compiler can easily
distinguish
(&foo()) as "take the address of a function foo with no params" versus "take
the address
of the result of function foo". (Is that even legal? Or useful?)
-- Chris Nicholson-Sauls
|
|