www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - D casting broke?

reply Joerg Joergonson <JJoergonson gmail.com> writes:
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).
Jun 19 2016
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
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?
Jun 19 2016
parent reply Joerg Joergonson <JJoergonson gmail.com> writes:
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.
Jun 19 2016
parent David Nadlinger <code klickverbot.at> writes:
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
Jun 19 2016
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
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
Jun 19 2016
parent reply Joerg Joergonson <JJoergonson gmail.com> writes:
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.
Jun 19 2016
next sibling parent reply ag0aep6g <anonymous example.com> writes:
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.
Jun 19 2016
parent reply Joerg Joergonson <JJoergonson gmail.com> writes:
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.
Jun 19 2016
parent reply ag0aep6g <anonymous example.com> writes:
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.
Jun 20 2016
parent reply Joerg Joergonson <JJoergonson gmail.com> writes:
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.
Jun 20 2016
parent reply ag0aep6g <anonymous example.com> writes:
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. I suspect that there would be major hurdles to overcome to make class templates work like that. And even if it can be made to work, it would be a surprising special case. At the core, templates are a simple concept: on instantiation, just replace the parameter with the argument. Adding some automatic inheritance would make things complicated. [...]
 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.
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. 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. [...]
 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.
As I mentioned in an earlier post, you can cast between unrelated class types by casting to void* first: `cast(Foo) cast(void*) bar`. It's highly unsafe, of course. Maybe it's even relying on undefined behavior, because there is no guarantee that the class layout is compatible. Regarding the list example, it's of course better to cast individual items when using them.
Jun 20 2016
parent reply Joerg Joergonson <JJoergonson gmail.com> writes:
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.
Jun 20 2016
parent reply "H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
On Tue, Jun 21, 2016 at 02:09:50AM +0000, Joerg Joergonson via
Digitalmars-d-learn wrote:
[...]
 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.
Correct.
 Template parameters also can have this property since they are types.
[...] The template *parameters* can also have this property. But the *template* itself may not. Contrived example: class A { int x; } class B : A { int y; } struct Templ(C : class) { int[C.sizeof] data; } Templ!A a; Templ!B b; a = b; // should this be allowed? It should be clear that allowing this would cause problems, because in spite of the relationship between A and B, and hence the relationship between the template arguments of a and b, the same relationship does not hold between Templ!A and Templ!B (note that .data is an array of ints, not ubytes, and may not contain data in any layout that has any corresponds with the relationship between A and B). Another contrived example: class A { int x; } class B : A { int y; } struct Templ(C : class) { static if (C.sizeof > 4) { string x; } else { float y; } } Allowing implicit casting from Templ!B to Templ!A would not make sense, because even though the respective template arguments have an inheritance relationship, the Templ instantiation made from these classes has a completely unrelated and incompatible implementation. Now granted, these are contrived examples, and in real-life we may not have any real application that requires such strange code. However, the language itself allows such constructs, and therefore the compiler cannot blindly assume any relationship between Templ!A and Templ!B even though there is a clear relationship between A and B themselves. What should be done if we wish to allow converting Templ!B to Templ!A, though? One way (though this still does not allow implicit casting) is to use opCast: struct Templ(C : class) { ... // implementation here auto opCast(D : class) if (is(C : D)) // C must be a base class of D { ... // do something here to make the conversion // valid. Maybe something as simple as: return cast(Templ!D) this; // (provided that there are no memory layout // problems in Templ's implementation, of // course). } } Implementing this using opCast actually gives us an even more powerful tool: provided it is actually possible to convert between potentially incompatible binary layouts of Templ!A and Templ!B, the opCast method can be written in such a way as to construct Templ!A from Templ!B in a consistent way, e.g., by treating B as a subclass of A and calling the ctor of Templ!A, or, in the case of my contrived examples, do something non-trivial with the .data member so that the returned Templ!A makes sense for whatever purpose it's designed for. It allows the implementor of the template to specify exactly how to convert between the two types when the compiler can't possibly divine this on its own. This is kind of bringing a nuclear warhead to an anthill, though. In my own code where I have a template wrapping around types that need to convert to a common base type, I find it more useful to use the following pattern instead: class A { ... } class B : A { ... } class WrapperBase { ... // common stuff for all instantiations of Wrapper!X } class Wrapper(C : class) : WrapperBase { ... // stuff specific to C } Wrapper!A a; Wrapper!B b; WrapperBase wb = a; // OK wb = b; // also OK This may or may not be what you're looking for, though. T -- It said to install Windows 2000 or better, so I installed Linux instead.
Jun 20 2016
parent Joerg Joergonson <JJoergonson gmail.com> writes:
On Tuesday, 21 June 2016 at 02:39:25 UTC, H. S. Teoh wrote:
 On Tue, Jun 21, 2016 at 02:09:50AM +0000, Joerg Joergonson via 
 Digitalmars-d-learn wrote: [...]
 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.
