假设在一个程序中,我得到了:
class Foo {
int x;
double y;
char z;
};
class Bar {
Foo f1;
int t;
Foo f2;
};
int main() {
Bar b;
bar.f1.z = 'h';
bar.f2.z = 'w';
... some crap setting value of b;
FILE *f = fopen("dump", "wb"); // c-style file
fwrite(&b, sizeof(Bar), 1, f);
}
假设在另一个程序中,我有:
int main() {
File *f = fopen("dump", "rb");
std::string Foo = "int x; double y; char z;";
std::string Bar = "Foo f1; int t; Foo f2;";
// now, given this is it possible to read out
// the value of bar.f1.z and bar.f2.z set earlier?
}
我要问的是: 鉴于我有一个类的类型,我能弄清楚C ++是如何解决它的吗?
答案 0 :(得分:4)
您需要研究“序列化”。有一个库, Boost Serialization ,人们一直在推荐。
FWIW,我建议不要在类,结构和联合上使用fwrite
或std::ostream::write
。允许编译器在成员之间插入填充,因此可能会写出垃圾。此外,指针不能很好地序列化。
要回答您的问题,为了确定从哪个结构加载数据,您需要某种 sentinel 来指示对象类型。这可以是从enum
到对象名称的任何内容。
同时调查Factory
设计模式。
答案 1 :(得分:3)
我不太确定你在问什么,所以我会跳跃......
如果您确实需要确定字段在结构中的位置,请使用offsetof。
请注意链接页面中的“POD”限制。这是一个C宏,由于兼容性原因包含在C ++中。我们应该使用成员指针代替这些天,虽然成员指针不能解决所有相同的问题。
有些编译器可以应对这种情况,因为它们已经用一个知道结构布局的内在函数替换了传统的offsetof宏,而没有尝试做虚构实例的技巧。即便如此,最好不要依赖它。
对于POD结构,offsetof是查找特定字段偏移的便捷方式,也是一种安全的方法,它确定实际偏移量,而不管平台应用的对齐方式。
对于字段的大小,您显然只使用sizeof。这只会留下特定于平台的问题 - 由于对齐而导致不同平台上的不同布局等,endianness等等; - )
修改强>
可能是一个愚蠢的问题,但是为什么不直接将文件中的数据直接转换到结构的实例中,基本上用fwrite执行的操作反过来呢?
您遇到与上述相同的可移植性问题,这意味着如果使用不同的选项,不同的编译器或不同的平台重新编译,您的代码可能无法读取自己的文件。但对于单平台应用程序,这种方式非常有效。
答案 2 :(得分:1)
您不能假设代表Bar的字节顺序。如果文件跨越系统或该程序使用不同的标志编译,那么您将以不同的顺序读取和写入。
我已经看到了解决这个问题的方法,但它可能仅适用于非常简单的类型。
我引用了raknet教程:
#pragma pack(push, 1)
struct structName
{
unsigned char typeId; // Your type here
// Your data here
};
#pragma pack(pop)
注意到#pragma pack(push,1)和#pragma pack(pop)?这些强制您的编译器(在本例中为VC ++)将结构打包为字节对齐。查看编译器文档以了解更多信息。
您想要序列化。
答案 3 :(得分:0)
对于您给出的示例,看起来您确实需要某种C解析器,它将使用您的类型声明来解析字符串。然后,您将能够以正确的方式解释从文件中读取的字节。
C中的结构按照声明的顺序列出成员。编译器可以根据特定于平台的对齐需求在成员之间插入填充。变量的大小也是特定于平台的。
答案 4 :(得分:0)
如果您可以控制该类,则可以使用成员指针。你肯定可以这样做。问题是你是否 ......
class Metadata
{
public:
virtual int getOffset() = 0;
};
template <typename THost, typename TField>
class TypedMetadata : Metadata
{
private:
TField (THost::*memberPointer_);
TypedMetadata(TField (THost::*memberPointer))
{
memberPointer_ = memberPointer;
}
public:
static Metadata* getInstance(TField (THost::*memberPointer))
{
return new TypedMetadata<THost, TField>(memberPointer);
}
virtual int getOffset()
{
THost* host = 0;
int result = (int)&(host->*memberPointer_);
return result;
}
};
template<typename THost, typename TField>
Metadata* getTypeMetadata(TField (THost::*memberPointer))
{
return TypedMetadata<THost, TField>::getInstance(memberPointer);
}
class Contained
{
char foo[47];
};
class Container
{
private:
int x;
int y;
Contained contained;
char c1;
char* z;
char c2;
public:
static Metadata** getMetadata()
{
Metadata** metadata = new Metadata*[6];
metadata[0] = getTypeMetadata(&Container::x);
metadata[1] = getTypeMetadata(&Container::y);
metadata[2] = getTypeMetadata(&Container::contained);
metadata[3] = getTypeMetadata(&Container::c1);
metadata[4] = getTypeMetadata(&Container::z);
metadata[5] = getTypeMetadata(&Container::c2);
return metadata;
}
};
int main(array<System::String ^> ^args)
{
Metadata** metadata = Container::getMetadata();
std::cout << metadata[0]->getOffset() << std::endl;
std::cout << metadata[1]->getOffset() << std::endl;
std::cout << metadata[2]->getOffset() << std::endl;
std::cout << metadata[3]->getOffset() << std::endl;
std::cout << metadata[4]->getOffset() << std::endl;
std::cout << metadata[5]->getOffset() << std::endl;
return 0;
}