Boost:序列化重构(加载)

boost:serialization reconstruction (loading)

本文关键字:加载 重构 序列化 Boost      更新时间:2023-10-16

我使用boost:serialization将数据结构保存到文件中。实际数据是类和子类的指针向量。然而,被序列化的类的构造函数将另一个实例化类Agent作为参数,Agent是一个控制与模拟API (webots)通信的对象。我看到在boost::序列化示例中,可序列化对象需要一个空构造函数class() {};用于重建。然而,这在我的情况下是不切实际的。我如何使用重建,但包括与API通信的对象?其中一个可序列化的类具有以下构造函数:

State(Agent &A, ACTION_MODE const& m);

,我从boost文档的例子中看到,我需要这样的东西:

State() {};

但是Agent &A必须作为参数传递。我是否应该找到一种方法来解决这个问题(使用外部对象,单例对象,全局对象),或者有一种方法可以在重构时修改这种行为?我肯定我遗漏了什么。

谢谢

编辑:也许我解释得不够清楚。当试图通过重建序列化数据来"加载"时,我得到一个错误消息。

error: no matching function to call State::State()

这就是为什么我看boost::serialize代码,并认为它是调用构造函数或复制操作符。如何使它使用特定的构造函数来序列化数据并将代理引用&a作为参数?

编辑# 2:

template <class S, class P, class A> void Task<S,P,A>::save(const char* file)
{
  std::ofstream ofs(file);
  assert(ofs.good());
  boost::archive::text_oarchive oa(ofs);
  oa << states;
  ofs.close();
}
template <class S, class P, class A> void Task<S,P,A>::load(const char* file)
{
  std::ifstream ifs(file);
  boost::archive::text_iarchive ia(ifs);
  ia >> states;
  ifs.close();
}

States是boost::serialization::access的友元,并且有一个serialize函数。保存工作很好,加载是问题。状态为:boost::ptr_vector<S> states;,其中S为一种状态多态类。

State是基类,具有"serialize"

template <class Archive>
void State::serialize(Archive& ar, const unsigned int version)
{
  ar & accel.Xaxis & accel.Yaxis & accel.Zaxis;
  ar & gyro.Xaxis & gyro.Yaxis & gyro.Zaxis;
  ar & gps.Yaxis;
  ar & positions;
  ar & reward & value & hash_value;
}

guState继承自State。

template <class Archive>
void guState::serialize(Archive& ar, const unsigned int version)
{
  ar & boost::serialization::base_object<State>(*this);
  ar & accel.Xaxis & accel.Yaxis & accel.Zaxis;
  ar & gyro.Xaxis & gyro.Yaxis & gyro.Zaxis;
  ar & gps.Yaxis;
  ar & positions;
  ar & reward & value & hash_value;
}

加速度、陀螺仪、GPS是3个双变量的简单结构。它们在^^上面序列化。position是一个std::map<std::string,float> positions;

查看序列化的文本文件,一切正常。我不明白为什么在加载文件时调用构造函数。

编辑# 3:

基本构造函数是:

State(Agent &A, ACTION_MODE const& m);

派生构造函数is:

guState::guState(Agent& A, ACTION_MODE const& m) : 
State(A, m)
{
...
}

Agent reference &A,保存在每个状态(或派生状态)中,是指从仿真API获得的对象。它控制着一个机器人。我不能序列化它,序列化它也没有意义。

当我使用

namespace boost { namespace serialization {
  template <class Archive>
    void save_construct_data(Archive & ar,const guState* d,const unsigned int file_version)
    {
      ar << guState::caller;
      ar << guState::mode;
    }
   template <class Archive>
   void load_construct_data(Archive & ar, guState* d,const unsigned int file_version)
   {
      Agent &a;
      ACTION_MODE &m;
      ar >> a;
      ar >> m;
      ::new(d) guState(a,m);
   }
  }
}

得到以下错误:

invalid use of non-static data member State::caller
invalid use of non-static data member State::mode

引用构造函数中使用的引用。和:

error: 'a' declared as reference but not initialized
error: 'm' declared as reference but not initialized

正如您所看到的,尝试将引用保存到Agent是没有意义的,因为该引用(即使可以保存或序列化)可能在每次启动应用程序时都不同。

在加载构造数据时,除了我可能使用了错误的语法之外,从序列化引用到代理进行构造是没有意义的。

我认为我需要的是一种方法来告诉load_construct_data如何获得对代理的引用(在初始化代理对象之后),并使用该引用来构造数据。

这有意义吗?你认为这可行吗?

编辑# 4

namespace boost { namespace serialization {
  template <class Archive>
    void save_construct_data(Archive & ar,const guState* d,const unsigned int file_version)
    {
      ar <<  guState::caller;
    }
   template <class Archive>
   void load_construct_data(Archive & ar, guState* d,const unsigned int file_version)
   {
      Agent * a;
      ACTION_MODE mode = RAND_SING;
      ar >> a;
      ::new(d) guState(*a,mode);
   }
  }
}

