How Can Cstring Be Passed to Format String %S

How can CString be passed to format string %s?

CString is specifically designed such that it only contains a pointer that points to the string data in a buffer class. When passed by value to printf it will be treated as a pointer when seeing the "%s" in the format string.

It originally just happened to work with printf by chance, but this has later been kept as part of the class interface.


This post is based on MS documentation long since retired, so I cannot link to their promise that they will continue to make this work.

However, before adding more downvotes please also read this blog post from someone sharing my old knowledge:

Big Brother helps you

ReSharper C++: Allow CString as a %s printf parameter?

We'll change the inspection to allow passing CString to corresponding string format specifiers, please follow https://youtrack.jetbrains.com/issue/RSCPP-20109.

Using CString object in CString::Format

From the documentation of the CStringT class template:

You can freely substitute CStringT objects for PCXSTR function arguments.

Whenever a function parameter expects a constant C-style string, you can pass a CStringT object, that is implicitly converted by invoking the operator PCXSTR().

A function with variadic arguments, on the other hand, takes an untyped list of arguments. In this scenario, passing a CStringT object where a PCXSTR is (semantically) expected, is not safe. The compiler has no means of knowing, that it should emit code to perform the implicit conversion. You are responsible for performing the conversion yourself, either by invoking operator PCXSTR(), or by calling the GetString() class member.

This is as far as the documented contract goes. To understand why it is still (technically) safe to pass a CStringT through a variadic argument list, it helps to take a look at the implementation1:

The CSimpleStringT class template is the base for all supported string classes. It contains a single (private) data member of type PCXSTR, and no virtual functions, making it a standard layout type. The binary representation is thus identical to that of its only member, a pointer to a zero-terminated array of characters.


Unrelated to the discussion of this Q&A, yet for completeness' sake, here is a brief explanation, of how the CSimpleStringT keeps track of the additional data (like string length or reference count). The only data member m_pszData points into a structure of type CStringData, that consists of an initial sequence of constant size, storing the bookkeeping information, immediately followed by an array of characters of length nAllocLength + 1. m_pszData points at this array, and the additional data is retrieved through pointer arithmetic, as implemented by the private GetData() member:

CStringData* GetData() const throw()
{
return( reinterpret_cast< CStringData* >( m_pszData )-1 );
}

The class layout is summarized in the following diagram:

                        +------------------------+
| CStringData |
+========================+
| pStringMgr |
+----------------+ | nDataLength |
| CSimpleStringT | | nAllocLength |
+================+ | nRefs |
| m_pszData | ---> | data[0] |
+----------------+ | data[1] |
. ... .
| data[nAllocLength - 1] |
| data[nAllocLength] |
+------------------------+

1 This is based on the implementation that ships with Visual Studio 2017, that can - in theory - change without prior notice.

Pass CString to fprintf

I'm assuming that you're passing a Microsoft "CString" object to a printf()-family function where the corresponding format specifier is %s. If I'm right, then your answer is here: How can CString be passed to format string %s? (in short, your code is OK).

It seems that originally an implementation detail allowed CString to be passed directly to printf(), and later it was made part of the contract. So you're good to go as far as your program being correct, but if you want to avoid the static analysis warning, you may indeed need to use the static_cast to a char pointer. I'm not sure it's worth it here...maybe there's some other way to make these tools place nice together, since they're all from Microsoft.

Can CString::Format() receive const std::string?

No. You need to use the return value from a_string.c_str() (which is a const char* that CString can understand).

Why does passing a std::string to CString.Format() only crash sometimes?

Passing a std::string to CString::Format is not right. From https://msdn.microsoft.com/en-us/library/aa314327(v=vs.60).aspx:

The format has the same form and function as the format argument for the printf function.

That means, when the format specifier is %s, the expected argument type is char const*, not std::string.

Hence, use of

cStr.Format("%s", IntToStdStringMap[1]);

is cause for undefined behavior while the behavior of

cStr.Format("%s", IntToStdStringMap[1].c_str());

is well defined.



Related Topics



Leave a reply



Submit