有没有办法做一个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
。我认为这是因为如果你的执行环境,endian'nes是一个属性。但是,在编译时考虑static_assert。
我建议您查看新的 GNU gold ELF链接器的代码。其作者Ian Lance Taylor使用模板在编译时选择正确的字节序,以确保在运行时获得最佳性能。他明确地实例化了所有可能的endians,因此他仍然有模板定义和声明的单独编译(不是标题中的所有模板)。他的代码很棒。
此答案基于以下规范(这是为了清晰起见):
语言: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&视窗
以下两行代码可用于成功检测处理器的字节顺序:
const uint8_t IsLittleEndian = char (0x0001);
或
#define IsLittleEndian char (0x0001)
这两个神奇的声明宝石充分利用了处理器如何在内存中存储16位值。
在“Little Endian”上处理器,如Intel和AMD芯片组,16位值存储在 [低位/最低有效字节] [高位/最高有效字节]
方式中(括号表示内存中的一个字节) )。
在“Big Endian”上处理器,如PowerPC,Sun Sparc和IBM S / 390芯片组,16位值存储在 [高阶/最高有效字节] [低阶/最低有效字节]
方式中。
例如,当我们将一个16位(两个字节)的值存储到C ++ uint16_t
(在C ++ v11中定义的类型,以及之后的类型)时,假设 0x1234
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/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 ++工具链编写了这个演示,并且没有办法在其他版本的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>
当我们使用代码片段 char(0x0001)
将16位值 0x0001
转换为char(8位)时,编译器使用第一个内存偏移量新值的16位值。这是另一张图表,显示了在“Little Endian”和“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”上亲