www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Segfault when casting array of Interface types

reply "rcor" <murphyslaw480 gmail.com> writes:
I'm back for another round of "is this a bug, or am I doing 
something stupid?".

C and D implement interface I, and I have an array of each. I'd 
like to combine these into one I[], but eventually I'd like to 
cast an element back to its original type.

interface I {}
class C : I {}
class D : I {}

void main() {
   C[] c = [new C, new C];
   D[] d = [new D, new D];
   auto i = cast(I[]) c ~ cast(I[]) d;
   assert(cast(C) i[0]); // segfault
}

casting each array to I[] is fine, but casting an element back to 
C segfaults (even if it weren't a C, it should just return null, 
right?)
Sep 15 2014
next sibling parent reply "Franz" <Franz yhsdf.ch> writes:
On Tuesday, 16 September 2014 at 02:21:42 UTC, rcor wrote:
 I'm back for another round of "is this a bug, or am I doing 
 something stupid?".

 C and D implement interface I, and I have an array of each. I'd 
 like to combine these into one I[], but eventually I'd like to 
 cast an element back to its original type.

 interface I {}
 class C : I {}
 class D : I {}

 void main() {
   C[] c = [new C, new C];
   D[] d = [new D, new D];
   auto i = cast(I[]) c ~ cast(I[]) d;
   assert(cast(C) i[0]); // segfault
 }

 casting each array to I[] is fine, but casting an element back 
 to C segfaults (even if it weren't a C, it should just return 
 null, right?)
look at what 's auto has created: import std.stdio; interface I {} class C : I {} class D : I {} void main() { C[] c = [new C, new C]; D[] d = [new D, new D]; auto i = cast(I[]) c ~ cast(I[]) d; writeln(typeof(i).stringof); } Your issue comme from auto.
Sep 15 2014
parent reply "Franz" <Franz yhsdf.ch> writes:
On Tuesday, 16 September 2014 at 03:03:16 UTC, Franz wrote:
 On Tuesday, 16 September 2014 at 02:21:42 UTC, rcor wrote:
 I'm back for another round of "is this a bug, or am I doing 
 something stupid?".

 C and D implement interface I, and I have an array of each. 
 I'd like to combine these into one I[], but eventually I'd 
 like to cast an element back to its original type.

 interface I {}
 class C : I {}
 class D : I {}

 void main() {
  C[] c = [new C, new C];
  D[] d = [new D, new D];
  auto i = cast(I[]) c ~ cast(I[]) d;
  assert(cast(C) i[0]); // segfault
 }

 casting each array to I[] is fine, but casting an element back 
 to C segfaults (even if it weren't a C, it should just return 
 null, right?)
look at what 's auto has created: import std.stdio; interface I {} class C : I {} class D : I {} void main() { C[] c = [new C, new C]; D[] d = [new D, new D]; auto i = cast(I[]) c ~ cast(I[]) d; writeln(typeof(i).stringof); } Your issue comme from auto.
i is a I[]
Sep 15 2014
next sibling parent reply "rcor" <murphyslaw480 gmail.com> writes:
On Tuesday, 16 September 2014 at 03:05:57 UTC, Franz wrote:
 Your issue comme from auto.
i is a I[]
I expected i to be an I[], but shouldn't a casting an element of an I[] to a C return either a C or null? It is if I do this: I[] i = [cast(I) new C, new D]; assert(cast(C) i[0]); // fine Come to think of it, the above will probably work in my situation. But I still wonder why the first example doesn't work.
Sep 15 2014
parent "rcor" <murphyslaw480 gmail.com> writes:
seemingly even weirder:

   I[] i0 = [new C, new C];
   assert(cast(C) i0[0]);  // fine

   C[] c = [new C, new C];
   I[] i1 = cast(I[]) c;
   assert(cast(C) i1[0]);  // fails

It works when I create an I[] from a C[] literal, but not when I 
cast a previously declared C[] to an I[].
Sep 15 2014
prev sibling parent reply "Klaus" <Klaus homework.de> writes:
On Tuesday, 16 September 2014 at 03:05:57 UTC, Franz wrote:
 On Tuesday, 16 September 2014 at 03:03:16 UTC, Franz wrote:
 On Tuesday, 16 September 2014 at 02:21:42 UTC, rcor wrote:
 I'm back for another round of "is this a bug, or am I doing 
 something stupid?".

 C and D implement interface I, and I have an array of each. 
 I'd like to combine these into one I[], but eventually I'd 
 like to cast an element back to its original type.

 interface I {}
 class C : I {}
 class D : I {}

 void main() {
 C[] c = [new C, new C];
 D[] d = [new D, new D];
 auto i = cast(I[]) c ~ cast(I[]) d;
 assert(cast(C) i[0]); // segfault
 }

 casting each array to I[] is fine, but casting an element 
 back to C segfaults (even if it weren't a C, it should just 
 return null, right?)
