Boost将派生类反序列化为基类指针

时间:2012-10-26 15:29:08

标签: c++ boost boost-serialization

请帮我将派生类反序列化为基类指针。我附上了完整的源代码示例。

request.hpp (没有配对cpp文件)

#ifndef REQUEST_HPP
#define REQUEST_HPP

#include <memory>
#include <string>

#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>

namespace demo {
namespace common {

        class request {
        public:
            static const int INVALID_ID = -42;

            request() 
                : id_(INVALID_ID), timestamp_(0), source_ip_("unknown") {};

            request(int id, long timestamp, const std::string& source_ip) 
                : id_(id), timestamp_(timestamp), source_ip_(source_ip) {};

            virtual ~request() {};

            int id() const { return id_; }
            long timestamp() const { return timestamp_; }
            std::string source_ip() const { return source_ip_; }



        protected:
            int id_;
            long timestamp_;
            std::string source_ip_;



        private:
            friend class boost::serialization::access;

            template<class Archive>
            void serialize(Archive& ar, const unsigned version) {
                ar & BOOST_SERIALIZATION_NVP(id_);
                ar & BOOST_SERIALIZATION_NVP(timestamp_);
                ar & BOOST_SERIALIZATION_NVP(source_ip_);
            }

        };

        typedef std::shared_ptr<request> request_ptr;

    }
};

#endif

command.hpp (派生类)

#ifndef COMMAND_HPP
#define COMMAND_HPP

#include <memory>
#include <string>

#include <boost/serialization/export.hpp>

#include <demo/common/request.hpp>

namespace demo {
    namespace common {

            class command : public request {
            public:
                command(): name_("untitled") {};
                explicit command(const std::string& name) : name_(name) {};
                virtual ~command() {};

                virtual void execute();

                std::string name() const { return name_; }

            protected:
                std::string name_;

            private:
                friend class boost::serialization::access;

                template<class Archive>
                void serialize(Archive& ar, const unsigned version) {
                    ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(request);
                    ar & BOOST_SERIALIZATION_NVP(name_);
                }

            };

            typedef std::shared_ptr<command> command_ptr;

        }
};

BOOST_CLASS_EXPORT_KEY(demo::common::command)


#endif

command.cpp

#include "command.hpp"
#include <iostream>

BOOST_CLASS_EXPORT_IMPLEMENT(demo::common::command)

namespace demo {
    namespace common {

            void command::execute() {
                std::cout << "  I am '" + name_ +"' and I am executing..." << std::endl;
            }

    }
};

serializer.hpp

#ifndef SERIALIZER_HPP
#define SERIALIZER_HPP

#include <sstream>
#include <string>

/* classes to serialize */
#include <demo/common/request.hpp>
#include <demo/common/command.hpp>

namespace demo {
    namespace common {

        class serializer {
        public:
            serializer() : {};

            template<typename T>
            std::string serialize(const T& t){  
                std::stringstream stream;
                boost::archive::xml_oarchive archive(stream);
                archive << BOOST_SERIALIZATION_NVP(t);
                std::string serialized = stream.str();

                return serialized;
            }


            template<typename T>
            void deserialize(const std::string& serialized, T& t) {
                std::stringstream stream(serialized);
                boost::archive::xml_iarchive archive(stream);
                archive >> BOOST_SERIALIZATION_NVP(t);
            }
        };

    }
}

#endif

样本使用

#include <iostream>

#include <demo/common/serializer.hpp>
#include <demo/common/command.hpp>


using namespace std;
using namespace demo::common;

int main(){
    serializer serializer_;

    command r("123"); // <-- (1) my desired way of declaring
    //request* r = new command("123"); <-- (2) replacing with this makes all work!
    //command* r = new command("123"); <-- (3) replacing with this crashes the app, like (1)
    std::string s = serializer_.serialize(r);
    std::cout << s << std::endl;
    request* rr = nullptr;
    serializer_.deserialize(s, rr); //this throws an exception

    command* rrr = dynamic_cast<command*>(rr);

    rrr->execute();
}

我以为我做了所有需要完成的事情,在任何类导出之前都包含了档案,所有默认构造函数都初始化成员..

请注意,可序列化类和序列化程序被编译为lib文件。然后,该lib用于两个子项目,这些子项目可以访问标题并链接该lib。他们使用这些类相互通信,通过网络发送序列化对象。

