Digital clock for my PC XT compatible, or having fun with Borland Graphics Interface (BGI)

The clock in action

Yet another day “wasted” playing with my Pravetz 16. Really I should sit on my … and do some real work, but the battery of my wall clock died and I decided to code myself a digital one πŸ™‚

The clock in action
The clock in action

The clock is written on C using Borland Graphics Interfaces also knows as BGI. The most easy and video hardware independent option for doing graphics under DOS. BGI comes bundled with drivers for common back then graphic adapters (CGA, VGA and EGA) and vector fonts.

There are other options such as using interrupt 10h or writing directly to the graphics hardware like most of the games were doing back then.

To build the code fire up the Turbo C IDE, go to Options->Linker and check “Graphics library”, then proceed as usual.

Turbo C Linker options
Turbo C Linker options

Compiled version of the CLOCK.EXE can be obtained from here and the source code (if you don’t like Ctrl + C & Ctrl + V) is here.

Wen I got some free time and motivation I will probably enhance the clock with some neat options such as 12/24 hours mode, moving objects in the background, drawing the “Pravetz” logo in the upper left corner of the screen, command line parameters for setting an alarm etc…

If you wan’t to learn more about BGI review the examples that comes with Turbo C such as BARCHART.C. View the GRAPHICS.H header. Also take a look here.

Code listing a.k.a “The Digital Clock” source πŸ™‚

#include <stdio.h>
#include <dos.h>
#include <graphics.h>
 
#define TIME_BUF_LEN 6
#define DATE_BUF_LEN 10
 
#define TIME_TEXT_TEMPLATE "00:00"
#define DATE_TEXT_TEMPLATE "00.00.0000"
 
#define NOTE_TEXT "Press any key to exit..."
 
#define BGI_LOCATION "C:\\TC\\bgi"
 
int gl_nMaxX = 0;
int gl_nMaxY = 0;
 
int gl_nCenterX = 0;
int gl_nCenterY = 0;
 
char gl_szTimeBuff[TIME_BUF_LEN];
char gl_szDateBuff[DATE_BUF_LEN];
 
int initGraphics( void )
{
	int nGraphicDriver = DETECT; 
	int nGraphMode;
	int nErrorCode;
 
	/* Initialize graphics system */
	initgraph(&nGraphicDriver, &nGraphMode, BGI_LOCATION);
	nErrorCode = graphresult();
 
	if (nErrorCode != grOk)
	{
	   return -1;
	}        
 
	/* Graphics OK, so return "true" */
	return 1; 
}
 
void drawNote()
{
	int textWidth, textHeight;
 
	settextstyle(DEFAULT_FONT, HORIZ_DIR, 0);
 
	textWidth = textwidth(NOTE_TEXT);
	textHeight = textheight(NOTE_TEXT);
 
	outtextxy(gl_nMaxX - textWidth,
			  gl_nMaxY - textHeight,
			  NOTE_TEXT);
}
 
void drawTime(struct time *pTime)
{
	int textWidth, textHeight;
 
	settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 8);
 
	textWidth = textwidth(TIME_TEXT_TEMPLATE);
	textHeight = textheight(TIME_TEXT_TEMPLATE);
 
	sprintf(gl_szTimeBuff, "%02d:%02d", pTime->ti_hour, pTime->ti_min);
 
	outtextxy(gl_nCenterX - (textWidth / 2), 
			  gl_nCenterY - textHeight, 
			  gl_szTimeBuff);
}
 
void drawDate(struct date *pDate)
{
	int textWidth;
 
	settextstyle(SANS_SERIF_FONT, HORIZ_DIR, 6);
 
	textWidth = textwidth(DATE_TEXT_TEMPLATE);
 
	sprintf(gl_szDateBuff, 
			"%02d.%02d.%d", 
			pDate->da_day, 
			pDate->da_mon, 
			pDate->da_year);
 
	outtextxy(gl_nCenterX - (textWidth / 2), gl_nCenterY, gl_szDateBuff);
}
 
