Это хак для удаления псевдонимов UB? - PullRequest
10 голосов
/ 20 сентября 2011

Мы только что обновили наш компилятор до gcc 4.6, и теперь мы получаем некоторые из этих предупреждений. На данный момент наша кодовая база не находится в состоянии для компиляции с c ++ 0x, и в любом случае мы не хотим запускать это в prod (по крайней мере, пока) - поэтому мне нужно было исправить это предупреждение.

Предупреждения обычно появляются из-за чего-то подобного:

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

позже, это используется следующим образом

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

Для чтения, обновления и возврата, например, использовалось следующее приведение

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

Это было хорошо с 4.4; в 4.6 выше выдает:

предупреждение: тип перенаправленного указателя нарушит правила строгого алиасинга

Теперь чистый способ устранить эту ошибку - использовать union, однако, как я уже сказал, мы не можем использовать c ++ 0x (и, следовательно, неограниченные объединения), поэтому я использовал ужасный хак ниже - теперь предупреждение исчезло, но могу ли я вызвать носовые демоны?

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

Кажется, это работает нормально (см. Простой пример здесь: http://www.ideone.com/9p3MS) и не генерирует предупреждений, это нормально (не в стилистическом смысле), чтобы использовать это до c ++ 0x?

ПРИМЕЧАНИЕ: я не хочу использовать -fno-strict-aliasing обычно ...

РЕДАКТИРОВАТЬ : Кажется, я ошибся, то же самое предупреждение есть на 4.4, я думаю, что мы только что подняли это с изменением (это всегда было маловероятно, чтобы быть проблемой компилятора), вопрос все еще стоит, хотя.

РЕДАКТИРОВАТЬ : дальнейшее исследование дало некоторую интересную информацию, кажется, что приведение и вызов функции-члена в одной строке вызывает то, что вызывает предупреждение, если код разбит на две строки следующим образом

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

это на самом деле не генерирует предупреждение. В результате мой простой пример с ideone некорректен и, что более важно, мой хак выше не исправляет предупреждение , единственный способ исправить это - отделить вызов функции от приведения - тогда приведение может быть оставил как reinterpret_cast.

Ответы [ 4 ]

2 голосов
/ 13 декабря 2011
SomeDataPage page;
new(page.vData) SomeType(); // non-trivial constructor
reinterpret_cast<SomeType*>(page.vData)->some_member();

Это было хорошо с 4.4; в 4.6 выше, генерирует:

warning: type punned pointer will break strict-aliasing rules

Вы можете попробовать:

SomeDataPage page;
SomeType *data = new(page.vData) SomeType(); // non-trivial constructor
data->some_member();
1 голос
/ 20 сентября 2011

Почему бы не использовать:

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

, а затем:

item->some_member ();

Я не думаю, что объединение является лучшим способом, оно также может быть проблематичным.Из документации gcc:

`-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;
      }

Как это относится к вашей проблеме, определить сложно.Я предполагаю, что компилятор не видит данные в SomeType такими же, как данные в vData.

0 голосов
/ 20 сентября 2011
struct SomeDataPage
{
  // members
  char vData[SOME_SIZE];
};

Это проблематично по причинам наложения / выравнивания. С одной стороны, выравнивание этой структуры не обязательно совпадает с типом, который вы пытаетесь изобразить внутри. Вы можете попробовать использовать атрибуты GCC для обеспечения определенного выравнивания:

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

Где выравнивание 16 должно быть достаточно для всего, с чем я сталкивался. Опять же, компилятору все равно не понравится ваш код, но он не сломается, если выравнивание будет хорошим. В качестве альтернативы вы можете использовать новый C ++ 0x alignof / alignas.

template <class T>
struct DataPage {
   alignof(T) char vData[sizeof(T)];
};
0 голосов
/ 20 сентября 2011

Я бы больше беспокоился о том, что SOME_SIZE, честно говоря, недостаточно велик.Однако является допустимым для псевдонимов любого типа с char*.Так что просто делать reinterpret_cast<T*>(&page.vData[0]) должно быть просто отлично.

Кроме того, я бы поставил под сомнение этот вид дизайна.Если вы не используете boost::variant или что-то подобное, нет особых оснований для его использования.

...