GCC: Cómo usar __Attribute ((__ May_alias__)) correctamente para evitar advertencia de "Desalteración de tipo de tipo de tipo

StackOverflow https://stackoverflow.com/questions/6313050

Pregunta

Tengo algún código que usa Tipo-Punning para evitar tener que llamar al constructor y destructor de un miembro de un miembro "a menos/hasta que sea realmente necesario usar el objeto.

Funciona bien, pero bajo G ++ 4.4.3, recibo esta temida advertencia del compilador:

jaf@jeremy-desktop:~$ g++ -O3 -Wall puns.cpp 
puns.cpp: In instantiation of ‘Lightweight<Heavyweight>’:
puns.cpp:68:   instantiated from here 
puns.cpp:12: warning: ignoring attributes applied to ‘Heavyweight’ after definition
puns.cpp: In destructor ‘Lightweight<T>::~Lightweight() [with T = Heavyweight]’:
puns.cpp:68:   instantiated from here
puns.cpp:20: warning: dereferencing type-punned pointer will break strict-aliasing rules
puns.cpp: In member function ‘void Lightweight<T>::MethodThatGetsCalledRarely() [with T = Heavyweight]’:
puns.cpp:70:   instantiated from here
puns.cpp:36: warning: dereferencing type-punned pointer will break strict-aliasing rules

Mi código intenta usar el __attribute de GCC ((__ may_alias__)) para que GCC sepa sobre el alias potencial, pero GCC no parece entender lo que estoy tratando de decirle. ¿Estoy haciendo algo mal, o GCC 4.4.3 solo tiene algunos problemas con el atributo __may_alias__?

El código de juguete para reproducir la advertencia del compilador está a continuación:

#include <stdio.h>
#include <memory>    // for placement new
#include <stdlib.h>  // for rand()

/** Templated class that I want to be quick to construct and destroy.
  * In particular, I don't want to have T's constructor called unless
  * I actually need it, and I also don't want to use dynamic allocation.
  **/
template<class T> class Lightweight
{
private:
   typedef T __attribute((__may_alias__)) T_may_alias;

public:
   Lightweight() : _isObjectConstructed(false) {/* empty */}

   ~Lightweight()
   {
      // call object's destructor, only if we ever constructed it
      if (_isObjectConstructed) (reinterpret_cast<T_may_alias *>(_optionalObject._buf))->~T_may_alias();
   }

   void MethodThatGetsCalledOften()
   {
      // Imagine some useful code here
   }

   void MethodThatGetsCalledRarely()
   {
      if (_isObjectConstructed == false)
      {
         // demand-construct the heavy object, since we actually need to use it now
         (void) new (reinterpret_cast<T_may_alias *>(_optionalObject._buf)) T();
         _isObjectConstructed = true;
      }
      (reinterpret_cast<T_may_alias *>(_optionalObject._buf))->DoSomething();
   }

private:
   union {
      char _buf[sizeof(T)];
      unsigned long long _thisIsOnlyHereToForceEightByteAlignment;
   } _optionalObject;

   bool _isObjectConstructed;
};

static int _iterationCounter = 0;
static int _heavyCounter     = 0;

/** Example of a class that takes (relatively) a lot of resources to construct or destroy. */
class Heavyweight
{
public:
   Heavyweight()
   {
      printf("Heavyweight constructor, this is an expensive call!\n");
      _heavyCounter++;
   }

   void DoSomething() {/* Imagine some useful code here*/}
};

static void SomeMethod()
{
   _iterationCounter++;

   Lightweight<Heavyweight> obj;
   if ((rand()%1000) != 0) obj.MethodThatGetsCalledOften();
                      else obj.MethodThatGetsCalledRarely();
}

int main(int argc, char ** argv)
{
   for (int i=0; i<1000; i++) SomeMethod();
   printf("Heavyweight ctor was executed only %i times out of %i iterations, we avoid %.1f%% of the ctor calls!.\n", _heavyCounter, _iterationCounter, 100.0f*(1.0f-(((float)_heavyCounter)/((float)_iterationCounter))));
   return 0;
}
¿Fue útil?

Solución

Pienso que el typedef es confuso GCC. Este tipo de atributos parece funcionar mejor cuando se aplica directamente a las definiciones variables.

Esta versión de tu clase funciona para mí (GCC 4.6.0):

template<class T> class Lightweight
{
private:
  //  typedef T __attribute((__may_alias__)) T_may_alias;

public:
  Lightweight() : _isObjectConstructed(false) {/* empty */}

  ~Lightweight()
  {
    // call object's destructor, only if we ever constructed it
    if (_isObjectConstructed) {
      T * __attribute__((__may_alias__)) p
        = (reinterpret_cast<T *>(_optionalObject._buf));
      p->~T();
    }
  }

  void MethodThatGetsCalledOften()
  {
    // Imagine some useful code here
  }

  void MethodThatGetsCalledRarely()
  {
    T * __attribute__((__may_alias__)) p
      = (reinterpret_cast<T *>(_optionalObject._buf));
    if (_isObjectConstructed == false)
      {
        // demand-construct the heavy object, since we actually need to use it now

        (void) new (p) T();
        _isObjectConstructed = true;
      }
      p->DoSomething();
  }

  [etc.]

Otros consejos

Argumentaría por que su clase de contenido contenga una matriz de caracteres de tamaño suficiente para contener su "objeto" miembro y luego usar la colocación nueva para inicializarse en la parte superior de la matriz de char. Eso tiene la ventaja de ser compatible con las especificaciones y compiladores cruzados. El único problema es que debe saber el tamaño en los caracteres de su objeto miembro, lo que puede meterlo en problemas.

¿Hay alguna razón por la que no pueda que el miembro sea un puntero y use nuevo y elimine?

¿Qué pasa si reemplaza _isObjectConstructed con un puntero al objeto:

class Lightweight
{
public:
   Lightweight() : object(NULL) {/* empty */}

   ~Lightweight()
   {
      // call object's destructor, only if we ever constructed it
      if (object) object->~T();
   }

   void MethodThatGetsCalledOften()
   {
      // Imagine some useful code here
   }

   void MethodThatGetsCalledRarely()
   {
      if (!object)
      {
         // demand-construct the heavy object, since we actually need to use it now
         object = new (_optionalObject._buf) T();
      }
      object->DoSomething();
   }

private:
   union {
      char _buf[sizeof(T)];
      unsigned long long _thisIsOnlyHereToForceEightByteAlignment;
   } _optionalObject;

   T *object;
};

Tenga en cuenta que no hay extensión GCC, solo código C ++ puro.

Usando un T* en lugar de un bool ni siquiera hará Lightweight ¡Más grande!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top