我正在调查使用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();
}
答案 0 :(得分:4)
因此,您希望将之前序列化的own_string
反序列化,就好像它是std::string
一样。
默认情况下,对于序列化的每个类,类信息都会写入存档。此信息包括版本号,实施级别和跟踪行为。这是必要的,以便即使后续版本的程序更改某个类的某些当前特征值,也可以正确地反序列化存档。此数据的空间开销很小。由于必须检查每个类以查看它是否已将其类信息包含在归档中,因此存在一些运行时开销。在某些情况下,即使这可能被认为太多了。通过将实现级别类特征设置为: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)
请注意,该示例会消除许多其他噪音/开销源。