Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pass CString to printf?

1,224 views
Skip to first unread message

Hannes

unread,
Sep 25, 2008, 3:54:00 AM9/25/08
to
Hi,

Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?

The following MSDN article suggests it is NOT safe:
http://msdn.microsoft.com/en-us/library/awkwbzyc(VS.71).aspx
"...it is essential that you use an explicit type cast when passing a
CString object to a function that takes a variable number of arguments.
...
CString kindOfFruit = "bananas";
int howmany = 25;
printf( "You have %d %s\n", howmany, (LPCTSTR)kindOfFruit );"


On the other hand, various newsgroup posts claim that CString was designed
specifically in such a way that the (LPCTSTR) cast would not be needed with
printf.

We are a team of 40+ developers who are converting several large projects
from char* to CString, and avoiding (LPCTSTR) would save us a lot of time.

Can someone with insight please clarify what the software developing
community should do in this case?

Thanks,

/ Hannes.

Mikel

unread,
Sep 25, 2008, 4:42:31 AM9/25/08
to
Just in case, I would follow MSDN's recommendation. Using the cast
won't do any harm, except for losing some time (is writing code
properly a time loss?), and not using it might do it, so...

Giovanni Dicanio

unread,
Sep 25, 2008, 8:54:37 AM9/25/08
to

"Hannes" <hanne...@newsgroup.nospam> ha scritto nel messaggio
news:77D19855-6CFF-4690...@microsoft.com...

> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?

CString is designed in a special way such that this works safely.

Since VC2003 CString is actually a template class, and is derived from
template class CSimpleStringT.
You can read the implementation of CSimpleStringT in <atlsimpstr.h> header.
CSimpleStringT has a private data member:

PXSTR m_pszData;

