【问题标题】:Custom boost serialization input archive fails only with vectors (boost::archive::archive_exception)自定义 boost 序列化输入存档仅使用向量失败 (boost::archive::archive_exception)
【发布时间】:2021-05-03 06:13:02
【问题描述】:

我正在使用 boost 序列化,并且我编写了一个自定义输入存档,它源自 boost 的二进制输入存档。事件虽然这个类现在与 boost 的 binary_iarchive 完全相同,但在反序列化基本类型的 std::vectors 时,我得到了 boost::archive::archive_exception。我没有遇到任何序列化问题,例如std::string 或 std::vector<std::string>。

这里是完整的代码:

InputArchive 实现:

#ifndef __INPUT_ARCHIVE_H
#define __INPUT_ARCHIVE_H

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/impl/basic_binary_iprimitive.ipp>
#include <boost/archive/impl/basic_binary_iarchive.ipp>

class InputArchive;

using iarchive = boost::archive::binary_iarchive_impl<
    InputArchive,
    std::istream::char_type,
    std::istream::traits_type>;

class InputArchive : public iarchive {

    friend class boost::archive::detail::interface_iarchive<InputArchive>;
    friend class boost::archive::basic_binary_iarchive<InputArchive>;
    friend class boost::archive::load_access;

    public:

    template<typename ... Args>
    InputArchive(Args&& ... args)
    : iarchive(std::forward<Args>(args)..., boost::archive::archive_flags::no_header)
    {}

};

#endif

虚拟类,用于测试目的:

#ifndef __DUMMY_PRODUCT_H
#define __DUMMY_PRODUCT_H

#include <vector>
#include <boost/serialization/vector.hpp>
#include <string>
#include <boost/serialization/string.hpp>

struct dummy_product {
    std::vector<char> data;

    template<typename A>
    void serialize(A& ar, const unsigned int version) {
        ar & data;
    }

};

#endif

以及使用存档的示例代码:

#include <boost/archive/binary_oarchive.hpp>
#include "InputArchive.hpp"
#include "DummyProduct.hpp"
#include <string>
#include <sstream>
#include <iostream>

int main() {

    dummy_product p;
    p.data.resize(16);

    std::string buffer;
    {
        std::stringstream ss_value;
        boost::archive::binary_oarchive oa(ss_value, boost::archive::archive_flags::no_header);
        oa << p;
        buffer = ss_value.str();
    }
    for(auto i : buffer) {
        std::cout << (int)i << " ";
    }
    std::cout << std::endl;

    {
        std::stringstream ss_value(buffer);
        //boost::archive::binary_iarchive ia(ss_value, boost::archive::archive_flags::no_header);
        InputArchive ia(ss_value);
        ia >> p;
    }

}

此代码引发以下异常:

terminate called after throwing an instance of 'boost::archive::archive_exception'
  what():  input stream error

如您所见,InputArchive 只是从适当的boost::archive::binary_iarchive_impl 继承而来,仅此而已。如果我将 InputArchive 替换为 boost::archive::binary_iarchive,则没有问题。如果我在dummy_product 中使用std::string 而不是std::vector&lt;char&gt; 也没有问题。异常似乎只发生在原始类型的std::vector。

知道这个问题是从哪里来的吗? 我正在使用 boost 1.75.0 和 gcc 8.3.0。

