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