www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - When is array-to-array cast legal, and what does actually happen?

reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Is the following cast legal with dmd 2.040?

struct MyChar
{
     dchar d;

     this(dchar param)
     in {
         assert(false);       // not called
     }
     body
     {
         d = param;
         assert(d != param);  // not called
     }

     this(this)
     in {
         assert(false);       // not called
     }
     body
     {
         assert(d != d);      // not called
     }
}

void main()
{
     MyChar[] mine = cast(MyChar[])("hello"d);   // legal?
     assert(mine[0].d == 'h');
}

I see that neither the constructor nor the postblit is called. 
Apparently the bit representation is used. This has the risk of 
violating struct invariants.

Is it legal?

Thank you,
Ali
Feb 22 2010
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
 ...

 I see that neither the constructor nor the postblit is called.
 Apparently the bit representation is used. This has the risk of
 violating struct invariants.
 
 Is it legal?
 
 Thank you,
 Ali

cast is to value conversions what a tactical nuclear strike is to peaceful negotiations. cast is specifically *designed* to circumvent the type system's protections [1]. If you want to do a value conversion, *do a value conversion*. Allocate a new array and convert each member. cast doesn't call the constructor or the postblit because it's doing a pointer conversion. Your code is basically equivalent to this: void main() { auto tmp = "hello"d; auto mine = cast(MyChar*)(tmp.ptr) [0..(tmp.length*typeof(tmp[0]).sizeof)/MyChar.sizeof)]; } That is, it's doing an unsafe, unchecked pointer cast, then re-slicing the array. To answer your question: yes, it's legal. Not what you wanted, but legal. [1] Except for int<->float. Oh, and objects. Really, this is one thing I could just about strangle K&R for: conflating value-preserving, non-value-preserving *AND* unsafe conversions all into a single construct. Walter, gets slapped with a fish for not putting a bullet in cast's head when he had the chance. Argh!
Feb 22 2010
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Daniel Keep wrote:
 ...

 I see that neither the constructor nor the postblit is called.
 Apparently the bit representation is used. This has the risk of
 violating struct invariants.

 Is it legal?

 Thank you,
 Ali

cast is to value conversions what a tactical nuclear strike is to peaceful negotiations.

Yes, that's the C-style casts...
  cast is specifically *designed* to circumvent
 the type system's protections [1].

 If you want to do a value conversion, *do a value conversion*.  Allocate
 a new array and convert each member.  cast doesn't call the constructor
 or the postblit because it's doing a pointer conversion.

As you point out in the footnote, cast are not always pointer conversions. :) I am wondering whether the compiler could call the constructors for each array element in array-to-array conversion.
 Your code is basically equivalent to this:

 void main()
 {
     auto tmp = "hello"d;
     auto mine = cast(MyChar*)(tmp.ptr)
         [0..(tmp.length*typeof(tmp[0]).sizeof)/MyChar.sizeof)];
 }

 That is, it's doing an unsafe, unchecked pointer cast, then re-slicing
 the array.

Especially when there is the method above, it feels more like array casting could call the constructors.
 To answer your question: yes, it's legal.  Not what you wanted, but 

Actually it will work for me, but I will leave it for later when optimizing the code. And there are no invariants in my case; any dchar will do.
 [1] Except for int<->float.  Oh, and objects.  Really, this is one thing
 I could just about strangle K&R for: conflating value-preserving,
 non-value-preserving *AND* unsafe conversions all into a single
 construct.  Walter, gets slapped with a fish for not putting a bullet in
 cast's head when he had the chance.  Argh!

Ali
Feb 22 2010
parent div0 <div0 users.sourceforge.net> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ali Çehreli wrote:
 Daniel Keep wrote:
 ...



Well I forget the details, but it's been pointed out before that D's cast is fundamentally broken. You get one cast operator that hides the full set of c++ static, dynamic, const & reinterpret casts. Which one actually happens is a mystery. Not ideal by any means and really should be addressed before D2 is called final. Not going to happen though. - -- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iD8DBQFLhHH6T9LetA9XoXwRAjavAJ9LV2XmRQ67GdC7qv03E9DMyaFPVACgp+JZ w4qYBuMc++WUWJPkc1ZR93c= =F1Qb -----END PGP SIGNATURE-----
Feb 23 2010
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Daniel Keep wrote:
 ...

 I see that neither the constructor nor the postblit is called.
 Apparently the bit representation is used. This has the risk of
 violating struct invariants.

 Is it legal?

 Thank you,
 Ali

cast is to value conversions what a tactical nuclear strike is to peaceful negotiations. cast is specifically *designed* to circumvent the type system's protections [1]. If you want to do a value conversion, *do a value conversion*. Allocate a new array and convert each member. cast doesn't call the constructor or the postblit because it's doing a pointer conversion. Your code is basically equivalent to this: void main() { auto tmp = "hello"d; auto mine = cast(MyChar*)(tmp.ptr) [0..(tmp.length*typeof(tmp[0]).sizeof)/MyChar.sizeof)]; } That is, it's doing an unsafe, unchecked pointer cast, then re-slicing the array.

There is another gotcha to watch out for: Array literals are treated differently from other arrays. That is, the compiler does a value conversion for literals. import std.stdio; void main() { auto a = [1, 2, 3]; auto b = cast(float[]) a; auto c = cast(float[]) [1, 2, 3]; writeln(a); // Prints "1 2 3" writeln(b); // Prints "1.4013e-45 2.8026e-45 4.2039e-45" writeln(c); // Prints "1 2 3" } -Lars
Feb 23 2010
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Daniel Keep:
 [1] Except for int<->float.  Oh, and objects.  Really, this is one thing
 I could just about strangle K&R for: conflating value-preserving,
 non-value-preserving *AND* unsafe conversions all into a single
 construct.  Walter, gets slapped with a fish for not putting a bullet in
 cast's head when he had the chance.  Argh!

From your idea I have written some comments here: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=106750 Bye, bearophile
Feb 24 2010