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 forPCXSTR
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
When Pass a Variable to a Function, Why the Function Only Gets a Duplicate of the Variable
Does Reinterpret_Cast Lead to Undefined Behavior
Best Bignum Library to Solve Project Euler Problems in C++
What Is the Purpose of the _Chkstk() Function
Adding Signals/Slots (Qobject) to Qgraphicsitem: Performance Hit
Does One Double Promote Every Int in the Equation to Double
Overload Resolution with Std::Function
Using Boost Adaptors with C++11 Lambdas
Why Is the Order of Evaluation for Function Parameters Unspecified in C++
Iterator Adapter to Iterate Just the Values in a Map
Sorting a Vector of Objects by a Property of the Object
Should Std::Common_Type Use Std::Decay
Narrowing Conversion from Unsigned to Double
Why Does Pointer to Int Convert to Void* But Pointer to Function Convert to Bool