序列化多态接口

时间:2018-09-29 10:19:56

标签: c++ boost boost-serialization

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

在这里,我找到了这个问题,似乎可以满足我的需要: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,但是没有成功,我只是遇到了很多错误。

如何从实现我的序列化方法的类的接口实现序列化?

1 个答案:

答案 0 :(得分:0)

您正在混合动态多态性(虚拟)和静态多态性(通用模板功能)。

那将是棘手的。尤其是在这种情况下,我认为您需要确保在实例化类导出机器时,除了多态类型之外,其他具体的归档类型均不可见。由于POI可能位于转换单元(TU)的末尾,因此您可能必须分离导出KEY / IMPLEMENTATION宏,并将IMPLEMENTATION位放在单独的TU中。

以下是经过编译的概念证明: Live On Wandbox

警告 该代码已损坏!

问题是它巧妙地破坏了Boost序列化对多态序列化类型的支持。

最重要的是,base_object解析器将被无意中重定向到最派生类的do_serialize实现,从而使最派生的do_serialize运行多次,并且所有的基类序列化都不会运行。

因此,要使其真正起作用,您需要解决它,并将所有序列化移到基类中。现在,您必须手动处理注册转换和注册功能,因为除非最终要在输出中重复所有详细信息,否则无法使用base_object

Live On Wandbox

  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)
    
  2. 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)
    
  3. 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