D casting broke?
Joerg Joergonson via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Sun Jun 19 14:06:43 PDT 2016
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.
More information about the Digitalmars-d-learn
mailing list