【问题标题】:Why does an non-intrusive serialization add a 5 byte zero prefix?为什么非侵入式序列化会添加一个 5 字节的零前缀?
【发布时间】:2017-12-02 14:13:49
【问题描述】:

我正在使用 boost::archive 的应用程序中调查从非标准到标准字符串的端口。非标准字符串的(反)序列化以非侵入式风格定义,如下例所示。序列化和反序列化按预期工作,但是当移植的应用程序收到旧消息时,它会因分配错误而崩溃。这是因为在字符串大小之前插入了 5 个字节(全为零)。

是什么导致了这 5 个额外字节的插入?这是某种魔法标记吗?

例子:

#include <iostream>
#include <string>
#include <sstream>
#include <boost/serialization/split_free.hpp>
#include <boost/archive/binary_oarchive.hpp>

struct own_string { // simplified custom string class
    std::string content;
};

namespace boost
{
    namespace serialization
    {
        template<class Archive>
        inline void save(
            Archive & ar,
            const own_string & t,
            const unsigned int /* file_version */)
        {
            size_t size = t.content.size();
            ar << size;
            ar.save_binary(&t.content[0], size);
        }

        template<class Archive>
        inline void load(
            Archive & ar,
            own_string & t,
            const unsigned int /* file_version */)
        {
            size_t size;
            ar >> size;
            t.content.resize(size);
            ar.load_binary(&t.content[0], size);
        }

// split non-intrusive serialization function member into separate
// non intrusive save/load member functions
        template<class Archive>
        inline void serialize(
            Archive & ar,
            own_string & t,
            const unsigned int file_version)
        {
            boost::serialization::split_free(ar, t, file_version);
        }

    } // namespace serialization
} // namespace boost

std::string string_to_hex(const std::string& input)
{
    static const char* const lut = "0123456789ABCDEF";
    size_t len = input.length();

    std::string output;
    output.reserve(2 * len);
    for (size_t i = 0; i < len; ++i)
    {
        const unsigned char c = input[i];
        output.push_back(lut[c >> 4]);
        output.push_back(lut[c & 15]);
    }
    return output;
}

void test_normal_string()
{
    std::stringstream ss;
    boost::archive::binary_oarchive ar{ss};

    std::string test = "";

    std::cout << string_to_hex(ss.str()) << std::endl;
    ar << test;

    //adds 00 00 00 00 00 00 00 00
    std::cout << string_to_hex(ss.str()) << std::endl;
}

void test_own_string()
{
    std::stringstream ss;
    boost::archive::binary_oarchive ar{ss};

    std::string test = "";

    own_string otest{test};
    std::cout << string_to_hex(ss.str()) << std::endl;
    ar << otest;

    //adds 00 00 00 00 00 00 00 00 00 00 00 00 00
    std::cout << string_to_hex(ss.str()) << std::endl;
}

int main()
{
    test_normal_string();
    test_own_string();
}

【问题讨论】:

  • 什么是“旧消息”?关于额外的字节,我猜这只是版本控制数据......
  • 旧消息是使用了自定义字符串的消息。
  • @choeger 只需修正问题措辞即可消除混乱! +1 优秀的独立样本
  • size_t 的大小可能因编译器、编译器版本和平台而异。序列化非固定大小的类型往往会破坏大多数序列化协议。
  • @Yakk 不是这样。追踪这一点实际上很有趣

标签: c++ serialization boost


【解决方案1】:

因此,您希望反序列化以前序列化的own_string,就好像它是std::string

来自boost(1.65.1) doc

默认情况下,对于每个序列化的类,类信息都会写入存档。此信息包括版本号、实施级别和跟踪行为。这是必要的,以便即使程序的后续版本更改了类的某些当前特征值,存档也可以正确反序列化。此数据的空间开销很小。由于必须检查每个类以查看它是否已经将其类信息包含在存档中,因此存在一点运行时开销。在某些情况下,即使这样也可能被认为太多了。这种额外的开销可以通过将实现级别的类特征设置为:boost::serialization::object_serializable 来消除。

现在,可能(*)这是标准类的默认值。事实上,添加

BOOST_CLASS_IMPLEMENTATION(own_string, boost::serialization::object_serializable)

在全局范围内使 test_X_string 结果产生相同的字节。这应该可以解释观察到的额外字节差异。

