如何为Boost :: Nested_Container实现Boost :: Serialize [英] How to implement Boost::Serialize for Boost::Nested_Container

查看:81
本文介绍了如何为Boost :: Nested_Container实现Boost :: Serialize的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(另一个问题的后续报道.)

Boost :: Serialize通常会在归档时产生异常,抱怨重新创建特定对象会导致对象重复.一些档案成功保存并重新加载,但许多档案导致上述错误.我还无法确定发生错误的确切条件,但是我已经证明,用于填充nested_container和平面对象列表的内容均不包含重复的对象ID.我正在使用文本存档,而不是二进制文件.这是我如何修改nested_container以及另一个单独的平面对象列表的代码,以进行Boost :: Serialize:

Boost::Serialize often delivers an exception on oarchive, complaining that re-creating a particular object would result in duplicate objects. Some archives save and re-load successfully, but many result in the error above. I have not been able yet to determine the exact conditions under which the error occurs, but I have proven that none of the content used to populate the nested_container and the flat object list contains duplicate object IDs. I am using text archive, not binary. Here is how I have modified the code for nested_container and also for another, separate flat object list in order to do Boost::Serialize:

struct obj
{
    int             id;
    const obj * parent = nullptr;

    obj()
        :id(-1)
    { }

    obj(int object)
        :id(object)
    { }

    int getObjId() const
    {
        return id;
    }

    bool operator==(obj obj2)
    {
        if (this->getObjId() == obj2.getObjId())
            return true;
        else
            return false;
    }
#if 1
private:
    friend class boost::serialization::access;
    friend std::ostream & operator<<(std::ostream &os, const obj &obj);

    template<class Archive>
    void serialize(Archive &ar, const unsigned int file_version)
    {
        ar & id & parent;
    }
#endif
};

struct subtree_obj
{
    const obj & obj_;

    subtree_obj(const obj & ob)
        :obj_(ob)
    { }
#if 1
private:
    friend class boost::serialization::access;
    friend std::ostream & operator<<(std::ostream &os, const subtree_obj &obj);

    template<class Archive>
    void serialize(Archive &ar, const unsigned int file_version)
    {
        ar & obj_;
    }
#endif
};

struct path
{
    int         id;
    const path *next = nullptr;

    path(int ID, const path *nex)
        :id(ID), next(nex)
    { }

    path(int ID)
        :id(ID)
    { }
#if 1
private:
    friend class boost::serialization::access;
    friend std::ostream & operator<<(std::ostream &os, const path &pathe);

    template<class Archive>
    void serialize(Archive &ar, const unsigned int file_version)
    {
        ar & id & next;
    }
#endif
};

struct subtree_path
{
    const path & path_;

    subtree_path(const path & path)
        :path_(path)
    { }
#if 1
private:
    friend class boost::serialization::access;
    friend std::ostream & operator<<(std::ostream &os, const subtree_path &pathe);

    template<class Archive>
    void serialize(Archive &ar, const unsigned int file_version)
    {
        ar & path_;
    }
#endif
};

//
// My flattened object list
//

struct HMIObj
{
    int         objId;
    std::string objType;

    HMIObj()
        :objId(-1), objType("")
    { }

    bool operator==(HMIObj obj2)
    {
        if (this->getObjId() == obj2.getObjId())
            && this->getObjType() == obj2.getObjType())
            return true;
        else
            return false;
    }

    int getObjId() const
    {
        return objId;
    }

    std::string getObjType() const
    {
        return objType;
    }
#if 1
private:
    friend class boost::serialization::access;
    friend std::ostream & operator<<(std::ostream &os, const HMIObj &obj);

    template<class Archive>
    void serialize(Archive &ar, const unsigned int file_version)
    {
        ar & objId & objType;
    }
#endif
};

推荐答案

您遇到的问题很可能是由于在索引#0(散列的元素)中遍历元素的特定顺序造成的.例如,如果我们这样填充容器:

The problem you're experiencing is most likely due to, again, the particular order in which elements are traversed in index #0 (the hashed one). For instance, if we populate the container like this:

nested_container c;
c.insert({54});
auto it=c.insert({0}).first;
  insert_under(c,it,{1});

然后,这些元素在索引#0中列为(1、54、0).这里的关键问题是1是0的子元素:以与保存顺序相同的顺序加载元素时,第一个元素为1,但是为了正确指向它,需要先加载0.这就是Boost.Serialization非常聪明地检测和抱怨的东西.这种先于父母的情况取决于在哈希索引中对元素进行排序的方式非常难以预测,这就是为什么您有时会看到问题的原因.

Then the elements are listed in index #0 as (1, 54, 0). The crucial problem here is that 1 is a child of 0: when loading elements in the same order as they were saved, the first one is then 1, but this needs 0 to be loaded before in order to properly point to it. This is what Boost.Serialization very smartly detects and complains about. Such child-before-parent situations depend on the very unpredictable way elements are sorted in a hashed index, which is why you see the problem just sometimes.

