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.













Reply via email to