Existe-t-il un moyen de faire une assertion au moment de la compilation de style C ++ pour déterminer la finalité de la machine?

StackOverflow https://stackoverflow.com/questions/280162

Question

J'ai un code de sérialisation de bas niveau qui est basé sur un modèle et j'ai évidemment besoin de connaître l'endianité du système au moment de compiletime (car les modèles sont spécialisés en fonction de l'endianité du système).

À l’heure actuelle, j’ai un en-tête avec des définitions de plate-forme, mais je préférerais pouvoir faire des affirmations sur l’endianness avec un test basé sur un modèle (comme un static_assert ou un boost_if). Raison d'être mon code devra être compilé et exécuté sur un large éventail de machines, de nombreux fournisseurs spécialisés, et probablement de périphériques qui n'existent pas en 2008, donc je ne peux pas vraiment deviner ce qui pourrait être nécessaire d'entrer dans ces années d'en-tête en bas de la route. Et puisque la base de code a une durée de vie prévue d’environ 10 ans. Je ne peux donc pas suivre le code pour toujours.

J'espère que cela clarifie ma situation.

Quelqu'un at-il donc connaissance d’un test à la compilation qui puisse déterminer l’endianisme, sans s’appuyer sur des définitions spécifiques du fournisseur?

Était-ce utile?

La solution

Si vous utilisez autoconf, vous pouvez utiliser la macro AC_C_BIGENDIAN , qui a toutes les chances de fonctionner (définition du WORDS_BIGENDIAN défini par défaut)

alternativement, vous pouvez essayer quelque chose comme ceci (tiré de autoconf) pour obtenir un test qui sera probablement optimisé (GCC, au moins, supprime l’autre branche)

int is_big_endian()
{
    union {
        long int l;
        char c[sizeof (long int)];
    } u;

    u.l = 1;

    if (u.c[sizeof(long int)-1] == 1)
    {
        return 1;
    }
    else
        return 0;
}

Autres conseils

Il n’existe aucun moyen portable de le faire au moment de la compilation, votre meilleur choix est probablement d’utiliser le Boost . macros endian ou émulent les méthodes qu'ils utilisent.

Hmm, c'est une question intéressante. Mon pari est que ce n'est pas possible. Je pense que vous devez continuer à utiliser les macros et utiliser BOOST_STATIC_ASSERT (! BIG_ENDIAN); ou static_assert en c ++ 0x. La raison pour laquelle je pense que c'est parce que l'endian'nes est une propriété de votre environnement d'exécution. Cependant, static_assert est considéré à la compilation.

Je vous suggère de consulter le code du nouvel éditeur de liens ELF GNU gold . Ian Lance Taylor, son auteur, a utilisé des modèles pour sélectionner la bonne endianité au moment de la compilation, afin d’assurer des performances optimales au moment de l’exécution. Il instancie explicitement tous les endians possibles, de sorte qu'il dispose toujours d'une compilation séparée (tous les modèles dans les en-têtes) de la définition et de la déclaration du modèle. Son code est excellent.

Cette réponse est basée sur les spécifications suivantes (pour plus de clarté):

  

Langue: C ++ v17, 64 bits
  Compilateurs: g ++ v8 (collection du compilateur GNU https://www.gnu.org/software/gcc/ ) & amp; Chaîne d'outils MingW 8.1.0 ( https://sourceforge.net/projects/mingw-w64/files / )
  OS: Linux Mint & amp; Windows

Les deux lignes de code suivantes peuvent être utilisées pour détecter avec succès l'endianité d'un processeur:

const uint8_t IsLittleEndian = char (0x0001);

ou

#define IsLittleEndian char (0x0001)

Ces deux joyaux d’instruction magiques tirent parti de la manière dont le processeur stocke une valeur 16 bits en mémoire.

Sur un "Little Endian" processeur, comme les jeux de puces Intel et AMD, une valeur de 16 bits est stockée dans un mode [octet de poids faible / poids le moins significatif] [octet de poids fort / élevé]] (les crochets représentent un octet en mémoire ).

Sur un "Big Endian" processeur, comme les jeux de puces PowerPC, Sun Sparc et IBM S / 390, une valeur de 16 bits est stockée dans un mode [octet de poids fort / important]] [octet de poids faible / minimal] .

