Повысить десериализацию производного класса до указателя на базовый класс

Пожалуйста, помогите мне десериализовать производный класс в указатель на базовый класс. Я прилагаю полный пример исходного кода.

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. Затем эта библиотека используется в двух подпроектах, которые имеют доступ к заголовкам и имеют связанную библиотеку. Они используют эти классы для связи друг с другом, они отправляют сериализованные объекты по сети.

Почему я не могу десериализовать производный класс в указатель базового класса?
Я использую Boost 1.51 и VC11.

2

Решение

Вы, вероятно, получаете input_stream_error в вашем демо и unregistered_class исключение при использовании вашей библиотеки. Это вызвано тем, как Boost автоматически регистрирует классы.

Похоже, что процесс автоматической регистрации запутывается при сериализации производного объекта и десериализации его базы, несмотря на использование макросов BOOST_CLASS_EXPORT *.

Однако вы можете явно зарегистрировать классы, прежде чем выполнять какие-либо операции ввода-вывода в архиве:

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

Используйте тот же порядок регистрации при сериализации. Это делает экспорт макросов излишним.

1

Другие решения

Проблемы:

Вот две основные вещи, которые я нашел привередливыми и недостаточно документированными в Boost :: serialization, которые вызвали у меня следующие проблемы:

  1. Сериализация / десериализация объектов в стеке, смешанных с объектами в куче. Например, если вы сериализуете из объекта в стеке, то попробуйте десериализовать указатель (например, вызвать ваши load_construct_data<>) может возникнуть исключение. То же самое с обратным сценарием.
  2. Неправильно связан ваш экспорт. Если вы создаете шаблоны / классы сериализации и помещаете их, например, в .lib, кажется, что экспорты могут быть неправильно связаны в / выставлены. Это касается связывания и последующего использования из общего объекта / DLL.

Решения:

Для # 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 с вашими модулями напрямую. Другими словами, у вас будет .cpp с кучей BOOST_CLASS_EXPORT_IMPLEMENT ():

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);
4

По вопросам рекламы [email protected]