我正在尝试使用 boost 序列化以下字符串类型:
using CustomString = std::basic_string<char, std::char_traits<char>, CustomAllocator<char>>;
1. 由于该类型与 std::string 的定义相同,除了分配器之外,我的第一个方法是执行与 boost 对 std::string 所做的相同的操作。
我在 using 语句之后添加了
BOOST_CLASS_IMPLEMENTATION(CustomString, boost::serialization::primitive_type)
。
这似乎工作正常,直到我尝试序列化其中有空格的字符串。
序列化为 boost::archive::text_oarchive 并将分隔符设置为空格。因此,反序列化后,仅从存档中读取字符串的第一部分,例如我将“Hello World”写入存档,但反序列化后只得到“Hello”。 对于 std::string boost 在文本之前添加了一个长度字段。自定义字符串并非如此。
长度条目的来源是 boost rchive\impl ext_oarchive_impl.ipp
text_oarchive_impl<Archive>::save(const std::string &s)
{
const std::size_t size = s.size();
*this->This() << size;
this->This()->newtoken();
os << s;
}
我在回答以下问题时发现了同样的问题:Can boost::container::strings be serialized using boost serialization?
我扩展了示例以使问题变得可见: https://coliru.stacked-crooked.com/a/84b2cb162d58534a
2. 现在我编写了自己的序列化函数
namespace boost
{
namespace serialization
{
template<class Archive>
inline void serialize(Archive& ar, CustomString & s, const unsigned int file_version)
{
boost::serialization::split_free(ar, s, file_version);
}
template<typename Archive>
inline void save(
Archive& ar, const CustomString & s, const unsigned int /* file_version */
)
{
ar << s.size();
for (size_t i = 0; i < s.size(); i++)
{
ar << s.c_str()[i];
}
}
template<typename Archive>
inline void load(
Archive& ar, CustomString & s, const unsigned int /* file_version */
)
{
size_t size;
ar >> size;
char c;
for (size_t i=0; i < size;i++)
{
ar >> c;
s.push_back(c);
}
}
} // namespace serialization
} // namespace boost
此代码确实有效,但会生成相当长的存档,因为字符被编码为十进制值:
std::string 存档:
22 serialization::archive 19 11 Hello World
自定义字符串存档:
22 serialization::archive 19 0 0 11 72 101 108 108 111 32 87 111 114 108 100
我将不胜感激有关如何改进这两种方法之一的提示。 谢谢!
是的,我仍然很惊讶对
{std,boost::container}::basic_string
的支持并不是开箱即用的。另外,很惊讶没有人注意到旧答案被破坏了。
我同意自定义序列化是更安全的选择。它可以更简单、更轻量一点:
#include <boost/algorithm/string.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/binary_object.hpp>
#include <iomanip>
#include <iostream>
namespace Funky {
template <typename T> struct FunkyAlloc : std::allocator<T> {
using std::allocator<T>::allocator;
using std::allocator<T>::operator=;
};
using String = std::basic_string<char, std::char_traits<char>, FunkyAlloc<char>>;
template <typename Ar, typename TCh, typename TChT, typename Allocator>
void serialize(Ar& ar, std::basic_string<TCh, TChT, Allocator>& s, unsigned) {
size_t n = s.length();
ar& n;
if (Ar::is_loading::value)
s.resize(n);
// ar& boost::serialization::make_array(s.data(), n);
ar& boost::serialization::make_binary_object(s.data(), n);
}
}
int main() {
for (auto test_case : {"", " ", " ", "ford mustang", "hallberg rassy"}) {
std::stringstream ss;
Funky::String fs = test_case;
boost::archive::text_oarchive(ss) << fs;
std::cout << "Archive reads: " << quoted(boost::replace_all_copy(ss.str(), "\n", "\\n")) << std::endl;
Funky::String roundtrip;
boost::archive::text_iarchive (ss)>>roundtrip;
std::cout << "Roundtripped Funky::String: " << quoted(roundtrip) << "\n";
assert(roundtrip == test_case);
}
}
传递断言并打印预期的内容:
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization && ./a.out
Archive reads: "22 serialization::archive 20 0 0 0\\n\\n"
Roundtripped Funky::String: ""
Archive reads: "22 serialization::archive 20 0 0 1\\n\\nIA==\\n"
Roundtripped Funky::String: " "
Archive reads: "22 serialization::archive 20 0 0 3\\n\\nICAg\\n"
Roundtripped Funky::String: " "
Archive reads: "22 serialization::archive 20 0 0 12\\n\\nZm9yZCBtdXN0YW5n\\n"
Roundtripped Funky::String: "ford mustang"
Archive reads: "22 serialization::archive 20 0 0 14\\n\\naGFsbGJlcmcgcmFzc3k=\\n"
Roundtripped Funky::String: "hallberg rassy"
如果您注释掉
make_array
行,您会接近手动编码的内容:(Live)
Archive reads: "22 serialization::archive 20 0 0 0\\n"
Roundtripped Funky::String: ""
Archive reads: "22 serialization::archive 20 0 0 1 32\\n"
Roundtripped Funky::String: " "
Archive reads: "22 serialization::archive 20 0 0 3 32 32 32\\n"
Roundtripped Funky::String: " "
Archive reads: "22 serialization::archive 20 0 0 12 102 111 114 100 32 109 117 115 116 97 110 103\\n"
Roundtripped Funky::String: "ford mustang"
Archive reads: "22 serialization::archive 20 0 0 14 104 97 108 108 98 101 114 103 32 114 97 115 115 121\\n"
Roundtripped Funky::String: "hallberg rassy"