310

10 Lessons About C++ You Need To Learn To Become A … · 10 Lessons About C++ You Need To Learn to Become a Master Programmer Andrew Webb

  • Upload
    vannhan

  • View
    219

  • Download
    0

Embed Size (px)

Citation preview

10LessonsAboutC++YouNeedToLearntoBecomeaMasterProgrammer

AndrewWebb

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;

}

break;

}

returnFALSE;

}

Thiswillproducethefollowingdialogwhenyouruntheapplication:

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

appearingaroundtheeditablecontrol:

Andthatanyofthefieldscanbeeditediftheuserchooses:

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:

Nowrestarttheapplication,openthefileSerialize1.txtinordertore-open:

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:

ThisexampleusageofBoostFilesystemshouldnowcompileandrun:

ABC.txt

RUNSUCCESSFUL(totaltime:282ms)

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:

Uncheckallboxesandselectadialog-basedapplication,thenclickNext:

Againuncheckallboxes:

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;

visited.push_back(s);

DepthFirst(network,visited,d,max_hops,min_hops);

}

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,

x,

x+sizeof(x)/sizeof(x[0]),

””);

return0;

}

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.