Jump to page: 1 2 3
Thread overview
reimplementing an interface in a derived class
Jan 03, 2019
kdevel
Jan 03, 2019
Neia Neutuladh
Jan 03, 2019
Alex
Jan 04, 2019
Neia Neutuladh
Jan 04, 2019
Alex
Jan 04, 2019
Neia Neutuladh
Jan 04, 2019
Alex
Jan 04, 2019
Simen Kjærås
Jan 04, 2019
Alex
Jan 04, 2019
bauss
Jan 04, 2019
Alex
Jan 04, 2019
kdevel
Jan 05, 2019
kdevel
Jan 04, 2019
bauss
Jan 04, 2019
Alex
Jan 04, 2019
bauss
Jan 04, 2019
Alex
Jan 04, 2019
Ali Çehreli
Jan 04, 2019
Alex
Jan 04, 2019
Ali Çehreli
Jan 04, 2019
Neia Neutuladh
Jan 04, 2019
Alex
Jan 10, 2019
Heromyth
Jan 03, 2019
bauss
January 03, 2019
https://dlang.org/spec/interface.html #11 has this code example:

```
interface D
{
    int foo();
}

class A : D
{
    int foo() { return 1; }
}

class B : A, D
{
    override int foo() { return 2; }
}

...

B b = new B();
b.foo();            // returns 2
D d = cast(D) b;
d.foo();            // returns 2
A a = cast(A) b;
D d2 = cast(D) a;
d2.foo();           // returns 2, even though it is A's D, not B's D
```

