Boost 序列化加载失败并抛出异常
我很长时间以来一直在努力让这项工作成功。
在我的项目中,有 6 个类正在使用 boost 中的确切教程通过实现模板函数序列化进行序列化。
这些类是:State、guState、Policy、Action、Param、Vec3D。
当我序列化并保存它们时,它工作正常。我确实得到了一个文本文件,其中包含各种数字和字符串。
没有抱怨,没有警告,没有抛出异常。唯一的情况是,如果我尝试序列化类的指针成员,空洞进程就会变成僵尸。所以我不尝试这样做,并保存作品。
然而,当我尝试加载时,我得到:
抛出实例后调用终止 “boost::archive::archive_exception”what():流错误
现在有趣的部分是我序列化了两个 boost::ptr_vector,一个由状态指针组成,另一个由策略指针组成。
状态向量,我保存并加载没有问题。 策略向量,我可以保存,但是当我尝试加载时,我得到了异常。
此外,在阅读了 boost 教程后,我的印象是,为了加载,除了序列化函数之外,我不需要任何其他东西。
然而,当我尝试加载时,boost 序列化会抱怨找不到默认构造函数,例如 State()、Policy() 等(我在每个类中实现了自己的构造函数)。
阅读本教程后,我实现了一个默认构造函数它什么也不做,因此升压序列化可以工作。确实编译通过了,我得到了上面提到的结果。
我尝试沿着我的老问题中看到的一条非常复杂的道路走下去,我尝试分离和实现 save_construct_data和 load_construct_data,但我发现这太侵入性了,我再次得到了如上所述的确切错误。
有人可以帮我解释一下加载是如何工作的吗?默认构造函数有什么用?或者至少给我指出一个可能有用的链接。我已经浏览了boost中的手册,他们并没有解释太多关于重建的内容。
谢谢。
编辑(添加了一些片段)
class State
{
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version);
protected:
std::map<std::string,float> positions;
float reward;
std::size_t hash_value;
bool exists(const Action* A);
bool operator== (State const& S);
std::size_t hash();
void clean_noise();
State(){}; // NOTE: This is used only by serializer, without it, code won't compile
public:
enum position { standing, on_chest, on_back, on_left, on_right, getting_up };
position current_position;
Policy *my_policy;
Vec3D gps;
boost::ptr_vector<Action> actions;
State(ACTION_MODE &m);
~State();
bool operator== (State const* S);
bool operator< (State const* S) const ;
const float& getR() const;
bool addAction(Action *A);
Action* findAction(const Action *A);
boost::ptr_vector<Action>& getAllActions();
void printState();
virtual bool isTerm();
};
template <class Archive>
void State::serialize(Archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(positions);
ar & BOOST_SERIALIZATION_NVP(gps);
ar & BOOST_SERIALIZATION_NVP(current_position);
ar & BOOST_SERIALIZATION_NVP(reward);
ar & BOOST_SERIALIZATION_NVP(hash_value);
ar & BOOST_SERIALIZATION_NVP(actions);
ar & BOOST_SERIALIZATION_NVP(my_policy);
}
从 State 继承的其他类也有其序列化功能,使用:
ar & boost::serialization::base_object<State>(*this);
类策略:
class Policy
{
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version);
Policy() {}; // NOTE: Again same as with state, used only by serialize load
protected:
float QValue;
State *state;
public:
//! Base class constructor
Policy(State *s);
...
};
template <class Archive>
void Policy::serialize(Archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(action);
ar & BOOST_SERIALIZATION_NVP(state);
ar & BOOST_SERIALIZATION_NVP(QValue);
ar & BOOST_SERIALIZATION_NVP(r);
}
正如您所看到的,这是两个主要类,由于重建依赖关系,其他类也被序列化(Action 类、Param 类)等)
主类:
template <class S, class P> class Task
{
protected:
...
//! Container of states of type S (template parameter)
boost::ptr_vector<S> states;
//! Container of policies of type P (template parameter)
boost::ptr_vector<P> policies;
...
public:
Task(Agent &a, ACTION_MODE &m);
...
void save_to_file();
void load_from_file(std::string filename);
};
template <class S, class P>
void Task<S,P>::save_to_file()
{
std::string output = ramdisk+"serialized";
char *file = (char*)output.c_str();
std::ofstream ofs(file);
assert(ofs.good());
boost::archive::text_oarchive oa(ofs);
oa << states;
oa << policies;
ofs.close();
}
template <class S, class P>
void Task<S,P>::load_from_file(std::string filename)
{
char *file = (char*)output.c_str();
std::cout << file << std::endl;
std::ifstream ifs(file);
boost::archive::text_iarchive ia(ifs);
ia >> states;
ia >> policies;
ifs.close();
}
有效包含两个保存状态和策略的 boost::ptr_vector。 状态的保存和加载没有问题。
加载策略时会出现问题。保存它们似乎不会产生问题(但我可能又错了)。
在不使用策略和使用策略的情况下测试保存/加载后,问题似乎与策略重建有关。
请注意仅由序列化使用的默认构造函数,如果没有它,代码将无法编译。
编辑#2:使用 valgrind 和 memcheck 运行应用程序后,它报告存在指针内存泄漏。然而,由于我不擅长使用 valgrind 进行调试,我无法真正判断泄漏发生在哪里,或者它是否与我的序列化相关(我认为是)。
I have been trying to make this work for a long time now.
In my project there are 6 classes that are being serialized using the exact tutorial from boost, by implementing the template function serialize.
Those classes are: State, guState, Policy, Action, Param, Vec3D.
When I serialize and save them, it works fine. I get indeed a text file, with various numbers and strings in it.
No complains, no warnings, no exceptions thrown. The only case is that if I try to serialize a pointer member of a class, the hole process becomes a zombie. So I do not try doing so, and saving works.
When however I try loading, I get:
terminate called after throwing an instance of
'boost::archive::archive_exception' what(): stream error
Now the interesting part is that I serialize two boost::ptr_vectors, one which consists of State pointers and one which consists of Policy pointers.
The state vector, I have saved and loaded without a problem.
The policy vector, I can save, but when I try loading i get the exception.
Furthermore, after reading the boost tutorials, I was under the impression that In order to load I did not need anything else other than the serialize function.
However when I tried loading, boost serialization would complain for not finding a default constructor such as State(), Policy(), etc (I implement my own constructors in each class ).
After reading this tutorial here I implemented a default constructor which does nothing, so that boost-serialization would work. Indeed it did compile, and I got the results mentioned above.
I have tried going down a much complicated road seen in my old question here where I tried seperating and implementing save_construct_data and load_construct_data, but I found this too instrusive, again I got the exact error as above.
Can someone please help me, explain how the loading works, what is the deal with the default constructors ? Or at least point me to a link that might be helpfull. I have gone through the manuals in boost, and they do not explain much about reconstruction.
Thank you.
Edit (Added a few snippets)
class State
{
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version);
protected:
std::map<std::string,float> positions;
float reward;
std::size_t hash_value;
bool exists(const Action* A);
bool operator== (State const& S);
std::size_t hash();
void clean_noise();
State(){}; // NOTE: This is used only by serializer, without it, code won't compile
public:
enum position { standing, on_chest, on_back, on_left, on_right, getting_up };
position current_position;
Policy *my_policy;
Vec3D gps;
boost::ptr_vector<Action> actions;
State(ACTION_MODE &m);
~State();
bool operator== (State const* S);
bool operator< (State const* S) const ;
const float& getR() const;
bool addAction(Action *A);
Action* findAction(const Action *A);
boost::ptr_vector<Action>& getAllActions();
void printState();
virtual bool isTerm();
};
template <class Archive>
void State::serialize(Archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(positions);
ar & BOOST_SERIALIZATION_NVP(gps);
ar & BOOST_SERIALIZATION_NVP(current_position);
ar & BOOST_SERIALIZATION_NVP(reward);
ar & BOOST_SERIALIZATION_NVP(hash_value);
ar & BOOST_SERIALIZATION_NVP(actions);
ar & BOOST_SERIALIZATION_NVP(my_policy);
}
Other Classes inheriting from State, have also their serialize functions, using:
ar & boost::serialization::base_object<State>(*this);
Class Policy:
class Policy
{
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version);
Policy() {}; // NOTE: Again same as with state, used only by serialize load
protected:
float QValue;
State *state;
public:
//! Base class constructor
Policy(State *s);
...
};
template <class Archive>
void Policy::serialize(Archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(action);
ar & BOOST_SERIALIZATION_NVP(state);
ar & BOOST_SERIALIZATION_NVP(QValue);
ar & BOOST_SERIALIZATION_NVP(r);
}
As you can see those are the two main classes, Other classes are also serialized because of reconstruction dependencies ( class Action, class Param, etc )
The master Class:
template <class S, class P> class Task
{
protected:
...
//! Container of states of type S (template parameter)
boost::ptr_vector<S> states;
//! Container of policies of type P (template parameter)
boost::ptr_vector<P> policies;
...
public:
Task(Agent &a, ACTION_MODE &m);
...
void save_to_file();
void load_from_file(std::string filename);
};
template <class S, class P>
void Task<S,P>::save_to_file()
{
std::string output = ramdisk+"serialized";
char *file = (char*)output.c_str();
std::ofstream ofs(file);
assert(ofs.good());
boost::archive::text_oarchive oa(ofs);
oa << states;
oa << policies;
ofs.close();
}
template <class S, class P>
void Task<S,P>::load_from_file(std::string filename)
{
char *file = (char*)output.c_str();
std::cout << file << std::endl;
std::ifstream ifs(file);
boost::archive::text_iarchive ia(ifs);
ia >> states;
ia >> policies;
ifs.close();
}
Effectively contains the two boost::ptr_vectors which hold the States and Policies.
States are saved and loaded without a problem.
The problem arises when loading policies. Saving them does not seem to create a problem (but then again I might be wrong).
Having tested save/load without policies, and with, The issue appears to be with policy reconstruction.
Note the default constructors used only by serialization, without which the code will not compile.
EDIT#2: After running application using valgrind and memcheck, it reports that there is a pointer memory leak. However since I am not good at debugging with valgrind I can't really tell where the leak is occuring or if it is relevant to my serialization (I think it is).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题是您正在序列化状态和策略,而策略还保存对状态相同实例的引用。您只能序列化没有此类交叉引用的类。写入文件时,Boost应该抛出指针冲突异常。在我的测试中,它取决于是否抛出异常的写入顺序 - 这是不幸的,因为即使写入成功,加载也会失败。
解决方法是删除行
oa <<保存和加载时的状态,并在加载后步骤中手动修复指针。
关于构造函数:这主要是 boost-api 需要执行其模板魔法的东西。但是,在使用版本控制时,为成员变量指定默认值非常重要,这样在加载具有旧版本号的文件时它们就不会处于未初始化状态。
The problem is that you are serializing
states
andpolicies
whilePolicy
also holds references to the same instances ofState
. You can only serialize classes which don't have such cross-references. Boost should throw a pointer-conflict exception when writing to the file. In my tests it dependet on the order of writes if the exception was thrown or not - which is unfortunate because loading fails even if writing succeeded.A workaround would be to delete the line
oa << states
when saving and loading and fix up the pointers by hand in a post-loading step.About the constructors: it's mostly something the boost-api needs to do it's template magic. However when using versioning it's important to specify default-values for your member-variables so they are not left uninitialized when loading files with an older version-number.