gcc: Come usare __attribute ((__ may_alias__)), onde evitare di “derefencing tipo puntatore-punned” warning

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

Domanda

Ho avuto qualche codice che utilizza il tipo-gioco di parole per evitare di dover chiamare un membro di "oggetto" 's costruttore e distruttore a meno che / fino a quando è effettivamente necessario per utilizzare l'oggetto.

Funziona bene, ma sotto g ++ 4.4.3, ottengo questo avviso del compilatore temuto:

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

I miei tentativi di codice a __attribute l'uso di gcc ((__ may_alias__)) per far gcc sapere circa il potenziale aliasing, ma gcc non sembra capire quello che sto cercando di raccontarla. Sto facendo qualcosa di sbagliato, o non gcc 4.4.3 basta avere alcuni problemi con l'attributo __may_alias__?

Codice giocattolo per riprodurre l'avviso del compilatore è qui sotto:

#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;
}
È stato utile?

Soluzione

Penso che la typedef confonde GCC. Questi tipi di attributi sembrano funzionare meglio se applicato direttamente alle definizioni di variabili.

Questa versione della classe funziona per me (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.]

Altri suggerimenti

I sosterrebbe di avere la classe che contiene solo contenere un array di caratteri di dimensioni sufficienti a contenere il vostro "oggetto" membro e quindi utilizzando nuova collocazione per inizializzare in cima alla array di caratteri. Che ha il vantaggio di essere compatibile con le specifiche così come cross-compilatore. L'unico problema è che si deve conoscere la dimensione in caratteri del vostro oggetto membro, che può metterti nei guai.

C'è una ragione per cui non si può avere il membro sia un puntatore e utilizzare nuovi e cancellare?

Che cosa succede se si sostituisce _isObjectConstructed con un puntatore all'oggetto:

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

Nota, senza estensione GCC, solo puro codice C ++.

Utilizzando un T* invece di un bool non sarà nemmeno fare Lightweight ancor più grande!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top