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

tzcvj98z  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(175)

我正在尝试使用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\archive\impl\text_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::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

#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);
    }
}

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

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"

相关问题