Correct.
 Template parameters also can have this property since they are 
 types.
[...] The template *parameters* can also have this property. But the *template* itself may not. Contrived example: class A { int x; } class B : A { int y; } struct Templ(C : class) { int[C.sizeof] data; } Templ!A a; Templ!B b; a = b; // should this be allowed? It should be clear that allowing this would cause problems, because in spite of the relationship between A and B, and hence the relationship between the template arguments of a and b, the same relationship does not hold between Templ!A and Templ!B (note that .data is an array of ints, not ubytes, and may not contain data in any layout that has any corresponds with the relationship between A and B). Another contrived example: class A { int x; } class B : A { int y; } struct Templ(C : class) { static if (C.sizeof > 4) { string x; } else { float y; } } Allowing implicit casting from Templ!B to Templ!A would not make sense, because even though the respective template arguments have an inheritance relationship, the Templ instantiation made from these classes has a completely unrelated and incompatible implementation.
Well, I never mentioned any implicit casting. Obviously explicit casting wouldn't make sense either. It is a good example as it can show that Templ!A is completely different than Templ!B and no conversion is every possible even if A and B are related. But I still think these are different examples. You are talking about A!a vs A!b while I am talking about A!a vs B!b. I believe, but could be mistaken, that there is a subtle difference. I know it seems like B!b can be reduced to A!b, and the type system allows this... but if it never happens then all these cases explaining the problem of going from A!b to A!a are moot.
 Now granted, these are contrived examples, and in real-life we 
 may not have any real application that requires such strange 
 code. However, the language itself allows such constructs, and 
 therefore the compiler cannot blindly assume any relationship 
 between Templ!A and Templ!B even though there is a clear 
 relationship between A and B themselves.
I agree. I am not asking for blind assumptions. When I inform the compiler I want to cast to an object that I know should succeed(it was designed to do so) I don't expect a null. (As has been mentioned, I can do this using the void* trick, so there is a way)
 What should be done if we wish to allow converting Templ!B to 
 Templ!A, though?  One way (though this still does not allow 
 implicit casting) is to use opCast:

 	struct Templ(C : class) {
 		... // implementation here

 		auto opCast(D : class)
 			if (is(C : D)) // C must be a base class of D
 		{
 			...
 			// do something here to make the conversion
 			// valid. Maybe something as simple as:
 			return cast(Templ!D) this;

 			// (provided that there are no memory layout
 			// problems in Templ's implementation, of
 			// course).
 		}
 	}

 Implementing this using opCast actually gives us an even more 
 powerful tool: provided it is actually possible to convert 
 between potentially incompatible binary layouts of Templ!A and 
 Templ!B, the opCast method can be written in such a way as to 
 construct Templ!A from Templ!B in a consistent way, e.g., by 
 treating B as a subclass of A and calling the ctor of Templ!A, 
 or, in the case of my contrived examples, do something 
 non-trivial with the .data member so that the returned Templ!A 
 makes sense for whatever purpose it's designed for.  It allows 
 the implementor of the template to specify exactly how to 
 convert between the two types when the compiler can't possibly 
 divine this on its own.
While this is nice, the problem was how to convert. Even in opCast I would get a null and I wouldn't want to reconstruct A!a from B!b because that would essentially entail duplication. Of course, now I know I can just cast to void* then back and essentially trick/bypass the compilers type system checking.
 This is kind of bringing a nuclear warhead to an anthill, 
 though.  In my own code where I have a template wrapping around 
 types that need to convert to a common base type, I find it 
 more useful to use the following pattern instead:

 	class A { ... }
 	class B : A { ... }

 	class WrapperBase {
 		... // common stuff for all instantiations of Wrapper!X
 	}

 	class Wrapper(C : class) : WrapperBase {
 		... // stuff specific to C
 	}

 	Wrapper!A a;
 	Wrapper!B b;
 	WrapperBase wb = a; // OK
 	wb = b; // also OK

 This may or may not be what you're looking for, though.
