Jump to page: 1 2
Thread overview
D casting broke?
Jun 19, 2016
Joerg Joergonson
Jun 19, 2016
Adam D. Ruppe
Jun 19, 2016
Joerg Joergonson
Jun 19, 2016
David Nadlinger
Jun 19, 2016
ag0aep6g
Jun 19, 2016
Joerg Joergonson
Jun 19, 2016
ag0aep6g
Jun 19, 2016
Joerg Joergonson
Jun 20, 2016
ag0aep6g
Jun 20, 2016
Joerg Joergonson
Jun 20, 2016
ag0aep6g
Jun 21, 2016
Joerg Joergonson
Jun 21, 2016
H. S. Teoh
Jun 21, 2016
Joerg Joergonson
Jun 21, 2016
Joerg Joergonson
June 19, 2016
import std.stdio;

class X { X Parent; }

class x : X { }

class a : x
{
    void Do()
    {
        auto p = cast(A!a)(this.Parent);  // works as long as we are in A
        assert(p !is null);
    }
}


class A(T : a) : X
{
    X Parent = new X();
    T _y = 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._y.Parent = _A;
    _B.Parent = _B;    // works if _A, since _B is of type _A it should still work
    _B._y.Parent = _B; // ...
			
    _A._y.Do();
    _B._y.Do();
	
}


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

