序列化多态接口 [英] Serialize Polymorph Interface
问题描述
我希望从其关联的接口序列化一个多态的类.
I am looking to serialize a polymorphed class from its associated interface.
Here is what I have found this question, which seems to do what I need to: How to create a interface for serialization in Boost Serialization?
但是,序列化是从类本身而不是接口完成的.到目前为止,我得到了什么:
However the serialization is done from the class itself and not the interface. What I have got so far:
INetworkMessage.hpp
INetworkMessage.hpp
using PolyArchive = boost::variant<
boost::archive::polymorphic_oarchive &,
boost::archive::polymorphic_iarchive&>;
class INetworkMessage {
public:
INetworkMessage() = default;
virtual ~INetworkMessage() = default;
virtual void serialize(PolyArchive ar, unsigned int version) = 0;
};
namespace visitor {
template <typename F> struct wrap_visitor : boost::static_visitor<>
{
wrap_visitor(F const& f) : f_(f) { }
wrap_visitor(F&& f) : f_(std::move(f)) { }
template<typename... T> void operator()(T&&... t) const
{
f_(std::forward<T>(t)...);
}
private:
F f_;
};
template <typename F>
wrap_visitor<F> make_visitor(F&& f)
{
return std::forward<F>(f);
}
}
BOOST_SERIALIZATION_ASSUME_ABSTRACT(INetworkMessage)
NetworkMessage.hpp
NetworkMessage.hpp
class NetworkMessage : public INetworkMessage {
public:
struct Header
{
enum MessageType
{
TYPE_LOGIN,
TYPE_LOGOUT,
TYPE_CONTROL,
TYPE_VOICE
};
unsigned long long int to;
unsigned long long int from;
enum MessageType type;
size_t size;
};
NetworkMessage();
NetworkMessage(const struct NetworkMessage::Header &header);
virtual ~NetworkMessage() = 0;
struct NetworkMessage::Header &getHeader();
virtual void serialize(PolyArchive ar, unsigned int) = 0;
private:
struct Header header;
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(NetworkMessage)
NetworkMessageLogin.hpp
NetworkMessageLogin.hpp
class NetworkMessageLogin : public NetworkMessage {
public:
NetworkMessageLogin();
NetworkMessageLogin(const struct NetworkMessage::Header &header);
~NetworkMessageLogin();
void setId(unsigned long long int id) noexcept;
unsigned long long int getId() const noexcept;
void setName(const std::string &name) noexcept;
const std::string &getName() const noexcept;
virtual void serialize(PolyArchive ar, unsigned int) override;
protected:
unsigned long long int id;
std::string name;
};
这就是我想要做的:
struct NetworkMessage::Header header = { 0, 1, NetworkMessage::Header::TYPE_LOGIN, 4 };
NetworkMessageLogin msg(header);
msg.setId(1245);
msg.setName("Test");
INetworkMessage *interface = new NetworkMessageLogin(msg);
std::stringstream ss;
boost::archive::polymorphic_text_oarchive oa(ss);
oa << interface;
std::cout << "Serial: " << ss.str() << std::endl;
通过这种尝试,我得到一个异常 what():未注册的类-未注册或导出的派生类
.
With this attempt I get an exception what(): unregistered class - derived class not registered or exported
.
我尝试在 NetworkMessageLogin
上使用 CLASS_BOOST_EXPORT
,但是没有成功,我只是遇到了很多错误.
I've tried using the CLASS_BOOST_EXPORT
on NetworkMessageLogin
, but without success, I just had a bunch of errors.
如何从实现我的序列化方法的类的接口实现序列化?
How can I achieve serialization from the interface of a class implementing my serialization method ?
推荐答案
您正在混合动态多态性(虚拟)和静态多态性(通用模板函数).
You're mixing dynamic polymorphism (virtuals) and static polymorphism (generic template functions).
那将是棘手的.特别是在这种情况下,我认为您需要保证在实例化类导出机器时,除了多态类型之外,其他具体的归档类型均不可见.由于POI可能位于转换单元(TU)的末尾,因此您可能必须分离导出KEY/IMPLEMENTATION宏,并将 IMPLEMENTATION
位放在单独的TU中.
That's going to be tricky. In particular in this case I think you need to guarantee that no concrete archive type other than the polymorphic ones are visible at the time of instantiation of the class-export machineries. Because the POI may be at the end of the translation unit (TU), you may have to separate the export KEY/IMPLEMENTATION macros, and put the IMPLEMENTATION
bits in a separate TU.
以下是已编译的概念证明: 在魔盒上直播
Here's a proof of concept that compiled: Live On Wandbox
警告 该代码已损坏!
WARNING That code is broken!
问题在于它巧妙地破坏了Boost序列化对多态序列化类型的支持.
The problem with this is that it sublty breaks Boost Serialization's support for polymorphic serialized types.
最重要的是, base_object
解析器将被无意中重定向到派生最多的类的 do_serialize
实现,从而使派生最多的 do_serialize
运行多次,并且所有基类序列化都不会运行.
Most importantly, the base_object
parsers will be inadvertantly redirected to the do_serialize
implementation of the most-derived class, making it so that the most-derived do_serialize
runs more than once, and all the base-class serialization isn't run.
因此,要使其真正起作用,您需要解决它,并将所有序列化移到基类中.现在,您必须手动处理注册转换和注册功能,因为 base_object
不能使用,除非您希望最终在输出中重复所有详细信息.
So to make it actually work you need to account for it and move all serialization into the base class. Now, you have to manually take care of registering conversion and registration functions, because base_object
cannot be used unless you want to end up with all details repeated inside your output.
在魔盒中直播 >
Live On Wandbox
-
network.h
network.h
#pragma once
#include <boost/archive/polymorphic_oarchive.hpp>
#include <boost/archive/polymorphic_iarchive.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/variant.hpp>
struct INetworkMessage {
virtual ~INetworkMessage() = default;
protected:
using Archive = boost::variant<boost::archive::polymorphic_oarchive&, boost::archive::polymorphic_iarchive&>;
virtual void do_serialize(Archive, unsigned) = 0;
private:
friend class boost::serialization::access;
template<class Ar> void serialize(Ar& ar, unsigned version) {
this->do_serialize(Archive{ar}, version);
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(INetworkMessage)
class NetworkMessage : public INetworkMessage {
public:
struct Header {
enum MessageType { TYPE_LOGIN, TYPE_LOGOUT, TYPE_CONTROL, TYPE_VOICE, TYPE_UNSPECIFIED };
unsigned long long int to = 0;
unsigned long long int from = 0;
enum MessageType type = TYPE_UNSPECIFIED;
std::size_t size = 0;
template<class Ar> void serialize(Ar& ar, unsigned) {
ar & to & from & type & size;
}
};
NetworkMessage() = default;
NetworkMessage(Header const &header) : header(header) {}
NetworkMessage::Header &getHeader();
private:
Header header;
protected:
virtual void do_serialize(Archive ar, unsigned) override {
boost::apply_visitor([=](auto& ar) {
boost::serialization::void_cast_register<NetworkMessage, INetworkMessage>(this, this);
ar & header;
}, ar);
}
};
class NetworkMessageLogin : public NetworkMessage {
public:
NetworkMessageLogin(const NetworkMessage::Header &header = {}) : NetworkMessage(header) {}
void setId(unsigned long long int id) noexcept { this->id = id; }
unsigned long long int getId() const noexcept { return id; }
void setName(const std::string &name) noexcept { this->name = name; }
const std::string& getName() const noexcept { return name; }
protected:
unsigned long long int id;
std::string name;
virtual void do_serialize(Archive ar, unsigned version) override {
boost::apply_visitor([=](auto& ar) {
boost::serialization::void_cast_register<NetworkMessageLogin, NetworkMessage>(this, this);
NetworkMessage::do_serialize(ar, version);
ar & id & name;
}, ar);
}
};
BOOST_CLASS_EXPORT_KEY(INetworkMessage)
BOOST_CLASS_EXPORT_KEY(NetworkMessage)
BOOST_CLASS_EXPORT_KEY(NetworkMessageLogin)
network.cpp
network.cpp
#include "network.h"
#include <boost/serialization/string.hpp>
BOOST_CLASS_EXPORT_IMPLEMENT(INetworkMessage)
BOOST_CLASS_EXPORT_IMPLEMENT(NetworkMessage)
BOOST_CLASS_EXPORT_IMPLEMENT(NetworkMessageLogin)
test.cpp
test.cpp
#include "network.h"
#include <boost/archive/polymorphic_text_oarchive.hpp>
#include <boost/archive/polymorphic_text_iarchive.hpp>
#include <iostream>
INetworkMessage* sample_msg() {
NetworkMessage::Header header { 0, 1, NetworkMessage::Header::TYPE_LOGIN, 4 };
auto msg = new NetworkMessageLogin(header);
msg->setId(1245);
msg->setName("Test");
return msg;
}
int main() {
std::stringstream ss;
{
boost::archive::polymorphic_text_oarchive oa(ss);
INetworkMessage* interface = sample_msg();
oa << interface;
delete interface;
}
std::cout << "Serial: " << ss.str() << std::endl;
{
boost::archive::polymorphic_text_iarchive ia(ss);
INetworkMessage* roundtripped = nullptr;
ia >> roundtripped;
if (auto login = dynamic_cast<NetworkMessageLogin*>(roundtripped)) {
std::cout << "Name: " << login->getName() << "\n";
std::cout << "Id: " << login->getId() << "\n";
}
delete roundtripped;
}
}
以例如
g++ -std=c++14 network.cpp test.cpp -o ./test.exe -lboost_serialization
打印
Serial: 22 serialization::archive 16 0 19 NetworkMessageLogin 1 0
0 0 0 0 1 0 4 1245 4 Test
Name: Test
Id: 1245
这篇关于序列化多态接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!