您的位置:首页 > 其它

CString Management_by Joseph M. Newcomer@code project

2006-09-12 22:32 447 查看
CStringconvert(BSTRb)
{
if(b==NULL)
returnCString(_T(""));
CStrings(b);//inUNICODEmode
returns;
}

IfyouareinANSImode,youneedtoconvertthestringinamorecomplexfashion.Thiswillaccomplishit.Notethatthiscodeusesthesameargumentvaluesto
::WideCharToMultiByte
thattheimplicitconstructorfor
CString
uses,soyouwouldusethistechniqueonlyifyouwantedtochangetheseparameterstodotheconversioninsomeotherfashion,forexample,specifyingadifferentdefaultcharacter,adifferentsetofflags,etc.


Collapse
CStringconvert(BSTRb)
{
CStrings;
if(b==NULL)
returns;//emptyforNULLBSTR
#ifdefUNICODE
s=b;
#else
LPSTRp=s.GetBuffer(SysStringLen(b)+1);
::WideCharToMultiByte(CP_ACP,//ANSICodePage
0,//noflags
b,//sourcewidecharstring
-1,//assumeNUL-terminated
p,//targetbuffer
SysStringLen(b)+1,//targetbufferlength
NULL,//usesystemdefaultchar
NULL);//don'tcareifdefaultused
s.ReleaseBuffer();
#endif
returns;
}

NotethatIdonotworryaboutwhathappensifthe
BSTR
containsUnicodecharactersthatdonotmaptothe8-bitcharacterset,becauseIspecify
NULL
asthelasttwoparameters.Thisisthesortofthingyoumightwanttochange.

VARIANTtoCString

Actually,I'veneverdonethis;Idon'tworkinCOM/OLE/ActiveXwherethisisanissue.ButIsawapostingbyRobertQuirkonthe
microsoft.public.vc.mfc
newsgrouponhowtodothis,anditseemedsillynottoincludeitinthisessay,sohereitis,withabitmoreexplanationandelaboration.Anyerrorsrelativetowhathewrotearemyfault.

