2017-08-26 3 views
8

classe Facebook fbstring_core utilise la « Petite chaîne d'optimisation » décrite dans this talk dans laquelle le stockage pour les données membres de la classe - un Char*, size et capacity - sera réaffecté aux données de caractères magasin si la chaîne est suffisamment faible. Les bits de drapeau utilisés pour distinguer ces cas sont situés dans le "caractère le plus à droite du stockage". Ma question est de savoir si l'accès à ces bits par l'intermédiaire du membre syndical bytes_, qui n'est jamais réellement écrit, constitue un comportement indéfini selon la norme C++ 11? La réponse à Accessing inactive union member and undefined behavior? suggère que c'est.L'optimisation de petite chaîne de FBString repose-t-elle sur un comportement indéfini?

L'extrait suivant contient la déclaration de ces membres et la fonction membre category() utilisée pour déterminer si cette optimisation est effective.

typedef uint8_t category_type; 

    enum class Category : category_type { 
     isSmall = 0, 
     isMedium = kIsLittleEndian ? 0x80 : 0x2, 
     isLarge = kIsLittleEndian ? 0x40 : 0x1, 
    }; 

    Category category() const { 
     // works for both big-endian and little-endian 
     return static_cast<Category>(bytes_[lastChar] & categoryExtractMask); 
    } 

    struct MediumLarge { 
     Char * data_; 
     size_t size_; 
     size_t capacity_; 

     size_t capacity() const { 
     return kIsLittleEndian 
      ? capacity_ & capacityExtractMask 
      : capacity_ >> 2; 
     } 

     void setCapacity(size_t cap, Category cat) { 
     capacity_ = kIsLittleEndian 
      ? cap | (static_cast<size_t>(cat) << kCategoryShift) 
      : (cap << 2) | static_cast<size_t>(cat); 
     } 
    }; 

    union { 
     uint8_t bytes_[sizeof(MediumLarge)]; // For accessing the last byte. 
     Char small_[sizeof(MediumLarge)/sizeof(Char)]; 
     MediumLarge ml_; 
    }; 

Il semble que cette mise en œuvre repose sur l'utilisation de « type calembour » pour accéder à un octet qui pourrait en fait faire partie de l'organe size_t capacity_. De la réponse à la question liée ci-dessus, je comprends que ce est comportement défini dans C99, mais pas en C++ 11?

+1

L'accès à un «char» de toute forme est toujours autorisé. – o11c

+1

Un code similaire dans Boost qui a entraîné un bogue: https://svn.boost.org/trac10/ticket/12183 –

+0

@ o11c: Est-ce que '' uint8_t'' est défini comme étant un alias pour un type '' char'' , bien que? Il semble plus sûr d'utiliser '' unsigned char''. –

Répondre

11

Non seulement qui semblent être UB, il est tout à fait inutile, parce que l'utilisation seule de bytes_ semble être pour lire le dernier octet de this, qui peut être fait sans UB:

reinterpret_cast<const char*>(this)[sizeof(*this) - 1] 

C'est grâce à l'exception spéciale en C++ qui permet de réinterpréter des objets comme des tableaux char.