boost:序列化重构(加载)

发布于 2024-11-25 06:22:38 字数 4358 浏览 3 评论 0原文

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

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

并且我从 boost 文档中的示例中看到我需要这样的东西:

State() {};

然而 Agent &A 必须作为参数传递。 我应该找到解决这个问题的方法(使用外部、单例、全局对象)还是有办法在重建时修改这种行为?我确信我在这里遗漏了一些东西。

谢谢

编辑: 也许我解释得不够清楚。尝试通过重建序列化数据“加载”时收到错误消息。

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

这就是让我研究 boost::serialize 代码的原因,并认为它正在调用构造函数或复制运算符。 如何让它使用特定的构造函数来序列化数据并将 Agent 引用 &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 的朋友,并且具有序列化函数。 保存工作正常,加载是问题。 状态为:boost::ptr_vector; states; 其中 S 是 State 多态类的一种类型。

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 个双变量的简单结构。它们在上面进行了序列化^^。 Positions 是一个 std::map。 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。

I'm using boost:serialization to save data structures into a file. The actual data is a pointer vector of classes and subclasses.
However the constructor of the class which is serialized takes as parameter another instantiated class Agent which is an object that controls communication with a simulation API (webots).
I see that in boost::serialization examples, the serializable objects need an empty constructor class() {}; to be used for the reconstruction. However this is impractical in my case. How can I use reconstruction but include the object which communicates with the API ?
One of the serializable classes has this constructor:

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

and I've seen from examples in boost docs that I need something like this:

State() {};

Yet Agent &A must be passed as parameter.
Should I find a way around this (using an extern, a singleton, a global object) or is there a way to modify this behavior when reconstructing ? I'm sure I'm missing something here.

Thank you

EDIT:
Maybe I didn't explain this clearly enough. I get an error message when trying to "load" by reconstructing the serialized data.

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

Which is what made me look into boost::serialize code, and think that it is calling a constructor or copy operator.
How do I make it use a specific constructor to serialize the data and take as an argument the Agent reference &a ?

EDIT#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 is friend to boost::serialization::access and has a function serialize.
Saving works fine, loading is the problem.
states is: boost::ptr_vector<S> states; where S is a type of State polymorphic class.

State is the base class and has "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 inherits from 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 are simple structures with 3 double variables. They get serialized above^^.
Positions is an std::map<std::string,float> positions;

Looking at the serialized text file, everything appears Ok.
I cannot understand why it calls a constructor when trying to load the file.

EDIT#3:

Base Constructor is:

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

Derived Constuctor is:

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

Agent reference &A, kept in each State (or derived State) refers to an object obtained from the simulation API. It controls a robot. I cannot serialize it, and it doesn't make sense serializing it.

When I use:

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

I get the following errors:

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

referring to the references used from the constructor.
And:

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

As you can see, it makes no sense to try and save the reference to Agent, because that reference (even if it could be saved or serialized) will probably be different every time the application is started.

And in loading the construct data, apart from me probably using the wrong syntax, it makes no sense to construct from a serialized reference to agent.

What I belive i need, is a way to tell the load_construct_data how to obtain a reference to an Agent (after initializing an agent object) and use that reference to construct the data.

Does that make any sense ? Do you think this is doable ?

EDIT#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);
   }
  }
}

It will not allow to serialize guState::caller

I have also made class Agent serializable, and overloaded the load_construct_data and save_construct_data of Agent in order to request from the simulation app a new instance of Agent to control the API.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

━╋う一瞬間旳綻放 2024-12-02 06:22:38

编辑:

我认为我们都错过了手册的一部分:非默认构造函数部分此处。要使其工作,您需要一个 save_construct_data 和一个 load_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()
}

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

EDIT:

There is a part of the manual that I think we both missed: The section non-default constructors here. To make it work you need to a save_construct_data and a load_construct_data function. There is a slight technicallity in terms of where these are friended explored here.

Also, you said you had this problem when trying to load only, but that you could save fine. This makes me think that you might have omitted

BOOST_CLASS_EXPORT_GUID(state, "state")

This omission might lead to segmentation faults once you get the load compiling (see the export section of the manual)

To make sure I was not mistaken, I made a compiling example, which I add in case its useful.

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

}

OLD RESPONSE:

Not sure I entirely understood your problem (a fuller example would help me) -
The constructor doesn't usually come into it when you serialize an object: you serialize the raw data?

Do you mean that you don't want to serialize all the raw data of the object, but just want to reconstruct it again when you deserialize the object (using the constructor)? If so then you can do this by seriazing the data that you need for the reconstruction and splitting the save and load operations.

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()
}

Does that help, or have I missed the point?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文