January 04, 2019
On Friday, 4 January 2019 at 08:40:04 UTC, Alex wrote:
> class A
> {
> public:
>     int foo(){return 1;}
> };
> class B : public A
> {
> public:
>     int foo(){return 2;}
> };

In C++, methods are non-virtual by default. In D, they are virtual by default. Because of this, the two examples are different. In fact, D disallows overriding non-virtual, non-private functions, as specified in https://dlang.org/spec/function.html#virtual-functions. For private functions it does work, though:

class A {
    private final int foo() { return 1; }
}
class B : A {
    final int foo() { return 2; }
}

unittest {
    assert((new A).foo == 1);
    assert((new B).foo == 2);
    assert((cast(A)(new B)).foo == 1);
}

> 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.


Imagine you give me a box with an Animal in it. You know it's a Bird, but I only know it's an Animal of some kind.

Case 1: I tell it to move() to the top of a tree. Would you expect it to climb or fly?
(let's not get into penguins and other flightless birds right now, the OOP animal metaphor is strained enough as it is)

Case 2: I try to tell it to bark(), but there's no way to do that, because I have access to it as an Animal, not a Dog.
Cutting off its beak and gluing a muzzle there (casting a Bird to a Dog) will only lead to suffering.

Case 1 is overriding - Bird has defined how move() should work, and it will do that even if you only know it's an animal of some kind. Case 2 is subclassing - a Dog can do things that the average Animal can't, and this is reflected in it having a wider interface - more methods.

This can be exemplified in D as:

import std.stdio : writeln;

abstract class Animal {
    abstract void move();
}

class Bird : Animal {
    override void move() {
        writeln("Flying.");
    }
}

class Dog : Animal {
    override void move() {
        writeln("Walking."); // Assume this is not Muttley
    }
    void bark() {
        writeln("Woof!");
    }
}

unittest {
    (new Bird).move(); // Flying
    (cast(Animal)new Bird).move(); // Flying
    //(cast(Animal)new Bird).bark(); // Fails to compile - generic animals can't bark
    (cast(Dog)new Bird).bark(); // Crashes because a Bird is not a Dog, cast returns null.
}

--
  Simen
January 04, 2019
On Friday, 4 January 2019 at 08:40:04 UTC, Alex wrote:
> 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...)

Your C++ example is not the same as in D because in C++ functions aren't virtual by default, they are in D.

Mark your functions as virtual in your C++ example and see what happens.

All functions in D are virtual by default!
January 04, 2019
On Friday, 4 January 2019 at 09:19:48 UTC, Simen Kjærås wrote:
> On Friday, 4 January 2019 at 08:40:04 UTC, Alex wrote:
>> class A
>> {
>> public:
>>     int foo(){return 1;}
>> };
>> class B : public A
>> {
>> public:
>>     int foo(){return 2;}
>> };
>
> In C++, methods are non-virtual by default. In D, they are virtual by default. Because of this, the two examples are different. In fact, D disallows overriding non-virtual, non-private functions, as specified in https://dlang.org/spec/function.html#virtual-functions. For private functions it does work, though:
>

Ha... got it!
Because final methods are allowed in interfaces, maybe this is the answer to the OP...