不允许序列化guState::caller

我还使Agent类可序列化,并重载了Agent的load_construct_data和save_construct_data,以便从仿真应用程序请求一个新的Agent实例来控制API。

EDIT:

手册中有一部分我想我们都错过了:这里的非默认构造函数部分。要使其工作,您需要save_construct_dataload_construct_data功能。这里有一个轻微的技术术语,在这里讨论这些。

还有,你说你在尝试只加载时遇到了这个问题,但是你可以保存好。这让我觉得你可能省略了

BOOST_CLASS_EXPORT_GUID(state, "state")

一旦进行加载编译(请参阅手册的导出部分),此遗漏可能会导致分段错误

为了确保我没有弄错,我做了一个编译示例,我添加了它,以防它有用。

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <iostream>
#include <fstream>
//base class
struct base
{
  base(double d) : m_d(d) {}
  virtual double run() = 0;
private:
  friend class boost::serialization::access;
  double m_d;
  template <class Archive>
  void serialize(Archive& ar, const unsigned int version)
  {
    ar & m_d;
  }
};
//forward declare the save construct data before friending it 
// (something about friend being in a different namespace)
class derived;
namespace boost { namespace serialization {
template<class Archive>
inline void save_construct_data(Archive & ar, const derived * t, const unsigned int file_version);
}}

//derived class with non-default constructor
struct derived : public base
{
  derived(double a , double b) : 
    base(a+b),
    m_a(a),m_b(b),m_c(a*b) 
  {}
  //some checks
  double get_a() const {return m_a;}
  double get_b() const {return m_b;}
  double get_c() const {return m_c;}
  double run(){return 1.0;}
private:
  friend class boost::serialization::access;
  template<class Archive> 
  friend void boost::serialization::save_construct_data(Archive & ar, const derived * t, const unsigned int file_version);
  template <class Archive>
  void serialize(Archive& ar, const unsigned int version)
  {
    ar & boost::serialization::base_object<base>(*this);
    //only need to return c, a and b already done for constructor
    ar & m_c;
  }
  double m_a, m_b, m_c;
 };
//Save and load the data required for the constructor.
namespace boost { namespace serialization {
  template <class Archive>
    inline void save_construct_data(
                    Archive & ar,const derived* d,const unsigned int file_version
                    )
    {
      // save data required to construct instance
      ar << d->m_a;
      ar << d->m_b;
    }
    template <class Archive>
    inline void load_construct_data(
                        Archive & ar, derived* d,const unsigned int file_version
                        )
    {
      double a,b;
      ar >> a;
      ar >> b;
    // invoke inplace constructor to initialize instance of my_class
      ::new(d) derived(a,b);
    }
  }
}
//register the derived class with boost.
BOOST_CLASS_EXPORT_GUID(derived, "derived")
int
main  (int ac, char **av)
{
  std::ofstream ofs("filename");
  base* p = new derived(2,3);
  // save data to archive
  {
    boost::archive::text_oarchive oa(ofs);
    oa << p;
  }
  // ... some time later restore the class instance to its orginal state
  base* p2;
  {
     std::ifstream ifs("filename");
     boost::archive::text_iarchive ia(ifs);
     ia >> p2;
  }
  derived* d = static_cast<derived*>(p2);
  std::cout<<"p2 vals are: "<<d->get_a()<<" "<<d->get_b()<<" "<<d->get_c()<<std::endl;
}

老回应:

不确定我完全理解你的问题(一个更完整的例子会帮助我)-当您序列化对象时,构造函数通常不会出现在其中:您序列化原始数据?

你的意思是你不想序列化对象的所有原始数据,而只是想在反序列化对象时再次重构它(使用构造函数)?如果是这样,那么您可以通过序列化重建所需的数据并拆分保存和加载操作来实现此目的。

struct my_class
{
  my_class(Agent& A, ACTION_MODE const & m)
    : m_state(A,M)
  {}

private: 
  State m_state;
  friend class boost::serialization::access;
  void save(Archive & ar, const unsigned int version) const
  {
      // note, version is always the latest when saving
      Agent tmp_A = m_state.get_A();
      ACTION_MODE tmp_m = m_state.get_m();
      ar & tmp_A; 
      ar & tmp_m;
  }
  template<class Archive>
  void load(Archive & ar, const unsigned int version)
  {
      Agent tmp_A;
      ACTION_MODE tmp_m
      ar & tmp_A; 
      ar & tmp_m;
      m_state = State(tmp_A,tmp_m);
  }
  BOOST_SERIALIZATION_SPLIT_MEMBER()
}

这有帮助吗,还是我没抓住重点?