Par exemple, lorsque nous stockons une valeur 16 bits (deux octets), disons 0x1234 , dans un C ++ uint16_t (type défini dans C ++ v11, et plus tard https://en.cppreference.com/w/cpp/types/integer) variable de taille sur un "Little Endian" Processeur, puis postez dans le bloc mémoire dans lequel la valeur est stockée, vous trouverez la séquence d'octets [34] [12] .

Sur un "processeur Big Endian", la valeur 0x1234 est stockée sous la forme [12] [34] .

Voici une petite démonstration permettant de montrer comment diverses variables entières de taille C ++ sont stockées en mémoire sur les processeurs petit et grand endian:

#define __STDC_FORMAT_MACROS // Required for the MingW toolchain
#include <iostream>
#include <inttypes.h>

const uint8_t IsLittleEndian = char (0x0001);
//#define IsLittleEndian char (0x0001)

std::string CurrentEndianMsg;
std::string OppositeEndianMsg;

template <typename IntegerType>
void PrintIntegerDetails(IntegerType IntegerValue)
{
    uint16_t SizeOfIntegerValue = sizeof(IntegerValue);
    int8_t i;

    std::cout << "Integer size (in bytes): " << SizeOfIntegerValue << "\n";
    std::cout << "Integer value (Decimal): " << IntegerValue << "\n";
    std::cout << "Integer value (Hexidecimal): ";

    switch (SizeOfIntegerValue)
    {
        case 2: printf("0x%04X\n", (unsigned int) IntegerValue);
                break;
        case 4: printf("0x%08X\n", (unsigned int) IntegerValue);
                break;
        case 8: printf("0x%016" PRIX64 "\n", (uint64_t) IntegerValue);
                break;
    }

    std::cout << "Integer stored in memory in byte order:\n";
    std::cout << "        " << CurrentEndianMsg << " processor [current]: ";

    for(i = 0; i < SizeOfIntegerValue; i++)https://stackoverflow.com/qhttps://stackoverflow.com/questions/280162/is-there-a-way-to-do-a-c-style-compile-time-assertion-to-determine-machines-e/54175491#54175491uestions/280162/is-there-a-way-to-do-a-c-style-compile-time-assertion-to-determine-machines-e/54175491#54175491
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n        " << OppositeEndianMsg << " processor  [simulated]: ";

    for(i = SizeOfIntegerValue - 1; i >= 0; i--)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n\n";
}


int main()
{
    uint16_t ValueUInt16a = 0x0001;
    uint16_t ValueUInt16b = 0x1234;
    uint32_t ValueUInt32a = 0x00000001;
    uint32_t ValueUInt32b = 0x12345678;
    uint64_t ValueUInt64a = 0x0000000000000001;
    uint64_t ValueUInt64b = 0x123456789ABCDEF0;

    std::cout << "Current processor endianness: ";

    switch (IsLittleEndian) {
        case 0: CurrentEndianMsg = "Big Endian";
                OppositeEndianMsg = "Little Endian";
                break;
        case 1: CurrentEndianMsg = "Little Endian";
                OppositeEndianMsg = "Big Endian";
                break;
    }

    std::cout << CurrentEndianMsg << "\n\n";

    PrintIntegerDetails(ValueUInt16a);
    PrintIntegerDetails(ValueUInt16b);
    PrintIntegerDetails(ValueUInt32a);
    PrintIntegerDetails(ValueUInt32b);
    PrintIntegerDetails(ValueUInt64a);
    PrintIntegerDetails(ValueUInt64b);

    return 0;
}

Voici la sortie de la démo sur ma machine:

Current processor endianness: Little Endian

Integer size (in bytes): 2
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x0001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00
        Big Endian processor  [simulated]: 00 01

Integer size (in bytes): 2
Integer value (Decinal): 4660
Integer value (Hexidecimal): 0x1234
Integer stored in memory in byte order:
        Little Endian processor [current]: 34 12
        Big Endian processor  [simulated]: 12 34

Integer size (in bytes): 4
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x00000001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00 00 00
        Big Endian processor  [simulated]: 00 00 00 01

Integer size (in bytes): 4
Integer value (Decinal): 305419896
Integer value (Hexidecimal): 0x12345678
Integer stored in memory in byte order:
        Little Endian processor [current]: 78 56 34 12
        Big Endian processor  [simulated]: 12 34 56 78

Integer size (in bytes): 8
Integer value (Decinal): 1
Integer value (Hexidecimal): 0x0000000000000001
Integer stored in memory in byte order:
        Little Endian processor [current]: 01 00 00 00 00 00 00 00
        Big Endian processor  [simulated]: 00 00 00 00 00 00 00 01

Integer size (in bytes): 8
Integer value (Decinal): 13117684467463790320
Integer value (Hexidecimal): 0x123456789ABCDEF0While the process
Integer stored in memory in byte order:
        Little Endian processor [current]: F0 DE BC 9A 78 56 34 12
        Big Endian processor  [simulated]: 12 34 56 78 9A BC DE F0

J'ai écrit cette démonstration avec la chaîne d'outils GNU C ++ dans Linux Mint et je n'ai pas les moyens de tester d'autres versions de C ++ comme Visual Studio ou la chaîne d'outils MingW. Par conséquent, je ne sais pas ce qu'il faut faire pour compiler cela. Je n'ai pas accès à Windows pour le moment.

Cependant, un de mes amis a testé le code avec MingW, 64 bits (x86_64-8.1.0-release-win32-seh-rt_v6-rev0) et des erreurs s’y sont glissées. Après quelques recherches, j'ai découvert que je devais ajouter la ligne #define __STDC_FORMAT_MACROS en haut du code pour qu'il soit compilé avec MingW.

Maintenant que nous pouvons voir visuellement comment une valeur 16 bits est stockée en mémoire, voyons comment l’utiliser à notre avantage pour déterminer l’endianité d’un processeur.

Pour vous aider un peu plus à visualiser la manière dont les valeurs 16 bits sont stockées en mémoire, examinons le tableau suivant:

16-Bit Value (Hex):  0x1234

Memory Offset:       [00] [01]
                     ---------
Memory Byte Values:  [34] [12]  <Little Endian>
                     [12] [34]  <Big Endian>

================================================

16-Bit Value (Hex):  0x0001

Memory Offset:       [00] [01]
                     ---------
Memory Byte Values:  [01] [00]  <Little Endian>
                     [00] [01]  <Big Endian>

Lorsque nous convertissons la valeur 16 bits 0x0001 en un caractère (8 bits) avec l'extrait de code char (0x0001) , le compilateur utilise le premier décalage en mémoire de la valeur 16 bits pour la nouvelle valeur. Voici un autre graphique qui montre ce qui se passe à la fois dans le fichier "Little Endian". et " Big Endian " processeurs:

Original 16-Bit Value: 0x0001

Stored in memory as: [01][00]  <-- Little Endian
                     [00][01]  <-- Big Endian

Truncate to char:    [01][xx]  <-- Little Endian
                     [01]      Final Result
                     [00][xx]  <-- Big Endian
                     [00]      Final Result

Comme vous pouvez le constater, nous pouvons facilement déterminer l’endianité d’un processeur.


MISE À JOUR:

Je ne parviens pas à tester la démo ci-dessus sur un "Big Endian". processeur, j'ai donc basé le code sur des informations trouvées sur le Web. Merci à M.M de m'avoir signalé l'évident.

J'ai mis à jour le code de démonstration (comme indiqué ci-dessous) pour tester correctement l'endianité ou un processeur.

#define __STDC_FORMAT_MACROS // Required for the MingW toolchain
#include <iostream>
#include <inttypes.h>

std::string CurrentEndianMsg;
std::string OppositeEndianMsg;

template <typename IntegerType>
void PrintIntegerDetails(IntegerType IntegerValue)
{
    uint16_t SizeOfIntegerValue = sizeof(IntegerValue);
    int8_t i;

    std::cout << "Integer size (in bytes): " << SizeOfIntegerValue << "\n";
    std::cout << "Integer value (Decimal): " << IntegerValue << "\n";
    std::cout << "Integer value (Hexidecimal): ";

    switch (SizeOfIntegerValue)
    {
        case 2: printf("0x%04X\n", (unsigned int) IntegerValue);
                break;
        case 4: printf("0x%08X\n", (unsigned int) IntegerValue);
                break;
        case 8: printf("0x%016" PRIX64 "\n", (uint64_t) IntegerValue);
                break;
    }

    std::cout << "Integer stored in memory in byte order:\n";
    std::cout << "        " << CurrentEndianMsg << " processor [current]: ";

    for(i = 0; i < SizeOfIntegerValue; i++)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n        " << OppositeEndianMsg << " processor  [simulated]: ";

    for(i = SizeOfIntegerValue - 1; i >= 0; i--)
    {
        printf("%02X ", (((unsigned char*) &IntegerValue)[i]));
    }

    std::cout << "\n\n";
}


int main()
{
    uint16_t ValueUInt16a = 0x0001;
    uint16_t ValueUInt16b = 0x1234;
    uint32_t ValueUInt32a = 0x00000001;
    uint32_t ValueUInt32b = 0x12345678;
    uint64_t ValueUInt64a = 0x0000000000000001;
    uint64_t ValueUInt64b = 0x123456789ABCDEF0;

    uint16_t EndianTestValue = 0x0001;
    uint8_t IsLittleEndian = ((unsigned char*) &EndianTestValue)[0];

    std::cout << "Current processor endianness: ";

    switch (IsLittleEndian) {
        case 0: CurrentEndianMsg = "Big Endian";
                OppositeEndianMsg = "Little Endian";
                break;
        case 1: CurrentEndianMsg = "Little Endian";
                OppositeEndianMsg = "Big Endian";
                break;
    }

    std::cout << CurrentEndianMsg << "\n\n";

    PrintIntegerDetails(ValueUInt16a);
    PrintIntegerDetails(ValueUInt16b);
    PrintIntegerDetails(ValueUInt32a);
    PrintIntegerDetails(ValueUInt32b);
    PrintIntegerDetails(ValueUInt64a);
    PrintIntegerDetails(ValueUInt64b);

    return 0;
}

Cette démonstration mise à jour crée une valeur 16 bits 0x0001 , puis lit le premier octet dans la mémoire des variables. Comme indiqué dans la sortie ci-dessus, sur le "Little Endian" processeurs, la valeur serait 0x01. Sur "Big Endian" processeurs, la valeur serait 0x00.

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