您可以忘记类型,但显然您不能再阅读任何包含旧类型的档案。由于这正是您要尝试的,因此它会中断。
简化示例Live On Coliru
如果您的存档不包含该类型,您仍然会收到异常,因为您注册类型的方式:Live On Coliru 抛出“未注册的类”。
注册类型的方式意味着您必须始终匹配注册的顺序和数量。
导出类
相反,请考虑使用导出机制:https://www.boost.org/doc/libs/1_80_0/libs/serialization/doc/special.html#export
这是演示:Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
struct Base {
virtual ~Base() = default;
virtual void display(std::ostream& os) const = 0;
void serialize(auto&, unsigned) {}
};
struct MyType1 : public Base {
int i, j;
MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
};
struct MyType2 : public Base {
float a;
MyType2(float a_ = 0.f): a {a_} {}
void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
};
BOOST_CLASS_EXPORT(MyType1)
BOOST_CLASS_EXPORT(MyType2)
static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
int main() {
std::stringstream ss;
{
boost::archive::binary_oarchive oa{ss};
Base* foo1 = new MyType1{42, 12};
Base* foo2 = new MyType2{32.f};
oa << foo1 << foo2;
delete foo1;
delete foo2;
}
std::ofstream("output.bin", std::ios::binary) << ss.str();
{
boost::archive::binary_iarchive ia{ss};
Base* obj1 = nullptr;
Base* obj2 = nullptr;
ia >> obj1 >> obj2;
std::cout << *obj1 << "
";
std::cout << *obj2 << "
";
delete obj2;
delete obj1;
}
}
这负责唯一标识(使用限定类型名称)。因此,当您准备放弃对 old 类的旧档案的支持时,您可以省略它,并且很高兴:Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/serialization.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
struct Base {
virtual ~Base() = default;
virtual void display(std::ostream& os) const = 0;
void serialize(auto&, unsigned) {}
};
struct MyType1 : public Base {
int i, j;
MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
};
struct MyType2 : public Base {
float a;
MyType2(float a_ = 0.f): a {a_} {}
void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
};
static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
#ifdef OLD_WRITER
BOOST_CLASS_EXPORT(MyType1)
BOOST_CLASS_EXPORT(MyType2)
int main() {
std::ofstream ofs("output.bin", std::ios::binary);
boost::archive::binary_oarchive oa{ofs};
Base* foo2 = new MyType2{42.f};
oa << foo2;
delete foo2;
}
#else
// forgotten: MyType1
BOOST_CLASS_EXPORT(MyType2)
int main() {
std::ifstream ifs("output.bin", std::ios::binary);
boost::archive::binary_iarchive ia{ifs};
Base* obj2 = nullptr;
ia >> obj2;
std::cout << *obj2 << "
";
delete obj2;
}
#endif
和
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DOLD_WRITER -o old
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DNEW_READER -o new
./old; xxd output.bin; ./new
印刷
00000000: 1600 0000 0000 0000 7365 7269 616c 697a ........serializ
00000010: 6174 696f 6e3a 3a61 7263 6869 7665 1300 ation::archive..
00000020: 0408 0408 0100 0000 0000 0700 0000 0000 ................
00000030: 0000 4d79 5479 7065 3201 0000 0000 0000 ..MyType2.......
00000040: 0000 0000 0000 0000 0028 42 .........(B
MyType2{42}
高级:版本控制
你能够也使用显式寄存器类型,但您必须进行版本控制才能获得某种兼容性,而不是UB。
您可以将存档数据包装在一个进行注册并进行版本控制的类中:
Live On Coliru: V0
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <fstream>
#include <iostream>
#include <sstream>
struct Base {
virtual ~Base() = default;
virtual void display(std::ostream& os) const = 0;
void serialize(auto&, unsigned) {}
};
struct MyType1 : public Base {
int i, j;
MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
};
struct MyType2 : public Base {
float a;
MyType2(float a_ = 0.f): a {a_} {}
void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
};
static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
struct MyArchiveData {
std::vector<std::unique_ptr<Base>> data;
void serialize(auto& ar, unsigned version) {
switch (version) {
case 0: {
ar.template register_type<MyType1>();
ar.template register_type<MyType2>();
ar& data;
break;
}
default:
using E = boost::archive::archive_exception;
throw E(E::exception_code::unsupported_class_version);
}
}
};
int main() {
{
std::ofstream ofs("output.bin", std::ios::binary);
boost::archive::binary_oarchive oa{ofs};
MyArchiveData db;
db.data.emplace_back(new MyType1(42, 12));
db.data.emplace_back(new MyType2(32.f));
oa << db;
}
{
std::ifstream ifs("output.bin", std::ios::binary);
boost::archive::binary_iarchive ia{ifs};
MyArchiveData db;
ia >> db;
for (auto& el : db.data)
std::cout << *el << "
";
}
}
请注意我是如何借此机会摆脱原始指针的。
MyArchiveData V1 简介
我们声明新的类版本:
BOOST_CLASS_VERSION(MyArchiveData, 1)
并实现新的逻辑:
struct MyArchiveData {
std::vector<std::unique_ptr<Base>> data;
void serialize(auto& ar, unsigned version) {
switch (version) {
case 0: {
ar.template register_type<MyType1>();
ar.template register_type<MyType2>();
ar& data;
break;
}
#ifdef V1
case 1: {
// MyType1 forgotten!
ar.template register_type<MyType2>();
ar& data;
break;
}
#endif
default:
using E = boost::archive::archive_exception;
throw E(E::exception_code::unsupported_class_version);
}
}
};
查看结果Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/version.hpp>
#include <fstream>
#include <iomanip>
#include <iostream>
struct Base {
virtual ~Base() = default;
virtual void display(std::ostream& os) const = 0;
void serialize(auto&, unsigned) {}
};
struct MyType1 : public Base {
int i, j;
MyType1(int i_ = 0, int j_ = 0) : i{i_}, j{j_} {}
void serialize(auto& ar, unsigned) { ar& boost::serialization::base_object<Base>(*this) & i& j; }
void display(std::ostream& os) const override { os << "MyType1{" << i << ", " << j << "}"; }
};
struct MyType2 : public Base {
float a;
MyType2(float a_ = 0.f): a {a_} {}
void serialize(auto& ar, unsigned) { ar & boost::serialization::base_object<Base>(*this) & a; }
void display(std::ostream& os) const override { os << "MyType2{" << a << "}"; }
};
static inline std::ostream& operator<<(std::ostream& os, Base const& b) { return b.display(os), os; }
struct MyArchiveData {
std::vector<std::unique_ptr<Base>> data;
void serialize(auto& ar, unsigned version) {
switch (version) {
case 0: {
ar.template register_type<MyType1>();
ar.template register_type<MyType2>();
ar& data;
break;
}
#ifdef V1
case 1: {
// MyType1 forgotten!
ar.template register_type<MyType2>();
ar& data;
break;
}
#endif
default:
throw std::runtime_error("MyArchiveData: version not supported");
}
}
};
#ifndef V1
// default class version is 0
static constexpr bool is_V1 = false;
#else
BOOST_CLASS_VERSION(MyArchiveData, 1)
static constexpr bool is_V1 = true;
#endif
int main(int argc, char** argv) {
{
std::ofstream ofs(is_V1 ? "v1.bin" : "v0.bin", std::ios::binary);
boost::archive::binary_oarchive oa{ofs};
MyArchiveData db;
if (!is_V1)
db.data.emplace_back(new MyType1(42, 12));
db.data.emplace_back(new MyType2(32.f));
oa << db;
}
for (auto fname : std::vector(argv + 1, argv + argc)) {
std::cout << (is_V1?"V1":"V0") << " Reading " << std::quoted(fname) << std::endl;
std::ifstream ifs(fname, std::ios::binary);
boost::archive::binary_iarchive ia{ifs};
MyArchiveData db;
ia >> db;
for (auto& el : db.data)
std::cout << *el << std::endl;
}
}
测试
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DV0 -o v0
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization -DV1 -o v1
./v0 v0.bin
./v1 v0.bin v1.bin
# but this isn't going to work:
./v0 v1.bin
输出:
V0 Reading "v0.bin"
MyType1{42, 12}
MyType2{32}
V1 Reading "v0.bin"
MyType1{42, 12}
MyType2{32}
V1 Reading "v1.bin"
MyType2{32}
V0 Reading "v1.bin"
terminate called after throwing an instance of 'std::runtime_error'
what(): MyArchiveData: version not supported