序列化多态接口 [英] Serialize Polymorph Interface

查看:76
本文介绍了序列化多态接口的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望从其关联的接口序列化一个多态的类.

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

  1. network.h

  1. 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屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