Question

J'ai un problème causé par la rupture stricte règle de l'aliasing pointeur. J'ai un type T qui vient d'un modèle et une Int de type intégral de la même taille (comme sizeof). Mon code fait essentiellement ce qui suit:

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

Parce que T est un peu arbitraire (autre que la restriction de taille) type qui pourrait avoir un constructeur, je ne peux pas faire une union de T et Int. (Ceci est uniquement autorisé en C ++ 0x uniquement et même pas encore pris en charge par GCC).

Est-il possible que je pourrais réécrire le pseudocode ci-dessus pour préserver la fonctionnalité et éviter de casser la règle stricte aliasing? Notez que ceci est un modèle, je ne peux pas contrôler T ou de la valeur de some_other_t; l'affectation et la comparaison ultérieure ne se produisent à l'intérieur du code templated.

(Pour mémoire, le code ci-dessus a commencé à briser le GCC 4.5 si T contient des champs de bits.)

Était-ce utile?

La solution

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

Sur mon système, GCC optimise détruiront is_T_0() et memcpy(), ce qui en quelques instructions de montage dans myfunc().

Autres conseils

Avez-vous entendu parler boost::optional?

Je dois admettre que je suis pas clair quant au vrai problème ici ... mais boost :: en option permettent de stocker en valeur et savoir encore a été initialisé ou non la mémoire réelle. Je permet également en place la construction et la destruction, pourrait donc être un bon ajustement je suppose.

EDIT :

Je pense que j'ai finalement saisi le problème: vous voulez être en mesure d'allouer beaucoup d'objets, à divers points en mémoire, et que vous souhaitez savoir si la mémoire ou non à ce point tient vraiment un objet ou non .

Malheureusement, votre solution a un énorme problème: il est incorrect. Si jamais T peut en quelque sorte être représenté par un motif de bits de null, alors vous pensez que c'est la mémoire non initialisée.

Vous devrez vous recourir à ajouter au moins un bit d'information. Il n'y a pas beaucoup vraiment, après tout ce qui est seulement 3% de la croissance (33 bits pour 4 octets).

Vous pouvez par exemple utiliser certaines Mimick boost::optional mais d'une manière de tableau (pour éviter la perte de remplissage).

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

Ensuite, il est aussi simple que cela:

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

Remarque : Par souci de simplicité, je n'ai pas examiné la question de l'alignement. Si vous ne savez pas sur le sujet, lire à ce sujet avant de jongler avec la mémoire:)

Que diriez-vous ceci:

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

Il pourrait ne pas être aussi efficace, mais il devrait se débarrasser de l'avertissement.


ADDENDA # 1:

Depuis T est contrainte d'être la même taille que Int, vous faire un mannequin zéro valeur de type binaire T et comparer directement contre elle (au lieu de la coulée et la comparaison agaist Int(0)).

Si votre programme est mono-thread, vous pourriez avoir quelque chose comme ceci:

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 elle est multi-thread, vous voulez éviter d'utiliser un membre statique zero_, et ont chaque instance de conteneur tenir son propre membre de 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_;
};

ADDENDA # 2:

Permettez-moi de mettre l'additif ci-dessus d'une autre manière plus 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)

Pourquoi ne pas simplement:

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

(vous pouvez essayer d'ajouter aussi le qualificatif de static à zero pour voir si cela fait une différence en terme de performance)

Utilisation d'un ordinateur 33 bits. ;-P

Il se sent comme un hack, mais apparemment je l'ai trouvé une solution: en utilisant volatile pour la coulée de Int. Essentiellement, ce que je fais est maintenant:

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

Le problème avec T bitfield est maintenant disparu. Pourtant, je ne me sens pas tout à fait heureux de ce que volatile est pas bien définie en C ++ ... AFAIK

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top