2016-04-14 1 views
1

Je me demande quelle est la méthode recommandée pour convertir un entier en/de little-endian de manière portable.Comment convertir un nombre entier en little-endian d'une manière portable

Y a-t-il une bibliothèque pour cela?

Nous pouvons utiliser htonl and ntohl, puis effectuer une autre conversion big-endian vers (from) little-endian, mais ce n'est pas efficace.

+0

Qu'essayez-vous d'accomplir? À moins de faire un casting et un déréférencement funky de pointeurs vers des entiers de tailles différentes, vous n'avez pas besoin de vous soucier de l'endianess. Voir aussi cette question sur ["htonl() vs __builtin_bswap32()"] (http://stackoverflow.com/questions/21527957/htonl-vs-builtin-bswap32) qui parle d'autres façons d'échanger des nombres par octets. – Jens

+2

Si vous devez jouer avec l'endianess, vous marchez déjà dans les domaines de "non portable". Donc, isolez ceci de telle sorte que le reste de votre code fonctionne avec des abstractions appropriées, et *** de petites implémentations spécifiques à la plate-forme sont implémentées pour des plates-formes spécifiques. –

Répondre

1

La méthode portable consiste à utiliser des bits de décalage et des masques dans une chaîne de taille appropriée. Notez que je dis chaîne, parce que c'est vraiment la seule fois où vous devez vous occuper de l'endianness - lors du transfert d'octets entre les systèmes.

Si vous voulez éviter les conversions inutiles (par exemple en convertissant en little-endian sur une architecture little-endian), il n'y a pas de manière complètement portable de le faire au moment de la compilation. Mais vous pouvez vérifier au moment de l'exécution de sélectionner dynamiquement l'ensemble des fonctions de conversion.

Cela présente l'inconvénient de ne pas pouvoir insérer le code. Il peut être plus efficace d'écrire les conversions de manière portable et d'utiliser des modèles ou des inlining. Combiné avec des contrôles de compilation semi-portables, c'est à peu près aussi bon que vous obtiendrez.

Lecture supplémentaire: Detecting Endianness à la compilation.

+0

les solutions de ce lien ne fonctionnent pas réellement/ne sont pas portables. Il s'avère qu'il est illégal de convertir un entier en une séquence d'octets dans le contexte constexpr, que ce soit par transtypage ou union explicite. –

1

Ceci est une excellente question. Cela m'a incité à voir s'il y avait un moyen de déterminer l'endianness à la compilation en utilisant l'expression constexpr.

Il s'avère que sans astuces de préprocesseur, cela n'est pas possible, car il est impossible de transformer un entier en une séquence d'octets (via des conversions ou des unions) lors de l'évaluation dans un contexte constexpr.

Cependant il se trouve que dans gcc, un simple contrôle d'exécution est optimisé de façon loin lorsqu'il est compilé avec -O2, donc c'est en fait d'une efficacité optimale:

#include <cstdint> 
#include <iostream> 

constexpr bool is_little_endian() 
{ 
    union endian_tester { 
     std::uint16_t n; 
     std::uint8_t p[4]; 
    }; 

    constexpr endian_tester sample = {0x0102}; 
    return sample.p[0] == 0x2; 
} 

template<class Int> 
Int to_little_endian(Int in) 
{ 
    if (is_little_endian()) { 
    return in; 
    } 
    else { 
    Int out = 0; 
    std::uint8_t* p = reinterpret_cast<std::uint8_t*>(std::addressof(out)); 
    for (std::size_t byte = 0 ; byte < sizeof(in) ; ++byte) 
    { 
     auto part = (in >> (byte * 8)) & 0xff; 
     *p++ = std::uint8_t(part); 
    } 

    return out; 
    } 
} 

int main() 
{ 
    auto x = to_little_endian(10); 
    std::cout << x << std::endl; 
} 

est ici la sortie assembleur lors de la compilation sur une plate-forme Intel (little-endian):

main: 
     subq $8, %rsp 
# 
# here is the call to to_little_endian() 
# 

     movl $10, %esi 
# 
# that was it - it's been completely optimised away 
# 

     movl std::cout, %edi 
     call std::basic_ostream<char, std::char_traits<char> >::operator<<(int) 
     movq %rax, %rdi 
     call std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&) 
     xorl %eax, %eax 
     addq $8, %rsp 
     ret