自定义 boost 序列化输入存档仅对向量失败 (boost::archive::archive_exception)

Custom boost serialization input archive fails only with vectors (boost::archive::archive_exception)

我正在使用 boost 序列化,我已经编写了一个自定义输入存档,它派生自 boost 的二进制输入存档。尽管此 class 目前与 boost 的 binary_iarchive 完全相同,但在反序列化原始类型的 std::vector 时我得到了 boost::archive::archive_exception。我没有遇到任何序列化问题,例如std::stringstd::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

虚拟 class,用于测试目的:

#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<char> 也没有问题。异常似乎只发生在 std::vector 原始类型中。

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

缺少两个主要的东西:

  1. Q. As you can see, the InputArchive simply inherits from the appropriate boost::archive::binary_iarchive_impl and does nothing more.

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

    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 并在 class 定义中拼出名称。]

现在可以使用了

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