Есть ли способ сделать утверждение времени компиляции в стиле С++ для определения конечности машины?

У меня есть код низкоуровневой сериализации, который является шаблоном, и мне нужно знать системную сущность в compiletime, очевидно (потому что шаблоны специализируются на основе системной сущности).

Прямо сейчас у меня есть заголовок с некоторыми определениями платформы, но я бы предпочел бы кое-что сделать утверждения о endianness с некоторым шаблоном теста (например, static_assert или boost_if). Причина, по которой мой код должен быть скомпилирован и запущен на широком спектре машин, многих специализированных поставщиков и, вероятно, устройств, которые не существуют в 2008 году, поэтому я не могу догадаться, что может понадобиться для этого года заголовка по дороге. А так как кодовая база имеет ожидаемый срок службы около 10 лет. Поэтому я не могу следовать коду навсегда.

Надеюсь, это делает мою ситуацию ясной.

Знает ли кто-нибудь о тестировании времени компиляции, который может определять endianness, не полагаясь на специфику поставщика?

Ответ 1

Если вы используете autoconf, вы можете использовать макрос AC_C_BIGENDIAN, который довольно гарантированно работает (настройка по умолчанию WORDS_BIGENDIAN)

поочередно, вы можете попробовать что-то вроде следующего (взятого из autoconf), чтобы получить тест, который, вероятно, будет оптимизирован (GCC, по крайней мере, удаляет другую ветку)

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;
}

Ответ 2

Нет никакого переносного способа сделать это во время компиляции, лучше всего использовать Boost endian macros или эмулировать методы, которые они используют.

Ответ 3

Хм, это интересный вопрос. Моя ставка заключается в том, что это невозможно. Я думаю, вам нужно продолжать использовать макросы и перейти с BOOST_STATIC_ASSERT(!BIG_ENDIAN); или static_assert в С++ 0x. Причина, по которой я думаю, это потому, что endian'nes - это свойство, если ваша среда исполнения. Однако static_assert учитывается во время компиляции.

Я предлагаю вам заглянуть в код нового GNU gold ELF-линкера. Ян Лэнс Тейлор (Ian Lance Taylor), ее автор, использовал шаблоны для выбора правильной сущности во время компиляции, чтобы обеспечить оптимальную производительность во время выполнения. Он явно создает все возможные континенты, так что у него все еще есть отдельная компиляция (не все шаблоны в заголовках) определения шаблона и объявления. Его код отличный.

Ответ 4

Этот ответ основан на следующих спецификациях (это для ясности):

Язык: C++ v17, 64-битный
Компиляторы: g++ v8 (коллекция компиляторов GNU https://www.gnu.org/software/gcc/) и набор инструментов MingW 8.1.0 (https://sourceforge.net/projects/mingw-w64/files/)
ОС: Linux Mint & Windows

Следующие две строки кода могут быть использованы для успешного определения порядка процессора:

const uint8_t IsLittleEndian = char (0x0001);

или же

#define IsLittleEndian char (0x0001)

Эти две маленькие магические жемчужины утверждения используют то, как процессор хранит 16-битное значение в памяти.

На процессоре "Little Endian", таком как наборы микросхем Intel и AMD, 16-битное значение хранится в виде [low order/least significant byte][high order/most significant byte] младший/младший [low order/least significant byte][high order/most significant byte] (скобки представляют байт в памяти),

На процессоре "Big Endian", таком как наборы микросхем PowerPC, Sun Sparc и IBM S/390, 16-битное значение сохраняется в виде [high order/most significant byte][low order/least significant byte].

Например, когда мы сохраняем 16-битное (двухбайтовое) значение, скажем, 0x1234, в C++ uint16_t (тип, определенный в C++ v11, а затем https://en.cppreference.com/w/cpp Переменная размера /types/integer) на процессоре "Little Endian", затем заглянуть в блок памяти, в котором хранится значение, вы найдете последовательность байтов, [34][12].

На "процессоре Big Endian" значение 0x1234 сохраняется как [12][34].

Вот небольшая демонстрация, чтобы продемонстрировать, как целочисленные переменные различного размера C++ хранятся в памяти на процессорах с младшим и старшим порядком байтов:

#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/info/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;
}

Вот вывод демо на моей машине:

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): 0x123456789ABCDEF0
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

Я написал эту демонстрацию с помощью набора инструментов GNU C++ в Linux Mint, и у меня нет средств для тестирования в других вариантах C++, таких как Visual Studio или набор инструментов MingW, поэтому я не знаю, что требуется для компиляции в них нет доступа к Windows на данный момент.

Однако мой друг протестировал код с MingW, 64-битный (x86_64-8.1.0-release-win32-seh-rt_v6-rev0), и у него были ошибки. После небольшого исследования я обнаружил, что мне нужно добавить строку #define __STDC_FORMAT_MACROS в верхней части кода для его компиляции с MingW.

Теперь, когда мы можем наглядно увидеть, как 16-битное значение хранится в памяти, давайте посмотрим, как мы можем использовать это в наших интересах, чтобы определить порядковый номер процессора.

Чтобы немного больше помочь в визуализации способа хранения 16-битных значений в памяти, давайте взглянем на следующую таблицу:

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>

Когда мы конвертируем 16-битное значение 0x0001 в символ (8-бит) с помощью фрагмента char (0x0001), компилятор использует первое смещение памяти 16-битного значения для нового значения. Вот еще одна диаграмма, которая показывает, что происходит на процессорах "Little Endian" и "Big Endian":

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

Как видите, мы можем легко определить порядковый номер процессора.