6

I am using std::string in my MFC application and I want to store it in doc's Serialize() function. I don't want to store them as CString because it writes its own stuff in there and my goal is to create a file that I know the format of and can be read by other application without needing CString. So I would like to store my std::strings as 4 bytes (int) string length followed by buffer of that size containing the string.

void CMyDoc::Serialize(CArchive& ar)
{
    std::string theString;

    if (ar.IsStoring())
    {
        // TODO: add storing code here
        int size = theString.size();
        ar << size;
        ar.Write( theString.c_str(), size );

    }
    else
    {
        // TODO: add loading code here
        int size = 0;
        ar >> size;
        char * bfr = new char[ size ];
        ar.Read( bfr, size);
        theString = bfr;
        delete [] bfr;
    }
}

The above code is not great and I have to allocate a temp bfr to read the string. First can I read the string directly into std::string without the temp buffer? Secondly can I overload the << buffer for std::string / CArchive so I can simply use ar << theString? Overall is there a better way to read/write std::string using CArchive object?

zar
  • 10,321
  • 11
  • 83
  • 159

5 Answers5

2

You could build an inplace CString from your stl string and serialize that. Something like:

CString c_string(my_stl_string.c_str();
ar << c_string;

You could put this in a global operater overload so it can you can just

ar << my_c_string;

from anywhere eg:

CArchive& operator<<(CArchive rhs, string lhs) {
    CString c_string(lhs.c_str());
    rhs << c_string;
}
Ricibob
  • 7,307
  • 5
  • 42
  • 63
1

Its probably better to write the data as a CString for various reasons, but if you have to convert your String (m_sString) into an ASCII character string, maybe something like this will work for you...

void myclass::Serialize(CArchive & ar)
{
    CHAR* buf;
    DWORD len;
    if (ar.IsStoring()) // Writing
    {
        len = m_sString.GetLength(); // Instead of null terminated string, store size.
        ar << len;
        buf = (CHAR*)malloc(len);
        WideCharToMultiByte(CP_UTF8, 0, m_sString, len, buf, len, NULL, NULL); // Convert wide to single bytes
        ar.Write(buf, len); // Write ascii chars
        free(buf);
    }
    else // Reading
    {
        ar >> len;
        buf = (CHAR*)malloc(len);
        ar.Read(buf, len); // Read ascii string
        MultiByteToWideChar(CP_UTF8, 0, buf, len, m_sString.GetBufferSetLength(len), len); // Convert ascii bytes to CString wide bytes
        free(buf);
    }
}
Duncan
  • 11
  • 1
1

Try:

theString.resize(size);
ar.Read(&theString[0], size);

Technically &theString[0] is not guaranteed to point to a contiguous character buffer, but the C++ committee did a survey and found that all existing implementations work this way.

Mark Ransom
  • 286,393
  • 40
  • 379
  • 604
0

If you are working with a library that only works with c-style strings, there is no way to safely write directly to the std::string. That issue is fixed in C++0x. So something like

// NOT PORTABLE, don't do this
theString.resize(size);
ar.Read( const_cast<char *>(theString.c_str(), size);

Would probably work, but it could create some subtle, hard-to-track bugs later on. Of course your question implies that you have profiled your code and figured out that creating the buffer and copying the data twice is actually a bottleneck in your code. If you haven't, then you shouldn't be fretting about inefficiencies yet.

Community
  • 1
  • 1
David Nehme
  • 21,138
  • 8
  • 77
  • 116
  • I have tried this but c_str() returns a 'const char *' and that's a problem. I probably could typecast it to simply 'char *' but that would somewhat violating the c_str() function. – zar Sep 16 '11 at 15:32
  • 1
    Thus the "don't do this" comment. You can use a `std::vector` or since you're in MFC land use `CString` – AJG85 Sep 16 '11 at 16:29
0

I suppose you could violate STL guidelines and inherit std::string and add your own buffer getter/setter. Then override the copy constructor for std::string and transfer ownership of the buffer.

l33t
  • 15,615
  • 14
  • 88
  • 169