The Worst and Best Delphi Programs I Ever Created

My Worst Delphi Program

The worst “on-the-fly” (pun intended) Delphi demo I ever wrote was a thread based sort program (Dreaded Sorts). I created the code on the flight from San Jose to Seattle for the Microsoft Windows 95 launch. The launch took place on Thursday August 24, 1995 on the Microsoft campus. Multiple software vendors, including Borland, demonstrated their support for Win95 in tents on the lawn.

During the flight I realized that I didn’t have a Delphi 2 demo that took full advantage of Win95’s 32-bit OS and other features. So, for the duration of flight (approximately 2 hours) I hacked together a 32-bit VCL application using a pre-release version of Delphi. Note: the program still compiles and runs using the latest release of Delphi 11 Alexandria!

Charlie Calvert included my “Dreaded Sorts” program in his Delphi 2 Unleashed book (pages 240-243). The source code for the project can be found on the book’s CD.

To introduce my program Charlie wrote:

“The program shown in Listing 7.9 was written by David Intersimone, a fellow Borland employee. The code has some historical significance, as it was written on the flight to the Windows 95 launch in Seattle. The fact that David was able to do some serious coding on a West Coast shuttle flight shows that almost anything is possible if you set your mind to it!”

The Original Dreaded Sorts Source Code (THSORTS.DPR)

The original name of my on-the-fly demo was THSorts. I eventually called it my “Dreaded Sorts” program when Charlie Calvert asked to include it in his Delphi 2 Unleased book. Take a look at the source code below. You’ll see that it contains many bad programming practices and hacks that I used on the flight to get the demo working. Examples of bad programming practices include using magic numbers, offset coordinate hacks, hard coded array size (Delphi now supports dynamic arrays), changing the caption for the input error message instead of a Message Box popup, etc. The most glaring warning was the note at the top of the main form’s source code: “This example program shows how to set a thread’s priority. Don’t use Canvas property in program like this! Not unless you also use the TThread object and its Synchronize procedure!”

MAIN.PAS (note: you can download a zip file of the complete program)

unit main;

{
  Dreaded Sorts
  copyright (c) 1996 by David Intersimone

  This example program shows how to set a
  thread's priority. Don't use Canvas property
  in program like this! Not unless you also
  use the TThread object and its Synchronize
  procedure!
}
  
interface

uses
  SysUtils, WinTypes, WinProcs,
  Messages, Classes, Graphics,
  Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, Buttons;

const
  aMax = 300;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Label2: TLabel;
    Label1: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    BitBtn1: TBitBtn;
    BubbleTrackBar: TTrackBar;
    QuickTrackBar: TTrackBar;
    procedure Button1Click(Sender: TObject);
  private
    T1 : THandle;
    T2 : THandle;
  end;

var
  Form1: TForm1;
  a,b : array[0..aMax-1] of integer;
  numItems : integer;

implementation

uses
  secform, thform;

{$R *.DFM}

procedure BubbleSort(var ia:array of integer; items: integer);
var
  i,j,t : integer;
  DC: HDC;
begin
  DC := GetDC(Form2.Handle);
  for i := items downto 0 do
  begin
    for j := 0 to items-1 do
      if ia[j] < ia[j+1] then
      begin
        t := ia[j];
        SetPixel(DC, ia[j+1]+5, j+1+5, clBlue);
        SetPixel(DC, ia[j]+5, j+5, clBlue);
        ia[j] := ia[j+1];
        ia[j+1] := t;
        Setpixel(DC, ia[j+1]+5,j+1+5, clYellow);
        Setpixel(DC, ia[j]+5,j+5, clYellow);
      end;
   end;
   ReleaseDC(Form2.Handle, DC);
end;

procedure QuickSort(var ia:array of integer; iLo,iHi : integer);
var
  Lo,Hi,Mid,T : integer;
  DC: HDC;
begin
  Lo := iLo;
  Hi := iHi;
  mid := ia[(Lo+hi) div 2];
  repeat
    DC := GetDC(Form3.Handle);
    while ia[Lo] < mid do Inc(Lo);
    while ia[Hi] > mid do Dec(Hi);
    if Lo <= Hi then
    begin
      T := ia[Lo];
      SetPixel(DC, ia[Lo]+5,Lo+5, clBlue);
      SetPixel(DC, ia[Hi]+5,Hi+5, clBlue);
      ia[Lo] := ia[Hi];
      ia[Hi] := T;
      SetPixel(DC, ia[Lo]+5,Lo+5, clLime);
      SetPixel(DC, ia[Hi]+5,Hi+5, clLime);
      inc(Lo);
      dec(Hi);
    end;
  until Lo > Hi;
  if Hi > iLo then QuickSort(ia,iLo,Hi);
  if Lo < iHi then QuickSort(ia,Lo,iHi);
  ReleaseDC(Form3.Handle, DC);
end;

function BubbleThread(parms:pointer) : LongInt; far;
begin
  BubbleSort(a,numItems-1);
end;

function QuickThread(parms:pointer) : LongInt; far;
begin
  QuickSort(b,0,numItems-1);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i : integer;
  ThreadID : dWord;
begin
  numItems := strToInt(Edit1.Text);
  if numItems <= aMax then
  begin
    form2.free;
    form2 := TForm2.Create(self);
    form2.top := 140;
    form2.left := 2;
    form2.clientheight := numItems+10;
    form2.clientwidth := numItems+10;
    form2.color := clBlue;
    form2.caption := 'Bubble Sort';
    form2.show;

    form3.free;
    form3 := TForm3.Create(self);
    form3.top := 140;
    form3.left := 320;
    form3.clientheight := numItems+10;
    form3.clientwidth := numItems+10;
    form3.color := clBlue;
    form3.caption := 'Quick Sort';
    form3.show;

    Randomize;
    for i := 0 to numItems-1 do
    begin
      a[i] := random(numItems);
      b[i] := a[i];
      form2.canvas.pixels[a[i]+5,i+5] := clYellow;
      form3.canvas.pixels[b[i]+5,i+5] := clLime;
    end;
    T1 := createThread(nil,0,@BubbleThread,nil,0,threadID);
    setThreadPriority(T1, BubbleTrackBar.Position);
    T2 := createThread(nil,0,@QuickThread,nil,0,threadID);
    setThreadPriority(T2, QuickTrackBar.Position);
  end
  else
    Form1.Caption := 'Too Large!';
end;

end.

A Much Better Version of a Delphi Multi-Threaded Sort Demo

A much better version of a Delphi multi-threaded demo shipped in the release version of Delphi 2 (release date: February 10, 1996) is available on GitHub. You can download the Delphi multi threading demo by Bob Ainsbury and Ray Konopka that first appeared at the 1995 Borland Conference.

My Best Delphi Program

The best Delphi program I ever created is one that I haven’t written yet!

Additional Information

Embarcadero Delphi product page – https://www.embarcadero.com/products/delphi

Parts of this post first appeared on my Embarcadero Blog article celebrating the 27th birthday of Delphi – https://blogs.embarcadero.com/happy-27th-birthday-delphi-building-the-future-how-we-get-to-delphi-36/

Remembering the Iconic Windows 95 Launch by Lance Ulanof – https://onezero.medium.com/remembering-the-iconic-windows-95-launch-94cfcf215d50

Delphi 2 Unleased by Charlie Calvert – https://www.abebooks.com/9780672308581/Delphi-2-Unleashed-Calvert-Charles-0672308584/plp

Multithreaded Sorting with C++Builder and the Parallel Programming Library (PPL)

The Parallel Programming Library (PPL) is one of my favorite features in C++Builder runtime library. PPL allows developers to create tasks that run in parallel to take advantage of multi-core processors.

Using the PPL, you can: 1) speed up loops with a Parallel For, 2) run multiple tasks in parallel using TTask, and 3) use Future Objects to allow a process run with your program focused on other work until the future value is set.

To showcase the TTask feature of the PPL, I’ve created a C++Builder VCL application (build and tested using the C++Builder 10.4 Sydney release) that runs three sort algorithms in separate tasks – Bubble Sort, Shell Sort and the ISO C++ standard Sort (which implements the Quicksort algorithm).

The User Interface

The VCL user interface for my application includes a TButton, two TMemos, and four TLabels. The TButton onClick event handler creates a vector of integers, creates three TTasks (for the sort algorithms) and waits for the sort tasks to complete using the TTask::WaitForAll method.

The Code

MainUnit.h:

#ifndef MainUnitH
#define MainUnitH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
	TButton *Button1;
	TLabel *SortingStatusLabel;
	TLabel *BubbleSortLabel;
	TMemo *Data_Memo;
	TMemo *SortResult_Memo;
	TLabel *ShellSortLabel;
	TLabel *STDSortLabel;
	void __fastcall Button1Click(TObject *Sender);
private:	// User declarations
public:		// User declarations
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

MainUnit.cpp:

//---------------------------------------------------------------------------

#include <vcl.h>
#include <System.Threading.hpp>
#include <vector>
#include <algorithm>
#pragma hdrstop