您有两个简单的解决方案:

You have two simple solutions:

  1. 在嵌套容器的定义中交换索引#0和#1:由于索引#1的排序顺序是树的预排序,因此可以确保父母在孩子之前得到处理.
  2. 重写嵌套容器的序列化代码,以通过索引#1:

 

template<class Archive>
void serialize(Archive& ar,nested_container& c,unsigned int)
{
  if constexpr(Archive::is_saving::value){
    boost::serialization::stl::save_collection(ar,c.get<1>());
  }
  else{
    boost::serialization::load_set_collection(ar,c.get<1>());
  }
}

解决方案2的完整演示代码如下:

Complete demo code for solution #2 follows:

在Coliru上直播

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <iterator>

struct obj
{
  int        id;
  const obj* parent=nullptr;
};

namespace boost{
namespace serialization{

template<class Archive>
void serialize(Archive& ar,obj& x,unsigned int)
{
  ar&x.id&x.parent;
}

}} /* namespace boost::serialization */

struct subtree_obj
{
  const obj& obj_;
};

struct path
{
  int         id;
  const path* next=nullptr;
};

struct subtree_path
{
  const path& path_;
};

inline bool operator<(const path& x,const path& y)
{
       if(x.id<y.id)return true;
  else if(y.id<x.id)return false;
  else if(!x.next)  return y.next;
  else if(!y.next)  return false;
  else              return *(x.next)<*(y.next);
}

inline bool operator<(const subtree_path& sx,const path& y)
{
  const path& x=sx.path_;

       if(x.id<y.id)return true;
  else if(y.id<x.id)return false;
  else if(!x.next)  return false;
  else if(!y.next)  return false;
  else              return subtree_path{*(x.next)}<*(y.next);
}

inline bool operator<(const path& x,const subtree_path& sy)
{
  return x<sy.path_;
}

struct obj_less
{
private:
  template<typename F>
  static auto apply_to_path(const obj& x,F f)
  {
    return apply_to_path(x.parent,path{x.id},f); 
  }

  template<typename F>
  static auto apply_to_path(const obj* px,const path& x,F f)
    ->decltype(f(x))
  { 
    return !px?f(x):apply_to_path(px->parent,{px->id,&x},f);
  }

public:
  bool operator()(const obj& x,const obj& y)const
  {
    return apply_to_path(x,[&](const path& x){
      return apply_to_path(y,[&](const path& y){
        return x<y;
      });
    });
  }

  bool operator()(const subtree_obj& x,const obj& y)const
  {
    return apply_to_path(x.obj_,[&](const path& x){
      return apply_to_path(y,[&](const path& y){
        return subtree_path{x}<y;
      });
    });
  }

  bool operator()(const obj& x,const subtree_obj& y)const
  {
    return apply_to_path(x,[&](const path& x){
      return apply_to_path(y.obj_,[&](const path& y){
        return x<subtree_path{y};
      });
    });
  }
};

using namespace boost::multi_index;
using nested_container=multi_index_container<
  obj,
  indexed_by<
    hashed_unique<member<obj,int,&obj::id>>,
    ordered_unique<identity<obj>,obj_less>
  >
>;

#if 1 /* set to 0 to trigger pointer conflict exception */

#include <boost/serialization/set.hpp>

namespace boost{
namespace serialization{

template<class Archive>
void serialize(Archive& ar,nested_container& c,unsigned int)
{
  if constexpr(Archive::is_saving::value){
    boost::serialization::stl::save_collection(ar,c.get<1>());
  }
  else{
    boost::serialization::load_set_collection(ar,c.get<1>());
  }
}

}} /* namespace boost::serialization */

#endif

template<typename Iterator>
inline auto insert_under(nested_container& c,Iterator it,obj x)
{
  x.parent=&*it;
  return c.insert(std::move(x));
}

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <iostream>
#include <sstream>

void print(const nested_container& c)
{
  for(const obj& x:c){
    std::cout<<"("<<x.id;
    if(x.parent)std::cout<<"->"<<x.parent->id;
    std::cout<<")";
  }
  std::cout<<"\n";
}

int main()
{
  nested_container c;
  c.insert({54});
  auto it=c.insert({0}).first;
    insert_under(c,it,{1});

  print(c);

  std::ostringstream oss;
  boost::archive::text_oarchive oa(oss);
  oa<<c;

  nested_container c2;
  std::istringstream iss(oss.str());
  boost::archive::text_iarchive ia(iss);
  ia>>c2;  

  print(c2);
}

顺便说一句,为什么要为subtree_objpathsubtree_path提供serialize功能?您不需要该序列化nested_container.

By the way, why are you providing serialize functions for subtree_obj, path and subtree_path? You don't need that to serialize nested_containers.

这篇关于如何为Boost :: Nested_Container实现Boost :: Serialize的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