Thread overview | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
March 16, 2015 std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
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 } |
March 16, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lukasz Wrzosek | 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?
|
March 16, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to John Colvin | 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.
|
March 16, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lukasz Wrzosek | On 03/16/2015 08:28 AM, Lukasz Wrzosek wrote: > 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. 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, Ali |
March 16, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | Bug reported as https://issues.dlang.org/show_bug.cgi?id=14298 |
March 17, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Lukasz Wrzosek | On 03/16/2015 04:59 PM, Lukasz Wrzosek wrote: > Bug reported as > https://issues.dlang.org/show_bug.cgi?id=14298 Thanks... 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 |
March 17, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Tuesday, 17 March 2015 at 05:32:49 UTC, Ali Çehreli wrote:
> On 03/16/2015 04:59 PM, Lukasz Wrzosek wrote:
>> Bug reported as
>> https://issues.dlang.org/show_bug.cgi?id=14298
>
> Thanks...
>
> 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
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;
}
|
March 17, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | On 03/17/2015 12:14 AM, Meta wrote: > On Tuesday, 17 March 2015 at 05:32:49 UTC, Ali Çehreli wrote: >> On 03/16/2015 04:59 PM, Lukasz Wrzosek wrote: >>> Bug reported as >>> https://issues.dlang.org/show_bug.cgi?id=14298 >> >> Thanks... >> >> 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 > > That will throw an exception if the conversion can't be done instead of > just returning null, won't it? 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.) > 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 |
March 17, 2015 Re: std.typecons.Proxy + inheritance breaks cast'ing to inherited type | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Tuesday, 17 March 2015 at 07:56:19 UTC, Ali Çehreli wrote:
> > 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
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.
|
Copyright © 1999-2021 by the D Language Foundation