#include "MainUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{

	_di_ITask My_Tasks[3];
	const int max_data = 15000;   // number of random numbers to create

	int LoopValue = 0;

	Button1->Enabled = false;
	Button1->Update();
	BubbleSortLabel->Caption = "Bubble Sort";
	BubbleSortLabel->Update();
	ShellSortLabel->Caption = "Shell Sort";
	ShellSortLabel->Update();
	STDSortLabel->Caption = "std::sort";
	STDSortLabel->Update();

	SortingStatusLabel->Caption = "Sorting "+IntToStr(max_data)+" integers!";
	SortingStatusLabel->Update();

	// Windows GetTicks() start and stop for each sort method
	int StartBubble,StopBubble;
	int StartShell,StopShell;
	int StartSTD,StopSTD;

	Data_Memo->Lines->Clear();
	SortResult_Memo->Lines->Clear();

	// create a vector of data that all sort algorithms will use
	std::vector<int> my_data;

	// populate the vector with integers
	for (int i = 1; i <= max_data; i++) {
		int random_value = Random(max_data);
		my_data.push_back(random_value);
		// Data_Memo->Lines->Add(IntToStr(random_value));
	}

	// copy data vector to sort vectors
	std::vector<int> bubble_data = my_data;
	std::vector<int> shell_data = my_data;
	std::vector<int> std_data = my_data;

	// First Task - Bubble Sort
	My_Tasks[0] = TTask::Create([&](){
		// Set Bubble Sort Windows GetTickCount()
		StartBubble = GetTickCount();
		// body of Bubble Sort
		bool swapp = true;
		while(swapp){
			swapp = false;
			for (size_t i = 0; i < bubble_data.size()-1; i++) {
				if (bubble_data[i]>bubble_data[i+1] ){
					bubble_data[i] += bubble_data[i+1];
					bubble_data[i+1] = bubble_data[i] - bubble_data[i+1];
					bubble_data[i] -=bubble_data[i+1];
					swapp = true;
				}
			}
		}
		// Set the Bubble Sort Stop GetTicks()
		StopBubble = GetTickCount();
	});
	// Start the Bubble Sort Task
	My_Tasks[0]->Start();


	// Second Task - Shell Sort
	My_Tasks[1] = TTask::Create([&](){
		// Set Shell Sort Windows GetTickCount()
		StartShell = GetTickCount();
		// body of the Shell Sort
		for (int gapSize = shell_data.size() / 2; gapSize > 0; gapSize /= 2) {
			for (int currentIndex = gapSize; currentIndex < shell_data.size(); currentIndex++) {
				// save the currentIndex
				int currentIndexCopy = currentIndex;
				// save the value of the currentIndex
				int item = shell_data[currentIndex];
				while (currentIndexCopy >= gapSize && shell_data[currentIndexCopy - gapSize] > item) {
					shell_data[currentIndexCopy] = shell_data[currentIndexCopy - gapSize];
					currentIndexCopy -= gapSize;
				}
				shell_data[currentIndexCopy] = item;
			}
		}
		// Set the Shell Sort Stop GetTicks()
		StopShell = GetTickCount();
	});
	// Start the Shell Sort
	My_Tasks[1]->Start();


	// Third Task - std::sort
	My_Tasks[2] = TTask::Create([&](){
		// Set std::sort Windows GetTickCount()
		StartSTD = GetTickCount();
		// Body of the std::sort
		std::sort(std_data.begin(),std_data.end());
		// Set the std::sort Stop GetTicks()
		StopSTD = GetTickCount();
	});
	// Start the Shell Sort
	My_Tasks[2]->Start();

	// wait until all of the sorting tasks complete
	TTask::WaitForAll(My_Tasks, sizeof(My_Tasks)/sizeof(My_Tasks[0])-1);

	SortingStatusLabel->Caption = "Sorting All done!";

	BubbleSortLabel->Caption = "Bubble Sort Time: "
		+ IntToStr(StopBubble - StartBubble)
		+ " ms";
	BubbleSortLabel->Update();

	ShellSortLabel->Caption = "Shell Sort Time: "
		+ IntToStr(StopShell - StartShell)
		+ " ms";
	ShellSortLabel->Update();

	STDSortLabel->Caption = "std::sort: "
		+ IntToStr(StopSTD - StartSTD)
		+ " ms";
	STDSortLabel->Update();

	// if you want to see the sort results un-comment
	// one of the for loops and the sort result memo statement
	// for(int n : std_data) {
	// for(int n : bubble_data) {
	// for(int n : shell_data) {
		// SortResult_Memo->Lines->Add(IntToStr(n));
	// }

	// clear the vectors
	my_data.clear();
	bubble_data.clear();
	shell_data.clear();
	std_data.clear();

	Button1->Enabled = true;
}
//---------------------------------------------------------------------------

The form after sorting 15,000 integers

References

PPL – Parallel Programming Library – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_the_Parallel_Programming_Library

Using TTask – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_TTask_from_the_Parallel_Programming_Library

Using TParallel::For – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_TParallel.For_from_the_Parallel_Programming_Library

Using TTask::IFuture – http://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_TTask.IFuture_from_the_Parallel_Programming_Library

Algorithms Library – https://en.cppreference.com/w/cpp/algorithm

std::sort – https://en.cppreference.com/w/cpp/algorithm/sort

C++Builder Product Information

C++Builder Product Page – Native Apps that Perform. Build Windows C++ Apps 10x Faster with Less Code
C++Builder Product Editions – C++Builder is available in four editions – Professional, Enterprise, Architect and Community (free). C++Builder is also available as part of the RAD Studio development suite.