C++ 将任何数据类型序列化为向量<;uint8_t>;-使用reinterpret_cast?
我在搜索中没有发现任何直接相关的内容,因此如果这是重复的,请原谅 我要做的是通过网络连接序列化数据。我的方法是将需要传输的所有内容转换为C++ 将任何数据类型序列化为向量<;uint8_t>;-使用reinterpret_cast?,c++,templates,serialization,network-programming,C++,Templates,Serialization,Network Programming,我在搜索中没有发现任何直接相关的内容,因此如果这是重复的,请原谅 我要做的是通过网络连接序列化数据。我的方法是将需要传输的所有内容转换为std::vector,并在接收端将数据解包为适当的变量。我的方法如下: template <typename T> inline void pack (std::vector< uint8_t >& dst, T& data) { uint8_t * src = static_cast < uint8_t*
std::vector
,并在接收端将数据解包为适当的变量。我的方法如下:
template <typename T>
inline void pack (std::vector< uint8_t >& dst, T& data) {
uint8_t * src = static_cast < uint8_t* >(static_cast < void * >(&data));
dst.insert (dst.end (), src, src + sizeof (T));
}
template <typename T>
inline void unpack (vector <uint8_t >& src, int index, T& data) {
copy (&src[index], &src[index + sizeof (T)], &data);
}
模板
内嵌无效包(标准::向量&dst、t和数据){
uint8_t*src=静态强制转换(静态强制转换(&data));
dst.insert(dst.end(),src,src+sizeof(T));
}
模板
内联void解包(向量和src、整数索引、T和数据){
复制(&src[索引],&src[索引+大小(T)],&data);
}
我现在用的是
vector< uint8_t > buffer;
uint32_t foo = 103, bar = 443;
pack (buff, foo);
pack (buff, bar);
// And on the receive side
uint32_t a = 0, b = 0;
size_t offset = 0;
unpack (buffer, offset, a);
offset += sizeof (a);
unpack (buffer, offset, b);
vector缓冲区;
uint32_t foo=103,bar=443;
包(buff,foo);
包(buff,bar);
//在接收端
uint32_t a=0,b=0;
尺寸偏差=0;
拆包(缓冲、偏移、a);
偏移量+=尺寸f(a);
拆包(缓冲区、偏移量、b);
我关心的是
uint8_t*src=static_cast(static_cast(&data))代码>
行(我理解为与重新解释cast
相同)。有没有更好的方法在没有双重施法的情况下实现这一点
我天真的方法是只使用static\u cast(&data)
,但失败了。我认为重新解释演员阵容是不好的。因此,如果可能的话,我希望避免它(或我目前的构造)
当然,始终存在uint8\u t*src=(uint8\u t*)(&data)
建议?我的建议是忽略所有告诉你“重新解释演员阵容”不好的人。他们告诉你这是不好的,因为取一种类型的内存映射并假装它是另一种类型通常不是一个好的做法。但在本例中,这正是您想要做的,因为您的全部目的是将内存映射作为一系列字节进行传输 它远比使用双精度静态转换要好得多,因为它充分详细说明了您正在使用一种类型并故意假装它是其他类型的事实。这种情况正是重新解释cast的用意所在,回避使用void指针中介只会模糊你的意思,没有任何好处
另外,我相信您知道这一点,但请注意T中的指针。这里您没有进行任何实际编码,您只是将数据的原始表示形式从内存复制到字节数组中,然后通过网络发送出去。那是行不通的。下面是一个简单的例子,说明原因:
struct A {
int a;
};
struct B {
A* p_a;
}
当您使用方法通过网络发送B
时会发生什么情况?收件人收到您计算机上某个对象的地址p_a
,但该对象不在其计算机上。即使你也给他们发送了一个对象,它也不会在同一个地址。如果只发送原始的B
struct,这是不可能的。这甚至没有考虑到更微妙的问题,比如endianness和浮点表示,它们会影响像int
和double
这样的简单类型的传输
从根本上讲,您现在所做的与仅强制转换到uint8\u t*
没有什么不同,因为它是否会起作用(除了最琐碎的情况外,它不会起作用)
您需要做的是设计一种序列化方法。序列化意味着解决这类问题的任何方法:如何将内存中的对象以某种形式输出到网络上,以便它们可以在另一端进行有意义的重构。这是一个棘手的问题,但它是一个众所周知且反复解决的问题。这里有一个很好的阅读起点:您可以利用任何指针都可以隐式转换到void*
的事实,从而摆脱一次转换。此外,您可能需要添加一些const
:
//Beware, brain-compiled code ahead!
template <typename T>
inline void encode (std::vector< uint8_t >& dst, const T& data)
{
const void* pdata = &data;
uint8_t* src = static_cast<uint8_t*>(pdata);
dst.insert(dst.end(), src, src + sizeof(T));
}
//当心,brain提前编译了代码!
模板
内联无效编码(std::vector和dst、常数和数据)
{
常量void*pdata=&data;
uint8_t*src=静态转换(pdata);
insert(dst.end(),src,src+sizeof(T));
}
您可能希望为T
添加一个编译时检查,它是一个POD、无struct、
和无指针
然而,在字节级别解释某些对象的内存永远不会被保存。如果你必须这样做,那就用一个漂亮的包装纸来包装(就像你已经做的那样),然后克服它。当您移植到不同的平台/编译器时,请注意以下事项 你的情况正是重新解释演员的目的,它比双重的静态演员更简单,并且清楚地记录了你在做什么
为了安全起见,您应该使用无符号字符
而不是uint8\u t
:
- 执行
重新解释\u cast
到无符号字符*
,然后取消对结果指针的引用是安全和可移植的,并且是[basic.lval]§3.10/10明确允许的
- 对
std::uint8\u t*
执行重新解释\u cast
,然后取消对结果指针的引用违反了严格的别名规则,如果std::uint8\u t
实现为扩展无符号整数类型,则这是未定义的行为
如果存在,uint8\u t
必须始终具有与无符号字符相同的宽度。但是,它不必是同一类型;它可能是一个独特的扩展整数类型。它也不必具有与无符号字符
相同的表示形式(请参阅)
(这并不完全是假设性的:将[u]int8\t
作为一种特殊的扩展整数类型允许进行一些积极的优化)
如果确实需要uint8\t
,可以添加:
static_assert(std::is_same<std::uint8_t, unsigned char>::value,
"We require std::uint8_t to be implemented as unsigned char");
静态断言(std::is)_