具有boost :: shared_ptr类型成员的std :: map的序列化失败

时间:2014-05-02 02:15:48

标签: c++ serialization boost boost-serialization

我试图在结构化类型的序列化和反序列化之后比较两个映射。它在“MyExample”中给出了错误 - “错误:检查e1_i1->second == e2_i1->second失败”

我无法弄清楚以下代码中可能出现的问题:

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/map.hpp> 
#include <boost/serialization/shared_ptr.hpp>

struct A
{
public:
   std::string oldname;
   std::string newname;

   friend class boost::serialization::access;
      template<class Archive>
      void serialize(Archive &ar, const unsigned int version)
      {
         ar & oldname;
         ar & newname;
      }
};

struct Example
{
public:
   bool check;
   std::map<std::string,boost::shared_ptr<A>> Atype;

   private:
      friend class boost::serialization::access;
      template<class Archive>
      void serialize(Archive &ar, const unsigned int version)
      {
         ar & check;
         ar & Atype;
      }
};

void set_data(boost::shared_ptr<A> e)
{
    e->oldname="a";
    e->newname="b";
}

void set_data(Example *e)
{
   e->check=false;

   // to initialize e->Atype
   boost::shared_ptr<A> a1 (new A());
   set_data(a1);
   e->Atype.insert(std::make_pair("a",a1));
}

void compare_data(std::map<std::string,boost::shared_ptr<A>> e1,std::map<std::string,boost::shared_ptr<A>> e2)
{
   // because it is of type map, it may be not in proper order   
   typedef std::map<std::string,boost::shared_ptr<A>>::const_iterator i1;

   i1 e1_i1= e1.begin();
   i1 e2_i1 =e2.begin();

   while ( e1_i1 != e1.end() && e2_i1 != e2.end()) 
   {
      BOOST_CHECK( e1_i1->first == e2_i1->first);

      const std::string &key = e1_i1->first;
      e2_i1 =e2.find(key);
      BOOST_CHECK(e1_i1->second == e2_i1->second);

      e1_i1++;
      e2_i1++;
    }
}

void compare_data(Example *e1,Example *e2)
{
   BOOST_CHECK(e1->check == e2->check);

   // need to compare e1->Atype with e2->Atype
   compare_data(e1->Atype,e2->Atype);
}

BOOST_AUTO_TEST_CASE(MyExample)
{
   boost::archive::text_oarchive ao(std::cout);

   Example c;
   set_data(&c);

   const Example & oc=c;
   ao << oc;

   std::stringstream ss; 
   boost::archive::text_oarchive oa(ss);
   oa << oc;

   boost::archive::text_iarchive ia(ss);

   Example d;
   ia >> d;

   compare_data(&c,&d); 

}

我确保包含所有头文件,但在这里我没有包括所有。 真的不确定在上面的代码中可能出现什么问题,或者数据的设置值不正确或者比较可能是错误的。

我尝试初始化地图的不同方法:

// e->Atype=map_list_of ("a",a1);
// e->Atype.insert(std::make_pair("a",a1));
// e->Atype.insert(std::map<std::string,boost::shared_ptr<A>>::value_type("a",a1));
// e->Atype["a"]=a1;

谢谢,

2 个答案:

答案 0 :(得分:3)

您的比较代码应更改为

void compare_data(const std::map<std::string,boost::shared_ptr<A>>& e1,
                  const std::map<std::string,boost::shared_ptr<A>>& e2)
{
   // because it is of type map, order IS guaranteed
   typedef std::map<std::string,boost::shared_ptr<A>>::const_iterator i1;

   i1 e1_i1= e1.begin();
   i1 e2_i1 =e2.begin();
   while ( e1_i1 != e1.end() && e2_i1 != e2.end()) 
   {
      BOOST_CHECK( e1_i1->first == e2_i1->first );
      if (e1_i1->second) {
         // Data value is present on e1, must be present on e2 too
         BOOST_CHECK( e2_i1->second );
         // The two data values must be equal
         BOOST_CHECK( *e1_i1->second == *e2_i1->second ); // Note the *
      } else {
         // Data value is not present on e1, must not be present in e2 either
         BOOST_CHECK( !e2_i1->second );
      }
      e1_i1++;
      e2_i1++;
   }
   // The iteration must terminate at the same time for e1 and e2
   // (i.e. neither of them must have extra elements in respect to the other)
   BOOST_CHECK( e1_i1 == e1.end() && e2_i1 == e2.end() );
}