为什么我不能将派生类反序列化为基类指针? 我正在使用Boost 1.51和VC11。

2 个答案:

答案 0 :(得分:4)

<强>问题

我发现的两个主要问题很糟糕,而且没有足够的关于引起我问题的Boost :: serialization的文档记录如下:

  1. 堆栈上的对象的序列化/反序列化与堆上的对象混合。例如,如果从堆栈上的对象序列化,则尝试反序列化为指针(例如,调用load_construct_data&lt;&gt; ;)可能发生异常。与反向情况相同。
  2. 没有正确链接您的导出。例如,如果您创建序列化模板/类并将它们放在.lib中,则似乎导出可能未在/ exposed中正确链接。这适用于链接,然后使用共享对象/ DLL。
  3. <强>解决方案

    对于#1,我发现最简单的做法是始终对指针进行序列化/反序列化。甚至堆栈中的对象在序列化时也可以使用临时指针来允许此规则。例如:

    // serialize
    MyObject myobj;
    std::ostringstream oss;
    boost::archive::text_oarchive oa(oss);
    MyObject* myObjPtr = &myObj;
    oa << myObjPtr; // this is different than oa << myObj!!
    std::string serialized = oss.str();
    
    // deserialize
    MyObject* myNewObjPtr;
    std::stringstream iss(serialized);
    boost::archive::text_iarchive ia(iss);
    ia >> myNewObjPtr; // invokes new, don't forget to delete (or use smart ptrs!!!)
    

    对于#2,只需创建一个包含所有导出的.cpp文件。将此CPP直接链接到您的模块中。换句话说,你将拥有一堆带有BOOST_CLASS_EXPORT_IMPLEMENT()的.cpp:

    BOOST_CLASS_EXPORT_IMPLEMENT(MyObject);
    // ...
    

    更完整的示例
    下面是一个更完整的示例,显示了一些使用非侵入式模板的序列化技巧。侵入式成员方法将非常相似:

    <强> MyObject.h

    // Can be broken into MyObject.h, MyObject.cpp, MyObjectSerialization.h for example as well. 
    // This stuff can live in your .lib
    #include <boost/serialization/export.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/archive/text_iarchive.hpp>
    // assume this class contains GetSomeMember() returning SomeMemberType
    class MyObject { /* ... */ };
    BOOST_CLASS_EXPORT_KEY(MyObject);
    namespace boost { namespace serialization {
      template<class Archive>
          void serialize(Archive& ar, MyObject& myObj, const unsigned int version)
      {
          ar & myObj.m_someMember;
      }
    
      template<class Archive>
          inline void save_construct_data(Archive& ar, const MyObject* myObj, const unsigned int version)
      {
        ar & boost::serialization::make_nvp("SomeMemberType", static_cast<const SomeMemberType&>(myObj->GetSomeMember()));
      }
    
      template<class Archive>
      inline void load_construct_data(Archive& ar, MyObject* myObj, const unsigned int version)
      {
          SomeMemberType t;
          ar & boost::serialization::make_nvp("SomeMemberType", t);
          ::new(myObj)MyObject(t);
      }
    } } // end boost::serialization ns
    

    <强> MyObjectExports.cpp

    // This file must be explicitly linked into your module(s) that use serialization.
    // This means your executable or shared module/DLLs
    #include <boost/serialization/export.hpp>
    #include <boost/archive/text_oarchive.hpp>
    #include <boost/archive/text_iarchive.hpp>
    #include "MyObject.h"
    
    BOOST_CLASS_EXPORT_IMPLEMENT(MyObject);
    

答案 1 :(得分:1)

您在演示时可能会收到 input_stream_error ,而在使用您的库时, unregistered_class 异常。这是由于boost在你的情况下自动注册类的方式。

尽管使用了BOOST_CLASS_EXPORT *宏,但在序列化派生对象并反序列化到其基础时,自动注册过程似乎会混淆。

但是,您可以在对存档执行任何i / o操作之前明确注册类:

// ...
boost::archive::xml_iarchive archive(stream);
// register the class(es) with the archive
archive.template register_type<command>();
archive >> BOOST_SERIALIZATION_NVP(t);
// ...

序列化时使用相同的注册顺序。这使得导出宏变得多余。