look at what 's auto has created: import std.stdio; interface I {} class C : I {} class D : I {} void main() { C[] c = [new C, new C]; D[] d = [new D, new D]; auto i = cast(I[]) c ~ cast(I[]) d; writeln(typeof(i).stringof); } Your issue comme from auto.
i is a I[]
writing auto i = cast(I[]) c ~ cast(I[]) d; is just a horrible way of shortcuting the static typing. You write this thinking that i "has to be..." and then you complain latter because the cast does not work. D is a strongly typed lang. in your example you use "auto" because your brain doesnt give you what the type of i has to be, which is an error. D is not a scripting lang. You made a wrong usage of "auto".
Sep 15 2014
next sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 16 September 2014 at 06:27:59 UTC, Klaus wrote:
 On Tuesday, 16 September 2014 at 03:05:57 UTC, Franz wrote:
 On Tuesday, 16 September 2014 at 03:03:16 UTC, Franz wrote:
 On Tuesday, 16 September 2014 at 02:21:42 UTC, rcor wrote:
 I'm back for another round of "is this a bug, or am I doing 
 something stupid?".

 C and D implement interface I, and I have an array of each. 
 I'd like to combine these into one I[], but eventually I'd 
 like to cast an element back to its original type.

 interface I {}
 class C : I {}
 class D : I {}

 void main() {
 C[] c = [new C, new C];
 D[] d = [new D, new D];
 auto i = cast(I[]) c ~ cast(I[]) d;
 assert(cast(C) i[0]); // segfault
 }

 casting each array to I[] is fine, but casting an element 
 back to C segfaults (even if it weren't a C, it should just 
 return null, right?)
look at what 's auto has created: import std.stdio; interface I {} class C : I {} class D : I {} void main() { C[] c = [new C, new C]; D[] d = [new D, new D]; auto i = cast(I[]) c ~ cast(I[]) d; writeln(typeof(i).stringof); } Your issue comme from auto.
i is a I[]
writing auto i = cast(I[]) c ~ cast(I[]) d; is just a horrible way of shortcuting the static typing. You write this thinking that i "has to be..." and then you complain latter because the cast does not work. D is a strongly typed lang. in your example you use "auto" because your brain doesnt give you what the type of i has to be, which is an error. D is not a scripting lang. You made a wrong usage of "auto".
AFACIS there's nothing wrong with his use of casting. It's fine here, because `I` is a base type of `C` and `D`. If it weren't for the arrays, the cast wouldn't even be necessary. I think it's a bug.
Sep 16 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 16 September 2014 at 08:39:43 UTC, Marc Schütz wrote:
 AFACIS there's nothing wrong with his use of casting. It's fine 
 here, because `I` is a base type of `C` and `D`. If it weren't 
 for the arrays, the cast wouldn't even be necessary. I think 
 it's a bug.
Correction: AFAIK casting between interfaces and classes needs to adjust the underlying pointer. Therefore, when casting an array, the compiler would have to do that with the entire array, which cannot be copied without allocating memory (and mustn't be modified in-place for consistency reasons). This means that the cast is instead a pure reinterpret cast (repainting). Whether the compiler should accept that or not is a different question. I guess it should, because if it doesn't, there wouldn't be an easy way to achieve a reinterpret cast (only via an intermediate cast to `void*`, which is clumsy). Anyway, using `std.conv.to` is the way to go here (if you don't require that last bit of performance), because it is safer in general (also checks for overflows and the like, for example).
Sep 16 2014
parent reply "rcor" <murphyslaw480 gmail.com> writes:
On Tuesday, 16 September 2014 at 08:49:04 UTC, Marc Schütz wrote:
 On Tuesday, 16 September 2014 at 08:39:43 UTC, Marc Schütz 
 wrote:
 Whether the compiler should accept that or not is a different 
 question. I guess it should, because if it doesn't, there 
 wouldn't be an easy way to achieve a reinterpret cast (only via 
 an intermediate cast to `void*`, which is clumsy).

 Anyway, using `std.conv.to` is the way to go here (if you don't 
 require that last bit of performance), because it is safer in 
 general (also checks for overflows and the like, for example).
Thanks, didn't think of trying std.conv.to. Can someone expand a bit on what to! is doing in this situation that cast isn't? I looked up 'reinterpret cast' but didn't see the connection to this.
 AFAIK casting between interfaces and classes needs to adjust 
 the underlying pointer. Therefore, when casting an array, the 
 compiler would have to do that with the entire array, which 
 cannot be copied without allocating memory (and mustn't be 
 modified in-place for consistency reasons). This means that the 
 cast is instead a pure reinterpret cast (repainting).
