Вопрос

We just upgraded our compiler to gcc 4.6 and now we get some of these warnings. At the moment our codebase is not in a state to be compiled with c++0x and anyway, we don't want to run this in prod (at least not yet) - so I needed a fix to remove this warning.

The warnings occur typically because of something like this:

struct SomeDataPage
{
  // members
  char vData[SOME_SIZE];
};

later, this is used in the following way

SomeDataPage page;
new(page.vData) SomeType(); // non-trivial constructor

To read, update and return for example, the following cast used to happen

reinterpret_cast<SomeType*>(page.vData)->some_member();

This was okay with 4.4; in 4.6 the above generates:

warning: type punned pointer will break strict-aliasing rules

Now a clean way to remove this error is to use a union, however like I said, we can't use c++0x (and hence unrestricted unions), so I've employed the horrible hack below - now the warning has gone away, but am I likely to invoke nasal daemons?

static_cast<SomeType*>(reinterpret_cast<void*>(page.vData))->some_member();

This appears to work okay (see simple example here: http://www.ideone.com/9p3MS) and generates no warnings, is this okay(not in the stylistic sense) to use this till c++0x?

NOTE: I don't want to use -fno-strict-aliasing generally...

EDIT: It seems I was mistaken, the same warning is there on 4.4, I guess we only picked this up recently with the change (it was always unlikely to be a compiler issue), the question still stands though.

EDIT: further investigation yielded some interesting information, it seems that doing the cast and calling the member function in one line is what is causing the warning, if the code is split into two lines as follows

SomeType* ptr = reinterpret_cast<SomeType*>(page.vData);
ptr->some_method();

this actually does not generate a warning. As a result, my simple example on ideone is flawed and more importantly my hack above does not fix the warning, the only way to fix it is to split the function call from the cast - then the cast can be left as a reinterpret_cast.

Это было полезно?

Решение

SomeDataPage page;
new(page.vData) SomeType(); // non-trivial constructor
reinterpret_cast<SomeType*>(page.vData)->some_member();

This was okay with 4.4; in 4.6 the above generates:

warning: type punned pointer will break strict-aliasing rules

You can try:

SomeDataPage page;
SomeType *data = new(page.vData) SomeType(); // non-trivial constructor
data->some_member();

Другие советы

Why not use:

SomeType *item = new (page.vData) SomeType();

and then:

item->some_member ();

I don't think a union is the best way, it may also be problematic. From the gcc docs:

`-fstrict-aliasing'
 Allows the compiler to assume the strictest aliasing rules
 applicable to the language being compiled.  For C (and C++), this
 activates optimizations based on the type of expressions.  In
 particular, an object of one type is assumed never to reside at
 the same address as an object of a different type, unless the
 types are almost the same.  For example, an `unsigned int' can
 alias an `int', but not a `void*' or a `double'.  A character type
 may alias any other type.

 Pay special attention to code like this:
      union a_union {
        int i;
        double d;
      };

      int f() {
        a_union t;
        t.d = 3.0;
        return t.i;
      }
 The practice of reading from a different union member than the one
 most recently written to (called "type-punning") is common.  Even
 with `-fstrict-aliasing', type-punning is allowed, provided the
 memory is accessed through the union type.  So, the code above
 will work as expected.  However, this code might not:
      int f() {
        a_union t;
        int* ip;
        t.d = 3.0;
        ip = &t.i;
        return *ip;
      }

How this relates to your problem is tricky to determine. I guess the compiler is not seeing the data in SomeType as the same as the data in vData.

I'd be more concerned about SOME_SIZE not being big enough, frankly. However, it is legal to alias any type with a char*. So simply doing reinterpret_cast<T*>(&page.vData[0]) should be just fine.

Also, I'd question this kind of design. Unless you're implementing boost::variant or something similar, there's not much reason to use it.

struct SomeDataPage
{
  // members
  char vData[SOME_SIZE];
};

This is problematic for aliasing/alignment reasons. For one, the alignment of this struct isn't necessarily the same as the type you're trying to pun inside it. You could try using GCC attributes to enforce a certain alignment:

struct SomeDataPage { char vData[SOME_SIZE] __attribute__((aligned(16))); };

Where alignment of 16 should be adequate for anything I've come across. Then again, the compiler still won't like your code, but it won't break if the alignment is good. Alternatively, you could use the new C++0x alignof/alignas.

template <class T>
struct DataPage {
   alignof(T) char vData[sizeof(T)];
};
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top