January 04, 2019
On Friday, 4 January 2019 at 21:08:24 UTC, Ali Çehreli wrote:
> [...]
> In this case, casting is using the B object through it's A interface. The overridden behavior does not change.

Yeah... This was my mistake.

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

Nice :)

> [...]

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

Well, not really. I was just astonished that casting is not _the_ forge tool. Then, I checked, that the type indeed changes and wondered why the behavior was still from the object before casting.

But I assume, as in an A-type an A object, as well as a B object, can be stored, and the creation is under my own control - a use case would be some corner case... And the default virtual definition explains the behavior very well...
January 04, 2019
On Friday, 4 January 2019 at 21:47:59 UTC, Neia Neutuladh wrote:
> 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.

I see... That's cool nevertheless!
January 04, 2019
On 01/04/2019 01:08 PM, Ali Çehreli wrote:

> There is only one vtbl per class object

Correcting myself after reading Neia Neutuladh's post: I should have said "There is only one vtbl per class type". Every class object has a vtbl pointer that points at its type's vtbl.

So, it's normally two pointer hops to execute a virtual function:

1) Get the object's vtbl pointer to reach the vtbl of that type; pseudo code:

  v = o.vtbl_ptr

2) Get the function pointer from a slot in that table (assuming foo() happens to be at index 5); pseudo code:

  f = v[5]  // This must be a generic type like void*

3) Call the function by passing arguments; pseudo code:

  alias F = TheActualTypeOfTheMemberFunction;
  (cast(F)f)(42, "hello", ...)

Ali

January 05, 2019
On Friday, 4 January 2019 at 20:21:56 UTC, Steven Schveighoffer wrote:
>> 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.

I overlooked that Alex wrote

   class A

and not

   class A : D

which I came across in the examples in #10 and #11 of

   https://dlang.org/spec/interface.html

and to which I referred to in my OP.

> https://dlang.org/spec/expression.html#cast_expressions
>
> See parts 2 and 3.

   part 2
   "Any casting of a class reference to a derived class reference
   is done with a runtime check to make sure it really is a downcast.
   null is the result if it isn't."

Part 2 is about downcasting. Does that apply here? Due to the omission
of ": D" in Alex' example the cast is not a cast "to a derived class
reference" and hence this part does not apply. OTOH upcasting is also
not covered by part 2 and seems to be legal:

part 2 at its face value:
```part2.d
import std.stdio;

class A {
   void foo () { __FUNCTION__.writeln; }
}

class B : A {
   override void foo () { __FUNCTION__.writeln; }
}

void main ()
{
   auto x = new B;
   auto u = cast (A) x;
   typeof (u).stringof.writeln;
   u.foo;
}
```
$ ./part2
A
part2.B.foo


January 07, 2019
On 1/4/19 7:16 PM, kdevel wrote:
> On Friday, 4 January 2019 at 20:21:56 UTC, Steven Schveighoffer wrote:
>>> 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.
> 
> I overlooked that Alex wrote
> 
>     class A
> 
> and not
> 
>     class A : D
> 
> which I came across in the examples in #10 and #11 of
> 
>     https://dlang.org/spec/interface.html
> 
> and to which I referred to in my OP.
> 
>> https://dlang.org/spec/expression.html#cast_expressions
>>
>> See parts 2 and 3.
> 
>     part 2
>     "Any casting of a class reference to a derived class reference
>     is done with a runtime check to make sure it really is a downcast.
>     null is the result if it isn't."
> 
> Part 2 is about downcasting. Does that apply here? 

Yes. If the requested interface or class isn't a base class, or a base interface, then a downcast must be involved. Essentially the downcast is a runtime check to see if this class instance actually is a derived one, or a derived class implements the specified interface. D does not have multiple inheritance.

> Due to the omission
> of ": D" in Alex' example the cast is not a cast "to a derived class
> reference" and hence this part does not apply.

Of course it applies :) The fact that the class is not an instance of D in a derived class means that it returns null. The spec statement could be worded better.

> OTOH upcasting is also
> not covered by part 2 and seems to be legal:

Upcasting is trivial, and the compiler will not even spend time doing a runtime check, it knows how to do this at compile time. It doesn't even require a cast.

Just like casting a short to an int doesn't require a cast, but you are free to put one in if you like.

-Steve
January 10, 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.

As for class B, it has already included foo(), even if it doesn't override this method. So, is it necessary to override it again? It not always needed to override foo(). Sometimes, we just want to keep it as the one in class A and override it as necessary. Honestly hope that the compiler can do this.
1 2 3
Next ›   Last »