我能否重载CArchive <<运算符以处理std::string?

6

我在我的MFC应用程序中使用std::string,并希望将其存储在文档的Serialize()函数中。我不想将它们存储为CString,因为它会写入自己的内容,在此我要创建一个文件,我知道其格式,并且可以被其他应用程序读取而无需使用CString。因此,我希望将我的std::strings存储为4个字节(int)的字符串长度,后跟该大小的缓冲区包含字符串。

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

上述代码并不完美,我必须分配一个临时缓冲区来读取字符串。首先,能否直接将字符串读入std::string而无需使用临时缓冲区?其次,我能否重载std::string / CArchive的<<缓冲区,以便我可以简单地使用ar << theString? 总体而言,有没有更好的方法使用CArchive对象读写std::string?

6个回答

2
您可以从STL字符串中构建一个inplace CString,并对其进行序列化。类似于以下内容:
CString c_string(my_stl_string.c_str();
ar << c_string;

您可以将此放在全局运算符重载中,这样您就能够轻松使用。
ar << my_c_string;

从任何地方,例如:

CArchive& operator<<(CArchive rhs, string lhs) {
    CString c_string(lhs.c_str());
    rhs << c_string;
}

1

由于各种原因,最好将数据写成 CString 格式,但如果你必须将你的字符串(m_sString)转换为 ASCII 字符串,也许以下代码适用于你...

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

1

为了提供一个完整功能且正确的示例(希望如此)涉及std::stringstd::wstring:

#include <string>
#include <gsl/gsl>

template <typename Char>
CArchive& operator<<(CArchive& ar, const std::basic_string<Char>& rhs)
{
    const auto size = rhs.size();
    ar << size;
    ar.Write(rhs.data(), gsl::narrow_cast<UINT>(size) * sizeof(Char));
    return ar;
}

template <typename Char>
CArchive& operator>>(CArchive& ar, std::basic_string<Char>& rhs)
{
    size_t size{};
    ar >> size;
    rhs.resize(size);
    ar.Read(rhs.data(), gsl::narrow_cast<UINT>(size) * sizeof(Char));
    return ar;
}

Writing ...

std::wstring ws{ L"wide string" };
ar << ws;

std::string ns{ L"narrow string" };
ar << ns;

Reading ...

std::wstring ws;
ar >> ws;

std::string ns;
ar >> ns;

1

尝试:

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

从技术上讲,&theString[0]不能保证指向一个连续的字符缓冲区,但C++委员会进行了一项调查,发现所有现有的实现都是这样工作的。

0

如果你正在使用一个只能处理C风格字符串的库,那么就没有安全地直接写入std::string的方法。这个问题在C++0x中得到了解决。

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

可能可以工作,但以后可能会出现一些微妙而难以追踪的错误。 当然,你的问题意味着你已经对代码进行了分析,并发现创建缓冲区并将数据复制两次实际上是代码中的瓶颈。如果没有这样做,那么你不应该担心效率问题。


我已经尝试过这个,但是c_str()返回的是'const char *',这是一个问题。我可能可以将其强制转换为简单的'char *',但那会有点违反c_str()函数的规定。 - zar
1
因此有了“不要这样做”的注释。您可以使用std::vector<char>或者既然您在MFC环境中,可以使用CString - AJG85

0

我想你可以违反STL准则,并且继承std::string,并添加自己的缓冲区getter/setter。然后重载std::string的拷贝构造函数并转移缓冲区所有权。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接