So, my code crashes because when I do the cast in the base class A!T, and it is used in the derived class(which the cast should be valid), a null pointer is created which is used in the base class. (Basically, B!T doesn't have to have any code in it, just create the object, the code in A!T then will crash if such a cast exists)


The obviously question: Is there a simple way around this? I'd ask, how long to fix but that might take months/years. I can override in b and duplicate code but why? That makes life more difficult than having things work as they should(having to maintain twice is much code is not a solution).








June 19, 2016
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.



> The obviously question: Is there a simple way around this?

What are you actually trying to do?
June 19, 2016
On 06/19/2016 09:59 PM, Joerg Joergonson wrote:
> This should be completely valid since B!T' obviously derives from A!T
> directly

ok

> and we see that T' derives from b which derives from a
> directly.

ok

> So B!b is an entirely derived from A!a

No. B!b is derived from A!b, not from A!a. `b` being derived from `a` does not make A!b derived from A!a.

Here is the full inheritance tree:

X
├─x
│ └─a
│   └─b
├─A!a
└─A!b
  └─B!b
June 19, 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.





















June 19, 2016
On Sunday, 19 June 2016 at 20:21:35 UTC, ag0aep6g wrote:
> On 06/19/2016 09:59 PM, Joerg Joergonson wrote:
>> This should be completely valid since B!T' obviously derives from A!T
>> directly
>
> ok
>
>> and we see that T' derives from b which derives from a
>> directly.
>
> ok
>
>> So B!b is an entirely derived from A!a
>
> No. B!b is derived from A!b, not from A!a. `b` being derived from `a` does not make A!b derived from A!a.

why not? This doesn't seem logical!

> Here is the full inheritance tree:
>
> X
> ├─x
> │ └─a
> │   └─b
> ├─A!a
> └─A!b
>   └─B!b


But b is derived from a. Your tree completely ignores under A.

> X
> ├─x
> │ └─a
> │   └─b
> ├─A!a
    |  \
    └─A!b
      └─B!b

Just because D doesn't understand this logical consistency between inheritance doesn't mean D is right. (Hence, why D's type system is broke)


In fact, the tree should look like this:

> X
> ├─x
> │ └─a
> │   └─b
> └─A!x
    │  \
    └─A!a
      │  \
      └─A!b
        │  \
        └─B!b


Basically you are treating A!a and A!b as if a and be have no relationship. BUT THEY DO! If you don't take that into account then your wrong.

Simply stating how D behaves is not proof of why it is right or wrong.

This is very easy to see, check my other post using a Widget Example and you will see that it is a logical extension.

D doesn't check parameter inheritance relationships properly. A!b is a derivation of A!a.

import std.stdio;
class a { }
class b : a { }

class A(T : a)
{
   T x;
}



void main(string[] argv)
{
    auto _A = new A!a();
    auto _C = new A!b();

    auto p = cast(A!a)_C;
}

p is null. My example with B is irrelevant. The issue is with the parameter.

As you can see, D thinks that A!b and A!a are completely unrelated... as do you and arsd.

Do you seriously think this is the case? That

class b : a { }

and

class b { }

effectively mean the same with regards to A?

The whole problem comes about at this line:

auto p = cast(A!a)_C;

We are trying to cast `T x` in C, which is effectively `b x` to `a x`.

Is that not possible to do? We do it all the time, right?

That is my point. D doesn't see that it can do this but it can, if not, prove me wrong.














June 19, 2016
On Sunday, 19 June 2016 at 21:06:43 UTC, Joerg Joergonson wrote:
> 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.

You are wrong.

The array example given by Adam is actually a neat illustration of precisely your question if you just think of `T[]` as `Slice!T`. In other words, `Slice!b` cannot be derived from `Slice!a` automatically, since then you could use the `Slice!a` interface to push `a` instances into your `Slice!b`.

If that's a bit too far of a mental leap to make, you can certainly find a more thorough illustration of the concepts at play here by looking up covariance vs. contravariance in containers/generics online.

 — David
June 20, 2016
On 06/19/2016 11:19 PM, Joerg Joergonson wrote:
> On Sunday, 19 June 2016 at 20:21:35 UTC, ag0aep6g wrote:
[...]
>> No. B!b is derived from A!b, not from A!a. `b` being derived from `a`
>> does not make A!b derived from A!a.
>
> why not? This doesn't seem logical!

Template parameters simply don't work like that. A template can result in completely unrelated types based on template parameters.

For example:

    template C(T)
    {
        static if (is(T == A)) class C {}
        else static if(is(T == B)) alias C = int;
        else struct C {int x;}
    }

As you see there can't be any inheritance relation between the different instantiations of the C template here. Having such a relation for different instantiation that result in classes would be a weird special case.

There's probably a really simple and obvious reason why that special would be a bad idea in itself, but I'm not able to point it out. Maybe make a thread in the General group. I think the language people tend to focus their attention there.

Criticism and improvement proposals are also better directed to the General group.

>> Here is the full inheritance tree:
>>
>> X
>> ├─x
>> │ └─a
>> │   └─b
>> ├─A!a
>> └─A!b
>>   └─B!b
>
>
> But b is derived from a.

Yeah, that's right there in the middle.

> Your tree completely ignores under A.

Clearly, there's B!b under A!b. That's it. Nothing exists below B!b. B!a doesn't exist either. A!a is on a different branch. I don't think I've missed anything.

[...]
> Just because D doesn't understand this logical consistency between
> inheritance doesn't mean D is right. (Hence, why D's type system is broke)
>
>
> In fact, the tree should look like this:
>
>> X
>> ├─x
>> │ └─a
>> │   └─b
>> └─A!x
>      │  \
>      └─A!a
>        │  \
>        └─A!b
>          │  \
>          └─B!b

I'm having trouble reading this. A!x isn't valid, as the constraint on A says `T : a`, but x doesn't satisfy that.

I also don't understand what the backslashes mean. They just repeat the other lines, don't they? Or do they connect x, a, and b? That's already expressed in the upper section.

As for A!b being below A!a, I can only repeat that this inheritance is not implied. You would have to spell it out explicitly for the compiler to pick it up.

> Basically you are treating A!a and A!b as if a and be have no
> relationship. BUT THEY DO!

Well, to the compiler they don't.

> If you don't take that into account then your
> wrong.
>
> Simply stating how D behaves is not proof of why it is right or wrong.

For discussions about how D should behave, please post to General. Here in the Learn group I (and I think we) tend to focus on how D works or is meant to work. The way you think it should behave here is not how it's meant to work by the designers and implementors.

[...]
> import std.stdio;
> class a { }
> class b : a { }
>
> class A(T : a)
> {
>     T x;
> }
>
>
>
> void main(string[] argv)
> {
>      auto _A = new A!a();
>      auto _C = new A!b();
>
>      auto p = cast(A!a)_C;
> }
>
> p is null. My example with B is irrelevant. The issue is with the
> parameter.
>
> As you can see, D thinks that A!b and A!a are completely unrelated... as
> do you and arsd.
>
> Do you seriously think this is the case? That
>
> class b : a { }
>
> and
>
> class b { }
>
> effectively mean the same with regards to A?

Yes. A!a is a class like this: `class A!a {a x;}`. A!b is this: `class A!b {b x;}`. The generated classes have members that have an inheritance relationship, but that doesn't imply an inheritance relationship between A!a and A!b.

> The whole problem comes about at this line:
>
> auto p = cast(A!a)_C;
>
> We are trying to cast `T x` in C, which is effectively `b x` to `a x`.

No. You try to cast from one class type to another, that's different from casting the members.

You can have two classes with exactly the same members, yet they're not (directly) castable to each other when there's no inheritance relation:

    class A {int x;}
    class B {int x;}
    void main()
    {
        assert((cast(B) new A) is null); /* holds */
        assert((cast(A) new B) is null); /* holds */
    }

Maybe this is the crux? I'd expect you to be of the opinion that those casts should work.

Of course, you can force an unsafe conversion by casting to void* first:

    cast(B) cast(void*) new A

A reinterpret-style cast would also work:

    A a = new A;
    B b = * cast(B*) &a;

I suppose a simple cast doesn't do that because it's deemed more useful to get null when the inheritance doesn't work out, even when the member layout would work out. This way `cast` can be used for downcasts in a safe manner.

Because, when there is no inheritance relation, that means there is no guarantee that a class can be used in place of another one, even when the fields are compatible. Consider A and B having methods with the same name that do completely unrelated things. Converting accidentally from one to the other would be bad.
June 19, 2016
On Sunday, 19 June 2016 at 23:00:03 UTC, ag0aep6g wrote:
> On 06/19/2016 11:19 PM, Joerg Joergonson wrote:
>> On Sunday, 19 June 2016 at 20:21:35 UTC, ag0aep6g wrote:
> [...]
>>> No. B!b is derived from A!b, not from A!a. `b` being derived from `a`
>>> does not make A!b derived from A!a.
>>
>> why not? This doesn't seem logical!
>
> Template parameters simply don't work like that. A template can result in completely unrelated types based on template parameters.
>
> For example:
>
>     template C(T)
>     {
>         static if (is(T == A)) class C {}
>         else static if(is(T == B)) alias C = int;
>         else struct C {int x;}
>     }
>
> As you see there can't be any inheritance relation between the different instantiations of the C template here. Having such a relation for different instantiation that result in classes would be a weird special case.
>
> There's probably a really simple and obvious reason why that special would be a bad idea in itself, but I'm not able to point it out. Maybe make a thread in the General group. I think the language people tend to focus their attention there.
>
> Criticism and improvement proposals are also better directed to the General group.
>
>>> Here is the full inheritance tree:
>>>
>>> X
>>> ├─x
>>> │ └─a
>>> │   └─b
>>> ├─A!a
>>> └─A!b
>>>   └─B!b
>>
>>
>> But b is derived from a.
>
> Yeah, that's right there in the middle.
>
>> Your tree completely ignores under A.
>
> Clearly, there's B!b under A!b. That's it. Nothing exists below B!b. B!a doesn't exist either. A!a is on a different branch. I don't think I've missed anything.
>
> [...]
>> Just because D doesn't understand this logical consistency between
>> inheritance doesn't mean D is right. (Hence, why D's type system is broke)
>>
>>
>> In fact, the tree should look like this:
>>
>>> X
>>> ├─x
>>> │ └─a
>>> │   └─b
>>> └─A!x
>>      │  \
>>      └─A!a
>>        │  \
>>        └─A!b
>>          │  \
>>          └─B!b
>
> I'm having trouble reading this. A!x isn't valid, as the constraint on A says `T : a`, but x doesn't satisfy that.
>

