尝试使用boost :: serialization通过boos :: asio套接字发送派生类

尝试使用boost :: serialization通过boos :: asio套接字发送派生类

问题描述:

我正在尝试使用UDP通过boost :: asio套接字发送一个对象,该对象是派​​生类的实例.

I'm trying to send an object that is an instance of a derived class through a boost::asio socket using UDP.

假设子类是PacketA,基类是Packet.

Let's say the child class is PacketA with the base class being the Packet.

我能够在客户端程序中序列化PacketA,但是每当我尝试在服务器中反序列化PacketA时,它将引发以下错误:

I'm able to serialize PacketA in the client program but whenever I try to deserialize it in the server it throws the following error:

抛出'boost :: archive :: archive_exception'实例后调用

terminatewhat():未注册的课程

terminate called after throwing an instance of 'boost::archive::archive_exception' what(): unregistered class

要尝试解决此问题,我在PacketA cpp文件中添加了宏 BOOST_CLASS_EXPORT_IMPLEMENT ,并在头文件中添加了 BOOST_CLASS_EXPORT_KEY ,而在Packet类中,我没有添加任何宏宏,但仍然无法正常工作.由于提升文档.我还尝试使用 register_type()函数注册子类,但是我也没有成功,解决方案似乎比宏更糟糕.

To try and fix this I added the macros BOOST_CLASS_EXPORT_IMPLEMENT in the PacketA cpp file and the BOOST_CLASS_EXPORT_KEY in the header file while in the Packet class I didn't add any macro, but it still doesn't work. I added these macros because of this section of the boost docs. I also tried to use the register_type() function to register the child classes but I wasn't successful either and the solutions seems to be worse than the macros.

我犯了任何明显的错误还是我错误地使用了API?

Is there any obvious mistake that I'm making or am I using the API wrongly?

代码:

反序列化:

        udp::endpoint senderEndPoint;
        char buffer[MAX_PACKET_SIZE] = {"\n"};
        int bytes = socket->receive_from(boost::asio::buffer(buffer, MAX_PACKET_SIZE), senderEndPoint, 0,error);
   
        std::stringstream stringStream(buffer);
        boost::archive::text_iarchive ia{stringStream};
        Packet *packet; //<-It throws the exception in this line but If I switch this pointer to 
                        //PacketA it works fine but the idea is to deserialize multiple child 
                        //packets that came from the sockets.
        ia & packet; 
        packet->bytes = 0;
        packet->senderEndPoint = senderEndPoint;

Packet.cpp:

Packet.cpp:

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include "Packet.hpp"
template<class Archive>
void Packet::serialize(Archive &ar, unsigned int version) {
  //I didnt add any code in here since I don't really need to serialize any information just the child packets
}

template void Packet::serialize(boost::archive::text_iarchive &arch, const unsigned int version);

template void Packet::serialize(boost::archive::text_oarchive &arch, const unsigned int version);

Packet.hpp:

Packet.hpp:

#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include <boost/asio/ip/udp.hpp>

using PacketType = std::string;

class Packet {
public:
    friend class boost::serialization::access;

    /*Some variables and functions from packet*/

    template<class Archive>
    void serialize(Archive &, unsigned int version);

};

PacketA.cpp:

PacketA.cpp:

#include "PacketA.hpp"
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>

/*Some other functions*/

template<class Archive>
void PacketA::serialize(Archive &ar, unsigned int version) {
    ar & boost::serialization::base_object<Packet>(*this);
    ar & boost::serialization::make_nvp("PacketType", packetType);
}

BOOST_CLASS_EXPORT_IMPLEMENT(PacketA)

PacketA.hpp:

PacketA.hpp:

#include <boost/serialization/export.hpp>
#include "../Packet.hpp"

class PacketA : public Packet {
public:
    PacketType packetType = "PacketA";

    friend class boost::serialization::access;

    /*Some functions*/

    template<class Archive>
    void serialize(Archive &ar, unsigned int version);
};

BOOST_CLASS_EXPORT_KEY(PacketA)

要序列化我正在使用此功能的所有数据包:

To serialize all the packets I'm using this function:

std::stringstream foo::serializePacket(Packet *packet) { //<-Here the *packet could be any 
                                                         //packet child
    std::stringstream ss;
    boost::archive::text_oarchive oa{ss};
    oa & packet;
    return ss;
}

您的注册实现无法看到输入档案定义,因为PacketA.cpp无法包括:

You registration implementation cannot see the input archive definition because PacketA.cpp fails to include:

#include <boost/archive/text_iarchive.hpp>

BOOST_CLASS_EXPORT在包含任何以下内容的同一源模块中存档类标题将实例化序列化所需的代码指示类型的多态指针指向所有那些归档类.如果不包括存档类标题,那么将不会有任何代码被实例化.

BOOST_CLASS_EXPORT in the same source module that includes any of the archive class headers will instantiate code required to serialize polymorphic pointers of the indicated type to the all those archive classes. If no archive class headers are included, then no code will be instantiated.

请注意,此功能的实现要求包含任何档案后,BOOST_CLASS_EXPORT宏就会出现要为其实例化代码的类头.

Note that the implemenation of this functionality requires that the BOOST_CLASS_EXPORT macro appear after the inclusion of any archive class headers for which code is to be instantiated.

