将派生的类启用到基类指针

Boost deserialize a derived class to base class pointer

本文关键字:基类 指针 启用 派生      更新时间:2023-10-16

请帮助我将派生的类挑选到基础级指针。我附上完整的源代码示例。

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。

问题

我发现的两件主要内容且对BOOST ::导致我问题的序列化的记录不够记录,如下所示:

  1. 序列化/对堆栈上的对象的序列化与堆上混合的对象。)可能会发生例外。与反向方案相同。
  2. 没有正确链接的导出。例如,如果您创建序列化模板/类并将其放置在.lib中,则似乎可能无法在/暴露中链接导出。这是用于链接并从共享对象/dll中使用。

solutions

对于#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);

在使用库时,您可能会在演示中获得 input_stream_error 。这是由Boost在您的情况下自动注册课程的方式引起的。

尽管使用BOOST_CLASS_EXPORT* MACROS。

但是,您可以在档案上执行任何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);
// ...

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