기계의 엔디안을 결정하기 위해 C++ 스타일의 컴파일 타임 어설션을 수행할 수 있는 방법이 있습니까?
-
07-07-2019 - |
문제
템플릿화된 낮은 수준의 직렬화 코드가 있고 컴파일 타임에 시스템의 엔디안을 분명히 알아야 합니다(템플릿은 시스템의 엔디안을 기반으로 전문화되기 때문입니다).
지금은 일부 플랫폼 정의가 포함된 헤더가 있지만 어떻게든 일부 템플릿 테스트(예: 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입니다.