Сериализация Полиморфного Интерфейса - PullRequest
0 голосов
/ 29 сентября 2018

Я хочу сериализовать полиморфный класс из связанного с ним интерфейса.

Вот что я нашел в этом вопросе, который, кажется, делает то, что мне нужно: Как создать интерфейс для сериализациив 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 on NetworkMessageLogin, но безуспешно, у меня просто была куча ошибок.

Как я могу добиться сериализации из интерфейса класса, реализующего мой метод сериализации?

1 Ответ

0 голосов
/ 30 сентября 2018

Вы смешиваете динамический полиморфизм (виртуальные) и статический полиморфизм (универсальные функции шаблонов).

Это будет сложно.В частности, в этом случае я думаю, что вам нужно гарантировать, что ни один конкретный тип архива, кроме полиморфных, не будет виден во время создания механизмов экспорта классов.Поскольку 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
...