Cereal 序列化 CString Vector

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

编辑://由于下面的答案,让它工作起来,添加当前正在工作的代码和测试用例,以防万一有人可能会发现它有用。

// Add as another type for Cereal or inside string.hpp in Cereal includes      
  template<class Archive> inline
      void CEREAL_SAVE_FUNCTION_NAME(Archive & ar, CString str)
  {
      // Save number of chars + the data
      size_type size = (str.GetLength() + 1) * sizeof(TCHAR);
      ar(size);
      ar(binary_data(str.GetBuffer(), static_cast<std::size_t>(size)));
      str.ReleaseBuffer();
  }

  template<class Archive> inline
      void CEREAL_LOAD_FUNCTION_NAME(Archive & ar, CString & str)
  {
      size_type size;
      ar(size);
      ar(binary_data(str.GetBuffer(static_cast<std::size_t>(size)), static_cast<std::size_t>(size)));
      str.ReleaseBuffer();
  }

下面是我用来测试的代码,它正确输出了向量的所有元素。

class Stuff
{
public:
    Stuff() {}
    std::vector<CString> vec;
private:
    friend class cereal::access;
    template <class Archive>
    void serialize(Archive & ar)
    {
        ar(vec);
    }
};

int main()
{
    Stuff myStuff, otherStuff;

    myStuff.vec.push_back(L"Testing different length CStrings");
    myStuff.vec.push_back(L"Separator");
    myStuff.vec.push_back(L"Is it working yet??");
    myStuff.vec.push_back(L"1234567890");
    myStuff.vec.push_back(L"TestingTestingTestingtestingTesting");

    {
    std::ofstream file("out.txt", std::ios::binary);
    cereal::BinaryOutputArchive output(file);
    output(myStuff);
    }

    {
        std::ifstream file("out.txt", std::ios::binary);
        cereal::BinaryInputArchive input(file);
        input(otherStuff);
    }

    int nSize = otherStuff.vec.size();

    for (int x = 0; x < nSize; x++)
    {
        std::wcout << (LPCWSTR)otherStuff.vec[x] << std::endl;
    }
    return 0;
}

感谢 Barmak Shemirani 的帮助。

c++ visual-c++ vector mfc cereal
2个回答
1
投票

如果您的序列化类知道如何处理

std::vector<std::string>
(您应该使用不同大小的字符串来测试它),它也可能知道如何通过将其视为二进制数据来处理
CString
(宽字符版本),假设文件已打开二进制。

这些标志需要在 Windows 中以二进制模式打开:

std::ofstream file("out.txt", std::ios::binary);
std::ifstream file("out.txt", std::ios::binary);

如果序列化类使用文本文件进行存储,那么不要使用二进制模式,请阅读下一行:

或者,您可以将

CString
(UTF16) 转换为 UTF8

std::vector<std::string> vec;
vec.push_back((const char*)CW2A(L"abc-unicode-ελληνική", CP_UTF8));

从存档中读取后,您必须转换回

CString

CString s = CA2W(vec[0].c_str(), CP_UTF8);


在二进制中,使用正确的大小来保存
CString
。比如:

CEREAL_SAVE_FUNCTION_NAME...
size_type size = (str.GetLength() + 1) * sizeof(TCHAR);
ar(size);
ar(binary_data(str.GetBuffer(), size));
str.ReleaseBuffer();

读入

CString
时,通过调用
str.GetBuffer(size)
(而不是
str.GetBuffer()
)确保缓冲区足够大。代码应如下所示:

CEREAL_LOAD_FUNCTION_NAME...
size_type size;
ar(size);
ar(binary_data(str.GetBuffer(size), size);
str.ReleaseBuffer();

要序列化向量,请保存向量的维度,然后保存每个向量元素。读取向量维度,然后从存档中读取那么多元素。除非你的序列化类具有自动化功能。


0
投票

可以支持CString对Binary/json/xml三种方式的读取和保存:

// CString.hpp
#ifndef CEREAL_TYPES_CSTRING_HPP_  
#define CEREAL_TYPES_CSTRING_HPP_  

#include "cereal/cereal.hpp"  
#include "cereal/archives/binary.hpp"  
#include "cereal/archives/json.hpp"  
#include "cereal/archives/xml.hpp"  

namespace cereal
{
    // 二进制存档的序列化  
    template<class Archive> inline
        typename std::enable_if<traits::is_output_serializable<BinaryData<TCHAR>, Archive>::value, void>::type
        CEREAL_SAVE_FUNCTION_NAME(Archive & ar, const CString& str)
    {
        // 获取字符串长度  
        int length = str.GetLength();
        ar(length);

        // 直接使用 GetString() 避免缓冲区问题  
        if (length > 0) {
            ar(binary_data(str.GetString(), length * sizeof(TCHAR)));
        }
    }

    template<class Archive> inline
        typename std::enable_if<traits::is_input_serializable<BinaryData<TCHAR>, Archive>::value, void>::type
        CEREAL_LOAD_FUNCTION_NAME(Archive & ar, CString& str)
    {
        int length;
        ar(length);

        if (length > 0) {
            // 使用临时缓冲区  
            std::vector<TCHAR> buffer(length + 1, 0);
            ar(binary_data(buffer.data(), length * sizeof(TCHAR)));

            // 直接赋值  
            str = buffer.data();
        }
        else {
            str.Empty();
        }
    }

    // JSON 和 XML 存档的序列化  
    template<class Archive> inline
        typename std::enable_if<traits::is_output_serializable<std::string, Archive>::value, void>::type
        CEREAL_SAVE_FUNCTION_NAME(Archive & ar, const CString& str)
    {
        // 使用更安全的转换方法  
        std::string utf8Str;
        int wideCharLength = str.GetLength();

        if (wideCharLength > 0) {
            // 计算所需缓冲区大小  
            int requiredSize = WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, nullptr, nullptr);

            // 调整缓冲区大小  
            utf8Str.resize(requiredSize);

            // 执行转换  
            WideCharToMultiByte(CP_UTF8, 0, str, -1, &utf8Str[0], requiredSize, nullptr, nullptr);

            // 移除结尾的 null 终止符  
            utf8Str.resize(requiredSize - 1);
        }

        ar(utf8Str);
    }

    template<class Archive> inline
        typename std::enable_if<traits::is_input_serializable<std::string, Archive>::value, void>::type
        CEREAL_LOAD_FUNCTION_NAME(Archive & ar, CString& str)
    {
        std::string utf8Str;
        ar(utf8Str);

        if (!utf8Str.empty()) {
            // 计算所需缓冲区大小  
            int wideCharLength = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0);

            // 使用 vector 作为临时缓冲区  
            std::vector<wchar_t> wideCharBuffer(wideCharLength);

            // 执行转换  
            MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, wideCharBuffer.data(), wideCharLength);

            // 直接赋值  
            str = wideCharBuffer.data();
        }
        else {
            str.Empty();
        }
    }
} // namespace cereal  

#endif // CEREAL_TYPES_CSTRING_HPP_
© www.soinside.com 2019 - 2024. All rights reserved.