为什么非侵入式序列化会添加5字节的零前缀?

时间:2017-12-02 14:13:50

标签: c++ serialization boost

我正在调查使用boost :: archive的应用程序中从非标准到标准字符串的端口。非标准字符串的(de-)序列化以非侵入式样式定义,如下例所示。序列化和反序列化按预期工作,但是当移植的应用程序收到旧消息时,它会因错误的分配而崩溃。这是由于在字符串大小之前插入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();
}

2 个答案:

答案 0 :(得分:4)

因此,您希望将之前序列化的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)在这种情况下会给出相同的字节。

答案 1 :(得分:1)

我认为您要求提供无证实施细节。没有必要成为原因。它是存档格式的实现细节。

这是一个有趣的问题。

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

  

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

<强> 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)

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