There is no virtual method, so CSimpleStringT has no v-table pointers
(there's no "lpVtbl").

So, as for memory layout, the "address" of a CSimpleStringT instance could
be safely "reinterpreted" as LPCTSTR (pointing to m_pszData), and this is
why passing CString instances to printf() works without the explicit
static_cast< LPCTSTR >.

Note that Microsoft programmers who wrote CSimpleStringT were very clever,
and put the pointer to CStringData (which is an internal class that stores
information like reference counts to string, etc.) *before* the m_pszData
member of CSimpleStringT.

In fact, you can read that CSimpleStringT::GetData does an "unusual" (but
meaningful) cast like this:

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

This "magic" (actually, clever behaviour) allows safe use of CString
instances (simply derived from CSimpleStringT, with no virtual methods) in
%s slots for printf().

However, in the future this "clever" behaviour and this "magic" may
disappear... so I think that the best investment in future robust code is to
do the explicit static_cast.

Giovanni

Jonathan Wood

unread,
Sep 25, 2008, 2:29:52 PM9/25/08
to
I looked into this at one time. While it works and is safe, I recall that it
is not as efficient. I believe the code generated will create a copy of the
CString object and then pass that. Because of the way the CString class is
designed, it is equal to the address of the string.

By using the cast or calling the GetBuffer() method, you may be producing
much more efficient code.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com

"Hannes" <hanne...@newsgroup.nospam> wrote in message
news:77D19855-6CFF-4690...@microsoft.com...

Tom Serface

unread,
Sep 25, 2008, 2:55:01 PM9/25/08
to
That may be significant if you are doing the printf call in a tight loop
like updating status information.

I'd still be more likely to just use CString::Format or
CString::FormatMessage and skip printf altogether. That is a pretty thin
layer on top of it all.

Tom

"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:u8PGJzzH...@TK2MSFTNGP03.phx.gbl...

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 3:45:57 PM9/25/08
to
"Hannes" <hanne...@newsgroup.nospam> wrote in message
news:77D19855-6CFF-4690...@microsoft.com...
> Hi,
>
> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?


Yes, and that's how it should be passed - with NO cast.

Using the cast will hide a bug if you move the code to a Unicode build.


Really, if you're using a generic TCHAR character type string like CString,
then you should be using generic text function mappings.
You should VERY rarely need a string cast if you do..

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++

Jonathan Wood

unread,
Sep 25, 2008, 3:55:55 PM9/25/08
to
Mark,

>> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?
>
> Yes, and that's how it should be passed - with NO cast.
>
> Using the cast will hide a bug if you move the code to a Unicode build.

I must confess that I'm pretty surprised someone would suggest in a thread
like this that the typecast can cause a bug, and yet not even hint at what
that bug might be.

I posted that the typecase can be more efficient, and then explained the
details of why.

Any chance you'll do the same and explain why we should use the less
efficient syntax?

Jonathan Wood

unread,
Sep 25, 2008, 3:59:02 PM9/25/08
to
Tom,

> That may be significant if you are doing the printf call in a tight loop
> like updating status information.
>
> I'd still be more likely to just use CString::Format or
> CString::FormatMessage and skip printf altogether. That is a pretty thin
> layer on top of it all.

Well, I can tell you that I often use Format and rarely use printf in a
Windows application!

However, that said, Format is not that thin of a wrapper. It actually parses
the format string twice. The first time is to determine the size of the
required buffer. In fact, last time I looked into this, the code to do this
was not in the C run time, it was actually MFC code that parsed through the
format string, looking at each field and determining its length.

John Sw

unread,
Sep 25, 2008, 4:40:00 PM9/25/08
to
On 9/25/2008 3:55 PM Jonathan Wood wrote:
> Mark,
>
>>> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?
>>
>> Yes, and that's how it should be passed - with NO cast.
>>
>> Using the cast will hide a bug if you move the code to a Unicode build.
>
> I must confess that I'm pretty surprised someone would suggest in a
> thread like this that the typecast can cause a bug, and yet not even
> hint at what that bug might be.
>
> I posted that the typecase can be more efficient, and then explained the
> details of why.
>
> Any chance you'll do the same and explain why we should use the less
> efficient syntax?

I won't speak for anyone else, but the danger here isn't in using the
cast (which as you point out is a good idea). It is in using a dumb
cast. It should really use static_cast<LPCTSTR>(), which will not cause
unexpected conversions and is also Unicode safe. The only reason to use
the C-style casts is to save typing, and we know how much time that
saves in the long run.


Mark Salsbery [MVP]

unread,
Sep 25, 2008, 5:13:18 PM9/25/08
to
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:#0rjOj0H...@TK2MSFTNGP02.phx.gbl...

> Mark,
>
>>> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?
>>
>> Yes, and that's how it should be passed - with NO cast.
>>
>> Using the cast will hide a bug if you move the code to a Unicode build.
>
> I must confess that I'm pretty surprised someone would suggest in a thread
> like this that the typecast can cause a bug, and yet not even hint at what
> that bug might be.


On a Unicode build, a const wchar_t * would be passed - that would be a bug.
Of course it shouldn't compile I suppose, so I retract my statements.

The entire subject of using CString with printf() is kind of silly IMO, but
this would be a more proper way to pass a CString:

// This should work on Unicode and non-Unicode builds
CString kindOfFruit = _T("bananas");
int howmany = 25;
printf("You have %d %s", howmany, CT2A(kindOfFruit) );


Typecasts cause LOTS of bugs.

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++

>

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 5:15:01 PM9/25/08
to
I was wrong again - the code I described as a bug (using that cast in a
Unicode build) WOULD compile with no warning/error.

Tom Walker

unread,
Sep 25, 2008, 5:13:11 PM9/25/08
to
"Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in message
news:#igcYd0H...@TK2MSFTNGP02.phx.gbl...

>> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?
>
> Yes, and that's how it should be passed - with NO cast.
>
> Using the cast will hide a bug if you move the code to a Unicode build.

It's true that C-style casts should be avoided, but a C++ static_cast is
safe:
static_cast<LPCTSTR>(myStr);

However, the CString::GetString method is cleaner:
myStr.GetString();

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 5:17:04 PM9/25/08
to
"Tom Walker" <nob...@example.com> wrote in message
news:OD40FO1H...@TK2MSFTNGP03.phx.gbl...


My point was Unicode build in this specific instance. Try the OP's code on
a Unicode build - you'll see what I mean.
A static cast won't fix that - the string needs to be converted to ANSI.

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 5:34:36 PM9/25/08
to
"John Sw" <jswa...@noemail.noemail> wrote in message
news:uarui70...@TK2MSFTNGP03.phx.gbl...

>
>It should really use static_cast<LPCTSTR>(), which will not cause
>unexpected conversions and is also Unicode safe.


Safe? Maybe, but it doesn't work, unless the 'b' in the string "bananas" is
all you wanted. Try it.

Jonathan Wood

unread,
Sep 25, 2008, 5:46:56 PM9/25/08
to
Mark,

> On a Unicode build, a const wchar_t * would be passed - that would be a
> bug.
> Of course it shouldn't compile I suppose, so I retract my statements.

Ah... got ya. Personally, I don't consider that a bug in the typecast. But
if you are calling printf in a Unicode app, then you definitely need to know
what you are doing.

> The entire subject of using CString with printf() is kind of silly IMO,
> but this would be a more proper way to pass a CString:
>
> // This should work on Unicode and non-Unicode builds
> CString kindOfFruit = _T("bananas");
> int howmany = 25;
> printf("You have %d %s", howmany, CT2A(kindOfFruit) );

Sure, but this code would probably be better written to call _tprintf
instead of printf.

Jonathan Wood

unread,
Sep 25, 2008, 5:49:14 PM9/25/08
to
I must be getting old. I still use the only type of cast available back when
I learned C.

Can anyone tell me the difference, specifically, in the code generated by
the compiler for these two statements?

1. p = static_cast<LPCTSTR>(myStr);
2. p = (LPCTSTR)myStr;

Thanks.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com

"Tom Walker" <nob...@example.com> wrote in message
news:OD40FO1H...@TK2MSFTNGP03.phx.gbl...

Jonathan Wood

unread,
Sep 25, 2008, 5:51:26 PM9/25/08
to
A cast to LPCTSTR in an argument to printf() in a Unicode build, should
cause a warning/error. In a Unicode build, LPCTSTR is LPCWSTR and printf()
expects LPCSTR.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com

"Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in message
news:OM7FKP1H...@TK2MSFTNGP06.phx.gbl...

David Wilkinson

unread,
Sep 25, 2008, 6:03:36 PM9/25/08
to

Mark:

I'm not quite sure what your point is here. If it is that you should always use
_tprintf() or CString::Format() rather than printf(), then I would agree with
you. But surely then it does not matter whether you use the LPCTSTR cast or not.

But suppose you do use printf(). In a Unicode build I can see that you will not
get the answer you expect, but what is the difference between using the LPCTSTR
cast or not?

--
David Wilkinson
Visual C++ MVP

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 6:11:47 PM9/25/08
to
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:OaZaRh1H...@TK2MSFTNGP05.phx.gbl...

> Mark,
>
>> On a Unicode build, a const wchar_t * would be passed - that would be a
>> bug.
>> Of course it shouldn't compile I suppose, so I retract my statements.
>
> Ah... got ya. Personally, I don't consider that a bug in the typecast.


Whatever :) The entire statement would cause bug-like behavior at runtime.