Is to! creating a new array of pointers while cast isn't? This isn't a performance critical section and it's not a huge array, so I ask mostly out of curiosity.
Sep 16 2014
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Tuesday, 16 September 2014 at 11:26:05 UTC, rcor wrote:
 On Tuesday, 16 September 2014 at 08:49:04 UTC, Marc Schütz 
 wrote:
 On Tuesday, 16 September 2014 at 08:39:43 UTC, Marc Schütz 
 wrote:
 Whether the compiler should accept that or not is a different 
 question. I guess it should, because if it doesn't, there 
 wouldn't be an easy way to achieve a reinterpret cast (only 
 via an intermediate cast to `void*`, which is clumsy).

 Anyway, using `std.conv.to` is the way to go here (if you 
 don't require that last bit of performance), because it is 
 safer in general (also checks for overflows and the like, for 
 example).
Thanks, didn't think of trying std.conv.to. Can someone expand a bit on what to! is doing in this situation that cast isn't? I looked up 'reinterpret cast' but didn't see the connection to this.
Reinterpret cast means that the compiler should treat whatever is at the memory location as the given type, and not modify it in any way. `std.conv.to` can do more work, for example it can also parse strings into integers: assert("42".to!int == 42);
 AFAIK casting between interfaces and classes needs to adjust 
 the underlying pointer. Therefore, when casting an array, the 
 compiler would have to do that with the entire array, which 
 cannot be copied without allocating memory (and mustn't be 
 modified in-place for consistency reasons). This means that 
 the cast is instead a pure reinterpret cast (repainting).
Is to! creating a new array of pointers while cast isn't? This isn't a performance critical section and it's not a huge array, so I ask mostly out of curiosity.
Yes, it is. (Probably. I don't have the time to test it now, but it's likely if my theory about the pointer adjustment is correct.)
Sep 16 2014
parent "rcor" <murphyslaw480 gmail.com> writes:
On Tuesday, 16 September 2014 at 14:13:48 UTC, Marc Schütz wrote:
 On Tuesday, 16 September 2014 at 11:26:05 UTC, rcor wrote:
 Is to! creating a new array of pointers while cast isn't? This 
 isn't a performance critical section and it's not a huge 
 array, so I ask mostly out of curiosity.
Yes, it is. (Probably. I don't have the time to test it now, but it's likely if my theory about the pointer adjustment is correct.)
I guess I could have checked that out myself: import std.stdio; import std.conv; interface I {} class C : I {} class D : I {} void main() { C[] c = [new C, new C]; I[] i = cast(I[]) c; I[] i2 = c.to!(I[]); assert(c is cast(C[]) i); // i and c point to same address assert(i !is i2); // to! appears to create a new array }
Sep 16 2014
prev sibling parent "rcor" <murphyslaw480 gmail.com> writes:
On Tuesday, 16 September 2014 at 06:27:59 UTC, Klaus wrote:
 is just a horrible way of shortcuting the static typing. You 
 write this thinking that i "has to be..." and then you complain 
 latter because the cast does not work.
 D is a strongly typed lang. in your example you use "auto" 
 because your brain doesnt give you what the type of i has to 
 be, which is an error. D is not a scripting lang. You made a 
 wrong usage of "auto".
Admittedly this came about as a result of some poor design on my part, but I don't get what you're saying about auto. I thought auto was supposed to relieve the cognitive load of manual type identification. Without it, std.algorithm would be a pain to use. My brain doesn't intuitively tell me that std.algorithm.filter returns a FilterResult, but I can use it effectively with auto.
Sep 16 2014
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/15/2014 07:21 PM, rcor wrote:
 I'm back for another round of "is this a bug, or am I doing something
 stupid?".

 C and D implement interface I, and I have an array of each. I'd like to
 combine these into one I[], but eventually I'd like to cast an element
 back to its original type.

 interface I {}
 class C : I {}
 class D : I {}

 void main() {
    C[] c = [new C, new C];
    D[] d = [new D, new D];
    auto i = cast(I[]) c ~ cast(I[]) d;
    assert(cast(C) i[0]); // segfault
 }

 casting each array to I[] is fine, but casting an element back to C
 segfaults (even if it weren't a C, it should just return null, right?)
I don't have an answer and I am not even sure whether this is a bug. However, std.conv.to works with the example: import std.conv; interface I {} class C : I {} class D : I {} void main() { C[] c = [new C, new C]; D[] d = [new D, new D]; auto i = c.to!(I[]) ~ d.to!(I[]); // <-- here assert(cast(C)(i[0])); } Ali
Sep 15 2014