Thread overview
Problem with type-casting to interface arrays
Mar 19, 2007
Vladimir Panteleev
Mar 19, 2007
Derek Parnell
Mar 19, 2007
Johan Granberg
Mar 20, 2007
Lionello Lunesu
Mar 20, 2007
Vladimir Panteleev
Mar 21, 2007
Daniel Keep
Mar 26, 2007
Lionello Lunesu
May 11, 2007
Vladimir Panteleev
March 19, 2007
Hi,

With an interface I and an implementation C : I, is this code sane?

     C[] c = [new C()];
     I[] i = cast(I[]) c;

Currently, any attempts to do this typecast fail for me (the objects inside i are corrupted/unusable).
I don't see why it should fail (per the spec), so I'm inclined to believe it's a compiler bug.

I've attached three test cases (simple Phobos, Phobos, Tango).

EDIT: Someone has explained to me why does this happen, and that I simply cannot do this because that's how things work. Interface Vtable offsets and such - it would have to create a copy to do this (adjust pointers and such). Still, I think it's just too easy to fall for this - and it's hard to figure out a cause for such a bug. The compiler should at least return null for such a typecast - and, in the best case, display a warning or error.

-- 
Best regards,
  Vladimir                          mailto:thecybershadow@gmail.com

March 19, 2007
On Mon, 19 Mar 2007 07:52:15 +0200, Vladimir Panteleev wrote:

> Hi,
> 
> With an interface I and an implementation C : I, is this code sane?
> 
>      C[] c = [new C()];
>      I[] i = cast(I[]) c;
> 
> Currently, any attempts to do this typecast fail for me (the objects
> inside i are corrupted/unusable).
> I don't see why it should fail (per the spec), so I'm inclined to
> believe it's a compiler bug.

The thing is that "cast(I)" and "cast(I[])" do different things.

The code ...
   I[] i = cast(I[]) c;

literally makes i.ptr the same as c.ptr. And so it effectively means that 'i' is now an array of classes and not an array interfaces.

Consider this ...

   float[] f = [4.23, 1.2345];
   int[] i = cast(int[])f;

This does not cause each float in 'f' to be converted to 'int' but instead just makes 'i' point to the floats in 'f'. This is important in your case because the RAM structure of an interface is not the same as a class. So even though you code "writefln(i[0].classinfo.name);" in your example, the code is actually referencing a class layout and not an interface layout.

To do what you hoped, you'd have to this horrible kludge ...

  writefln((cast(I)(8+(cast(void*)i[0]))).classinfo.name);

where the literal '8' is used to find the offset of the real vtable of the interface.

You could of course build the interface array at run time ...

   C[] c = [new C()];
   I[] i;
   foreach(C k; c) i &= cast(I)k;

The "cast(I)" actually causes the compiler to convert the class to an
interface just like "cast(int)4.213" causes it to convert the float to an
int.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Justice for David Hicks!"
19/03/2007 5:30:44 PM
March 19, 2007
Derek Parnell wrote:
> where the literal '8' is used to find the offset of the real vtable of the interface.

Shouldn't that be sizeof(void*)*2 or something similar instead of 8 as pointers isn't the same size on all architectures?
March 20, 2007
Derek Parnell wrote:
> On Mon, 19 Mar 2007 07:52:15 +0200, Vladimir Panteleev wrote:
> 
>> Hi,
>>
>> With an interface I and an implementation C : I, is this code sane?
>>
>>      C[] c = [new C()];
>>      I[] i = cast(I[]) c;
>>
>> Currently, any attempts to do this typecast fail for me (the objects
>> inside i are corrupted/unusable).
>> I don't see why it should fail (per the spec), so I'm inclined to
>> believe it's a compiler bug.
> 
> The thing is that "cast(I)" and "cast(I[])" do different things.
> 
> The code ...
>    I[] i = cast(I[]) c;


Maybe it would be a good idea to add a separate cast expression for reinterpret_cast. The cast(..) can do anything from a C-cast to dynamic_cast to static_cast to reinterpret_cast. All the safe casts can use cast(..) but I think that for conversions without any link between the two types, it would be better to introduce some new cast syntax/keyword.. cast!(..)? :)

L.
March 20, 2007
On Tue, 20 Mar 2007 08:29:55 +0200, Lionello Lunesu <lio@lunesu.remove.com> wrote:

> Maybe it would be a good idea to add a separate cast expression for reinterpret_cast. The cast(..) can do anything from a C-cast to dynamic_cast to static_cast to reinterpret_cast. All the safe casts can use cast(..) but I think that for conversions without any link between the two types, it would be better to introduce some new cast syntax/keyword.. cast!(..)? :)

I agree. Imagine the mess this could accidentally cause in templates.

-- 
Best regards,
  Vladimir                          mailto:thecybershadow@gmail.com
March 21, 2007

Vladimir Panteleev wrote:
> On Tue, 20 Mar 2007 08:29:55 +0200, Lionello Lunesu <lio@lunesu.remove.com> wrote:
> 
>> Maybe it would be a good idea to add a separate cast expression for reinterpret_cast. The cast(..) can do anything from a C-cast to dynamic_cast to static_cast to reinterpret_cast. All the safe casts can use cast(..) but I think that for conversions without any link between the two types, it would be better to introduce some new cast syntax/keyword.. cast!(..)? :)
> 
> I agree. Imagine the mess this could accidentally cause in templates.

U[] arrcast(U, T)(T[] arr)
{
    U[] result;
    result.length = arr.length;
    foreach( i, a ; arr )
        result[i] = cast(U)a;
    return result;
}

...

I[] i = arrcast!(I)(c);

...

We don't need no op-er-a-tor!
Hey!  You guys!  Leave poor cast() alone!
All in all it's just a 'nother tem-plated call.

:3

	-- Daniel

-- 
int getRandomNumber()
{
    return 4; // chosen by fair dice roll.
              // guaranteed to be random.
}

http://xkcd.com/

v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP  http://hackerkey.com/
March 26, 2007
Daniel Keep wrote:
> 
> Vladimir Panteleev wrote:
>> On Tue, 20 Mar 2007 08:29:55 +0200, Lionello Lunesu <lio@lunesu.remove.com> wrote:
>>
>>> Maybe it would be a good idea to add a separate cast expression for
>>> reinterpret_cast. The cast(..) can do anything from a C-cast to
>>> dynamic_cast to static_cast to reinterpret_cast. All the safe casts can
>>> use cast(..) but I think that for conversions without any link between
>>> the two types, it would be better to introduce some new cast
>>> syntax/keyword.. cast!(..)? :)
>> I agree. Imagine the mess this could accidentally cause in templates.
> 
> U[] arrcast(U, T)(T[] arr)
> {
>     U[] result;
>     result.length = arr.length;
>     foreach( i, a ; arr )
>         result[i] = cast(U)a;
>     return result;
> }
> 
> ...
> 
> I[] i = arrcast!(I)(c);

Great example. Your arrcast can do anything, from class C:I to I and from class A to class B.

L.
May 11, 2007
On Wed, 21 Mar 2007 02:13:21 +0200, Daniel Keep <daniel.keep.lists@gmail.com> wrote:

> Vladimir Panteleev wrote:
>> Imagine the mess this could accidentally cause in templates.
>
> U[] arrcast(U, T)(T[] arr)
> {
>     U[] result;
>     result.length = arr.length;
>     foreach( i, a ; arr )
>         result[i] = cast(U)a;
>     return result;
> }

That's the solution I used in the end. What I meant, however, is:

void doTypeCast(T, U)(T from, inout U to)
{
     // perhaps other operations on `from` here
     to = cast(U)from;
     // perhaps other operations on `to` here
}

// with intf. I and class C:I :

C[] carr = ....;
I[] iarr;
// ...
doTypeCast(carr, iarr);  // this will cause a direct typecast from C[] to I[], which will just completely break things

My suggestion was to issue a warning when the user tries to do such broken array typecasts.

-- 
Best regards,
  Vladimir                          mailto:thecybershadow@gmail.com