> But if you are calling printf in a Unicode app, then you definitely need
> to know what you are doing.


Agreed - I'd go further and state one should know what they're doing when
using CString.

My original main point was really mixing fixed string types with generic
string types is, umm, counterproductive? It renders the whole usefulness of
generic string types useless.


> Sure, but this code would probably be better written to call _tprintf
> instead of printf.


I totally agree - that's why I mentioned the generic text mappings in my OP
:)

Cheers,
Mark

--
Mark Salsbery
Microsoft MVP - Visual C++


>

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 6:17:30 PM9/25/08
to
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:eXWDyj1H...@TK2MSFTNGP05.phx.gbl...

> A cast to LPCTSTR in an argument to printf() in a Unicode build, should
> cause a warning/error. In a Unicode build, LPCTSTR is LPCWSTR and printf()
> expects LPCSTR.
>


That's what I originally thought, but with var args the compiler doesn't
give a warning even at level 4.

Here's my test code for this thread:

//
// Tested on Visual Studio 2008 SP1
//


CString kindOfFruit = _T("bananas");
int howmany = 25;

char buff[80];
sprintf(buff, "You have %d %s", howmany, CT2A(kindOfFruit) );
sprintf(buff, "You have %d %s", howmany, (LPCTSTR)kindOfFruit );
sprintf(buff, "You have %d %s", howmany, static_cast<LPCTSTR>(kindOfFruit));


No warnings (except the deprecated functions).

Alexander Grigoriev

unread,
Sep 25, 2008, 6:41:33 PM9/25/08
to
Because CString is not a simple type, like an integer or pointer, (LPCTSTR)
cast is only interpreted as static_cast.

"Jonathan Wood" <jw...@softcircuits.com> wrote in message

news:Oc9Rji1H...@TK2MSFTNGP04.phx.gbl...

Alexander Grigoriev

unread,
Sep 25, 2008, 6:42:54 PM9/25/08
to
LPCTSTR cast of CString is always safe. There is NO cast to the "other"
string pointer (LPCXSTR) provided by CString; such a cast won't compile.

