Domanda

Ho un problema causato dalla rottura regola rigorosa puntatore aliasing. Ho un tipo T che viene da un modello e un certo tipo integrale Int della stessa dimensione (come con sizeof). Il mio codice fa essenzialmente la seguente:

T x = some_other_t;
if (*reinterpret_cast <Int*> (&x) == 0)
  ...

A causa T è un po 'arbitrario (diversa dalla restrizione di dimensione) tipo che potrebbe avere un costruttore, non posso fare un'unione di T e Int. (Questo è consentito solo in solo C ++ 0x e non è nemmeno supportato da GCC ancora).

C'è un modo ho potuto riscrivere la pseudocodice sopra per preservare la funzionalità ed evitare la rottura regola rigorosa aliasing? Si noti che questo è un modello, non posso controllare T o il valore di some_other_t; l'assegnazione e il successivo confronto accadono all'interno del codice basato su modelli.

(Per la cronaca, il codice di cui sopra ha iniziato rottura su GCC 4.5 se T contiene tutti i campi di bit.)

È stato utile?

Soluzione

static inline int is_T_0(const T *ob)
{
        int p;
        memcpy(&p, ob, sizeof(int));
        return p == 0;
}

void myfunc(void)
{
    T x = some_other_t;
    if (is_T_0(&x))
        ...

Sul mio sistema, GCC ottimizza via sia is_T_0() e memcpy(), con conseguente poche istruzioni di montaggio in myfunc().

Altri suggerimenti

Avete sentito parlare boost::optional?

Devo ammettere che sono poco chiare per quanto riguarda il vero problema qui ... ma boost :: opzionali consentono di memorizzare in base al valore e tuttavia sapere se la memoria effettiva è stata inizializzata. Mi permette anche in luogo di costruzione e distruzione, quindi potrebbe essere una buona misura immagino.

Modifica :

Credo di aver finalmente afferrato il problema: si vuole essere in grado di allocare un sacco di oggetti, in vari punti in memoria, e vuoi sapere se la memoria a questo punto detiene veramente un oggetto o no .

Purtroppo la soluzione ha un problema enorme: non è corretto. Se T mai possa in qualche modo essere rappresentato da uno schema di bit null Allora ti pensi che sia la memoria non inizializzate.

Si dovrà ricorrere a voi stessi di aggiungere almeno un bit di informazione. Non è molto in realtà, dopo tutto questo è solo il 3% della crescita (33 bit per 4 byte).

Si potrebbe ad esempio utilizzare un po 'di boost::optional Mimick ma in modo array (per evitare la perdita padding).

template <class T, size_t N>
class OptionalArray
{
public:


private:
  typedef unsigned char byte;

  byte mIndex[N/8+1];
  byte mData[sizeof(T)*N]; // note: alignment not considered
};

Allora è così semplice come sembra:

template <class T, size_t N>
bool OptionalArray<T,N>::null(size_t const i) const
{
  return mIndex[i/8] & (1 << (i%8));
}

template <class T, size_t N>
T& OptionalArray<T,N>::operator[](size_t const i)
{
  assert(!this->null(i));
  return *reinterpret_cast<T*>(mData[sizeof(T)*i]);
}

Nota : Per semplicità non ho considerato la questione dell'allineamento. Se non si conosce sull'argomento, leggere su di esso prima di giocherellare con la memoria:)

Che ne dite di questo:

Int zero = 0;
T x = some_other_t;
if (std::memcmp(&x, &zero, sizeof(zero)) == 0)

Si potrebbe non essere il più efficiente, ma dovrebbe sbarazzarsi di avvertimento.


APPENDICE # 1:

Poiché T è vincolata ad essere le stesse dimensioni Int, farsi un manichino bit valore zero di tipo T e confrontarlo direttamente contro di essa (invece di colata e confrontando agaist Int(0)).

Se il programma è single-threaded, si potrebbe avere qualcosa di simile:

template <typename T>
class Container
{
public:
    void foo(T val)
    {
        if (zero_ == val)
        {
            // Do something
        }
    }

private:
    struct Zero
    {
        Zero() {memset(&val, 0, sizeof(val));}
        bool operator==(const T& rhs) const {return val == rhs;}
        T val;
    };
    static Zero zero_;
};

Se è multi-threaded, ti consigliamo di evitare di utilizzare un zero_ membro statico, e avere ogni attesa esempio contenitore di un proprio membro zero_:

template <typename T>
class MTContainer
{
public:
    MTContainer() {memset(zero_, 0, sizeof(zero_));}

    void foo(T val)
    {
        if (val == zero_)
        {
            // Do something
        }
    }

private:
    T zero_;
};

Addendum # 2:

Mettiamola l'addendum di cui sopra in un altro modo più semplice:

// zero is a member variable and is inialized in the container's constructor
T zero;
std::memset(&zero, 0, sizeof(zero));

T x = some_other_t;
if (x == zero)

Perché non semplicemente:

const Int zero = 0;
if (memcmp(&some_other_t, &zero, sizeof(zero)) == 0)
  /* some_other_t is 0 */

(si consiglia di provare ad aggiungere anche la qualificazione per static zero per vedere se fa la differenza di prestazioni-saggio)

Usa un computer 33-bit. ;-P

Ci si sente come un hack, ma a quanto pare ho trovato una soluzione: utilizzando volatile per Int casting. In sostanza, quello che sto facendo ora è:

T x = some_other_t;
if (*reinterpret_cast <volatile Int*> (&x) == 0)
  ...

Il problema con T campo di bit è ormai andato. Eppure, non mi sento molto felice di questo come volatile non è ben definito in C ++ per quanto ne so ...

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