D casting broke?

Joerg Joergonson via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Jun 20 14:33:25 PDT 2016


On Monday, 20 June 2016 at 10:38:12 UTC, ag0aep6g wrote:
> On 06/20/2016 01:40 AM, Joerg Joergonson wrote:
>> public class Button(T : ButtonItem) : Widget { ... }
>> public class ButtonItem : Item
>> {
>>   void Do() { auto parent = 
>> (cast(Button!ButtonItem)this.Parent); }
>>   ...
>> }
>>
>> All this works great! As long as Do is not being called from a 
>> derived
>> class
>>
>> public class Slider(T : SliderItem) : Button!T { }
>> public class SliderItem : ButtonItem { }
>>
>>
>> The last two classes are truly empty. Now, when I use a Slider 
>> object,
>> things go to shit because the cast is invalid. this.Parent is 
>> of type
>> Slider!SliderItem.
>
> It's the same setup as with the A and B things, right?
>
> Parent is a Widget that holds a Slider!SliderItem. That's fine 
> because Slider!SliderItem is derived from Button!SliderItem 
> which is derived from Widget.
>
> But Button!SliderItem does not derive from Button!ButtonItem. 
> They both derive from Widget. So the cast fails.
>
> But you think it should succeed, of course.
>
> Is your position that Button!SliderItem should derive/inherit 
> from Button!ButtonItem, enabling the cast, or do you suppose 
> the cast should succeed because the fields are compatible?
>
> I.e., should this work?
>
>     class A {int x;}
>     class B {int x;}
>     A a;
>     B b = cast(B) a;
>

No, not at all. first, A and B are not related, so casting makes 
no sense unless there is a conversion(opCast) or whatever, but 
that is done by the user.

This is exactly opposite of what I am talking about.

>> SliderItem only sets the array type. So in Slider, I end up 
>> with a
>> SliderItem[] type then in ButtonItem's Do(which gets called 
>> since
>> SliderItem doesn't override), it tries to cast that down to a
>> ButtonItem. It should work. There is no reason it shouldn't 
>> logically.
>> There is no up casting.
>
> Some terminology clarification: Casting from SliderItem to 
> ButtonItem is upcasting. The other direction would be 
> downcasting. Upcasting a single object is trivial and can be 
> done implicitly. Downcasting must be done explicitly and may 
> yield null.
>
> You say that you cast from SliderItem to ButtonItem. But that's 
> not what's done in your snippet above. You try to cast from 
> Button!SliderItem to Button!ButtonItem. Completely different 
> operation.

Ok, I might have used terminology backwards.

The problem is more complex then maybe I demonstrated and anyone 
has mentioned. Yes, there might be an issue with 
downcasting/contravariance and all that. I think those problems 
though, are general issues.

The real issue is that Slider!SliderItem doesn't override a 
method that is called when a Slider!SliderItem object is used. 
The method, in Button!ButtonItem casts a Widget to 
Button!ButtonItem just fine because inside Button!ButtonItem, the 
Widget is of type Button!ButtonItem.

When we are inside a Slider!SliderItem though, the same code is 
executed with the same cast(using Button!ButtonItem) and this 
fails because if it succedded we could potentially store 
ButtonItems as SliderItems(being an "downcast", or similar to the 
example you gave).

This is the code that has the problem.

It is used inside ButtonItem

auto parent = (cast(cButton!cButtonItem)this.Parent);	

and not overridden in SliderItem, but still executed in there at 
some point.

this.Parent is a Slider!SliderItem and I need the cast to work so 
I can access the Item array.

But in Slider the array is of type SliderItem, not ButtonItem as 
I initially thought, because I particularized it.

Hence there is a "hidden" downcast going on. Now, in my case, it 
doesn't matter because I never store items in the wrong type. The 
code is automatically generated and creates the correct type for 
the correct storage class. I realize now though that it is 
possible that it can be done(If I just appended a ButtonItem to 
the array in ButtonItem, then when SliderItem is called, then 
"non-overridden" method will store a ButtonItem in the SliderItem 
array.


So, this isn't really a problem with casting so much as it is 
with the complexity of the inheritence. By doing it the way I 
did, to try to keep the Types and parameters synced and because 
they inherit from each other, there can be problems.

To get what I want, which is probably impossible, I'd need the 
cast to automatically cast in the correct type depending on where 
it is being executed:

auto parent = (cast(typeof(parent)!this)this.Parent);	

Which, of course, is impossible to do at compile time.

I only need parent to check if it's items exist in the array

if (parent.HoveredItems.canFind(this))


That is all it is used for, so there is no problem with it, but 
if I don't cast I obviously can't access the HoverdItems... but 
then the cast breaks for derived classes and parent is null.

To make it work I'd have to add, say, something like 
containsHovered to Widget. Then I wouldn't need the cast, but 
this doesn't make a lot of sense, since Widget doesn't contain an 
array of HoveredItems.

Alternatively I could add an interface to inherit that contains 
something like that and it would work...


Regardless though, it requires adding a lot of extra code 
duplication just to get the cast "to pass" when the only thing it 
is to prevent is storing a less derived type in a more derived 
type.... which I never do. I don't even store any types in 
anything. Everything is setup from the get go and pretty much 
static. (Although, again, I see that in general this is not the 
case)

I still think it is a grey area though.

e.g., List<string> and List<Mystring> may be problemmatic, I 
should still be able to cast List<Mystring> to List<string> and 
use elements, but not store them.

so something like

cast(out Button!ButtonItem)sliderSliderItem; could work, the out 
being obvious that sliderSliderItem is never used to store 
ButtonItems.

In any case, D can't do this easily it seems so it's all moot.  I 
just copied and pasted the code and changed the cast. It lets me 
get on with life.

Thanks.















More information about the Digitalmars-d-learn mailing list