使用自定义分配器提升 std::basic_string 的序列化

问题描述 投票:0回答:1

我正在尝试使用 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

我将不胜感激有关如何改进这两种方法之一的提示。 谢谢!

c++ serialization boost
1个回答
0
投票

是的,我仍然很惊讶对

{std,boost::container}::basic_string
的支持并不是开箱即用的。另外,很惊讶没有人注意到旧答案被破坏了。

我同意自定义序列化是更安全的选择。它可以更简单、更轻量一点:

住在Coliru

#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"
© www.soinside.com 2019 - 2024. All rights reserved.