On Sunday, 19 June 2016 at 20:18:14 UTC, Adam D. Ruppe wrote:
On Sunday, 19 June 2016 at 19:59:28 UTC, Joerg Joergonson wrote:
This should be completely valid since B!T' obviously derives from A!T directly and we see that T' derives from b which derives from a directly. So B!b is an entirely derived from A!a and hence the cast should be successful

I don't see how you think that. Here's the parent chain:

B!b -> A!b -> X -> x -> Object

There's no A!a in there, the cast is failing correctly.

Just because `b` is a child of `a` doesn't mean that `A!b` is the same as `A!a`. Consider an array:

MyClass[] arr;
arr ~= new MyClass(); // ok cool

Object[] obj_arr = arr; // won't work! because...
obj_arr[0] = new RandomClass(); // this would compile...

// but obj_arr and arr reference the same data, so now:
arr[0] is typed MyClass... but is actually RandomClass! It'd crash horribly.



Array is just one example of where converting A!b to A!a is problematic. The same principle can apply anywhere, so it won't implicitly cast them.


I'm not saying they are the same! They don't have to be the same. That is the whole point of inheritance and casting. A!b is derived from A!a if b is derived from a, is it not? If not, then I am wrong, if so then D casting has a bug.





The obviously question: Is there a simple way around this?

What are you actually trying to do?

Do you really want to know? It's very simple and logical and might blow your mind and show you it's more complex than the example you have

I have a widget class

class Widget { Widget Parent; }

I have a button item class

class ButtonItem : Widget;

I have a button class

class Button : Widget { ButtonItem[] items; }

Make sense so far? Very logical and all that?

NOW, suppose I want to create a derived type from button? Say, a slider that effectively is a button that can move around:

class Slider : Button { }

So far so good, right?

WRONG! Slider shouldn't contain button items but slider items! How to get around this?


class SliderItem : ButtonItem; (since sliders are buttons slider items should be button items, right?)

So, to make this work we have to parameterize Button.

class Button(T : ButtonItem) : Widget { T[] items; }


So far so good!

and

class SliderItem : ButtonItem;

Very logical, Spock would be proud!

Now

class Slider(T : SliderItem) : Button!T;


Very logical still, right? Because T is of type SliderItem which is of type ButtonItem and therefor Button!SliderItem is of type Button!ButtonItem.

Everything works, right? Of course, I have a working example!

Slider!T is a type of Button!T, Slider!SliderItem is a type of Button!ButtonItem. Surely items in Button can hold SliderItems? (since they are derived from ButtonItems and ButtonItems work)

Ok, everything works!

Now what?

Well, In ButtonItem, I have to get the parent items to do some work. i.e.,

Work(Parent.items);

But this can't work because Parent is a Widget, so we must cast to a Button.

Work((cast(Button)Parent).items);

But this doesn't work because Button is parameterized. so

Work((cast(Button!T)Parent).items);

But this doesn't work because there is no T in ButtonItem, which is were we are at, so lets cast to a ButtonItem.

Work((cast(Button!ButtonItem)Parent).items);

This works!! At least as long as we are in ButtonItems!

When our parent is a Slider then the cast fails and everything goes to shit.

I have to duplicate the code AND only change the cast to cast(Slider!SliderItem)Parent and then everything works.


But, you might think that Slider!SliderItem is somehow not derived from Button!ButtonItem but it is, it was created to be that way by god himself.


Widget -> Button     -> Slider
             |             |
       -> ButtonItem -> SliderItem


First, for one, everything is an Widget, lets get that clear.

Second, Slider!SliderItem is just a wrapper to Button!ButtonItem. This allows us to add additional slider based code to a button to make it act like a slider(which is more than a button, but still a button).


This is just a 2D case of the 1D inheritance Slider is a Button. Just because we add a parameterization to it DOESN'T NECESSARILY change that. If the parameter also has an inheritance relationship then we have a fully valid inheritance relationship.


e.g., Slider!Pocahontas has only a partial inheritance to Button!ButtonItem because Pocahontas is not in any way derived from ButtonItem. But if Pocahontas is fully derived from ButtonItem then the partial inheritance is full inheritance.

Do you understand that?

Else, if you were correct, something like Slider!Widget and Button!Widget would never be relatable. Yet it's obvious that it is trivially relatable because Widget = Widget. In my case the only difference is SliderItem derives from ButtonItem.

We can always cast to a super class. ALWAYS! Slider!SliderItem is a super class of Button!ButtonItem.

In fact, if we had some way to do partial casting, we could write it this way

typeof(x = cast(Slider!ButtonItem)(SliderSliderItem)) = Slider!ButtonItem;

then

typeof(cast(Button!ButtonItem)x) = Button!ButtonItem


This may make more sense to you.

The first cast forces the items to look like button items, which is ok but slider items look like button items, right?

The second cast forces this entire thing now look like a Button, but that too is ok because a Slider is a Button.


Here's more code that shows where the partial casting fails:


import std.stdio;

class X
{
        X Parent;
}

class x : X
{

}

class a : x
{
        void Do()
        {
                auto pp = cast(B!b)Parent;
                if (pp !is null)
                {
                        auto ppp = cast(A!b)pp;
                        if (ppp !is null)
                        {
                                auto pppp = cast(A!a)ppp;
                        }
                }
                auto p = cast(A!a)Parent;
                assert(p !is null);
        }

}


class A(T : a) : X
{
        X Parent = new X();
        T _a = new T();
}

class b : a { }
class B(T : b) : A!T { }

void main(string[] argv)
{
    auto _A = new A!a();
    auto _B = new B!b();
        _A.Parent = _A;
        _A._a.Parent = _A;
        _B.Parent = _B;
        _B._a.Parent = _B;
                        
        _A._a.Do();

        _B._a.Do();
        
    auto xxx = cast(A!a)_B;     

        
}



As you can see, the problem is that D thinks A!b can't be cast to A!a. This is obviously easy to see that it can because

a[] Items;

can clearly hold objects of type b. D is ignoring this fact because it doesn't check inheritance on the parameters properly. It thinks they are unrelated and this is simply not true.

QED.





















Reply via email to