For CString, C-style LPCTSTR cast (or LPCTSTR(s)) can only be interpreted
one way: by invoking LPCTSTR operator. There is no reinterpret_cast
interpretation for that, because the source is not a builtin type.

"Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in message

news:OvRpMO1H...@TK2MSFTNGP02.phx.gbl...

Alexander Grigoriev

unread,
Sep 25, 2008, 6:42:50 PM9/25/08
to
Why? This is a variadic function, with arguments of any POD type.

"Jonathan Wood" <jw...@softcircuits.com> wrote in message

news:eXWDyj1H...@TK2MSFTNGP05.phx.gbl...

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 6:44:00 PM9/25/08
to

"David Wilkinson" <no-r...@effisols.com> wrote in message
news:Ouq4Oq1H...@TK2MSFTNGP04.phx.gbl...


>
> Mark:
>
> I'm not quite sure what your point is here. If it is that you should
> always use _tprintf() or CString::Format() rather than printf(), then I
> would agree with you.


That was my point yes. The mixing of generic and non generic strings is a
peeve of mine. It's the number one source of string-related bugs IME
looking at people's posted code.

I suppose I should have said nothing.


>
> But suppose you do use printf(). In a Unicode build I can see that you
> will not get the answer you expect, but what is the difference between
> using the LPCTSTR cast or not?


I didn't say there was a difference (I don't think).

Jonathan Wood stated using the cast is more efficient, but I just looked at
the disassembly of my test code (below) and it seems NOT using the cast is
the most efficient. I could be wrong :)

//
// Test platform: VS 2008 SP1 on Vista Ultimate
//
// Unicode build!
//


CString kindOfFruit = _T("bananas");
int howmany = 25;

char bufff[250];
sprintf(bufff, "You have %d %s", howmany, kindOfFruit);
sprintf(bufff, "You have %d %s", howmany, (LPCTSTR)kindOfFruit );
sprintf(bufff, "You have %d %s", howmany,
static_cast<LPCTSTR>(kindOfFruit));
sprintf(bufff, "You have %d %s", howmany, CT2A(kindOfFruit) );

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++

>

Giovanni Dicanio

unread,
Sep 25, 2008, 6:43:38 PM9/25/08
to

"Jonathan Wood" <jw...@softcircuits.com> ha scritto nel messaggio
news:Oc9Rji1H...@TK2MSFTNGP04.phx.gbl...

>I must be getting old. I still use the only type of cast available back
>when I learned C.
>
> Can anyone tell me the difference, specifically, in the code generated by
> the compiler for these two statements?
>
> 1. p = static_cast<LPCTSTR>(myStr);
> 2. p = (LPCTSTR)myStr;

I think that, if the class has a operator LPCTSTR() for conversion, like
CString has, these are equivalent.

However, the point is doing:

CString myStr;

_tprintf( TEXT("My string: %s"), myStr ); // #1

vs.

_tprintf( TEXT("My string: %s"), (LPCTSTR)myStr ); // #2

I believe #2 is equivalent to:

_tprintf( TEXT("My string: %s"), static_cast< LPCTSTR >(myStr) );

Instead, #1 is possible thanks to CStringT/CSimpleStringT memory layout,
that I tryed to explain in my first post, and is confirmed also by this
working code:

CString str = TEXT("ciao");
const TCHAR * psz = *reinterpret_cast< const TCHAR ** >(&str);

the debugger shows "ciao" for 'psz'.

Giovanni


Mark Salsbery [MVP]

unread,
Sep 25, 2008, 6:52:21 PM9/25/08
to
"Alexander Grigoriev" <al...@earthlink.net> wrote in message
news:#H7kMA2H...@TK2MSFTNGP04.phx.gbl...

> LPCTSTR cast of CString is always safe. There is NO cast to the "other"
> string pointer (LPCXSTR) provided by CString; such a cast won't compile.
>
> For CString, C-style LPCTSTR cast (or LPCTSTR(s)) can only be interpreted
> one way: by invoking LPCTSTR operator. There is no reinterpret_cast
> interpretation for that, because the source is not a builtin type.
>


Ok. I'm not sure how any of that applies to and/or contradicts any
statements I made :)

The cast is is not only perfectly fine in the OP's case, but it isn't even
necessary.


My point was that it would not give the desired results on a Unicode build.

IMO either use generic types (e.g. TCHAR, CString, generic-text mapped
functions) everywhere or use specific types (e.g. CStringA, CStringT, char,
printf()).