This is exactly what I have done! ;) Just a bit more. Essentially I have class DoubleWrapper(D : C) : Wrapper!C and the problem is that Wrapper contains overloads from WrapperBase that cast to Wrapper and when these overloads are called in DoubleWrapper(because I didn't overload anything in DoubleWrapper) the casts return null. (ecause DoubleWrapper!D cannot convert o Wrapper!C. Basically I have to down cast something in WrapperBase to use it in Wrapper, but I know it is of type Wrapper when I am "in" Wrapper. I also know it is of type DoubleWrapper when in DoubleWrapper. The cast sort of needs to be "polymorphic"... but, of course casts are static so this doesn't work. In all the counter examples give, it's as if I had class DoubleWrapper(D : C); instead of class DoubleWrapper(D : C) : Wrapper!C Or no DoubleWrapper at all. Again, I think there is some difference and some information provided by the inheritance that makes things different. It seems like a complete hack to have to cast to void* and back to make things work when there is a way. For example, Your Templ class is very different, but DoubleWrapper can't do this and be *completely* different because it inherits Wrapper; class DoubleWrapper(D : C) { ... does something strange depending on different D... } Different than class DoubleWrapper(D : C) : Wrapper!C { ... does something crazy depending on different D... ... But cannot change the fact that it has a compatible memory footprint of Wrapper!C(since it inherits from it) } To take your example:
 	class A { int x; }
 	class B : A { int y; }
class X(T : A);
 	struct Templ(T : (B : A)) : X!A {
 		static if (C.sizeof > 4) {
 			string x;
 		} else {
 			float y;
 		}
// + There's still a complete "copy" of X!C lurking in here
 	}
Different right? The X!C makes Templ!C different, regardless of the crazy stuff its does based on C, it still must be compatible with X!C because that's what inheritance tells us. We don't get that in your original example because there is no inheritance going on(well, excluding A and B) In your original, Templ!A and templ!B may not be relatable, but in mine X!A and Templ!B are. Now, I suppose you might be able to prove this wrong, say by constructing something funky in X: class X(T : A) { static if (T.sizeof > 4) int[] x; else string y; } But regardless, X!A is fixed w.r.t to Templ!B. So even though it is "crazy" depending on the template parameter, the craziness doesn't get though. If we had class Templ(T : B) : X!T then it can be crazy just as Templ can be, but this is different. This has very little to do with A. Basically there are constraints in the design that prevent craziness from taking place(I think). My Button!ButtonItem and Slider!SliderItem could have easily been coded as ButtonButtonItem and SliderSliderItem : ButtonButtonItem(although with increased obfuscation and reduced extensibility). My point is, unless someone can prove me wrong, is in the actual problem I am presenting, not in the over simplified cases that exclude my case(In which case one would have to prove why the simplification holds rather than assuming and proving why the simplification is false), resorting to a void* cast is a hack and not necessary. It should be quite logical for the compiler, possibly with a little help, to realize the cast works. e.g., Parallel_Cast(Button!ButtonItem)Slider!SliderItem. (remember, Slider!(X : SliderItem) inherits from Button!ButtonItem directly! In fact, I could simply alias Button!ButtonItem to Q and then it inherits from Q... and so must always be castable to Q, right?) e.g., class Button(T : ButtonItem); alias Q = Button!ButtonItem; class Slider(T : (SliderItem : ButtonItem)) : Q; cast(Q)Slider!X; // Should always work or no? This shows in a pretty obvious way that Button!ButtonItem is static and hence is just regular inheritance. Now, my case is slightly more complex: class Slider(T : (SliderItem : ButtonItem)) : (Z!T : Q); Z!T is a more general Button!ButtonItem, but it is still, at its base, a Q(hence the notation)... so always casting to Q should still work. Q is a sort of "fixed point" and no matter what we build on top of it, no matter how complex, we should be able to cast down to it. Just like I can cast to Widget or WrapperBase because they are "fixed points"(and the kinda the reason why they are useful) Again, it's possible I'm missing something or just being ignorant, but I don't think anyone has truly shown this(contrived examples are ok as long as they are analogous to the real problem and not oversimplified). It would be nice to know, though, if this is just a short coming of D or if there a flaw in my logic. At least I could live with D's flaws ;)
Jun 21 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/19/16 5:19 PM, Joerg Joergonson wrote:
 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!
Because: class A(T : a) { static if(is(T == a)) int oops; ... } Now A!b and A!a have different layouts. They cannot be related, even if the template arguments are related. I could introduce another virtual function inside the static if, same result -- vtable is messed up. In general, an instantiation of a template aggregate (class or struct) is not castable implicitly to another instantiation of the same aggregate unless explicitly declared. And note that D does not allow multiple inheritance. I don't think you can solve this problem in D. -Steve
Jun 20 2016
parent reply Joerg Joergonson <JJoergonson gmail.com> writes:
On Monday, 20 June 2016 at 23:35:28 UTC, Steven Schveighoffer 
wrote:
 On 6/19/16 5:19 PM, Joerg Joergonson wrote:
 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!
Because: class A(T : a) { static if(is(T == a)) int oops; ... } Now A!b and A!a have different layouts. They cannot be related, even if the template arguments are related. I could introduce another virtual function inside the static if, same result -- vtable is messed up. In general, an instantiation of a template aggregate (class or struct) is not castable implicitly to another instantiation of the same aggregate unless explicitly declared. And note that D does not allow multiple inheritance. I don't think you can solve this problem in D. -Steve
Yes, but all you guys are doing is leaving out what I'm actually doing and creating a different problem that may not have the same issues. I have this: (Button!ButtonItem) : (Slider!SliderItem) In my code/design that is different than Button!ButtonItem : Button!SliderItem : Slider!SliderItem The middle case, the case you guys are reducing do, never occurs. I never have that problem because I never *mix* a Button with a SliderItem. It makes no sense in my design. Hence I don't have to worry about that case, but that is the case you guys keep bringing up. A SliderItem adds info to a ButtonItem that makes it "slider like". In my case, A slide amount. This field is useless to use in a Button. It is only used by the Slider class(In fact, only by SliderItem). I realize that if one did a cast of a Button!SliderItem down to a Button!ButtonItem, things can become problematic. This doesn't occur in my design. I see it more like [Button!ButtonItem] | v [Slider!SliderItem] rather than Button!ButtonItem | v Button!SliderItem | v Slider!SliderItem or Button!ButtonItem | v Slider!ButtonItem | v Slider!SliderItem The first has the problem casting Button!SliderItem to Button!ButtonItem. (Same as List!mystring to List!string) The second has the problem Slider!SliderItem to Slider!ButtonItem (It's the same problem in both) There seems to be three relationships going on and D does one. Let != not related(not derived from) For a and b a != b. D's assumption, never safe a -> b works some of the time depending on usage a = b works all the time But it is more complex with A!a and B!b A != B and a != b. never safe to cast in any combination A != B and a = b. never safe to cast A != B and a -> b. never safe to cast A -> B and a != b. never safe to cast A = B and a != b. never safe to cast A -> B and a -> b. Sometimes safe to cast A -> B and a = b. Sometimes safe to cast A = B and a = b. Always safe to cast Things get "safer" to cast as the relationships between the types becomes more "exact". D always assumes worst case for the template parameters. Some designs, though, work in the A -> B and a -> b 'region' with the fact that A!b never occurs, which as been shown is problematic through out this thread(but it is really just an extension of the first case because both are derivation from A and it really adds nothing to the complexity).
Jun 20 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/20/16 10:45 PM, Joerg Joergonson wrote:
 On Monday, 20 June 2016 at 23:35:28 UTC, Steven Schveighoffer wrote:
 On 6/19/16 5:19 PM, Joerg Joergonson wrote:
 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!
Because: class A(T : a) { static if(is(T == a)) int oops; ... } Now A!b and A!a have different layouts. They cannot be related, even if the template arguments are related. I could introduce another virtual function inside the static if, same result -- vtable is messed up. In general, an instantiation of a template aggregate (class or struct) is not castable implicitly to another instantiation of the same aggregate unless explicitly declared. And note that D does not allow multiple inheritance. I don't think you can solve this problem in D.
Yes, but all you guys are doing is leaving out what I'm actually doing and creating a different problem that may not have the same issues.
We're not "creating" any different problems. The compiler has to assume the worst, especially when it must make assumptions at runtime. The same template instantiated with different parameters is different, not related. In order for it to be related, you have to declare that somehow. -Steve
Jun 21 2016