【问题标题】:Serialize Polymorph Interface序列化多态接口
【发布时间】:2019-03-05 03:54:26
【问题描述】:

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

这是我发现的这个问题,这似乎可以满足我的需要: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

我尝试在NetworkMessageLogin 上使用CLASS_BOOST_EXPORT,但没有成功,我只是遇到了一堆错误。

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

【问题讨论】:

    标签: c++ boost boost-serialization


    【解决方案1】:

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

    这会很棘手。特别是在这种情况下,我认为您需要保证在类导出机器的实例化时,除了多态类型之外,没有任何具体的存档类型可见。由于 POI 可能位于翻译单元 (TU) 的末尾,因此您可能必须将导出 KEY/IMPLEMENTATION 宏分开,并将IMPLEMENTATION 位放在单独的 TU 中。

    这是编译后的概念证明:Live On Wandbox

    警告该代码已损坏!

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

    最重要的是,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
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-25
    • 2012-02-27
    相关资源
    最近更新 更多