기계의 엔디안을 결정하기 위해 C++ 스타일의 컴파일 타임 어설션을 수행할 수 있는 방법이 있습니까?

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

문제

템플릿화된 낮은 수준의 직렬화 코드가 있고 컴파일 타임에 시스템의 엔디안을 분명히 알아야 합니다(템플릿은 시스템의 엔디안을 기반으로 전문화되기 때문입니다).

지금은 일부 플랫폼 정의가 포함된 헤더가 있지만 어떻게든 일부 템플릿 테스트(예: static_assert 또는 Boost_if)를 사용하여 엔디안에 대한 어설션을 만들고 싶습니다.이유는 내 코드가 다양한 컴퓨터, 많은 전문 공급업체 및 아마도 2008년에는 존재하지 않는 장치에서 컴파일되고 실행되어야 하기 때문입니다. 따라서 해당 헤더 연도에 무엇이 필요할지 실제로 추측할 수 없습니다. 도로 아래로.그리고 코드 베이스의 예상 수명은 약 10년입니다.그래서 나는 코드를 영원히 따라갈 수 없습니다.

이것이 내 상황을 명확하게 해주기를 바랍니다.

그렇다면 공급업체별 정의에 의존하지 않고 엔디안을 확인할 수 있는 컴파일 타임 테스트를 아는 사람이 있습니까?

도움이 되었습니까?

해결책

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

다른 팁

컴파일 시간에 이것을 할 수있는 휴대용 방법은 없으며, 가장 좋은 방법은 아마도 사용하는 것입니다. 후원 엔디 언 매크로 또는 그들이 사용하는 방법을 모방합니다.

흠, 그것은 흥미로운 질문입니다. 내 베팅은 이것이 불가능하다는 것입니다. 매크로를 계속 사용하고 함께 가야한다고 생각합니다. BOOST_STATIC_ASSERT(!BIG_ENDIAN);, 또는 static_assert C ++ 0x에서. 내가 생각하는 이유는 Endian'nes가 귀하의 실행 환경이라면 재산이기 때문입니다. 그러나 STATIC_ASSERT는 컴파일 시간에 고려됩니다.

나는 당신이 새로운 코드를 조사하는 것이 좋습니다 GNU 금 엘프 링커. 저자 인 이안 랜스 테일러 (Ian Lance Taylor)는 템플릿을 사용하여 컴파일 타임에 올바른 엔지니어를 선택하여 실행 시간에 최적의 성능을 보장했습니다. 그는 가능한 모든 엔디언을 설명하여 템플릿 정의 및 선언에 대한 별도의 편집 (헤더의 모든 템플릿이 아님)을 가지고 있습니다. 그의 코드는 훌륭합니다.

이 답변은 다음 사양을 기반으로 합니다(명확성을 위한 설명입니다).

언어:C++ v17, 64비트
컴파일러:g++ v8(GNU 컴파일러 컬렉션 https://www.gnu.org/software/gcc/) 및 MingW 8.1.0 툴체인(https://sourceforge.net/projects/mingw-w64/files/)
OS:리눅스 민트 & 윈도우

다음 두 줄의 코드를 사용하여 프로세서의 엔디안을 성공적으로 감지할 수 있습니다.

const uint8_t IsLittleEndian = char (0x0001);

또는

#define IsLittleEndian char (0x0001)

이 두 개의 작은 마법 문장 보석은 프로세서가 메모리에 16비트 값을 저장하는 방법을 활용합니다.

Intel 및 AMD 칩셋과 같은 "Little Endian" 프로세서에서는 16비트 값이 [low order/least significant byte][high order/most significant byte] 패션(괄호는 메모리의 바이트를 나타냄).

PowerPC, Sun Sparc 및 IBM S/390 칩셋과 같은 "Big Endian" 프로세서에서는 16비트 값이 [high order/most significant byte][low order/least significant byte] 패션.

예를 들어 16비트(2바이트) 값을 저장할 때 다음과 같이 가정해 보겠습니다. 0x1234, C++로 uint16_t (C++ v11 이상에 정의된 유형 https://en.cppreference.com/w/cpp/types/integer) "Little Endian" 프로세서의 크기 변수를 확인한 다음 값이 저장된 메모리 블록을 들여다보면 바이트 시퀀스를 찾을 수 있습니다. [34][12].

"빅 엔디안 프로세서"에서는 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/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;
}

내 컴퓨터의 데모 결과는 다음과 같습니다.

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

저는 Linux Mint의 GNU C++ 툴체인을 사용하여 이 데모를 작성했지만 Visual Studio나 MingW 툴체인과 같은 다른 C++ 버전에서 테스트할 수단이 없습니다. 따라서 이 데모를 컴파일하는 데 무엇이 필요한지 모르겠습니다. 지금 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

보시다시피 프로세서의 엔디안을 쉽게 확인할 수 있습니다.


업데이트:

"Big Endian" 프로세서에서는 위의 데모를 테스트할 수 없으므로 웹에서 찾은 정보를 기반으로 코드를 작성했습니다.나에게 명백한 점을 지적해 준 M.M에게 감사드립니다.

엔디안 또는 프로세서를 올바르게 테스트하기 위해 데모 코드(아래 참조)를 업데이트했습니다.

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

이 업데이트된 데모는 16비트 값을 생성합니다. 0x0001 그런 다음 변수 메모리의 첫 번째 바이트를 읽습니다.위에 표시된 출력에서 ​​볼 수 있듯이 "Little Endian" 프로세서에서 값은 0x01입니다."Big Endian" 프로세서에서는 값이 0x00입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top