A
VARIANT
isagenericparameter/returntypeinCOMprogramming.Youcanwritemethodsthatreturnatype
VARIANT
,andwhichtypethefunctionreturnsmay(andoftendoes)dependontheinputparameterstoyourmethod(forexample,inAutomation,dependingonwhichmethodyoucall,
IDispatch::Invoke
mayreturn(viaoneofitsparameters)a
VARIANT
whichholdsa
BYTE
,a
WORD
,an
float
,a
double
,adate,a
BSTR
,andaboutthreedozenothertypes(seethespecificationsofthe
VARIANT
structureintheMSDN).Intheexamplebelow,itisassumedthatthetypeisknowntobeavariantoftype
BSTR
,whichmeansthatthevalueisfoundinthestringreferencedby
bstrVal
.Thistakesadvantageofthefactthatthereisaconstructorwhich,inanANSIapplication,willconvertavaluereferencedbyan
LPCWCHAR
toa
CString
(see
BSTR
-to-
CString
).InUnicodemode,thisturnsouttobethenormal
CString
constructor.Seethecaveatsaboutthedefault
::WideCharToMultibyte
conversionandwhetherornotyoufindtheseacceptable(mostly,youwill).

VARIANTvaData;

vaData=m_com.YourMethodHere();
ASSERT(vaData.vt==VT_BSTR);

CStringstrData(vaData.bstrVal);

Notethatyoucouldalsomakeamoregenericconversionroutinethatlookedatthe
vt
field.Inthiscase,youmightconsidersomethinglike:

CStringVariantToString(VARIANT*va)
{
CStrings;
switch(va->vt)
{/*vt*/
caseVT_BSTR:
returnCString(vaData->bstrVal);
caseVT_BSTR|VT_BYREF:
returnCString(*vaData->pbstrVal);
caseVT_I4:
s.Format(_T("%d"),va->lVal);
returns;
caseVT_I4|VT_BYREF:
s.Format(_T("%d"),*va->plVal);
caseVT_R8:
s.Format(_T("%f"),va->dblVal);
returns;
...remainingcasesleftasanExerciseForTheReader
default:
ASSERT(FALSE);//unknownVARIANTtype(thisASSERTisoptional)
returnCString("");
}/*vt*/
}

LoadingSTRINGTABLEvalues

Ifyouwanttocreateaprogramthatiseasilyportedtootherlanguages,youmustnotincludenative-languagestringsinyoursourcecode.(Fortheseexamples,I'lluseEnglish,sincethatismynativelanguage(aberIchkanneinbischenDeutschsprechen).Soitisvery
badpracticetowrite

CStrings="Thereisanerror";

Instead,youshouldputallyourlanguage-specificstrings(except,perhaps,debugstrings,whichareneverinaproductdeliverable).Thismeansthatisfinetowrite

s.Format(_T("%d-%s"),code,text);

inyourprogram;thatliteralstringisnotlanguage-sensitive.However,youmustbeverycarefultonotusestringslike

//fmtis"Errorin%sfile%s"
//readorwriteis"reading"or"writing"
s.Format(fmt,readorwrite,filename);

Ispeakofthisfromexperience.InmyfirstinternationalizedapplicationImadethiserror,andinspiteofthefactthatIknowGerman,andthatGermanwordorderplacestheverbattheendofasentence,Ihaddonethis.OurGermandistributorcomplainedbitterlythathehadtocomeupwithtrulyweirderrormessagesinGermantogettheformatcodestodotherightthing.Itismuchbetter(andwhatIdonow)tohavetwostrings,oneforreadingandoneforwriting,andloadtheappropriateone,makingthemstringparameter-insensitive,thatis,insteadofloadingthestrings"reading"or"writing",loadthewholeformat:

//fmtis"Errorinreadingfile%s"
//"Errorinwritingfile%s"
s.Format(fmt,filename);

Notethatifyouhavemorethanonesubstitution,youshouldmakesurethatifthewordorderofthesubstitutionsdoesnotmatter,forexample,subject-object,subject-verb,orverb-object,inEnglish.

Fornow,Iwon'ttalkabout
FormatMessage
,whichactuallyisbetterthan
sprintf
/
Format
,butispoorlyintegratedintothe
CString
class.Itsolvesthisbynamingtheparametersbytheirpositionintheparameterlistandallowsyoutorearrangethemintheoutputstring.

Sohowdoweaccomplishallthis?Bystoringthestringvaluesintheresourceknownasthe
STRINGTABLE
intheresourcesegment.Todothis,youmustfirstcreatethestring,usingtheVisualStudioresourceeditor.AstringisgivenastringID,typicallystarting
IDS_
.Soyouhaveamessage,youcreatethestringandcallit
IDS_READING_FILE
andanothercalled
IDS_WRITING_FILE
.Theyappearinyour.rcfileas

STRINGTABLE
IDS_READING_FILE"Readingfile%s"
IDS_WRITING_FILE"Writingfile%s"
END

Note:theseresourcesarealwaysstoredasUnicodestrings,nomatterwhatyourprogramiscompiledas.TheyareevenUnicodestringsonWin9xplatforms,whichotherwisehavenorealgraspofUnicode(buttheydoforresources!).Thenyougotowhereyouhadstoredthestrings

//previouscode
CStringfmt;
if(...)
fmt="Readingfile%s";
else
fmt="Writingfile%s";
...
//muchlater
CStrings;
s.Format(fmt,filename);

andinsteaddo

//revisedcode
CStringfmt;
if(...)
fmt.LoadString(IDS_READING_FILE);
else
fmt.LoadString(DS_WRITING_FILE);
...
//muchlater
CStrings;
s.Format(fmt,filename);

Nowyourcodecanbemovedtoanylanguage.The
LoadString
methodtakesastringIDandretrievesthe
STRINGTABLE
valueitrepresents,andassignsthatvaluetothe
CString
.

Thereisacleverfeatureofthe
CString
constructorthatsimplifiestheuseof
STRINGTABLE
entries.Itisnotexplicitlydocumentedinthe
CString::CString
specification,butisobscurelyshownintheexampleusageoftheconstructor!(Whythiscouldn'tbepartoftheformaldocumentationandhastobeshowninanexampleescapesme!).Thefeatureisthatifyoucasta
STRINGTABLE
IDtoan
LPCTSTR
itwillimplicitlydoa
LoadString
.Thusthefollowingtwoexamplesofcreatingastringvalueproducethesameeffect,andthe
ASSERT
willnottriggerindebugmodecompilations:

CStrings;
s.LoadString(IDS_WHATEVER);
CStringt((LPCTSTR)IDS_WHATEVER);
ASSERT(s==t);

Now,youmaysay,howcanthispossiblywork?Howcanittellavalidpointerfroma
STRINGTABLE
ID?Simple:allstringIDsareintherange1..65535.Thismeansthatthehigh-orderbitsofthepointerwillbe0.Soundsgood,butwhatifIhavevaliddatainalowaddress?Well,theansweris,youcan't.Thelower64Kofyouraddressspacewillnever,ever,exist.Anyattempttoaccessavalueintheaddressrange
0x00000000
through
0x0000FFFF
(0..65535)willalwaysandforevergiveanaccessfault.Theseaddressesarenever,evervalidaddresses.Thusavalueinthatrange(otherthan0)mustnecessarilyrepresenta
STRINGTABLE
ID.

Itendtousethe
MAKEINTRESOURCE
macrotodothecasting.Ithinkitmakesthecodeclearerregardingwhatisgoingon.Itisastandardmacrowhichdoesn'thavemuchapplicabilityotherwiseinMFC.Youmayhavenotedthatmanymethodstakeeithera
UINT
oran
LPCTSTR
asparameters,usingC++overloading.ThisgetsusaroundtheuglinessofpureCwherethe"overloaded"methods(whicharen'treallyoverloadedinC)requiredexplicitcasts.Thisisalsousefulinassigningresourcenamestovariousotherstructures.

CStrings;
s.LoadString(IDS_WHATEVER);
CStringt(MAKEINTRESOURCE(IDS_WHATEVER));
ASSERT(s==t);

Justtogiveyouanidea:IpracticewhatIpreachhere.Youwillrarelyifeverfindaliteralstringinmyprogram,otherthantheoccasionaldebugoutputmessages,and,ofcourse,anylanguage-independentstring.

CStringsandtemporaryobjects

Here'salittleproblemthatcameuponthe
microsoft.public.vc.mfc
newsgroupawhileago.I'llsimplifyitabit.ThebasicproblemwastheprogrammerwantedtowriteastringtotheRegistry.Sohewrote:

Iamtryingtosetaregistryvalueusing
RegSetValueEx()
anditisthevaluethatIamhavingtroublewith.IfIdeclareavariableof
char[]
itworksfine.However,Iamtryingtoconvertfroma
CString
andIgetgarbage."ÝÝÝÝ...ÝÝÝÝÝÝ"tobeexact.Ihavetried
GetBuffer
,typecastingto
char*
,
LPCSTR
.Thereturnof
GetBuffer
(fromdebug)isthecorrectstringbutwhenIassignittoa
char*
(or
LPCSTR
)itisgarbage.Followingisapieceofmycode:

char*szName=GetName().GetBuffer(20);
RegSetValueEx(hKey,"Name",0,REG_SZ,
(CONSTBYTE*)szName,
strlen(szName+1));

The
Name
stringislessthen20charslong,soIdon'tthinkthe
GetBuffer
parameteristoblame.Itisveryfrustratingandanyhelpisappreciated.



DearFrustrated,

Youhavebeendoneinbyafairlysubtleerror,causedbytryingtobeabittooclever.Whathappenedwasthatyoufellvictimtoknowingtoomuch.Thecorrectcodeisshownbelow:

CStringName=GetName();
RegSetValueEx(hKey,_T("Name"),0,REG_SZ,
(CONSTBYTE*)(LPCTSTR)Name,
(Name.GetLength()+1)*sizeof(TCHAR));

Here'swhymycodeworksandyoursdidn't.Whenyourfunction
GetName
returneda
CString
,itreturneda"temporaryobject".SeetheC++Referencemanual§12.2.

Insomecircumstancesitmaybenecessaryorconvenientforthecompilertogenerateatemporaryobject.Suchintroductionoftemporariesisimplementationdependent.Whenacompilerintroducesatemporaryobjectofaclassthathasaconstructoritmustensurethataconstructiscalledforthetemporaryobject.Similarly,thedestructormustbecalledforatemporaryobjectofaclasswhereadestructorisdeclared.

Thecompilermustensurethatatemporaryobjectisdestroyed.Theexactpointofdestructionisimplementationdependent....Thisdestructionmusttakeplacebeforeexitfromthescopeinwhichthetemporaryiscreated.

Mostcompilersimplementtheimplicitdestructorforatemporaryatthenextprogramsequencingpointfollowingitscreation,thatis,forallpracticalpurposes,thenextsemicolon.Hencethe
CString
existedwhenthe
GetBuffer
callwasmade,butwasdestroyedfollowingthesemicolon.(Asanaside,therewasnoreasontoprovideanargumentto
GetBuffer
,andthecodeaswrittenisincorrectsincethereisno
ReleaseBuffer
performed).Sowhat
GetBuffer
returnedwasapointertostorageforthetextofthe
CString
.Whenthedestructorwascalledatthesemicolon,thebasic
CString
objectwasfreed,alongwiththestoragethathadbeenallocatedtoit.TheMFCdebugstorageallocatorthenrewritesthisfreedstoragewith0xDD,whichisthesymbol"Ý".BythetimeyoudothewritetotheRegistry,thestringcontentshavebeendestroyed.

Thereisnoparticularreasontoneedtocasttheresulttoa
char*
immediately.Storingitasa
CString
meansthatacopyoftheresultismade,soafterthetemporary
CString
isdestroyed,thestringstillexistsinthevariable's
CString
.ThecastingatthetimeoftheRegistrycallissufficienttogetthevalueofastringwhichalreadyexists.

Inaddition,mycodeisUnicode-ready.TheRegistrycallwantsabytecount.Notealsothatthecall
lstrlen(Name+1)
returnsavaluethatistoosmallby2foranANSIstring,sinceitdoesn'tstartuntilthesecondcharacterofthestring.Whatyoumeanttowritewas
lstrlen(Name)+1
(OK,Iadmitit,I'vemadethesameerror!).However,inUnicode,whereallcharactersaretwobyteslong,weneedtocopewiththis.TheMicrosoftdocumentationissurprisinglysilentonthispoint:isthevaluegivenfor
REG_SZ
valuesabytecountoracharactercount?I'massumingthattheirspecificationof"bytecount"meansexactlythat,andyouhavetocompensate.

CStringEfficiency

Oneproblemof
CString
isthatithidescertaininefficienciesfromyou.Ontheotherhand,italsomeansthatitcanimplementcertainefficiencies.Youmaybetemptedtosayofthefollowingcode

CStrings=SomeCString1;
s+=SomeCString2;
s+=SomeCString3;
s+=",";
s+=SomeCString4;

thatitishorriblyinefficientcomparedto,say

chars[1024];
lstrcpy(s,SomeString1);
lstrcat(s,SomeString2);
lstrcat(s,SomeString3);
lstrcat(s,",");
lstrcat(s,SomeString4);

Afterall,youmightthink,firstitallocatesabuffertohold
SomeCString1
,thencopies
SomeCString1
toit,thendetectsitisdoingaconcatenate,allocatesanewbufferlargeenoughtoholdthecurrentstringplus
SomeCString2
,copiesthecontentstothebufferandconcatenatesthe
SomeCString2
toit,thendiscardsthefirstbufferandreplacesthepointerwithapointertothenewbuffer,thenrepeatsthisforeachofthestrings,beinghorriblyinefficientwithallthosecopies.

Thetruthis,itprobablynevercopiesthesourcestrings(theleftsideofthe+=)formostcases.

InVC++6.0,inReleasemode,all
CString
buffersareallocatedinpredefinedquanta.Thesearedefinedas64,128,256,and512bytes.Thismeansthatunlessthestringsareverylong,thecreationoftheconcatenatedstringisanoptimizedversionofa
strcat
operation(sinceitknowsthelocationoftheendofthestringitdoesn'thavetosearchforit,as
strcat
would;itjustdoesa
memcpy
tothecorrectplace)plusarecomputationofthelengthofthestring.Soitisaboutasefficientastheclumsierpure-Ccode,andonewholeloteasiertowrite.Andmaintain.Andunderstand.

Thoseofyouwhoaren'tsurethisiswhatisreallyhappening,lookinthesourcecodefor
CString
,strcore.cpp,inthemfc/srcsubdirectoryofyourvc98installation.Lookforthemethod
ConcatInPlace
whichiscalledfromallthe+=operators.

Aha!So
CString
isn'treally"efficient!"Forexample,ifIcreate

CStringcat("Mew!");

thenIdon'tgetanice,tidylittlebuffer5byteslong(4databytesplustheterminal
NUL
).Insteadthesystemwastesallthatspacebygivingme64bytesandwasting59ofthem.

Ifthisishowyouthink,bepreparedtoreeducateyourself.Somewhereinyourcareersomebodytaughtyouthatyoualwayshadtouseaslittlespaceaspossible,andthiswasaGoodThing.

Thisisincorrect.Itignoressomeseriouslyimportantaspectsofreality.

Ifyouareusedtoprogrammingembeddedapplicationswith16KEPROMs,youhaveaparticularmindsetfordoingsuchallocation.Forthatapplicationdomain,thisishealthy.ButforwritingWindowsapplicationson500MHz,256MBmachines,itactuallyworksagainstyou,andcreatesprogramsthatperformfarworsethanwhatyouwouldthinkofas"lessefficient"code.

Forexample,sizeofstringsisthoughttobeafirst-ordereffect.ItisGoodtomakethissmall,andBadtomakeitlarge.Nonsense.Theeffectofpreciseallocationisthatafterafewhoursoftheprogramrunning,theheapisclutteredupwithlittletinypiecesofstoragewhichareuselessforanything,buttheyincreasethestoragefootprintofyourapplication,increasepagingtraffic,canactuallyslowdownthestorageallocatortounacceptableperformancelevels,andeventuallyallowyourapplicationtogrowtoconsumeallofavailablememory.Storagefragmentation,asecond-orderorthird-ordereffect,actuallydominatessystemperformance.Eventually,itcompromisesreliability,whichiscompletelyunacceptable.

NotethatinDebugmodecompilations,theallocationisalwaysexact.Thishelpsshakeoutbugs.

Assumeyourapplicationisgoingtorunformonthsatatime.Forexample,IbringupVC++,Word,PowerPoint,FrontPage,OutlookExpress,FortéAgent,InternetExplorer,andafewotherapplications,andessentiallyneverclosethem.I'veeditedusingPowerPointfordaysonend(ontheotherhand,ifyou'vehadthemisfortunetohavetousesomethinglikeAdobeFrameMaker,youbegintoappreciatereliability;I'verarelybeenabletousethisapplicationwithoutitcrashingfourtosixtimesaday!Andalwaysbecauseithasrunoutofspace,usuallybyfillingupmyentiremassiveswapspace!)Preciseallocationisoneofthemisfeaturesthatwillcompromisereliabilityandleadtoapplicationcrashes.

Bymaking
CString
sbemultiplesofsomequantum,thememoryallocatorwillendupclutteredwithchunksofmemorywhicharealmostalwaysimmediatelyreusableforanother
CString
,sothefragmentationisminimized,allocatorperformanceisenhanced,applicationfootprintremainsalmostassmallaspossible,andyoucanrunforweeksormonthswithoutproblem.

Aside:Manyyearsago,atCMU,wewerewritinganinteractivesystem.Somestudiesofthestorageallocatorshowedthatithadatendencytofragmentmemorybadly.JimMitchell,nowatSunMicrosystems,createdastorageallocatorthatmaintainedrunningstatisticsaboutallocationsize,suchasthemeanandstandarddeviationofallallocations.Ifachunkofstoragewouldbesplitintoasizethatwassmallerthanthemeanminusone
s
thantheprevailingallocation,hedidn'tsplititatall,thusavoidingclutteringuptheallocatorwithpiecestoosmalltobeusable.Heactuallyusedfloatingpointinsideanallocator!Hisobservationwasthatthelong-termsavingininstructionsbynothavingtoignoreunusablesmallstoragechunksfarandawayexceededtheadditionalcostofdoingafewfloatingpointoperationsonanallocationoperation.Hewasright.

Never,everthinkabout"optimization"intermsofsmall-and-fastanalyzedonaper-line-of-codebasis.Optimizationshouldmeansmall-and-fastanalyzedatthecompleteapplicationlevel(ifyoulikeNewAgebuzzwords,thinkofthisastheholisticapproachtoprogramoptimization,awholelotbetterthantheper-linebasisweteachnewprogrammers).Atthecompleteapplicationlevel,minimum-chunkstringallocationisabouttheworstmethodyoucouldpossiblyuse.

Ifyouthinkoptimizationissomethingyoudoatthecode-linelevel,thinkagain.Optimizationatthislevelrarelymatters.ReadmyessayonOptimization:YourWorstEnemyforsomethought-provokingideasonthistopic.

Notethatthe+=operatorisspecial-cased;ifyouweretowrite:

CStrings=SomeCString1+SomeCString2+SomeCString3+","+SomeCString4;

theneachapplicationofthe+operatorcausesanewstringtobecreatedandacopytobedone(althoughitisanoptimizedversion,sincethelengthofthestringisknownandtheinefficienciesof
strcat
donotcomeintoplay).

Summary

Thesearejustsomeofthetechniquesforusing
CString
.Iusetheseeverydayinmyprogramming.
CString
isnotaterriblydifficultclasstodealwith,butgenerallytheMFCmaterialsdonotmakeallofthisapparent,leavingyoutofigureitoutonyourown.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: