Boost(C++)中没有类跟踪的派生类序列化

Alv*_*nte 15 c++ serialization boost boost-serialization

通过基类指针序列化派生类时,我遇到了一些boost序列化问题.我需要一个系统,它在系统中接收某些对象时将其序列化,因此我需要随着时间的推移进行序列化.这不是一个真正的问题,因为我可以boost::archive::binary_oarchive在需要时打开并序列化对象.我很快注意到boost是通过内存地址执行对象跟踪,所以第一个问题是共享相同内存地址的不同对象被保存为同一个对象.这可以通过在所需的派生类中使用以下宏来修复:

BOOST_CLASS_TRACKING(className, boost::serialization::track_never)

这样可以正常工作,但同样,当基类不是抽象类时,基类没有正确序列化.在以下示例中,基类序列化方法仅使用第一个对象调用一次.在下文中,boost假定此对象之前已被序列化,尽管该对象具有不同的类型.

#include <iostream>
#include <fstream>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/list.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/archive/archive_exception.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>

using namespace std;

class AClass{
public:
    AClass(){}
    virtual ~AClass(){}
private:
    double a;
    double b;
    //virtual void virtualMethod() = 0;
private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & a;
        ar & b;
        cout << "A" << endl;
    }
};
//BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass)
//BOOST_CLASS_TRACKING(AClass, boost::serialization::track_never)

class BClass : public AClass{
public:
    BClass(){}
    virtual ~BClass(){}
private:
    double c;
    double d;
    virtual void virtualMethod(){};
private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & boost::serialization::base_object<AClass>(*this);
        ar & c;
        ar & d;
        cout << "B" << endl;
    }
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(BClass)
BOOST_CLASS_TRACKING(BClass, boost::serialization::track_never)


class CClass : public AClass{
public:
    CClass(){}
    virtual ~CClass(){}
private:
    double c;
    double d;
    virtual void virtualMethod(){};
private:
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & boost::serialization::base_object<AClass>(*this);
        ar & c;
        ar & d;
        cout << "C" << endl;
    }
};
// define export to be able to serialize through base class pointer
BOOST_CLASS_EXPORT(CClass)
BOOST_CLASS_TRACKING(CClass, boost::serialization::track_never)

int main() {
    cout << "Serializing...." << endl;
    {
        ofstream ofs("serialization.dat");
        boost::archive::binary_oarchive oa(ofs);
        for(int i=0;i<5;i++)
        {
            AClass* baseClassPointer = new BClass();
            // serialize object through base pointer
            oa << baseClassPointer;
            // free the pointer so next allocation can reuse memory address
            delete baseClassPointer;
        }

        for(int i=0;i<5;i++)
        {
            AClass* baseClassPointer = new CClass();
            // serialize object through base pointer
            oa << baseClassPointer;
            // free the pointer so next allocation can reuse memory address
            delete baseClassPointer;
        }
    }
    getchar();
    cout << "Deserializing..." << endl;
    {
        ifstream ifs("serialization.dat");
        boost::archive::binary_iarchive ia(ifs);
        try{
            while(true){
                AClass* a;
                ia >> a;
                delete a;
            }
        }catch(boost::archive::archive_exception const& e)
        {

        }
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

执行这段代码时,结果如下:

Serializing....
A
B
B
B
B
B
C
C
C
C
C

Deserializing...
A
B
B
B
B
B
C
C
C
C
C
Run Code Online (Sandbox Code Playgroud)

因此,基类只被序列化一次,尽管派生类明确地具有track_never标志.有两种不同的解决方法可以解决此问题.第一个是使用纯虚方法使基类抽象并调用宏BOOST_SERIALIZATION_ASSUME_ABSTRACT(Aclass),第二个是将track_never标志也放在基类中(在代码中注释).

这些解决方案都不符合我的要求,因为我希望将来系统状态的准时序列化,这将需要跟踪特定的DClass扩展A(不是B或C),并且AClass也不应该是抽象的.

任何提示?有没有办法显式调用基类序列化方法,避免基类中的跟踪功能(已在派生类中禁用)?

hr_*_*117 2

在仔细研究了 boost::serialization 之后,我也确信没有简单的解决方案可以满足您的要求。\n正如您已经提到的,序列化的跟踪行为是通过 BOOST_CLASS_TRACKING 在类基上声明的。\n此 const 全局信息将在类 oserializer 的虚拟方法跟踪中进行解释。

\n\n
   virtual bool tracking(const unsigned int /* flags */)\n
Run Code Online (Sandbox Code Playgroud)\n\n

因为这是一个模板类,所以您可以为您的类显式实例化此方法。

\n\n
namespace boost {\nnamespace archive {\nnamespace detail {\n\ntemplate<>\n    virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {\n        return do_your_own_tracking_decision();\n    }\n\n}}}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在您可以尝试使用诸如全局变量之类的东西并不时更改跟踪行为。(例如,取决于将哪个派生类写入存档。)\n这似乎适用于 \xe2\x80\x9cSerializing\xe2\x80\x9c 但 \xe2\x80\x9cDeserializing\xe2\x80\x9c 会抛出异常。\n原因是,每个类的 \xe2\x80\x9ctracking\xe2\x80\x9d 的状态仅写入存档。因此,如果读取 BClass 或 CClass,反序列化总是期望 AClass 的数据(至少如果 AClass 的第一次写入尝试禁用了跟踪)。

\n\n

一种可能的解决方案是在tracking()方法中使用flags参数。\n此参数表示创建存档时使用的标志,默认为\xe2\x80\x9c0\xe2\x80\x9d。

\n\n
binary_oarchive(std::ostream & os, unsigned int flags = 0) \n
Run Code Online (Sandbox Code Playgroud)\n\n

存档标志在 basic_archive.hpp 中声明

\n\n
enum archive_flags {\n    no_header = 1,  // suppress archive header info\n    no_codecvt = 2,  // suppress alteration of codecvt facet\n    no_xml_tag_checking = 4,   // suppress checking of xml tags\n    no_tracking = 8,           // suppress ALL tracking\n    flags_last = 8\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

目前似乎不支持 no_tracking,但您现在可以将此行为添加到跟踪中。

\n\n
template<>\n    virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {\n        return !(f & no_tracking);\n    } \n
Run Code Online (Sandbox Code Playgroud)\n\n

现在您至少可以针对不同的档案决定是否应该跟踪 AClass。

\n\n
 boost::archive::binary_oarchive oa_nt(ofs, boost::archive::archive_flags::no_tracking);\n
Run Code Online (Sandbox Code Playgroud)\n\n

这就是你的例子中的变化。

\n\n
int main() {\n    cout << "Serializing...." << endl;\n    {\n        ofstream ofs("serialization1.dat");\n        boost::archive::binary_oarchive oa_nt(ofs, boost::archive::archive_flags::no_tracking);\n        //boost::archive::binary_oarchive oa(ofs);\n        for(int i=0;i<5;i++)\n        {\n            AClass* baseClassPointer = new BClass();\n            // serialize object through base pointer\n            oa_nt << baseClassPointer;\n            // free the pointer so next allocation can reuse memory address\n            delete baseClassPointer;\n        }\n\n        ofstream ofs2("serialization2.dat");\n        boost::archive::binary_oarchive oa(ofs2);\n        //boost::archive::binary_oarchive oa(ofs);\n\n        for(int i=0;i<5;i++)\n        {\n            AClass* baseClassPointer = new CClass();\n            // serialize object through base pointer\n            oa << baseClassPointer;\n            // free the pointer so next allocation can reuse memory address\n            delete baseClassPointer;\n        }\n    }\n    getchar();\n    cout << "Deserializing..." << endl;\n    {\n        ifstream ifs("serialization1.dat");\n        boost::archive::binary_iarchive ia(ifs);\n        try{\n            while(true){\n                AClass* a;\n                ia >> a;\n                delete a;\n            }\n        }catch(boost::archive::archive_exception const& e)\n        {\n\n        }\n\n        ifstream ifs2("serialization2.dat");\n        boost::archive::binary_iarchive ia2(ifs2);\n        try{\n            while(true){\n                AClass* a;\n                ia2 >> a;\n                delete a;\n            }\n        }catch(boost::archive::archive_exception const& e)\n        {\n\n        }\n\n    }\n    return 0;\n}\n\n\nnamespace boost {\nnamespace archive {\nnamespace detail {\n\ntemplate<>\n    virtual bool oserializer<class binary_oarchive, class AClass >::tracking(const unsigned int f /* flags */) const {\n        return !(f & no_tracking);\n    }\n\n}}}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这可能仍然不是您正在寻找的。还有很多方法可以通过自己的实现进行调整。或者你必须派生你自己的存档类。

\n