> class A {
>     private final int foo() { return 1; }
> }
> class B : A {
>     final int foo() { return 2; }
> }
>
> unittest {
>     assert((new A).foo == 1);
>     assert((new B).foo == 2);
>     assert((cast(A)(new B)).foo == 1);
> }
>
> Imagine you give me a box with an Animal in it. You know it's a Bird, but I only know it's an Animal of some kind.
>
> Case 1: I tell it to move() to the top of a tree. Would you expect it to climb or fly?
> (let's not get into penguins and other flightless birds right now, the OOP animal metaphor is strained enough as it is)
>
> Case 1 is overriding - Bird has defined how move() should work, and it will do that even if you only know it's an animal of some kind.
>
> This can be exemplified in D as:
>
> import std.stdio : writeln;
>
> abstract class Animal {
>     abstract void move();
> }
>
> class Bird : Animal {
>     override void move() {
>         writeln("Flying.");
>     }
> }
>
>
> unittest {
>     (new Bird).move(); // Flying
>     (cast(Animal)new Bird).move(); // Flying
> }
>
> --
>   Simen

Case 2 is clear. I was wondering about the behavior in case 1: not because of the override behavior, but because there is an explicit cast in between.
I assume the move method of an Animal is not abstract, and therefore I supposed, casting to this type explicitly should restore this very non-abstract behavior. But this is not the case.
And the final/virtual thing above explains this to some extent, too... I think...
January 04, 2019
On Friday, 4 January 2019 at 09:30:32 UTC, bauss wrote:
> Your C++ example is not the same as in D because in C++ functions aren't virtual by default, they are in D.
>
> Mark your functions as virtual in your C++ example and see what happens.
>
> All functions in D are virtual by default!

Yep. Got it! Thanks :)
January 04, 2019
On Friday, 4 January 2019 at 09:53:18 UTC, Alex wrote:
> I assume the move method of an Animal is not abstract, and therefore I supposed, casting to this type explicitly should restore this very non-abstract behavior. But this is not the case.
> And the final/virtual thing above explains this to some extent, too... I think...

A cast is only a way to tell the compiler the signature of a type.

In most cases it actually does nothing at runtime and is just a way to help the compiler.

Ex.
auto b = new B;
auto a = cast(A)b;

Will just tell the compiler that all usage of a will be restricted to the signature of A.
It doesn't tell the compiler that all usage of a should be the same as A.

At runtime it would actually just be:
auto b = new B;
auto a = b;
January 04, 2019
On Friday, 4 January 2019 at 09:58:59 UTC, bauss wrote:
> On Friday, 4 January 2019 at 09:53:18 UTC, Alex wrote:
>> I assume the move method of an Animal is not abstract, and therefore I supposed, casting to this type explicitly should restore this very non-abstract behavior. But this is not the case.
>> And the final/virtual thing above explains this to some extent, too... I think...
>
> A cast is only a way to tell the compiler the signature of a type.
>
> In most cases it actually does nothing at runtime and is just a way to help the compiler.
>
> Ex.
> auto b = new B;
> auto a = cast(A)b;
>
> Will just tell the compiler that all usage of a will be restricted to the signature of A.
> It doesn't tell the compiler that all usage of a should be the same as A.
>
> At runtime it would actually just be:
> auto b = new B;
> auto a = b; //*

* The last line is thought to be restricted to the signature of A, but the behavior of B.

Yeah... I think this is a matter of habituation. I assumed casting is something more powerful and overcomes the virtuality of functions if their body exists. But its the other way round. Thanks for the clarification.

As for the OP, I think here the usefulness of ", D" should be visible:

´´´
interface D
{
    int foo();
    final int fun(){ return 42; }
}

class A
{
    //size_t dummy;
    int foo() { return 1; }
    int fun() { return 72; }
}

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

void main()
{
    B b = new B();
    assert(b.foo == 2);             // returns 2
    assert(b.fun == 72);            // fun returns 72, as defined in A

    D d = cast(D) b;
    assert(d.foo == 2);             // returns 2
    assert(d.fun == 42);            // fun returns 42, as defined in D

    A a = cast(A) b;
    assert(a.foo == 2);             // returns 2, as it remains B
    assert(a.fun == 72);            // fun returns 72, as defined in A

    D d2 = cast(D) a;
    assert(d2.foo == 2);            // returns 2, as it remains B
    assert(d2.fun == 42);           // fun returns 42, as defined in D

    A a2 = new A();
    assert(a2.foo == 1);            // returns 1, as defined in A
    assert(a2.fun == 72);           // returns 72, as defined in A
    assert((cast(D) a2) is null);   // not castable to D
}
´´´
January 04, 2019
On Friday, 4 January 2019 at 11:27:59 UTC, Alex wrote:
> On Friday, 4 January 2019 at 09:58:59 UTC, bauss wrote:

