D casting broke?

Joerg Joergonson via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Jun 20 19:09:50 PDT 2016


On Monday, 20 June 2016 at 23:10:14 UTC, ag0aep6g wrote:
> On 06/20/2016 11:33 PM, Joerg Joergonson wrote:
>> On Monday, 20 June 2016 at 10:38:12 UTC, ag0aep6g wrote:
> [...]
>>> 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.
>
> Ok, so you suppose there should be a inheritance implied when 
> there's an inheritance relation between template arguments. 
> I.e., A!a should be a superclass of A!b when a is a superclass 
> of b.


Well, Now I don't think this is possible in all circumstances but 
I really fail to see how it is any different than any normal 
cast(the examples give using arrays and storing junk in them). If 
one is consistent, then I think it is valid and works... but it 
might require the type system to be too restrictive to be of much 
use.

But yes, the last line was what I have been stating. I don't 
think treating A!a and A!b as completely different when a is 
related to b.

Lets suppose A -> B means B is derived from A. That is, any 
object of B can be cast to A because the memory layout of A is 
contained in B and any object of B can be accessed as if it were 
an A.

Template parameters also can have this property since they are 
types.

Hence

We have two scenarios:

class A(T);
class a;
class b;
A!a -> A!b   // false, because, while both sides contain an A, 
and there is overlap(this is a partial relationships for 
everything that is identical in both types... that is, all the 
stuff that doesn't depend on a and b).


AND

class A(T);
class a;
class b : a;

A!a -> A!b  // ? This seems like an obvious logical consequence 
of inheritance and the ->.

This is the way I am thinking about it.

in A!b, everything that depends on b also depends on a because b 
is basically `more than` a.

So, if we cast A!b down to A!a, and IF A!b never uses the "extra" 
part of b that makes it different than a, then the cast should 
pass.

That is, if all b's in A!b could be cast to a and the code work, 
then A!b should be cast-able to A!a.

Obviously if A!b uses b objects in a way that can be treated like 
a's then casting will break if we use a's. (but only if we use 
a's when b's are expected).

The problem is more complex than just a one time fits all rule 
which the D type system uses.

A simple example is basically my problem:

class A(T);
class a { stuff using a...}
class b : a { }

In this case, down casting works because b doesn't do anything 
different than a. Effective b is exactly an a so there is no 
possible way to have any problems.

So, cast(A!a)A!b should pass.  Surely the compiler can be smart 
enough to figure this out? It's as if b is just an alias for a.

> This whole Button/Slider/ButtonItem/SliderItem/etc setup may be 
> too complex for me.
>
> This is what I understand you have right now, basically:
>
>     class ButtonItem {}
>     class SliderItem : ButtonItem {}
>     class Widget {}
>     class Button(T : ButtonItem) : Widget { T[] items; }
>     class Slider(T : SliderItem) : Button!T {}
>
> And I guess the point of having Button templated is so that 
> Slider gets a `SliderItem[] items`, which is more restricted 
> and nicer to use than a `ButtonItem[] items` would be.

Yes. And it makes sense to do that, right? Because, while, we 
could use a ButtonItem for Sliders, we would expect since a 
ButtonItem goes with Buttons that SliderItems should go with 
Sliders?

E.g., our SliderItems might need to have somewhat different 
behavior than ButtonItems... the thing that makes them "go with 
the slider".

Sure, we can include that info in slider but we shouldn't have to.

For example. In my code I have a Moved value for SliderItems. 
This tells you have far they have moved(been "slid"). Button 
Items should have this cause they can't move. The Slider type 
doesn't really care about how much they have been slid. But it 
fits nicely in SliderItem.

When I downcast to cast(Button!ButtonItem)Slider, I never use 
anything from Slider/SliderItem because I'm dealing with the 
Button!ButtonItem portion of Slider(the things that makes it a 
Button).

Of course, I could, in that part of the code, end up adding a 
ButtonItem to Slider and that wouldn't be logical. That just 
never happens in my code because of the design. Items are 
statically added to the classes they are part of and items are 
never added or removed... Since it's a gui there is no need for 
dynamic creation(at least in my apps). I even if I want to do 
some dynamic creation, it's not a huge deal because I'll just use 
a Factory to create the objects properly.




> Maybe un-templatizing Button and Slider is worth exploring. 
> I.e.:
>
>     class Button : Widget { ButtonItem[] items; }
>     class Slider : Button {}
>
> Button itself probably doesn't need the most derived type, and 
> can work with  with just ButtonItem, right? Of course, Slider 
> would have to make sure that only SliderItems find their way 
> into items, and it would need to cast accordingly when it wants 
> to use an item as a SliderItem.
>
> Just a thought. Seems simpler than the template stuff, but you 
> may have other reasons for the templates which I didn't catch.

The only difference is I would have to do a cast every time I use 
a SliderItem in Slider. That is kinda what I was trying to avoid.

That was the point of inheriting from ButtonItem and letting the 
compiler know that SliderItem is ButtonItem. I mistakenly thought 
that it would understand there is a separation between the two 
types(there are only two types Button!ButtonItem and 
Slider!SliderItem... not Button!SliderItem or Button!X or 
Slider!ButtonItem or whatever).

Remember, all this is because parent is a widget and I need. If I 
could do something like cast(this)(this.Parent) it would be great 
and always valid in the code I would create. (because if this = 
ButtonItem then this.Parent is always a Button. Similarly for 
Slider/SliderItem).

That is, I never actually do anything like casting a 
Button!SliderItem down to Button!ButtonItem.

The cast is always on `both sides`, so to speak. Maybe that is 
the fundamental difference. (I never break the cast up in to two 
parts like

cast(Button!ButtonItem)cast(Button!SliderItem)slider.

It's always done together because it doesn't make sense to do 
them partially.

Diagrammatically:

Give,
    Button -> Slider
    ButtonItem -> SliderItem

Then

    Button!ButtonItem -> Slider!SliderItem

Not

    Button!ButtonItem -> Button!SliderItem -> Slider!SliderItem

I don't know if there is a difference, I imagine knowing that the 
casting always occurs together means something. The intermediate 
case, which is the problem, never occurs, so to speak.

Of course, I have no way informed the compiler of that... and it 
wouldn't understand it if I did. Button!SliderItem just makes no 
sense in my design, and if the compiler could understand that, 
then surely it could reason a little better about things?


Anyways, it's all kinda moot. I already copied and pasted the 
code.







More information about the Digitalmars-d-learn mailing list