Pregunta

Tengo un problema causado por romper la regla de alias de puntero estricto. Tengo un tipo T que proviene de una plantilla y algún tipo integral Int del mismo tamaño (como con sizeof). Mi código esencialmente hace lo siguiente:

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

Porque T es algún tipo arbitrario (aparte de la restricción de tamaño) que podría tener un constructor, no puedo hacer una unión de T y Int. (Esto solo está permitido solo en C ++ 0x y aún no es compatible con GCC).

¿Hay alguna forma de reescribir el pseudocódigo anterior para preservar la funcionalidad y evitar romper la regla de alias estricto? Tenga en cuenta que esta es una plantilla, no puedo controlar T o valor de some_other_t; La asignación y la comparación posterior ocurren dentro del código plantado.

(Para el registro, el código anterior comenzó a romperse en GCC 4.5 si T contiene cualquier campos de bits.)

¿Fue útil?

Solución

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))
        ...

En mi sistema, GCC optimiza ambos is_T_0() y memcpy(), lo que resulta en solo algunas instrucciones de ensamblaje en myfunc().

Otros consejos

¿Has oído hablar de boost::optional ?

Debo admitir que no estoy claro en cuanto al problema real aquí ... pero Boost :: opcional Permitir almacenar por valor y, sin embargo, saber si la memoria real se ha inicializado o no. También permite la construcción y destrucción en su lugar, por lo que podría ser un buen ajuste, supongo.

EDITAR:

Creo que finalmente entendí el problema: desea poder asignar muchos objetos, en varios puntos de la memoria, y le gustaría saber si la memoria en este punto realmente tiene un objeto o no.

Desafortunadamente, su solución tiene un gran problema: es incorrecto. Si alguna vez T de alguna manera puede ser representado por un null Patrón de bit, entonces pensarás que es memoria unitializada.

Tendrá que recurrir para agregar al menos un poco de información. Realmente no es mucho, después de todo, eso es solo el 3% del crecimiento (33 bits para 4 bytes).

Podrías, por ejemplo, usar algo de Mimick boost::optional pero en forma de matriz (para evitar la pérdida de relleno).

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

Entonces es tan simple como eso:

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: Por simplicidad, no he considerado el problema de la alineación. Si no sabe sobre el tema, lea sobre él antes de jugar con la memoria :)

Qué tal esto:

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

Puede que no sea tan eficiente, pero debería eliminar la advertencia.


Anexo #1:

Ya que T está obligado a ser del mismo tamaño que Int, hazte un valor cero de tipo ficticio bit a bit a T y comparar directamente contra él (en lugar de lanzar y comparar Agaist Int(0)).

Si su programa es de un solo hilo, podría tener algo como esto:

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

Si es multiproceso, querrá evitar usar un miembro estático zero_, y que cada instancia de contenedor se mantenga propia zero_ miembro:

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

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

private:
    T zero_;
};

Anexo #2:

Permítanme poner el anexo anterior de otra manera más simple:

// 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)

¿Por qué no simplemente?

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

(es posible que desee intentar agregar también el static calificador para zero Para ver si marca la diferencia en cuanto al rendimiento)

Use una computadora de 33 bits. ;-PAGS

Se siente como un truco, pero aparentemente encontré una solución: usar volatile por Int fundición. Esencialmente, lo que estoy haciendo ahora es:

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

El problema con bitfield T ahora se ha ido. Aún así, no me siento muy feliz por esto como volatile no está bien definido en C ++ Afaik ...

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