附加说明

  • 文件 Packet.hpp

 #include <boost/serialization/access.hpp>
 #include <boost/serialization/export.hpp>
 #include <boost/asio/ip/udp.hpp>
 #include <string>

 using PacketType = std::string;

 class Packet {
 public:
     virtual ~Packet() = default;
     friend class boost::serialization::access;

     /*Some variables and functions from packet*/
     int bytes = 0;
     boost::asio::ip::udp::endpoint senderEndPoint;

     template<class Archive>
     void serialize(Archive & /*ar*/, unsigned version);
 };

  • 文件 Packet.cpp

     #include <boost/archive/text_iarchive.hpp>
     #include <boost/archive/text_oarchive.hpp>
     #include <boost/serialization/string.hpp>
     #include "Packet.hpp"
     template <class Archive> void Packet::serialize(Archive& /*ar*/, unsigned /*version*/)
     {
         // I didnt add any code in here since I don't really need to serialize any
         // information just the child packets
     }
    
     template void Packet::serialize(
         boost::archive::text_iarchive& arch, const unsigned int version);
    
     template void Packet::serialize(
         boost::archive::text_oarchive& arch, const unsigned int version);
    

  • 文件 PacketA.hpp

     #include <boost/serialization/export.hpp>
     #include "Packet.hpp"
    
     #define DECLARE_PACKET(Name)                                                   \
         struct Name : Packet {                                                     \
             PacketType packetType = #Name;                                         \
             /*Some functions*/                                                     \
                                                                                    \
           private:                                                                 \
             friend class boost::serialization::access;                             \
             template <class Archive>                                               \
             void serialize(Archive& ar, unsigned int version);                     \
         };                                                                         \
                                                                                    \
         BOOST_CLASS_EXPORT_KEY(Name)
    
     DECLARE_PACKET(PacketA)
     DECLARE_PACKET(PacketB)
     DECLARE_PACKET(PacketC)
     DECLARE_PACKET(PacketD)
     DECLARE_PACKET(PacketE)
    

  • 文件 PacketA.cpp

     #include "PacketA.hpp"
     #include <boost/archive/text_oarchive.hpp>
     #include <boost/archive/text_iarchive.hpp>
     #include <boost/serialization/base_object.hpp>
    
     #define IMPLEMENT_PACKET(Name)                                                 \
         /*Some other functions*/                                                   \
                                                                                    \
         template <class Archive>                                                   \
         void Name::serialize(Archive& ar, unsigned /*version*/)                    \
         {                                                                          \
             ar& boost::serialization::base_object<Packet>(*this);                  \
             ar& boost::serialization::make_nvp("PacketType", packetType);          \
         }                                                                          \
                                                                                    \
         BOOST_CLASS_EXPORT_IMPLEMENT(Name)
    
    
     IMPLEMENT_PACKET(PacketA)
     IMPLEMENT_PACKET(PacketB)
     IMPLEMENT_PACKET(PacketC)
     IMPLEMENT_PACKET(PacketD)
     IMPLEMENT_PACKET(PacketE)
    

  • 文件 test.cpp

     #include <boost/asio.hpp>
     #include <iostream>
     #include <iomanip>
     using boost::asio::ip::udp;
    
     #include "PacketA.hpp"
     #include <boost/archive/text_iarchive.hpp>
     #include <boost/archive/text_oarchive.hpp>
     #include <boost/core/demangle.hpp> // for test output
    
     static constexpr size_t MAX_PACKET_SIZE = 1024;
    
     std::unique_ptr<Packet> receive_packet(uint16_t port) {
         boost::asio::io_context io;
         udp::endpoint senderEndPoint;
         auto socket = std::make_unique<udp::socket>(io, udp::endpoint { {}, port });
    
         std::array<char, MAX_PACKET_SIZE> buffer {'\0'}; // fill with NULs
         boost::system::error_code error;
         int bytes = 0 = socket->receive_from(boost::asio::buffer(buffer, MAX_PACKET_SIZE), senderEndPoint, 0,error);
    
         Packet* packet = nullptr;
    
         if (!error) {
             {
                 std::stringstream stringStream(std::string(buffer.data(), bytes));
                 boost::archive::text_iarchive ia{stringStream};
                 ia & packet;
             }
    
             packet->bytes = 0;
             packet->senderEndPoint = senderEndPoint;
         }
    
         return std::unique_ptr<Packet>(packet); // take ownership
     }
    
     struct foo {
         static std::stringstream serializePacket(Packet* packet);
     };
    
     std::stringstream foo::serializePacket(Packet* packet)
     { //<-Here the *packet could be any packet child
         std::stringstream ss;
         boost::archive::text_oarchive oa { ss };
         oa& packet;
         return ss;
     }
    
     template <typename Type>
     void send() {
         auto request = std::make_unique<Type>();
         auto msg = foo::serializePacket(request.get()).str();
    
         boost::asio::system_executor ex;
         udp::socket s { ex };
         s.open(udp::v4());
         s.send_to(boost::asio::buffer(msg), { {}, 9977 });
     }
    
     template <typename Type>
     void test_roundtrip() {
         auto fut = std::async(std::launch::async, receive_packet, 9977);
    
         std::this_thread::yield(); // be reasonably sure the read started
         send<Type>();
    
         if (auto p = fut.get()) {
             std::cout << "Deserialized a "
                       << boost::core::demangle(typeid(*p).name()) << " packet"
                       << std::endl;
         }
     }
    
     int main() {
         test_roundtrip<PacketA>();
         test_roundtrip<PacketB>();
         test_roundtrip<PacketC>();
         test_roundtrip<PacketD>();
         test_roundtrip<PacketE>();
     }
    

  • 打印

    Deserialized a PacketA packet
    Deserialized a PacketB packet
    Deserialized a PacketC packet
    Deserialized a PacketD packet
    Deserialized a PacketE packet