no, my point was that a is derived from x, b from a, hence we have a derivation change x -> a -> b. So, similarly A!x -> A!a -> A!b

> I also don't understand what the backslashes mean. They just repeat the other lines, don't they? Or do they connect x, a, and b? That's already expressed in the upper section.

Yes, they connect them. Yes, exactly, But this time they connect in terms of A. The compiler doesn't seem to use the fact that x -> a -> -> b to infer anything about A!x -> A!a -> A!b, and it should.

>
> As for A!b being below A!a, I can only repeat that this inheritance is not implied. You would have to spell it out explicitly for the compiler to pick it up.
>

Maybe so. But that is kinda my point.

>> Basically you are treating A!a and A!b as if a and be have no
>> relationship. BUT THEY DO!
>
> Well, to the compiler they don't.

Yes, exactly.


basically I am doing a cast(A!a)this because all I care about is this in terms of A!a. If it's a B!b or B!a or A!b is immaterial to me since casting to A!a gets me what I need. (It's no different than if I was doing simpler inheritance) D doesn't understand this and there is no simple fix that anyone has presented.

The casting is the only problem and there is no reason it should fail because the object I am casting on can be cast to it's base class.

If we assume that I'm wrong or D can't do this because of a bug or shortsightedness... the issue remains on how to make it work.

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.

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.

If I duplicate Do() and put it in SliderItem and change the cast to use Slider/SliderItem, it works. The cast is the only problem. Not the objects themselves.

If this wasn't a parameterized class, everything would work. If I made Slider use ButtonItems everything would work. It's only because I derived a new type SldierItem from ButtonItem that breaks and only at the cast.


I'm almost 100% sure this should work and haven't seen anyone actually show why this would not work(the examples given are simply wrong and are not understanding the problem... or there is more going than anyone has said).

Again, do we not expect derived types to be able to be down cast? Just because they are parameterized on other types doesn't change this fact? It just makes it more complicated because we now have to have type logic on the parameterized types.


I tried to avoid posting in General but I guess I will have to.


June 20, 2016
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;

> 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.

June 20, 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.













« First   ‹ Prev
1 2