August 05, 2007
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.)
August 05, 2007
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
August 05, 2007
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.)
August 05, 2007
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.
August 06, 2007
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.

August 06, 2007
"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.


August 06, 2007
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
August 06, 2007
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
August 06, 2007
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
August 06, 2007
This post looks like a prime candidate for an article page on the D website (with "Const", "Memory Management", etc).

Regan
« First   ‹ Prev
1 2 3 4 5 6
Top | Discussion index | About this forum | D home