View mode: basic / threaded / horizontal-split · Log in · Help
August 05, 2007
Hijacking
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
Re: Hijacking
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
Re: Hijacking
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
Re: Hijacking
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
Re: Hijacking
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
Re: Hijacking
"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
Re: Hijacking
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
Re: Hijacking
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
Re: Hijacking
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
Re: Hijacking
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
Top | Discussion index | About this forum | D home