Mixing them makes non-portable code and introduces undesired behavior, as
demonstrated by the OP's code built with UNICODE/_UNICODE defined.

Jonathan Wood

unread,
Sep 25, 2008, 8:15:18 PM9/25/08
to
Doh! You're right. The format string must be the right type, but any
additional arguments are untyped.

'scuse me.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com

"Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in message

news:ulD3Ey1H...@TK2MSFTNGP04.phx.gbl...

Jonathan Wood

unread,
Sep 25, 2008, 8:16:25 PM9/25/08
to
So, if I follow, it doesn't matter in this case. Which is what I was
thinking.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com

"Alexander Grigoriev" <al...@earthlink.net> wrote in message
news:OVaNc$1HJHA...@TK2MSFTNGP02.phx.gbl...

Jonathan Wood

unread,
Sep 25, 2008, 8:16:56 PM9/25/08
to
Yes, I've already been corrected on this. The format string must be of the
correct type, but all arguments after that are untyped.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com

"Alexander Grigoriev" <al...@earthlink.net> wrote in message
news:eWszJA2...@TK2MSFTNGP06.phx.gbl...

Alexander Grigoriev

unread,
Sep 25, 2008, 8:23:57 PM9/25/08
to
Passing non-POD to a variadic function is not valid C++. Microsoft CSTring
documentation EXPLICITLY says that you need LPCTSTR cast.

"Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in message

news:OlJVjF2H...@TK2MSFTNGP05.phx.gbl...


> "Alexander Grigoriev" <al...@earthlink.net> wrote in message
> news:#H7kMA2H...@TK2MSFTNGP04.phx.gbl...
>> LPCTSTR cast of CString is always safe. There is NO cast to the "other"
>> string pointer (LPCXSTR) provided by CString; such a cast won't compile.
>>
>> For CString, C-style LPCTSTR cast (or LPCTSTR(s)) can only be interpreted
>> one way: by invoking LPCTSTR operator. There is no reinterpret_cast
>> interpretation for that, because the source is not a builtin type.
>>
>
>
> Ok. I'm not sure how any of that applies to and/or contradicts any
> statements I made :)
>
> The cast is is not only perfectly fine in the OP's case, but it isn't even
> necessary.
>
>
> My point was that it would not give the desired results on a Unicode
> build.
>

Both cast and non-cast won't magically make the argument with the necessary
type. Such is danger of printf and alike functions.


Jonathan Wood

unread,
Sep 25, 2008, 8:23:31 PM9/25/08
to
Mark,

> Jonathan Wood stated using the cast is more efficient, but I just looked
> at the disassembly of my test code (below) and it seems NOT using the cast
> is the most efficient. I could be wrong :)

Some time ago, I looked into this. At that time, passing a CString object
untyped to _tprintf() caused a copy of the CString to be created and then
passed to _tprintf(). It still works because of the memory layout. On the
other hand, passing the address of the string withing the CString object
requires no memory to be copied.

I suppose it's possible this has changed but I strongly suspect it has not.
If you have something that shows the typecast is not more efficent, please
share.

David Wilkinson

unread,
Sep 25, 2008, 9:11:03 PM9/25/08
to
Mark Salsbery [MVP] wrote:
> Jonathan Wood stated using the cast is more efficient, but I just looked
> at the disassembly of my test code (below) and it seems NOT using the
> cast is the most efficient. I could be wrong :)
>
> //
> // Test platform: VS 2008 SP1 on Vista Ultimate
> //
> // Unicode build!
> //
> CString kindOfFruit = _T("bananas");
> int howmany = 25;
> char bufff[250];
> sprintf(bufff, "You have %d %s", howmany, kindOfFruit);
> sprintf(bufff, "You have %d %s", howmany, (LPCTSTR)kindOfFruit );
> sprintf(bufff, "You have %d %s", howmany, static_cast<LPCTSTR>(kindOfFruit));
> sprintf(bufff, "You have %d %s", howmany, CT2A(kindOfFruit) );

Mark:

Maybe the assembly is not exactly the same, but don't the first three all give
the same "wrong" result, but without doing any actual conversion or memory
allocation?

While the last gives the "right" result, at the cost of a conversion (and a
memory allocation if the string is big enough).

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 9:16:05 PM9/25/08
to
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:OvPox42H...@TK2MSFTNGP02.phx.gbl...
> Mark,