[...]

> As for the OP, I think here the usefulness of ", D" should be visible:

[...]

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

[...]

>     D d = cast(D) b;
>     assert(d.foo == 2);             // returns 2

If I remove the ", D" the program segfaults in this line:

Program received signal SIGSEGV, Segmentation fault.
0x000000000042c184 in D main () at ggg.d:26
26	    d.foo();
(gdb) p d
$1 = (ggg.D *) 0x0

There is clearly an

   assert (d);

missing in the source. But why is d a null reference in the first place?
January 04, 2019
On 1/4/19 2:55 PM, kdevel wrote:
> On Friday, 4 January 2019 at 11:27:59 UTC, Alex wrote:
>> On Friday, 4 January 2019 at 09:58:59 UTC, bauss wrote:
> 
> [...]
> 
>> As for the OP, I think here the usefulness of ", D" should be visible:
> 
> [...]
> 
>> class B : A, D
>> {
>>     override int foo() { return 2; }
>> }
> 
> [...]
> 
>>     D d = cast(D) b;
>>     assert(d.foo == 2);             // returns 2
> 
> If I remove the ", D" the program segfaults in this line:
> 
> Program received signal SIGSEGV, Segmentation fault.
> 0x000000000042c184 in D main () at ggg.d:26
> 26        d.foo();
> (gdb) p d
> $1 = (ggg.D *) 0x0
> 
> There is clearly an
> 
>     assert (d);
> 
> missing in the source. But why is d a null reference in the first place?

Because when you dynamically cast one object or interface to another object or interface, and that result is not possible (if you remove ",D" from the example you quoted, then neither A nor B implement D), then the result is null.

https://dlang.org/spec/expression.html#cast_expressions

See parts 2 and 3.

-Steve
January 04, 2019
On 01/04/2019 12:46 AM, Alex wrote:
> 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?

Not possible by default. However, a B object is already behaving like an A object because e.g. it overrides the foo() member function.

> I thought casting is a possible approach...

In this case, casting is using the B object through it's A interface. The overridden behavior does not change. (Actually, that is possible in languages that support multiple inheritance through multiple virtual function pointer tables (vtbl) but D does not support that.)

Although I'm getting into implementation details here, I think it helps with understanding the semantics. There is only one vtbl per class object in D and the function entries are all filled in during construction. So, a B object's 'foo' slot in that table is filled with B.foo. So, such an object can foo() only as a B.

If there is such a need and B can indeed support behaving like an A, it can do so itself by calling A.foo, not through vtbl, but directly:

class B : A {
  override void foo() {
    A.foo();  // Calling directly
  }
}

By the way, do you have a use case in mind? Perhaps there are other ways to achieve that.

Ali

January 04, 2019
On Fri, 04 Jan 2019 08:46:24 +0000, Alex wrote:
> 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...

It requires a bit of surgery:

    import std.stdio;
    class A
    {
        void foo() { writeln("hello from A!"); }
    }
    class B : A
    {
        override void foo() { writeln("hello from B!"); }
    }
    void main()
    {
        auto b = new B;
        auto ptrB = cast(void**)b;
        ptrB[0] = A.classinfo.vtbl.ptr;
        b.foo();
    }

This takes advantage of the object layout used by DMD. 'vtbl' is the virtual function table, which is basically an array of function pointers. Each member function defined in a type (and its super types) gets a unique index into that array.

So when you write:

    b.foo();

That works out to:

    (cast(void function(B))b.vtbl[5])(b);

We replace object b's vtbl with class A's, and now b is just an A with some extra stuff allocated in its memory block.

Don't do this in production code. This is a terrible thing to do in production code, or any code you intend to use other than to see how D's object model works.