What is the meaning of the ", D"? It does not seem to make a difference if it is omitted.
January 03, 2019
On Thu, 03 Jan 2019 22:30:48 +0000, kdevel wrote:
> class A : D {
>      int foo() { return 1; }
> }
> 
> class B : A, D {
> [...]
>
> What is the meaning of the ", D"? It does not seem to make a difference if it is omitted.

B must provide its own implementation of D. It can't simply use A's implementation.
January 03, 2019
On Thursday, 3 January 2019 at 22:30:48 UTC, kdevel wrote:
> https://dlang.org/spec/interface.html #11 has this code example:
>
> ```
> interface D
> {
>     int foo();
> }
>
> class A : D
> {
>     int foo() { return 1; }
> }
>
> class B : A, D
> {
>     override int foo() { return 2; }
> }
>
> ...
>
> B b = new B();
> b.foo();            // returns 2
> D d = cast(D) b;
> d.foo();            // returns 2
> A a = cast(A) b;
> D d2 = cast(D) a;
> d2.foo();           // returns 2, even though it is A's D, not B's D
> ```
>
> What is the meaning of the ", D"? It does not seem to make a difference if it is omitted.

When you override foo() from A within B then you're omitting A's implementation.

That's because all functions are virtual by default and thus you override the implementation.

Basically you have something like (Simplified example)

A:
auto b = new B;
b.v_table["foo"] = &b.super.foo; // If B doesn't override foo. (super = A)
b.v_table["foo"] = &b.foo; // If B overrides foo.

When you call foo() it basically looks it up in the v_table.

Even if you cast B to A then the reference to A is still a reference to B but you just tell the compiler that the signature of your reference is A.

So when you do the following:
> B b = new B();
> b.foo();            // returns 2
> D d = cast(D) b;
> d.foo();            // returns 2
> A a = cast(A) b;
> D d2 = cast(D) a;
> d2.foo();           // returns 2, even though it is A's D, not B's D

It really translates to this:
B b = new B();
b.foo();
D d = use_signature_of_D_on_a_reference_to_B(b);
d.foo(); // Still calling b.foo() because D is not really a type and still a reference to B.
A a = use_signature_of_A_on_a_reference_to_B(b);
a.foo(); // Still calling b.foo() because B implement's A's signature by having it as a super class. This means that right now A is technically B and you just stripped away the signature of B to the signature of A.
D d2 = use_signature_of_D_on_a_reference_to_A(a);
d2.foo(); // Calls B.foo() because as stated earlier A is really B.

There is no A actually ever existing in your scenario.

Only a reference to B that different signatures are cast from.
January 03, 2019
On Thursday, 3 January 2019 at 23:23:12 UTC, Neia Neutuladh wrote:
> On Thu, 03 Jan 2019 22:30:48 +0000, kdevel wrote:
>> class A : D {
>>      int foo() { return 1; }
>> }
>> 
>> class B : A, D {
>> [...]
>>
>> What is the meaning of the ", D"? It does not seem to make a difference if it is omitted.
>
> B must provide its own implementation of D. It can't simply use A's implementation.

I assume that is another bug and has nothing to do with interfaces...

´´´
import std.traits;

alias parentOfB = BaseClassesTuple!B[0];

void main()
{
	static assert(is(typeof(cast(parentOfB)(new B)) == parentOfB));
	assert((cast(parentOfB)(new B)).foo == (new parentOfB).foo);
}

class A
{
	int foo() { return 1; }
}

class B : A
{
    override int foo() { return 2; }
}
´´´
January 04, 2019
On Thu, 03 Jan 2019 23:44:15 +0000, Alex wrote:
> I assume that is another bug and has nothing to do with interfaces...

B.foo is both overriding A.foo and implementing D.foo, so that's not a bug.
January 04, 2019
On Friday, 4 January 2019 at 00:15:28 UTC, Neia Neutuladh wrote:
> On Thu, 03 Jan 2019 23:44:15 +0000, Alex wrote:
>> I assume that is another bug and has nothing to do with interfaces...
>
> B.foo is both overriding A.foo and implementing D.foo, so that's not a bug.

I don't have any interfaces in my example.
B.foo overrides A.foo. By casting a B object to be an A object, A's behavior should be granted, shouldn't it?
January 04, 2019
On Fri, 04 Jan 2019 00:19:05 +0000, Alex wrote:
> B.foo overrides A.foo. By casting a B object to be an A object, A's behavior should be granted, shouldn't it?

I can't think of a single class system that works like that. C++, Java, C#, Dart, and TypeScript all work like D here. GObject in C works like D.

The point of OOP is that a bundle of data has particular ways of dealing with it. B has different data (at least theoretically), and that data has different ways of working with it. So if casting to a base class changed something to use the base class's behavior, you'd get bugs almost anywhere you used inheritance, since the derived class's data isn't being modified properly.

The only situation in which you might possibly want that sort of behavior is if inheritance were only for ease of implementation, but then you'd want to disallow that sort of cast anyway. It would be like trying to cast an object to one of its fields.
January 04, 2019
On Friday, 4 January 2019 at 00:19:05 UTC, Alex wrote:
> On Friday, 4 January 2019 at 00:15:28 UTC, Neia Neutuladh wrote:
>> On Thu, 03 Jan 2019 23:44:15 +0000, Alex wrote:
>>> I assume that is another bug and has nothing to do with interfaces...
>>
>> B.foo is both overriding A.foo and implementing D.foo, so that's not a bug.
>
> I don't have any interfaces in my example.
> B.foo overrides A.foo. By casting a B object to be an A object, A's behavior should be granted, shouldn't it?

No, because you OVERRIDE A's foo().

A does not exist. A is B and when you cast B to A you just tell the compiler that the reference should only have A's signature available.

You're not assigning B to A.
January 04, 2019
On Friday, 4 January 2019 at 02:13:27 UTC, Neia Neutuladh wrote:
> I can't think of a single class system that works like that. C++, Java, C#, Dart, and TypeScript all work like D here. GObject in C works like D.

In the example below, the "2" of B.foo is printed only once. Independently of the cast type, after degrading a B to an A the degraded object behaves like an A.

´´´ C++ ´´´
#include <iostream>
class A
{
public:
    int foo(){return 1;}
};
class B : public A
{
public:
    int foo(){return 2;}
};

void performFoo(A *a)
{
    std::cout << a->foo() << std::endl;
}

int main() {
    auto a = new A();
    auto b = new B();
    std::cout << a->foo() << std::endl;
    std::cout << b->foo() << std::endl;
    std::cout << dynamic_cast<A*>(b)->foo() << std::endl;
    std::cout << static_cast<A*>(b)->foo() << std::endl;
    std::cout << reinterpret_cast<A*>(b)->foo() << std::endl;
    performFoo(a);
    performFoo(b);
    return 0;
}
´´´
> The point of OOP is that a bundle of data has particular ways of dealing with it. B has different data (at least theoretically), and that data has different ways of working with it. So if casting to a base class changed something to use the base class's behavior, you'd get bugs almost anywhere you used inheritance, since the derived class's data isn't being modified properly.

Now I have the feeling, I'm missing something elementary... sorry for this...
But take the classic example of OOP of an Animal and a Dog: Animal.
Let the animal implement some default move and eat behavior.
Let the dog override the move method and implement bark.
If you degrade the dog to the animal by casting it should still be able to move and eat, but not bark.

This should always be true for inherited objects as the base classes define enough content to manage proper behavior of their own. (Given they are not abstract, a function is not virtual and so on...)
January 04, 2019
On Friday, 4 January 2019 at 07:37:43 UTC, bauss wrote:
> No, because you OVERRIDE A's foo().
>
> A does not exist. A is B and when you cast B to A you just tell the compiler that the reference should only have A's signature available.
>
> You're not assigning B to A.

Let's assume this is right. How to force a B object to behave like an A object? I thought casting is a possible approach...

« First   ‹ Prev
1 2 3