2016-12-07 5 views
1

Je suis actuellement en train d'implémenter CBOR et j'ai besoin à plusieurs reprises de lire 1, 2, 4 ou 8 octets à partir d'un tableau d'octets qui doivent ensuite être combinés en un type entier de 1, 2, 4 ou 8 octets.Type d'octet en nombre entier: décalage et ajout ou conversion implicite à travers une union?

Pour le 4 cas d'octets, j'utilise actuellement cette fonction de modèle (vec est le vecteur d'octet Je lis, current_idx marque la position dans le vecteur d'où je veux commencer à lire 4 octets):

template<typename T, typename std::enable_if<sizeof(T) == 4, int>::type = 0> 
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_idx) 
{ 
    return static_cast<T>((static_cast<T>(vec[current_idx]) << 030) + 
          (static_cast<T>(vec[current_idx + 1]) << 020) + 
          (static_cast<T>(vec[current_idx + 2]) << 010) + 
          static_cast<T>(vec[current_idx + 3])); 
} 

(I ont trois fonctions similaires pour le cas de 1, 2, et 8 octets, respectivement.)

Un appel d'exemple serait

std::vector<uint8_t> vec {0x01, 0x00, 0x00, 0xff}; 
auto num = get_from_vector<uint32_t>(vec, 0); 
assert(num == 0x10000FF); 

performances Bien que ne semble pas la question ici, mais je me demande quand même si le code suivant peut être plus efficace ou au moins plus lisible:

template<typename T, typename std::enable_if<sizeof(T) == 4, int>::type = 0> 
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_idx) 
{ 
    union U 
    { 
     T result_type; 
     uint8_t bytes[4]; 
    } u; 
    u.bytes[3] = vec[current_idx]; 
    u.bytes[2] = vec[current_idx + 1]; 
    u.bytes[1] = vec[current_idx + 2]; 
    u.bytes[0] = vec[current_idx + 3]; 
    return u.result_type; 
} 

Toute réflexion sur ce sujet?

+1

L'utilisation d'un syndicat pour le type calembour fonctionne en général, mais est UB, si cela est important pour vous. Sur un autre point, c'est la première utilisation raisonnable que j'ai vu des octals depuis longtemps;) –

+0

Ce type d'utilisation d'Union est UB comme l'a dit @ IanM_Matrix1. Mais cela fonctionnera avec la plupart des compilateurs. Si je ne me trompe pas, le document de GCC fait même une mention explicite de l'utilisation de l'union pour le type punting, qui est traité comme une exception de ce type d'UB. –

+0

pourquoi ne pas avoir juste quelque chose comme 'T résultat; uint8_t * res_ptr = reinterpret_cast (& résultat); * résultat ++ = vec [current_idx + 3]; ... '? – akappa

Répondre

1

Personnellement, je préfère votre deuxième choix (en utilisant des syndicats), car il semble être un peu plus rapide et plus lisible.

Mais il existe une autre façon de définir votre fonction: utiliser des pointeurs. Un avantage est que vous devrez définir une seule fonction au lieu de la surcharger.

template<typename T> 
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_index){ 
    T result; 
    uint8_t *ptr = (uint8_t *) &result; 
    size_t idx = current_index + sizeof(T); 
    while(idx > current_index) 
     *ptr++ = vec[--idx]; 
    return result; 
} 

votre exemple Modifier appelant:

int main(){ 
    std::vector<uint8_t> vec {0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xff}; 

    auto byte1 = get_from_vector<uint8_t>(vec, 3); 
    assert(byte1 == 0xff); 

    auto byte2 = get_from_vector<uint16_t>(vec, 3); 
    assert(byte2 == 0xff01); 

    auto byte4 = get_from_vector<uint32_t>(vec, 4); 
    assert(byte4 == 0x010000ff); 

    auto byte8 = get_from_vector<uint64_t>(vec, 0); 
    assert(byte8 == 0x010000ff010000ffUL); 
} 
+0

Merci - cela fonctionne pour moi! –