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

It's worth noting that C++, C#, and Java don't work that way either.

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.
June 20, 2016
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
June 21, 2016
On Monday, 20 June 2016 at 23:10:14 UTC, ag0aep6g wrote:
> On 06/20/2016 11:33 PM, Joerg Joergonson wrote:
>> On Monday, 20 June 2016 at 10:38:12 UTC, ag0aep6g wrote:
> [...]
>>> Is your position that Button!SliderItem should derive/inherit from
>>> Button!ButtonItem, enabling the cast, or do you suppose the cast
>>> should succeed because the fields are compatible?
>>>
>>> I.e., should this work?
>>>
>>>     class A {int x;}
>>>     class B {int x;}
>>>     A a;
>>>     B b = cast(B) a;
>>>
>>
>> No, not at all. first, A and B are not related, so casting makes no
>> sense unless there is a conversion(opCast) or whatever, but that is done
>> by the user.
>>
>> This is exactly opposite of what I am talking about.
>
> Ok, so you suppose there should be a inheritance implied when there's an inheritance relation between template arguments. I.e., A!a should be a superclass of A!b when a is a superclass of b.


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

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

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

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

Hence

We have two scenarios:

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


AND

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

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

This is the way I am thinking about it.

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

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

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

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

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

A simple example is basically my problem:

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

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

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

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

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

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

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

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

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

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




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

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

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

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

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

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

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

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

Diagrammatically:

Give,
   Button -> Slider
   ButtonItem -> SliderItem

Then

   Button!ButtonItem -> Slider!SliderItem

Not

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

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

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


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





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






























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



1 2
Next ›   Last »