digitalmars.D.learn - std.typecons.Proxy + inheritance breaks cast'ing to inherited type
- Lukasz Wrzosek (25/25) Mar 16 2015 Hello
- John Colvin (3/29) Mar 16 2015 What behaviour would you expect if both IA and C inherited from
- Lukasz Wrzosek (4/6) Mar 16 2015 This case should assert at compile time.
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (8/14) Mar 16 2015 Yes, it's a bug because Proxy mixes in an opCast that always casts to T
- Lukasz Wrzosek (2/2) Mar 16 2015 Bug reported as
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (13/15) Mar 16 2015 Thanks...
- Meta (9/25) Mar 17 2015 That will throw an exception if the conversion can't be done
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (7/39) Mar 17 2015 You are right. I did not think it through but we can add template
- anonymous (60/70) Mar 17 2015 For classes, `T opCast(T)() {return cast(T) super;}` may be
Hello I was just exploring possibility to mimic multiple inheritance from C++ (do not ask why, just for fun). I've stumbled on below issue (let's say corner case) and most likely this is bug in implementation of template Proxy, isn't it ? import std.typecons; class IA {} class IB {} class C : IB { IA a; mixin Proxy!a; public this() { a = new IA; } } void main() { C c = new C; IA a = cast(IA)c; IB b_ok = c; IB b_not_ok = cast(IB)c; assert(c !is null); assert(a !is null); assert(b_ok !is null); assert(b_not_ok !is null); //fails }
Mar 16 2015
On Monday, 16 March 2015 at 09:03:18 UTC, Lukasz Wrzosek wrote:Hello I was just exploring possibility to mimic multiple inheritance from C++ (do not ask why, just for fun). I've stumbled on below issue (let's say corner case) and most likely this is bug in implementation of template Proxy, isn't it ? import std.typecons; class IA {} class IB {} class C : IB { IA a; mixin Proxy!a; public this() { a = new IA; } } void main() { C c = new C; IA a = cast(IA)c; IB b_ok = c; IB b_not_ok = cast(IB)c; assert(c !is null); assert(a !is null); assert(b_ok !is null); assert(b_not_ok !is null); //fails }What behaviour would you expect if both IA and C inherited from IB?
Mar 16 2015
On Monday, 16 March 2015 at 12:03:12 UTC, John Colvin wrote:What behaviour would you expect if both IA and C inherited from IB?This case should assert at compile time. But my example shows that case with implicit case is working, but the explicit cast is not. That seems to be incorrect IMO.
Mar 16 2015
On 03/16/2015 08:28 AM, Lukasz Wrzosek wrote:On Monday, 16 March 2015 at 12:03:12 UTC, John Colvin wrote:Yes, it's a bug because Proxy mixes in an opCast that always casts to T (IA in your case): auto ref opCast(T, this X)() { return cast(T)a; } Please file an issue for Phobos at https://issues.dlang.org/enter_bug.cgi Thank you, AliWhat behaviour would you expect if both IA and C inherited from IB?This case should assert at compile time. But my example shows that case with implicit case is working, but the explicit cast is not. That seems to be incorrect IMO.
Mar 16 2015
Bug reported as https://issues.dlang.org/show_bug.cgi?id=14298
Mar 16 2015
On 03/16/2015 04:59 PM, Lukasz Wrzosek wrote:Bug reported as https://issues.dlang.org/show_bug.cgi?id=14298Thanks... I have carried the discussion over to the main newsgroup: http://forum.dlang.org/thread/me8e0a$1kp6$1 digitalmars.com As I mention there, there is a workaround: Add a catch-all opCast to the class in question, which can simply forward to the all-powerful std.conv.to: T opCast(T)() { import std.conv; return this.to!T; } Now it compiles and works as expected. Ali
Mar 16 2015
On Tuesday, 17 March 2015 at 05:32:49 UTC, Ali Çehreli wrote:On 03/16/2015 04:59 PM, Lukasz Wrzosek wrote:That will throw an exception if the conversion can't be done instead of just returning null, won't it? Why can't you do this instead? t opCast(t)() if (is(typeof(cast(T)this))) { return cast(T)this; }Bug reported as https://issues.dlang.org/show_bug.cgi?id=14298Thanks... I have carried the discussion over to the main newsgroup: http://forum.dlang.org/thread/me8e0a$1kp6$1 digitalmars.com As I mention there, there is a workaround: Add a catch-all opCast to the class in question, which can simply forward to the all-powerful std.conv.to: T opCast(T)() { import std.conv; return this.to!T; } Now it compiles and works as expected. Ali
Mar 17 2015
On 03/17/2015 12:14 AM, Meta wrote:On Tuesday, 17 March 2015 at 05:32:49 UTC, Ali Çehreli wrote:You are right. I did not think it through but we can add template constraints to make it impossible to throw. (Still, we shouldn't need to do that for a cast to base types.)On 03/16/2015 04:59 PM, Lukasz Wrzosek wrote:That will throw an exception if the conversion can't be done instead of just returning null, won't it?Bug reported as https://issues.dlang.org/show_bug.cgi?id=14298Thanks... I have carried the discussion over to the main newsgroup: http://forum.dlang.org/thread/me8e0a$1kp6$1 digitalmars.com As I mention there, there is a workaround: Add a catch-all opCast to the class in question, which can simply forward to the all-powerful std.conv.to: T opCast(T)() { import std.conv; return this.to!T; } Now it compiles and works as expected. AliWhy can't you do this instead? t opCast(t)() if (is(typeof(cast(T)this))) { return cast(T)this; }That has the same problem: 'cast(T)this' happens to be an explicit cast, which is disabled by the opCast overload for int. Ali
Mar 17 2015
On Tuesday, 17 March 2015 at 07:56:19 UTC, Ali Çehreli wrote:For classes, `T opCast(T)() {return cast(T) super;}` may be viable. For structs, it's seems more difficult to me. `return this.to!T;` may try a cast(T), so it has the same problem as `return cast(T) this;`: infinite recursion. Example: ---- struct StdConvTo { int* p; int opCast(T : int)() {return 42;} T opCast(T)() {import std.conv; return this.to!T;} } unittest { assert((cast(int) StdConvTo()) == 42); /* ok */ auto i = cast(immutable StdConvTo) StdConvTo(); /* infinite recursion */ } ---- This applies to classes, too. A reinterpret cast would allow casts that should be rejected: ---- struct Reinterpet { int* p; int opCast(T : int)() {return 42;} T opCast(T)() {return * cast(T*) &this;} } unittest { assert((cast(int) Reinterpet()) == 42); /* ok */ auto s = cast(float) Reinterpet(); /* compiles, but shouldn't */ } ---- Here's something that might work. Construct a "tuple" of the struct's fields (i.e. same data but no opCast). Then cast that to T. ---- struct TupleOf { int* p; int opCast(T : int)() {return 42;} T opCast(T)() { static struct Tuple(S) {S stuff;} static Tuple!S tuple(S)(S stuff) {return Tuple!S(stuff);} return cast(T) tuple(this.tupleof); } } unittest { assert((cast(int) TupleOf()) == 42); /* ok */ auto i = cast(immutable TupleOf) TupleOf(); /* ok */ static assert(!__traits(compiles, cast(float) TupleOf())); /* ok */ } ---- This is probably not perfect either.Why can't you do this instead? t opCast(t)() if (is(typeof(cast(T)this))) { return cast(T)this; }That has the same problem: 'cast(T)this' happens to be an explicit cast, which is disabled by the opCast overload for int. Ali
Mar 17 2015