Upload
vannhan
View
219
Download
0
Embed Size (px)
Citation preview
ABERRYPUBLISHINGHOUSEBOOK
FirstpublishedintheUK
byBerryPublishingHouse
7KewLodge
227KewRoad
Richmond
SurreyTW93LQ
Copyright©AndrewWebb2015-CoverGettyImages(RightsPurchased)
Allrightsreserved.Nopartofthispublicationmaybereproducedinanymaterialform(includingprintingorstoringinanymediumbyelectronicmeansandwhetherornottransientlyorincidentallytosomeotheruseofthispublication)withoutthewrittenpermissionofthecopyrightowner,exceptinaccordancewiththeprovisionsoftheCopyright,DesignsandPatentsAct1988orunderthetermsofalicenceissuedbytheCopyrightLicencingAgency,90TottenhamCourtRoad,LondonW1P9HE.Applicationsforthecopyrightowner’swrittenpermissiontoreproduceanypartofthispublicationshouldbeaddressedtothepublisher.
YourFreeGift
Asawayofsayingthankyouforyourpurchase,I’mofferinganexclusivefreecopyofallthecodingscriptsinfullcolourandindentedthatareincludedwithinthisbook.Thisenablesreaderstocopy,pasteandalterthecodingtosuittheirownneedsandalsomaketheexactapplicationscontainedinthebookfullyfunctionalwithoutspendinghoursdebuggingthesyntax,asIknowhowannoyingandtimeconsumingthisis.
Clickheretorequestyourfreedownloadablecopyofallthecodingscriptscontainedinthebook.
Chapter1:WindowsPrograming
1.1GettingStarted:Asimplewindowwitheventhandling
TheproblemwithteachingWindowsprogramming—especiallytothosewishingtobecomemasterprogrammers—isthatitisavastsubject,onethatdeservesabookorseveralbooksinitsownright.InordertogiveanintroductionIhavebeenveryselective,focusingonafewimportantbasicswiththeintentionthatthereadercanusethesetobuildhisorherownunderstanding.
ThegistofWindowsprogrammingisthis:theuserinitiatesaneventsuchaspressingakeyorclickingamouse,theWindowsoperatingsystemrecognizesandinterceptsthisevent,preprocessesit,anddispatchesittotheappropriateuserprogram.Whentheusereventisreceivedbytheprogram,itcandecideonhowitwillactuponit,orotherwiseleaveitaloneandletWindowshandleitviaitsdefaultprocessing.
ThereareafewthingsneededtogeteventhesimplestofWindowsapplicationsupandrunning.Programmerswhowishtocreateevenaminimalistapplicationwillneedtousethedatatypes,classes,constants,functionsandstructurescontainedintheMicrosoftWin32baselibrary.Ihavealwayspreferredtotrythingsoutandlearnabouttheminmoredepthlater,soIwilldothesamehere.Let’sstartwithabarebonesWindowsapplicationwithexplanationstofollow.
EveryWindowsprogramhasanentry-pointfunctionthatisnamedeitherWinMainorwWinMain:
intWINAPIwWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
PWSTRpCmdLine,
intnCmdShow);
1. hInstance-ahandletoamoduletheWindowsoperatingsystemusestoidentifytheexecutable(.exe)whenloadedintomemory.ThishandleinstanceisrequiredforcertainWindowsoperationssuchasloadingiconsandbitmaps.
2. hPrevInstance-isrelevantto16-bitWindowsapplicationsbutisnolongerused.
Nowitisalwayssettozero.
3. pCmdLine-containsthecommandlineargumentsasaUnicodestring.
4. nCmdShow-aflagindicatingwhetherthemainapplicationwindowistobeminimized,maximizedordisplayednormally.
Whencreatingabasicwindowprogrammaticallythefourminimumstepsnecessaryareasfollows:
1.CreatethewindowclassusedtostoreinformationaboutthingssuchasthetypeofWindowbeingused,theWindowsProcedurefunctionusedtocontrolthewindow,icons,coloursandotherparameters.ThisisheldintheWNDCLASSEXstructure.
WNDCLASSEXwindowClass;
windowClass.cbSize=sizeof(WNDCLASSEX);
windowClass.style=CS_HREDRAW|CS_VREDRAW;
windowClass.lpfnWndProc=WndProc;
windowClass.cbClsExtra=0;
windowClass.cbWndExtra=0;
windowClass.hInstance=hInstance;
windowClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
windowClass.hCursor=LoadCursor(NULL,IDC_ARROW);
windowClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.lpszMenuName=NULL;
windowClass.lpszClassName=“MyClass”;
windowClass.hIconSm=LoadIcon(NULL,IDI_WINLOGO);
2.RegistertheWindowsclass.WheneveraWindowsprogramstarts,itregistersthewindowsclasswiththesystem,andgivesthesystemapointertoacallbackfunctioncalledthewindowsprocedure.ThiswindowsprocedureiscalledeachtimeWindowswantstopassaneventmessagebacktotheprogram.Youneeddothisonlyoncebeforeusingitasmanytimesasnecessary.Thewindowattributesdefinedinstep1canbechangedatanytimeifdesired.
UseRegisterClassExtodotheregistering.
RegisterClassEx(&windowClass);
3.CreatethewindowobjectusingtheCreateWindowExfunction(SeeMSDNdocumentationforanexplanationofCreateWindowExanditsparameters:https://msdn.microsoft.com/en-us/library/ms908193.aspx)
CreateWindowEx(
WS_EX_CLIENTEDGE,
g_szClassName,
“Thetitleofmywindow”,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
240,
120,
NULL,
NULL,
hInstance,
NULL);
SeeMSDNdocumentationforanexplanationofCreateWindowExanditsparameters:
https://msdn.microsoft.com/en-us/library/ms908193.aspx
4.Enterthemainmessageloop,whichiswherealleventmessageswillgetsentforprocessing.Abareminimumexampleisshownbelow:
LRESULTCALLBACKWndProc(HWNDhwnd,UINTmsg,WPARAMwParam,LPARAMlParam)
{
switch(msg)
{
caseWM_CLOSE:
DestroyWindow(hwnd);
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hwnd,msg,wParam,lParam);
}
return0;
}
Amouseclick,forexample,getsdispatchedtotheprogramthatcreatedthewindowoverwhichthemousewaspositionedandclicked.ThereareliterallydozensofpossiblemessagesthatwillgethandledbyWindows–everytimeyouresize,minimize,maximize,pressakey,andsoforth.
Thentheprogramresponsibleforthatwindowwillgetachancetointerceptandprocessthemessageforthateventinwhateverwaytheprogrammerchooses.Thisexampleshowsyounotonlyhowtocreateabasicwindowprogrammatically,butalsoaddthecapabilitytorespondtomouseeventnotifications.Hardlyearth-shattering,butitdoeshelpyoutogetthehangofhandlingmessages.Whentheprogramisfirstrunwearepresentedwiththesimplewindow:
Thecurrentmousecursorpositionistrackedeverytimeitmoves,sothatwhentheleftmousebuttonhasbeenclicked,ayellowcircleispaintedatthelocationofthemouseclick:
Thefullcodelistingisasfollows:
#include<windows.h>
#include<set>
#defineWIN32_LEAN_AND_MEAN
constunsignedradius=3;
intxPos=0;
intyPos=0;
typedefstd::pair<int,int>coordinate;
typedefstd::set<coordinate>::const_iteratorcoord_iter;
//Step4:ProcessanymessagesenttotheWindow
LRESULTCALLBACKWndProc(HWNDhwnd,
UINTmessage,
WPARAMwParam,
LPARAMlParam)
{
PAINTSTRUCTpaintStruct;
HDChDC;
HBRUSHhOldBrush,hNewBrush;
staticstd::set<coordinate>coords;
switch(message)
{
caseWM_CREATE:
return0;
break;
caseWM_CLOSE:
PostQuitMessage(0);
return0;
break;
caseWM_PAINT:
//Paintthesetofcircles
hDC=BeginPaint(hwnd,&paintStruct);
hNewBrush=CreateSolidBrush(RGB(255,255,0));
hOldBrush=(HBRUSH)SelectObject(hDC,hNewBrush);
for(coord_iterit=coords.begin();
it!=coords.end();
++it)
{
constintx=(*it).first;
constinty=(*it).second;
Ellipse(hDC,x-radius,y+radius,x+radius,y-radius);
}
SelectObject(hDC,hOldBrush);
DeleteObject(hNewBrush);
EndPaint(hwnd,&paintStruct);
return0;
break;
caseWM_MOUSEMOVE:
//Storethemousecursorpositionateverymovement
xPos=LOWORD(lParam);
yPos=HIWORD(lParam);
break;
caseWM_LBUTTONDOWN:
//Storeuniquesetofmousecoordinatesandredraw
coords.insert(std::make_pair(xPos,yPos));
InvalidateRect(hwnd,NULL,TRUE);
break;
default:
break;
}
returnDefWindowProc(hwnd,message,wParam,lParam);
}
intAPIENTRYWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPSTRlpCmdLine,
intnCmdShow)
{
WNDCLASSEXwindowClass;//windowclass
HWNDhwnd;//windowhandle
MSGmsg;//message
//Step1:CreatetheWindowsclass,exitifunsuccessful
windowClass.cbSize=sizeof(WNDCLASSEX);
windowClass.style=CS_HREDRAW|CS_VREDRAW;
windowClass.lpfnWndProc=WndProc;
windowClass.cbClsExtra=0;
windowClass.cbWndExtra=0;
windowClass.hInstance=hInstance;
windowClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
windowClass.hCursor=LoadCursor(NULL,IDC_ARROW);
windowClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
windowClass.lpszMenuName=NULL;
windowClass.lpszClassName=“MyClass”;
windowClass.hIconSm=LoadIcon(NULL,IDI_WINLOGO);
//Step2:Registerthewindow,exitifunsuccessful
if(!RegisterClassEx(&windowClass))return0;
//Step3:Createthewindow,exitifunsuccessful
if(!CreateWindowEx(
NULL,//extendedstyle
“MyClass”,//classname
“MyFirstWindow”,//appname
WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_SYSMENU,//windowstyle
50,//xcoordinate
50,//ycoordinate
250,//width
250,//height
NULL,//handletoparent
NULL,//handletomenu
hInstance,//applicationinstance
NULL))return0;//noextraparameter’s
//Step4:Enterthemainmessageloopandeventhandling
while(GetMessage(&msg,NULL,0,0)>0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}
1.2Creatingdialogsprogrammatically
DialogboxesareWindowsapplicationsthatcandisplaymessagesandcontrolswhileallowinguserinteractionviaeventssuchasmouseclicksandkeypresses.Dialogboxescanbecreatedasapplicationsintheirownright,ortheycanbeusedinadditiontoanexistingapplication.Adialogboxiscreatedfromaresourcefile,whichisrecognizedashavingthe.rcextension.Thisfilecontainsinformationaboutthedialogboxandspecifiesparameterssuchasthedialogboxsizeandlocation,itstextualcaption,andwhetheritcontainscontrolslikebuttons,editboxes,andsoforth.
TherearetwomainapproachestodefiningMicrosoftFoundationClass(MFC)dialogboxesforUserInterface-basedapplications.EitherusetheResourceEditor(atooltodefinethedialogappearanceandgeneratethecodeassociatedwiththedialog)orcreate(andhencelatermodify)dialogboxesatruntimebyusingusingtheWin32APIdirectly.
TheVisualStudioResourceEditorcanbeusedtopositiondialogcomponentsinconjunctionwiththeClassWizardtogeneratethecorrespondingC++controlcodeinseparate.cppand.hfiles.TheResourceEditorisveryconvenientinthatalldialogboxlayoutinformationisstoredintheresource(.rc)file,andallcorrespondingresourceidentifiersarestoredintheresource.hfile.Toprogrammaticallycreateadialogbox,usetheDialogBoxfunction.Itsusageis:
INT_PTRDialogBox(HINSTANCEhInstance,
LPCTSTRlpTemplate,
HWNDhWndParent,
DLGPROClpDialogFunc);
ThefirstargumenthInstanceisthehandletotheapplicationusingthedialogbox.ThelpTemplateargumentspecifiesthedialogboxtemplate.ThehWndParentargumentisahandletotheparentwindowthatownsthedialogbox.ThelpDialogFuncargumentisapointertothedialogboxprocedure,implyingtheneedtodefineacallbackprocedurewhoseusageis:
INT_PTRCALLBACKDialogProc(HWNDhwndDlg,
UINTuMsg,
WPARAMwParam,
LPARAMlParam);
TodothisinVisualStudiofirstcreateanewWin32application.Specifythedesiredlocationofyourprojectandentertheprojectname,sayWin32Dialog,andthenclickOK:
IntheApplicationWizardthatappearsmakesureyoucreateitasanEmptyProjectbeforeclickingonFinish:
Nowyouneedtoactuallycreateyourdialog.Right-clicktheprojectfolderthatVisualStudiohascreatedandselectAdd>Resource:SelectDialogandthenselectNew:
Thiswillgeneratetheresourceheaderandrcfileusedtodefinethedialog.IntheResourceEditor,right-clickonthenewlycreateddialogboxandselectProperties.ThenameoftheIDdefaultstoIDD_DIALOG1.Changethisnameifyouwish.
ThencreatethemainsourcefilebyselectingAdd>NewItem.IntheNameeditboxtypethenameofthesourcefile,suchasmain.cpp,andpressAdd:
Inthemain.cppfile,insertthefollowingcode:
#include<windows.h>
#include“resource.h”
HWNDhWnd;
LRESULTCALLBACKDlgProc(HWNDhWnd,
UINTMsg,
WPARAMwParam,
LPARAMlParam);
INTWINAPIWinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPSTRlpCmdLine,
intnCmdShow)
{
DialogBox(hInstance,
MAKEINTRESOURCE(IDD_DIALOG1),
hWnd,
reinterpret_cast<DLGPROC>(DlgProc));
returnFALSE;
}
LRESULTCALLBACKDlgProc(HWNDhWndDlg,
UINTMsg,
WPARAMwParam,
LPARAMlParam)
{
switch(Msg)
{
caseWM_INITDIALOG:
returnTRUE;
caseWM_COMMAND:
switch(wParam)
{
caseIDOK:
EndDialog(hWndDlg,0);
returnTRUE;
1.3CreatingDialogApplicationsUsingtheMFCApplicationWizard
AlternativelyyoucancreateaDialogapplicationusingtheMFCApplicationWizardinjustafewsteps.FromtheFilemenuselectNew>Project,andintheInstalledTemplatessectionoftheNewProjectdialogselecttheMFC>MFCApplicationoption.Givetheprojectanameandselectthelocationoftheprojectfolder,thenclickOK:
IntheMFCApplicationWizardthatappearschoosetheDialogoptionfortheApplicationtypeandclickNext:
SelecttheparticularMainFramestylesyoupreferandclickNext:
FinallyforourminimalistapplicationwewilluncheckanyremainingoptionsbeforeclickingonFinish:
TheApplicationWizardwillautomaticallygeneratethenecessaryresourcefilesandtheapplicationwhoseuserinterfaceisadialogbox,thesameasintheprevioussection:
1.4AddingControlstoDialogBoxes:ListControl
Toaddcontrolstoyourdialog,selectthecontrolyouwantfromtheToolboxWindowanddragittothelocationonyourdialogbox.Asanexample,intheDialogApplicationyoucreatedusingtheMFCApplicationWizard,youwanttoselecttheListControlanddragthisontoyourDialog:
RightclickonyourListControlandselectProperties.IntheAppearancesectionsettheViewpropertytoList.YoucankeepitsIDpropertyasIDC_LIST1:
Right-clickthelistcontrolandselectAddVariable.SettheVariableNametom_Items.ClickFinish.
NowpopulatethelistcontrolwithitemsusingtheCListCtrl::InsertItemmethod.Useitasfollows:
intInsertItem(constLVITEM*pItem);
ThismethodrequiresanLVITEMpointerasanargument.TheLVITEMstructureisdefinedasfollows:
typedefstruct_LVITEM
{
UINTmask;
intiItem;
intiSubItem;
UINTstate;
UINTstateMask;
LPTSTRpszText;
intcchTextMax;
intiImage;
LPARAMlParam;
#if(_WIN32_IE>=0x0300)
intiIndent;
#endif
}LVITEM,FAR*LPLVITEM;
Themaskparameterspecifiesthetypesofvaluesyouwanttosetforthecurrentitem.iItemspecifiesthezero-basedindexoftheitembeingchanged.iSubItemistheindexofthesubitemforthecurrentvalue.Ifthecurrentitemwillbetheleader,theiSubItemisstoredina0-basedarray.Ifitisasubitem,thenitisstoredina1-basedarray.ThepszTextvariableistheitemstringdisplayed,thelengthofwhichcanbespecifiedbyassigningavaluetocchTextMax.
AfterinitializingtheLVITEM,itispassedtotheInsertItemmethodtoadditasanewlistitem.HereisanexamplethatcreatesitemsanddisplaysasaListview:
CreatingCustomMFCControls–anEditableListControl
SometimesyoumayneedmorethanthanwhattheMicrosoftFoundationClassesprovide.TheListControl,forexample,isgreatfordisplayinglistsofrecordsinvariousmodes.Butwhatifyouwishtomakeindividualgriditemsinthelistcontroleditable,forexample?TheansweristooverridetheMFCclassandprovidecustommethodstoimplementthebehaviouryoudesire.Thisexampledescribesthestepsnecessarytodojustthat.
Step1:CreatetheMFCProject
Startbycreatingadialog-basedapplicationinVisualStudio.SelectFile>New>Project>MFCApplication.WhentheApplicationWizardislaunched,selectDialogbasedastheApplicationType:
AndthenselectFinish.IntheResourcesView,noticethatanewdialogiscreated:
Togetstarted,firstdeletethestaticcontrol“TODO:Placedialogcontrolshere”thatgetscreatedautomatically.ThenintheToolbox,selectthe“ListControl”andintheResourceViewplacethiswithinyourdialogarea:
Right-clicktheListControlyoujustaddedandselectProperties.InthePropertieswindow,makesuretheViewsectionofAppearanceissetto‘Report’style:
Step2:DeriveaclassfromCListCtrl
CreateanewListControlclassCEditableListCtrl,thatpubliclyinheritsfromthestandardCListCtrl.AnefficientwaytodothisistousetheClassWizard.RightclickyourprojectfolderandselectClassWizard…ThenpresstheAddClassbuttonthatappearsintheMFCClassWizarddialog:
InournewderivedCEditableListCtrlclass,weneedtodefinesixnewmethodsinordertomaketheListControlgriditemseditable:
classCEditableListCtrl:publicCListCtrl
{
public:
intGetRowFromPoint(CPoint&point,int*col)const;
CEdit*EditSubLabel(intnItem,intnCol);
voidOnHScroll(UINTnSBCode,UINTnPos,CScrollBar*pScrollBar);
voidOnVScroll(UINTnSBCode,UINTnPos,CScrollBar*pScrollBar);
voidOnEndLabelEdit(NMHDR*pNMHDR,LRESULT*pResult);
voidOnLButtonDown(UINTnFlags,CPointpoint);
};
GetRowFromPoint–todeterminetherowandcolumnnumberthatthecursorfallson,ifany:
intCEditableListCtrl::GetRowFromPoint(CPoint&point,int*col)const
{
intcolumn=0;
introw=HitTest(point,NULL);
if(col)*col=0;
//MakesurethattheListViewisinLVS_REPORT
if((GetWindowLong(m_hWnd,GWL_STYLE)&LVS_TYPEMASK)!=LVS_REPORT)
{
returnrow;
}
//Getthetopandbottomrowvisible
row=GetTopIndex();
intbottom=row+GetCountPerPage();
if(bottom>GetItemCount())
{
bottom=GetItemCount();
}
//Getthenumberofcolumns
CHeaderCtrl*pHeader=(CHeaderCtrl*)GetDlgItem(0);
intnColumnCount=pHeader->GetItemCount();
//Loopthroughthevisiblerows
for(;row<=bottom;row++)
{
//Getboundingrectangleofitemandcheckwhetherpointfallsinit.
CRectrect;
GetItemRect(row,&rect,LVIR_BOUNDS);
if(rect.PtInRect(point))
{
//Findthecolumn
for(column=0;column<nColumnCount;column++)
{
intcolwidth=GetColumnWidth(column);
if(point.x>=rect.left&&point.x<=(rect.left+colwidth))
{
if(col)*col=column;
returnrow;
}
rect.left+=colwidth;
}
}
}
return-1;
}
EditSubLabel–isamethodtoedittheindividualcellsoftheListControl.Takingtherowandcolumnintegersasarguments,EditSubLabelcreatesandmakesvisibleaneditcontroloftheappropriatesizeandtextjustification.(Thisalsorequiresanothereditcontrolclass,CInPlaceEditwhichwederivedfromthestandardCEditclassandisdescribedinthenextsection.)
CEdit*CEditableListCtrl::EditSubLabel(intnItem,intnCol)
{
//Thereturnedpointershouldnotbesaved,makesureitemvisible
if(!EnsureVisible(nItem,TRUE))returnNULL;
//Makesurethatcolumnnumberisvalid
CHeaderCtrl*pHeader=(CHeaderCtrl*)GetDlgItem(0);
intnColumnCount=pHeader->GetItemCount();
if(nCol>=nColumnCount||GetColumnWidth(nCol)<5)returnNULL;
//Getthecolumnoffset
intoffset=0;
for(inti=0;i<nCol;i++){
offset+=GetColumnWidth(i);
}
CRectrect;
GetItemRect(nItem,&rect,LVIR_BOUNDS);
//Scrollhorizontallyifweneedtoexposethecolumn
CRectrcClient;
GetClientRect(&rcClient);
if(offset+rect.left<0||offset+rect.left>rcClient.right)
{
CSizesize;
size.cx=offset+rect.left;
size.cy=0;
Scroll(size);
rect.left-=size.cx;
}
//GetColumnalignment
LV_COLUMNlvcol;
lvcol.mask=LVCF_FMT;
GetColumn(nCol,&lvcol);
DWORDdwStyle;
if((lvcol.fmt&LVCFMT_JUSTIFYMASK)==LVCFMT_LEFT){
dwStyle=ES_LEFT;
}
elseif((lvcol.fmt&LVCFMT_JUSTIFYMASK)==LVCFMT_RIGHT){
dwStyle=ES_RIGHT;
}
else{
dwStyle=ES_CENTER;
}
rect.left+=offset+4;¬
rect.right=rect.left+GetColumnWidth(nCol)-3;
if(rect.right>rcClient.right){
rect.right=rcClient.right;
}
dwStyle|=WS_BORDER|WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
CEdit*pEdit=newCInPlaceEdit(nItem,nCol,GetItemText(nItem,nCol));
pEdit->Create(dwStyle,rect,this,IDC_LIST1);
returnpEdit;
}
AnotheressentialmodificationistoaddthemeansbywhichtheusercaninitiateaneditoftheselectedListControlcellbymodifyingtheOnLButtonDownmethod:
voidCEditableListCtrl::OnLButtonDown(UINTnFlags,CPointpoint)
{
intindex;
CListCtrl::OnLButtonDown(nFlags,point);
ModifyStyle(0,LVS_EDITLABELS);
intcolnum;
if((index=GetRowFromPoint(point,&colnum))!=-1)
{
UINTflag=LVIS_FOCUSED;
if((GetItemState(index,flag)&flag)==flag)
{
//AddcheckforLVS_EDITLABELS
if(GetWindowLong(m_hWnd,GWL_STYLE)&LVS_EDITLABELS)
{
EditSubLabel(index,colnum);
}
}
else
{
SetItemState(
index,
LVIS_SELECTED|LVIS_FOCUSED,
LVIS_SELECTED|LVIS_FOCUSED);
}
}
}
Step3:DeriveaclassfromCEdit
WecreateaderivedinstanceoftheCEditclasstosatisfyanumberofrequirements:itneedstosendtheLVN_ENDLABELEDITmessageandself-destructuponcompletionofediting;expandalittletoaccommodatethetext;andalsoterminateuponpressingtheEscapeorEnterkeysorwhentheeditcontrollosesfocus.
classCInPlaceEdit:publicCEdit
{
public:
CInPlaceEdit(intiItem,intiSubItem,CStringsInitText);
//ClassWizardgeneratedvirtualfunctionoverrides
//{{AFX_VIRTUAL(CInPlaceEdit)
public:virtualBOOLPreTranslateMessage(MSG*pMsg);
//}}AFX_VIRTUAL
public:virtual~CInPlaceEdit();
//Generatedmessagemapfunctions
protected:
//{{AFX_MSG(CInPlaceEdit)
afx_msgvoidOnKillFocus(CWnd*pNewWnd);
afx_msgvoidOnNcDestroy();
afx_msgvoidOnChar(UINTnChar,UINTnRepCnt,UINTnFlags);
afx_msgintOnCreate(LPCREATESTRUCTlpCreateStruct);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
intm_iItem;
intm_iSubItem;
CStringm_sInitText;
BOOLm_bESC;
};
ThederivedCEditcontrol,CInPlaceEdit,isdescribedasfollows.TheoverriddenPreTranslateMessage()determineswhethercertainkeystrokesmakeittotheeditcontrol.OnKillFocus()sendstheLVN_ENDLABELEDITnotificationanddestroystheeditcontrol.Thenotificationissenttotheparentofthelistviewcontrolandnottothelist
viewcontrolitself.Whensendingthenotification,them_bESCmembervariableisusedtodeterminewhethertosendaNULLstring.OnNcDestroy()istheappropriateplacetodestroytheC++object.TheOnChar()functionterminateseditingiftheEscapeortheEnterkeyispressed.
Otherwiseforanyothercharacter,itletsthebaseclassfunctiontakecareofitbeforeittriestodetermineifthecontrolneedstoberesized.Thefunctiongetstheextentofthenewstringandthencomparesittothedimensionoftheeditcontrol.Ifthestringistoolongtofitwithintheeditcontrol,itresizestheeditcontrolaftercheckingifthereissufficientspaceforittoexpand.
OnCreate()createstheeditcontrol,initialisingitwiththepropervalues.
FullcodelistingoftheCInPlaceEditcontrol:
CInPlaceEdit::CInPlaceEdit(intiItem,intiSubItem,CStringsInitText):m_sInitText(sInitText)
{
m_iItem=iItem;
m_iSubItem=iSubItem;
m_bESC=FALSE;
}
CInPlaceEdit::~CInPlaceEdit(){}
BEGIN_MESSAGE_MAP(CInPlaceEdit,CEdit)
//{{AFX_MSG_MAP(CInPlaceEdit)
ON_WM_KILLFOCUS()
ON_WM_NCDESTROY()
ON_WM_CHAR()
ON_WM_CREATE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//CInPlaceEditmessagehandlers
//TranslatewindowmessagesbeforetheyaredispatchedtotheTranslateMessageandDispatchMessageWindowsfunctions.
BOOLCInPlaceEdit::PreTranslateMessage(MSG*pMsg)
{
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN||
pMsg->wParam==VK_DELETE||
pMsg->wParam==VK_ESCAPE||
GetKeyState(VK_CONTROL))
{
::TranslateMessage(pMsg);
::DispatchMessage(pMsg);
returnTRUE;
//DONOTprocessfurther
}
}
returnCEdit::PreTranslateMessage(pMsg);
}
//Calledimmediatelybeforelosingtheinputfocus
voidCInPlaceEdit::OnKillFocus(CWnd*pNewWnd)
{
CEdit::OnKillFocus(pNewWnd);
CStringstr;
GetWindowText(str);
DestroyWindow();
}
//Calledwhennonclientareaisbeingdestroyed
voidCInPlaceEdit::OnNcDestroy()
{
CEdit::OnNcDestroy();
deletethis;
}
//Calledfornonsystemcharacterkeystrokes
voidCInPlaceEdit::OnChar(UINTnChar,UINTnRepCnt,UINTnFlags)
{
if(nChar==VK_ESCAPE||nChar==VK_RETURN)
{
if(nChar==VK_ESCAPE)
{
m_bESC=TRUE;
}
GetParent()->SetFocus();
return;
}
CEdit::OnChar(nChar,nRepCnt,nFlags);
//Resizeeditcontrolifneeded
CStringstr;
GetWindowText(str);
CWindowDCdc(this);
CFont*pFont=GetParent()->GetFont();
CFont*pFontDC=dc.SelectObject(pFont);
CSizesize=dc.GetTextExtent(str);
dc.SelectObject(pFontDC);
size.cx+=5;
//Gettheclientrectangle
CRectrect,parentrect;
GetClientRect(&rect);
GetParent()->GetClientRect(&parentrect);
//Transformrectangletoparentcoordinates
ClientToScreen(&rect);
GetParent()->ScreenToClient(&rect);
//Checkwhethercontrolneedsresizingandifsufficientspacetogrow
if(size.cx>rect.Width())
{
if(size.cx+rect.left<parentrect.right)
{
rect.right=rect.left+size.cx;
}
else
{
rect.right=parentrect.right;
}
MoveWindow(&rect);
}
//Constructlistcontrolitemdata
LV_DISPINFOdispinfo;
dispinfo.hdr.hwndFrom=GetParent()->m_hWnd;
dispinfo.hdr.idFrom=GetDlgCtrlID();
dispinfo.hdr.code=LVN_ENDLABELEDIT;
dispinfo.item.mask=LVIF_TEXT;
dispinfo.item.iItem=m_iItem;
dispinfo.item.iSubItem=m_iSubItem;
dispinfo.item.pszText=m_bESC?NULL:LPTSTR((LPCTSTR)str);
dispinfo.item.cchTextMax=str.GetLength();
//SendthisNotificationtoparentofListViewctrl
CWnd*pWndViewAttachmentsDlg=GetParent()->GetParent();
if(pWndViewAttachmentsDlg)
{
pWndViewAttachmentsDlg->SendMessage(
WM_NOTIFY_DESCRIPTION_EDITED,
GetParent()->GetDlgCtrlID(),
(LPARAM)&dispinfo);
}
}
//CalledwhenapplicationrequeststheWindowswindowbecreatedbycalling
//Create/CreateExmemberfunction.
intCInPlaceEdit::OnCreate(LPCREATESTRUCTlpCreateStruct)
{
if(CEdit::OnCreate(lpCreateStruct)==-1)
{
return-1;
}
//Settheproperfont
CFont*font=GetParent()->GetFont();
SetFont(font);
SetWindowText(m_sInitText);
SetFocus();
SetSel(0,-1);
return0;
}
Step4:AddtheCEditableListCtrlasacontrolvariable
InthemainCEditableListControlDlgclass,addCEditableListCtrlasacontrolvariable:
CEditableListCtrlm_EditableList;
AndmodifyDoDataExchangeaccordingly:
voidCEditableListControlDlg::DoDataExchange(CDataExchange*pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX,IDC_LIST1,m_EditableList);
}
AndinOnInitDialoglet’saddafewsamplelistcontrolentries:
BOOLCEditableListControlDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
//Add“About…”menuitemtosystemmenu.
//IDM_ABOUTBOXmustbeinthesystemcommandrange.
ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX<0xF000);
CMenu*pSysMenu=GetSystemMenu(FALSE);
if(pSysMenu!=NULL)
{
BOOLbNameValid;
CStringstrAboutMenu;
bNameValid=strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if(!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,IDM_ABOUTBOX,strAboutMenu);
}
}
//Settheiconforthisdialog.Theframeworkdoesthisautomatically
//whentheapplication’smainwindowisnotadialog
SetIcon(m_hIcon,TRUE);//Setbigicon
SetIcon(m_hIcon,FALSE);//Setsmallicon
//TODO:Addextrainitializationhere
CRectrect;
CWnd*pWnd=this->GetDlgItem(IDC_LIST1);
pWnd->GetWindowRect(&rect);
this->ScreenToClient(&rect);//optionalstep-seebelow
m_xoffset=rect.left;
m_yoffset=rect.top;
LVCOLUMNlvColumn;
intnCol;
lvColumn.mask=LVCF_FMT|LVCF_TEXT|LVCF_WIDTH;
lvColumn.fmt=LVCFMT_LEFT;
lvColumn.cx=150;
lvColumn.pszText=“Name”;
nCol=m_EditableList.InsertColumn(0,&lvColumn);
lvColumn.mask=LVCF_FMT|LVCF_TEXT|LVCF_WIDTH;
lvColumn.fmt=LVCFMT_CENTER;
lvColumn.cx=150;
lvColumn.pszText=“Occupation”;
m_EditableList.InsertColumn(1,&lvColumn);
lvColumn.mask=LVCF_FMT|LVCF_TEXT|LVCF_WIDTH;
lvColumn.fmt=LVCFMT_LEFT;
lvColumn.cx=150;
lvColumn.pszText=“Country”;
m_EditableList.InsertColumn(2,&lvColumn);
m_EditableList.SetExtendedStyle(m_EditableList.GetExtendedStyle()|LVS_EX_FULLROWSELECT|LVS_EDITLABELS);
//Insertafewexamplelistitems
intl_iItem=m_EditableList.InsertItem(LVIF_TEXT|LVIF_STATE,0,“Andrew”,0,LVIS_SELECTED,0,0);
m_EditableList.SetItemText(l_iItem,1,“Bricklayer”);
m_EditableList.SetItemText(l_iItem,2,“Australia”);
l_iItem=m_EditableList.InsertItem(LVIF_TEXT|LVIF_STATE,0,“Peter”,0,LVIS_SELECTED,0,0);
m_EditableList.SetItemText(l_iItem,1,“Lecturer”);
m_EditableList.SetItemText(l_iItem,2,“NewZealand”);
l_iItem=m_EditableList.InsertItem(LVIF_TEXT|LVIF_STATE,0,“Richard”,0,LVIS_SELECTED,0,0);
m_EditableList.SetItemText(l_iItem,1,“Dentist”);
m_EditableList.SetItemText(l_iItem,2,“Botswana”);
returnTRUE;//returnTRUEunlessyousetthefocustoacontrol
}
Step5:IntroducenotifiersforhandlingupdatesandWindowsmessages
BeginningwiththeWindowsmessagemap:
BEGIN_MESSAGE_MAP(CEditableListControlDlg,CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_NOTIFY(NM_CLICK,IDC_LIST1,OnNMClickList)
ON_MESSAGE(WM_NOTIFY_DESCRIPTION_EDITED,OnNotifyDescriptionEdited)
END_MESSAGE_MAP()
Forwhentheuserclicksontheeditablelistcontrolinparticular:
voidCEditableListControlDlg::OnNMClickList(NMHDR*pNMHDR,LRESULT*pResult)
{
m_fClickedList=true;
m_EditableList.OnLButtonDown(MK_LBUTTON,InterviewListCursorPosition());
*pResult=0;
}
Alsoaddanotifierforhandlingwhentheeditcontrolhasbeenupdated:
LRESULTCEditableListControlDlg::OnNotifyDescriptionEdited(
WPARAMwParam,
LPARAMlParam)
{
//GetthechangedDescriptionfieldtextviathecallback
LV_DISPINFO*dispinfo=reinterpret_cast<LV_DISPINFO*>(lParam);
//Persisttheselectedattachmentdetailsuponupdatingitstext
m_EditableList.SetItemText(
dispinfo->item.iItem,
dispinfo->item.iSubItem,
dispinfo->item.pszText);
return0;
}
RunthesampleprojecttoseethedefaultListControlentriesdisplayed:
Andthenseehowuponleft-clickingindividualcells,theybecomeeditable,anoutline
ChapterSummary
Tosummarizeinthefirstofthese10masterlessons,wehavelearnedhowto:
CreateourfirstWindowsapplicationwitheventhandling.
CreateadialogapplicationbothprogrammaticallyandbyusingtheMFCApplicationWizard.
Addcontrolstodialogapplications.
CreatecustomizedMFCcontrols,suchastheCListCtrl(ordinarilyread-only),sothatitsindividualgriditemsbecomemodifiable.
Chapter2:FurtherWindowsProgramming
2.1Buildingawebbrowser
CreatingaWebbrowserinVisualC++isremarkablyeasy.That’sbecauseMicrosoftInternetExplorercomeswithVisualC++andbyusingafewsimplesteps,itcanbeusedasaWindowscontrolsimilartoanyother.TheprocessofcreatingasimpleWebbrowsercanbesummarizedas:addaMicrosoftWebBrowsercontroltoyourWindowsprogram,createamembercontrolvariableforit,andusetheNavigatemethodtobrowsetheonlinelocationofyourchoice.ThenaddaneditcontroltospecifytheURLofthewebsiteyouwishtobrowse.
UsetheAppWizardtocreateanewdialog-basedapplicationcalledWebBrowser.
MakesuretheMicrosoftWebBrowserisavailableforuseinyourapplication.FromtheToolboxwindow,right-clickandselectChooseItems…(ForolderversionsofMicrosoftVisualStudioyoumayneedtoaccessthisoptionbyinsteadselectingProject>AddtoProject>ComponentsandControlsandthendouble-clickingtheentrymarkedRegisteredActiveXControls.)WhenthedialogtochooseToolboxitemsappears,selecttheCOMComponentstab,andselecttheMicrosoftWebBrowsercontrol:
ThebrowsercontrolwillthenappearintheToolboxwindow:
InyourResourceView,dragthisnewlyaddedcontrolontoyourdialogwindowwhileresizingitaccordingtoyourpreference.ThenaddaneditcontrolwithwhichtoenteraURLstringvariable,aswellasabuttontoyourdialogwiththecaptionBrowse(youcanmodifytheOKbuttonthatisautomaticallygeneratedbysettingitsproperties).Right-clicktheOKbutton,selectProperties,andrenamethecaptionfromOKtoBrowse.RenameitsIDfromIDOKtoID_BROWSE:
Nowcreatethemembervariablesforeachofthecontrolsthathavebeenadded.CreateamembervariablefortheWebBrowsercontrolbyright-clickingontheWebBrowsercontrolandselectingAddVariable.IntheAddMemberVariableWizardthatappears,giveitanamesuchasm_WebBrowserandclickOK:
Inthesamemanner,createamembervariableforeditcontrolaswell:right-clicktheeditcontrolandselectAddVariable,andgivethiscontrolanamesuchasm_URL:
Nowcreateanevent-handlerfortheBrowsebutton,sothatwhenclickedtheapplicationnavigatestheWebBrowsertothelocationenteredintheeditcontrol.Double-clickontheBrowsebuttonintheResourceViewandthiswillautomaticallygeneratetheevent-handlingmethodforyou.InthismethodwesimplyaddthefewlinesofcodethatwillnavigateourWebbrowsertothedesiredinternetlocation:
voidCWebBrowserDlg::OnBnClickedButton()
{
CStringl_strURL;
m_URL.GetWindowTextA(l_strURL);
m_WebBrowser.Navigate(l_strURL,0,0,0,0);
}
Runtheprogramasshown:enterthedesiredinternetlocationandclicktheBrowsebutton:
2.2DownloadingfilesviaFileTransferProtocol(FTP)
FileTransferProtocol(FTP)isaprotocolfortransferringdatafilesovertheInternet.ThisnextexamplewillnowdemonstratehowtouseFTPtodownloadafilefromanFTPsite.OnmanyFTPsites,filesseemtocomeandgobutthereisonefilethathasremainedforyearsandlookssettostaythatway:thetextfileoftheFTPstandardsitself,rfc959.txt:
ftp://ftp.funet.fi/pub/standards/RFC/rfc959.txt
Wewilldownloadrfc959.txtinourexampleprogram.Thisdialogwilldisplayabuttonnamed“Download”andaread-onlytextboxtodisplaythestatusofthedownload.
UsetheAppWizardtocreateadialog-basedapplicationnamedFTP.Inthisdialogwewillneedjustonebutton(labeledDownload)andthreeeditboxes.Twooftheeditboxeswillbeeditable:onetoenterthelocationoftheFTPsiteandanothertoenterthefilepath.Anotherread-onlyeditboxwillbeaddedtodisplaythestatus/successofthedownload:
Tosettheread-onlystatusofaneditcontrol,right-clickthecontrol,selectProperties,andsettheReadOnlyproperty.ThisshowstheRead-onlypropertyoftheFTPaddresseditcontrol:
Createamembervariableforthefirsteditcontrol.WewillusethistoentertheFTPfilelocation.AndintheResourceView,right-clickthecontrol,selectAddVariableandgiveitanamesuchasm_FileLocation:
Repeatforthenexteditcontrol,theonewewillusetoenterthepathlocation.Nameitm_FilePath:
Forthethirdread-onlyeditcontrol,wewillalsoaddavariable,thistimecallingitm_StatusText:
IntheOnInitDialogmethod,afterthecommenttellingyoutoaddyourowninitializationcode,wewillsetsomedefaultsforthedefaultFTPaddressofthefilewewishtodownload:
CStringl_strFtpLocation=“ftp.funet.fi”;
m_FtpLocation.SetWindowTextA(l_strFtpLocation);
CStringl_strFilePath=“pub/standards/RFC/rfc959.txt”;
m_FilePath.SetWindowTextA(l_strFilePath);
DoubleclicktheDownloadbuttontoautomaticallygeneratetheeventhandlingcodewhenthisbuttonisclicked.OnclickingtheDownloadbutton,thisroutinewillfirstobtaintheFTPlocationandfilepathfromtheeditcontrols.ThenitcreatesanewInternetsessionobjectpointer,andifsuccessfulitcreatesanFTPobjectpointerusingtheFTPlocationpassedtoit,whichinthisexampleis“ftp.funet.fi”.
FinallyitwillattempttodownloadthefilebypassingtheFTPfilepathlocationandwritingthefiletothechosendestination,whichinthisprogramhasbeenhardcodedtoC:\Temp.
voidCFTPDlg::OnBnClickedDownload()
{
CStringstrFtpLocation,strFilePath;
m_FtpLocation.GetWindowTextA(strFtpLocation);
m_FilePath.GetWindowTextA(strFilePath);
intnPosition=strFilePath.ReverseFind(‘/’);
CStringstrFileName=nPosition==-1?
strFilePath:strFilePath.Mid(nPosition+1);
CStringstrFtpFilePath=strFtpLocation+“/”+strFilePath;
CStringstrDestinationPath=“C:\temp”;
CInternetSession*pInternetSession=newCInternetSession();
CFtpConnection*pFTPConnection=NULL;
if(NULL==pInternetSession)
{
MessageBox(NULL,“CouldnotestablishInternetsession”,MB_OK);
}
pFTPConnection=pInternetSession->GetFtpConnection(strFtpLocation);
if(NULL==pFTPConnection)
{
MessageBox(NULL,“CouldnotestablishFTPconnection”,MB_OK);
}
Sleep(5000);
BOOLsuccess=pFTPConnection->GetFile(
strFilePath,
strDestinationPath+“\”+strFileName,
FALSE,
FILE_ATTRIBUTE_NORMAL,
FTP_TRANSFER_TYPE_ASCII,
INTERNET_FLAG_DONT_CACHE);
if(TRUE==success)
{
m_FtpLocation.SetWindowTextA(strFtpFilePath);
CStringstrDownloadMessage=strFileName+
”successfullydownloadedto”+
strDestinationPath;
m_StatusText.SetWindowTextA(strDownloadMessage);
}
else
{
m_StatusText.SetWindowTextA(“Downloadfailed.”);
}
UpdateData(FALSE);
pFTPConnection->Close();
pInternetSession->Close();
}
RunningtheapplicationaccessesthefilefromthespecifiedFTPsiteandwritesittothechosendestination:
2.3PlayingMediaFilesusingDirectShow
DirectShowisaframeworkconsistingofasuiteofAPIsthatcanbeusedbysoftwaredevelopersformediastreamingonWindowsplatforms.UsingDirectShowenablesyoutoplayorcaptureaudioandvideostreamsoreditmedia.Thissectionisahigh-levelintroductiontousingDirectShowthatshowsyouhowtoplayanaudiooravideofile.
Therearesomepre-requisitesforusingDirectShow,however.AllapplicationsusingDirectShowmustincludetheDshow.hheaderfileandlinktothestaticlibraryfilestrmiids.lib.ThesearerequiredforallDirectShowapplications:
#include<dshow.h>
#pragmacomment(lib,“Strmiids.lib”)
GiventhatDirectShowisbasedontheComponentObjectModel(COM),someknowledgeofapplyingCOMclientprogrammingisnecessarytowriteaDirectShowapplication.(ShouldyouatsomepointwishtoextendDirectShowtosupportnewformatsordevelopyourowncustomeffects,youwouldneedtowriteyourowncomponentsandimplementthemasCOMobjects.)
ItisnecessarytoinitializetheCOMlibrarybeforehand,usingCoInitialize()andthencheckingtheresult:
HRESULThr=CoInitialize(NULL);
if(FAILED(hr))
{
std::cout<<“CouldnotinitializeCOMlibrary”<<std::endl;
return;
}
WhenyouarefinishedusingCOMyoumustalsouninitializeitusingCoUninitialize(),meaningthatallDirectShowfunctioncallstakeplacebetweenCOMCoInitialize()andCoUninitialize().
DefinetheDirectShowinterfacesyouwillneedforthisapplicationbybuildingthefilter
graphandthencontrollingmediastreamingandmediaeventhandling:
IGraphBuilder*pGraph=NULL;
IMediaControl*pControl=NULL;
IMediaEvent*pEvent=NULL;
CallCoCreateInstance()tocreatetheFilterGraphManager.Afiltergraphinprocessingmediaisadirectedgraphwhosenodesrepresentdataprocessingstagesandwhoseedgesrepresentuni-directionaldataflow.TheFilterGraphManageralsohandlessynchronization,eventnotification,andotheraspectsofthecontrollingthefiltergraph.
hr=CoCreateInstance(
CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void**)&pGraph);
OncetheinstanceoftheFilterGraphManageriscreated,therealworkstarts.UsethereturnedIGraphBuilderpointertoqueryfortheIMediaControlandIMediaEventinterfaces:
hr=pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl);
hr=pGraph->QueryInterface(IID_IMediaEvent,(void**)&pEvent);
ThenuseIGraphBuilder::RenderFiletobuildthefiltergraphautomaticallyforthemediasuppliedtoit–theFilterGraphmanagerwillconnecttheappropriatefiltersforthespecifiedmediafile.Thisisotherwiseknownas“Intelligentconnect”inDirectShow:
hr=pGraph->RenderFile(L”C:\temp\Song.mp4”,NULL);
UseIMediaControl::Runtostartthedataflowinthefiltergraph.IMediaControlprovidesthemethodstorun,pauseorstopthemedia.
UseIMediaEvent::WaitForCompletiontowaitforittocomplete(caution:usingINFINITEinarealapplicationcouldcauseittoblockindefinitely.)
pControl->Run();
pEvent->WaitForCompletion(INFINITE,&evCode);
Fullcodelisting:
#include<dshow.h>
#include<iostream>
#pragmacomment(lib,“Strmiids.lib”)
voidmain()
{
IGraphBuilder*pGraph=NULL;
IMediaControl*pControl=NULL;
IMediaEvent*pEvent=NULL;
//InitializetheCOMlibrary.
HRESULThr=CoInitialize(NULL);
if(FAILED(hr))
{
std::cout<<“CouldnotinitializeCOMlibrary”<<std::endl;
return;
}
//Createthefiltergraphmanagerandqueryforinterfaces.
hr=CoCreateInstance(
CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGraphBuilder,
(void**)&pGraph);
if(FAILED(hr))
{
std::cout<<“CouldnotcreatetheFilterGraphManager.”
<<std::endl;
return;
}
hr=pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl);
hr=pGraph->QueryInterface(IID_IMediaEvent,(void**)&pEvent);
//Buildandrungraph.NOTE:changepathtoanactualfileonyoursystem
hr=pGraph->RenderFile(L”C:\temp\Song.mp4”,NULL);
if(SUCCEEDED(hr))
{
hr=pControl->Run();
if(SUCCEEDED(hr))
{
longevCode;
pEvent->WaitForCompletion(INFINITE,&evCode);
}
}
pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();
}
2.4Creatingadynamiclinklibrary(DLL)
ADLLisacodeordatalibrarythatcanbeusedbyotherapplicationsorlibraries.Forexample,commonmethodsforlogging,processingorworkingwithuserinterfacecontrolscanbemadeavailableinlibrariessothatseveralapplicationscanusethesamefunctionality.Thisnotonlyreducestheneedtocreatethesamecodemultipletimes,butalsoensurescommonality(thesamedialogisusedacrossdifferentapplications).ThissectiondescribeshowtocreateyourownDLLandutilizeitsroutinesfromanapplicationcreatedelsewhere.
Createanewdllproject
FromtheFilemenu,selectNewandthenProject.IntheProjecttypespane,underVisualC++,selectWin32>Win32ConsoleApplication.IntheNamefieldchooseanamefortheproject,suchasMathFuncsDll.AndintheSolutionnamefield,chooseanameforthesolution,suchasDynamicLibrary.Thisinitialcreationissummarizedbythefollowingscreenshot:
NowpressOKtostarttheWin32applicationwizard.PressNextandintheApplicationSettingspagesetDLLastheApplicationtypeifavailableorConsoleapplicationifDLLisnotavailable.SomeversionsofVisualStudiodonotsupportcreatingaDLLprojectusingwizards.(Ifso,itispossibletochangethislatertomakeyourprojectcompileintoaDLL.)InAdditionaloptionsselectEmptyproject.Thisissummarizedasfollows:
PressFinishtocreatetheproject.
Addclass(es)tothedynamiclinklibrary
SelecttheProjectmenu>AddNewItemandintheCategoriespanethatappears,undertheVisualC++sectionofInstalledTemplates,selectCode.SelectHeaderFile(.h)andchooseanamefortheheaderfile,suchasMathFuncsDll.h:
ThenpressAdd.Ablankfilewillbedisplayed.Pasteintheexamplecode:
//MathFuncsDll.h
namespaceMathFuncs
{
classMyMathFuncs
{
public:
//Returnsa+b
static__declspec(dllexport)doubleAdd(doublea,doubleb);
//Returnsa-b
static__declspec(dllexport)doubleSubtract(doublea,doubleb);
//Returnsa*b
static__declspec(dllexport)doubleMultiply(doublea,doubleb);
//Returnsa/b
//ThrowsDivideByZeroExceptionifbis0
static__declspec(dllexport)doubleDivide(doublea,doubleb);
};
}
Notethe__declspec(dllexport)modifiersinthemethoddeclarationsabove.ThesemodifiersenablethemethodtobeexportedbytheDLLsotheycanbeusedbyotherapplications.
Createthesourcecodefortheclass(es)
FromtheProjectmenu,selectAddNewItem.TheAddNewItemdialogwillbedisplayed.FromtheCategoriespane,underVisualC++,selectCode.SelectC++File(.cpp).Chooseanameforthesourcefile,suchasMathFuncsDll.cpp.
PressAdd.Ablank.cppfilewillbedisplayed.Pasteinthisexamplecode:
//MathFuncsDll.cpp
//compilewith:/EHsc/LD
#include“MathFuncsDll.h”
#include<stdexcept>
usingnamespacestd;
namespaceMathFuncs
{
doubleMyMathFuncs::Add(doublea,doubleb)
{
returna+b;
}
doubleMyMathFuncs::Subtract(doublea,doubleb)
{
returna-b;
}
doubleMyMathFuncs::Multiply(doublea,doubleb)
{
returna*b;
}
doubleMyMathFuncs::Divide(doublea,doubleb)
{
if(b==0)
{
thrownewinvalid_argument(“bcannotbezero!”);
}
returna/b;
}
}
BuildtheprojectintoaDLL
FromtheProjectmenu,selectMathFuncsDllProperties,andunderConfigurationProperties,selectGeneral.Intherightpane,underProjectDefaults,changetheConfigurationTypetoDynamicLibrary(.dll).
PressOKtosavethechanges.
CompiletheDLL
SelectBuildSolutionfromtheBuildmenu.ThiscreatesaDLLthatcanbeusedbyotherprograms.
CreateaseparateapplicationthatreferencestheDLL
NowwewilldemonstratehowtoreferencetheDLLwecreatedfromotherapplications.FromtheFilemenu,selectNewandthenProject.FromtheProjecttypespane,underVisualC++,selectWin32.IntheTemplatespane,selectWin32ConsoleApplication.Chooseanamefortheproject,suchasMyExecRefsDll,andenteritintheNamefield.NexttoSolution,selectAddtoSolutionfromthedropdownlist.Thiswilladdthenewprojecttothesamesolutionasthedynamiclinklibrary.Thesummaryisshowninthefollowingscreenshot:
PressOKtostarttheWin32ApplicationWizard.FromtheOverviewpageoftheWin32ApplicationWizard,pressNext.FromtheApplicationSettingspageoftheWin32ApplicationWizard,underApplicationtype,selectConsoleapplication.FromtheApplicationSettingspageoftheWin32ApplicationWizard,underAdditionaloptions,deselectPrecompiledheader:
PressFinishtocreatetheproject.
StartusingtheDLLfunctionality
AftercreatingthenewConsoleApplication,anemptyprogramiscreatedforyouwhichis
namedthesameasthenameyouchosefortheprojectabove.Inthisexample,MyExecRefsDll.cpp.
TousetheroutinesyoucreatedintheDLL,youreferenceitasfollows:fromtheProjectmenu,selectReferences.AndfromthePropertyPagesdialog,expandtheCommonPropertiesnodeandselectFrameworkandReferences.
SelecttheAddNewReferencebutton.ThiswilldisplaytheAddReferencedialog,listingallthelibrariesthatyoucanreference.FromtheProjectstab,selectMathFuncsDll,thenselectOK:
ReferencetheDLLheaderfiles
FromthePropertyPagesdialog,expandtheConfigurationPropertiesnode,thentheC/C++node,andselectGeneral.NexttoAdditionalIncludeDirectories,typeinthepathtothelocationoftheMathFuncsDll.hheaderfile:
TellthesystemwheretolocatetheDLLsatruntime
FromthePropertyPagesdialog,expandtheConfigurationPropertiesnodeandselectDebugging.NexttoEnvironment,typeinthefollowing:PATH=,wheretheblankisreplacedwiththeactuallocationofyourMathFuncsDll.dll.
PressOKtosaveallthechangesmade.
StartusingtheactualDLLcomponents
NowreplacethecontentsofMyExecRefsDll.cppwiththefollowingcode:
//MyExecRefsDll.cpp
//compilewith:/EHsc/linkMathFuncsDll.lib
#include<iostream>
#include“MathFuncsDll.h”
usingnamespacestd;
intmain()
{
doublea=7.4;
intb=99;
cout<<“a+b=”<<MathFuncs::MyMathFuncs::Add(a,b)<<endl;
cout<<“a-b=”<<MathFuncs::MyMathFuncs::Subtract(a,b)<<endl;
cout<<“a*b=”<<MathFuncs::MyMathFuncs::Multiply(a,b)<<endl;
cout<<“a/b=”<<MathFuncs::MyMathFuncs::Divide(a,b)<<endl;
return0;
}
Right-clicktheMyExecRefsDllprojectand‘Selectasstartupproject’.Rightclickonyoursolution,doacompletecleanandrebuildandthenrun.
2.5SerializingYourData
Thissectionshowsyouhowtoutilizethebuilt-infilehandlingcapabilitiesthatMFChastoserializeyourdata.Thatmeansreadingfromthedataorwritingittoadisk.
UsingtheMFCdocumentobject
OnewayistousetheMFCdocumentobjecttodotheserializingforyou.ThiswillbedemonstratedbycreatingasimpleSDI(singledocumentinterface)usingtheAppWizard.SelectFile>New>ProjectandselecttheVisualC++>MFCApplicationasthechoiceofinstalledtemplate:
IntheApplicationType,selectSingleDocument:
IntheAdvancedFeaturessection,unchecktheAdvancedframepanes:
Forthisexample,wewilllettheprogramacceptanddisplayinputfromthekeyboard.
ThefirstbitofcodingistostorethecharacterstheusertypesinusingaCStringobject,m_strDatainthedocumentclassthatiscreatedinSerializeSDIexampleDoc.h:
classCSerializeSDIexampleDoc:publicCDocument
{
protected://createfromserializationonly
CSerializeSDIexampleDoc();
DECLARE_DYNCREATE(CSerializeSDIexampleDoc)
//Attributes
public:
CStringm_strData;
…
Andinitialisethevalueofm_strDatainthedocument’sconstructor:
CSerializeSDIexampleDoc::CSerializeSDIexampleDoc()
{
m_strData=””;
}
WethenneedtowritesomecodetohandletheWM_CHARWindowsmessagesthatarereceivedwhentheuserinputscharactersfromthekeyboard.Youcaneitherjustwritethiscodeyourself,orusetheClassWizard.TousetheClassWizard,right-clicktheprojectfolderandselectClassWizard…
EnsuretheCSerializeSDIexampleViewclassnameisselected.SelecttheMessagetabandthenselectthe‘AddHandler…’button.
ThenpressApply.ClicktheEditCodebuttonsowecanwritethecodetostorethecharacterstheusertypesin.
Inaddition,thishandlercheckstoseeiftheuserhasenteredbackspace,inwhichcaseitdeletesthelastcharacterbyshorteningthestringdatamemberby1:
voidCSerializeSDIexampleView::OnChar(UINTnChar,UINTnRepCnt,UINTnFlags)
{
//TODO:Addyourmessagehandlercodehereand/orcalldefault
CSerializeSDIexampleDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
if(nChar==8)
{
intlength=pDoc->m_strData.GetLength();
CStringstr=pDoc->m_strData.Left(length-1);
pDoc->m_strData=str;
}
else
{
CStringstr;
str.Format(TEXT(“%c”),nChar);
pDoc->m_strData+=str;
}
Invalidate();
CView::OnChar(nChar,nRepCnt,nFlags);
}
Invalidate()isusedtoinvalidatetheviewwhentheusertypesanewcharacter.WethenaddanmessagehandlertodisplaythenewtextstringinOnDraw()methodinsideCSerializeSDIexampleView,whichisautomaticallycreated.UncommentCDC*/*pDC*/andaddthecodetodisplaythetext:
voidCSerializeSDIexampleView::OnDraw(CDC*pDC)
{
CSerializeSDIexampleDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
if(!pDoc)return;
pDC->TextOut(0,0,pDoc->m_strData);
}
NoticethattheCSerializeSDIexampleDocclassalreadyhasaSerialize()method.Thisiswhereweserializethem_strDatastring.UsetheCArchivereferencepassedtoitinthesameasyouwouldusestd::coutorstd::cintoreadandwritetothediskrespectively:
voidCSerializeSDIexampleDoc::Serialize(CArchive&ar)
{
if(ar.IsStoring())
{
ar<<m_strData;
}
else
{
ar>>m_strData;
}
}
Onelastmodificationwemaketotheprojectistoensuretheapplicationrememberswhentheuserhasaddedmoredatatothem_strDataobject.Thismeanstheapplicationwillprompttheuserwitha“Savechangestodocument”messagewhentheuserattemptstoexittheapplication.
InCSerializeSDIexampleView::OnCharindicatethatthedocumenthaschangedbysettingthemodifiedflag:
voidCSerializeSDIexampleView::OnChar(UINTnChar,UINTnRepCnt,UINTnFlags)
{
//TODO:Addyourmessagehandlercodehereand/orcalldefault
CSerializeSDIexampleDoc*pDoc=GetDocument();
ASSERT_VALID(pDoc);
if(nChar==8)
{
intlength=pDoc->m_strData.GetLength();
CStringstr=pDoc->m_strData.Left(length-1);
pDoc->m_strData=str;
}
else
{
CStringstr;
str.Format(TEXT(“%c”),nChar);
pDoc->m_strData+=str;
}
Invalidate();
pDoc->SetModifiedFlag();
CView::OnChar(nChar,nRepCnt,nFlags);
}
Whenrunningtheprogramwecanentercharactersordeletethemusingthebackspace:
Nowtryclosingtheapplicationwithoutsavingthetext,sothatyouarepromptedifyouwishtosave:
Givethefileanameandthensave:
ChapterSummary
Tosummarizethismasterlesson,youcannow:
Createyourownwebbrowser.
DownloadfilesviatheFileTransferProtocol.
PlayorcapturemediafilesusingDirectShow.
Createadynamiclinklibrary(DLL)andreferenceitfromanotherapplication.
ApplytheserializationmechanismprovidedbyMFCtostoreandretrievethemembervariablesofyourapplicationonastoragemediumsuchasaharddisk.
Chapter3:GettingStartedwiththeBoostLibraries
3.1Introduction
Boostisapowerfulsetoflibrariesthatcanbeappliedtoawiderangeoftasksandapplicationdomains.IfyouareseekingtoaccomplishaC++programmingtaskinarobustandgenericway,thereisastrongchancethataBoostlibraryalreadyexistsforthispurpose.Theyrangefromthreadsandfileprocessingtonumericalanalysisandgraphalgorithms.Acomprehensivedescriptionofanymorethanasmallnumberoftheselibrariesisbeyondthescopeofthisebook.TheBoosthomepagecontainsextensivedocumentationonalloftheindividualcomponentsandtherearesomegoodbooksavailableonthesubject.
Whatwewillfocusonhereishowtoreallygetstarted.KnowinghowtogetBoostinstalledandreadyonyourchosendevelopmentenvironmentandgettingafeelforhowtheselibrariesareusedisoftentrickyforthosewhoareunfamiliar.Itdoesnothelpthatsomeoftheavailabledocumentationcanbesketchy.
Sothischapterisanin-depthguidetosettingupBoostinanumberofcommonIntegratedDevelopmentEnvironments(IDEs)includingsomeusageexamples.
3.2Pre-requisites
BeforegettingtogripswithusingBoost,firstmakesureyouhavethenecessarypre-requisitesdescribedinthischapter.IfyoudonotalreadyhaveBoost,downloadandextracttheBoostzipfiletoadestinationofyourchoice.OfficialreleasesofBoostmaybeobtainedfromhttp://www.boost.org/users/download/
Generatingcompiledlibraries.
Asstatedonthe‘GettingStartedonWindows’Boostwebpage,mostBoostlibrariesareoftheheader-basedtype.Inotherwords,oncetheyareinstalledtoyourcomputertheyrequirenofurthercompilationorinterventiontomakethemwork.Tousesuchlibraries,youneedonlyaddthecorrectincludeslikethis:
#include<boost/lambda/lambda.hpp>
ThereexistotherBoostlibrariesthatrequireadditionalcompilationtousethem,suchasthreads,filesystems,regularexpressionsandtheunittestframework.ThefollowingsectionsdescribehowtogeneratecompiledlibrariesforthefollowingWindows-basedC++compilerenvironments:VisualC++,MinGWandCygWin.Followtheinstructionsthatareappropriatetoyourenvironment.
GeneratingcompiledlibrariesforVisualStudio
Openacommandpromptandnavigatetothelocationofyourboostrootfolder.Enclosethepathinquotationmarksifitcontainswhitespaces:
Fromthecommandpromptrunthebootstrapcommandbootstrap.bat:
Run.\b2fromthecommandprompt.Thiscantakequitesometimetofinish:
Andthat’sallthereistoit.ThecompiledlibrariesthatVisualStudiousesgetinstalledtothe<root>/stage/libdirectory:
GeneratingcompiledlibrariesforMinGW
MinGW(MinimalistGNUforWindows)isafreeandopensourcealternativefordevelopingMicrosoftWindowsapplications.ItcontainstheGNUCompilerCollection(GCC)andvariousWindowsheaderfilesandstaticlibrariestoenabletheuseoftheWindowsAPI.
GetstartedbysettingthePATHenvironmentvariablebyright-clickingMyComputerandselectingProperties,followedbytheAdvancedtab,followedbytheEnvironmentVariablesbutton.IntheSystemVariablessectionselectthePATHenvironmentvariableandthenselecttheEditbutton:
IntheEditcontrolthatappears,includethesettingforincludingMinGWbyappending“C:\mingw\bin”followedbyasemi-colonasshown:
TogeneratetheBoostcompiledlibrariesusingMinGW,openacommandprompt,navigatetotheBoostrootdirectoryandrunthecommandbootstrap.batmingw:
ThiswillgeneratethetwoexecutablesneededtocompiletheBoostlibraries,b2.exeandbjam.exe.Oncedoneexecutethecommandb2.exetoolset=gcc
ThenthecompiledlibrariestobeusedbyMinGWaregeneratedasshown:
GeneratingcompiledlibrariesforCygWin
TheadvantageofusingCygwinisthatmakesavailablemanyUNIXtoolslackingintheWindowsoperatingsystem.IfCygwinisyourchoiceofcompiler,settheEnvironmentVariablessothatyourIDEcanaccesstheCygwintoolcollection.FromtheStartbutton,right-click‘MyComputer’andselect‘Properties’.Selectthe‘Advanced’tabandthen‘EnvironmentVariables’.Selectthe‘Path’inthe‘SystemVariables’sectionandthenselectthe‘Edit’button:
AndthensetthelocationoftheCygwintoolset:
ToconfigureBoosttoworkwithCygwin,opentheCygwinterminalandnavigatetothelocationwhereyourBoostlibrarieshavebeeninstalled.ForWindowsdirectorieswithwhitespacesencloseitinquoteslikethis:
$cd“C:\ProgramFiles\boost_1_55_0”
Checktoseethattheboost/folderexistsatthislocationusingthelscommand:
Iftheboostdirectoryispresent,runthebootstrapcommand:
$./bootstrap.sh–prefix=boost/
Thenrunthe$./bjam.execommand(thiscantakeawhile):
Nowrunthe$./bjaminstallcommand(thiscantakeaverylongtime):
Oncethisstagehasbeencompletedyouwillfindthatthreeadditionalfoldershavebeencreatedwithintheboost/folder,namelybin,includeandlib.
Lastly,recursivelycopythefolderstothe/usr/local/destination:
$cp-rboost/bin/usr/local/
$cp-rboost/include/usr/local/
$cp-rboost/lib/usr/local/
3.3ConfiguringBoostinMicrosoftVisualStudio
Header-onlylibraryexample:BoostFormat
TogetafeelforusingBoostinVisualStudiolet’sstartwithalibraryrequiringnoseparatecompilation:BoostFormat,atype-safeutilityforformattingargumentsaccordingtoaformatstring.CreateanemptyprojectbyselectingFile>New>Project.
Setthe‘Createdirectoryforsolution’checkboxandnameyourproject:
Addthecodebyright-clickingtheprojectfolderandselectingAdd>NewItem.SelectC++Fileandnameitas‘main.cpp’:
Insertthiscodesnippetintomain.cppasanexampleofhowtoformatandoutputanarrayofhexadecimalvalues:
#include<iostream>
#include<boost/format.hpp>
intmain()
{
unsignedintarr[5]={0x05,0x04,0xAA,0x0F,0x0D};
std::cout<<boost::format(“%02X-%02X-%02X-%02X-%02X”)
%arr[0]
%arr[1]
%arr[2]
%arr[3]
%arr[4]
<<endl;
}
NoticethatbuilderrorswilloccurifwetrytocompiletheprojectwithouthavingfirstspecifiedtheadditionalincludesrequiredtousetheBoostlibraries:
fatalerrorC1083:Cannotopenincludefile:‘boost/format.hpp’:Nosuchfileordirectory
Right-clickyourprojectfolderandselect‘Properties’.IntheC++>GeneraltabedittheAdditionalIncludeDirectoriessection:
TellVisualStudiowhereyourBoostincludefilesreside:
Andthat’sallyouneedtodotousenon-compiledBoostlibraries.Yourprogramshouldnowsuccessfullycompile.
Runningtheprogramgeneratesthehexadecimaloutput:
Compiledlibraryexample:BoostRegex
ABoostlibraryrequiringseparatecompilationisBoostRegex,aC++implementationofregularexpressions,whichareaformofpatternmatchingoftenusedintextprocessing.
SelectFile>New>Project>VisualC++>EmptyProject.
CallitBoostRegex:
SelectNew>File>C++Fileandnameitmain.cpp.
Intomain.cppcopythefollowingcode:
#include<boost/regex.hpp>
#include<iostream>
#include<string>
#include<sstream>
intmain()
{
//RegextomatchYYYY-MM-DDdates
staticconstboost::regexdate_regex(“([0-9][0-9][0-9][0-9])-([1-9]|0[1-9]|1[012])-([1-9]|[0-2][1-9]|3[01])”);
std::stringinput=“2014-08-28”;
boost::smatchchar_matches;
if(boost::regex_match(input,char_matches,date_regex))
{
std::stringstreamss_match;
ss_match<<char_matches[1]
<<“-”
<<char_matches[2]
<<“-”
<<char_matches[3];
std::cout<<ss_match.str();
}
else
{
std::cout<<“Firstexampleshouldhavematchedtheregex.\n”;
}
}
SettheincludedirectoryforBoostRegex.Failingtodothiswillgeneratethefollowingcompilationerror:
fatalerrorC1083:Cannotopenincludefile:‘boost/regex.hpp’:Nosuchfileordirectory
Right-clickyourprojectfolderandselectProperties.IntheC/C++>GeneraltabsettheAdditionalIncludeDirectoriesfield:
Failingtosetthelocationoftheadditionallibrarydirectorieswillgeneratethefollowinglinkerrorwhenyoutrytocompileyourproject:
LINK:fatalerrorLNK1104:cannotopenfile‘libboost_regex-vc100-mt-gd-1_55.lib’
Right-clickyourprojectfolderandselectProperties.
IntheLinker>Generaltab,settheAdditionalLibraryDirectoriesfieldtotellVisualStudiowheretoobtaintherequiredlibraryfiles:
Andthat’sit.YourBoostRegexVisualStudioprojectshouldnowcompileandallowyoutoruntheaboveexample:
3.4ConfiguringBoostinEclipse
Header-onlylibraryexample:BoostDateTime
BoostDateTimeisahighlycustomizablelibraryforprogrammingwithdatesandtimes.OpenEclipseandselectNew>C++Project.Inthe‘C++Project’dialogthatappears,selectEmptyProject,givetheprojectanamesuchas‘BoostDateTime’,andselectthedesiredtoolchain(suchasCygwin):
ClickNextandmakesuretheReleaseandDebugcheckboxesareset:
ClickFinish.
Nowthatwehavecreatedtheproject,rightclickonthe‘BoostDateTime’projectfolderandselectNew>SourceFile.
Fillinthefieldsaccordingtoyourpreferences,andthenclickFinish:
Overwritethemain.cppfilethatgetscreatedwiththefollowingcodesothatwemayusethisexampleofaheader-onlyexampleofBoostDateTime:
#include<boost/format.hpp>
#include<boost/date_time.hpp>
#include<iostream>
intmain()
{
constboost::posix_time::ptimenow=
boost::posix_time::second_clock::local_time();
constboost::wformatf=boost::wformat(L”%02d.%02d.%s%02d:%02d”)
%now.date().year_month_day().day.as_number()
%now.date().year_month_day().month.as_number()
%now.date().year_month_day().year
%now.time_of_day().hours()
%now.time_of_day().minutes();
std::wcout<<f.str()<<std::endl;
}
Notethatwheninsertingthiscode,theProblemstabispopulatedwitherrormessagessignifyinganumberofunresolveddependencies.ThisisbecausewehaveyettoconfigureourprojecttoworkwithBoost.
Right-clicktheprojectfolderandselectProperties.IntheC/C++Buildsection,verifythatyouhaveatoolchainselected,suchasCygwinGCC:
SelecttheC/C++Generalsection.InthePathsandSymbolssectionselecttheIncludestabandthe‘GNUC++’language.
NowweneedtotellEclipsewheretofindtheBoostlibraryfolderweextracted.SelecttheAddbuttonandwhentheAdddirectorypath’dialogislaunched,selectthe‘File
system…’buttontosettheBoostfolderdirectory:
ClickOKandtheclickApplywhenyouarereturnedtotheprojectPropertiesdialog:
Repeatbyaddingthesamepathforthe‘GNUC’language.Fromthemenu,selectSelectProject>Clean,makingsuretochecktheBoostDateTimecheckbox:
PressOKtocleanandbuildtheproject.TostartdebuggingyourEclipseproject,right-clicktheleft-sideofthecodewindowandselect‘Togglebreakpoint’:
Asmallbuttonshouldappearatthepointatwhereyouchosethebreakpoint.Tostartdebugging,selectRun>Debugfromthemainmenu.OncethedebuggingprocessstartsyouwillgetdirectedtotheDebugperspective.PressF6tostepthrougheachlineofcode,andviewtheresultsintheConsoletab:
ThisgivesusthedesiredoutputviaBoostFormatandthat’sallyouneedtodotouseBoostnon-CompiledlibrariesinEclipse.
Compiledlibraryexample:BoostSerialization
UsingthecompiledBoostlibrariesinEclipseinvolvesafewadditionalstepstotheonesdescribedforheader-onlylibraries.
Forthisexample,wewilldemonstratetheuseofBoostSerialization,whichisameansofconvertingC++objectsintoaformatthatcanbesavedandrestoredatalaterdate.
Asbefore,selectNew>C/C++Project.
ClicknextandensuretheDebugandReleasecheckboxesaresetasbefore.
ClickFinish.
Aswiththenon-compiledexample,right-clicktheprojectandselectNew>SourceFile.
ThenrightclickthesrcfolderjustcreatedandselectNew>SourceFile.Wewillcallitmain.cpp.ClickFinishandcopyinthefollowingcode,anexampleusageofBoostSerialization:
#include<vector>
#include<fstream>
#include“boost/serialization/vector.hpp”
#include“boost/archive/text_oarchive.hpp”
#include“boost/archive/text_iarchive.hpp”
classInfo
{
private:
//Allowserializationtoaccessnon-publicdatamembers.
friendclassboost::serialization::access;
//Serializethestd::vectormemberofInfo
template<classArchive>
voidserialize(Archive&ar,constunsignedintversion)
{
ar&filenames;
}
std::vector<std::string>filenames;
public:
voidAddFilename(conststd::string&filename);
voidPrint()const;
};
voidInfo::Print()const
{
std::copy(filenames.begin(),
filenames.end(),
std::ostream_iterator<std::string>(std::cout,“\n”));
}
voidInfo::AddFilename(conststd::string&filename)
{
filenames.push_back(filename);
}
intmain(intargc,char**argv)
{
Infoinfo;
info.AddFilename(“ThisFile.txt”);
info.AddFilename(“ThatFile.txt”);
info.AddFilename(“OtherFile.txt”);
//SavefilenamedatacontainedinInfoobject
{
//Createanoutputarchive
std::ofstreamofs(“store.dat”);
boost::archive::text_oarchivear(ofs);
//Savethedata
ar&info;
}
//Restorefromsaveddataandprinttoverifycontents
Inforestored_info;
{
//Createandinputarchive
std::ifstreamifs(“store.dat”);
boost::archive::text_iarchivear(ifs);
//Loadthedata
ar&restored_info;
}
restored_info.Print();
return0;
}
Aswiththeheader-onlyexample,weneedtosettheincludedirectoryforthisproject:
Forourcompiledlibraryexamplewealsoneedtosetthelocationofthelibrarypath.InthePropertiesdialog,selectthe‘LibraryPaths’tab,clickthe‘Add’button.Inthe‘Add’dialogthatappears,setthelibrarypathlocation:
IntheLibrariestab,addthe‘boost_serialization’library:
Thereremainsonefinaltaskforourexampletowork:copyingthefollowingdllsfromtheC:\Cygwin/binfolderintotheDebug/Releasefolderwhereourexecutableislocated:
cyggcc_s-1.dll
cygstdc++-6.dll
cygwin1.dll
That’sbecausedirectoriesontheEnvironmentVariablesPATHarenotalwayssearched(inmyexperience),soweputtheDLLsinourapplicationdirectorytobesafeandenableustobuildandruneachapplication.
3.5ConfiguringBoostinNetBeans
Header-onlylibraryexample:BoostLexicalCast
ForourNetBeansheader-onlyexamplewewilldemonstratetheuseofBoostLexicalCase,asafemeansofconvertingarbitrarydatatypessuchasintegersintoliteraltextforms,andviceversa.InNetBeansselectFile>NewProject>C/C++Application:
ClickNextandgiveyourprojectanameandclickFinish.
NoticehowNetBeansrecognizestheToolscollectioninstalledasbeingCygwinifinstalled.
Locatethemain.cppsourcefileintheProjectstabandreplaceitwiththefollowingcodeexamplethatimplementsBoostLexicalCast.
#include<boost/lexical_cast.hpp>
intmain()
{
try
{
std::cout<<boost::lexical_cast<double>(“3.141592”)<<“\n”;
}
catch(boost::bad_lexical_castconst&e)
{
std::cout<<“Error:”<<e.what()<<“\n”;
}
return0;
}
GiventhatBoostLexicalCastisaheader-onlylibrary,weneedonlysettheadditionalincludedirectoriesinourNetBeansproject.Right-clickyourprojectfolderandselect
Properties.IntheBuild>C++Compilercategory,editthe‘IncludeDirectories’sectiontosetwheretheboostincludefilesarelocated.Youcanuseabsolutepathsorrelativepaths:
TheIncludedirectorieswilllooklikethis:
WearenowdonewithconfiguringNetbeanstoworkwithaBoostheader-onlylibrary.Allthatremainsistobuildtheprogramwhichgivesusthefollowingoutput:
30.08.201421:34
RUNSUCCESSFUL(totaltime:219ms)
Compiledlibraryexample:BoostFilesystem
TheBoostFilesystemlibraryisaninterfaceforidentifyingandmanipulatingfilesanddirectories,andisanotherexampleofaBoostlibraryrequiringseparatecompilation.CreateanewC++ApplicationasperthepreviousNetbeansexample:
Inmain.cppcopythisexampleofusingBoostFilesystemtoiterateoverandprintthenamesoffilesinadirectory:
#include<boost/filesystem.hpp>
#include<boost/foreach.hpp>
intmain()
{
boost::filesystem::pathtargetDir(“C:\MyStuff”);
boost::filesystem::directory_iteratorit(targetDir),eod;
BOOST_FOREACH(boost::filesystem::pathconst&p,std::make_pair(it,eod))
{
if(is_regular_file(p)){
std::stringfilename=p.filename().string();
std::cout<<filename<<std::endl;
}
}
return0;
}
Togetthisprojecttoproperlybuild,firstthesettheBoostincludedirectoryasbefore:
Andbecausethisisusingacompiledlibrary,weneedtosettheLinkeroptions,wherethelibrarydirectoriesresideandwhatadditionalcommandlineoptionsaretobeused.IntheLinkersection,select‘AdditionalLibraryDirectories’:
Whensettingadditionalincludelibraries,Ipreferusingrelativepathsasthefullabsolutefilepathisnotneeded.ThengototheLibrariessection,andselectthe‘AddOption…’button.Checkthe‘OtherOption’radiobuttonandtype–lboost_filesystemandclickOK.
WhenusingBoostFilesystemyouwillneedtoinclude–lboost_systemaswell,sorepeatforthisoption:
WhendonetheLinkerpropertypagewilllooklikethis:
ChapterSummary
Tosummarizethislatestmasterlesson,youarenowableto:
ConfiguretheBoostlibraries,bothheader-onlyandcompiled,foruseincommonly-usedIntegratedDevelopmentenvironmentsincludingMicrosoftVisualStudio,EclipseandNetBeans.
Createandusethecompiledlibrariesrequiredbycommonly-usedcompilerenvironmentssuchasVisualC++,MinGWandCygwin.
ApplytheBoostlibrariesinyourapplicationsincludingthosethatimplementfilesystems,datesandtimes,regularexpressions,serializationandoutputformatting.
Chapter4:PracticalApplicationsofGraphAlgorithms
Agraphisadatastructurethatisusedtorepresentnetworksandtheirinterconnections.Networknodesarefrequentlyknownasvertices,whilethelinksconnectingthemareknownasedgesorsometimesarcs.Inpracticalscenarios,graphscanbeusedtomodeltelecommunicationsnetworks,transportationsystems,waterdistributionnetworks(pipes)andelectricalwiringamongstotherthings.Graphsareusuallyvisuallyrepresentedasaseriesofdotsorcirclesrepresentingtheverticesandlinesdrawnbetweenthemtorepresenttheedges,sometimesusingarrowstoindicatedirection.
4.1ModelingNetworksasGraphs
ThisexampleGraphclassisbasedonSedgewick’sC++implementation2,specifically,theDenseGRAPHclass.Sedgewick’simplementation(seehttps://www.cs.princeton.edu/~rs/Algs3.cxx5/code.txt)specificallytrackthenumberofedgesandvertices,havemethodsforinsertingandremovingedges,anduseanadjacencymatrixoradjacencylisttotrackinterconnectionsbetweenvertices.IhavemodifiedtheclassslightlytoallocateEdgeobjectsassmartpointerstoensurememoryisdeallocateduponcompletion.
#include<vector>
//Thiscodeisfrom“AlgorithmsinC++,ThirdEdition,”
//byRobertSedgewick,Addison-Wesley,2002.
classEdge
{
public:
intv,w;
doublewt;
Edge(intv=-1,intw=-1,doublewt=0.0):
v(v),w(w),wt(wt){}
};
typedefstd::shared_ptr<Edge>EdgePtr;
template<classEdge>
classDenseGRAPH
{
private:
intVcnt,Ecnt;
booldigraph;
std::vector<std::vector<EdgePtr>>adj;
public:
DenseGRAPH(intV,booldigraph=false):
adj(V),
Vcnt(V),
Ecnt(0),
digraph(digraph)
{
for(inti=0;i<V;i++)adj[i].resize(V);
}
intV()const{returnVcnt;}
intE()const{returnEcnt;}
booldirected()const{returndigraph;}
voidinsert(EdgePtre)
{
intv=e->v;
intw=e->w;
if(adj[v][w]==0)Ecnt++;
adj[v][w]=e;
if(!digraph)adj[w][v]=e;
}
voidremove(EdgePtre)
{
intv=e->v;
intw=e->w;
if(adj[v][w]!=0)
Ecnt—;
adj[v][w]=0;
if(!digraph)adj[w][v]=0;
}
EdgePtredge(intv,intw)const
{returnadj[v][w];}
classadjIterator;
friendclassadjIterator;
};
template<classEdge>
classDenseGRAPH<Edge>::adjIterator
{
private:
constDenseGRAPH&G;
inti,v;
public:
adjIterator(constDenseGRAPH<Edge>&G,intv):
G(G),
v(v),
i(0){}
EdgePtrbeg(){i=-1;returnnxt();}
EdgePtrnxt()
{
for(i++;i<G.V();i++)
if(G.edge(v,i))
returnG.adj[v][i];
return0;
}
boolend()const{returni>=G.V();}
};
RepresentationsofnetworktopologiescanbecreatedbycreatinganinstanceofDenseGRAPH(renamedasGraph)andaddingthesetsofedges:
#include“Graph.h”
#include<iostream>
intmain()
{
Graph<Edge>*graph=newGraph<Edge>(5,false);
graph->insert(EdgePtr(newEdge(0,1)));
graph->insert(EdgePtr(newEdge(1,2)));
graph->insert(EdgePtr(newEdge(2,3)));
graph->insert(EdgePtr(newEdge(3,4)));
graph->insert(EdgePtr(newEdge(0,4)));
std::cout<<“No.vertices=”<<graph->V()<<std::endl;
std::cout<<“No.edges=”<<graph->E()<<std::endl;
EdgePtredgePtr=graph->edge(2,3);
return0;
4.2ImplementingDijkstra’sAlgorithm
Dijkstra’salgorithmsolvestheshortestpathproblemforagraphwithnon-negativeedgeweightsbyproducingashortestpathtree.Thisalgorithmisoftenusedinroutingandasasubroutineinothergraphalgorithmssuchasthek-shortestpathsalgorithm.Webuildashortestpathtree(SPT)oneedgeatatimebyalwaystakingthenextedgethatgivesashortestpathfromthesourcetoavertexnotontheSPT.Inotherwords,weaddverticestotheSPTinorderoftheirdistance(throughtheSPT)tothestartvertex.
TheoriginalDijkstra’salgorithmdidnotuseminpriorityqueuesandwasofcomplexityO(|V|2),whereVisthenumberofvertices.Thisperformancewaslaterimprovedbytheuseoflinked-listrepresentationsandminpriorityqueues.Edgesthatconnecttreeverticestonontreeverticesarestoredusingapriorityqueueimplementationthatallowsfortheupdatingofpriorities.
ThisimplementationinitiallyfindsallshortestpathswithintheconstructoroftheSPTtemplateclassandusesapriorityqueueofverticesheldinorderoftheirdistancefromthesourcenode.Thisdeterminesalltheshortestpathsfromthesourcenode.
Thequeueisinitializedwithpriority0forthesourcenodeandpriorityinfinityfortheothernodes.Theimplementationthenentersaloopwherebythelowest-prioritynodefromthequeueismovedtotheSPTandrelaxedalongitsincidentedges.Torelaxanedgev->wmeanstotestwhetherthebestknownwaytogetfromstowistogofromstov,andthengofromvtow.Ifso,weupdateourdatastructures.
#include“Graph.h”
template<classGraph,classEdge>
classSPT
{
private:
constGraph&G;
std::vector<double>wt;
std::vector<EdgePtr>spt;
public:
SPT(constGraph&G,ints):
G(G),
spt(G.V()),
wt(G.V(),G.V())
{
PQi<double>pQ(G.V(),wt);
for(intv=0;v<G.V();v++)
{
pQ.insert(v);
}
wt[s]=0.0;
pQ.lower(s);
while(!pQ.empty())
{
intv=pQ.getmin();
if(v!=s&&spt[v]==0)return;
typenameGraph::adjIteratorA(G,v);
for(EdgePtre=A.beg();!A.end();e=A.nxt())
{
intw=e->w;
doubleP=wt[v]+e->wt;
if(P<wt[w])
{
wt[w]=P;
pQ.lower(w);
spt[w]=e;
}
}
}
}
EdgePtrpathR(intv)const
{
returnspt[v];
}
doubledist(intv)const
{
returnwt[v];
}
};
SupposewewishtousetheGraphdatastructuretorepresentthefollowingnetworknodesandinterconnections:
WecanrepresentthisbybuildingaGraphstructurewithcallstotheinsertfunction:
std::auto_ptr<Graph<Edge>>graph(newGraph<Edge>(8,true));
graph->insert(EdgePtr(newEdge(1,6,1.0)));
graph->insert(EdgePtr(newEdge(1,2,2.5)));
graph->insert(EdgePtr(newEdge(1,4,3.0)));
graph->insert(EdgePtr(newEdge(1,5,0.5)));
graph->insert(EdgePtr(newEdge(2,1,0.5)));
graph->insert(EdgePtr(newEdge(2,3,3.0)));
graph->insert(EdgePtr(newEdge(2,4,2.0)));
graph->insert(EdgePtr(newEdge(2,5,2.5)));
graph->insert(EdgePtr(newEdge(3,4,1.0)));
graph->insert(EdgePtr(newEdge(3,7,1.5)));
graph->insert(EdgePtr(newEdge(4,5,1.0)));
graph->insert(EdgePtr(newEdge(5,4,0.5)));
graph->insert(EdgePtr(newEdge(5,6,0.5)));
graph->insert(EdgePtr(newEdge(5,7,1.5)));
graph->insert(EdgePtr(newEdge(7,6,1.0)));
Tofindalltheshortestpathsemanatingfromnode2isastraightforwardmatterofcreatingtheSPTtemplateclassinstance.
Thiscomputesthecompleteshortestpathtreeintheconstructor.Thefollowingcodesnippetshowshowtogeneratetheshortestpathsanddisplaytheshortestpathpossiblebetweennodes2to7,whichinthiscasewouldbe2->1->5->7:
//Findshortestpathbetweennodes2->7
intsource=2;
inttarget=7;
std::stack<int>path;
SPT<Graph<Edge>,Edge>sp(*graph,source);
while(true)
{
EdgePtre=sp.pathR(target);
path.push(target);
if(target==source)
break;
target=e->v;
}
//Printthepathnodes
while(!path.empty())
{
std::cout<<path.top()<<”“;
path.pop();
}
Thisgivestheshortestpossiblepathbetweennodes2and7as[2,1,5,7]:
4.3TestingforGraphConnectivity
Afrequentrequirementforgraphsisasanefficientmeansofdeterminingwhetheritisconnected-ornot.
Isitpossibleaselectedpairofnodestocommunicatewitheachother?Oristhegraphdisconnectedinsomeway?
UsingthesameGraphclassdiscussedintheprevioussections,thisprogramusesadepth-firstsearchalgorithmtotestwhetherallthegraphnodesgetvisitedduringtherecursivesearch.Herearesomeexamples:
1.Adisconnectedun-directedgraph,wherebynodes[3,4]aredisconnectedfromnodes[0,1,2],therebymakingitimpossiblefornode[0]tocommunicatewithnode[3]:
2.Adisconnecteddirectedgraph.Node[1]cancommunicatewithnodes[0,2,3]butnotnode[4],sinceadirectedlinkexistsbetweennode[4]tonode[0],butnotnode[0]tonode[4]:
3.Aconnectedun-directedgraph.Allnodescancommunicatewitheachother:
4.Aconnecteddirectedgraph.Allnodescancommunicatewithanyothernode.Thoughthereisnolinkbetweennodes[0,3],apathbetweenthetwonodesexistsvianodes[0,1,2,3].
Nowlet’saddtoourGraphclassthefollowingutilityfunctionsusedtotestifthegraphisstronglyconnected:
//ArecursivefunctiontoprintDFSstartingfromv
voidGraph::DFSUtil(intv,boolvisited[])
{
//Markthecurrentnodeasvisitedandprintit
visited[v]=true;
typenameGraph::adjIteratorA(*this,v);
for(EdgePtre=A.beg();!A.end();e=A.nxt()){
intw=e->w==v?e->v:e->w;
if(!visited[w])DFSUtil(w,visited);
}
}
//Functionthatreturnsreverse(ortranspose)ofthisgraph
Graph*Graph::getTranspose()
{
Graph<Edge>*g=newGraph<Edge>(Vcnt,digraph);
for(intv=0;v<Vcnt;v++)
{
typenameGraph::adjIteratorA(*this,v);
for(EdgePtre=A.beg();!A.end();e=A.nxt()){
intw=e->w;
g->insert(EdgePtr(newEdge(w,v)));
}
}
returng;
}
//Themainfunctionthatreturnstrueifgraphisstronglyconnected
boolGraph::isSC()
{
//Step1:Markalltheverticesasnotvisited(ForfirstDFS)
for(inti=0;i<Vcnt;i++)visited[i]=false;
//Step2:DoDFStraversalstartingfromfirstvertex.
DFSUtil(0,visited);
//IfDFStraversaldoesn’tvisitallvertices,thenreturnfalse.
for(inti=0;i<Vcnt;i++)
if(visited[i]==false)returnfalse;
//Step3:Createareversedgraph
Graph*gr=getTranspose();
//Step4:Markalltheverticesasnotvisited(ForsecondDFS)
for(inti=0;i<Vcnt;i++)visited[i]=false;
//Step5:DoDFSforreversedgraphstartingfromfirstvertex.
//StaringVertexmustbesamestartingpointoffirstDFS
gr->DFSUtil(0,visited);
//IfallverticesarenotvisitedinsecondDFS,returnfalse
for(inti=0;i<Vcnt;i++)
if(visited[i]==false)returnfalse;
returntrue;
}
Thefollowingcodelistingshowshowtoimplementeachofthefourexampleslistedabove:
#include“Graph.h”
#include<iostream>
intmain()
{
//Example1:disconnectedun-directedgraph:
//
//03
///\/
//124
//nodes[3,4]disconnectedfromnodes[0,1,2]
Graph<Edge>g1(5,false);
g1.insert(EdgePtr(newEdge(0,1)));
g1.insert(EdgePtr(newEdge(0,2)));
g1.insert(EdgePtr(newEdge(3,4)));
g1.isSC()?std::cout<<“Yes\n”:std::cout<<“No\n”;
//Example2:disconnected(directed)graph:
//
//3<-2<-1->0<-4
//eg1cancommunicatewith[2,3,0]butnot4
Graph<Edge>g2(5,true);
g2.insert(EdgePtr(newEdge(1,0)));
g2.insert(EdgePtr(newEdge(4,0)));
g2.insert(EdgePtr(newEdge(1,2)));
g2.insert(EdgePtr(newEdge(2,3)));
g2.isSC()?std::cout<<“Yes\n”:std::cout<<“No\n”;
//Example3:connected(un-directed)graph:
//
//3<->2<->1<->0<->4
//Allnodescancommunicatewithanyothernode
Graph<Edge>g3(5,false);
g3.insert(EdgePtr(newEdge(1,0)));
g3.insert(EdgePtr(newEdge(4,0)));
g3.insert(EdgePtr(newEdge(1,2)));
g3.insert(EdgePtr(newEdge(2,3)));
g3.isSC()?std::cout<<“Yes\n”:
std::cout<<“No\n”;
//Example4:connecteddirectedgraph
//
//0->1->2->3->0
//
//4->2->4
//Allnodescancommunicatewithanyothernode
Graph<Edge>g4(5,true);
g4.insert(EdgePtr(newEdge(0,1)));
g4.insert(EdgePtr(newEdge(1,2)));
g4.insert(EdgePtr(newEdge(2,3)));
g4.insert(EdgePtr(newEdge(3,0)));
g4.insert(EdgePtr(newEdge(2,4)));
g4.insert(EdgePtr(newEdge(4,2)));
g4.isSC()?std::cout<<“Yes\n”:
std::cout<<“No\n”;
return0;
}
Thisgivesthefollowingoutputtodeterminetheconnectedstatusofeachgraph:
4.4Kruskal’sAlgorithm
Nowlet’slookatasimpleC++implementationofKruskal’salgorithmwhichisusedforfindingminimalspanningtreesinnetworks.ThisalgorithmemploysthesameGraphstructuretomodelanetwork,insertanddeleteedges,andsoforth.
Considerthefollowing11-nodeexamplenetworkshowingthenetworknodesandtheweightvaluesofthelinksconnectingthem:
Thisisbuiltusingmultiplecallstoinsertasseenhere:
graph.insert(EdgePtr(newEdge(0,1,1.0)));
Kruskal’salgorithmworksbyobtainingasortedlistofthenetworklinksandthenaddingeachleast-costlinktoanothersetwhiledisallowingcycles.Thecompletecodeisasfollows:
Graph.h
#include<vector>
#include<algorithm>
#include<memory>
//Thiscodeisfrom“AlgorithmsinC++,ThirdEdition,”
//byRobertSedgewick,Addison-Wesley,2002.
classEdge
{
public:
intv,w;
doublewt;
Edge(intv=-1,intw=-1,doublewt=0.0):
v(v),w(w),wt(wt){}
};
typedefstd::shared_ptr<Edge>EdgePtr;
structEdgeSort
{
booloperator()(constEdgePtr&ep1,constEdgePtr&ep2)
{
returnep1->wt<ep2->wt;
}
};
template<classEdge>
classDenseGRAPH
{
private:
intVcnt,Ecnt;
booldigraph;
std::vector<std::vector<EdgePtr>>adj;
public:
DenseGRAPH(intV,booldigraph=false):
adj(V),
Vcnt(V),
Ecnt(0),
digraph(digraph)
{
for(inti=0;i<V;i++)adj[i].resize(V);
}
intV()const{returnVcnt;}
intE()const{returnEcnt;}
booldirected()const{returndigraph;}
voidinsert(EdgePtre)
{
intv=e->v;
intw=e->w;
if(adj[v][w]==0)Ecnt++;
adj[v][w]=e;
if(!digraph)adj[w][v]=e;
}
voidremove(EdgePtre)
{
intv=e->v;
intw=e->w;
if(adj[v][w]!=0)
Ecnt—;
adj[v][w]=0;
if(!digraph)adj[w][v]=0;
}
EdgePtredge(intv,intw)const
{returnadj[v][w];}
//Gettheweightededges
voidedges(std::vector<EdgePtr>&edges)
{
for(intu=0;u<V();++u)
{
DenseGRAPH<Edge>::adjIteratorA(*this,u);
for(EdgePtre=A.beg();!A.end();e=A.nxt())
{
if(NULL!=e)
{
edges.push_back(e);
}
}
}
}
classadjIterator;
friendclassadjIterator;
};
template<classEdge>
classDenseGRAPH<Edge>::adjIterator
{
private:
constDenseGRAPH&G;
inti,v;
public:
adjIterator(constDenseGRAPH<Edge>&G,intv):
G(G),
v(v),
i(0){}
EdgePtrbeg(){i=-1;returnnxt();}
EdgePtrnxt()
{
for(i++;i<G.V();i++)
if(G.edge(v,i))
returnG.adj[v][i];
return0;
}
boolend()const{returni>=G.V();}
};
Main.cpp
#include“Graph.h”
#include<iostream>
//Astructuretorepresentasubsetforunion-find
structsubset
{
intparent;
intrank;
};
typedefsubsetSubset;
//Autilityfunctiontofindsetofanelementi
//(usespathcompressiontechnique)
intFind(Subsetsubsets[],inti)
{
//findrootandmakerootasparentofi(pathcompression)
if(subsets[i].parent!=i)
subsets[i].parent=Find(subsets,subsets[i].parent);
returnsubsets[i].parent;
}
//Afunctionthatdoesunionoftwosetsofxandybyrank
voidUnion(Subsetsubsets[],intx,inty)
{
intxroot=Find(subsets,x);
intyroot=Find(subsets,y);
//Attachsmallerranktreeunderrootofhighranktree
//(UnionbyRank)
if(subsets[xroot].rank<subsets[yroot].rank)
{
subsets[xroot].parent=yroot;
}
elseif(subsets[xroot].rank>subsets[yroot].rank)
{
subsets[yroot].parent=xroot;
}
else
{
//Ifranksaresame,thenmakeoneasrootandincrement
//itsrankbyone
subsets[yroot].parent=xroot;
subsets[xroot].rank++;
}
}
//ThemainfunctiontoconstructMSTusingKruskal’salgorithm
voidKruskalMST(DenseGRAPH<Edge>*graph,std::vector<EdgePtr>&mst)
{
constintV=graph->V();
//Sortedgesinnon-decreasingorderoftheirweight
std::vector<EdgePtr>edges;
graph->edges(edges);
std::sort(edges.begin(),edges.end(),EdgeSort());
//AllocatememoryforcreatingVssubsets
std::unique_ptr<subset[]>subsets(newsubset[V]());
//CreateVsubsetswithsingleelements
for(intv=0;v<V;++v)
{
subsets[v].parent=v;
subsets[v].rank=0;
}
for(std::vector<EdgePtr>::iteratorit=edges.begin();
it!=edges.end();++it)
{
EdgePtredgePtr=*it;
intx=Find(subsets.get(),edgePtr->v);
inty=Find(subsets.get(),edgePtr->w);
//Ifincludingthisedgedoes’tcausecycle,includeit
//inresultandincrementtheindexofresultfornextedge
if(x!=y)
{
mst.push_back(edgePtr);
Union(subsets.get(),x,y);
}
}
}
intmain()
{
DenseGRAPH<Edge>*graph=newDenseGRAPH<Edge>(11,true);
graph->insert(EdgePtr(newEdge(0,1,1.0)));
graph->insert(EdgePtr(newEdge(0,2,0.6)));
graph->insert(EdgePtr(newEdge(0,3,1.0)));
graph->insert(EdgePtr(newEdge(0,4,1.0)));
graph->insert(EdgePtr(newEdge(0,5,1.1)));
graph->insert(EdgePtr(newEdge(1,4,1.8)));
graph->insert(EdgePtr(newEdge(1,10,1.9)));
graph->insert(EdgePtr(newEdge(2,3,2.1)));
graph->insert(EdgePtr(newEdge(2,5,2.3)));
graph->insert(EdgePtr(newEdge(2,6,1.7)));
graph->insert(EdgePtr(newEdge(3,4,1.7)));
graph->insert(EdgePtr(newEdge(5,6,0.5)));
graph->insert(EdgePtr(newEdge(5,7,0.7)));
graph->insert(EdgePtr(newEdge(5,8,0.9)));
graph->insert(EdgePtr(newEdge(5,9,0.9)));
graph->insert(EdgePtr(newEdge(5,10,1.0)));
graph->insert(EdgePtr(newEdge(6,7,1.9)));
graph->insert(EdgePtr(newEdge(7,8,1.7)));
graph->insert(EdgePtr(newEdge(8,9,1.6)));
graph->insert(EdgePtr(newEdge(9,10,2.2)));
std::vector<EdgePtr>mst;
KruskalMST(graph,mst);
for(std::vector<EdgePtr>::const_iteratorit=mst.begin();
it!=mst.end();++it)
{
std::cout<<it->get()->v<<”“
<<it->get()->w<<”“
<<it->get()->wt<<std::endl;
}
return0;
}
Thisgivesthefollowingoutput:
Whichinturnrepresentsthisminimalspanningtree:
ChapterSummary
Tosummarizethismasterlesson,youcannow:
UtilizeaGraphclasstomodelnetworksasasetofnodesandinterconnectingedges
UseGraphclassmethodstoinsert,removeandkeeptrackoftheedgesandnodesinyournetwork.
Applyusefulproblem-solvingalgorithmsfordeterminingshortestpaths,connectivityandminimalspanningtrees.
Chapter5:ASodukuPuzzleSolver
Sudokuisalogicpuzzleinwhichyouaregivena9×9gridofnumbersintherange1to9.Thatgridisthenfurthersub-dividedinto9lotsof3×3sectors.
Therulesaresimple:ineachrow,column,and3x3sector,eachoneofthenumbers1-9mustappear.Atthestartofthepuzzle,justenoughofthegrid’selementsareallocatedtoguaranteeasingleuniquesolutionbasedontheserules.ThischapterpresentsaC++GridclassforbothgeneratingandsolvingaSodukupuzzlegrid.
Attheheartoftheclassisarecursivebacktrackingalgorithmusedtoactuallysolvethepuzzle.EachrecursivecalltotheSolvefunctionsolvesthepuzzleonestepatatimebyassigningafeasiblenumbertoeachemptycell.Beforethenumberisassigneditisfirstcheckedforfeasibility:wecheckifthenumberisnotalreadypresentinthecurrentrow,inthecurrentcolumnandinthecurrent3x3grid.Ifitissafetodoso,thenumberisassignedandwerecursivelychecktoseeifthislatestassignmenthassolvedthesolutionornot.Ifasolutionisstillnotfound,wetrythenextnumberforthecurrentemptySodukucell.Ifnoneofthenumbers1-9wehavetriedaresuccessfulthenwereturnafalse.
#include<vector>
#include<set>
#include<algorithm>
#include<ctime>
#include<iostream>
constintUNASSIGNED=0;
constintN=9;
classGrid
{
private:
std::vector<unsigned>grid;
std::set<unsigned>locations;
unsignedsizeSquared;
public:
Grid()
{
sizeSquared=N*N;
grid.resize(sizeSquared);
}
voidCopy(unsignedmatrix[N][N])
{
for(introw=0;row<N;++row){
for(intcol=0;col<N;++col){
grid[row+col*N]=matrix[row][col];
}
}
}
voidPrint()const
{
for(introw=0;row<N;row++){
for(intcol=0;col<N;col++)
std::cout<<grid[row+col*N]<<”“;
std::cout<<std::endl;
}
std::cout<<std::endl;
}
boolSolve()
{
introw,col;
if(!HasUnassignedLocations(row,col))returntrue;
//considerdigits1to9
for(intnum=1;num<=9;num++){
if(IsFeasible(row,col,num)){
grid[row+col*N]=num;
if(Solve())
{
returntrue;
}
//Tryagain
grid[row+col*N]=UNASSIGNED;
}
}
returnfalse;
}
private:
boolHasUnassignedLocations(int&row,int&col)
{
for(row=0;row<N;row++)
for(col=0;col<N;col++)
if(grid[row+col*N]==UNASSIGNED)
returntrue;
returnfalse;
}
boolIsFeasible(introw,intcol,intnum)
{
//Checkifnumbernotalreadyplacedincurrentrow,col,3x3box
return!InRow(row,num)&&
!InColumn(col,num)&&
!In3x3Box(row-row%3,col-col%3,num);
}
boolInRow(introw,intnum)
{
for(intcol=0;col<N;col++)
if(grid[row+col*N]==num)
returntrue;
returnfalse;
}
boolInColumn(intcol,intnum)
{
for(introw=0;row<N;row++)
if(grid[row+col*N]==num)
returntrue;
returnfalse;
}
boolIn3x3Box(intboxStartRow,intboxStartCol,intnum)
{
for(introw=0;row<3;row++)
for(intcol=0;col<3;col++)
{
intrown=row+boxStartRow;
intcoln=col+boxStartCol;
if(grid[rown+coln*N]==num)
returntrue;
}
returnfalse;
}
};
intmain()
{
Gridgr;
unsignedgrid[N][N]={{0,7,4,3,0,2,0,0,0},
{0,0,0,0,0,5,0,4,0},
{0,0,0,6,0,7,9,0,0},
{0,5,6,0,0,0,7,9,0},
{3,0,0,0,0,0,0,0,5},
{0,2,7,0,0,0,6,8,0},
{0,0,5,7,0,1,0,0,0},
{0,1,0,2,0,0,0,0,0},
{0,0,0,4,0,8,1,6,0}};
gr.Copy(grid);
std::cout<<“InitialPuzzle:”<<std::endl<<std::endl;
gr.Print();
if(true==gr.Solve())
{
std::cout<<“SolvedPuzzle:”<<std::endl<<std::endl;
gr.Print();
}
else
{
std::cout<<“Nosolutionexists”;
}
return0;
}
NowhereistheconsoleoutputfortheC++implementationoftheSudokupuzzlesolver.Itprintstheinitialgridandthecompletelyfilledgridasoutputs:
Inthisshortmasterlesson,youhavelearnedtheintricaciesofSudokupuzzleprogrammingasapracticalapplicationoftheGridclassinC++.
Chapter6:AMathematicalExpressionParser
Whenimplementingyourowncalculatoritisnecessarytobeabletotransformmathematicalexpressionsintoaformatthatissuitableforevaluationbycomputers.Expressionslike(1+8)–((3*4)/2)(knownas“infixnotation“)appearsimpleandintuitivetohumans,butarelessstraightforwardtoimplementinaprogramminglanguage.
Theshunting-yardalgorithmisamethodforparsingmathematicalexpressionswrittenininfixnotationintoaformatknownasReversePolishNotation(RPN).RPNnotationisdifferenttoinfixnotationinthateveryoperator(+,-,*etc)comesaftertheoperands(numbers)andtherearenoparentheses(brackets).Thisstructureismoresuitedtoevaluationbycomputers.
Theinfixexpression3*4forexamplebecomes34*.
Pseudocodefortheshunting-yardalgorithmtoconvertaninfixexpressionintoReversePolishNotation:
While(tokensexist)
Readtoken.
If(tokenisanumber}
addtokentooutputqueue
If(tokenisafunction)
pushtokenontostack
If(tokenisafunctionargumentseparatoregacomma)
While(tokenatthetopofthestackisNOTaleftparenthesis)
popoperatorsoffstackontooutputqueue.
If(noleftparentheseswereencountered)
Error-invalidinput
If(tokenisanoperator,o1)
While(operatortoken,o2,existsatthetopofthestackAND(o1isleft-associativeanditsprecedence<=o2ORo1isrightassociativeanditsprecedence<o2)
popo2offthestackontotheoutputqueue
pusho1ontothestack
If(tokenisaleftparenthesis)
pushtokenontothestack.
If(tokenisrightparenthesis)
While(tokenatthetopofthestackisNOTaleftparenthesis)
popoperatorsoffthestackontotheoutputqueue
Popleftparenthesisfromthestack,butnotontooutputqueue.
If(tokenatthetopofthestackisafunctiontoken)
poptokenontotheoutputqueue.
If(stackhasrunoutwithoutfindingaleftparenthesis)
Error-mismatchedparentheses.
While(operatortokensremaininthestack)
If(operatortokenonthetopofthestackisaparenthesis)
Error-mismatchedparentheses.
Poptheoperatorontotheoutputqueue.
WhentheinputexpressionbecomesavailableinReversePolishNotation,itispossibletoemployanotherstack-basedalgorithmtoevaluatetheRPNexpressionandhencethearithmeticresult.
Thepseudocodeisasfollows:
Foreachtoken
If(tokenisanumber)
Pushvalueontostack
If(tokenisanoperator)
Popthe2xtopvaluesfromthestack
Evaluatetheoperatorusingpoppedvaluesararguments
Pushresultontothestack
Thesinglevalueremainingonthestackistheevaluation.
Fullcodeimplementationasfollows:
ExpressionParser.h
#include<algorithm>
#include<string>
#include<vector>
#include<list>
#include<iostream>
#include<fstream>
#include<iterator>
#include<queue>
#include<stack>
#include<sstream>
#include<locale>
#include<stdlib.h>
#include<math.h>
classExpressionParser
{
public:
ExpressionParser(conststd::string&input);
boolMatchingParetheses();
boolEvaluate(conststd::vector<std::string>&rpn,std::string&result);
boolInfixToRPN(std::vector<std::string>&inputs);
private:
voidReplaceAll(std::string&str,
conststd::string&from,
conststd::string&to);
voidTokenize(std::list<std::string>&tokens,
conststd::string&delimiter);
private:
std::stringm_strInput;
};
ExpressionParser.cpp
#include“ExpressionParser.h”
conststd::stringcharSet[]={“(“,“)”,“%”,“+”,“-“,“*”,“/”,“^”,“,”};
constdoublepi=3.1415927;
constdoublee=2.71828182846;
template<classBidirIt>
BidirItPrev(BidirItit,
typenamestd::iterator_traits<BidirIt>::difference_typen=1)
{
std::advance(it,-n);
returnit;
}
intModulo(intnum,intdiv)
{
intmod=num%div;
return(num>=0||mod==0)?mod:div+mod;
}
unsignedintOpArgCount(conststd::string&s)
{
unsignedintval=1;
if(s==“*”||s==“/”||s==“%”||
s==“+”||s==“-”||s==“=”||
s==“^”||s==“POW”)
{
val=2;
}
elseif(s==“!”)
{
val=1;
}
returnval;
}
//Returnoperatorprecedence
intOpPrecedence(conststd::string&s)
{
intprecedence=1;
if(s==“!”)
precedence=4;
elseif(s==“*”||s==“/”||s==“%”)
precedence=3;
elseif(s==“+”||s==“-”)
precedence=2;
elseif(s==“=”)
precedence=1;
returnprecedence;
}
//Returntrueifleftassociative;falseotherwise
boolOpLeftAssoc(conststd::string&s)
{
boolopLeftAssoc=false;
if(s==“*”||s==“/”||
s==“%”||s==“+”||s==“-”)
{
opLeftAssoc=true;
}
elseif(s==“=”||s==“!”)
{
opLeftAssoc=false;
}
returnopLeftAssoc;
}
//Istokenanoperator
boolIsOperator(conststd::string&s)
{
returns==“+”||s==“-”||
s==“/”||s==“*”||
s==“!”||s==“%”||
s==“=”;
}
//Istokenafunctionargumentseparatoregcomma
boolIsComma(conststd::string&s)
{
returns==“,”;
}
//Convertstringintoalluppercase
std::stringUpperCase(std::stringinput)
{
for(std::string::iteratorit=input.begin();
it!=input.end();
++it)
*it=toupper(*it);
returninput;
}
//IstokenPI
boolIsPi(conststd::string&s)
{
if(UpperCase(s)==“PI”)
returntrue;
returnfalse;
}
//IstokenEuler’sconstant
boolIsE(conststd::string&s)
{
if(UpperCase(s)==“E”)
{
returntrue;
}
returnfalse;
}
//Isthetokenafunction
boolIsFunction(conststd::string&s)
{
std::stringstr=UpperCase(s);
boolisFunction=false;
if(str.find(“^”)!=std::string::npos||
str.find(“SIN”)!=std::string::npos||
str.find(“COS”)!=std::string::npos||
str.find(“TAN”)!=std::string::npos||
str.find(“LN”)!=std::string::npos||
str.find(“LOG”)!=std::string::npos||
str.find(“EXP”)!=std::string::npos||
str.find(“POW”)!=std::string::npos||
str.find(“SQRT”)!=std::string::npos)
{
isFunction=true;
}
returnisFunction;
}
//Isthenumberafloat
boolIsFloat(conststd::string&s)
{
std::istringstreamiss(s);
floatf;
iss>>std::noskipws>>f;
returniss.eof()&&!iss.fail();
}
//Isthestringanumber
boolIsNumber(conststd::string&s)
{
std::string::const_iteratorit=s.begin();
while(it!=s.end()&&std::isdigit(*it,std::locale()))
{
++it;
}
return!s.empty()&&it==s.end();
}
ExpressionParser::ExpressionParser(conststd::string&input)
{
m_strInput=input;
}
//Determineifmatchingnumberofleftandrightparentheses
boolExpressionParser::MatchingParetheses()
{
constsize_tnLeft=std::count(m_strInput.begin(),
m_strInput.end(),‘(‘);
constsize_tnRight=std::count(m_strInput.begin(),
m_strInput.end(),‘)’);
returnnLeft==nRight&&!m_strInput.empty();
}
//Splitselectedtextintodelimitedvectorarrayofstrings
voidExpressionParser::Tokenize(std::list<std::string>&tokens,
conststd::string&delimiter)
{
//Insertwhitepacesbeforeandaftereachspecialcharacters
size_tsize=sizeof(charSet)/sizeof(std::string);
for(inti=0;i<static_cast<int>(size);i++){
std::strings=charSet[i];
ReplaceAll(m_strInput,s,””+s+””);
}
size_tnext_pos=0;
size_tinit_pos=m_strInput.find_first_not_of(delimiter,next_pos);
while(next_pos!=std::string::npos&&
init_pos!=std::string::npos){
next_pos=m_strInput.find(delimiter,init_pos);
std::stringtoken=m_strInput.substr(init_pos,next_pos-init_pos);
tokens.push_back(token);
init_pos=m_strInput.find_first_not_of(delimiter,next_pos);
}
std::stringfirstToken=tokens.front();
if(firstToken==“-”){
std::list<std::string>::iteratorit=tokens.begin();
it++;
if(it==tokens.end())return;
std::stringnextToken=*(it);
if(IsNumber(nextToken)||IsFloat(nextToken)){
tokens.pop_front();
tokens.front()=firstToken+nextToken;
}
elseif(nextToken==“(”||IsFunction(nextToken)){
tokens.front()=firstToken+“1”;
tokens.insert(it,“*”);
}
//minusminusisaplus
elseif(nextToken==“-”&&firstToken==“-”){
std::list<std::string>::iteratornit=it;
std::advance(nit,-1);
tokens.erase(it);
tokens.erase(nit);
}
}
//Dealwithminussignafteropeningparenthesisoroperator
typedefstd::list<std::string>::iteratort_iter;
std::stringprevToken=””;
for(t_iterit=tokens.begin();it!=Prev(tokens.end());it++){
std::stringtoken=*it;
std::list<std::string>::iteratornit=it;
std::advance(nit,1);
if(nit==tokens.end())break;
std::stringntoken=*nit;
if(token==“-”&&prevToken==“(”){
if(IsNumber(ntoken)||IsFloat(ntoken)){
tokens.erase(nit);
*it=“-”+ntoken;
token=*it;
}
}
elseif(token==“-”&&
(IsOperator(prevToken)||prevToken==“^”||prevToken==“%”)){
//Minusminusbecomesaplus
if(token==“-”&&prevToken==“-”){
std::list<std::string>::iteratornit=it;
std::list<std::string>::iteratornnit=nit;
nnit++;
std::advance(nit,-1);
tokens.erase(it);
*nit=“+”;
std::list<std::string>::iteratorpnit=nit;
std::advance(pnit,-1);
if(IsOperator(*pnit)||*pnit==“(”)
tokens.erase(nit);
token=*nnit;it=nnit;
if(it==Prev(tokens.end()))break;
}
elseif(IsNumber(ntoken)||
IsFloat(ntoken)||
IsFunction(ntoken)){
boolexit=false;
if(nit==Prev(tokens.end()))exit=true;
tokens.erase(nit);
*it=“-”+ntoken;
token=*it;
if(exit)break;
}
elseif(ntoken==“(”){
*it=“-1”;
token=*it;
tokens.insert(nit,“*”);
}
}
prevToken=token;
}
//Dealwithminussignbeforeopeningparenthesis
prevToken=””;t_iterprevIt;
for(t_iterit=tokens.begin();it!=tokens.end();it++){
std::stringtoken=*it;
if(token==“(”&&prevToken==“-”){
tokens.insert(it,“1”);
tokens.insert(it,“*”);
}
prevToken=token;
prevIt=it;
}
}
//Replaceallinstancesofselectedstringwithreplacementstring
voidExpressionParser::ReplaceAll(std::string&str,conststd::string&from,
conststd::string&to)
{
size_tstart_pos=0;
while((start_pos=str.find(from,start_pos))!=std::string::npos)
{
str.replace(start_pos,from.length(),to);
start_pos+=to.length();
}
}
//DeducethenumericalresultfromtheRPNstringpassedtoit
boolExpressionParser::Evaluate(
conststd::vector<std::string>&rpn,
std::string&result)
{
typedefstd::vector<std::string>::const_iteratorrpn_iter;
std::stack<std::string>stack;
for(rpn_iterit=rpn.begin();it!=rpn.end();it++)
{
std::stringtoken=*it;
if(IsNumber(token)||IsFloat(token)||
IsPi(token)||IsE(token))
{
if(IsPi(token))
{
std::stringstreams;
s<<pi;
token=s.str();
}
elseif(IsE(token))
{
std::stringstreams;
s<<e;
token=s.str();
}
stack.push(token);
}
elseif(IsOperator(token)||IsFunction(token))
{
doubleresult=0.0;
unsignedintnargs=OpArgCount(UpperCase(token));
boolisUnary=false;
unsignedintstackArgs=stack.size();
std::vector<double>args;
if(stackArgs<nargs)
{
//Fordealingwithunary‘-‘orunary‘+’
if(stackArgs==1&&
nargs==2&&(token==“+”||token==“-”))
{
std::stringvalue=stack.top();
result=strtod(value.c_str(),NULL);
stack.pop();isUnary=true;
}
else{//(Error)Insufficientvaluesintheexpression.
returnfalse;
}
}
else
{
//Else,Popthetopnvaluesfromthestack.
while(nargs>0)
{
std::stringvalue=stack.top();
doubled=strtod(value.c_str(),NULL);
args.push_back(d);
stack.pop();nargs—;
}
}
//Evaluatetheoperator,withthevaluesasarguments.
if(IsOperator(token)&&!isUnary)
{
doubled2=args[0];
doubled1=args[1];
if(token==“+”){
result=d1+d2;
}
elseif(token==“-”){
result=d1-d2;
}
elseif(token==“*”){
result=d1*d2;
}
elseif(token==“/”){
result=d1/d2;
}
elseif(token==“%”){
inti2=(int)args[0];
inti1=(int)args[1];
doubleiresult=Modulo(i1,i2);
result=iresult;
}
}
elseif(IsFunction(token))
{
doubled0=args[0];
std::stringcapToken=UpperCase(token);
doublemult=token.find(“-”)!=std::string::npos?-1:1;
if(capToken.find(“SIN”)!=std::string::npos){
result=sin(d0);
}
elseif(capToken.find(“COS”)!=std::string::npos){
result=cos(d0);
}
elseif(capToken.find(“TAN”)!=std::string::npos){
result=tan(d0);
}
elseif(capToken.find(“LN”)!=std::string::npos){
result=log(d0);
}
elseif(capToken.find(“LOG”)!=std::string::npos){
result=log10(d0);
}
elseif(capToken.find(“EXP”)!=std::string::npos){
result=exp(d0);
}
elseif(capToken.find(“^”)!=std::string::npos){
doubled2=args[0];doubled1=args[1];
if(d1<0)mult=-1.0;
result=pow((double)d1,d2);
}
elseif(capToken.find(“POW”)!=std::string::npos){
doubled2=args[0];doubled1=args[1];
result=pow(d1,d2);
}
elseif(capToken.find(“SQRT”)!=std::string::npos){
result=sqrt(d0);
}
result*=mult;
}
//Pushthereturnedresults,ifany,backontothestack
if(result==(long)result)
{
result=long(result);
}
std::stringstreams;s<<result;
stack.push(s.str());
}
}
if(stack.size()==1)
{
result=stack.top();
doubleres=strtod(result.c_str(),NULL);
if(res==(long)res)
{
longlres=(long)res;
std::stringstreams;
s<<lres;
result=s.str();
}
returntrue;
}
returnfalse;//(Error)Theuserinputhastoomanyvalues
}
//ConvertinfixexpressionformatintoreversePolishnotation
boolExpressionParser::InfixToRPN(std::vector<std::string>&inputs)
{
typedefstd::list<std::string>::const_iteratortok_iter;
std::list<std::string>infix;
std::stack<std::string>stack;
std::queue<std::string>outputQueue;
Tokenize(infix,””);
boolsuccess=true;
for(tok_iterit=infix.begin();it!=infix.end();it++)
{
std::stringtoken=*it;
if(IsNumber(token)||
IsFloat(token)||
IsPi(token)||
IsE(token))
{
outputQueue.push(token);
}
elseif(IsFunction(token))
{
stack.push(token);
}
elseif(IsComma(token))
{
std::stringstackToken=stack.top();
while(stackToken!=“(”)
{
outputQueue.push(stackToken);
stack.pop();stackToken=stack.top();
}
if(stackToken==“(”)
{
success=true;
}
else
{
success=false;
}
}
elseif(IsOperator(token))
{
while(!stack.empty()&&
IsOperator(stack.top())&&
((OpLeftAssoc(token)&&OpPrecedence(token)==OpPrecedence(stack.top()))||
(OpPrecedence(token)<OpPrecedence(stack.top())))){
std::stringstackToken=stack.top();
stack.pop();outputQueue.push(stackToken);
}
stack.push(token);
}
elseif(token==“(”)
{
stack.push(token);
}
elseif(token==“)”)
{
while(!stack.empty()&&stack.top()!=“(”)
{
outputQueue.push(stack.top());
stack.pop();
}
if(!stack.empty())
{
std::stringstackToken=stack.top();
if(stackToken!=“(”)
success=false;
}
else
{
returnfalse;
}
stack.pop();
if(!stack.empty())
{
std::stringstackToken=stack.top();
if(IsFunction(stackToken))
{
outputQueue.push(stackToken);
stack.pop();
}
}
}
}
while(!stack.empty())
{
outputQueue.push(stack.top());
stack.pop();
}
while(!outputQueue.empty()){
std::stringtoken=outputQueue.front();
inputs.push_back(token);
outputQueue.pop();
}
returnsuccess;
}
main.cpp
#include“ExpressionParser.h”
#include<assert.h>
#include<sstream>
//Printiteratorsinagenericway
template<typenameT,typenameInputIterator>
voidPrint(conststd::string&message,
constInputIterator&itbegin,
constInputIterator&itend,
conststd::string&delimiter)
{
std::cout<<message<<std::endl;
std::copy(itbegin,itend,
std::ostream_iterator<T>(std::cout,delimiter.c_str()));
std::cout<<std::endl;
}
std::pair<std::string,double>tests[]=
{
std::make_pair<std::string,double>(“((2*(6-1))/2)*4”,20.0),
std::make_pair<std::string,double>(“-11^-7”,5.13158e-08),
std::make_pair<std::string,double>(“-1*-sin(Pi/-2)”,-1.0),
std::make_pair<std::string,double>(“-8+5”,-3.0),
std::make_pair<std::string,double>(“2—2”,4.0),
std::make_pair<std::string,double>(“34.5*(23+1.5)/2”,422.625),
std::make_pair<std::string,double>(“3/2+4*(12+3)”,61.5),
std::make_pair<std::string,double>(“pow(2,3)”,8),
std::make_pair<std::string,double>(“((2*(6-1))/2)*4”,20),
std::make_pair<std::string,double>(“ln(2)+3^5”,243.693147181),
std::make_pair<std::string,double>(“-8+3”,-5),
std::make_pair<std::string,double>(“2.5^3”,15.625),
std::make_pair<std::string,double>(“SQRT(4)”,2),
std::make_pair<std::string,double>(“5+(-1+2)”,6),
std::make_pair<std::string,double>(“-(2+3)*(4*-10-1)+100”,305),
std::make_pair<std::string,double>(“(2+3)*-(4*10-1)+100”,-95),
std::make_pair<std::string,double>(“1-(-2^2)-1”,4),
std::make_pair<std::string,double>(“1-(—2^2)—1”,-2),
std::make_pair<std::string,double>(“(4*-10-1)”,-41),
std::make_pair<std::string,double>(“-(2+3)*(4*-10-1)”,205),
std::make_pair<std::string,double>(“-3/2+4*-(12+3)”,-61.5),
std::make_pair<std::string,double>(“-3/2+4*(-12-3)”,-61.5),
std::make_pair<std::string,double>(“-3/2+-4*(-12-3)”,58.5),
std::make_pair<std::string,double>(“1—cos(PI)”,0.0),
};
doublestring_to_double(conststd::string&s)
{
std::istringstreami(s);
doublex;
if(!(i>>x))
return0;
returnx;
}
intmain(intargc,char**argv)
{
std::stringresult;
size_ttest_size=sizeof(tests)/sizeof(tests[0]);
for(inti=0;i<test_size;++i)
{
std::stringoriginalInput=tests[i].first;
doubleexpected_result=tests[i].second;
Print<char,std::string::iterator>(
“Inputexpression:”,
originalInput.begin(),
originalInput.end(),””);
ExpressionParserparser(originalInput);
if(!parser.MatchingParetheses())
{
std::cout<<“Error:mismatchedparenthesesoremptyinput”
<<std::endl;
return1;
}
std::vector<std::string>RPN;
if(parser.InfixToRPN(RPN))
{
Print<std::string,std::vector<std::string>::const_iterator>(
“RPNtokens:“,RPN.begin(),
RPN.end(),
””);
std::stringstr_result;
if(parser.Evaluate(RPN,str_result))
{
std::cout<<“Result=”<<str_result<<std::endl;
std::cout<<std::endl;
doubleresult=string_to_double(str_result);
//Compareexpectedvsactualtowithinaprecision:1
assert(fabs(result-expected_result)<0.001);
}
}
else
{
std::cout<<“Error:mismatchedparentheses”<<std::endl;
}
}
return0;
}
Theresultofrunningthrougheachoftheexampletestinputsandthencomparingtheirexpectedoutputwiththeactualoutputishere:
ChapterSummary
Asaresultofcompletingthislesson,younowhave:
Aworkingexampleofamathematicalexpressionparserbasedontheshunting-yardalgorithm.
Abetterunderstandingofhowtoconvertpseudocode(codewrittenforhumanunderstanding,notacomputer)intoworkingC++code.
Chapter7:AGeneticAlgorithmFunctionOptimizer
Anexampleispresentedhereonhowanalgorithmemployingstandardgeneticoperatorscrossover,mutationandselectioncanbeappliedtooptimizeastandardmathematicalfunction,suchastheRosenbrockfunction:
TheRosenbrockfunctionisanon-convexfunctionusedtotesttheefficiencyandeffectivenessofoptimizationalgorithmsintroducedbyHowardH.Rosenbrockin1960.TheRosenbrockfunctionisdefinedby:
Asshowninthediagram,theglobalminimumliesinsidealong,narrow,parabolicshapedflatvalley.Locatingthevalleyvicinityisfairlystraightforward.Convergingtotheglobalminimum,locatedat:
istrickier.Thisapplicationemploysstandardgeneticoperatorscrossover,mutationandselectionasappliedtochromosomerepresentationsoffloating-pointnumbers.Thereareanumberofpossiblewaysofrepresentingrealnumbersusingbinaryarrays.Inthisapplication,theIEEE754standardisonesuchmethod,wherethethefloating-pointnumberisrepresentedin32bits.ExperimentsusingGraycodesarealsopresented.Inshort,Thefirstbitrepresentsthesign(0=plus,1=minus);thefollowing8bitsstorethebiasedexponent(bias=127)andtheremaining23bitsrepresentthefractionalpart(mantissa).Anin-depthdescriptionofhowtheseoperatorsworkisbeyondthescopeofthispostandtherethereisawealthofmaterialalreadyavailable.Thecodesamplesgivenhereshouldbereasonablyself-explanatory.
The32-bitbinaryencodingsareimplementedusingtheChromosomeclassasameansofhousingthebinaryvaluesandperformingothersimpleget/settasksamdsoforth.ThePopulationclassmaintainsapopulationofChromosomeobjectsandappliesthestandardgeneticoperatorscrossover,mutationandselectiononthem.Italsogeneratesaninitialarbitrarypopulationofchromosomeswithrandomvaluesandhasotherhelperfunctionswithwhichtoconvertbetweenbinaryanddecimalrepresentationsofthebinarystrings:
TheGeneticAlgorithmclassrunsthegeneticalgorithmoperatorsandappliesthemtothepopulationofchromosomeswiththeintentionofprogressivelyimprovingtheoverallsolutionfitness.
RunningtheGeneticAlgorithmusingIEEE754encodings
Ihaverunanumberofsimpleexperimentstoseehowwellthealgorithmconvergesover10,000iterations.Forthefirstexperiment(usingtheIEEE754bitstringrepresentationforfloating-pointnumbers)thealgorithmparametersusedwereasfollows:
encoding_type=1;//IEEE754
crossover_rate=50;
mutation_rate=5;
population_size=100;
tournament_size=population_size/4;
Iusetournamentselectionasthepreferred‘survivalofthefittest’mechanism.Thecode
hasasimpleloggerenablingyoutodumpresultstoatextfile.InthisexampleIenableditreturnthebestfitnessfoundsofarateverygeneration.Agraphshowingthebestvaluefoundatevery10thiteration:
Theglobalminimumforthisparticularfunctionis0.0andinthisrunthealgorithmgotitdownto0.02.Asyoucansee,nofurtherreductiontakesplaceforthenext800orsoiterations.
Feelfreetotryoutthiscodeandimproveuponit,orapplyittoyourownareaofinterest.
RunningtheGeneticAlgorithmusingGraycodes
Asafollowuptothisexperiment,Ihaveincludedanadditionalmeansofinterpretingthebinarychromosomespresentedtothegeneticalgorithm.ForthenextexperimentIusedaGrayencoding,wherebytwosuccessivevaluesdifferbyonlyonebit.Fora32-bitGraycode,Iusedthefirstbitasthesignbit,sothata‘0’correspondstoaplusanda‘1’correspondstoaminus.
Fortheremainingbits,Iextractthedecimalvalueandthendividethisby2^n–1togettherealnumberintheinterval0to1.
AfterdoingasomeexperimentswithparametervaluesIfoundthatanicecombinationseemedtobe:
encoding_type=0;//Graycode
crossover_rate=70;
mutation_rate=5;
population_size=100;
tournament_size=population_size/4;
MyperceptionisthatthealgorithmperformanceusingGraycodesseemedtoconvergesomewhatmoreeasilythanthoseofIEEE754.
However,thisobservationisonlyonthebasisofthefewsimpleexperimentsIhavedonesofar.Thealgorithminthiscasemanagedtogetthevalueoff(x,y)downto0.00538112(x=0.932924,y=0.873316)withinthesamenumberofiterations.Aplotforthisrunisshownbelow.
FullCodeListings
Chromosome.h
#pragmaonce
constintchrSize=64;
classChromosome
{
public:
Chromosome(void);
~Chromosome(void);
voidSetFitness(constdouble&value);
voidSetChromosome(constint&index,constunsignedchar&value);
unsignedcharGetChromosome(constint&index);
doubleGetFitness()const;
intsize()const;
voidPrint(constint&index)const;
private:
unsignedcharchr[chrSize];
doublefitness;
};
GeneticAlgorithm.h
#pragmaonce
#include“Population.h”
#include“Log.h”
classGeneticAlgorithm
{
public:
GeneticAlgorithm(void);
~GeneticAlgorithm(void);
voidInitialize(constint&enc,
constint&crate,
constint&mrate,
constint&psize,
constint&iter,
constint&csize,
constint&tsize,
conststd::string&path);
voidRun();
private:
voidCreatePopulation();
doubleEvaluate();
voidCrossover();
voidMutate();
voidSelect();
voidSetParameters(constint&enc,
constint&crate,
constint&mrate,
constint&psize,
constint&iter,
constint&csize,
constint&tsize);
voidLogResult(constdouble&result,
constint&iter,
constint&count);
private:
intencoding;
intmutationRate;
intcrossoverRate;
intpopulationSize;
intnumberIterations;
intchromosomeSize;
inttournamentSize;
intbestFitnessIndex;
doublebestFitness;
floatbest_x;
floatbest_y;
Populationpop;
Loglog;
};
Log.h
#pragmaonce
#include<fstream>
classLog
{
public:
Log();
Log(char*);
~Log();
voidWrite(char*txt);
voidOpen(constchar*);
private:
std::ofstreamm_stream;
};
Population.h
#pragmaonce
#include<vector>
#include<string>
#include“Chromosome.h”
constdoubleinfinity=9999999999999;
classPopulation
{
public:
enumEncoding{
GRAY=0,
IEEE_754,
};
Population(void);
~Population(void);
voidSetChromosomeEncoding(constint&type);
voidSetChromosomeSize(constint&size);
voidCreateRandomPopulation(constint&size);
voidCrossover(constint&index1,
constint&index2,
constint&point);
voidCrossover(constint&index1,
constint&index2,
constint&point1,
constint&point2);
voidMutation(constint&index);
doubleEvaluatePopulation(float&bx,float&by);
doubleCalcChromosomeFitness(constint&index,
float&xv,
float&yv);
doubleGetChromosomeFitness(constint&index)const;
voidCopyChromosome(constint&source,constint&dest);
private:
Chromosome*CreateRandomChromosome();
std::stringGetXstring(Chromosome*chr);
std::stringGetYstring(Chromosome*chr);
floatGetFloat32_IEEE754(std::stringBinary);
floatGetFloat32_Gray(std::stringBinary);
intBinary32ToHex(std::stringBinary);
doubleCalculateFitnessFunction(constfloat&x,
constfloat&y);
doubleCalcChromosomeFitness_IEEE754(constint&index,
float&xv,float&yv);
doubleCalcChromosomeFitnessGray(constint&index,
float&xv,float&yv);
std::stringGray2Bin(std::stringgray);
longBin2Dec(std::stringbin);
private:
std::vector<Chromosome*>pop;
intchrSize;
intencoding;
};
Chromosome.cpp
#include“Chromosome.h”
#include<iostream>
//Constructor
Chromosome::Chromosome(void){}
//Destructor
Chromosome::~Chromosome(void){}
//Setchromosomeelement
voidChromosome::SetChromosome(constint&index,constunsignedchar&value)
{
if(index<0||index>=chrSize)return;
chr[index]=value;
}
//Getchromosomeelement
unsignedcharChromosome::GetChromosome(constint&index)
{
unsignedcharelement=chr[index];
returnelement;
}
//Setfitnessofchromosome
voidChromosome::SetFitness(constdouble&value)
{
fitness=value;
}
//Getchromosomefitness
doubleChromosome::GetFitness()const
{
returnfitness;
}
//Getnumberofelementsinthechromosome
intChromosome::size()const
{
returnchrSize;
}
//Outputthechromosomeanditsfitness
voidChromosome::Print(constint&index)const
{
std::stringstr;
for(inti=0;i<chrSize;i++){
unsignedcharvalue=chr[i];
str.append(value==0?“0”:“1”);
}
std::cout<<index<<“\t”<<str.c_str()<<“\t”<<fitness<<std::endl;
}
GeneticAlgorithm.cpp
#include“GeneticAlgorithm.h”
#include<sstream>
#include<stdlib.h>
#include<math.h>
conststd::stringfilepath=“C:\dump\best.txt”;
GeneticAlgorithm::GeneticAlgorithm(void)
{
//Giveitsomedefaultparameters
encoding=0;
mutationRate=5;
crossoverRate=90;
populationSize=100;
numberIterations=10000;
chromosomeSize=64;
tournamentSize=populationSize/5;
bestFitness=infinity;
}
GeneticAlgorithm::~GeneticAlgorithm(void)
{}
//Initializeparameters,generatepopulationetc
voidGeneticAlgorithm::Initialize(constint&enc,
constint&crate,
constint&mrate,
constint&psize,
constint&iter,
constint&csize,
constint&tsize,
conststd::string&path)
{
SetParameters(enc,crate,mrate,psize,iter,csize,tsize);
CreatePopulation();
log.Open(path.c_str());
}
//Runthegeneticalgorithm
voidGeneticAlgorithm::Run()
{
for(inti=0;i<numberIterations;i++){
LogResult(Evaluate(),i,10);
Select();
Crossover();
Mutate();
}
}
//Evaulatefitnessesofpopulationchromosomes
doubleGeneticAlgorithm::Evaluate()
{
floatbx=-1;
floatby=-1;
doublebest=pop.EvaluatePopulation(bx,by);
if(best<bestFitness){
bestFitness=best;
best_x=bx;
best_y=by;
}
returnbestFitness;
}
//Applycrossovertoselectedchromosomepairs
voidGeneticAlgorithm::Crossover()
{
for(inti=0;i<populationSize;i++){
intr=rand()%100;
if(r<crossoverRate){
intindex1=rand()%populationSize;
intindex2=rand()%populationSize;
while(index1==index2){
index2=rand()%populationSize;
}
//Getcrossoverpoints
//Point1:0-31
intpoint1=rand()%chromosomeSize/2;
//Point1:32-64
intpoint2=chromosomeSize/2+
rand()%(chromosomeSize/2);
while(point1==point2){
point2=chromosomeSize/2+
rand()%(chromosomeSize/2);
}
if(point1>point2){
inttmp=point1;
point1=point2;
point2=tmp;
}
//Do1-pointcrossover
pop.Crossover(index1,index2,point1,point2);
}
}
}
//Mutateselectedchromosomes
voidGeneticAlgorithm::Mutate()
{
for(inti=0;i<populationSize;i++){
intr=rand()%100;
if(r<mutationRate){
pop.Mutation(i);
}
}
}
//Selectpopulationchromosomesaccordingtofitness
//Usingapairwisetournamentselectionmechanism
voidGeneticAlgorithm::Select()
{
//Foreachpairofchromosomesselected
inti=0;
while(i<tournamentSize){
//Getthechromosomepairfortournamentselection
intindex1=rand()%populationSize;
intindex2=rand()%populationSize;
while(index1==index2){
index2=rand()%populationSize;
}
doublefitness1=fabs(pop.GetChromosomeFitness(index1));
doublefitness2=fabs(pop.GetChromosomeFitness(index2));
//Weseektofind[x,y]thatminimizesthisfunction
//Thebiggetthevaluereturned,theloweritsfitness
if(fitness1>fitness2){
//Copychromosome1elementsintochromosome2
pop.CopyChromosome(index2,index1);
}
else{
//Copychromosome2elementsintochromosome1
pop.CopyChromosome(index1,index2);
}
i++;
}
}
//Setmutationrate,populationsizeetc
voidGeneticAlgorithm::SetParameters(constint&enc,
constint&crate,
constint&mrate,
constint&psize,
constint&iter,
constint&csize,
constint&tsize)
{
mutationRate=mrate;
crossoverRate=crate;
populationSize=psize;
numberIterations=iter;
chromosomeSize=csize;
tournamentSize=tsize;
pop.SetChromosomeEncoding(enc);
}
//Createinitialrandompopulationofchromosomes
voidGeneticAlgorithm::CreatePopulation()
{
pop.CreateRandomPopulation(populationSize);
}
//Logthevaluetoatextfile
voidGeneticAlgorithm::LogResult(constdouble&result,
constint&iter,
constint&count)
{
if(iter%count==0){
std::stringstreamss;
ss<<iter<<“\t”<<result<<“\t”<<best_x<<“\t”<<best_y;
log.Write((char*)ss.str().c_str());
}
}
Log.cpp
#include“Log.h”
Log::Log()
{}
Log::Log(char*filename)
{
m_stream.open(filename,std::fstream::app);
}
voidLog::Write(char*txt)
{
m_stream<<txt<<std::endl;
}
Log::~Log()
{
m_stream.close();
}
//Openthelogfileforwriting
voidLog::Open(constchar*filename)
{
m_stream.open(filename,std::fstream::app);
}
Population.cpp
#include“Population.h”
#include<bitset>
#include<iostream>
#include<math.h>
#include<stdlib.h>
constintdivisor_32=4294967295;//2^32-1
constintdivisor_31=2147483647;//2^31-1
Population::Population(void)
{
//Setdefaultchromosomesize
chrSize=64;
}
Population::~Population(void)
{
intsize=(int)pop.size();
for(inti=0;i<size;i++){
Chromosome*chr=pop.at(i);
if(chr){
deletechr;
}
}
pop.clear();
}
//Setthetypeofchromosomalencodingused:IEEE754,Grayetc
voidPopulation::SetChromosomeEncoding(constint&type)
{
encoding=type;
}
//Createinitialarbitrarypopulationofchromosomes
voidPopulation::CreateRandomPopulation(constint&size)
{
for(inti=0;i<size;i++){
Chromosome*chr=CreateRandomChromosome();
pop.push_back(chr);
}
}
//Applyone-pointcrossovertoselectedchromosomepair
voidPopulation::Crossover(constint&index1,constint&index2,
constint&point)
{
if(point<0||point>=chrSize)return;
Chromosome*chr1=pop.at(index1);
Chromosome*chr2=pop.at(index2);
for(inti=point;i<chrSize;i++){
unsignedcharv1=chr1->GetChromosome(i);
unsignedcharv2=chr2->GetChromosome(i);
chr1->SetChromosome(index1,v2);
chr2->SetChromosome(index1,v1);
}
}
//Applyone-pointcrossovertoselectedchromosomepair
voidPopulation::Crossover(constint&index1,constint&index2,
constint&point1,constint&point2)
{
if(point1<0||point1>=chrSize)return;
if(point2<0||point2>=chrSize)return;
intp1=point1;
intp2=point2;
if(p1>p2){
inttmp=p1;
p1=p2;
p2=tmp;
}
Chromosome*chr1=pop.at(index1);
Chromosome*chr2=pop.at(index2);
//Docrossoveronxportionofchromosome:
//eitherbeforeorafterpointp1
intstart=0;
intend=p1;
if(rand()%100<50){
start=p1;
end=chrSize/2;
}
for(inti=start;i<end;i++){
unsignedcharv1=chr1->GetChromosome(i);
unsignedcharv2=chr2->GetChromosome(i);
chr1->SetChromosome(i,v2);
chr2->SetChromosome(i,v1);
}
//Docrossoveronxportionofchromosome
//eitherbeforeorafterpointp2
start=0;
end=p2;
if(rand()%100<50){
start=p2;
end=chrSize;
}
for(inti=start;i<end;i++){
chr1->SetChromosome(i,chr2->GetChromosome(i));
chr2->SetChromosome(i,chr1->GetChromosome(i));
}
}
//Applymutationtoselectedchromosome:xpartorypart
voidPopulation::Mutation(constint&index)
{
Chromosome*chr=pop.at(index);
boolxpart=rand()%100<50?true:false;
intstart=xpart?0:chrSize/2;
intend=xpart?chrSize/2:chrSize;
for(inti=start;i<end;i++){
unsignedcharvalue=chr->GetChromosome(i);
value=value==0?1:0;
chr->SetChromosome(i,value);
}
}
//Evaluatethepopulationfitnesses
doublePopulation::EvaluatePopulation(float&bx,float&by)
{
doubletotalFitness=0.0;
//doubleaveFitness=0.0;
doublebestFitness=infinity;
intbestFitnessIndex=0;
for(inti=0;i<(int)pop.size();i++){
floatx,y;
doublefitness=CalcChromosomeFitness(i,x,y);
Chromosome*chr=pop.at(i);
chr->SetFitness(fitness);
//Outputthechromosome(optional-commentoutifdesired)
chr->Print(i);
totalFitness+=fitness;
//Storebestsolution
if(i==0)bestFitness=fitness;
if(fitness<bestFitness){
bestFitness=fitness;
bestFitnessIndex=i;
bx=x;
by=y;
}
}
std::cout<<“\n\n”;
//aveFitness=totalFitness/pop.size();
returnbestFitness;
}
//Createanarbitraryrandomchromosome
Chromosome*Population::CreateRandomChromosome()
{
Chromosome*chr=newChromosome();
//Randomizechromosomeelements
for(inti=0;i<chr->size();i++)
{
unsignedcharvalue=rand()%100<50?0:1;
chr->SetChromosome(i,value);
}
returnchr;
}
//Calculatechromosomefitnessaccordingtochromosomeencodingused
doublePopulation::CalcChromosomeFitness(
constint&index,
float&xv,
float&yv)
{
if(encoding==GRAY)
{
returnCalcChromosomeFitnessGray(index,xv,yv);
}
else
{
returnCalcChromosomeFitness_IEEE754(index,xv,yv);
}
}
//GetfitnessofselectedchromosomeaccordingtoIEEE754coding
doublePopulation::CalcChromosomeFitness_IEEE754(
constint&index,
float&xv,
float&yv)
{
//Getthechromosomeelements
Chromosome*chr=pop.at(index);
//Putthefirst32bits(X)intoastring
std::stringxstr=GetXstring(chr);
//GetxvaluebyconvertingfromIEEE754intodecimal
floatx=GetFloat32_IEEE754(xstr);
//Putthenext32bits(Y)intoastring
std::stringystr=GetYstring(chr);
//GetyvaluebyconvertingfromIEEE754intodecimal
floaty=GetFloat32_IEEE754(ystr);
doublefitness=CalculateFitnessFunction(x,y);
//Returnthechromosomefitness
xv=x;
yv=y;
returnfitness;
}
//GetfitnessofselectedchromosomeaccordingtoGraycoding
doublePopulation::CalcChromosomeFitnessGray(
constint&index,
float&xv,
float&yv)
{
//Getthechromosomeelements
Chromosome*chr=pop.at(index);
//Putthefirst32bits(X)intoastring
std::stringxstr=GetXstring(chr);
//GetxvaluebyconvertingfromGrayintodecimal
floatx=GetFloat32_Gray(xstr);
//Putthenext32bits(Y)intoastring
std::stringystr=GetYstring(chr);
//GetyvaluebyconvertingfromGrayintodecimal
floaty=GetFloat32_Gray(ystr);
doublefitness=CalculateFitnessFunction(x,y);
//Returnthechromosomefitness
xv=x;yv=y;
returnfitness;
}
//Setthesizeofthechromosome
voidPopulation::SetChromosomeSize(constint&size)
{
chrSize=size;
}
//Getthe‘X’stringportionoftyechromsome
std::stringPopulation::GetXstring(Chromosome*chr)
{
std::stringxstr;
for(inti=0;i<chrSize/2;i++)
{
unsignedcharvalue=chr->GetChromosome(i);
xstr.append(value==0?“0”:“1”);
}
returnxstr;
}
//Getthe‘Y’stringportionofthechromsome
std::stringPopulation::GetYstring(Chromosome*chr)
{
std::stringystr;
intstart=chrSize/2;
for(inti=start;i<chrSize;i++)
{
unsignedcharvalue=chr->GetChromosome(i);
ystr.append(value==0?“0”:“1”);
}
returnystr;
}
//Convert32-bitbinaryintothedecimal
//IEEE754encodingfor32bitbinarystring
floatPopulation::GetFloat32_IEEE754(std::stringBinary)
{
intHexNumber=Binary32ToHex(Binary);
boolnegative=!!(HexNumber&0x80000000);
intexponent=(HexNumber&0x7f800000)>>23;
intsign=negative?-1:1;
//Subtract127fromtheexponent
exponent-=127;
//Convertmantissaintodecimalusinglast23bits
intpower=-1;
floattotal=0.0;
for(inti=0;i<23;i++)
{
intc=Binary[i+9]-‘0’;
total+=(float)c*(float)pow(2.0,power);
power—;
}
total+=1.0;
floatvalue=sign*(float)pow(2.0,exponent)*total;
returnvalue;
}
//Convert32-bitgrayencodingintothedecimalequivalent
floatPopulation::GetFloat32_Gray(std::stringBinary)
{
//Getsignbit
intsign=Binary.at(0)==‘0’?1:-1;
//Getremainingstring
std::stringrem=Binary.substr(1);
std::stringbin=Gray2Bin(rem);
longbin_val=Bin2Dec(bin);
floatval=(float)bin_val/(float)divisor_31;
val*=sign;
returnval;
}
//Convertthe32-bitbinaryencodingintohexadecimal
intPopulation::Binary32ToHex(std::stringBinary)
{
std::bitset<32>set(Binary);
inthex=set.to_ulong();
returnhex;
}
//Calculatetheoverallfitness,f(x,y)
doublePopulation::CalculateFitnessFunction(constfloat&x,
constfloat&y)
{
//Rosenbrock:(1-x)^2+100(y-x*x)^2
//(seehttp://en.wikipedia.org/wiki/Rosenbrock_function)
doublefitness=(pow((double)(1.0-x),2))+
100*(pow((double)(y-(x*x)),2));
returnfitness;
}
//Getfitnessofselectedchromosome
doublePopulation::GetChromosomeFitness(constint&index)const
{
//Getthechromosome
Chromosome*chr=pop.at(index);
returnchr->GetFitness();
}
//Copythecontentsofthesourcechromosomeintodestination
voidPopulation::CopyChromosome(constint&source,constint&dest)
{
//Getthechromosomes
Chromosome*chr1=pop.at(source);
Chromosome*chr2=pop.at(dest);
//Copyelementsandfitnessofsourcechromosome
//intothedestinationchromosome
for(inti=0;i<chrSize;i++){
//Getsourcechromosomeelement
unsignedcharvalue=chr1->GetChromosome(i);
//Writethiselementvalueintothedestinationelement
chr2->SetChromosome(i,value);
}
//Setthedestinationchromosomefitness
doublefitness=chr1->GetFitness();
chr2->SetFitness(fitness);
}
//Convertgrayencodingintobinary
std::stringPopulation::Gray2Bin(std::stringgray)
{
intlen=gray.length();
std::stringbin=gray;
for(inti=len-2;i>=0;i—)
{
intbk=bin.at(i+1)-‘0’;
intgi=gray.at(i+1)-‘0’;
if(bk==0)
{
bin[i]=gi==1?‘1’:‘0’;
}
else
{
bin[i]=gi==1?‘0’:‘1’;
}
}
returnbin;
}
//Binarytoint
longPopulation::Bin2Dec(std::stringbin)
{
char*ptr;
longparsed=strtol(bin.c_str(),&ptr,2);
returnparsed;
}
main.cpp
#include“Log.h”
#include<sstream>
#include“GeneticAlgorithm.h”
constintencoding_type=0;
constintcrossover_rate=70;
constintmutation_rate=5;
constintpopulation_size=100;
constintnumber_iterations=10000;
constintchromosome_size=64;
constinttournament_size=population_size/4;
intmain()
{
//RuntheGA!
GeneticAlgorithmga;
ga.Initialize(encoding_type,
crossover_rate,
mutation_rate,
population_size,
number_iterations,
chromosome_size,
tournament_size,
“C:\dump\best.txt”);
ga.Run();
return0;
}
ChapterSummary
Tosummarize,thismasterlessonprovides:
ApracticalexampleofhowtodevelopageneticalgorithmapplicationinC++andapplyittoadifficultoptimizationproblemtogeneratebettersolutionsoversuccessiveiterations.
AdemonstrationofhowitispossibletoimprovetheperformanceofageneticalgorithmbyexperimentingwithbinaryencodingssuchasIEEE754andGrayandbyvaryingtheratesofgeneticoperatorcrossover,mutationandselection.
Chapter8:Applicationof2-opttotheTravelingSalesmanProblem
A2-optisasimplelocalsearchalgorithmforsolving‘travelingsalesman’typeproblems.Itsoperationissimple:itmodifiesaroutethatcrossesoveritself,reorderingitsotherearenolongeranycrossoversandthereforeashorteroverallroute:
Thiscanbeausefultechniqueforfindingreasonablesolutionstotravelingsalesmanproblemswithhundredsoreventhousandsofnodelocations.
TheMechanismbywhichthe2-optswapsagivenroutebetweennodesiandktocreateanewroute:
1.addroute[0]toroute[i-1]inordertothenewroute
2.addroute[i]toroute[k]inreverseordertothenewroute
3.addroute[k+1]toendinordertothenewroute;
Andasappliedtotheexamplerouteabove,followingsteps1-3:
route[0to3]:Glasgow>Edinburgh>Manchester>Leedswherei=1;k=2.Newroute:
1.Glasgow
2.Glasgow>(Manchester>Edinburgh)
3.Glasgow>Manchester>Edinburgh>(Leeds)
Thecomplete2-optswapheuristicmakinguseoftheabovemechanismtosearcheverypossiblevalidcombination:
While(nofurtherimprovementmade)
{
start_again:
best_distance=calculateTotalDistance(existing_route)
for(i=0;i<numberofnodeseligibletobeswapped-1;i++){
for(k=i+1;k<numberofnodeseligibletobeswapped;k++){
new_route=2optSwap(existing_route,i,k)
new_distance=calculateTotalDistance(new_route)
if(new_distance<best_distance){
existing_route=new_route
gotostart_again
}
}
}
}
ThecreationofaVisualStudio2010dialog-basedprojecttoimplementtheoptimizationalgorithmandpresenttheoutputgraphicallyisdescribed,togetherwithsomeresultsfromapplyingtheprogramtoastandardtravelingsalesmantestproblem.
Justfollowthesesteps:
Step1:Createastandarddialog-basedapplication:
InVisualStudioselectFile>New>Project.SelectMFCApplication:
Dothesamefortheadvancedfeaturespage:
ClickNextandthenclickFinish.
Step2:Createthedialogcontrols
IntheResourceViewseehowthebasicdialoghasbeencreated:
Removethe“TODO:Placedialogcontrolshere.”staticcontrolinthemiddle.Right-clicktheOKbuttonandselectPropertiesandrenameitsCaptionpropertytoRun.ChangetheIDpropertyfromIDOKtoIDC_RUNCreateonemorebutton:fromtheToolbox,selecttheButtoncontrolanddragitontothedialog.SetitsCaptionpropertytoOpenandsetitsIDtoIDC_OPEN.Repositionyourthreebuttonssotheyarelocatedinthetoprightpartofthedialog:
FromtheToolboxdragandresizetwostaticcontrolsontothedialog:
Letthetopstatictextcontrolbecaptionedas“Totaldistance:”andsetitsIDpropertytoIDC_DISTANCE.ThensetthebottomstatictextCaptionpropertyas“Tour:”andsetitsIDtoIDC_TOUR:
Thisiseverythingneededforcreatingtheuserinterface.
Step3:Addtheadditionalsourcefiles
Inthesolutionexplorer,rightclicktheprojectfolderandselectAdd>NewItem.DothistocreatethefollowingC++headerandsourcefiles:
1.CoordMatrix.h,CoordMatrix.cpp
2.Tour.h,Tour.cpp
3.TSPalgorithm.h,TSPalgorithm.cpp
4.MakeString.h
Yoursolutionexplorerwillbeupdatedasfollows:
Insertthecodeforeachofthesourcefiles.
FullCodeListings
CoordMatrix.h
#pragmaonce
#include<vector>
#include<map>
#include<fstream>
#include<tuple>
#include<string>
classCoordMatrix
{
public:
enumEDGE_WEIGHT_TYPE
{
ATT=0
};
CoordMatrix();
~CoordMatrix();
voidInitialize(std::stringfilepath);
intCalcPseudoEuclidDistance(
constint&x1,
constint&x2,
constint&y1,
constint&y2);
doubleDistance(constint&c1,constint&c2);
intsize()const;
intGetX(constint&index)const;
intGetY(constint&index)const;
intGetMinX()const;
intGetMinY()const;
intGetMaxX()const;
intGetMaxY()const;
std::stringGetFileTitle()const;
private:
voidtokenize(std::vector<std::string>&tokens,
conststd::string&text,
conststd::string&delimiter);
voidSetDistanceMatrix();
voidReset();
private:
std::vector<std::pair<int,int>>coords;
std::map<std::pair<int,int>,double>distMatrix;
std::stringtitle;
intminx,miny;
intmaxx,maxy;
intedge_weight_type;
};
CoordMatrix.cpp
#include“StdAfx.h”
#include“CoordMatrix.h”
#include<iostream>
#include<fstream>
#include<sstream>
#include<cmath>
constintinfinity=99999999;
#defineround(n)((int)((n)+0.5))
CoordMatrix::CoordMatrix()
{
minx=infinity;miny=infinity;
maxx=-infinity;maxy=-infinity;
edge_weight_type=ATT;
title=””;
}
CoordMatrix::~CoordMatrix(){}
//Initialisecost/distancematrixfrominputfile
voidCoordMatrix::Initialize(std::stringfilepath)
{
Reset();
minx=infinity;
miny=infinity;
maxx=infinity*-1;
maxy=infinity*-1;
std::vector<std::pair<int,int>>().swap(coords);
std::ifstreamfile;
file.open(filepath.c_str());
if(!file)
{
std::cout<<“Errorinopeningtext/tspfile\n”;
return;
}
intline=0;
while(!file.eof())
{
line++;
charbuf[255];
file.getline(buf,128);
std::vector<std::string>tokens;
std::stringtext(buf);
tokenize(tokens,text,””);
if(tokens.at(0)==“NAME”)
{
title=tokens.at(2);
}
if(tokens.at(0)==“EDGE_WEIGHT_TYPE”)
{
if(tokens.at(2)==“ATT”)
{
edge_weight_type=ATT;
}
}
if(line<7||(tokens.size()==1&&text!=“EOF”))
continue;
if(text==“EOF”)
break;
intnode=atoi(tokens.at(0).c_str());
intx=atoi(tokens.at(1).c_str());
inty=atoi(tokens.at(2).c_str());
std::pair<int,int>n(x,y);
coords.push_back(n);
if(x>maxx)maxx=x;
if(y>maxy)maxy=y;
if(x<minx)minx=x;
if(y<miny)miny=y;
}
file.close();
SetDistanceMatrix();
}
//GetEuclideandistancebetweentwocities
doubleCoordMatrix::Distance(constint&c1,constint&c2)
{
if(c1<c2)
{
std::pair<int,int>p(c1,c2);
returndistMatrix[p];
}
else
{
std::pair<int,int>p(c2,c1);
returndistMatrix[p];
}
}
//Resetthedistancematrix
voidCoordMatrix::Reset()
{
std::vector<std::pair<int,int>>().swap(coords);
std::map<std::pair<int,int>,double>().swap(distMatrix);
}
//Tokenizetheinputstring
voidCoordMatrix::tokenize(std::vector<std::string>&tokens,
conststd::string&text,
conststd::string&delimiter)
{
size_tnext_pos=0;
size_tinit_pos=text.find_first_not_of(delimiter,next_pos);
while(next_pos!=std::string::npos&&
init_pos!=std::string::npos){
next_pos=text.find(delimiter,init_pos);
std::stringtoken=text.substr(init_pos,next_pos-init_pos);
tokens.push_back(token);
init_pos=text.find_first_not_of(delimiter,next_pos);
}
}
//Getnumberofitemscontainedinmatrix
intCoordMatrix::size()const
{return(int)coords.size();}
//Getselectedxcoordinate
intCoordMatrix::GetX(constint&index)const
{
std::pair<int,int>n=coords.at(index);
returnn.first;
}
//Getselectedycoordinate
intCoordMatrix::GetY(constint&index)const
{
std::pair<int,int>n=coords.at(index);
returnn.second;
}
//Getminimumxcoordinate
intCoordMatrix::GetMinX()const
{returnminx;}
//Getminimumycoordinate
intCoordMatrix::GetMinY()const
{returnminy;}
//Getmaximumxcoordinate
intCoordMatrix::GetMaxX()const
{returnmaxx;}
//Getmaximumycoordinate
intCoordMatrix::GetMaxY()const
{returnmaxy;}
//Getthefilename
std::stringCoordMatrix::GetFileTitle()const
{returntitle;}
//Setupdistancematrixbetweennodepairs
voidCoordMatrix::SetDistanceMatrix()
{
for(inti=0;i<(int)coords.size()-1;i++)
{
std::pair<int,int>p1=coords.at(i);
intx1=p1.first;inty1=p1.second;
for(intj=i+1;j<(int)coords.size();j++)
{
std::pair<int,int>p2=coords.at(j);
intx2=p2.first;inty2=p2.second;
doubledist=0.0;
if(edge_weight_type==ATT)
{
dist=CalcPseudoEuclidDistance(x1,x2,y1,y2);
}
else
{
dist=(double)sqrt((double)(x1-x2)*(x1-x2)+
(double)(y1-y2)*(y1-y2));
}
std::pair<int,int>p(i,j);
distMatrix[p]=dist;
}
}
}
//Foredgeweighttype‘ATT’
//PseudoEuclideandistance
intCoordMatrix::CalcPseudoEuclidDistance(constint&x1,
constint&x2,
constint&y1,
constint&y2)
{
intdij=0;
intxd=x1-x2;intyd=y1-y2;
doublerij=sqrt((xd*xd+yd*yd)/10.0);
inttij=round(rij);
if(tij<rij)
dij=tij+1;
else
dij=tij;
returndij;
}
Tour.h
#pragmaonce
#include“CoordMatrix.h”
#include<vector>
#include<set>
classTour
{
public:
Tour();
Tour(constTour&t);
Tour&operator=(constTour&t);
~Tour();
voidDoTwoOpt(constint&c1,constint&c2,
constint&c3,constint&c4);
doubleTourDistance()const;
intTourSize()const;
intGetCity(constint&index);
voidSetCity(constint&index,constint&value);
voidSetMatrix(CoordMatrix*mat);
voidCreateRandomTour();
voidCreateTour();
voidCreateNearestNeighbourTour();
voidReset();
voidSetCities(conststd::vector<int>&v);
private:
doubleDistance(constint&c1,constint&c2)const;
intGetNearestNeighbour(constint&c,std::set<int>&cset);
private:
std::vector<int>cities;
CoordMatrix*matrix;
};
Tour.cpp
#include“StdAfx.h”
#include“Tour.h”
#include<algorithm>
#include<functional>
Tour::Tour(){}
Tour::~Tour(){}
Tour::Tour(constTour&t)
{cities=t.cities;}
Tour&Tour::operator=(constTour&t)
{
if(this!=&t)
cities=t.cities;
return*this;
}
voidTour::DoTwoOpt(constint&c1,constint&c2,
constint&c3,constint&c4)
{
if(c3==c1||c3==c2||c4==c1||c4==c2)
return;
intc1old=cities.at(c1);
intc2old=cities.at(c2);
intc3old=cities.at(c3);
intc4old=cities.at(c4);
doubleold_distance=TourDistance();
inttmp=cities.at(c2);
cities[c2]=cities.at(c3);
cities[c3]=tmp;
doublenew_distance=TourDistance();
if(new_distance>old_distance)
{
cities[c1]=c1old;
cities[c2]=c2old;
cities[c3]=c3old;
cities[c4]=c4old;
}
}
//Gettotaldistanceoftour
doubleTour::TourDistance()const
{
doubledist=0.0;
intsize=(int)cities.size();
intc1,c2;
for(inti=0;i<size-1;i++){
c1=cities.at(i);
c2=cities.at(i+1);
dist+=Distance(c1,c2);
}
c1=cities.at(size-1);
c2=cities.at(0);
dist+=Distance(c1,c2);
returndist;
}
//Generatearbitrarytour
voidTour::CreateRandomTour()
{
Reset();
for(inti=0;i<matrix->size();i++)
cities.push_back(i);
random_shuffle(cities.begin()+1,cities.end());
}
//Generatearbitrarytour
voidTour::CreateTour()
{
Reset();
for(inti=0;i<matrix->size();i++)
cities.push_back(i);
}
//Getdistancebewteenselectedcities
doubleTour::Distance(constint&c1,
constint&c2)const
{returnmatrix->Distance(c1,c2);}
//Setpointertocost/distancematrixobject
voidTour::SetMatrix(CoordMatrix*mat)
{matrix=mat;}
//Resetexistingtourdata
voidTour::Reset()
{std::vector<int>().swap(cities);}
//Returnthenumberofcitiesinthetour
intTour::TourSize()const
{return(int)cities.size();}
//Getthetourcity,giventheindexpassedtoit
intTour::GetCity(constint&index)
{
intnode=cities.at(index);
returnnode;
}
//Gettourfromthesetofnearestneighbours
voidTour::CreateNearestNeighbourTour()
{
Reset();
std::set<int>city_set;
std::set<int>::iteratorit;
for(inti=0;i<matrix->size();i++)
city_set.insert(i);
intcity=0;
for(inti=1;i<=matrix->size();i++)
{
cities.push_back(city);
it=city_set.find(city);
city_set.erase(it);
city=GetNearestNeighbour(city,city_set);
}
}
intTour::GetNearestNeighbour(constint&c,std::set<int>&cset)
{
intcity=0;
doublemin_dist=99999999;
std::set<int>::iteratorcit;
for(cit=cset.begin();cit!=cset.end();cit++){
intn=*cit;
if(n==c)continue;
doubledist=Distance(c,n);
if(dist<min_dist){
city=n;
min_dist=dist;
}
}
returncity;
}
voidTour::SetCities(conststd::vector<int>&v)
{cities=v;}
voidTour::SetCity(constint&index,constint&value)
{cities[index]=value;}
TSPalgorithm.h
#pragmaonce
#include“Tour.h”
#include<string>
classObserver;
classTSPalgorithm
{
public:
TSPalgorithm(void);
~TSPalgorithm(void);
voidInitialize(constint&iterations,CoordMatrix*mat);
voidTwoOptSwap(constint&i,constint&k);
voidRun();
intGetTourCity(constint&index);
voidAddObserver(Observer*ob);
voidNotify(constint&dist);
private:
voidTwoOpt();
private:
Tourtour,new_tour;
intiterations;
std::vector<Observer*>ob_set;
};
classObserver
{
public:
virtualvoidUpdateDisplay(constfloat&dist)=0;
};
TSPalgorithm.cpp
#include“StdAfx.h”
#include“TSPalgorithm.h”
TSPalgorithm::TSPalgorithm()
{}
TSPalgorithm::~TSPalgorithm()
{}
//SetupTSPproblemtorun
voidTSPalgorithm::Initialize(
constint&iter,
CoordMatrix*mat)
{
tour.SetMatrix(mat);
new_tour.SetMatrix(mat);
iterations=iter;
}
//Runtheoptimizationalgorithm
voidTSPalgorithm::Run()
{
tour.Reset();
tour.CreateNearestNeighbourTour();
TwoOpt();
}
//Getthetournode,giventheindex
intTSPalgorithm::GetTourCity(constint&index)
{
returntour.GetCity(index);
}
//Notifyobserversofchanges
voidTSPalgorithm::Notify(constint&dist)
{
for(std::vector<Observer*>::iteratorit=ob_set.begin();
it!=ob_set.end();
it++)
{
(*it)->UpdateDisplay(dist);
}
}
//Addobserver
voidTSPalgorithm::AddObserver(Observer*ob)
{
ob_set.push_back(ob);
}
//Doall2-optcombinations
voidTSPalgorithm::TwoOpt()
{
intsize=tour.TourSize();
new_tour=tour;
boolimprove=true;
doublebest_distance,new_distance;
while(improve)
{
best_distance=tour.TourDistance();
improve=false;
for(inti=0;i<size-1;i++)
{
for(intk=i+1;k<size;k++)
{
TwoOptSwap(i,k);
new_distance=new_tour.TourDistance();
if(new_distance<best_distance)
{
improve=true;
tour=new_tour;
best_distance=new_distance;
Notify(tour.TourDistance());
i=size;
break;
}
}
}
}
}
voidTSPalgorithm::TwoOptSwap(constint&i,constint&k)
{
for(intc=0;c<=i-1;++c)
{
new_tour.SetCity(c,tour.GetCity(c));
}
intdec=0;
for(intc=i;c<=k;++c)
{
new_tour.SetCity(c,tour.GetCity(k-dec));
dec++;
}
for(intc=k+1;c<tour.TourSize();++c)
{
new_tour.SetCity(c,tour.GetCity(c));
}
}
MakeString.h
#include<fstream>
#include<sstream>
#include<string>
classmake_string
{
public:
template<typenameT>
make_string&operator<<(Tconst&datum)
{
buffer_<<datum;
return*this;
}
operatorstd::string()const
{returnbuffer_.str();}
private:
std::ostringstreambuffer_;
};
Step4:addthestaticcontrolvariables
IntheResourceView,right-clickthe“TotalDistance”staticcontrolyouaddedandselectAddVariable.Giveitthevariablenamem_Distance:
Repeatthisstepforthe“Tour”staticcontrol,givingitthevariablenamem_Tour.NoticethatfollowingthisstepwillinserttheCStaticcontrolsinthedialogheaderclassCTwoOptDlg.handinserttheDDXcontrolsinCTwoOptDlg.cpp:
voidCTwoOptDlg::DoDataExchange(CDataExchange*pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX,IDC_DISTANCE,m_Distance);
DDX_Control(pDX,IDC_TOUR,m_Tour);
}
Step5:Implementthebuttoneventhandlinglogic
IntheResourceView,double-clicktheRunbuttontogeneratetheeventhandlercodeinCTwoOptDlg.cppforwhenthisbuttonisclicked.
Oncethisisgenerated,insertthefollowingcodeinsideOnBnClickedRuntoinvokethealgorithmanddisplayupdates:
voidCTwoOptDlg::OnBnClickedRun()
{
alg.Run();
hasRun=true;
RedrawWindow(0,0,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE);
}
DothesamefortheOpenbutton:DoubleclicktheOpenbuttontogenerateitseventhandlercode,andinsertthefollowingcodethatwillenabletheusertoopenthedesired*.tspfile:
RedrawWindow(0,0,RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE);
}voidCTwoOptDlg::OnBnClickedOpen()
{
CPaintDCdc(this);
CRectrect;
GetClientRect(&rect);
//Openthefileopeningdialog
CFileDialogfileDlg(
TRUE,
NULL,
NULL,
OFN_HIDEREADONLY|OFN_FILEMUSTEXIST,
(LPCTSTR)“.tspFiles(*.tsp)|*.tsp||”,
this);
fileDlg.m_ofn.lpstrTitle=(LPCSTR)“LoadingTSPProblem”;
INT_PTRresult=fileDlg.DoModal();
CStringpath=fileDlg.GetPathName();
if(result==IDOK)
{
//Resetmatrixdatacontents
CT2CApszConvertedAnsiString(path);
std::stringpath_str(pszConvertedAnsiString);
mat.Initialize(path_str);
hasRun=false;
(AfxGetMainWnd())->SetWindowText(mat.GetFileTitle().c_str());
}
Step6:UpdatetheOnPaintmethod
UpdateOnPaintmethodtodrawthecurrentbestsolutionfoundbythealgorithm:
voidCTwoOptDlg::OnPaint()
{
//devicecontextforpainting
CPaintDCdc(this);
if(IsIconic())
{
SendMessage(
WM_ICONERASEBKGND,
reinterpret_cast<WPARAM>(dc.GetSafeHdc()),0);
//Centericoninclientrectangle
intcxIcon=GetSystemMetrics(SM_CXICON);
intcyIcon=GetSystemMetrics(SM_CYICON);
CRectrect;
GetClientRect(&rect);
intx=(rect.Width()-cxIcon+1)/2;
inty=(rect.Height()-cyIcon+1)/2;
//Drawtheicon
dc.DrawIcon(x,y,m_hIcon);
}
else
{
if(mat.size()<1)return;
CRectrect;
GetClientRect(&rect);
intrectx1=rect.left+20;
intrectx2=rect.right-150;
intrecty1=rect.top+20;
intrecty2=rect.bottom-140;
CPenpen(PS_SOLID,1,RGB(0,0,0));
dc.SelectStockObject(BLACK_BRUSH);
inttour_size=mat.size();
for(count=0;count<tour_size;count++)
{
xc1=mat.GetX(count);
yc1=mat.GetY(count);
xn1=(float)(xc1-mat.GetMinX())/
(float)(mat.GetMaxX()-mat.GetMinX());
yn1=(float)(yc1-mat.GetMinY())/
(float)(mat.GetMaxY()-mat.GetMinY());
xcoord1=rectx1+(int)(float)(xn1*abs(rectx1-rectx2));
ycoord1=recty2-(int)(float)(yn1*abs(recty1-recty2));
dc.Ellipse(xcoord1-2,ycoord1-2,xcoord1+2,ycoord1+2);
if(hasRun&&count<tour_size-1)
{
cc1=alg.GetTourCity(count);
cc2=alg.GetTourCity(count+1);
xc1=mat.GetX(cc1);
yc1=mat.GetY(cc1);
xc2=mat.GetX(cc2);
yc2=mat.GetY(cc2);
xn1=(float)(xc1-mat.GetMinX())/
(float)(mat.GetMaxX()-mat.GetMinX());
yn1=(float)(yc1-mat.GetMinY())/
(float)(mat.GetMaxY()-mat.GetMinY());
xn2=(float)(xc2-mat.GetMinX())/
(float)(mat.GetMaxX()-mat.GetMinX());
yn2=(float)(yc2-mat.GetMinY())/
(float)(mat.GetMaxY()-mat.GetMinY());
xcoord1=rectx1+(int)(float)(xn1*abs(rectx1-rectx2));
ycoord1=recty2-(int)(float)(yn1*abs(recty1-recty2));
xcoord2=rectx1+(int)(float)(xn2*abs(rectx1-rectx2));
ycoord2=recty2-(int)(float)(yn2*abs(recty1-recty2));
dc.MoveTo(xcoord1,ycoord1);
dc.LineTo(xcoord2,ycoord2);
}
}
if(hasRun)
{
cc1=alg.GetTourCity(tour_size-1);
cc2=alg.GetTourCity(0);
xc1=mat.GetX(cc1);
yc1=mat.GetY(cc1);
xc2=mat.GetX(cc2);
yc2=mat.GetY(cc2);
xn1=(float)(xc1-mat.GetMinX())/
(float)(mat.GetMaxX()-mat.GetMinX());
yn1=(float)(yc1-mat.GetMinY())/
(float)(mat.GetMaxY()-mat.GetMinY());
xn2=(float)(xc2-mat.GetMinX())/
(float)(mat.GetMaxX()-mat.GetMinX());
yn2=(float)(yc2-mat.GetMinY())/
(float)(mat.GetMaxY()-mat.GetMinY());
xcoord1=rectx1+(int)(float)(xn1*abs(rectx1-rectx2));
ycoord1=recty2-(int)(float)(yn1*abs(recty1-recty2));
xcoord2=rectx1+(int)(float)(xn2*abs(rectx1-rectx2));
ycoord2=recty2-(int)(float)(yn2*abs(recty1-recty2));
dc.MoveTo(xcoord1,ycoord1);
dc.LineTo(xcoord2,ycoord2);
}
CDialogEx::OnPaint();
}
}
Step7:Addmethodstoinitializeobserverandrefreshdisplay
InsertthiscodetoCTwoOptDlg.cpptoinitializetheobserverpatternandoutputthedetailsofthebesttourfound:
voidCTwoOptDlg::Init(TSPalgorithm*tsp)
{
alg.AddObserver(this);
}
voidCTwoOptDlg::UpdateDisplay(constfloat&dist)
{
hasRun=true;
std::strings=make_string()<<“Totaldistance:”
<<dist;
m_Distance.SetWindowText(s.c_str());
inttour_size=mat.size();
std::stringtour=make_string()<<“Tour:\n”;
std::stringt;
for(inti=0;i<tour_size;i++)
{
t=make_string()<<alg.GetTourCity(i)+1
<<“-“;
tour.append(t);
if(i>0&&i%35==0)
{
tour.append(“\n”);
}
}
t=make_string()<<alg.GetTourCity(0)+1;
tour.append(t);
m_Tour.SetWindowText(tour.c_str());
RedrawWindow(
0,
0,
RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE);
}
Step8:EnsurenecessaryheadersarepresentinCTwoOptDlg.cppandupdateOnInitDialog
MakesurethefollowingheadersareincludedinCTwoOptDlg.cpp:
#include“stdafx.h”
#include“Two-Opt.h”
#include“Two-OptDlg.h”
#include“MakeString.h”
#include<fstream>
#include<sstream>
#include<string>
#include“afxdialogex.h”
BOOLCTwoOptDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
SetIcon(m_hIcon,TRUE);//Setbigicon
SetIcon(m_hIcon,FALSE);//Setsmallicon
Init(&alg);
alg.Initialize(10000,&mat);
hasRun=false;
font.CreatePointFont(100,_T(“Arial”));
m_Distance.SetFont(&font);
m_Tour.SetFont(&font);
GetDlgItem(IDC_DISTANCE)->SetFont(&font);
GetDlgItem(IDC_TOUR)->SetFont(&font);
returnTRUE;//returnTRUEunlessyousetthefocustoacontrol
}
Step9:AddadditionalvariablesandmethodstotheCTwoOptDlgclass
ThisincludestheTSPalgorithmandCoordMatrixobjectsandmethodstoinitializetheobserverandupdatethedisplay.YoucanjustcopyandinsertthecompleteCTwoOptDlg.hcodelistingshownhere:
#pragmaonce
#include“TSPalgorithm.h”
#include“CoordMatrix.h”
#include“afxwin.h”
//CTwoOptDlgdialog
classCTwoOptDlg:publicCDialogEx,Observer
{
//Construction
public:
CTwoOptDlg(CWnd*pParent=NULL);
//DialogData
enum{IDD=IDD_TWOOPT_DIALOG};
protected:
virtualvoidDoDataExchange(CDataExchange*pDX);
//Implementation
protected:
HICONm_hIcon;
//Generatedmessagemapfunctions
virtualBOOLOnInitDialog();
afx_msgvoidOnPaint();
afx_msgHCURSOROnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
afx_msgvoidOnBnClickedOk();
afx_msgvoidOnBnClickedOpen();
afx_msgvoidOnBnClickedRun();
voidInit(TSPalgorithm*tsp);
voidUpdateDisplay(constfloat&dist);
private:
TSPalgorithmalg;
CoordMatrixmat;
boolhasRun;
intcc1,cc2,xc1,xc2,yc1,yc2;
floatxn1,xn2,yn1,yn2;
intxcoord1,xcoord2,ycoord1,ycoord2;
intx1,y1,count;
CStaticm_Distance,m_Tour;
LOGFONTlf;
CFontfont;
};
Oncetheprojectiscompleted,usingitissimple:downloadatestproblemasa*.tspfilesuchasAtt48.tsp,representing48capitalcitiesoftheUSA:
http://www.technical-recipes.com/Downloads/att48.tsp
ClickOpentoloadthistestproblemandthenclickRun.
Totryoutthealgorithm,firstclickOpentoloadthetestproblem.Thiswillrepresentthegeographicallocationsofcitiesasaseriesofnodes:
UponpressingRun,the2-optalgorithmisappliedtorunthroughalltwo-edgecombinationsandfindthebestsolutionobtainedfromthisprocess.Theprogramwillperiodicallyupdatethegraphicallayoutasthealgorithmfindsprogressivelybettersolutions.
ChapterSummary
Tosummarizethismasterlesson,youhavelearnedhowto:
Applythe2-optalgorithminC++togeneratenear-optimalsolutionstotravelingsalesmantestproblems.
CreateaMFCdialogapplicationandusestandarddrawingobjectstopresenttheresultsgraphically.
Chapter9:Pathfindinginnetworks
Afrequentlyoccurringrequirementinsolvingnetworkoptimizationproblemsistofindallpathcombinationsthatcontainnocyclesbetweenaselectedpairofcommunicatingendnodes.Anadditionalfactorinfindingallcombinationsofpathsisthatthealgorithmshouldbeabletohandlebothdirectedorundirectedgraphs.TheNationalInstituteofStandardsandTechnology(NIST)onlineDictionaryofAlgorithmsandDataStructuresdescribesthisparticularproblemas“allsimplepaths”andrecommendsadepth-firstseatchtofindallnon-cyclicalpathsbetweenarbitrarypairsofnodes.
Problemdefinition:Findallsimplepathsfromastartingvertex(source)toadestinationvertex(sink)inadirectedgraph.Inanundirectedgraph,findallsimplepathsbetweentwovertices.
NetworkconnectivityismodeledusingaGraphclassasthemeanstouniquelyidentifyindividuallinks,aswellasaddnewlinksandcheckforthepresenceofexistinglinks.Italsoinvolvessettingagraph’sdirected/undirectedstatusofaddedlinks.
Thealgorithmusedforidentifyingandenumeratingpathsusesusingarecursivedepth-firstsearch.Thesearchavoidsrepeatingvertices(nodes)bymarkingthemwithabooleanvisitedvalueastheyarevisitedduringtherecursion,thenunmarkingthenodejustbeforereturningfromtherecursivecall.
DepthFirstSearch(GraphG,vertexv)
{
Setvertexvasvisited;
For(eachneighborvertexwofv)
{
If(vertexwisunvisited)
{
DepthFirstSearch(w);
AddedgevwtotreeT
}
}
}
Practicalapplicationofthedepth-firstalgorithm
Beginningfromthestartnode,thedepthfirstsearchalgorithmexaminesalloutgoinglinksandprogressesbyexpandingthefirstchildnodeofthesearchtreethatappears.Itsearchesprogressivelydeeperuntilatargetnodeisfound,oruntilitencountersanodethathasnochildren.Thesearchthenbacktracks,returningtothemostrecentnodeithasn’tfinishedexploring.
Example1:networktopologywithuni-directionallinks
Hereisthegraphicalrepresentationofa5-nodedirectedgraphproblemusedintheexamplepresentedhere:
Inthemainmainprogramloop,thenetworkwassetashavingdirectededgeswhichareinsertedusingcallstotheNetworkobject’sAddLinkmethod.
Forexampleadirectededgeexistsbetweennodes[1,3],butnotnodes[3,1],hencethesinglearrowbetweenthenode[1,3]pair.Directededgesexistbetweennodes[1,2]andnodes[2,1],hencethebi-directionallinkbetweenthem.
Tofindallpossiblecombinationsofpathsbetweennodes[2,5]forexample,wesimplysetthestartandtargetnodesandfeedtheGetAllPathsmethodwiththem.
The-1valuepassedtoGetAllPathssignifiesthatwedonotwishtofilteranyofthesearchresultsformaximumnumberofhops,butreturnallpossiblepathsitfinds.
Examplemain.cppusage:
#include“Algorithm.h”
#include<iostream>
#include<vector>
constLink<int>linkset[]={Link<int>(1,2),
Link<int>(1,3),
Link<int>(1,4),
Link<int>(2,1),
Link<int>(2,4),
Link<int>(2,3),
Link<int>(3,5),
Link<int>(4,5),
Link<int>(5,3)};
constsize_tsize=sizeof(linkset)/sizeof(linkset[0]);
conststd::vector<Link<int>>links(linkset,linkset+size);
intmain()
{
//Createnetworkfrominterconnectionsgiven
Networknw(links,false);
//Createthealgorithmobject
Algorithmalgorithm(&nw);
//Getallpathsbetweennodes2and5
algorithm.GetAllPaths(&nw,2,5,-1,-1);
//Outputthesetofpathsfound
nw.ShowPaths();
return0;
}
Thisgivesthefollowingoutputforfindingallpathsbetweennodes2and5:
Example2:networktopologywithbi-directionallinks
Usingaverysimpletweakwecanalsoconstructnetworktopologieswithlinkinterconnectionsthatarearebi-directional;thatis,alinkbetween[1,2]isthesameasthelinkbetween[2,1].Inotherwords,addinglink[2,1]whenlink[1,2]alreadyexistswillmakenodifference.
Simplysetthebi-directionalflagintheNetworkclassconstructortotrue,andbuildthenetworkthesamewayasshowninthismain.cppexample:
#include“Algorithm.h”
#include<iostream>
#include<vector>
constLink<int>linkset[]={Link<int>(1,2),
Link<int>(1,4),
Link<int>(1,7),
Link<int>(2,4),
Link<int>(2,3),
Link<int>(3,5),
Link<int>(3,8),
Link<int>(4,5),
Link<int>(4,9),
Link<int>(5,6),
Link<int>(5,7),
Link<int>(5,9),
Link<int>(6,8),
Link<int>(6,10),
Link<int>(7,8),
Link<int>(8,10),
Link<int>(9,10)};
constsize_tsize=sizeof(linkset)/sizeof(linkset[0]);
conststd::vector<Link<int>>links(linkset,linkset+size);
intmain()
{
//Createnetworkfrominterconnectionsgiven
Networknw(links,true);
//Createthealgorithmobject
Algorithmalgorithm(&nw);
//Getallpathsbetweennodes2and5
algorithm.GetAllPaths(&nw,2,5,-1,-1);
//Outputthesetofpathsfound
nw.ShowPaths();
return0;
}
Thisgivesthefollowingoutputforfindingallpathsbetweennodes2and5:
Example3:filteringbythemaximumnumberofhops
Wemaywishtorestrictpathsfoundtothosewhosemaximumhopcountisnolargerthanasetmaximum,say4hops.Byusinganothersimpletweakwecanachievethis.Simplysetthemax_hopsargumentthatispassedtoAlgorithm::GetAllPathstosomevaluegreaterthanzero.Themin_hopsargumentiskeptasdefault-1:
algorithm.GetAllPaths(&nw,2,5,4,-1);
Re-runningtheprogramwiththesamenetworkasinexample2givesthefollowingpaths(restrictedtoamaximumof4hops):
Example4:Labelingnetworknodesarbitrarily
Apossibleenhancementcouldbetoallowthenodestobelabeledasstringvariablesaswellasintegers.Considerthefollowing9-nodenetworkrepresentingUKtownsandcities:
Giventhatwewanttolabelthenodestogiveamoremeaningfuldescription,itispossibletomaintainatwo-waymappingbetweentheintegerusedtouniquelyidentifyeachnodecreatedandthetextlabelusedtodescribeit.SupposingwewishtoconstructtheabovenetworkandfindallpossiblepathsbetweenBristolandEdinburghforanundirected
representationofthegraph.Theusagewouldbeasfollows:
#include“Algorithm.h”
#include<iostream>
#include<vector>
constLink<std::string>linkset[]=
{Link<std::string>(“Bristol”,“London”),
Link<std::string>(“Bristol”,“Cardiff”),
Link<std::string>(“Oxford”,“Cardiff”),
Link<std::string>(“Glasgow”,“Manchester”),
Link<std::string>(“Manchester”,“Leeds”),
Link<std::string>(“Manchester”,“Cardiff”),
Link<std::string>(“Glasgow”,“Edinburgh”),
Link<std::string>(“Edinburgh”,“Newcastle”),
Link<std::string>(“Leeds”,“London”),
Link<std::string>(“Newcastle”,“Leeds”)};
constsize_tsize=sizeof(linkset)/sizeof(linkset[0]);
conststd::vector<Link<std::string>>links(linkset,linkset+size);
intmain()
{
//Createnetworkfrominterconnectionsgiven
Networknw(links,true);
//Createthealgorithmobject
Algorithmalgorithm(&nw);
//GetallpathsbetweennodesBristolandBath
algorithm.GetAllPaths(&nw,“Bristol”,“Edinburgh”,-1,-1);
//Outputthesetofpathsfound
nw.ShowPaths();
return0;
}
Thisgivesthefollowingoutput:
Example5:Findingallpathsbetweenthesamenode
Youmaywishtofindallpathsbetweenthesamenode.Takethefollowingnetworkwithbi-directionallinks:
Tofindallpathsthatstartfromthenode2andreturntonode2,anexampleisshownhere:
#include“Algorithm.h”
#include<iostream>
#include<vector>
constLink<int>linkset[]={Link<int>(1,2),
Link<int>(1,3),
Link<int>(1,4),
Link<int>(2,1),
Link<int>(2,4),
Link<int>(2,3),
Link<int>(3,5),
Link<int>(4,5),
Link<int>(5,3)};
constsize_tsize=sizeof(linkset)/sizeof(linkset[0]);
conststd::vector<Link<int>>links(linkset,linkset+size);
intmain()
{
//Createnetworkfrominterconnectionsgiven
Networknw(links,true);
//Createthealgorithmobject
Algorithmalgorithm(&nw);
//Getallpathsbetweennodes2and5
algorithm.GetAllPaths(&nw,2,2,-1,-1);
//Outputthesetofpathsfound
nw.ShowPaths();
return0;
}
Thisgivesthelistofallfoundpathstoandfromthenodelabelled2:
Ifyouwouldrathernothavepathssuchas2–1–2thenspecifyaminimumhopcountof3:
algorithm.GetAllPaths(&nw,2,2,-1,3);
Whichwouldgivethefollowingpathlist:
FullCodelistings
Graph.h
#pragmaonce
#include“PriorityQueue.h”
#include<vector>
#include<memory>
classEdge
{
public:
intv,w;
doublewt;
Edge(intv=-1,intw=-1,doublewt=0.0):
v(v),w(w),wt(wt){}
};
typedefstd::shared_ptr<Edge>EdgePtr;
template<classEdge>
classDenseGRAPH
{
private:
intVcnt,Ecnt;
booldigraph;
std::vector<std::vector<EdgePtr>>adj;
public:
DenseGRAPH(intV,booldigraph=false):
adj(V),Vcnt(V),Ecnt(0),digraph(digraph)
{
for(inti=0;i<V;i++){
adj[i].resize(V);
}
}
intV()const{returnVcnt;}
intE()const{returnEcnt;}
booldirected()const{returndigraph;}
voidinsert(EdgePtre)
{
intv=e->v;
intw=e->w;
if(adj[v][w]==0)Ecnt++;
adj[v][w]=e;
if(!digraph)
adj[w][v]=e;
}
voidremove(EdgePtre)
{
intv=e->v;intw=e->w;
if(adj[v][w]!=0)Ecnt—;
adj[v][w]=0;
if(!digraph)adj[w][v]=0;
}
EdgePtredge(intv,intw)const
{
returnadj[v][w];
}
classadjIterator;
friendclassadjIterator;
};
template<classEdge>
classDenseGRAPH<Edge>::adjIterator
{
private:
constDenseGRAPH&G;
inti,v;
public:
adjIterator(constDenseGRAPH<Edge>&G,intv):
G(G),v(v),i(0){}
EdgePtrbeg(){i=-1;returnnxt();}
EdgePtrnxt()
{
for(i++;i<G.V();i++)
if(G.edge(v,i))
returnG.adj[v][i];
return0;
}
boolend()const{returni>=G.V();}
};
PriorityQueue.h
#include<memory>
#include<vector>
template<classkeyType>
classPriorityQueue
{
private:
intd,N;
std::vector<int>pq,qp;
conststd::vector<keyType>&a;
voidExchange(inti,intj)
{
intt=pq[i];
pq[i]=pq[j];
pq[j]=t;
qp[pq[i]]=i;
qp[pq[j]]=j;
}
voidfixUp(intk)
{
while(k>1&&a[pq[(k+d-2)/d]]>a[pq[k]])
{
Exchange(k,(k+d-2)/d);
k=(k+d-2)/d;
}
}
voidfixDown(intk,intN)
{
intj;
while((j=d*(k-1)+2)<=N)
{
for(inti=j+1;i<j+d&&i<=N;i++)
if(a[pq[j]]>a[pq[i]])
j=i;
if(!(a[pq[k]]>a[pq[j]]))
break;
Exchange(k,j);
k=j;
}
}
public:
PriorityQueue(intN,conststd::vector<keyType>&a,intd=3):
a(a),pq(N+1,0),qp(N+1,0),N(0),d(d){}
intempty()const{returnN==0;}
voidinsert(intv)
{
pq[++N]=v;
qp[v]=N;
fixUp(N);
}
intgetmin()
{
Exchange(1,N);
fixDown(1,N-1);
returnpq[N—];
}
voidlower(intk)
{
fixUp(qp[k]);
}
};
Network.h
#pragmaonce
#include“Graph.h”
#include<vector>
#include<map>
#include<string>
typedefstd::vector<int>Path;
typedefstd::vector<Path>PathSet;
typedefstd::vector<Path>::const_iteratorPaths;
template<classT>
classLink
{
private:
Tfrom;
Tto;
doubleweight;
public:
Link(constT&from,constT&to,constdouble&weight=1.0):
from(from),to(to),weight(weight){}
constT&From()const
{returnfrom;}
constT&To()const
{returnto;}
constdouble&Weight()const
{returnweight;}
};
classNetwork
{
private:
DenseGRAPH<Edge>*graph;
PathSetpathset;
std::map<std::string,int>label2index;
std::map<int,std::string>index2label;
public:
Network(){}
Network(conststd::vector<Link<std::string>>&links,
constbool&bi=false);
Network(conststd::vector<Link<int>>&links,
constbool&bi=false);
~Network();
voidAddPath(constPath&p);
voidShowPaths()const;
intNodeIndex(conststd::string&label)const;
std::vector<int>GetAdjNodeIDs(constint&n)const;
};
Network.cpp
#include“Network.h”
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<set>
#include<stack>
Network::~Network()
{
if(graph!=NULL){
deletegraph;
graph=NULL;
}
}
Network::Network(conststd::vector<Link<std::string>>&links,
constbool&bi_connected)
{
typedefstd::vector<Link<std::string>>::const_iteratorv_iter;
label2index.clear();
index2label.clear();
std::set<std::string>unique_id_str;
for(v_iterit=links.begin();it!=links.end();++it){
constLink<std::string>l=(*it);
std::stringfirst=l.From();
std::stringsecond=l.To();
unique_id_str.insert(first);
unique_id_str.insert(second);
}
inti=0;
for(std::set<std::string>::const_iteratorit=unique_id_str.begin();
it!=unique_id_str.end();
++it)
{
std::stringlabel=(*it);
index2label[i]=label;
label2index[label]=i;
i++;
}
std::set<int>unique_ids;
constintnodes=unique_id_str.size();
graph=newDenseGRAPH<Edge>(nodes+1,!bi_connected);
for(v_iterit=links.begin();it!=links.end();++it)
{
constLink<std::string>l=(*it);
std::stringfirst=l.From();
std::stringsecond=l.To();
ints=label2index.find(first)->second;
intd=label2index.find(second)->second;
doubleweight=l.Weight();
EdgePtre(newEdge(s,d,weight));
graph->insert(e);
}
}
Network::Network(conststd::vector<Link<int>>&links,
constbool&bi_connected)
{
typedefstd::vector<Link<int>>::const_iteratorv_iter;
label2index.clear();
index2label.clear();
std::set<std::string>unique_id_str;
for(v_iterit=links.begin();it!=links.end();++it)
{
constLink<int>l=(*it);
intfirst=l.From();
intsecond=l.To();
std::stringstrFirst=static_cast<std::ostringstream*>(
&(std::ostringstream()<<first))->str();
std::stringstrSecond=static_cast<std::ostringstream*>(
&(std::ostringstream()<<second))->str();
unique_id_str.insert(strFirst);
unique_id_str.insert(strSecond);
}
inti=0;
for(std::set<std::string>::const_iteratorit=unique_id_str.begin();
it!=unique_id_str.end();
++it)
{
std::stringlabel=(*it);
index2label[i]=label;
label2index[label]=i;
i++;
}
std::set<int>unique_ids;
constintnodes=unique_id_str.size();
graph=newDenseGRAPH<Edge>(nodes+1,!bi_connected);
for(v_iterit=links.begin();it!=links.end();++it){
constLink<int>l=(*it);
intfirst=l.From();
intsecond=l.To();
std::stringstrFirst=static_cast<std::ostringstream*>(
&(std::ostringstream()<<first))->str();
std::stringstrSecond=static_cast<std::ostringstream*>(
&(std::ostringstream()<<second))->str();
ints=label2index.find(strFirst)->second;
intd=label2index.find(strSecond)->second;
doubleweight=l.Weight();
EdgePtre(newEdge(s,d,weight));
graph->insert(e);
}
}
voidNetwork::AddPath(constPath&p)
{
pathset.push_back(p);
}
//Getthesetofnodesadjacenttothisnode
std::vector<int>Network::GetAdjNodeIDs(constint&n)const
{
std::vector<int>adj;
DenseGRAPH<Edge>::adjIteratorA(*graph,n);
for(EdgePtre=A.beg();!A.end();e=A.nxt()){
constints=e->v;
constintd=e->w;
if(!graph->directed()&&(s==n||d==n)){
adj.push_back(s==n?d:s);
}
elseif(s==n){
adj.push_back(d);
}
}
returnadj;
}
//Outputthesetofpathsfound
voidNetwork::ShowPaths()const
{
for(Pathspaths=pathset.begin();
paths!=pathset.end();
++paths)
{
Pathpath=*paths;
for(std::vector<int>::const_iteratorit=path.begin();
it!=path.end();
++it)
{
intn=(*it);
std::stringnode_str=index2label.find(n)->second;
std::cout<<node_str<<”“;
}
std::cout<<std::endl;
}
}
intNetwork::NodeIndex(conststd::string&label)const
{
intn=label2index.find(label)->second;
returnn;
}
Algorithm.h
#include“Network.h”
#ifndefALGORITHM_H
#defineALGORITHM_H
classAlgorithm
{
public:
Algorithm(){}
Algorithm(Network*network);
voidGetAllPaths(Network*network,
conststd::string&start,
conststd::string&target,
constint&max_hops,
constint&min_hops);
voidGetAllPaths(Network*network,
constint&start,
constint&target,
constint&max_hops,
constint&min_hops);
private:
voidDepthFirst(Network*network,
Path&visited,
constint&end,
constint&max_hops,
constint&min_hops);
voidSetNetwork(Network*network);
boolContainsNode(Path&nodes,constint&node);
Network*nw;
intstartNodeId;
};
#endif/*ALGORITHM_H*/
Algorithm.cpp
#include<iostream>
#include<sstream>
#include<iterator>
#include“Algorithm.h”
Algorithm::Algorithm(Network*network)
{
SetNetwork(network);
}
voidAlgorithm::SetNetwork(Network*network)
{
nw=network;
}
//Algorithmtorecursivelysearchforallpathsbetween
//chosensource-destinationnodes
voidAlgorithm::DepthFirst(
Network*network,
Path&visited,
constint&end,
constint&max_hops,
constint&min_hops)
{
constintback=visited.back();
std::vector<int>adjNode=network->GetAdjNodeIDs(back);
//Examineadjacentnodes
for(std::vector<int>::const_iteratornode_it=adjNode.begin();
node_it!=adjNode.end();
node_it++)
{
constintnode=(*node_it);
boolstartEqualTarget=ContainsNode(visited,node)&&
startNodeId==end&&
node==startNodeId;
if(ContainsNode(visited,node)&&!startEqualTarget)
continue;
if(node==end)
{
visited.push_back(*node_it);
//Gethopcountforthispath
constintsize=(int)visited.size();
constinthops=size-1;
if((max_hops<1||hops<=max_hops)&&hops>=min_hops)
{
Pathpath(visited.begin(),visited.begin()+size);
network->AddPath(path);
}
visited.erase(visited.begin()+hops);
break;
}
}
//inbreadth-first,recursionneedstocomeaftervisitingadjacentnodes
for(std::vector<int>::const_iteratornode_it=adjNode.begin();
node_it!=adjNode.end();
node_it++)
{
intnode=(*node_it);
if(ContainsNode(visited,node)||node==end)
continue;
visited.push_back(node);
DepthFirst(network,visited,end,max_hops,min_hops);
intn=(int)visited.size()-1;
visited.erase(visited.begin()+n);
}
}
boolAlgorithm::ContainsNode(Path&nodes,constint&node)
{
for(std::vector<int>::const_iteratornodes_it=nodes.begin();
nodes_it!=nodes.end();nodes_it++)
{
if((*nodes_it)==node)returntrue;
}
returnfalse;
}
voidAlgorithm::GetAllPaths(Network*network,
conststd::string&start,
conststd::string&target,
constint&max_hops,
constint&min_hops)
{
ints=network->NodeIndex(start);
intd=network->NodeIndex(target);
startNodeId=s;
Pathvisited;
visited.push_back(s);
DepthFirst(network,visited,d,max_hops,min_hops);
}
voidAlgorithm::GetAllPaths(Network*network,
constint&start,
constint&target,
constint&max_hops,
constint&min_hops)
{
std::stringstrFirst=static_cast<std::ostringstream*>(
&(std::ostringstream()<<start))->str();
std::stringstrSecond=static_cast<std::ostringstream*>(
&(std::ostringstream()<<target))->str();
ints=network->NodeIndex(strFirst);
intd=network->NodeIndex(strSecond);
startNodeId=s;
Pathvisited;
ChapterSummary
Tosummarizethismasterlesson,youhave:
Implementedarecursivealgorithmtosolvethe“allsimplepaths”probleminC++.
Madefurtherrefinementsandenhancementstopresenttheresultsindifferentways,suchaslabelingnodeswithmeaningfulnames.
Appliedfurtheradjustmentssuchasfindingallpathsgoingoutfromandreturningtothesamenodeorhowtoconstrainthepathsfoundtoamaximumnumberofhops.
Chapter10:TheStandardTemplateLibrary
10.1Introduction
TheStandardTemplateLibrary(STL)asintroducedbyHewlettPackardtotheC++communitywasrevolutionary.Why?BecauseitgaveC++adimensionandcapabilityitpreviouslydidnothave.Beforethelibrary’sinception,programmersfocusedonclassesandObjectOrientedProgramming(OOP)inanattempttomodelreal-worldbehaviour.
ClassesandOOPhelpedboostthepopularityofC++immensely.Butbythemselves,classeslackedefficiencyinhandlingalgorithmsanddatastructuresinagenericway.AndOOPwasnevertheholygrailitwasfrequentlymadeouttobe–inmanycasesitdidnottrulypromotecodereuseandmodularityUntiltemplate-basedprogrammingwasincorporatedintoC++,developersandcompanieswouldhand-crafttheirowndatastructuresandalgorithmsbywritingcustomlibrariesandroutinesfromscratch.
Nowlet’sfastforwardtothedevelopmentofanewfeaturetoreceiveseriousattentionbyAlexanderStepanov(seehttp://en.wikipedia.org/wiki/Alexander_Stepanov)andotherresearchers:templates.Thesefacilitatedalibraryframeworkthatatlastallowedalgorithmsanddatastructures(containers)toworkwitharbitrarydatatypes.
STLnowcomprisesoffivekindsofcomponents:containers,iterators,algorithms,functionobjects(functors)andallocators.
Usingthesecomponentscandramaticallymakeyourprogramssolidandrobustinmanyways.Herearejustafewexamples:
1.Arraysofanydatatypewhichcanarbitrarilygroworshrinkandincorporateautomaticmemorymanagementasneeded.
2.Accesstoalargenumberofpowerfulalgorithmsforsearchingandsortingdata.
3.Setoperationsandothernumericaltransformations.
4.Customizedcodethatwillfrequentlyoutperformcodebuiltfromscratch.
ItisnoexaggerationtosaythatSTLhasmadepossiblearevolutioninthewaysoftwaretasksgetdone.IthasbecomeanintegralpartofanyC++programmer’sarsenaloftechniquesbyallowingunwieldycustomcodetobereplacedwithprogramsthatcanbeappliedacrossawiderangeofgenerictasks.STLconceptsthemselveswillbediscussedinmoredetailinthefollowingsections.
10.2ChoosinganSTLContainer
StandardSTLcontainerscanbegeneralizedasbelongingto:
Standardsequencecontainers(std::vector,std::list,std::dequeandstd::string)
Standardassociativecontainers(std::map,std::set,std::multimap,std::multiset)
GiventhenumberofdifferentSTLcontainersatyourdisposal,itisworthconsideringsomerulesofthumbforchoosingaSTLcontainerappropriatetoyourparticularscenario.Ifyouanticipateyourcollectionneedingtoholdjustafewitems,thenitisprobablyfinetousestd::vectorregardlessofwhatyouintendtodo.std::vectorremainsagoodchoiceifyouintendtomaintainacollectionofitemsthatwillchangeverylittlewhencomparedtothenumberoftimestheyareaccessed.Furthermore,thestd::vectorcontainerwillprovideeverythingyouexpectfromanarray,allowingforastraightforwardcollectionofitemsthatcanberandomlyaccessedoraddedandremoved(atthebackofthecontainer)inadynamicandtypesafeway:
#include<vector>
#include<string>
#include<algorithm>
#include<iostream>
#include<iterator>
intmain()
{
constintvalues[]={1,11,111,1111};
constsize_tsize=sizeof(values)/sizeof(values[0]);
std::vector<int>v(values,values+size);
v.push_back(11111);
std::cout<<“vector:“;
std::copy(v.begin(),
v.end(),
std::ostream_iterator<int>(std::cout,””));
return0;
}
Thisgivesthefollowingoutput:
Similarinrespectstostd::vectorisstd::deque(pronounced“deck”),shortfordouble-endedqueue.std::dequeallowsinsertionsateitherendofthecollection,iepush_backandpush_front,thusallowingyoutomodelaqueueaswellasastack.Butlet’smoveontoentirelynewground.Itmaybethecasethattheneedforrandomaccessisnotsoimportant,butinsteadyouneedacontainerthatcanefficientlyinsertorremoveitemsatarbitrarypositions.Anassociativecontainersuchasstd::mapwouldbeadefiniteno-noandstd::vectorwouldbemorecomputationallyexpensiveforinsertingdataitems,especiallyinthemiddle.However,std::listisidealforthiskindoftask.ApracticalapplicationcouldbethetrackingofallopenWindowsobjects,ormaintainingalistofprocessesrunninginthebackground,thethekindofsituationswherebythenumberofinsertionsanddeletionsislikelytobesubstantial.Auniquefeatureofstd::listisitsabilitytosplice:thatis,transferelementsfromonestd::listtoanother.
#include<list>
#include<string>
#include<algorithm>
#include<iostream>
#include<iterator>
intmain()
{
typedefstd::list<std::string>::iteratorlist_it;
std::list<std::string>list1;
std::list<std::string>list2;
list1.push_front(“A”);
list1.push_back(“E”);
list1.push_back(“F”);
list1.insert(++list1.begin(),“B”);
list2.push_front(“C”);
list2.push_front(“D”);
//Transfer(splice)elementsfromlist2intolist1.
list_itit=list1.begin();
std::advance(it,2);
list1.splice(it,list2);
std::cout<<“list1:“;
std::copy(list1.begin(),list1.end(),
std::ostream_iterator<std::string>(std::cout,””));
return0;
}
Theoutputisasshown:
Sothestd::vectorandstd::listcontainersareexamplesofsequencecontainerusesthatimplementdataitemstorage.Othersequencecontainersinthiscategoryincludestd::deque,std::arrayandstd::forward_list.Ineachcase,allthecontaineritemsmustbelongtothesamedatatype.Therecanneverbeamixofdatatypessuchasintorstd::stringwithinthesamecontainer.
Sowhatotherusesdowehaveforthesecontainers?Well,theremaybetimeswhenyouwanttoguaranteethatthecontainerstoresuniquevaluesonlyandpreventstheinsertionofduplicateitems.Inapracticalsettingthismightincludemaintainingalistofstudentsenrolledonacourse,oracollectionofuniquesocialsecuritynumbers.Thereforeanassociativecontainersuchasstd::setwouldbeareliablemeansofguaranteeingtheuniquenessofinserteddataitems,providedtheorderingoftheseuniqueitemsisofnoimportance.
Thestd::setcontainerpermitseasyinsertionandlookupofuniquevaluesbywayoftheinsertandfindmethodsrespectively.Thatisbecauseinsertwillnotaddannewitemifthevalueofthatitemisalreadyintheset,andnewelementswheninsertedareautomaticallysortedinascendingorder.Furthermorestd::set,likeotherassociativecontainers,hascertainperformanceguarantees:thetimerequiredtobothfindandinsertanitemisproportionaltologN,whereNisthenumberofitems.
#include<set>
#include<string>
#include<algorithm>
#include<iostream>
#include<iterator>
intmain()
{
//Initialisesamplesets
intvals1[]={4,2,1};
intvals2[]={1,7,5,2};
intsize1=sizeof(vals1)/sizeof(vals1[0]);
intsize2=sizeof(vals2)/sizeof(vals2[0]);
std::set<int>s1(vals1,vals1+size1);
std::set<int>s2(vals2,vals2+size2);
//Findtheintersection,providingitwithanoutputiterator
std::set<int>intersect;
set_intersection(s1.begin(),
s1.end(),
s2.begin(),
s2.end(),
std::inserter(intersect,intersect.begin()));
std::cout<<“intersection:“;
std::copy(intersect.begin(),
intersect.end(),
std::ostream_iterator<int>(std::cout,””));
return0;
}
Theoutputisasshown:
Butwhatifyoucareaboutthepositionofinserteditems?Inthiscasestd::setwouldnotbeappropriate.AndinfactthereisnoSTLcontainerthatdoesthisatthemoment,althoughstd::setcomestheclosestbecauseoneofthetypesusedtoparameterizethestd::settemplateisacomparisontype.Thiscomparisontypedeterminestheorderinwhichnewitemsareinserted.Whenthisisnotspecified,itdefaultstoaless-thancomparison.Sotomaintainasetofuniqueintegeritemspreservedintheorderinwhichtheywereinsertedsuchas{101,111,110,001,101},std::set(whileensuringtheuniquenessofthenumbersinserted)wouldnotmaintainthisparticularordering.Incaseslikethis,thesolutionwilllikelytoinvolvemorecomplexapproachessuchascreatingyourownclasstocombinecontainertypesinyourownclass.Youmightwantstd::setforstoringuniqueitems,andstd::vectorforkeepingtrackoftheorderinwhichtheywereinserted:
#include<set>
#include<vector>
#include<string>
#include<algorithm>
#include<iostream>
typedefstd::set<std::string>::iteratorIter;
classOrderedSet
{
private:
std::set<std::string>setItems;
std::vector<Iter>setOrder;
public:
OrderedSet(){}
voidinsert(conststd::string&item)
{
setItems.insert(item);
Iterit=setItems.find(item);
setOrder.push_back(it);
}
intorder(conststd::string&item)
{
Iterit=setItems.find(item);
std::vector<Iter>::iteratororder_it=
std::find(setOrder.begin(),setOrder.end(),it);
returnorder_it-setOrder.begin();
}
};
intmain()
{
OrderedSetorderedSet;
orderedSet.insert(“Bravo”);
orderedSet.insert(“Alpha”);
orderedSet.insert(“Charlie”);
orderedSet.insert(“Delta”);
std::cout<<“Order=”
<<orderedSet.order(“Alpha”)
<<std::endl;
return0;
}
Movingonnow,thestd::mapclassallowsustostoreandobtaindataitemsthataremappedtoakey.Thisisusefulwhenyouneedameansofquicklylookingupadataitemassociatedwithakeyvalue.Thestd::mapcontainer,whileallowingforefficientinsertionandretrievalaswithstd::set,differsfromthestd::setinthatstd::mapstorespairs,ratherthansinglevalues:std::map<Key,T>,whereKeyisusedtoreferencethedataobjectT.
Astd::set<Key>isanorderedcollectionofkeys,whileastd::map<Key,T>isacollectionofdataobjectsthatcanbereferencedbyakey.Acommonwaytousethestd::map<Key,T>isasanassociative-likearray,wherebytheusercanlookupagivendataelementusingaspecifickey.Superficiallythislooksalotlikeusingaconventionalarray,buttheimportantdifferenceisthattheindexusedtoretrievethearrayelementnolongerhastobeaninteger-likevariable,butinsteadcanbeANYtype.
Thesecretisthatstd::map<Key,T>definestheoperator[]functiontoperformthelookuptofindthedataelementassociatedwiththegivenkey.Ifamatchisfound,thefunctionreturnsthereferencetoitsmappedvalue;otherwiseanewdataitemisinsertedwiththatkeyandthefunctionreturnsthereferencetoitsnewlymappedvalue.Toputitmoresimply,std::mapisan“addorupdate”function(hat-tip:ScottMeyers).It’sworthpointingoutasubtleefficiencyissuehere:ifyou’readdingbrandnewitems,thentheinsertmethodispreferabletousingoperator[]sinceitwillonlycreatenewitemswithoutreplacingexistingones.Butifyouneedtoupdateanexistingiteminyourstd::map,thentheoperator[]wouldbethepreferredoption.
#include<map>
#include<string>
#include<algorithm>
#include<iostream>
intmain()
{
typedefstd::map<std::string,int>ResultsMap;
ResultsMaprmap;
rmap.insert(ResultsMap::value_type(“Physics”,88));
rmap.insert(ResultsMap::value_type(“Math”,67));
rmap.insert(ResultsMap::value_type(“English”,67));
//operator[]usage(“addorupdate”)
rmap[“Math”]=90;
std::for_each(rmap.begin(),rmap.end(),
[](std::pair<conststd::string,int>&map)
{
std::cout<<map.first<<””<<map.second<<std::endl;
});
return0;
}
Nowlet’sgoonestepfarther.Thestd::multimapcontaineroffersawayofmappinggroupsofdataitemstoakey.Thisisincontrasttothestd::mapbeingaone-to-onemappingbetweenkeysandtheirassociateddataitems.Ratherthanusingthefindmethodtolookupasingleitemassociatedwithitskey,theequal_rangemethodismorelikelytobeusedtoobtaintherangeofitemsassociatedwiththegroupanditeratethroughthese:
#include<map>
#include<string>
#include<algorithm>
#include<iostream>
intmain()
{
typedefstd::multimap<std::string,int>ResultsMap;
typedefstd::multimap<std::string,int>::const_iteratorResultsIter;
ResultsMaprmap;
rmap.insert(ResultsMap::value_type(“Geography”,87));
rmap.insert(ResultsMap::value_type(“Physics”,57));
rmap.insert(ResultsMap::value_type(“Physics”,69));
rmap.insert(ResultsMap::value_type(“Physics”,45));
conststd::stringsubject=“Physics”;
conststd::pair<ResultsIter,ResultsIter>group=
rmap.equal_range(subject);
std::cout<<subject<<”=>”;
std::for_each(group.first,group.second,
[](conststd::pair<conststd::string,int>&gr)
{
std::cout<<gr.second<<”“;
});
return0;
}
10.3CommonSTLAlgorithmsandtheirUsage
Replacingthetraditionalforloop
Besidesimprovedreadability,thereexistsomeniceadvantagestousingstd::for_eachinplaceofthetraditionalforloopforiteratingoversequences.Itwillallowyoutowriteanalgorithmontopofthestd::for_eachthatwillworkwithanyiteratorandreducestheriskoftypo-introducedbugs.Specificallyitappliesafunctionobject(functor)toeachelementwithinarangeandreturnsacopyofthefunctionobjectafterithasbeenappliedtoalloftheelementsintherange.Hereisanexampleofusingstd::for_eachtoappendasetofstringscontainedinastd::vectorcontainer.Thiscollectseach‘txt’itemstoredintheinthestd::vectorof‘Token’structs,appendingthemtothe‘txt’memberoftheAppenderfunctor,whichhastheWordmemberfunctiontoreturnthecompleteconcatenatedstring:
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
structToken
{
std::stringtxt;
Token(std::strings):txt(s){}
};
classAppend
{
private:
std::stringtxt;
public:
Append():txt(””){}
voidoperator()(constToken&x)
{txt.append(x.txt+””);}
std::stringWord()const
{returntxt;}
};
intmain()
{
std::vector<Token>tokens;
tokens.push_back(Token(“Hello”));
tokens.push_back(Token(“and”));
tokens.push_back(Token(“welcome!”));
std::cout<<std::for_each(
tokens.begin(),
tokens.end(),
Append()).Word()
<<std::endl;
return0;
}
Sortingandsearching
TheSTLsortalgorithmoperatesonaselectedrangeofcontainerelements,arrangingthemintoasortedorderaccordingtoauser-definedpredicatefunctionobjectthatdefineshowelementsarecompared.Hereisanexampleofhowtoreadalistofobjects,sortthemandoutputthemtothescreen:
#include<iostream>
#include<ostream>
#include<vector>
#include<algorithm>
#include<memory>
//Abaseclasstohold,returnandoutputavalue
classItem
{
intval;
public:
Item(constint&val):val(val){}
virtualchar*ItemName()=0;
intGetVal(){returnval;}
voidOutputToStream(std::ostream&os)
{
os<<ItemName()<<“\t”<<val<<std::endl;
}
};
//Derivedclass-“old”
classOldItem:publicItem
{
public:
OldItem(constint&val):Item(val){}
char*ItemName(){return(char*)“Old:“;}
};
//Derivedclass-“new”
classNewItem:publicItem
{
public:
NewItem(constint&val):Item(val){}
char*ItemName()
{
return(char*)“New:“;
}
};
//Autopointertode-allocateItemobjectsincaseyouareforgetful.
typedefstd::unique_ptr<Item>ItemPtr;
//Specifyhowwewanttosortthedata:
structAscending
{
booloperator()(constItemPtr&start,constItemPtr&end)
{
returnstart->GetVal()<end->GetVal();
}
};
intmain()
{
std::vector<ItemPtr>itemVector;
itemVector.push_back(static_cast<ItemPtr>(newOldItem(43)));
itemVector.push_back(static_cast<ItemPtr>(newNewItem(34)));
itemVector.push_back(static_cast<ItemPtr>(newNewItem(443)));
itemVector.push_back(static_cast<ItemPtr>(newOldItem(433)));
itemVector.push_back(static_cast<ItemPtr>(newOldItem(343)));
//Sortvaluesinascendingorder…
std::sort(
itemVector.begin(),
itemVector.end(),
Ascending());
std::for_each(itemVector.begin(),itemVector.end(),[](ItemPtr&item)
{
item->OutputToStream(std::cout);
});
return0;
}
Thisgivesthefollowingoutput:
Infact,STLhasanamplefundofalgorithmstohelpyoulocateitemsstoredincontainers.Thesecanbespeedy,typicallyoforderlogNforbinary_searchorequal_range.Andcontainerssuchasstd::maparedesignedtokeepthedatasortedinaself-balancingbinarysearchtreeastheyareinserted,whichallowscertainperformanceguaranteeswhenitcomestolookingtheseitemsup.
Copying,Filling,ReplacingandRemoving
Manytasksboildowntobeingabletocopyobjectsaroundorremovethem.Inputandoutputiteratorsareoftenanecessarymeanstoaccomplishthissinceweneedtospecifywhatdatawewanttoworkwithandthenwheretooutputtheresult.
Thisexamplecopiestheelementsofastd::vectorcontainertoastandardoutputwhereostream_iteratorisanadaptorofanoutputiteratortype.Theiteratoroperationsaredefinedsuchthatassigningthroughtheiteratorprintstostandardoutput,witheachprintfollowedbyawhitespacecharacter:
#include<vector>
#include<algorithm>
#include<iterator>
#include<iostream>
classdescending
{
public:
booloperator()(intx,inty)const
{returnx>y;}
};
intmain()
{
intvalues[]={100,101,-2,23,1010,44,55,3};
constsize_tsize=sizeof(values)/sizeof(values[0]);
std::vector<int>v(values,values+size);
std::sort(v.begin(),v.end(),descending());
std::copy(v.begin(),
v.end(),
std::ostream_iterator<int>(std::cout,””));
return0;
}
Thisgivesthefollowingoutput:
MutatingandTransforming
Thestd::transformalgorithmappliesafunctiontoeachobjectinagivenrangeandcopiestheresultofthefunctiontoanewrangewhichispointedtobyanoutputiterator.Thisexampleshowshowtouseafunctionadaptorthattakestwoinputrangesandincreasesthevaluesofthefirstrangebythesecondrange.
#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
template<typenameT,typenameInputIterator>
voidPrint(std::ostream&ostr,
InputIteratoritbegin,
InputIteratoritend,
conststd::string&delimiter)
{
std::copy(itbegin,itend,
std::ostream_iterator<T>(ostr,delimiter.c_str()));
}
template<classT>
classIncreaseByVal
{
public:
Toperator()(Ta,Tb)const
{returna+b;}
};
intmain()
{
intx[]={1,2,3,4,5};
inty[]={1,2,3,4,5};
std::transform(x,
x+sizeof(x)/sizeof(x[0]),
y,
x,
IncreaseByVal<int>());
Print<int,int[]>(std::cout,
10.4MoreSTLTips,TricksandExamples
SetAlgorithms
Hereisapracticalexample:usingunionandsetintersectiontocalculatetheJaccardIndex,ameansofmeasuringthesimilarityand/ordiversityofsamplesets.
#include<iostream>
#include<vector>
#include<algorithm>
#include<iterator>
#include<string>
classToken
{
private:
std::stringstr;
public:
Token(){}
Token(std::stringval):str(val){}
std::stringWord()const
{returnstr;}
};
structAscending
{
booloperator()(constToken&start,constToken&end)
{
returnstart.Word()<end.Word();
}
};
intmain()
{
std::stringstr1[]={“The”,“crazy”,“cat”,“sat”,“on”,“the”,“mat”};
std::stringstr2[]={“The”,“cat”,“sat”,“on”,“the”,“red”,“mat”};
std::vector<Token>::iteratortok_intersect,tok_union;
intsize1=sizeof(str1)/sizeof(str1[0]);
intsize2=sizeof(str2)/sizeof(str2[0]);
std::vector<Token>tokens1(str1,str1+size1);
std::vector<Token>tokens2(str2,str2+size2);
std::vector<Token>tokens(size1+size2);
std::sort(tokens1.begin(),tokens1.end(),Ascending());
std::sort(tokens2.begin(),tokens2.end(),Ascending());
tok_intersect=std::set_intersection(
tokens1.begin(),
tokens1.end(),
tokens2.begin(),
tokens2.end(),
tokens.begin(),
Ascending());
intintersect_size=int(tok_intersect-tokens.begin());
tok_union=std::set_union(
tokens1.begin(),
tokens1.end(),
tokens2.begin(),
tokens2.end(),
tokens.begin(),
Ascending());
intunion_size=int(tok_union-tokens.begin());
doubleJaccardIndex=(double)intersect_size/(double)union_size;
std::cout<<“JaccardIndex=”
<<JaccardIndex
<<std::endl;
return0;
}
Tokenizingandsplittingstrings
Tokenizationistheprocessofbreakinguptextintoseparateitems(‘tokens’)accordingtosomedelimiter,typicallyawhitespaceorotherpunctuationcharacter.
Thisexampletakesthesampletextandinsertseachtokenizedwordintoavectorarray:
#include<iostream>
#include<string>
#include<sstream>
#include<algorithm>
#include<iterator>
#include<vector>
template<typenameT,typenameInputIterator>
voidPrint(std::ostream&ostr,
InputIteratoritbegin,
InputIteratoritend,
conststd::string&delimiter)
{
std::copy(itbegin,
itend,
std::ostream_iterator<T>(ostr,delimiter.c_str()));
}
intmain()
{
typedefstd::vector<std::string>::iteratorstr_iter;
std::stringtxt=“Thecatsatonthemat…”;
std::istringstreamiss(txt);
std::vector<std::string>tokens;
std::copy(
std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter<std::vector<std::string>>(tokens));
//Printthevectorstd::stringitems
Print<std::string,str_iter>(std::cout,
tokens.begin(),
tokens.end(),
””);
}
Thisgivesthetokenizedoutputasshown:
Usingcontainersasmulti-dimensionalarrays
Nowhereisanexampleofhowtoinitializeandsetvaluesfromathree-dimensionalarrayimplementedusingthestd::vectorcontainer.TheSTLcontainerapproachhastheaddedbenefitthatunlikepointerstoobjects,thememoryusedincreatingthearraywillbeautomaticallyremovedwhengoingoutofscope.Justusestandardarraynotationtoretrievearrayvalues.Forexample:
#include<vector>
#include<stdlib.h>
constsize_tHEIGHT=5;
constsize_tWIDTH=3;
constsize_tDEPTH=7;
intmain()
{
std::vector<std::vector<std::vector<double>>>array3D;
//Allocatearraydimensionsbydoingtheresizing:HEIGHTxWIDTH
array3D.resize(HEIGHT);
for(inti=0;i<static_cast<int>(HEIGHT);++i)
{
array3D[i].resize(WIDTH);
for(intj=0;j<static_cast<int>(WIDTH);++j)
{
array3D[i][j].resize(DEPTH);
}
}
//Initializewithsomevalues
array3D[1][2][5]=3.2;
array3D[2][0][3]=4.1;
return0;
}
Howtosortastd::map
Bydefinitionyoucannotsortastd::mapcontainerbyvalue,sinceastd::mapsortsitselementsbykey.Howeverwecangetaroundthisbydumpingstd::mapkey-valuepairsintoastd::vectorfirst,thensortingthatstd::vectorwithaless-thanfunctorafterwards.Thisisillustratedbythefollowingexamplewhichcreatesmappingsbetweenstd::pairanddoublepairs.TheSTLmaptocreateaone-to-onemappingbetweenapairoflinknodeIDs(usingastd::pair)andtheweightvalueconnectingthesenodes(usingadouble):
std::map<std::pair<int,int>,double>link_map;
Thefirststepistophysicallycreatethemappings:
link_map[std::pair<int,int>(0,1)]=1.1;
link_map[std::pair<int,int>(1,2)]=0.1;
link_map[std::pair<int,int>(2,3)]=6.2;
link_map[std::pair<int,int>(3,4)]=3.4;
link_map[std::pair<int,int>(4,5)]=5.7;
link_map[std::pair<int,int>(5,6)]=2.2;
link_map[std::pair<int,int>(0,8)]=1.8;
Thenthestd::pairnodeIDsandtheirrespectiveweightsareprintedasfollows.Noticethatsincethestd::pairisusedasthemapkey,thestd::mapitemsareautomaticallysortedaccordingtothis,nottheweightvalues:
011.1
081.8
120.1
236.2
343.4
455.7
562.2
Nowyouneedsomemeansofobtainingthemappingssotheyaresortedaccordingtotheirweights,nottheirstd::pairkeyvalues.Sinceyoucan’tsortthestd::mapitself,insteaduseafunctiontoinsertthesemappingsintoastd::vectorandthensortthestd::vectoritems.Thisinvolvesanadditionalfunctorwhichdefineshowtheitemsaresorted:
template<classT>
structless_second:std::binary_function<T,T,bool>
{
inlinebooloperator()(constT&lhs,constT&rhs)
{returnlhs.second<rhs.second;}
};
//Initializeandsortvectorwithdata_titemsfromlink_map
std::vector<data_t>sorted_edges()
{
std::vector<data_t>vec(link_map.begin(),link_map.end());
std::sort(vec.begin(),vec.end(),less_second<data_t>());
returnvec;
}
Alsorequiredisdata_t,anadditionaltypedefusedtodefinethemapping:
typedefstd::pair<std::pair<int,int>,double>data_t;
Sothatwhenweprintoutthevectorofdata_titems,wecanseethattheyaresortedaccordingtothesecondmapparameter(double),andnotthestd::pairrepresentingnodeIDs:
Fullcodelisting
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
typedefstd::pair<std::pair<int,int>,double>data_t;
std::map<std::pair<int,int>,double>link_map;
std::map<std::pair<int,int>,double>::iteratorlink_it;
template<classT>
structless_second:std::binary_function<T,T,bool>
{
inlinebooloperator()(constT&lhs,constT&rhs)
{
returnlhs.second<rhs.second;
}
};
//Initializeandsortvectorwithdata_titemsfromlink_map
std::vector<data_t>sorted_edges()
{
std::vector<data_t>vec(
link_map.begin(),
link_map.end());
std::sort(
vec.begin(),
vec.end(),
less_second<data_t>());
returnvec;
}
voidPrintLinkMap()
{
std::cout<<std::endl;
std::for_each(link_map.begin(),link_map.end(),
[](std::pair<conststd::pair<int,int>,double>&lmap)
{
std::cout<<lmap.first.first<<””
<<lmap.first.second<<””
<<lmap.second<<std::endl;
});
}
voidPrintSortedEdges(conststd::vector<data_t>&vec)
{
std::cout<<std::endl;
std::for_each(vec.begin(),vec.end(),[](constdata_t&dt)
{
std::cout<<dt.first.first<<””
<<dt.first.second<<””
<<dt.second
<<std::endl;
});
}
intmain()
{
//Createmappingbetweennodepairsandweights
link_map[std::pair<int,int>(0,1)]=1.1;
link_map[std::pair<int,int>(1,2)]=0.1;
link_map[std::pair<int,int>(2,3)]=6.2;
link_map[std::pair<int,int>(3,4)]=3.4;
link_map[std::pair<int,int>(4,5)]=5.7;
link_map[std::pair<int,int>(5,6)]=2.2;
link_map[std::pair<int,int>(0,8)]=1.8;
PrintLinkMap();
//Sortthemappingaccordingtolinkweight
std::vector<data_t>vec=sorted_edges();
PrintSortedEdges(vec);
return0;
}
PermanentlyremovingSTLcontainerelements
LikeallSTLalgorithms,removereceivesapairofiteratorstoidentifytherangeofcontainerelementsoverwhichitneedstooperate,asshowninitsdeclaration:
template<classForwardIterator,classT>
ForwardIteratorremove(ForwardIteratorfirst,
ForwardIteratorlast,
constT&value);
However,removedoesnotknowaboutthecontainerthatholdstheiteratorelements,onlytheiterators.Soremoveisnotabletogofromtheiteratortothecontainerholdingthatiterator,andthereforeisnotphysicallyabletoremoveelementsfromthatcontainer.Thatremovedoesnotactuallyremoveintheliteralsenseisasourceofconfusionformany!Tryitandseehowremoveingelementsfromacontainerdoesnotactuallychangethetotalnumberofelementsinthatcontainer:
Thisgivesthefollowingoutput:
UsingSTLtogeneratepermutations
Putsimply,givenaninputstringhowtowegenerateallofitspossiblepermutations?Astringpermutationisfoundbyreorderingthepositionofitselements.Weknowthatastringoflengthnwillhaven!(nfactorial)permutations.Forexample,thestring“xyz”willhavethepermutations:
xyz
xzy
yxz
yzx
zxy
zyx
Youcanseethiswiththefollowingcode:
#include<iostream>
#include<algorithm>
#include<iterator>
#include<string.h>
intmain()
{
charstr[]=“abcd”;
constsize_tlen=strlen(str);
do
{
std::copy(str,
str+len,
std::ostream_iterator<char>(std::cout));
std::cout<<std::endl;
}
while(std::next_permutation(str,str+len));
}
Thisgivesthefollowingoutput:
Countingtheoccurrencesofwordsinatextfile
NowhereisanexampleofthepowerofusingSTL’sassociativearraysasawordcounter.Thiscodereadsthecontentsofatextfilewhilekeepingarunningtotalofthenumberofoccurrencesofeachword.Discountingthecodethatoutputstheresultsandstripsoutthepunctuationcharacterswedonotwishtocountusesjustafewlinesofcode.Thisdeceptivelysimplelineofcode…
counter[tok]++;
…isusedtolookupthecountervalueforagivenwordandincrementitsvalue.Itnavigatesthered-blacktreeusedbythestandardmaptofindtheappropriatetreenode,
selectstheTportionofthepair<Key,T>,andthenincrementsit.
Notethatthewordcounterhasadefaultconstructortosetthecountervaluetozero.Withoutthis,thecounterissettosomearbitraryvaluecontainedinthememorywhenthecounter[tok]++lineofcodeisrunforthefirsttime.Notthezerovaluewewant.
#include<iostream>
#include<iomanip>
#include<fstream>
#include<algorithm>
#include<map>
#include<string>
#include<vector>
#include<iterator>
template<classT>
structless_second:std::binary_function<T,T,bool>
{
booloperator()(constT&lhs,constT&rhs)
{returnlhs.second.value<rhs.second.value;}
};
classWordCounter
{
public:
intvalue;
WordCounter():value(0){}
voidoperator++(int){value++;}
};
//Removeunwantedcharactersfromastring
boolfilter(charc)
{
returnisalpha(c)==0;
}
conststd::stringpath=“Hamlet.txt”;
intmain()
{
typedefstd::pair<std::string,WordCounter>word_mapping;
std::map<std::string,WordCounter>counter;
std::ifstreaminput;
input.open(path.c_str());
if(!input)
{
std::cout<<“Errorinopeningfile\n”;
return0;
}
std::stringtok;
while(true)
{
input>>tok;
//Remove?,!,etccharactersetcfromstring
tok.resize(std::remove_if(tok.begin(),
tok.end(),
filter)-tok.begin());
if(tok==””||tok==“\t”||tok==“\n”)
continue;
if(input)
{
counter[tok]++;
}
elsebreak;
}
std::map<std::string,WordCounter,std::less<std::string>>::iteratorit;
//Andthensortanddisplaytheresult:
std::vector<word_mapping>result(counter.begin(),counter.end());
std::sort(result.begin(),result.end(),less_second<word_mapping>());
std::for_each(result.begin(),result.end(),[](constword_mapping&wmp)
{
std::cout<<std::setw(20)<<wmp.first<<“\t”
<<wmp.second.value<<std::endl;
});
return0;
}
Theconsoleoutputisshownhere:
Conclusion
Igenerallyfindprogrammingbooksdenseanddifficulttoassimilate.Thoughthecontentmaybeexcellenttheyarefrequentlyletdownthroughpoorpresentationandexcessivewordiness.Thelikelihoodofsuccessfullyworkingthroughathousandpagetomeofdryexpositionwithinaweekortwoisslim.Thechancesareyouwillburnoutwithinafewdays.IknowbecauseIhavetriedandfailedwiththisapproachanumberoftimes.
Thisebookattemptstopresentadifficultsubjectasaseriesoflessonsinwaysthatarecomprehensibletotheintelligentlayperson.Atraditional‘professional’bookcouldhavebeenaboutfivetimesasbig,whilestillmissingimportantinformation.OnecouldgoonabouthowdifficultWindowsprogrammingis,orhowincomprehensibletheerrormessagesinBoostappear,butbyusingplentyofexamplesandscreenshots,thisebookshowsyousomequickwaysofgettingtogripswithsuchthings.
ForthosereasonablyfamiliarwithprogramminginCorC++inthetraditionalscenarioofcreatingconsoleapplications,thefirsttwolessonswillbeeffectiveinmakingthetransitiontowritingWindowsapplicationseasier.Youwillalsogainanunderstandingofhowtocreateyourowncustomizedcontrols,buildadialog-basedwebbrowserandplaybackmediafilesusingDirectShow,amongstotherthings.
LessonthreeisanaccessibleintroductiontogettingstartedwiththeBoostlibrariesinavarietyofdevelopmentenvironments.ExamplesofusingBoostforsixdifferentlibrariesaregiven.Havingthisvaluableskillwillincreaseyourproductivitybyprovidingtailor-madesolutionstomanycommonprogrammingproblems.
Alsoincludedarelessonstogetyoustartedwithusinggraphalgorithms,whichareparticularlyusefulinareasliketelecommunications.Hereweshowyouhowtomodelnetworks,aswellasapplyingtechniqueswithwhichtoascertainconnectivity,findshortestpathsanddetermineminimalspanningtrees.
Therearetimeswhenasadeveloperyoumaybeaskedtocodeacomplexalgorithm.Anumberofthelessonsgivenhereshowyouhowtoprototypealgorithmsandde-mystifytheprocess.Examplesincludetheshunting-yardalgorithmusedforevaluatingmathematicalexpressions,the“allsimplepaths”problemforfindingroutesinnetworksandaSodukupuzzle-solver.Inalltheseexamplesweverifythatthecomputationalresultsareasexpected.
WhenyoutakeadvantageoftheStandardTemplateLibrary(STL)youmakeyourlifesimplerandmoreefficient.ThefinallessononSTLwilldramaticallyimproveyourprogrammingproductivityandstyle,allowingyourcodetobefarmorereliableandreusable.
LearningC++isnosmalltask.Ihopethisebookhasprovedtobeapracticalresourcethatyoucanactuallyread,andmoreimportantly,putintopractice.