>
> Some time ago, I looked into this. At that time, passing a CString object
> untyped to _tprintf() caused a copy of the CString to be created and then
> passed to _tprintf(). It still works because of the memory layout. On the
> other hand, passing the address of the string withing the CString object
> requires no memory to be copied.
>
> I suppose it's possible this has changed but I strongly suspect it has
> not. If you have something that shows the typecast is not more efficent,
> please share.


I posted the code along with my test platform info in the post you responded
to here.

I'm not agreeing or disagreeing with your comments.
I just looked at the disassembly of a non-optimized build of the code I
posted and it looked like not using the cast was the most efficient.
I also said I may be wrong. I'll leave it for someone else to
prove/disprove.

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++


>

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 9:26:31 PM9/25/08
to
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:OvPox42H...@TK2MSFTNGP02.phx.gbl...
>
> If you have something that shows the typecast is not more efficent, please
> share.

Here's what I see - VC 9 compiler, debug build, no optimizations.
Without cast and with...

sprintf(bufff, "You have %d %s", howmany, kindOfFruit);

00420E5A mov esi,esp
00420E5C mov eax,dword ptr [ebp-24h]
00420E5F push eax
00420E60 mov ecx,dword ptr [ebp-30h]
00420E63 push ecx
00420E64 push offset string "You have %d %s" (446B88h)
00420E69 lea edx,[ebp-134h]
00420E6F push edx
00420E70 call dword ptr [__imp__sprintf (0A1E650h)]
00420E76 add esp,10h
00420E79 cmp esi,esp
00420E7B call @ILT+5270(__RTC_CheckEsp) (41649Bh)


sprintf(bufff, "You have %d %s", howmany, (LPCTSTR)kindOfFruit );

00420E80 mov esi,esp
00420E82 lea ecx,[ebp-24h]
00420E85 call dword ptr
[__imp_ATL::CSimpleStringT<wchar_t,1>::operator wchar_t const * (0A1EB98h)]
00420E8B cmp esi,esp
00420E8D call @ILT+5270(__RTC_CheckEsp) (41649Bh)
00420E92 mov esi,esp
00420E94 push eax
00420E95 mov eax,dword ptr [ebp-30h]
00420E98 push eax
00420E99 push offset string "You have %d %s" (446B88h)
00420E9E lea ecx,[ebp-134h]
00420EA4 push ecx
00420EA5 call dword ptr [__imp__sprintf (0A1E650h)]
00420EAB add esp,10h
00420EAE cmp esi,esp
00420EB0 call @ILT+5270(__RTC_CheckEsp) (41649Bh)


I'm open to any interpretation of which is more efficient :)

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 9:46:41 PM9/25/08
to
"David Wilkinson" <no-r...@effisols.com> wrote in message
news:uCw5#S3HJH...@TK2MSFTNGP05.phx.gbl...

> Mark:
>
> Maybe the assembly is not exactly the same, but don't the first three all
> give the same "wrong" result, but without doing any actual conversion or
> memory allocation?
>
> While the last gives the "right" result, at the cost of a conversion (and
> a memory allocation if the string is big enough).


Sure.

