Nicola Iuretigh Nicola Iuretigh - 2 months ago 14
C++ Question

Different behaviour between CString operators "+=" and "+"

While migrating an application from VisualStudio 2005 to VisualStudio 2015 we found a different behaviour in some code that concatenates CString instances when that code is built with VS2015.

So, I've created a simple Win32 console application to demonstrate the problem.

The console application (using MFC as shared dll and Unicode charachter set) executes this simple function:

void f()
{
CString x( '\0' );

CString r( 'a' );
r += x;
CString rr( 'a' );
rr = rr + x;

int rSize = r.GetLength();
int rrSize = rr.GetLength();

assert( rSize == rrSize ); // This assert fires when compiled and run
// under Visual Studio 2015!
}


It shows that, when a CString containing a '\0' char is concatenated to another CString instance, using '+=' or using '+' leads to different results!

When '+=' is used the size of the result is calculated counting all chars till the first '\0'... hence the final size is 1!

Conversely, when the operator '+' is used the result CString size is 2, that is the sum of the sizes of the concatenated instances!

In VisualStudio 2005 the result size is always the sum of the sizes of the concatenated instances!

I filed a bug to Microsoft a few weeks ago, but up until now I have no answer from those guys.

My questions:

1. Have somebody stumbled onto this bug in the MCF library??

2. How have you worked around this bug? We are thinking to ban the use of the += operator or else to replace the CString class with the a custom class, but all of this seems to me "a bit" invasive.

Answer

The documentation for the CStringT Class contains the following cryptic statement:

Although it is possible to create CStringT instances that contain embedded null characters, we recommend against it. Calling methods and operators on CStringT objects that contain embedded null characters can produce unintended results.

Frankly, I don't really know what to make of the final sentence. I take it as a warning to be careful when embedding null characters. Regardless, contractual guarantees should still hold when doing so.

Analysis:

This apparently is not the case with CStringT::operator+=, though. In the sample code of the question the implementation of operator+= invokes the

CSimpleStringT& operator+=( const CSimpleStringT& strSrc )

overload, which modifies the current instance by calling

void Append( const CSimpleStringT& strSrc )

which in turn calls

void Append( PCXSTR pszSrc, int nLength )

passing an explicit length argument. This should suffice to deal with C-style strings with embedded null characters. Oddly enough, the implementation then starts to second-guess the input by calling StringLengthN(pszSrc, nLength) (implemented as a call to wcsnlen), to re-calculate the length of pszSrc. This returns 0 for the CStringT instance x in the sample code.

Result:

To me, this appears to be a bug in the implementation. Incidentally, if you reverse the arguments to operator+= (i.e. x += r; vs. r += x;), the result is a string with length 2, as expected.

Resolution:

The only clean solution would be to have Microsoft acknowledge the bug, and provide a fix for it. I wouldn't hold my breath, though, as Microsoft usually doesn't ship bug fixes, if they change behavior of a shipped product.

If you cannot convince Microsoft to fix that bug, your only other option is to not use the operator with undesired behavior. One way would be to use another string class. A well established replacement is std::wstring, which you can convert to a CStringW where necessary (CStringW cstr(s.c_str(), s.length());).


Update (2016-09-13):

The OP filed a defect report with Microsoft, and they acknowledged the bug. A bug fix was implemented, but "it won't appear until the next major version of the libraries (which may not necessarily ship in the current [2016-03-31] Visual Studio vNext)".

Comments