c++ 使用自定义分配器对std::basic_string进行Boost序列化

tzcvj98z  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(238)

我正在尝试使用boost序列化以下字符串类型:

  1. 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\archive\impl\text_oarchive_impl.ipp

  1. text_oarchive_impl<Archive>::save(const std::string &s)
  2. {
  3. const std::size_t size = s.size();
  4. *this->This() << size;
  5. this->This()->newtoken();
  6. os << s;
  7. }


我在以下问题的答案中发现了同样的问题:Can boost::container::strings be serialized using boost serialization?
我扩展了这个例子,以使问题可见:https://coliru.stacked-crooked.com/a/84b2cb162d58534a

**2.**现在我编写了自己的序列化函数

  1. namespace boost
  2. {
  3. namespace serialization
  4. {
  5. template<class Archive>
  6. inline void serialize(Archive& ar, CustomString & s, const unsigned int file_version)
  7. {
  8. boost::serialization::split_free(ar, s, file_version);
  9. }
  10. template<typename Archive>
  11. inline void save(
  12. Archive& ar, const CustomString & s, const unsigned int /* file_version */
  13. )
  14. {
  15. ar << s.size();
  16. for (size_t i = 0; i < s.size(); i++)
  17. {
  18. ar << s.c_str()[i];
  19. }
  20. }
  21. template<typename Archive>
  22. inline void load(
  23. Archive& ar, CustomString & s, const unsigned int /* file_version */
  24. )
  25. {
  26. size_t size;
  27. ar >> size;
  28. char c;
  29. for (size_t i=0; i < size;i++)
  30. {
  31. ar >> c;
  32. s.push_back(c);
  33. }
  34. }
  35. } // namespace serialization
  36. } // namespace boost


这段代码确实可以工作,但会产生一个相当长的归档文件,因为字符被编码为十进制值:
std::initializer_list
自定义字符串存档:22 serialization::archive 19 0 0 11 72 101 108 108 111 32 87 111 114 108 100
如果您能给我一些提示,告诉我如何改进这两种方法中的一种,我将不胜感激。谢谢!

fivyi3re

fivyi3re1#

是的,我仍然很惊讶对{std,boost::container}::basic_string的支持不是开箱即用的。而且,很惊讶没有人注意到旧的答案被打破了。
我同意自定义序列化是更安全的选择。它可以更简单和轻量级:

Live On Coliru

  1. #include <boost/algorithm/string.hpp>
  2. #include <boost/archive/text_iarchive.hpp>
  3. #include <boost/archive/text_oarchive.hpp>
  4. #include <boost/serialization/binary_object.hpp>
  5. #include <iomanip>
  6. #include <iostream>
  7. namespace Funky {
  8. template <typename T> struct FunkyAlloc : std::allocator<T> {
  9. using std::allocator<T>::allocator;
  10. using std::allocator<T>::operator=;
  11. };
  12. using String = std::basic_string<char, std::char_traits<char>, FunkyAlloc<char>>;
  13. template <typename Ar, typename TCh, typename TChT, typename Allocator>
  14. void serialize(Ar& ar, std::basic_string<TCh, TChT, Allocator>& s, unsigned) {
  15. size_t n = s.length();
  16. ar& n;
  17. if (Ar::is_loading::value)
  18. s.resize(n);
  19. // ar& boost::serialization::make_array(s.data(), n);
  20. ar& boost::serialization::make_binary_object(s.data(), n);
  21. }
  22. }
  23. int main() {
  24. for (auto test_case : {"", " ", " ", "ford mustang", "hallberg rassy"}) {
  25. std::stringstream ss;
  26. Funky::String fs = test_case;
  27. boost::archive::text_oarchive(ss) << fs;
  28. std::cout << "Archive reads: " << quoted(boost::replace_all_copy(ss.str(), "\n", "\\n")) << std::endl;
  29. Funky::String roundtrip;
  30. boost::archive::text_iarchive (ss)>>roundtrip;
  31. std::cout << "Roundtripped Funky::String: " << quoted(roundtrip) << "\n";
  32. assert(roundtrip == test_case);
  33. }
  34. }

字符串
传递Assert并打印预期的:

  1. g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp -lboost_serialization && ./a.out
  2. Archive reads: "22 serialization::archive 20 0 0 0\\n\\n"
  3. Roundtripped Funky::String: ""
  4. Archive reads: "22 serialization::archive 20 0 0 1\\n\\nIA==\\n"
  5. Roundtripped Funky::String: " "
  6. Archive reads: "22 serialization::archive 20 0 0 3\\n\\nICAg\\n"
  7. Roundtripped Funky::String: " "
  8. Archive reads: "22 serialization::archive 20 0 0 12\\n\\nZm9yZCBtdXN0YW5n\\n"
  9. Roundtripped Funky::String: "ford mustang"
  10. Archive reads: "22 serialization::archive 20 0 0 14\\n\\naGFsbGJlcmcgcmFzc3k=\\n"
  11. Roundtripped Funky::String: "hallberg rassy"


如果注解掉make_array行,则会接近于手动编码的内容:(Live

  1. Archive reads: "22 serialization::archive 20 0 0 0\\n"
  2. Roundtripped Funky::String: ""
  3. Archive reads: "22 serialization::archive 20 0 0 1 32\\n"
  4. Roundtripped Funky::String: " "
  5. Archive reads: "22 serialization::archive 20 0 0 3 32 32 32\\n"
  6. Roundtripped Funky::String: " "
  7. Archive reads: "22 serialization::archive 20 0 0 12 102 111 114 100 32 109 117 115 116 97 110 103\\n"
  8. Roundtripped Funky::String: "ford mustang"
  9. Archive reads: "22 serialization::archive 20 0 0 14 104 97 108 108 98 101 114 103 32 114 97 115 115 121\\n"
  10. Roundtripped Funky::String: "hallberg rassy"

展开查看全部

相关问题