|
MFC got
BOOL AfxExtractSubstring(CString strDest, CString strToExtractFrom, int nSubstringNumber, CString strSeps)<br /> function, or something like this.
Could it be possible to add ExtractSubstring function to the CStr class?
It may look like follow:
BOOL ExtractSubstring( CStr& strDest, int nSubstringNumber, CStr& strSeps )<br />
BOOL ExtractSubstring( LPCTSTR pszDest, int nSubstringNumber, LPCTSTR pszSeps )<br /> Got source code and simplest test app, would post it to anywhere.
Sincerely,
dandy_andy.
|
|
|
|
|
Why is all this so windows-centric? I do not have windows.h on my system..
|
|
|
|
|
http://codeguru.earthweb.com/string/faststring.shtml
Kochise
In Cod we trust !
|
|
|
|
|
http://codeguru.earthweb.com/string/HuangString.shtml
Harsh ? Noooon, just difficult to find an unique name
Kochise
In Cod we trust !
|
|
|
|
|
When using CString I can assign a character value to the string:
CString MyString = 'A';
This will leave MyString with "A".
I haven't tried it, but I suspect that the integer assignment in CStr will cause it to work a little differently:
CStr MyStr = 'A';
I suspect this will leave MyStr with "65".
|
|
|
|
|
Hi,
Yes you're right.
In the next update I'll add a method to deal with
TCHAR unfortunately it cannot be done to automatically
use a TCHAR from = and << operators and get the
'right' results, because of the many numeric
overloads.
soso
soso
|
|
|
|
|
I'm a embedded coder, and I came to the world of Windows coding. I encountered many messy practices, and above all, an incredible waste of time and power, especialy while using the MFC...
This set of classes, developped by MicroSoft, are VERY easy to use, but Hell ! Sooooo sloooow ! Using replacement routines based on TCHAR are easily 100 times faster, no tricks at all, just brute power...
In embedded, while coding with CPU running at 8, 16 or 32 MHz, with less than 256 KB of RAM and 512 KB of ROM, you have to code very efficiently. What it's obviously not in the goal of MFC, allowing coder to release a program in days, but loosing time at each run !
Now the interrest I find in this class is not to chain operations, what I agree with most of you is an incredible stupidity. But 'soso_pub' never stated to chain thousands of operations.
As an assembly coder first, the development orientation should be 'vertical', one instruction per line. So that in case of breakpoint, you don't have to look which instruction in the line caused the break...
The PRIMAL interrest I have in this class is it's construction over a clean TCHAR* coding, I can improve in case of needs, and perhaps port on Pocket PC, which plateform needs some efficient coding, even running at 400 MHz...
SOooo, thanks alot dear 'soso_pub', I'm saving your component in my 'Essential' folder
Kochise
In Cod we trust !
|
|
|
|
|
Kochise wrote:
Using replacement routines based on TCHAR are easily 100 times faster, no tricks at all, just brute power...
And this speed gain is totally wasted, because the limiting factor is the user pushing a mouse slowly across his mousepad to click the next button.
As an embedded coder, you probably are acustomed to program machines with a computing power more like the keyboard processor of a normal PC. In this environment savings of 32 byte or 12 processor cycles may matter.
In application development, most of the times the development costs (through application price!) are much more important than the price to replace your old 500Mhz PIII with a new Multi Ghz PC.
Here ease of use of a framework does matter much more(and MFC is abysmally bad here - or we won't need all this CP stuff), together with experience of the developers.
If your application proves too slow, you profile it and revise only the parts or algorithms that form the bottleneck. That way you may waste even more resources, but your application does what it is suposed to do in a cost-efficient way.
My opinions may have changed, but not the fact that I am right.
|
|
|
|
|
...if the code was using only once the CStr !
Now for instance, use a modifed version of SuperGrid ( http://www.codeproject.com/listctrl/supergrid.asp ) in order to use CStr instead of CString, works on 100+ MB text files (I have such DXF files), and you'll understand what I mean !
But obviously you're not working on heavy-duty projects, just dialog based ones Naaah, pulling your leg
Get nice habits, code efficently, don't just do it on purposes !
Kochise
PS : For instance, even little CImageList::Draw(...) can be speed up ( http://www.codeproject.com/useritems/CSkinProgress.asp )
In Cod we trust !
|
|
|
|
|
Kochise wrote:
In Cod we trust !
Isn't that the fish in fish-n-chips? And you trust in something like THAT?
My application involves operation on spectra, and these can get amazingly big: a few hundred MB are not unusual.
But even with that, only a small portion of the application is really speed sensitive, and most of that are numeric algorithms totally without MFC. Using a design which tries not to copy the data does more speedup than optimizing the algorithms (which I don't understand anyway )
Within the GUI, it does not matter if writing an XML file could be an order of magnitude faster using char* and legacy C string-routines.
The writing goes to a disk file, and that forms the bottleneck.
With your SuperGrid example, I would say that using a callback approach instead of copying the data into the grid would improve speed sufficiently. Most of the time using different algorithms is much easier than optimizing existing ones.
My opinions may have changed, but not the fact that I am right.
|
|
|
|
|
Add this part of code at the end of [BOOL CTestApp::InitInstance()] in 'test.cpp' :
AfxMessageBox( _T("All tests succeeded!") );
CString* oStrCStringTest;
CStr* oStrCStrTest;
CString* oStrCStringPointer;
CStr* oStrCStrPointer;
int nLoop;
int nDichoMin;
int nDichoMed;
int nDichoMax;
int nDichoDlt;
CPtrArray aCString;
CPtrArray aCStr;
SYSTEMTIME sSysTimeStartCString;
SYSTEMTIME sSysTimeStartCStr;
SYSTEMTIME sSysTimeEndCString;
SYSTEMTIME sSysTimeEndCStr;
FILETIME sTimeFileStart;
FILETIME sTimeFileEnd;
ULARGE_INTEGER uLargeStart;
ULARGE_INTEGER uLargeEnd;
GetLocalTime(&sSysTimeStartCString);
for(
nLoop = 0;
nLoop < 100000;
nLoop += 1
)
{
oStrCStringTest = new CString();
oStrCStringTest->Format("%X", (rand() << 16) | rand());
nDichoMin = 0;
nDichoDlt = 0;
nDichoMax = aCString.GetSize();
if(nDichoMax > 0)
{
do
{
nDichoMed = (nDichoMin + nDichoMax) / 2;
oStrCStringPointer = (CString*) aCString.GetAt(nDichoMed);
if(oStrCStringTest->Compare(*oStrCStringPointer) > 0)
{
nDichoMin = nDichoMed;
nDichoDlt = 1;
}
else if(oStrCStringTest->Compare(*oStrCStringPointer) < 0)
{
nDichoMax = nDichoMed;
}
else
{
break;
}
}
while(nDichoMax > (nDichoMin + 1));
nDichoMed = (nDichoMin + nDichoMax) / 2;
aCString.InsertAt(nDichoMed + nDichoDlt, oStrCStringTest);
}
else
{
aCString.Add(oStrCStringTest);
}
}
GetLocalTime(&sSysTimeEndCString);
for(
nLoop = 0;
nLoop < aCString.GetSize();
nLoop += 1
)
{
delete aCString.GetAt(nLoop);
}
aCString.RemoveAll();
GetLocalTime(&sSysTimeStartCStr);
for(
nLoop = 0;
nLoop < 100000;
nLoop += 1
)
{
oStrCStrTest = new CStr();
oStrCStrTest->Format("%X", (rand() << 16) | rand());
nDichoMin = 0;
nDichoDlt = 0;
nDichoMax = aCString.GetSize();
if(nDichoMax > 0)
{
do
{
nDichoMed = (nDichoMin + nDichoMax) / 2;
oStrCStrPointer = (CStr*) aCStr.GetAt(nDichoMed);
if(oStrCStrTest->Compare(*oStrCStrPointer) > 0)
{
nDichoMin = nDichoMed;
nDichoDlt = 1;
}
else if(oStrCStrTest->Compare(*oStrCStrPointer) < 0)
{
nDichoMax = nDichoMed;
}
else
{
break;
}
}
while(nDichoMax > (nDichoMin + 1));
nDichoMed = (nDichoMin + nDichoMax) / 2;
aCStr.InsertAt(nDichoMed + nDichoDlt, oStrCStrTest);
}
else
{
aCStr.Add(oStrCStrTest);
}
}
GetLocalTime(&sSysTimeEndCStr);
for(
nLoop = 0;
nLoop < aCStr.GetSize();
nLoop += 1
)
{
delete aCStr.GetAt(nLoop);
}
aCStr.RemoveAll();
SystemTimeToFileTime(&sSysTimeStartCString, &sTimeFileStart);
uLargeStart.QuadPart = sTimeFileStart.dwHighDateTime;
uLargeStart.QuadPart <<= 32;
uLargeStart.QuadPart |= sTimeFileStart.dwLowDateTime;
SystemTimeToFileTime(&sSysTimeEndCString, &sTimeFileEnd);
uLargeEnd.QuadPart = sTimeFileEnd.dwHighDateTime;
uLargeEnd.QuadPart <<= 32;
uLargeEnd.QuadPart |= sTimeFileEnd.dwLowDateTime;
s.Format("CString : %d ms\n", (int) (uLargeEnd.QuadPart - uLargeStart.QuadPart) / 10000);
SystemTimeToFileTime(&sSysTimeStartCStr, &sTimeFileStart);
uLargeStart.QuadPart = sTimeFileStart.dwHighDateTime;
uLargeStart.QuadPart <<= 32;
uLargeStart.QuadPart |= sTimeFileStart.dwLowDateTime;
SystemTimeToFileTime(&sSysTimeEndCStr, &sTimeFileEnd);
uLargeEnd.QuadPart = sTimeFileEnd.dwHighDateTime;
uLargeEnd.QuadPart <<= 32;
uLargeEnd.QuadPart |= sTimeFileEnd.dwLowDateTime;
s2.Format("CStr : %d ms\n", (int) (uLargeEnd.QuadPart - uLargeStart.QuadPart) / 10000);
CString oCStrOperatorMissing1;
CString oCStrOperatorMissing2;
oCStrOperatorMissing1 = s;
oCStrOperatorMissing2 = s2;
AfxMessageBox( _T(oCStrOperatorMissing1 + oCStrOperatorMissing2) );
return TRUE;
}
Run the test, then after the "All tests succeeded!" message box, wait 30 seconds without touching to your computer, and get such a result :
CString : 28125 ms (28 seconds)
CStr : 3328 ms (3 seconds)
The process is to sort 100 thousands random strings ! The set of strings is not the same (get with [rand()])... To avoid to test the copy routines (what you seems to hate), I use pointers !
So CStr is there almost 9 times faster than CString. The 100 times faster was obtained in very particular cases, when there wasn't any call to others functions (such [rand()])
So, please believe in 'soso_pub'
Kochise
PS : Hey 'soso_pub', there is some operator missing, as HeavyHenke said
In Cod we trust !
|
|
|
|
|
Kochise wrote:
CString : 28125000 ms (28 seconds)
CStr : 3328000 ms (3 seconds)
Wow... I have to admit that I wasn't aware at all about the performance difference between CString and CStr. Thanks for the comparision, great!
Kochise wrote:
PS : Hey 'soso_pub', there is some operator missing, as HeavyHenke said
I will make a new update in a few days I think, I'll add some more operators plus some fixes and enhancements that are currently on my computer.
soso
|
|
|
|
|
But not in the same stress some people awaited for the 4th release of Harry Potter
Kochise
PS : I'm myself about to release my update of CSkinProgress. And I'm sure everyone will fall in love with
In Cod we trust !
|
|
|
|
|
Well here my results with VS 2005:
---------------------------
StringTest
---------------------------
CString : 4859 ms
CStr : 15891 ms
---------------------------
OK
---------------------------
|
|
|
|
|
There is a major bug with your test app in the code which tests CStr.
nDichoMax = aCString.GetSize(); is incorrect, and should be nDichoMax = aCStr.GetSize();
When this is corrected, you will find that CString is faster than CStr (at least it was on my machine when compiled with VC++ 2005 SP1).
There is another flaw with the test app in that the same random strings are not used for testing each class. I have revised your code to address the bug and design flaw, and have added a new test which uses std:string. I ran three tests changing the order is which the string classes were tested and my results are shown below:
CString 13119
CStr 14340
std::string 12068
std::string 9494
CString 10916
CStr 10385
CStr 14121
std::string 12578
CString 10715
I know there are six possible combinations, but I could only be bothered to run the three above
Conclusion: use std::string or CString!
AfxMessageBox( _T("All tests succeeded!") );<br />
<br />
<br />
CString* oStrCStringTest;<br />
CStr* oStrCStrTest;<br />
CString* oStrCStringPointer;<br />
CStr* oStrCStrPointer;<br />
std::string* oStrSTLStringTest;<br />
std::string* oStrSTLStringPointer;<br />
<br />
int nLoop;<br />
int nDichoMin;<br />
int nDichoMed;<br />
int nDichoMax;<br />
int nDichoDlt;<br />
CPtrArray aCString;<br />
CPtrArray aCStr;<br />
CPtrArray aSTLString;<br />
SYSTEMTIME sSysTimeStartCString;<br />
SYSTEMTIME sSysTimeStartCStr;<br />
SYSTEMTIME sSysTimeEndCString;<br />
SYSTEMTIME sSysTimeEndCStr;<br />
SYSTEMTIME sSysTimeStartSTLString;<br />
SYSTEMTIME sSysTimeEndSTLString;<br />
FILETIME sTimeFileStart;<br />
FILETIME sTimeFileEnd;<br />
ULARGE_INTEGER uLargeStart;<br />
ULARGE_INTEGER uLargeEnd;<br />
<br />
std::vector<std::string> vRandomStrings;<br />
std::ostringstream osRandomString;<br />
<br />
vRandomStrings.reserve(100000);<br />
<br />
for (nLoop = 0; nLoop < 100000; ++nLoop)<br />
{<br />
osRandomString << std::hex << std::uppercase << ((rand() << 16) | rand());
vRandomStrings.push_back(osRandomString.str());<br />
osRandomString.seekp(0);<br />
}<br />
<br />
<br />
<br />
GetLocalTime(&sSysTimeStartCStr);<br />
for(<br />
nLoop = 0;<br />
nLoop < 100000;<br />
++nLoop<br />
)<br />
{<br />
oStrCStrTest = new CStr(vRandomStrings[nLoop].c_str());<br />
<br />
nDichoMin = 0;<br />
nDichoDlt = 0;<br />
nDichoMax = aCStr.GetSize();<br />
if(nDichoMax > 0)<br />
{<br />
do<br />
{<br />
nDichoMed = (nDichoMin + nDichoMax) / 2;<br />
oStrCStrPointer = (CStr*) aCStr.GetAt(nDichoMed);<br />
if(oStrCStrTest->Compare(*oStrCStrPointer) > 0)<br />
{<br />
nDichoMin = nDichoMed;<br />
nDichoDlt = 1;<br />
}<br />
else if(oStrCStrTest->Compare(*oStrCStrPointer) < 0)<br />
{<br />
nDichoMax = nDichoMed;<br />
}<br />
else<br />
{<br />
break;<br />
}<br />
}<br />
while(nDichoMax > (nDichoMin + 1));<br />
<br />
nDichoMed = (nDichoMin + nDichoMax) / 2;<br />
aCStr.InsertAt(nDichoMed + nDichoDlt, oStrCStrTest); <br />
}<br />
else<br />
{<br />
aCStr.Add(oStrCStrTest); <br />
}<br />
}<br />
GetLocalTime(&sSysTimeEndCStr);<br />
<br />
for(<br />
nLoop = 0;<br />
nLoop < aCStr.GetSize();<br />
++nLoop<br />
)<br />
{<br />
delete aCStr.GetAt(nLoop);<br />
}<br />
aCStr.RemoveAll();<br />
<br />
<br />
GetLocalTime(&sSysTimeStartSTLString);<br />
for(<br />
nLoop = 0;<br />
nLoop < 100000;<br />
++nLoop<br />
)<br />
{<br />
oStrSTLStringTest = new std::string(vRandomStrings[nLoop].c_str());<br />
<br />
nDichoMin = 0;<br />
nDichoDlt = 0;<br />
nDichoMax = aSTLString.GetSize();<br />
if(nDichoMax > 0)<br />
{<br />
do<br />
{<br />
nDichoMed = (nDichoMin + nDichoMax) / 2;<br />
oStrSTLStringPointer = (std::string*) aSTLString.GetAt(nDichoMed);<br />
if(oStrSTLStringTest->compare(*oStrSTLStringPointer) > 0)<br />
{<br />
nDichoMin = nDichoMed;<br />
nDichoDlt = 1;<br />
}<br />
else if(oStrSTLStringTest->compare(*oStrSTLStringPointer) < 0)<br />
{<br />
nDichoMax = nDichoMed;<br />
}<br />
else<br />
{<br />
break;<br />
}<br />
}<br />
while(nDichoMax > (nDichoMin + 1));<br />
<br />
nDichoMed = (nDichoMin + nDichoMax) / 2;<br />
aSTLString.InsertAt(nDichoMed + nDichoDlt, oStrSTLStringTest); <br />
}<br />
else<br />
{<br />
aSTLString.Add(oStrSTLStringTest); <br />
}<br />
}<br />
GetLocalTime(&sSysTimeEndSTLString);<br />
<br />
for(<br />
nLoop = 0;<br />
nLoop < aSTLString.GetSize();<br />
++nLoop<br />
)<br />
{<br />
delete aSTLString.GetAt(nLoop);<br />
}<br />
aSTLString.RemoveAll();<br />
<br />
<br />
GetLocalTime(&sSysTimeStartCString);<br />
for(<br />
nLoop = 0;<br />
nLoop < 100000;<br />
++nLoop<br />
)<br />
{<br />
oStrCStringTest = new CString(vRandomStrings[nLoop].c_str());<br />
<br />
nDichoMin = 0;<br />
nDichoDlt = 0;<br />
nDichoMax = aCString.GetSize();<br />
if(nDichoMax > 0)<br />
{<br />
do<br />
{<br />
nDichoMed = (nDichoMin + nDichoMax) / 2;<br />
oStrCStringPointer = (CString*) aCString.GetAt(nDichoMed);<br />
if(oStrCStringTest->Compare(*oStrCStringPointer) > 0)<br />
{<br />
nDichoMin = nDichoMed;<br />
nDichoDlt = 1;<br />
}<br />
else if(oStrCStringTest->Compare(*oStrCStringPointer) < 0)<br />
{<br />
nDichoMax = nDichoMed;<br />
}<br />
else<br />
{<br />
break;<br />
}<br />
}<br />
while(nDichoMax > (nDichoMin + 1));<br />
<br />
nDichoMed = (nDichoMin + nDichoMax) / 2;<br />
aCString.InsertAt(nDichoMed + nDichoDlt, oStrCStringTest); <br />
}<br />
else<br />
{<br />
aCString.Add(oStrCStringTest); <br />
}<br />
}<br />
GetLocalTime(&sSysTimeEndCString);<br />
<br />
for(<br />
nLoop = 0;<br />
nLoop < aCString.GetSize();<br />
++nLoop<br />
)<br />
{<br />
delete aCString.GetAt(nLoop);<br />
}<br />
aCString.RemoveAll();<br />
<br />
<br />
<br />
SystemTimeToFileTime(&sSysTimeStartCString, &sTimeFileStart);<br />
uLargeStart.QuadPart = sTimeFileStart.dwHighDateTime;<br />
uLargeStart.QuadPart <<= 32;<br />
uLargeStart.QuadPart |= sTimeFileStart.dwLowDateTime;<br />
<br />
SystemTimeToFileTime(&sSysTimeEndCString, &sTimeFileEnd);<br />
uLargeEnd.QuadPart = sTimeFileEnd.dwHighDateTime;<br />
uLargeEnd.QuadPart <<= 32;<br />
uLargeEnd.QuadPart |= sTimeFileEnd.dwLowDateTime;<br />
<br />
s.Format("CString : %d ms\n", (int) (uLargeEnd.QuadPart - uLargeStart.QuadPart) / 10000);<br />
<br />
SystemTimeToFileTime(&sSysTimeStartCStr, &sTimeFileStart);<br />
uLargeStart.QuadPart = sTimeFileStart.dwHighDateTime;<br />
uLargeStart.QuadPart <<= 32;<br />
uLargeStart.QuadPart |= sTimeFileStart.dwLowDateTime;<br />
<br />
SystemTimeToFileTime(&sSysTimeEndCStr, &sTimeFileEnd);<br />
uLargeEnd.QuadPart = sTimeFileEnd.dwHighDateTime;<br />
uLargeEnd.QuadPart <<= 32;<br />
uLargeEnd.QuadPart |= sTimeFileEnd.dwLowDateTime;<br />
<br />
s2.Format("CStr : %d ms\n", (int) (uLargeEnd.QuadPart - uLargeStart.QuadPart) / 10000);<br />
<br />
CStr s3;<br />
SystemTimeToFileTime(&sSysTimeStartSTLString, &sTimeFileStart);<br />
uLargeStart.QuadPart = sTimeFileStart.dwHighDateTime;<br />
uLargeStart.QuadPart <<= 32;<br />
uLargeStart.QuadPart |= sTimeFileStart.dwLowDateTime;<br />
<br />
SystemTimeToFileTime(&sSysTimeEndSTLString, &sTimeFileEnd);<br />
uLargeEnd.QuadPart = sTimeFileEnd.dwHighDateTime;<br />
uLargeEnd.QuadPart <<= 32;<br />
uLargeEnd.QuadPart |= sTimeFileEnd.dwLowDateTime;<br />
<br />
s3.Format("STLString : %d ms\n", (int) (uLargeEnd.QuadPart - uLargeStart.QuadPart) / 10000);<br />
<br />
<br />
CString oCStrOperatorMissing1;<br />
CString oCStrOperatorMissing2;<br />
CString oCStrOperatorMissing3;<br />
<br />
oCStrOperatorMissing1 = s;<br />
oCStrOperatorMissing2 = s2;<br />
oCStrOperatorMissing3 = s3;<br />
<br />
AfxMessageBox( _T(oCStrOperatorMissing1 + oCStrOperatorMissing2 + oCStrOperatorMissing3) );<br />
<br />
<br />
return TRUE;<br />
}<br />
|
|
|
|
|
Thanks, obviously the cache has its importance (the order of running which API first). And I guess that CString benefit of a better integration inside the Windows' core, that may explain things...
Kochise
In Code we trust !
|
|
|
|
|
One of the design goals of the STL was efficiency, so that people wouldn't be tempted to write their own versions in the hope that it would be much faster. However, some STL implementations are better than others, so it's worth trying out various implementations if you are really concerned about efficiency.
|
|
|
|
|
Problem with templates is that they mostly depends on the compiler's abilities, and that their syntax isn't widely taught or easy to understand (a bit like imperative vs functional languages). The Boost libraries (templates) are good yet not widely used due to these problems... Note also that STL doesn't deals quite well with Unicode/Utf so I guess until you find a good template string library, hand-made ones might still have a future...
Kochise
In Code we trust !
|
|
|
|
|
But not asking anybody to play 1:5
Kochise
In Cod we trust !
|
|
|
|
|
Kochise wrote:
Now the interrest I find in this class is not to chain operations, what I agree with most of you is an incredible stupidity. But 'soso_pub' never stated to chain thousands of operations.
As an assembly coder first, the development orientation should be 'vertical', one instruction per line. So that in case of breakpoint, you don't have to look which instruction in the line caused the break...
Yep, this seems to be the major drawback, but for example I prefer something like str.TrimLeft().TrimRight() instead of writing a line for each trim (I'm talking from the perspective of CString here who - if my memory serves me right - doesn't have a Trim operation that does left and right trim).
Anyway, you can chain or you can not chain. Programmer's choice.
Kochise wrote:
SOooo, thanks alot dear 'soso_pub', I'm saving your component in my 'Essential' folder
You're welcomed!
soso
|
|
|
|
|
CStr t = "this is a test";
CStr f;
f.Format(_T("%s-%s"),t);
Crashes. I believe due to the various casting opertors that t is not ending up a pointer to a const char* and crashes the internal formatting routine.
It works if you cast the var arg to a LPCTSTR
CStr t = "this is a test";
CStr f;
f.Format(_T("%s-%s"),(LPCTSTR)t);
The CStr class should be able to handle this internally somehow I think.
|
|
|
|
|
Anonymous wrote:
CStr t = "this is a test";
CStr f;
f.Format(_T("%s-%s"),t);
Perhaps it is due to an error in your code: you want two strings à la %s to be formatted, but supply only one parameter t .
Best Regards,
Christian Richardt
Those who know don't talk.
Those who talk don't know.
|
|
|
|
|
First, as someone pointed in another reply you want two strings to be formatted but you supply only one parameter.
Second, Format uses a variable-arguments list and I don't think it's possible to find argument types at runtime (this is the reason you have to supply %s, %d, etc in format string). So instead of passing a LPCTSTR as parameter you are passing a CStr and this is why it crashes, besides supplying only one parameter.
I don't know right now a solution to use raw CStr as arguments, probabily introducing a new format specifier like %q. But in case of %s automatic conversion from CStr to the internal LPCTSTR will still not work.
Regarding casting operators with the current implementation if you try to use a CStr in the right part of << you will get an ambiguosity error. It is fixed on my computer -plus other small bugs/enhancements-, I'll have to update it sometimes soon.
soso
|
|
|
|
|
The extra format specifier was a typo and not really related to the crash at all since that part gets ignored by the internal formatting routine.
Anyway, I was looking at other string implementations and they suffer from the same problem which means that MFC must have been doing some hacky workarounds to make the code (that I really got used to using ) work.
So, I contend it is not really a bug afterall and the correct "fix" is for the client (me) to cast the objects to a const char* pointer before calling the Format() function which as I stated in my first post, does work as expected.
Thanks for the quick followups.
|
|
|
|
|
The reason CString works is because its first (and only) data member is a pointer to its char array. All its internal data is stored offset to this pointer, and does some iffy negative pointer offsetting to get to it.
So when you pass a CString to a formatting routine, it gets a verbatim copy of the CStrings member info, which happens to be a string pointer, just what it was looking for.
There is a temptation to make the first member of CStr a pointer to its own string data, but then the following member data would be interpreted as the following formatting arguments, I think. So that wouldnt work.
|
|
|
|
|