int main(void)
{
	struct time sysTime, startTime;
	struct date sysDate, startDate;	
 
	if(initGraphics() == -1)
	{
		printf("Error initializing BGI!\n");
		return 0;
	}
 
	gl_nMaxX = getmaxx();
	gl_nMaxY = getmaxy();
 
	gl_nCenterX = (getmaxx() / 2);
	gl_nCenterY = (getmaxy() / 2);		
 
	drawNote();
 
	gettime(&startTime);
	drawTime(&startTime);
 
	getdate(&sysDate);
	drawDate(&sysDate);
 
	while(!kbhit())
	{										
		gettime(&sysTime);
 
		/*
		 * To avoid flickering, redraw the screen only if there are 
		 * changes in the hour or minute values. Only some VGA and EGA
		 * drivers support 'setactivepage()' and 'setvisualpage()'.
		 */
		if(sysTime.ti_hour > startTime.ti_hour || 
		   sysTime.ti_min > startTime.ti_min)
		{
			cleardevice();
			getdate(&sysDate);
 
			drawNote();
			drawTime(&sysTime);
			drawDate(&sysDate);
 
			gettime(&startTime);
		}
	}
 
	closegraph();
 
	return 0;
}

SINFO.EXE or playing with bios interrupts on my PC XT clone

I am stuck with a problem in the game I work on for several days now. When I’m stuck I usually do something else for a couple of hours. For example playing with bios interrupts on my PC XT clone (which by the way is completely restored, but more on this in the upcoming posts).

Pravetz 16 (IBM PC XT clone)
Pravetz 16 (IBM PC XT clone) and yes that’s Windows 3.0.

NOTE: Writing code on monochrome monitor is something that every software developer should do at least ones in his career πŸ™‚

What’s interrupt? (From Wikipedia articles on interrupts)

In system programming, an interrupt is a signal to the processor emitted by hardware or software indicating an event that needs immediate attention. An interrupt alerts the processor to a high-priority condition requiring the interruption of the current code the processor is executing. The processor responds by suspending its current activities, saving its state, and executing a function called an interrupt handler (or an interrupt service routine, ISR) to deal with the event. This interruption is temporary, and, after the interrupt handler finishes, the processor resumes normal activities.[1] There are two types of interrupts: hardware interrupts and software interrupts.

The code below calls software interrupts 11h (Equipment Installed) and 12h (Memory Available) using inline assembly. To compile the source you will need Turbo C and TASM. They both run perfectly fine under DOSBox.

The executable is available for download here.

#include 
 
int main(void)
{
  int systemInfo;
  int floppyCount;
  int mem, memSize, memSizePost;
  int videoMode;
  int serialPorts;
  int paralelPorts;
 
  /* Call INT 11h ( Equipment Installed ) */
  asm int 11h;
  asm mov systemInfo, ax;
 
  /* Call INT 12h ( Memory Available ) */
  asm int 12h;
  asm mov memSizePost, ax;
 
  printf("********** SYSTEM INFO ***********\n");
  printf("Floppy drive(s): %s ", (systemInfo &amp; 0x0001) ? "yes" : "no");
 
  if((systemInfo &amp; 0x0001))
  {
    switch((systemInfo &amp; 0x00C0))
    {
      case 0:
      {
		floppyCount = 1;
		break;
      }
      case 64:
      {
		floppyCount = 2;
		break;
      }
      case 128:
      {
		floppyCount = 3;
		break;
      }
      case 192:
      {
		floppyCount = 4;
		break;
      }
    }
 
    printf("(%d)\n", floppyCount);
  }
  else
  {
    printf("\n");
  }
 
 
  printf("Math co-processor: %s\n", (systemInfo &amp; 0x0002) ? "yes" : "no");
 
  mem = (systemInfo &amp; 0x000C);
 
  switch(mem)
  {
    case 0:
    {
      memSize = 16;
      break;
    }    
    case 4:
    {
      memSize = 32;
      break;
    }
    case 8:
    {
      memSize = 48;
      break;
    }
    case 12:
    {
      memSize = 256;
      break;
    }
  }
  printf("On-board memory: %d K%s\n", memSize, (memSize &gt;= 256) ? "+" : " ");
  printf("Memory reported by BIOS POST: %dK\n", memSizePost);
 
  videoMode = (systemInfo &amp; 0x0030);
  switch(videoMode)
  {
    case 0:
    {
      printf("Video mode: EGA, VGA or PGA\n");
      break;
    }
    case 16:
    {
      printf("Video mode: CGA, 40 x 25\n");
      break;
    }
    case 32:
    {
      printf("Video mode: CGA, 80 x 25\n");
      break;
    }
    case 48:
    {
      printf("Video mode: monochrome, 80 x 25\n");
      break;
    }
  }
 
  printf("DMA support: %s\n", (systemInfo &amp; 0x0100) == 0 ? "yes" : "no");
  printf("Game adapter: %s\n", (systemInfo &amp; 0x1000) == 0 ? "no" : "yes");
 
  switch((systemInfo &amp; 0x0E00))
  {
    case 0:
    {
      serialPorts = 0;
      break;
    }
    case 512:
    {
      serialPorts = 1;
      break;
    }
    case 1024:
    {
      serialPorts = 2;
      break;
    }
    case 1536:
    {
      serialPorts = 3;
      break;
    }
    case 2048:
    {
      serialPorts = 4;
      break;
    }
  }
  printf("Serial ports: %d\n", serialPorts);
 
  switch((systemInfo &amp; 0xC000))
  {
    case 0:
    {
      paralelPorts = 0;
      break;
    }
    case 16384:
    {
      paralelPorts = 1;
      break;
    }
    case 32768:
    {
      paralelPorts = 2;
      break;
    }
    case 49152:
    {
      paralelPorts = 3;
      break;
    }
  }
  printf("Paralel ports: %d\n", paralelPorts);
 
  return 0;
}

CLastError

Small class for resolving and presenting in user readable form the codes from ::GetLastError() Win32 API function. The class has 2 static methods:

1
2
3
4
5
6
7
8
9
10
void CLastError::Get( const DWORD& dwErrorCode, CString& strErrMessage )
{
	LPTSTR lpErrorText = NULL;
 
	::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 
		0, dwErrorCode, 0, lpErrorText, MAX_PATH, 0 );
 
	strErrMessage = lpErrorText;
	::LocalFree( lpErrorText );
}

The ‘Get’ method supplied an error code will resolve the code to user readable error message and store it in the supplied ‘strErrMessage’ string!

1
2
3
4
5
6
7
8
9
10
11
12
void CLastError::Show( const DWORD& dwErrorCode, const CString& strAppName )
{
	CString strErrMessage;
	Get( dwErrorCode, strErrMessage );
 
	CString strDisplayError;
	strDisplayError.Format( 
		_T("%s encountered an error and needs to close!\n\nError was: %s"),
		strAppName, strErrMessage );
 
	AfxMessageBox( strDisplayError, MB_ICONERROR | MB_OK );
}

The ‘Show’ method supplied an error code will resolve the code to user readable error message and the message will be displayed to the user trough AfxMessageBox() function.

Class declaration:

class CLastError
{
public:
 
	CLastError( );
	virtual ~CLastError( );
 
public:
 
	static void Get( const DWORD& dwErrorCode, CString& strErrMessage );
	static void Show( const DWORD& dwErrorCode, const CString& strAppName );
};

Class implementation:

CLastError::CLastError( )
{
}
 
CLastError::~CLastError( )
{
}
 
void CLastError::Get( const DWORD& dwErrorCode, CString& strErrMessage )
{
	LPTSTR lpErrorText = NULL;
 
	::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 
		0, dwErrorCode, 0, lpErrorText, MAX_PATH, 0 );
 
	strErrMessage = lpErrorText;
	::LocalFree( lpErrorText );
}
 
void CLastError::Show( const DWORD& dwErrorCode, const CString& strAppName )
{
	CString strErrMessage;
	Get( dwErrorCode, strErrMessage );
 
	CString strDisplayError;
	strDisplayError.Format( 
		_T("%s encountered an error and needs to close!\n\nError was: %s"),
		strAppName, strErrMessage );
 
	AfxMessageBox( strDisplayError, MB_ICONERROR | MB_OK );
}

CSortArray updated

This is an update of the CSortArray class that I wrote few weeks ago. In this version the sort functionality is extended and search functionality is provided.

/// <summary> Extends CArray, providing search and sort functionality </summary>
template <class TYPE, class ARG_TYPE = const TYPE&> class CSortArray : public CArray<TYPE, ARG_TYPE>
{
// Public member functions
public:
 
	/// <summary> Sort's the array content </summary>
	/// <param name="compare"> User defined compare function </param>
	/// <param name="context"> Pointer to a user defined object that can be accessed in the compare function </param>
	void QuickSort( int ( __cdecl *compare )(const void*, const void*) )
	{
		qsort( m_pData, GetCount(), sizeof(TYPE), (*compare) );
	}
	void QuickSort( int ( __cdecl *compare )(void*, const void*, const void*), void* context )
	{
		qsort_s( m_pData, GetCount(), sizeof(TYPE), (*compare), context );
	}
 
	/// <summary> Searches element in the array </summary>
	/// <param name="oKey"> Key to search </param>
	/// <param name="compare"> User defined compare function </param>
	/// <param name="context"> Pointer to a user defined object that can be accessed in the compare function </param>
	TYPE* Search( const TYPE& oKey, int ( __cdecl *compare )(const void*, const void*) )
	{
		return (TYPE*)bsearch( &oKey, m_pData, GetCount(), sizeof(TYPE), (*compare) );
	}
	TYPE* Search( const TYPE& oKey, int ( __cdecl *compare )(void*, const void*, const void*), void* context )
	{
		return (TYPE*)bsearch_s( &oKey, m_pData, GetCount(), sizeof(TYPE), (*compare), context );
	}
};

The new thing about sorting is that and override of QuickSort() method is provided that accepts a sort context. Pretty useful when you want to sort an array of complex types, for example information about a locality consisting from city, county and community by city name, county name, etc.

#define SIZE_CITY_NAME 49
#define SIZE_COUNTY_NAME 49
#define SIZE_COMMUNITY_NAME 49
 
typedef struct LOCALITY
{
	int nId;
	TCHAR szCity[SIZE_CITY_NAME + 1];
	TCHAR szCommunity[SIZE_COMMUNITY_NAME + 1];
	TCHAR szCounty[SIZE_COUNTY_NAME + 1];
	int nContactsCount;
 
	LOCALITY()
	{
		::ZeroMemory(this,sizeof(*this));
	}
} LOCALITY;

Also in the QuickSort() override the pointer to a compare function now has the following form:

int ( __cdecl *compare )(void*, const void*, const void*)

where the first argument to the function is pointer to the context object provided as void *context.

The search method can use a compare function without a context or with a context. Below is an example application that sorts an array of localities first by city, then by county and community. Then searches the array for a particular locality, for example city or community:

// SortArrayApp.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.h"
#include "SortArrayApp.h"
#include "SortArray.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
// The one and only application object
CWinApp theApp;
 
using namespace std;
 
#define CONTEXT_ID 1
#define CONTEXT_CITY 2
#define CONTEXT_COMMUNITY 3
#define CONTEXT_COUNTY 4
#define CONTEXT_CONTACTS 5
 
#define SIZE_CITY_NAME 49
#define SIZE_COUNTY_NAME 49
#define SIZE_COMMUNITY_NAME 49
 
typedef struct LOCALITY
{
	int nId;
	TCHAR szCity[SIZE_CITY_NAME + 1];
	TCHAR szCommunity[SIZE_COMMUNITY_NAME + 1];
	TCHAR szCounty[SIZE_COUNTY_NAME + 1];
	int nContactsCount;
 
	LOCALITY()
	{
		::ZeroMemory(this,sizeof(*this));
	}
} LOCALITY;
 
int Compare( void* context, const void* element1, const void* element2 )
{
	LOCALITY *pLocality1 = (LOCALITY*) element1;
	LOCALITY *pLocality2 = (LOCALITY*) element2;
	int *pContext = (int*) context;
 
	switch( *pContext )
	{
	case CONTEXT_ID:
		{
			if( pLocality1->nId > pLocality2->nId )
			{
				return 1;
			} else if ( pLocality1->nId < pLocality2->nId ) {
				return -1;
			} else if ( pLocality1->nId == pLocality2->nId ) {
				return 0;
			}
			break;
		}
	case CONTEXT_COMMUNITY:
		{
			return _tcscmp( pLocality1->szCommunity, pLocality2->szCommunity );
		}
	case CONTEXT_COUNTY:
		{
			return _tcscmp( pLocality1->szCounty, pLocality2->szCounty );
		}
	case CONTEXT_CITY:
		{
			return _tcscmp( pLocality1->szCity, pLocality2->szCity );
		}
	case CONTEXT_CONTACTS:
		{
			if( pLocality1->nContactsCount > pLocality2->nContactsCount )
			{
				return 1;
			} else if ( pLocality1->nContactsCount < pLocality2->nContactsCount ) {
				return -1;
			} else if ( pLocality1->nContactsCount == pLocality2->nContactsCount ) {
				return 0;
			}
			break;
		}
	}
 
	return 0;
}
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	int nRetCode = 0;
 
	HMODULE hModule = ::GetModuleHandle(NULL);
 
	if (hModule != NULL)
	{
		// initialize MFC and print and error on failure
		if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
		{
			// TODO: change error code to suit your needs
			_tprintf(_T("Fatal Error: MFC initialization failed\n"));
			nRetCode = 1;
		}
		else
		{
			// SortArray usage
			CSortArray<LOCALITY> arrToSort;
 
			// Add locality information in the array
			LOCALITY recLocality;
 
			recLocality.nId = 2;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Varna") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Varna") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Varna") );
			recLocality.nContactsCount = 10;
			arrToSort.Add( recLocality );
 
			recLocality.nId = 1;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Balgarene") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Lovech") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Lovech") );
			recLocality.nContactsCount = 3;
			arrToSort.Add( recLocality );
 
			recLocality.nId = 4;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Veliko Turnovo") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Turnovo") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Turnovo") );
			recLocality.nContactsCount = 8;
			arrToSort.Add( recLocality );
 
			recLocality.nId = 3;
			_tcscpy_s( recLocality.szCity, ( SIZE_CITY_NAME + 1 ), _T("Sofia") );
			_tcscpy_s( recLocality.szCommunity, ( SIZE_COMMUNITY_NAME + 1 ), _T("Sofia Grad") );
			_tcscpy_s( recLocality.szCounty, ( SIZE_COUNTY_NAME + 1 ), _T("Sofia") );
			recLocality.nContactsCount = 8;
			arrToSort.Add( recLocality );
 
			// Display the unsorted elements
			cout << "Unsorted elements:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			int context = CONTEXT_ID;
 
			// Sort the array by ID
			arrToSort.QuickSort( Compare, &context );
 
			cout << "Elements sorted by locality ID:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			// Sort the array by contacts count
			context = CONTEXT_CONTACTS;
			arrToSort.QuickSort( Compare, &context );
 
			cout << "Elements sorted by contacts count:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			// Sort the array by City name
			context = CONTEXT_CITY;
			arrToSort.QuickSort( Compare, &context );
 
			cout << "Elements sorted by City name:" << endl;
			cout << "ID\tCity\tCommunity\tCounty\tContacts" << endl;
			for( int i = 0; i < arrToSort.GetCount(); i++ )
			{
				LOCALITY& currLocality = arrToSort[i];
				wcout << currLocality.nId << ".\t";
				wcout << currLocality.szCity << "\t";
				wcout << currLocality.szCommunity << "\t";
				wcout << currLocality.szCounty << "\t";
				wcout << currLocality.nContactsCount << endl;
			}
			wcout << endl;
 
			// Searching by city name
			context = CONTEXT_CITY;
			LOCALITY recSearch;
			wcout << "Enter city name: ";
			wcin >> recSearch.szCity;
 
			LOCALITY *pResult = arrToSort.Search( recSearch, Compare, &context );
			if( pResult )
			{
				wcout << pResult->nId << ".\t";
				wcout << pResult->szCity << "\t";
				wcout << pResult->szCommunity << "\t";
				wcout << pResult->szCounty << "\t";
				wcout << pResult->nContactsCount << endl;
			}
			else
			{
				wcout << recSearch.szCity << " not found!" << endl;
			}
		}
	}
	else
	{
		// TODO: change error code to suit your needs
		_tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
		nRetCode = 1;
	}
 
	return nRetCode;
}

NOTE: CSortArray uses the bsearch function to preform the search and expects a sorted array on which to preform the search. We can further implement a member of CSortArray that indicates if array is sorted or not.

Sorted MFC CArray

UPDATE: New version of this class is available here.

On a project I work in my spare time. I needed a quick way of sorting elements stored in CArray so CSortArray was born. It does nothing special. It extends the CArray class providing one public method named QuickSort who uses the qsort CRT function to do the sorting. Providing the right Compare function you can sort any type of array.

Below is the CSortArray class:

1
2
3
4
5
6
7
8
9
10
template <class TYPE, class ARG_TYPE = const TYPE&> class CSortArray : public CArray<TYPE, ARG_TYPE>
{
// Public member functions
public:
 
	void QuickSort( int (__cdecl *compare )(const void*, const void*) )
	{
		qsort( m_pData, GetCount(), sizeof(TYPE), (*compare) );
	}
};

The QuickSort member takes an address of user defined comparison function with the following declaration:

1
int Compare( const void* pElement1, const void* pElement2 );

where pElement1 is the first element used in the compare process and pElement2 is the second.

Your function must compare the elements and return one of the following:

  • < 0 if pElement1 is less than pElement2
  • 0 if pElement1 is equal to pElement2
  • > 0 if pElement1 is greater than pElement2

Below is an example usage of the CSortArray class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// User defined Compare function
int Compare( const void* element1, const void* element2 )
{
	int *pElem1 = (int*) element1;
	int *pElem2 = (int*) element2;
 
	if( *pElem1 < *pElem2 )
	{
		return -1;
	} else if ( *pElem1 == *pElem2 ) {
		return 0;
	} else {
		return 1;
	}
 
	return 0;
}
 
// Application entry point
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
        // SortArray usage
	CSortArray<int> arrToSort;
 
	// Add some unsorted elements to the array
	arrToSort.Add( 5 );
	arrToSort.Add( 2 );
	arrToSort.Add( 10 );
	arrToSort.Add( 17 );
	arrToSort.Add( 1 );
	arrToSort.Add( 8 );
	arrToSort.Add( 3 );
 
	// Display the unsorted elements
	cout << "Unsorted elements" << endl;
	for( int i = 0; i < arrToSort.GetCount(); i++ )
	{
		cout << arrToSort[i] << endl;
	}
 
	// Sort the array
	arrToSort.QuickSort( Compare );
 
	// Display the sorted elements
	cout << "Sorted elements" << endl;
	for( int j = 0; j < arrToSort.GetCount(); j++ )
	{
		cout << arrToSort[j] << endl;
	}
 
	return 0;
}

In the CSortArray class we can also implement a search method, but that’s a theme of another post.