From: gelists@googlemail.com   
      
   On 2013-01-15 14:04:43, in comp.lang.c++.moderated fmatthew5876 wrote:   
      
   >> I don't understand what would violate that fact. AIUI, a fact is   
   >> that the C++ standard doesn't guarantee any specific behavior for   
   >> reading a union member other than the one that was last written   
   >> to. And AIUI the   
   >   
   > It doesn't guarantee any behavior sure, but does it guarantee   
   > undefined behavior even if the addresses match?   
      
   No. The standard doesn't define what happens with such an access -- it   
   just doesn't require it to work. Any given compiler is free to allow   
   such a pattern.   
      
   >> compiler is free to perform optimizations that are based on the   
   >> assumption that you don't read a union member other than the last   
   >> written to. No?   
   >   
   > If reading and writing 2 union members invokes undefined behavior   
   > even if they actually alias the same memory of the same type, then   
   > you could just do this.   
   >   
   > vec2 v;   
   >   
   > //This is legal, take the address of 2 members of the union   
   > float* a = &v.m[1];   
   > float* b = &v.y;   
      
   Assigning the address of an int to a float*? What are you doing here?   
      
   > //a and b point to the same memory as argued above and have same type   
   > assert(a == b);   
      
   This statement compares uninitialized memory.   
      
   > //Therefore they alias the same object   
   > assert(*a == *b)   
   >   
   > Assuming my argument about the addresses of m[1] and y being the   
   > same is correct, this should work as intended. We now have 2   
   > pointers of type float which point to the same address. There would   
   > be something seriously deficient if a == b did not imply *a == *b   
   > where a and be are the same pointer type.   
      
   The problem here is that the standard says that only the member that   
   has last been written may be read. So first you need to write one,   
   then you can read it -- it, not the other(s).   
      
   Again, a compiler is free to allow this, but the standard doesn't seem   
   to require that it works.   
      
   Could you state your goal here? Precisely, unambiguously? Proof that   
   the standard requires that this works, even though it has some   
   seemingly clear language that it doesn't have to work?   
      
   > It looks like if you wanted to be really pedantic and avoid   
   > undefined behavior you could just do this every time you want to   
   > read and write a union member.   
   >   
   > *(&m[1]) = something;   
   > *(&y) = something;   
   >   
   > *(&y) == *(&m[1]);   
      
   The last line here seems to violate the standard. The last member to   
   be written to is the struct (for which you forgot to define a name),   
   but you're reading the array m (which specifically is not required to   
   work).   
      
   > Why does that work? Because as soon as you take the address of the   
   > member with & you are no longer working with a member of a union but   
   > instead a pointer to a T.   
      
   Which points to one or the other member of the union. AIUI, a pointer   
   to y accesses (reads or writes) y, and a pointer to m[1] accesses   
   m[1].   
      
   > I really like being able to alias all of the members of a vector   
   > like the vec4 in the original post and I see no reason why it should   
   > be a bad thing.   
      
   Who said it's a bad thing? I've done it before, and it seems that most   
   compilers/platforms tolerate such access patters. But it also seems   
   that the standard doesn't require that they do.   
      
   >> Because with arrays, the compiler doesn't really have a choice.   
   >> Assume T to be char[7]. It has a size of 7. An array of these   
   >> things will have them placed at intervals of 7. If best alignment   
   >> requires an alignment of, say, 8, this can be used with a struct,   
   >> but not with an array. Am I missing something?   
   >   
   > The alignment requirement of char is 1, so the alignment of char[7]   
   > will also be 1. Therefore the alignment of char[2][7] is also 1.   
   > There is no reason the compiler would need to align this object on   
   > an 8 byte boundary because its nothing more than an array of bytes.   
   > char[2][7] is really just a char[14] with build in pointer math to   
   > compute the indexing. The fact that it is contained within a struct   
   > doesn't make any difference.   
      
   Are we talking about the same thing here? Different platforms and   
   hardware designs have different access patters, and there are designs   
   where alignment on 8-byte boundaries results in better alignment than   
   on 7-byte boundaries.   
      
   As a simplified example, think of it like this... Imagine 8-byte wide   
   registers and memory data busses. Data aligned on 8-byte boundaries   
   can be moved (all imaginary and simplified) in a single operation from   
   memory into a register (where it can efficiently be processed). Data   
   aligned on 7-byte boundaries needs generally two memory accesses and a   
   shift to get it into a register.   
      
   So a compiler for this (imaginary) platform could very well decide to   
   pad a char[7] with 1 byte so that stuff is aligned on 8-byte   
   boundaries.   
      
   > The following code outputs "align 1 size 14" on gcc 4.7, linux,   
   > x86_64. g++ -std=c++11 test.cpp   
      
   Yup, no surprise here.   
      
   >> (FWIW, it seems that if you are writing high performance code on   
   >> such a platform, you better avoid arrays of such types. Even if you   
   >> only need 7 bytes in your T, it may be advantageous to make it 8   
   >> bytes wide.)   
   >   
   > If you are writing high performance code on any platform you should   
   > be using arrays (static or dynamic (aka std::vector)). Anything else   
   > will be destroying your cache. Why on earth would structs be laid   
   > out more optimally? Are we expecting people to do this if they want   
   > to process 10000 T's?   
   >   
   > template    
   > stuct {   
   > T t0;   
   > T t1;   
   > ...   
   > T t9999;   
   > };   
      
   Maybe re-read what I wrote. I never said not to use arrays or to   
   create such abominal structs (which couldn't replace the arrays anyway   
   -- no indexing possible). I just said that for a platform where   
   alignment on 8-byte boundaries brings performance advantages, you may   
   want to avoid using arrays with types T where sizeof(T)==7 (assuming a   
   char is a byte) and think of increasing the size of the type so that   
   sizeof(T)==8.   
      
   Gerhard   
      
      
   --   
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
    [ comp.lang.c++.moderated. First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|