我希望从其关联的接口序列化一个多态的类。
在这里,我找到了这个问题,似乎可以满足我的需要:How to create a interface for serialization in Boost Serialization?
但是序列化是从类本身而不是接口完成的。 到目前为止,我得到了什么:
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
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
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(): unregistered class - derived class not registered or exported
。
我尝试在CLASS_BOOST_EXPORT
上使用NetworkMessageLogin
,但是没有成功,我只是遇到了很多错误。
如何从实现我的序列化方法的类的接口实现序列化?
答案 0 :(得分:0)
您正在混合动态多态性(虚拟)和静态多态性(通用模板功能)。
那将是棘手的。尤其是在这种情况下,我认为您需要确保在实例化类导出机器时,除了多态类型之外,其他具体的归档类型均不可见。由于POI可能位于转换单元(TU)的末尾,因此您可能必须分离导出KEY / IMPLEMENTATION宏,并将IMPLEMENTATION
位放在单独的TU中。
以下是经过编译的概念证明: Live On Wandbox
警告 该代码已损坏!
问题是它巧妙地破坏了Boost序列化对多态序列化类型的支持。
最重要的是,base_object
解析器将被无意中重定向到最派生类的do_serialize
实现,从而使最派生的do_serialize
运行多次,并且所有的基类序列化都不会运行。
因此,要使其真正起作用,您需要解决它,并将所有序列化移到基类中。现在,您必须手动处理注册转换和注册功能,因为除非最终要在输出中重复所有详细信息,否则无法使用base_object
。
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
#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
#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