4

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

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

我从 boost docs 中的示例中看到我需要这样的东西:

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。保存工作正常,加载是问题。states 是:boost::ptr_vector<S> states;其中 S 是 State 多态类的一种类型。

状态是基类并且具有“序列化”

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;
}

accel、gyro、gps 是具有 3 个双变量的简单结构。它们在上面被序列化^^。职位是一个std::map<std::string,float> positions;

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

编辑#3:

基本构造函数是:

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

派生构造函数是:

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

每个状态(或派生状态)中保存的代理引用 &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。

4

1 回答 1

4

编辑:

我认为我们都错过了手册的一部分:此处的非默认构造函数 部分。要使其工作,您需要一个和一个功能。就这些在这里进行朋友探索的地方而言,存在轻微的技术性。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()
}

这有帮助吗,还是我错过了重点?

于 2011-07-18T15:19:08.303 回答