【问题标题】:How to implement Boost::Serialize for Boost::Nested_Container如何为 Boost::Nested_Container 实现 Boost::Serialize
【发布时间】:2020-05-19 08:43:42
【问题描述】:

another question的跟进。)

Boost::Serialize 经常在 oarchive 上传递一个异常,抱怨重新创建一个特定的对象会导致重复的对象。一些档案保存并重新加载成功,但许多导致上述错误。我还不能确定发生错误的确切条件,但我已经证明用于填充nested_container 和平面对象列表的内容都不包含重复的对象ID。我使用的是文本存档,而不是二进制文件。以下是我如何修改nested_container 以及另一个单独的平面对象列表的代码以执行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
};

【问题讨论】:

    标签: c++ serialization boost boost-multi-index


    【解决方案1】:

    您遇到的问题很可能再次归因于索引#0(散列的)中元素的特定顺序。例如,如果我们像这样填充容器:

    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 非常聪明地检测和抱怨的地方。这种先子后父的情况取决于元素在散列索引中的排序方式非常不可预测,这就是您有时会看到问题的原因。

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

    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 的完整演示代码如下:

    Live On 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_containers。

    【讨论】:

    • 感谢您的提醒,我将从序列化中删除 subtree_obj、path 和 subtree_path 并验证是否解决了存档投诉。
    • 请注意,subtree_objpathsubtree_path 是否有serialize 与您的原始问题无关。
    • 我实现了解决方案 #1 并运行了几个非常复杂的测试,所有这些测试以前都失败了 - 现在都通过了!我对你的知识和专长印象深刻。我将更仔细地检查解决方案 #2 并尽快实施它,以代替解决方案 #1。我也对 Boost::Multi-index 和 Boost::Serialize 的专业实施印象深刻!我已经成为一个超级粉丝。再次,华金,谢谢你。我将在这里深入研究您的代码以彻底掌握它。我可能有一些问题,但我不想在不理解的情况下简单地使用它。
    猜你喜欢
    • 2014-11-21
    • 2017-07-24
    • 1970-01-01
    • 2013-12-05
    • 1970-01-01
    • 2012-11-01
    • 2011-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多