写/读对象时有没有办法处理mixins?我正在使用Boost序列化,但这是一个相当普遍的问题。假设我通过mixins附加了属性,如下所示:
struct Point {
double x,y;
};
template<class Base>
class MyMixin1 : public Base {
public:
double someProperty;
};
template<class Base>
class MyMixin2 : public Base {
public:
double otherProperty;
};
int main() {
typedef MyMixin2<MyMixin1<PointTypeA> > Mixed12;
Mixed12 mixed12;
serialize(mixed12, "someFile.txt");
Mixed12 mixed12Read = deserialize("someFile.txt");
return 0;
}
void serialize(Mixed12 object, string filename)
{
Archive archive(filename);
WriteIfAvailable(archive, object, someProperty);
WriteIfAvailable(archive, object, otherProperty);
}
template <typename TObject>
TObject deserialize(string filename)
{
// How does this function know which data is present and in which order?
Archive archive(filename);
TSomeProperty someProperty;
archive >> someProperty; // We aren't sure if serialize() wrote 'someProperty' first, second, or at all
AssignIfAvailable(object, someProperty, someProperty);
TOtherProperty otherProperty;
archive >> otherProperty; // We aren't sure if serialize() wrote 'otherProperty' first, second, or at all
AssignIfAvailable(object, otherProperty, otherProperty);
}
在serialize()
中,我使用SFINAE来编写存档可用的所有属性,但是在deserialize()
中,我如何知道编写属性的顺序以反序列化它们?我想在开头写一个“标题”,表示写入的属性和顺序,但是如果我有一个std::vector<Mixed12>
那个方法需要为每个点写入标题,这似乎过多了。我也明确地处理std::vector
情况(或者更一般地说,可能是碰巧包含Mixin12或Mixin12集合的MyClass)并为外部对象写一个标题,但是这个对象应该依赖于使用其内容的序列化功能,不知道这个头的存在。
最好的结果是我们可以将一种类型(例如Mixed12)写入文件,然后将其读入“兼容”对象(例如Mixed21)。
答案 0 :(得分:0)
请在下面找到一个完整的工作示例。文本存档用于便于检查 - 您可以看到向量中的输出中没有存储冗余数据。该解决方案基于boost::serialization
docs推荐的派生类序列化的最佳实践方法。第三部分演示了如何将基类指针(例如Point
)的向量序列化为派生类(例如Mixed12
和Mixed21
)。请注意,由于使用了类GUID,归档中仍然没有冗余(如果想节省空间,可以缩短它们)。您还可以看到指向同一对象的指针是如何重复存储的事实 - boost仅在以后存储索引。
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
#include <vector>
namespace ser=boost::serialization;
namespace arch=boost::archive;
using std::cout;
using std::endl;
struct Point {
double x,y;
template<class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & x;
ar & y;
}
virtual ~Point() {};
};
template<class Base>
class MyMixin1 : public Base {
friend class ser::access;
public:
double someProperty;
template<class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & ser::base_object<Base>(*this);
ar & someProperty;
}
virtual ~MyMixin1() {}
};
template<class Base>
class MyMixin2 : public Base {
public:
double otherProperty;
template<class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar & ser::base_object<Base>(*this);
ar & otherProperty;
}
virtual ~MyMixin2() {}
};
typedef MyMixin2<MyMixin1<Point> > Mixed12;
typedef MyMixin1<MyMixin2<Point> > Mixed21;
BOOST_CLASS_EXPORT_GUID(Mixed12, "Mixed12")
BOOST_CLASS_EXPORT_GUID(Mixed21, "Mixed21")
int main() {
Mixed12 mixed12;
Mixed21 mixed21;
mixed12.someProperty = 5;
mixed12.otherProperty = 10;
mixed12.x = 2;
mixed12.y = 3;
mixed21.someProperty = 6;
mixed21.otherProperty = 7;
mixed21.x = 9;
mixed21.y = 8;
cout << "Testing simple serialization..." << endl;
{
std::ofstream f("someFile.txt");
arch::text_oarchive ar(f);
ar << mixed12;
ar << mixed21;
}
cout << "OK" << endl;
cout << "Testing simple deserialization..." << endl;
{
Mixed12 mixed12_deser;
Mixed21 mixed21_deser;
std::ifstream f("someFile.txt");
arch::text_iarchive ar(f);
ar >> mixed12_deser;
ar >> mixed21_deser;
cout << mixed12_deser.someProperty << " " << mixed12_deser.otherProperty
<< " " << mixed12_deser.x << " " << mixed12_deser.y << endl;
assert(mixed12.someProperty == mixed12_deser.someProperty &&
mixed12.otherProperty == mixed12_deser.otherProperty &&
mixed12.x == mixed12_deser.x &&
mixed12.y == mixed12_deser.y);
assert(mixed21.someProperty == mixed21_deser.someProperty &&
mixed21.otherProperty == mixed21_deser.otherProperty &&
mixed21.x == mixed21_deser.x &&
mixed21.y == mixed21_deser.y);
}
cout << "OK" << endl;
cout << "Testing vector serialization..." << endl;
{
std::vector<Mixed12> v12;
std::vector<Mixed21> v21;
std::ofstream f("vectorFile.txt");
arch::text_oarchive ar(f);
for (int i = 0; i < 16; i++) v12.push_back(mixed12);
for (int i = 0; i < 16; i++) v21.push_back(mixed21);
ar << v12;
ar << v21;
}
cout << "OK" << endl;
cout << "Testing vector deserialization..." << endl;
{
std::vector<Mixed12> v12;
std::vector<Mixed21> v21;
std::ifstream f("vectorFile.txt");
arch::text_iarchive ar(f);
ar >> v12;
ar >> v21;
for (std::vector<Mixed12>::const_iterator it = v12.begin(); it != v12.end(); it++)
assert(mixed12.someProperty == it->someProperty &&
mixed12.otherProperty == it->otherProperty &&
mixed12.x == it->x &&
mixed12.y == it->y);
for (std::vector<Mixed21>::const_iterator it = v21.begin(); it != v21.end(); it++)
assert(mixed21.someProperty == it->someProperty &&
mixed21.otherProperty == it->otherProperty &&
mixed21.x == it->x &&
mixed21.y == it->y);
}
cout << "OK!" << endl;
cout << "Testing base class serialization..." << endl;
{
std::vector<Point*> v;
for (int i = 0; i < 16; i++)
if (i % 2 == 0)
v.push_back(&mixed12);
else
v.push_back(&mixed21);
std::ofstream f("basePtrFile.txt");
arch::text_oarchive ar(f);
ar << v;
}
cout << "OK" << endl;
cout << "Testing base class deserialization..." << endl;
{
std::vector<Point*> v;
std::ifstream f("basePtrFile.txt");
arch::text_iarchive ar(f);
ar >> v;
assert(v.size() == 16);
for (int i = 0; i < 16; i++)
if (i % 2 == 0) {
assert(dynamic_cast<Mixed12*>(v[i]) != 0);
assert(dynamic_cast<Mixed21*>(v[i]) == 0);
} else {
assert(dynamic_cast<Mixed21*>(v[i]) != 0);
assert(dynamic_cast<Mixed12*>(v[i]) == 0);
}
}
cout << "OK" << endl;
return 0;
}
答案 1 :(得分:0)
mixins不是问题。为每种类型创建serialize
,并在serialize
转发&amp;确保基地也能够编写数据。这些属性将被写入&amp;以确定的方式阅读,不需要额外的数据。对于您的示例代码:
#include "boost/serialization/serialization.hpp"
#include "boost/archive/text_iarchive.hpp"
#include "boost/archive/text_oarchive.hpp"
#include <fstream>
struct Point {
double x, y;
template< typename TArch >
void serialize(TArch& a_Archive, const unsigned int a_Version )
{
a_Archive & x & y;
}
};
template<class Base>
class MyMixin1 : public Base {
public:
template< typename TArch >
void serialize(TArch& a_Archive, const unsigned int a_Version )
{
a_Archive & someProperty;
boost::serialization::serialize(a_Archive, static_cast<Base&>(*this), a_Version);
}
double someProperty;
};
template<class Base>
class MyMixin2 : public Base {
public:
template< typename TArch >
void serialize(TArch& a_Archive, const unsigned int a_Version)
{
a_Archive & otherProperty;
boost::serialization::serialize(a_Archive, static_cast<Base&>(*this), a_Version);
}
double otherProperty;
};
int main() {
typedef MyMixin2<MyMixin1<Point> > Mixed12;
Mixed12 mixed12;
{
std::ofstream ifs("someFile.txt");
boost::archive::text_oarchive oArch(ifs);
oArch << mixed12;
}
{
std::ifstream ifs("someFile.txt");
boost::archive::text_iarchive iArch(ifs);
iArch >> mixed12;
}
return 0;
}
如果您要将指向基类型的指针序列化,则需要一个ID以确保在保存/加载期间调用最派生类型的serialize
。 boost::serialize
手册中有一个名为Pointers to Objects of Derived Classes
的部分。然而,Mixin与否并没有真正改变任何东西。
答案 2 :(得分:0)
这是使用自定义序列化程序的另一种可能性,它满足您编写Mixed12和读取Mixed21等的要求,并且仍然谨慎地使用布局规范(仅当新布局被序列化时,它被嵌入到文件中,否则使用索引)。有趣的是,您甚至可以为相同的字段名称使用不同类型的类,只要您反序列化为正确的类它将完美地工作,因为只需要知道字段的顺序。免责声明:这是一个概念证明,还有一些需要解决的问题(例如,如果一个字段没有被消费,布局应该包含字段长度以便可以跳过它等等 - 这只是你需要做的一些工作)。
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
using std::vector;
using std::string;
namespace ser=boost::serialization;
using boost::archive::text_iarchive;
using boost::archive::text_oarchive;
using std::ifstream;
using std::ofstream;
using std::cout;
using std::endl;
typedef vector<string> FieldNames;
struct Serializer {
ofstream f;
text_oarchive ar;
vector<vector<string> > layouts;
Serializer(const char *fname): ar(f), f(fname) {
}
template <class Class>
Serializer& operator<<(Class &obj) {
vector<string> names;
obj.fieldNames(names);
unsigned int i;
for (i = 0; i < layouts.size(); i++)
if(layouts[i] == names) break;
if (i == layouts.size()) {
layouts.push_back(names);
unsigned int id(0x80000000 | i);
ar << id;
ar << names;
// cout << "New layout: " << i << endl;
} else {
// cout << "Reusing layout: " << i << endl;
ar << i;
}
obj.serialize(ar, 0);
return *this;
}
};
struct Deserializer {
ifstream f;
text_iarchive ar;
vector<vector<string> > layouts;
Deserializer (const char *fname): ar(f), f(fname) {
}
template <class Class>
Deserializer& operator>>(Class &obj) {
unsigned int i;
ar >> i;
vector<string> names;
if (0x80000000 & i) {
ar >> names;
// cout << "Reading names..." << endl;
layouts.push_back(names);
} else {
names = layouts[i];
}
for (i = 0; i < names.size(); i++) {
// cout << "Calling deserialize for name:" << names[i] << endl;
obj.deserialize(ar, names[i]);
}
return *this;
}
};
struct Point {
int x, y;
void fieldNames(FieldNames &names) {
names.push_back("x");
names.push_back("y");
}
void serialize(text_oarchive &ar, unsigned int ver) {
ar << x;
ar << y;
}
void deserialize(text_iarchive &ar, string &name) {
// cout << "Point::deserialize() name:" << name << endl;
if (name == "x") ar >> x;
if (name == "y") ar >> y;
}
};
template<class Base>
struct Mixin1: public Base {
int a;
void fieldNames(FieldNames &names) {
Base::fieldNames(names);
names.push_back("a");
}
void serialize(text_oarchive &ar, unsigned int ver) {
Base::serialize(ar, ver);
ar << a;
}
void deserialize(text_iarchive &ar, string &name) {
Base::deserialize(ar, name);
if (name == "a") ar >> a;
}
};
template<class Base>
struct Mixin2: public Base {
int b;
void fieldNames(FieldNames &names) {
Base::fieldNames(names);
names.push_back("b");
}
void serialize(text_oarchive &ar, unsigned int ver) {
Base::serialize(ar, ver);
ar << b;
}
void deserialize(text_iarchive &ar, string &name) {
Base::deserialize(ar, name);
if (name == "b") ar >> b;
}
};
typedef Mixin1<Mixin2<Point> > Mixed12;
typedef Mixin2<Mixin1<Point> > Mixed21;
int main() {
cout << "Testing serialization" << endl;
{
Serializer ser("ser2_test.txt");
Mixed12 m12;
Mixed21 m21;
m12.x = 1; m12.y = 2; m12.a = 3; m12.b = 4;
m21.x = 5; m21.y = 6; m21.a = 7; m21.b = 8;
for (int i = 0; i < 100; i++) {
ser << m12;
ser << m21;
}
}
cout << "OK" << endl;
cout << "Testing deserialization to Mixed12" << endl;
{
Deserializer des("ser2_test.txt");
Mixed12 m12;
for (int i = 0; i < 100; i++) {
des >> m12;
// cout << "m12: " << m12.x << " " << m12.y << " " << m12.a << " " << m12.b << endl;
assert(m12.x == 1 && m12.y == 2 && m12.a == 3 && m12.b == 4);
des >> m12;
assert(m12.x == 5 && m12.y == 6 && m12.a == 7 && m12.b == 8);
}
}
cout << "OK" << endl;
cout << "Testing deserialization to Mixed21" << endl;
{
Deserializer des("ser2_test.txt");
Mixed21 m21;
for (int i = 0; i < 100; i++) {
des >> m21;
assert(m21.x == 1 && m21.y == 2 && m21.a == 3 && m21.b == 4);
des >> m21;
assert(m21.x == 5 && m21.y == 6 && m21.a == 7 && m21.b == 8);
}
}
cout << "OK" << endl;
}