Search code examples
c++visual-c++vectormfccereal

Cereal serialize CString Vector


EDIT:// Got it working thanks to the answer below, adding the code that is currently working and test case just in case some one might find it useful.

// 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();
  }

Below is the code I used to test, it correctly outputs all the elements of the vector.

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

Thanks to Barmak Shemirani for the help.


Solution

  • If your serialization class knows how to handle std::vector<std::string> (you should test it with strings of different sizes) it may also know how to handle CString (wide char version) by treating it as binary data, assuming the file is opened in binary.

    These flags are required to open in binary mode in Windows:

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

    If the serialization class uses text files for storage, then don't use binary mode, read the next line:

    Alternatively, you can convert CString (UTF16) to UTF8

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

    You will have to convert back to CString once you read it from the archive:

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


    In binary, use the correct size for saving CString. Something like:

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

    When reading in to CString, make sure the buffer is large enough by calling str.GetBuffer(size), instead of str.GetBuffer() The code should look something like this:

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

    To serialize vectors, save the vector's dimension, then save each vector element. To read the vector dimension, and then read that many elements from the archive. Unless your serialization class has automation for this.