我正在尝试实现反序列化,其中到字段/成员的映射仅在运行时才知道(它很复杂)。无论如何,我想做的是如下所示:
Class A
{
public:
int a; // index 0
float b; // index 1
char c; // index 2
}
然后我有两个数组,一个带有字段的索引,另一个带有指示类型的东西。然后我想遍历数组并从字节流写入字段。
对不起,糟糕的描述,但我只是不知道如何在代码中实现它。任何想法将不胜感激谢谢!
我正在尝试实现反序列化,其中到字段/成员的映射仅在运行时才知道(它很复杂)。无论如何,我想做的是如下所示:
Class A
{
public:
int a; // index 0
float b; // index 1
char c; // index 2
}
然后我有两个数组,一个带有字段的索引,另一个带有指示类型的东西。然后我想遍历数组并从字节流写入字段。
对不起,糟糕的描述,但我只是不知道如何在代码中实现它。任何想法将不胜感激谢谢!
有很多问题和决定需要。最简单的是,您可以为A
每个字段保留一个偏移量,您可以打开类型并通过指向该字段的指针进行设置。例如 - 假设int16_t
输入流中有一个编码字段编号,不努力使用static_cast<>
等,这样做更好一点,并假设一个 0 字段编号输入终止符......
A a;
char* pa = (char*)&a;
char* p_v = (char*)&input_buffer;
...
while ((field_num = *(int16_t)p_v) && (p_v += sizeof(int16_t)))
switch (type[field_num])
{
case Int32:
*(int32_t*)(p_a + offset[field_num]) = *(int32_t*)(p_v);
p_v += sizeof(int32_t);
break;
...
}
您可能需要考虑使用例如ntohl()
等来处理字节顺序转换。
我想不出一种语言结构能够在运行时为您提供一个给定索引的字段地址。如果您可以让“类型”数组实际包含字段大小,您将能够执行以下操作:
istream &in = <get it somehow>;
size_t *field_size = <get it somehow>;
size_t num_of_fields = <get it somehow>;
A a;
char *ptr = reinterpret_cast<char *>(&a);
for (int i = 0; i < num_of_fields; i++)
{
in.read(ptr, field_size[i]);
ptr += field_size[i];
}
请注意,如果您的类很简单并且没有任何虚函数成员(或此类类的继承),这将是正确的。如果是这种情况,最好包含一个虚拟成员来获取类中字段开始的字节偏移量:
class A
{
int __dummy; /* must be the first data member in the class */
...
<rest of your class definition here>
};
现在更改ptr的初始化如下:
ptr = reinterpret_cast<char *>(&a) + offsetof(A, __dummy);
此代码的另一个隐含假设是,运行此代码的机器和接收序列化数据的机器的机器字节顺序相同。如果不是,那么您将需要转换从流中读取的数据的字节顺序。这种转换当然取决于类型,但每个字段可以有另一个转换函数数组。
是的,您可以,但是在执行此操作时需要注意两件事。
首先确保你开始写作,(const char*)&A.a
因为所有编译器都会在对象的开头附加一些与你无关的东西(例如,visualc 将 vtable 放在那里),如果你写的话,你就不会写你认为的东西从对象的地址开始。
其次,您可能希望#pragma pack(1)
在声明任何需要写入磁盘的类之前执行 a,因为编译器通常会对齐类成员以提高 DMA 传输效率,您最终也可能会遇到问题。
在它的动态部分,如果为你想要的每个字段组合创建一个类定义是可以接受的,那么可以这样做,否则你最好在你的类中包含一个哈希表并序列化/反序列化它的内容通过将键值对写入文件
让编译器来做:
写一个operator>>
函数。