Prev: draft standard n3090.pdf, std::unique() algorithm
Next: Errors using shared_ptr<> and get_deleter in g++-4.1/4.3/4.4
From: andhow on 11 Apr 2010 14:22 The following piece of code, which I believe is correct w.r.t strict- aliasing restrictions, generates a warning in gcc -O3 -Wall (tested 4.4.1 and trunk). This pattern is common, so it has to have come up for other people, so my question is: are there any good ways to achieve the same effect without getting the gcc warning? The problematic pattern occurs in classes that want to do conditional in-place construction, like Conditionally below: void *operator new (unsigned, void *p) { return p; } template <class T> struct Conditionally { union { char bytes[sizeof(T)]; int align; }; bool b; public: Conditionally(bool b) : b(b) { if (b) new(bytes) T(); } T &asT() { return *reinterpret_cast<T *>(bytes); } ~Conditionally() { if (b) asT().~T(); } }; struct A { int x; A() {} ~A() {} }; int main() { Conditionally<A> c(true); } > g++ -O3 -Wall test.cpp test.cpp: In member function �T& Conditionally<T>::asT() [with T = A]�: test.cpp:13:31: instantiated from �Conditionally<T>::~Conditionally() [with T = A]� test.cpp:19:28: instantiated from here test.cpp:12:51: warning: dereferencing type-punned pointer will break strict-aliasing rules The analysis seems to miss the fact that the memory represented by bytes is only ever being accessed as its effective type: T. Although I'd prefer not to use a #pragma, I have had problems even using #pragma: I've found that "#pragma GCC diagnostic ignored "- Wstrict-aliasing" only works if set for the entire translation unit. Specifically, to ignore the warning, the -Wstrict-aliasing has to be set to 'ignored' by the last line of the translation unit, at which point it applies globally to the entire tu. Or, that is what I have been able to determine through experiment; perhaps someone understands this better? In general, I like the warning; it finds real bugs, so I'd like to keep it active and watching as much of the codebase as possible. Thanks for any help! -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Mathias Gaunard on 12 Apr 2010 06:40 On 12 avr, 06:22, andhow <and...(a)gmail.com> wrote: > template <class T> > struct Conditionally { > union { > char bytes[sizeof(T)]; > int align; > }; > bool b; > public: > Conditionally(bool b) : b(b) { if (b) new(bytes) T(); } > T &asT() { return *reinterpret_cast<T *>(bytes); } > ~Conditionally() { if (b) asT().~T(); } > > }; Independently of whether this breaks strict aliasing rules or not, this is undefined behaviour. Adding an int to your union does not guarantee your bytes array will be properly aligned for values of type T, and if T happens to be char this is potentially suboptimal. Just so you know, that code already exists in boost, it's boost::optional. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: andhow on 12 Apr 2010 13:48 > Independently of whether this breaks strict aliasing rules or not, > this is undefined behaviour. Adding an int to your union does not > guarantee your bytes array will be properly aligned for values of type > T, and if T happens to be char this is potentially suboptimal. In this example, A is a POD containing an int. So, 'bytes' is aligned with 'align', x starts at the beginning of A, thus x is aligned. > Just so you know, that code already exists in boost, it's > boost::optional. Cool. Can't use it, but at least I can see how its implemented. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: andhow on 12 Apr 2010 18:51
For posterity: I was able to arrive at a solution: confuse the analysis by separating the char buffer into a separate templated class. That is, rewriting the above code as follows silences the warnings: void *operator new (unsigned, void *p) { return p; } template <unsigned nbytes> struct Storage { union { char bytes[nbytes]; int align; }; void *addr() { return bytes; } }; template <class T> struct Conditionally { Storage<sizeof(T)> storage; bool b; public: Conditionally(bool b) : b(b) { if (b) new(storage.addr()) T(); } T &asT() { return *reinterpret_cast<T *>(storage.addr()); } ~Conditionally() { if (b) asT().~T(); } }; struct A { int x; A() {} ~A() {} }; int main() { Conditionally<A> c(true); } -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |