From: andhow on
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
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
> 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
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! ]