www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Problem with type-casting to interface arrays

reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
------------iPLv9FTsz8I8Lu0r2g8y34
Content-Type: text/plain; charset=koi8-r
Content-Transfer-Encoding: Quoted-Printable

Hi,

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

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

Currently, any attempts to do this typecast fail for me (the objects ins=
ide i are corrupted/unusable).
I don't see why it should fail (per the spec), so I'm inclined to believ=
e 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 simpl=
y cannot do this because that's how things work. Interface Vtable offset=
s 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 warn=
ing or error.

-- =

Best regards,
  Vladimir                          mailto:thecybershadow gmail.com
------------iPLv9FTsz8I8Lu0r2g8y34
Content-Disposition: attachment; filename=arraytest_basic.d
Content-Type: text/x-dsrc; name=arraytest_basic.d
Content-Transfer-Encoding: Quoted-Printable

module arraytest_basic;

import std.stdio;

interface I
{
}

class C : I
{
}

void main()
{
    C[] c =3D [new C()];
    I[] i =3D cast(I[]) c;
    writefln(i[0].classinfo.name);   // prints out garbage and crashes w=
ith "Error: 4invalid UTF-8 sequence"

	writefln("Done!");
}
------------iPLv9FTsz8I8Lu0r2g8y34
Content-Disposition: attachment; filename=arraytest_tango.d
Content-Type: text/x-dsrc; name=arraytest_tango.d
Content-Transfer-Encoding: Quoted-Printable

import tango.io.Console;
import tango.text.convert.Integer;

interface ITest
{
	void doSomething();
}

class Test : ITest
{
	void doSomething()
	{
		Cout("Hi there!\n");
		//Cout("I am " ~ this.classinfo.name).newline;
		//Cout("I am " ~ this.classinfo.name ~ " (" ~ (cast(Object)this).class=
info.name ~ ")").newline;
	}
}

void main()
{
	Test obj =3D new Test();
	obj.doSomething();                         // works
	=

	Test[] objarr =3D [obj];
	objarr[0].doSomething();                   // works
	=

	ITest intf =3D obj;
	intf.doSomething();                        // works

	ITest[] intfarr =3D [intf];
	intfarr[0].doSomething();                  // works
	Cout(intfarr[0].classinfo.name).newline;   // works

	Cout("---").newline;
	ITest[] intfarr2 =3D cast(ITest[])objarr;
	intfarr2[0].doSomething();                 // does nothing?
	Cout(intfarr2[0].classinfo.name).newline;  // access violation

	Cout("Done!").newline;
}
------------iPLv9FTsz8I8Lu0r2g8y34
Content-Disposition: attachment; filename=arraytest_phobos.d
Content-Type: text/x-dsrc; name=arraytest_phobos.d
Content-Transfer-Encoding: Quoted-Printable

module arraytest_phobos;

import std.stdio;

interface ITest
{
	void doSomething();
}

class Test : ITest
{
	void doSomething()
	{
		writefln("Hi there!");
	}
}

void main()
{
	Test obj =3D new Test();
	obj.doSomething();                         // works
	=

	Test[] objarr =3D [obj];
	objarr[0].doSomething();                   // works
	=

	ITest intf =3D obj;
	intf.doSomething();                        // works

	ITest[] intfarr =3D [intf];
	intfarr[0].doSomething();                  // works
	writefln(intfarr[0].classinfo.name);       // works

	writefln("---");
	ITest[] intfarr2 =3D cast(ITest[])objarr;
	intfarr2[0].doSomething();                 // prints out "arraytest_pho=
bos.Test" - WTF
	//writefln(intfarr2[0].classinfo.name);      // when uncommented, print=
s out garbage and crashes with "Error: 4invalid UTF-8 sequence"

	writefln("Done!");
}
------------iPLv9FTsz8I8Lu0r2g8y34--
Mar 18 2007
parent reply Derek Parnell <derek nomail.afraid.org> writes:
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
Mar 18 2007
next sibling parent Johan Granberg <lijat.meREM OVE.gmail.com> writes:
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?
Mar 19 2007
prev sibling parent reply Lionello Lunesu <lio lunesu.remove.com> writes:
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.
Mar 19 2007
parent reply "Vladimir Panteleev" <thecybershadow gmail.com> writes:
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
Mar 20 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
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/
Mar 20 2007
next sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
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!(..)? :)


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.
Mar 26 2007
prev sibling parent "Vladimir Panteleev" <thecybershadow gmail.com> writes:
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 =3D arr.length; foreach( i, a ; arr ) result[i] =3D 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 =3D cast(U)from; // perhaps other operations on `to` here } // with intf. I and class C:I : C[] carr =3D ....; I[] iarr; // ... doTypeCast(carr, iarr); // this will cause a direct typecast from C[] t= o I[], which will just completely break things My suggestion was to issue a warning when the user tries to do such brok= en array typecasts. -- = Best regards, Vladimir mailto:thecybershadow gmail.com
May 11 2007