4

很长一段时间以来,我一直在努力完成这项工作。

在我的项目中,通过实现模板函数序列化,使用 boost 中的确切教程对 6 个类进行了序列化。

这些类是:State、guState、Policy、Action、Param、Vec3D。

当我序列化并保存它们时,它工作正常。我确实得到了一个文本文件,其中包含各种数字和字符串。

没有抱怨,没有警告,没有抛出异常。唯一的情况是,如果我尝试序列化一个类的指针成员,空洞进程就会变成僵尸。所以我不尝试这样做,并保存作品。

但是,当我尝试加载时,我得到:

抛出'boost::archive::archive_exception'的实例后调用终止what():流错误

现在有趣的部分是我序列化了两个 boost::ptr_vector,一个由 State 指针组成,一个由 Policy 指针组成。

状态向量,我保存并加载没有问题。策略向量,我可以保存,但是当我尝试加载时,我得到了异常。

此外,在阅读了 boost 教程之后,我的印象是,为了加载,除了 serialize 函数之外,我不需要其他任何东西。

但是,当我尝试加载时,boost 序列化会抱怨找不到默认构造函数,例如 State()、Policy() 等(我在每个类中实现自己的构造函数)。

在这里阅读本教程后,我实现了一个默认构造函数,它什么都不做,因此 boost-serialization 可以工作。确实它确实编译了,我得到了上面提到的结果。

我已经尝试走一条非常复杂的道路,在我的旧问题中看到,我尝试分离和实现 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_vectors。状态被保存和加载没有问题。

加载策略时会出现问题。保存它们似乎不会造成问题(但我可能又错了)。

在没有策略和有策略的情况下测试了保存/加载,问题似乎与策略重建有关。

请注意仅由序列化使用的默认构造函数,否则代码将无法编译。

编辑#2:使用 valgrind 和 memcheck 运行应用程序后,它报告存在指针内存泄漏。但是,由于我不擅长使用 valgrind 进行调试,所以我无法真正判断泄漏发生在哪里,或者它是否与我的序列化相关(我认为是)。

4

1 回答 1

3

问题是您正在序列化states并且policies同时Policy还持有对State. 您只能序列化没有此类交叉引用的类。Boost在写入文件时应该抛出一个指针冲突异常。在我的测试中,如果异常被抛出,它取决于写入的顺序——这很不幸,因为即使写入成功,加载也会失败。

一种解决方法是在保存和加载时删除该行oa << states,并在加载后的步骤中手动修复指针。

关于构造函数:这主要是 boost-api 需要做的事情,它是模板魔术。但是,在使用版本控制时,为您的成员变量指定默认值非常重要,这样在加载具有较旧版本号的文件时它们不会未初始化。

于 2011-09-05T12:39:28.010 回答