【问题讨论】:

    标签: c++ serialization boost


    【解决方案1】:

    缺少两个主要的东西:

    1. Q. 如您所见,InputArchive 只是从适当的 boost::archive::binary_iarchive_impl 继承而来,仅此而已。

      答。 是的,但不是以适当的方式。您的构造函数忘记初始化存档。只需转发到基类构造函数就完成了一半的工作:

      enum {
          forced_flags = boost::archive::archive_flags::no_header
      };
      
      InputArchive(std::istream & is, unsigned int flags = 0) :
          iarchive(is, flags | forced_flags)
      {
          init(flags | forced_flags);
      }
      InputArchive(std::streambuf & bsb, unsigned int flags = 0) :
          iarchive(bsb, flags | forced_flags)
      {
          init(flags | forced_flags);
      }
      
    2. 接下来,对允许按位二进制输出的存档的原始类型进行了优化。你的就是其中之一,所以你需要告诉 Boost:

      BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(InputArchive)
      
    3. 要非常酷,您可能还希望正确地为您的存档类型注册的多态类型提供类型信息。如果你这样做了,你也想告诉 Boost:

      BOOST_SERIALIZATION_REGISTER_ARCHIVE(InputArchive)
      

    松散的笔记:

    • 我不确定friends 的用途。这可能是我的性格缺陷
    • 考虑将事物移入命名空间,例如::iarchive 不会无缘无故地污染全局命名空间。 [另外,考虑只使用 CRTP 并在类定义中拼出名称。]

    现在可以了

    Live On Wandbox

    #ifndef __INPUT_ARCHIVE_H
    #define __INPUT_ARCHIVE_H
    
    #include <boost/archive/binary_iarchive.hpp>
    #include <boost/archive/impl/basic_binary_iprimitive.ipp>
    #include <boost/archive/impl/basic_binary_iarchive.ipp>
    
    class InputArchive;
    
    using iarchive = boost::archive::binary_iarchive_impl<
        InputArchive,
        std::istream::char_type,
        std::istream::traits_type>;
    
    class InputArchive : public iarchive {
        enum {
            forced_flags = boost::archive::archive_flags::no_header
        };
    
        //friend class boost::archive::detail::interface_iarchive<InputArchive>;
        //friend class boost::archive::basic_binary_iarchive<InputArchive>;
        //friend class boost::archive::load_access;
      public:
        InputArchive(std::istream & is, unsigned int flags = 0) :
            iarchive(is, flags | forced_flags)
        {
            init(flags | forced_flags);
        }
        InputArchive(std::streambuf & bsb, unsigned int flags = 0) :
            iarchive(bsb, flags | forced_flags)
        {
            init(flags | forced_flags);
        }
    };
    
    // required by export
    BOOST_SERIALIZATION_REGISTER_ARCHIVE(InputArchive)
    BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(InputArchive)
    
    #endif
    
    #ifndef __DUMMY_PRODUCT_H
    #define __DUMMY_PRODUCT_H
    
    #include <vector>
    #include <boost/serialization/vector.hpp>
    #include <string>
    #include <boost/serialization/string.hpp>
    
    struct dummy_product {
        std::vector<char> data;
    
        template<typename A>
        void serialize(A& ar, unsigned) {
            ar & data;
        }
    
    };
    
    #endif
    
    #include <boost/archive/binary_oarchive.hpp>
    //#include "InputArchive.hpp"
    //#include "DummyProduct.hpp"
    #include <string>
    #include <sstream>
    #include <iostream>
    #include <iomanip>
    
    int main() {
    
        dummy_product p;
        p.data.resize(16);
    
        std::string buffer;
        {
            std::stringstream ss_value;
            boost::archive::binary_oarchive oa(ss_value, boost::archive::archive_flags::no_header);
            oa << p;
            buffer = ss_value.str();
        }
        for(int i : buffer) {
            std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)i << " ";
        }
        std::cout << std::endl;
    
        {
            std::stringstream ss_value(buffer);
            // boost::archive::binary_iarchive ia(ss_value, boost::archive::archive_flags::no_header);
            InputArchive ia(ss_value);
            ia >> p;
        }
    
    }
    

    打印

    00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
    

    【讨论】:

    • 谢谢,这行得通!如果你想完成你的答案:如果 InputArchive 在命名空间中(我在我发布的代码中删除了命名空间),那么应该在命名空间之外调用两个 boost 宏(并传递 ns::InputArchive,其中 ns是命名空间)。
    猜你喜欢
    • 1970-01-01
    • 2016-01-23
    • 2012-05-28
    • 1970-01-01
    • 1970-01-01
    • 2019-11-21
    • 1970-01-01
    • 2017-09-24
    • 1970-01-01
    相关资源
    最近更新 更多