我是当前为现有网络远程开发C 11库接口控制文档(ICD)中描述的控制接口。
该接口基于TCP/IPv4,并使用网络字节顺序(aka bigendian )。
要求:应开发库 cross-platform 。
注意:我想使用预处理器开发一个解决方案(AB)。
在对www进行了简短的研究之后,我发现了boost.endian解决与多字节数据类型的Endianess有关的问题。我的方法是如下:
- 通过(多)字节数据类型序列化到流到流STD :: BASIC_OSTREAM ::写,更精确地通过
os.write(reinterpret_cast<char const *>(&kData), sizeof(kData))
。 - 将
std::basic_ostream
转换为std::vector<std::uint8_t>
。 - 通过网络通过boost.asio发送
std::vector<std::uint8_t>
。
到目前为止一切都很好。一切似乎都按预期工作,解决方案应该是平台独立。
现在是棘手的部分:ICD描述了由多个组成的消息单词和一个单词由8位组成。一条消息可以包含多个字段和字段不必与字节调整,这意味着一个单词可以包含多个字段。
示例:请考虑以下消息格式(消息主体开始单词10):
Word | Bit(s) | Name
-----|--------|----------
10 | 0-2 | a
10 | 3 | b
10 | 4 | c
10 | 5 | d
10 | 6 | e
10 | 7 | RESERVED
11 | 16 | f
等等...
所以现在我需要一个解决方案才能建模并序列化基于位的接口。
我已经看过以下方法:
- 位字段
-
std::bitset
boost::dynamic_bitset
- 1不是跨平台(依赖编译器)。
- 2和3似乎可以与本机字节订单一起使用(即 Little Endian on仅我的计算机),因此使用
boost::dynamic_bitset
的以下示例不为我的场景工作:
代码:
// Using a arithmetic type from the `boost::endian` namespace does not work.
using Byte = std::uint8_t;
using BitSet = boost::dynamic_bitset<Byte>;
BitSet bitSetForA{3, 1};
BitSet bitSetForB{1};
// [...]
BitSet bitSetForF{16, 0x400}; // 1024
因此,上面示例中的1024始终序列化为00 04
我的机器上的04 00
。
我真的不知道解决问题的最务实的方法是什么。也许您可以指导我走向正确的方向。
总而言之,我确实需要一个配方来实现现有网络接口定义位与平台无关的字段相对于本机字节顺序库已编译的机器。
最近有人向我指出了一篇关于我汲取灵感的好文章。
std::bitset
具有to_ulong
方法,可用于返回Bitfield的整数表示(Endian Independent),以下代码将以正确的顺序打印您的输出:
#include <iostream>
#include <iomanip>
#include <bitset>
int main()
{
std::bitset<16> flags;
flags[10] = true;
unsigned long rawflags = flags.to_ulong();
std::cout << std::setfill('0') << std::setw(2) << std::hex
<< (rawflags & 0x0FF) // little byte in the beginning
<< std::setw(2)
<< ((rawflags>>8) & 0x0FF) // big byte in the end
<< std::endl;
}
请注意,在这种情况下,没有使用位字段的解决方案很容易起作用,因为钻头也将交换在小endian机器上!
例如。在这样的结构中:
struct bits {
unsigned char first_bit:1;
unsigned char rest:7;
};
union {
bits b;
unsigned char raw;
};
将b.fist_bit设置为1将导致1或128的原始值,具体取决于endianness!
hth