2017-04-20 5 views
0

Je devais vérifier le code maladroit de quelqu'un qui utilisait plus de 20 unions et selon le nombre de méthodes qui calinaient et copiait des données depuis et vers un tableau d'octets, afin de mettre en œuvre ces méthodes via le template (second pour un char Ce qui m'inquiète si je reste conforme aux normes et que j'attribue de nouvelles valeurs via les références renvoyées est valide.type punning avec gabarit

+0

Eh bien, il est en tout cas un comportement non défini, selon la norme va. C'est votre travail de vous assurer que vous ne frayez pas d'hydras. – DeiDei

+0

@DeiDei Je ne fais pas d'hydras en fait, ce n'est pas le pire exemple de création d'hydras par des collègues. que s'est-il passé avec la règle de la conversion en caractère de type caractère et ainsi de suite? Vraiment, les commentaires sans fondement comme ça ne sont pas utiles, d'ailleurs, il n'y a aucun moyen de contourner A: le but est d'afficher les données et de les emballer dans l'ordre inverse ou direct B) rien d'autre que C++ est disponible – Swift

Répondre

1

Votre code semble légal. Il y a quelques améliorations:

constexpr bool k_little_endian 
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) 
    = true; 
#else 
    = false; 
#endif 
constexpr bool k_debug 
#ifdef QT_DEBUG 
    = true; 
#else 
    = false; 
#endif 

template <class T, 
    bool little_endian = k_little_endian, 
    bool debug = k_debug 
> 
class type_punner { 
    T* p; 
    unsigned char* pun() const { return reintepret_cast<unsigned char*>(p); } 
public: 
    static_assert(std::is_pod<T>::value, "type_punner can be used only for POD"); 
    type_punner(T& ref): 
    p (std::addressof(ref)) 
    {} 
    type_punner(type_punner const&)=default; 
    type_punner()=delete; 

    unsigned char& at(std::size_t i) const noexcept(!debug) { 
    if (debug && !(i<size())) throw std::out_of_range(__FUNCTION__); 
    if (little_endian) 
     return pun()[i]; 
    else 
     return pun()[size() - i - 1]; 
    } 

    unsigned char& reverse_at(std::size_t i) const noexcept(!debug) { 
    if(debug && !(i < size())) throw std::out_of_range(__FUNCTION__); 
    if (little_endian) 
     return pun()[size() - i - 1]; 
    else 
     return pun()[i]; 
    } 
    // = 0 is LSB 
    unsigned char& operator[](std::size_t i) const noexcept(!debug) { 
    return at(i); 
    } 
    static constexpr std::size_t size() noexcept(true) { return sizeof(T); } 
}; 

En premier lieu, cela déplace les macros de la route. Code qui est ce que vous regardez a tendance à être plus facile à raisonner, et les compilateurs sont parfaitement capables d'éliminer les branches mortes. Il est rarement une idée de stocker une référence sauf si vous voulez une sémantique de référence sur votre type, et il n'est presque jamais une bonne idée de stocker une référence à côté d'une non-référence dans la même classe/struct. Deuxièmement, les corps de classe en ligne sont redondants. En troisième lieu, size est à la fois constexpr et statique. Au contraire, T& avec copie/affectation par défaut ne fait rien de raisonnable. T* fait. J'ai donc utilisé T*.

Cinquièmement, pas besoin de stocker pun. Générez-le chaque fois pour un coût nul. Notez que le type_punner ci-dessus peut expérimenter avec un little_endian dans un gros environnement en boutiste, ou activer le débogage uniquement pour une partie où vous rencontrez des problèmes. Le coût pour cela est un minuscule compilateur.

+0

Oh, c'est intéressant champ de référence! bonne idée sur le déplacement des macros, 'constexpr' pas supporté, mais 'const' devrait suffire .. J'ai utilisé la définition de macro qui l'insère dans le cas si nous mettons à jour, et pour un des deux compilateurs utilisés inline n'est pas redunadant (il ne se développe jamais sans inline, ou il étend toutes les fonctions) et ne stocke pas de référence en classe, static_assert() ne fonctionne que des corps de fonctions, aucune méthode par défaut ou supprimée n'est prise en charge. Je vais certainement expérimenter avec le jeu de mots() à la place – Swift

+0

quoi de neuf noexcept (! Debug)? jamais vu ça avant – Swift

+1

@Swift It stats "c'est une garantie de ne pas lancer si' debug' est faux, mais peut lancer si 'debug' est vrai." – Yakk

0

Seulement ce qui m'inquiète si je reste conforme aux normes et que j'attribue de nouvelles valeurs via des références renvoyées est valide.

Il est valable, parce que ces références sont de type de caractères étroits qui sont spéciaux.