I just threw the code out there (based on the OP's code) for anyone In this
discussion to test with.

I did all four to test the validity of the efficiency comments.
The static_cast version assembly is identical to the C-style cast.
And yes, the one that does the conversion takes the most instructions but
yields the correct result on a Unicode build.

I'm really not sure what's being discussed at this point - I only originally
meant to respond to the OP.

Mark

--
Mark Salsbery
Microsoft MVP - Visual C++

Jonathan Wood

unread,
Sep 25, 2008, 9:48:31 PM9/25/08
to
Yep. In your example, the typecast version calls the LPCTSTR operation
method, which is less efficient.

Note that this will not happen in a release build. However, I did a test of
my own and looked at the release code. It looks like the CString object is
not copied as I had earlier observed.

Therefore, in this partiuclar example (my test used _tprintf()), there
doesn't appear to be any difference between passing the CString object with
and without a typecast as an untyped argument.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com

"Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in message
news:ujW4sb3H...@TK2MSFTNGP05.phx.gbl...

Mark Salsbery [MVP]

unread,
Sep 25, 2008, 10:22:12 PM9/25/08
to
"Jonathan Wood" <jw...@softcircuits.com> wrote in message
news:#CFwRo3H...@TK2MSFTNGP02.phx.gbl...

>
> Note that this will not happen in a release build. However, I did a test
> of my own and looked at the release code. It looks like the CString object
> is not copied as I had earlier observed.
>
> Therefore, in this partiuclar example (my test used _tprintf()), there
> doesn't appear to be any difference between passing the CString object
> with and without a typecast as an untyped argument.
>

Confirmed.

sprintf(bufff, "You have %d %s", howmany, kindOfFruit);

00405596 mov eax,dword ptr [ebp-1B8h]
0040559C push eax
0040559D mov ecx,dword ptr [ebp-308h]
004055A3 push ecx
004055A4 push 413590h
004055A9 lea edx,[ebp-814h]
004055AF push edx
004055B0 call dword ptr [__imp__sprintf (4111E4h)]
004055B6 add esp,10h

sprintf(bufff, "You have %d %s", howmany, (LPCTSTR)kindOfFruit );

004055B9 lea ecx,[ebp-1B8h]
004055BF call dword ptr
[__imp_ATL::CSimpleStringT<wchar_t,1>::operator wchar_t const * (411838h)]
004055C5 push eax
004055C6 mov eax,dword ptr [ebp-308h]
004055CC push eax
004055CD push 4135A0h
004055D2 lea ecx,[ebp-814h]
004055D8 push ecx
004055D9 call dword ptr [__imp__sprintf (4111E4h)]
004055DF add esp,10h


Good to know. I appreciate the discussion, thanks!!

Cheers,

Hannes

unread,
Sep 26, 2008, 3:36:01 AM9/26/08
to
Our team has a million lines of source code to maintain; replacing printf
with Format, or even using typecasts with printf, is not an easy task. My
original post seeks a safe middle-way to use while our code is in a mixed
stage of CString and char*.

We know now that we'll be pretty safe either way.

I thank you all for your efforts and valuable inputs on this matter!!

Cheers,

/ Hannes.

"Hannes" wrote:

> Hi,
>
> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?
>
> The following MSDN article suggests it is NOT safe:
> http://msdn.microsoft.com/en-us/library/awkwbzyc(VS.71).aspx
> "...it is essential that you use an explicit type cast when passing a
> CString object to a function that takes a variable number of arguments.
> ...
> CString kindOfFruit = "bananas";
> int howmany = 25;
> printf( "You have %d %s\n", howmany, (LPCTSTR)kindOfFruit );"
>
>
> On the other hand, various newsgroup posts claim that CString was designed
> specifically in such a way that the (LPCTSTR) cast would not be needed with
> printf.
>
> We are a team of 40+ developers who are converting several large projects
> from char* to CString, and avoiding (LPCTSTR) would save us a lot of time.
>
> Can someone with insight please clarify what the software developing
> community should do in this case?
>
> Thanks,
>
> / Hannes.

David Wilkinson

unread,
Sep 26, 2008, 6:53:57 AM9/26/08
to
Mark Salsbery [MVP] wrote:
> I just threw the code out there (based on the OP's code) for anyone In
> this discussion to test with.
>
> I did all four to test the validity of the efficiency comments.
> The static_cast version assembly is identical to the C-style cast.
> And yes, the one that does the conversion takes the most instructions
> but yields the correct result on a Unicode build.
>
> I'm really not sure what's being discussed at this point - I only
> originally meant to respond to the OP.


Mark:

I'm not quite sure either...

I certainly agree with you that mixed char/TCHAR code is a major source of
confusion and error.

John Sw

unread,
Sep 26, 2008, 12:09:14 PM9/26/08
to Mark Salsbery [MVP]
On 9/25/2008 5:34 PM Mark Salsbery [MVP] wrote:
> "John Sw" <jswa...@noemail.noemail> wrote in message
> news:uarui70...@TK2MSFTNGP03.phx.gbl...
>>
>> It should really use static_cast<LPCTSTR>(), which will not cause
>> unexpected conversions and is also Unicode safe.
>
>
> Safe? Maybe, but it doesn't work, unless the 'b' in the string
> "bananas" is all you wanted. Try it.

You're right and I wasn't thinking. The cast is Unicode safe, but it
isn't correct when you are calling a non-Unicode function. I'll still
stand by the fact that you should use C++ casts, not C casts and you
should cast varargs arguments if you want to be sure you get what you
want. If the type is known, unnecessary casts of any type are a bad idea.

John Sw

unread,
Sep 26, 2008, 12:16:58 PM9/26/08
to
On 9/25/2008 5:49 PM Jonathan Wood wrote:
> I must be getting old. I still use the only type of cast available back
> when I learned C.
>
> Can anyone tell me the difference, specifically, in the code generated
> by the compiler for these two statements?
>
> 1. p = static_cast<LPCTSTR>(myStr);
> 2. p = (LPCTSTR)myStr;
>
> Thanks.

In this case, there should be no difference. The problem is that the
(LPCTSTR) cast might be doing any of three different types of casts
(changing type, safe conversions, const casting) all at the same time
and they might not all be what you want. In some cases using this type
of cast instead of a dynamic_cast can result in you getting the wrong
pointer to an object (e.g., with multiple inheritance).

I'm definitely getting old, but one thing I've learned in the last
twenty plus years is that code generation is only a small part of what
we need to be concerned about.

Joseph M. Newcomer

unread,
Sep 26, 2008, 12:55:17 PM9/26/08
to
The bug is not related to the cast at all. Whether the cast is present or not, passing a
pointer to a Unicode string to a printf (which uses 8-bit characters) is going to produce
incorrect output if compiled for Unicode.
joe

On Thu, 25 Sep 2008 13:55:55 -0600, "Jonathan Wood" <jw...@softcircuits.com> wrote:

>Mark,


>
>>> Is it safe to pass a CString to printf without explicit (LPCTSTR) cast?
>>

>> Yes, and that's how it should be passed - with NO cast.
>>

>> Using the cast will hide a bug if you move the code to a Unicode build.
>
>I must confess that I'm pretty surprised someone would suggest in a thread
>like this that the typecast can cause a bug, and yet not even hint at what
>that bug might be.
>

>I posted that the typecase can be more efficient, and then explained the
>details of why.
>
>Any chance you'll do the same and explain why we should use the less
>efficient syntax?

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Joseph M. Newcomer

unread,
Sep 26, 2008, 1:03:29 PM9/26/08
to
BUt what if the programmer used %S as a formatting string? Passing an LPCWSTR is
perfectly valid and will work. There is NO type-checking of the arguments in the ...
part.

%hs always means "8-bit string formatting"; %ls always means "wide-character string
formatting", %S means "the other kind of formatting", that is, it means %hs for a
w-formatting operation and %ls for an a-formatting operation.

So it is always legitimate to write
CStringW s;
s = ...;
printf("%ls", s);
or
printf("%ls", (LPCWSTR)s);

so type-checking makes no sense, since you can't, without parsing the formatting string
(which might not be a compile-time constant) tell what types are required. So the
compiler simply trusts that you know what you are doing.
joe

On Thu, 25 Sep 2008 15:51:26 -0600, "Jonathan Wood" <jw...@softcircuits.com> wrote:

>A cast to LPCTSTR in an argument to printf() in a Unicode build, should
>cause a warning/error. In a Unicode build, LPCTSTR is LPCWSTR and printf()
>expects LPCSTR.

Mark Salsbery [MVP]

unread,
Sep 26, 2008, 1:34:07 PM9/26/08
to
"John Sw" <jswa...@noemail.noemail> wrote in message
news:48DD092A...@noemail.noemail...

> I'll still stand by the fact that you should use C++ casts, not C casts
> and you should cast varargs arguments if you want to be sure you get what
> you want. If the type is known, unnecessary casts of any type are a bad
> idea.


Agreed!

Leo Violette

unread,
Oct 9, 2008, 4:43:26 PM10/9/08
to
Not true. There are cases where it is perfectly valid to pass a UNICODE
character pointer to a printf style function.

Unless you expect the compiler to parse the format string (the %s's and %d's
and whatnot) to determine what arguments would be
valid and then give a warning if one of those arguments don't match. I
certainly don't expect that of the compiler. They would have to update
the compiler everytime some coder decides to enhance the printf style
functions.

printf allows a %S (note capital S). If it encounters one, then it's
expecting a wchar_t*.
Likewise, if wprintf encounters a %S, it wll expect a char*.
Leo V.

"Jonathan Wood" <jw...@softcircuits.com> wrote in message

news:eXWDyj1H...@TK2MSFTNGP05.phx.gbl...


>A cast to LPCTSTR in an argument to printf() in a Unicode build, should
>cause a warning/error. In a Unicode build, LPCTSTR is LPCWSTR and printf()
>expects LPCSTR.
>

> --
> Jonathan Wood
> SoftCircuits Programming
> http://www.softcircuits.com
>
> "Mark Salsbery [MVP]" <MarkSalsbery[MVP]@newsgroup.nospam> wrote in

> message news:OM7FKP1H...@TK2MSFTNGP06.phx.gbl...
>>I was wrong again - the code I described as a bug (using that cast in a
>>Unicode build) WOULD compile with no warning/error.
>>

0 new messages