也就是说,我没有找到任何关于标准类序列化特征的具体保证(其他人可能比我更了解)。

(*) 实际上section about portability of traits settings 提到:

避免此问题的另一种方法是将序列化特征分配给所有原始类型的模板 my_wrapper 的所有特化,以便永远不会保存类信息。 这就是我们为 STL 集合实现序列化所做的工作

所以这可能会给你足够的信心,标准集合(因此包括 std::string)在这种情况下会给出相同的字节。

【讨论】:

    【解决方案2】:

    我认为您要求提供未记录的实施细节。不必有为什么。这是归档格式的一个实现细节。

    这是一个有趣的问题。

    您必须告诉库您不想要您的类型的所有功能(对象跟踪、类型信息、版本控制)。具体来说,这演示了如何实现相同的足迹。

    请注意,您显然失去了您禁用的功能

    Live On Coliru

    #include <iostream>
    #include <string>
    #include <sstream>
    
    struct own_string { // simplified custom string class
        std::string content;
    };
    
    #include <boost/serialization/split_free.hpp>
    #include <boost/serialization/tracking.hpp>
    BOOST_CLASS_IMPLEMENTATION(own_string, boost::serialization::level_type::object_serializable)
    BOOST_CLASS_TRACKING(own_string, boost::serialization::track_never)
    
    //#include <boost/serialization/wrapper.hpp>
    //BOOST_CLASS_IS_WRAPPER(own_string)
    
    #include <boost/serialization/array_wrapper.hpp>
    
    namespace boost
    {
        namespace serialization
        {
            template<class Archive>
            inline void save(
                Archive & ar,
                const own_string & t,
                const unsigned int /* file_version */)
            {
                size_t size = t.content.size();
                ar & size;
                if (size)
                    ar & boost::serialization::make_array(&t.content[0], size);
            }
    
            template<class Archive>
            inline void load(
                Archive & ar,
                own_string & t,
                const unsigned int /* file_version */)
            {
                size_t size;
                ar & size;
                t.content.resize(size);
                if (size)
                    ar & boost::serialization::make_array(&t.content[0], size);
            }
    
            // split non-intrusive serialization function member into separate
            // non intrusive save/load member functions
            template<class Archive>
            inline void serialize(
                Archive & ar,
                own_string & t,
                const unsigned int file_version)
            {
                boost::serialization::split_free(ar, t, file_version);
            }
    
        } // namespace serialization
    } // namespace boost
    
    std::string string_to_hex(const std::string& input)
    {
        static const char* const lut = "0123456789ABCDEF";
        size_t len = input.length();
    
        std::string output;
        output.reserve(2 * len);
        for (size_t i = 0; i < len; ++i)
        {
            const unsigned char c = input[i];
            output.push_back(lut[c >> 4]);
            output.push_back(lut[c & 15]);
        }
        return output;
    }
    
    #include <boost/archive/binary_oarchive.hpp>
    
    void test_normal_string()
    {
        std::stringstream ss;
        {
            boost::archive::binary_oarchive ar{ss, boost::archive::no_header|boost::archive::no_codecvt};
    
            std::string test = "";
    
            //std::cout << string_to_hex(ss.str()) << std::endl;
            ar << test;
        }
    
        //adds 00 00 00 00 00 00 00 00
        std::string bytes = ss.str();
        std::cout << string_to_hex(bytes) << " (" << bytes.size() << " bytes)\n";
    }
    
    void test_own_string()
    {
        std::stringstream ss;
        {
            boost::archive::binary_oarchive ar{ss, boost::archive::no_header|boost::archive::no_codecvt};
    
            own_string otest{""};
            //std::cout << string_to_hex(ss.str()) << std::endl;
            ar << otest;
        }
    
        //adds 00 00 00 00 00 00 00 00 00 00 00 00 00
        std::string bytes = ss.str();
        std::cout << string_to_hex(bytes) << " (" << bytes.size() << " bytes)\n";
    }
    
    int main()
    {
        test_normal_string();
        test_own_string();
    }
    

    打印

    0000000000000000 (8 bytes)
    0000000000000000 (8 bytes)
    

    请注意,该示例消除了许多其他噪声源/开销。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 2011-03-05
      • 1970-01-01
      • 2012-07-17
      相关资源
      最近更新 更多