您还应该向bool operator==(const A& other) const课提供A,以检查A的两个实例是否具有相同的值。

当反序列化时,要求在同一个内存地址中分配对象没有意义,只有查看重新加载的值是否相同才有意义。

答案 1 :(得分:1)

你很困惑。

// because it is of type map, it may be not in proper order   

相反,map有序容器,而且不变量恰恰是元素始终按正确顺序

序列化很好:Coliru两次打印相同的数据:

22 serialization::archive 10 0 0 0 0 0 1 0 0 0 1 a 0 1 4 1 0
0 1 a 1 b

22 serialization::archive 10 0 0 0 0 0 1 0 0 0 1 a 0 1 4 1 0
0 1 a 1 b

所以这是比较被打破了。解决它:

MapOfA mapOfA;

bool operator==(Example const& other) const {
    return (check == other.check)
        && (mapOfA.size() == other.mapOfA.size())
        && (mapOfA.end() == std::mismatch(
                mapOfA.begin(), mapOfA.end(),
                other.mapOfA.begin(),
                &ComparePair
        ).first);
}

private:
typedef MapOfA::value_type Pair;
static bool ComparePair(Pair const& a, Pair const& b) {
    bool ok = (a.first == b.first);
    if (a.second || b.second)
        ok &= (a.second && b.second) && (*a.second == *b.second);
    return ok;
}

当你不使用指针和辅助函数时,你会发现整个程序变得更小,更不容易出错。相反,尽可能使用** const&引用和标准惯用法/算法。

以下是97 lines Live On Coliru中程序的一个版本。

完整列表

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/map.hpp> 
#include <boost/serialization/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <sstream>

struct A
{
    std::string oldname;
    std::string newname;

    A(std::string oldname = "", std::string newname = "") 
        : oldname(std::move(oldname)), newname(std::move(newname)) 
    { }

    bool operator==(A const& other) const {
        return (oldname == other.oldname)
            && (newname == other.newname);
    }

    friend class boost::serialization::access;
    template<class Archive>
        void serialize(Archive &ar, unsigned) {
            ar & oldname;
            ar & newname;
        }
};

struct Example
{
    typedef std::map<std::string, boost::shared_ptr<A> > MapOfA;

    bool check;
    MapOfA mapOfA;

    bool operator==(Example const& other) const {
        return (check == other.check)
            && (mapOfA.size() == other.mapOfA.size())
            && (mapOfA.end() == std::mismatch(
                    mapOfA.begin(), mapOfA.end(),
                    other.mapOfA.begin(),
                    &ComparePair
            ).first);
    }

  private:
    typedef MapOfA::value_type Pair;
    static bool ComparePair(Pair const& a, Pair const& b) {
        bool ok = (a.first == b.first);
        if (a.second || b.second)
            ok &= (a.second && b.second) && (*a.second == *b.second);
        return ok;
    }

    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive &ar, unsigned) {
        ar & check;
        ar & mapOfA;
    }
};

template <typename T>
std::string to_string(T const& o) {
    std::stringstream ss; 
    boost::archive::text_oarchive oa(ss);
    oa << o;
    return ss.str();
}

int main()
{
    Example const data = {
        false,
        { { "ONE", boost::make_shared<A>("old one","new one") },
          { "TWO", boost::make_shared<A>("old two","new two") },
          { "TRHEE", boost::make_shared<A>("old three","new three") },
        }
    };

    std::string const original = to_string(data);

    std::cout << original << "\n";

    {
        std::stringstream ss(original); 
        boost::archive::text_iarchive ia(ss);

        Example roundtrip;
        ia >> roundtrip;

        std::string const verification = to_string(roundtrip);

        std::cout << std::boolalpha;
        std::cout << "Matching: "   << (data == roundtrip)        << "\n";
        std::cout << "Serialized: " << (verification == original) << "\n";
    }
}