If you find this article useful then please consider making a donation. It will be appreciated however big or small it might be and will encourage Brian to continue researching and writing about interesting subjects in the future.
You can see from the spider slide that the products are (in order of release):
The environment is basically a cross between Delphi 2 and 3. And 1. Clearly the component palette looks just the same as it does in Delphi 2. However, most components have one or two extra properties, as sported by Delphi 3, such as ImeMode, for Win32 international language input support. But all of this notwithstanding, the menu structure is rather like that of Delphi 1, with an Options menu giving access to project and environment options. This is probably to make it more familiar to users of Borland C++ which has an Options menu.
The IDE is built out of the same source code base as Delphi's, with a few different files pulled in here and there, so you can feel sure of feeling at home. At least until you need to start writing code.
The most important thing to remember is to have lots of disk space available. C++Builder is very disk-hungry. The incremental linker technology generates four files per project which will consume a lot of disk space as you move from project to project. Even on a fresh project they amount to 3.25Mb. Each time you modify the project and recompile, they stand to increase in size. Also, there is a debugging information file that seems to have a minimum size of 768kb. On top of this, depending on your options, you may have some default incremental linker buffer files in the BIN directory taking up over 4.6 Mb.
But it's not just the files in the project directory that keep chomping bytes out of your hard drive. In my LIB subdirectory six files have been created over the last few days, related to pre-compiled headers amongst other things, which collectively take up just under 19Mb
Above all, you need to remember to avoid rebuilding the project (Project | Build All) as this takes much longer than is really bearable - all referenced C++ header files are recompiled and the incremental linker storage buffers are ignored. Generally, you should just make the project (Project | Make or Ctrl+F9) or run it (which invokes the make process) which will take most headers from the pre-compiled header caches that are stored here and there. My simple project mentioned above took 71 seconds to rebuild, just after the 3 second make process.
C++Builder comes with two IDE compilers for automatically compiling either C++ or Pascal source modules. This means that if you have a team developing a project then some can develop code in Pascal using Delphi and some can develop in C++ using C++Builder. It also means that C++Builder users can take advantage of the wealth of Delphi source code available.
C++Builder cannot manufacture Pascal files, but you can add existing Pascal files into your C++Builder projects.
As a small side note, the default project icon has been revamped for C++Builder.
The main project source file itself is always a C++ file (see below) - there is no getting away from that. If you examine this listing you can immediately see differences and similarities to what you are familiar with in Delphi.
#include <vcl\vcl.h> #pragma hdrstop //--------------------------------------------------------------------------- USEFORM("Unit1.cpp", Form1); USERES("Project1.res"); //--------------------------------------------------------------------- WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { Application->Initialize(); Application->CreateForm(__classid(TForm1), &Form1); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } return 0; }
When you open a C++Builder project, you do not have to pick the project source file name, as you do in Delphi. All C++ source files have a .CPP extension and so it isn't always easy to spot which file represents the project. Normally, you pick the project makefile which has a .MAK extension. A makefile is a historical text file that can be processed by a command-line tool called MAKE.EXE. It contains various options, rules and dependencies that make sure the most up to date version of the target file can be generated with least effort by the other command-line tools. The MAK file can still be used on the command-line, but most users will be happy to let the IDE interpret it.
To give you an idea of a project's structure and layout, the following sections list all the files that might get manufactured for a given project in C++Builder, and also highlights which are important for archival and, by implication, which can safely be deleted.
The list shows that C++Builder stores form code in two files, a .CPP file (source file) and a .H file (header file). In fact this is the same for any unit - it is split into two files. The .H file can be considered to be much the same as the interface section of a Pascal unit and the .CPP file the same as the implementation section. For one source module to refer to items in another source module, it (or its header) must include the other module's header file. This is achieved with a #include directive and is analogous to adding a unit name into the uses clause of the implementation or interface section. This process can be slightly automated by using File | Include Unit Hdr... (Alt+F11).
To access various C and C++ RTL routines you will need to include various headers from the large collection supplied in the INCLUDE directory hierarchy.
Filename | Purpose |
---|---|
PROJECT1.MAK | Project options file, as seen by selecting View | Project Makefile containing all the options from the project options dialog's Compiler, Linker and Directories/Conditionals pages. This stands for MAKefile. |
PROJECT1.CPP | Project source file, as seen by selecting View | Project Source |
UNIT1.CPP | Source module that implements a form's functionality |
UNIT1.H | Header file that defines a form class |
UNIT1.DFM | Binary file describing the form, all its components and their properties |
PROJECT1.RES | A resource file containing the project's icon. If no icon is specified in the project option dialog's Application page, a default one is supplied. This file needs to be archived if you set up a specific icon |
PROJECT1.EXE or PROJECT1.DLL | The generated executable |
UNIT1.OBJ | The compiled form of UNIT1.CPP. OBJ stands for OBJect file |
If Options | Project... | Linker | Use incremental linker is enabled when the project is compiled (as it is by default), the following files are manufactured to support incremental linking:
Filename | Purpose |
---|---|
PROJECT1.ILC | Incremental linker storage buffer |
PROJECT1.ILD | Incremental linker storage buffer |
PROJECT1.ILF | Incremental linker storage buffer for functions |
PROJECT1.ILS | Incremental linker storage buffer for symbols |
If the Options | Environment... | Editor display | Create backup file option has been selected, the following additional files will be generated each subsequent time the project is saved:
Filename | Purpose |
---|---|
PROJECT1.~MA | Backup of project makefile |
PROJECT1.~CP | Backup of project source file |
UNIT1.~DF | Backup of binary form file |
UNIT1.~CP | Backup of module source file |
UNIT1.~H | Backup of module source header file |
If Options | Environment... | Preferences | Desktop has been selected, the following file will be generated when the project is closed:
Filename | Purpose |
---|---|
PROJECT1.DSK | .INI file with different extension containing all the information required so when the project is re-opened, Delphi can restore the desktop just as it was when closed. |
If Options | Project... | Compiler | Debug information is enabled the following file will contain all the debugger symbol information, otherwise this file will contain very little. It is principally for use by the environment but can also be used by Turbo Debugger (TD32.EXE).
Filename | Purpose |
---|---|
PROJECT1.TDS | Turbo Debugger Symbol file |
If the Map file option from the Linker page of the project options dialog is set to anything other than Off, the following file is generated.
Filename | Purpose |
---|---|
PROJECT1.MAP | Text file containing varying details of information of use when performing low-level debugging tasks |
If the command-line tool CONVERT.EXE (found in the BIN subdirectory) has been used (it converts binary form files to text files and text files back to binary forms), the following file will be present.
Filename | Purpose |
---|---|
UNIT1.TXT | Text file containing description of form and all components on form, including values of all non-default properties |
If there was more than one form in the project, there would be files similar to the UNIT1.* files generated for each additional form.
The primary files to be concerned with are the .MAK, .CPP, .H and .DFM files, although the .RES file will probably also need to be saved.
Filename | Purpose |
---|---|
PROJECT1.DPR | Project source file, as seen by selecting View | Project Source |
UNIT1.PAS | Source module that represents a form |
UNIT1.DFM | Binary file describing the form and all its components. Stands for Delphi ForM. |
PROJECT1.RES | A resource file containing the project's icon. If no icon is specified in the project option dialog's Application page, a default one is supplied. This file needs to be archived if you set up a specific icon |
PROJECT1.DOF (Delphi 1 uses the .OPT extension) | .INI file with different extension containing all the options from the project options dialog's Compiler, Linker and Directories/Conditionals pages as well as anything specified in Run | Parameters.... If you change any of these settings, this file should be kept. Stands for Delphi Options File. |
PROJECT1.EXE or PROJECT1.DLL | The generated executable |
UNIT1.DCU | The compiled form of UNIT1.PAS. DCU stands for Delphi Compiled Unit. |
Delphi 3 adds two more files to a regular project set during the compilation cycle.
Filename | Purpose |
---|---|
PROJECT1.DRF | Resource file that lists all required Delphi 3 packages - stands for Delphi Requirements File. |
PROJECT1.STR | Resource file containing a string table that houses all the project's resource strings (new Delphi 3 feature). |
If the Delphi 2 option Tools | Options... | Display | Create backup file or the Delphi 1 option Options | Environment... | Editor display | Create backup file has been selected, the following additional files will be generated each time the project is saved:
Filename | Purpose |
---|---|
PROJECT1.~DP | Backup of project source file |
UNIT1.~DF | Backup of binary form file |
UNIT1.~PA | Backup of module source unit |
If the Desktop option has been selected from the Preferences page of Delphi 1's Options | Environment... or Delphi 2's Tools | Options... dialog, the following additional files will be generated when the project is closed (the latter only if Desktop and symbols is selected in the same options page):
Filename | Purpose |
---|---|
PROJECT1.DSK | .INI file with different extension containing all the information required so when the project is re-opened, Delphi can restore the desktop just as it was when closed |
PROJECT1.DSM | Symbol table for your application. If you load your project, Delphi will not need to compile your project to enable the Object Browser etc. |
If the Map file option from the Linker page of the project options dialog is set to anything other than Off, the following file is generated.
Filename | Purpose |
---|---|
PROJECT1.MAP | Text file containing varying details of information of use when performing low-level debugging tasks |
If the command-line tool CONVERT.EXE (found in the BIN subdirectory) has been used (it converts binary form files to text files and text files back to binary forms), the following file will be present.
Filename | Purpose |
---|---|
UNIT1.TXT | Text file containing description of form and all components on form, including values of all non-default properties |
If there was more than one form in the project, there would be files similar to the UNIT1.* files generated for each additional form.
The primary files to be concerned with are the .DPR, .PAS and .DFM files, although others that may need to be saved are the .RES and .DOF/.OPT files.
One nice thing is the addition of more keystroke shortcuts for various menu items. For example Project | Add To Project... is Shift+F11, File | Open Project... is Ctrl+F11, View | Call Stack is Ctrl+F3, Project | Options... is Shift+Ctrl+F11. Also, View | CPU (which is available in Delphi 2 and 3 if you set up an undocumented registry entry) is Alt+F2. Lastly, the equivalent of Delphi's File | Use Unit... in C++Builder is File | Include Unit Hdr... and that has a shortcut of Alt+F11.
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { // This is the form constructor } void __fastcall TForm1::Button1Click(TObject *Sender) { } void __fastcall TForm1::FormCloseQuery(TObject *Sender, Boolean &CanClose) { }
Notice the repeated use of the __fastcall modifier. This is the same as Delphi's register keyword and implements the same calling convention as 32-bit Delphi defaults to. If you ever set up event handlers by hand, do not forget to use this modifier.
Also notice that the standard Sender parameter, as taken by practically all event handlers, is explicitly declared to be a pointer to a TObject by way of an asterisk. This is emphasising that VCL objects are really pointers to objects, despite what the Delphi syntax might suggest. In C++Builder, VCL objects must be declared as pointers since they must live on the heap. You cannot declare a VCL object without the pointer syntax. This implies that stack-based VCL objects are nor allowed.
Notice in the OnCloseQuery event handler in the listing above, one of the parameters, CanClose, is declared with an ampersand. This is C++ syntax for a pass by reference parameter - like a Delphi var parameter. The event handler can write a value to the parameter and the code that called the event handler will see the new value.
A couple of important points to remember are:
Certain Delphi-specific types such as Pascal strings and sets are implemented as classes. These will be looked at more closely later. This listing shows a few variables being declared and used:
void __fastcall TForm1::Button1Click(TObject *Sender) { ShowMessage("In Button1's OnClick handler"); Integer Count, Result = 10; String S = "Variables declared after a code statement"; ShowMessage(S); }
When you need to dynamically create VCL objects in C++Builder, you do as is shown for a form object in the next listing. This includes the declaration of a VCL object pointer (or object reference as it would be called in Delphi) and simultaneous construction using the new operator. Objects are destroyed not by calling Free(), but by using the delete operator, for C++ conformance.
void __fastcall TForm1::About1Click(TObject *Sender) { TAboutBox *AboutBox = new TAboutBox(Application); AboutBox->ShowModal(); delete AboutBox; }
You have to be a bit careful with the expressions you write within these brackets since C++ operator precedence is a little different to that in Delphi.
To find Delphi's operator precedence table, choose Help | Help Topics and enter precedence. In C++Builder you can choose Help | Keyword Search and type precedence, press Enter and choose Precedence of operators. You will find that the equality operator (==) takes precedence over the Boolean "and" (&&) and "or" (||) operators, the opposite to how it is in Delphi.
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { String S; if (Y > ClientHeight / 2) S = "Bottom "; else S = "Top "; if (X > ClientWidth / 2) S += "right"; else S += "left"; Caption = S + " quadrant"; }
If you need the same actions performed for many values, you can list one case after another, as is done for mrNo and mrAbort in the listing. Notice that you need to use break to stop execution going onto the next case statements. In C++, the case values are used to indicate where to start execution, not to indicate which section to solely execute.
As in Pascal, the default section which executes if no other values match, is optional.
void __fastcall TForm1::Button1Click(TObject *Sender) { TForm2 *Form2 = new TForm2(Application); switch (Form2->ShowModal()) { case mrOk: { Caption = "Okay"; Color = clBlue; } break; case mrNo: case mrAbort: Caption = "No or Abort"; break; default: Caption = "Some other caption"; } delete Form2; }
So C++Builder implements a new string class called AnsiString, defined in the DSTRING.H header file. There is also a type called String in SYSDEFS.H, defined to be the same as AnsiString. Additionally, for supporting old-style Delphi 1 small strings, there is a template class called SmallString and a ShortString type (the same identifier as in Delphi), defined in terms of the SmallString type.
Incidentally, if you do not know what a template class, the details are not important for understanding this paper and so will not be covered here due to space considerations.
One side effect of strings being implemented as classes is that all strings that are declared without initial values will start off as empty strings. In Delphi, the initial value is undefined.
Another nice side-benefit of now having a C++ class for strings is that we have many constructors supplied. When a String parameter is required we are able to pass an Integer, a Double, a null-terminated C string, another String or a null terminated wide string (designed to support Unicode). This means that unlike in Delphi, we can call ShowMessage() with a parameter of 12. 12 will be passed to the String constructor and the resultant string is passed to ShowMessage().
Because strings are implemented as classes, normal string routines like Pos and Insert are implemented as member functions (or methods) of the relevant classes. To get a native C string from a String object, use the c_str() member function. Here are some examples of simple string manipulation:
void __fastcall TForm1::Button1Click(TObject *Sender) { String S = "Hello world"; S.Delete(1, 5); S.Insert("Goodbye cruel", 1); if (S.Pos("cruel") != 0) // != matches the Delphi <> operator Caption = S; //Displays "Goodbye cruel world" }
Unfortunately it is rather more involved in C++ due to the class implementation of sets and requires you to know the set type name. The listing below shows a Pascal call to MessageDlg and the equivalent C++ call. Notice that the TMessageDlgButtons class name is used to create a local set object. The << operator is used to include as many elements in the set as are required. The Set class also defines all the other set operators such as union (+), intersection (*) and difference (-).
MessageDlg('Hello from Delphi', mtInformation, [mbOK, mbCancel], 0); MessageDlg("Hello from C++Builder", mtInformation, TMsgDlgButtons() << mbOK << mbCancel, 0);The next listing shows an event handler that takes a set as a parameter (Shift). It allows you to Ctrl+drag from an edit control by checking if the ssCtrl is in the set. To manipulate the set there are a variety of methods and operators defined, such as Contains() which used in the snippet in the listing.
void __fastcall TForm1::Edit1MouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { if (Shift.Contains(ssCtrl)) Edit1->BeginDrag(False); }
void __fastcall TForm1::Button1Click(TObject *Sender) { enum TDayOfWeek {dwMon, dwTue, dwWed, dwThu, dwFri, dwSat, dwSun}; typedef Set<TDayOfWeek, dwMon, dwSun> TDaysOfWeek; TDaysOfWeek Weekend; Weekend << dwSat << dwSun; if (Weekend.Contains(dwMon)) ShowMessage("This is a long weekend"); else ShowMessage("Normal weekend"); }
There are two types of open arrays. Firstly there are those declared as, for example, array of Integer which take an arbitrary number of values of a fixed type. The TCanvas Polygon method takes an array of TPoints. The other type is declared as array of const and can take an arbitrary number of values of almost any type. The Format function and TTable FindNearest method both take these. Internally, array of const gets converted into array of TVarRec.
There are two listings that follow. The first one shows a Delphi event handler and the C++Builder equivalent can be found in the second. Three macros have been implemented to support open arrays - it is important to use EXISTINGARRAY if you are using an array that already exists in a variable. The macros are shorthand ways of manipulating instances of another couple of new classes, OpenArray and OpenArrayCount. Because of the implementation of class OpenArray, the word arbitrary as used above should be changed to "up to 19." The ARRAYOFCONST() macro is a shorthand way of referring to OPENARRAY(TVarRec, ()) although you do need to specify an extra pair of brackets before you get successful compilation. Delphi event handler
procedure TForm1.Button1Click(Sender: TObject); const Points: array[1..3] of TPoint = ((X: 1; Y: 1), (X: 100; Y: 1), (X: 50; Y: 100)); BigPoints: array[1..6] of TPoint = ((X: 1; Y: 200), (X: 100; Y: 200), (X: 50; Y: 300), (X: 151; Y: 200), (X: 250; Y: 200), (X: 200; Y: 300)); begin Canvas.Polygon(Points); Canvas.Polygon(Slice(BigPoints, 3)); Database1.ApplyUpdates([Table1, Table2]); Table1.FindNearest([Edit1.Text]); Caption := Format('%s (%d)', ['Error', 10]); Button1.Caption := Format('%s (%d)', ['Error', 20]); end;C++Builder of above Delphi event handler
void __fastcall TForm1::Button1Click(TObject *Sender) { TPoint Points[3] = {{1, 1}, {100, 1}, {50, 100}}; TPoint BigPoints[6] = {{1, 200}, {100, 200}, {50, 300}, {151, 200}, {250, 200}, {200, 300}}; Canvas->Polygon(EXISTINGARRAY(Points)); Canvas->Polygon(SLICE(BigPoints, 3)); Database1->ApplyUpdates(OPENARRAY(TDBDataSet *, (Table1, Table2))); Table1->FindNearest(OPENARRAY(TVarRec, (Edit1->Text))); Caption = Format("%s (%d)", OPENARRAY(TVarRec, ("Error", 10))); Button1->Caption = Format("%s (%d)", ARRAYOFCONST(("Error", 20))); }
Notice that I have chosen to use the C RTL sprintf() function instead of Format() in one handler. Also, three C++ RTL functions are being used to extract information about the source location of the exception and two C macros are used to identify the current line number and source file. This is something many Delphi users have wanted to be able to, but have been unable due to a lack of such information being generated by Delphi. Since the three functions are defined in a header not automatically included, we need the #include directive near the top of the file.
The help for two of these symbols states that the -xp+ compiler option must be used for them to work. This causes appropriate information to be generated by the compiler for the source code lines.
This means we must either add a #pragma option -xp+ to each of our source files, or modify the makefile. This can be done by choosing View | Project Makefile and locating the compiler flags - any line beginning with CFLAG - and adding the new option into the list. Unfortunately, these functions only appear to offer useful information if the exception was not caused by a VCL exception object. This is probably due to the VCL being written in Pascal and not having the relevant information to offer.
#include <except.h> ... try { //Stuff that might generate an exception Table2->FindNearest(ARRAYOFCONST((Edit1->Text))); StrToInt("Hello"); throw 1; } catch (EDBEngineError &E) { ShowMessage("Caught an EDBEngineError"); throw Exception("On receipt of one exception, this raises a different one"); } catch (EDatabaseError &E) { ShowMessage(Format("%s: %s", ARRAYOFCONST((E.ClassName(), E.Message)))); } catch (Exception &E) { ShowMessage("I can catch all other VCL exceptions"); throw; //re-raise same exception, like raise in Delphi except block } catch (...) { //This catches all other things that get thrown char msg[300], format[100]; strcpy(format, "Exception %s thrown in %s at line"); strcat(format, " %d and caught in %s at line %d"); sprintf(msg, format, __ThrowExceptionName(), __ThrowFileName(), __ThrowLineNumber(), __FILE__, __LINE__); ShowMessage(msg); }
So for all those mouse cursor changes, called to DisableControls() and EnableControls(), memory allocations and any other "resource allocation" statement pairs, we need to write a new class. The class constructor can do the allocation and the destructor can deallocate. I can foresee many people implementing much the same resource protection, or caretaker classes in much the same way. An example of such a class is shown below, written with inline functions (in other words the implementation of the methods can be found in the declaration of the class).
Template classes will aid reusability in many cases.
class TScreenCursorChanger { private: TCursor FCursor; public: TScreenCursorChanger(TCursor Cursor) // constructor { FCursor = Screen->Cursor; Screen->Cursor = Cursor; } ~TScreenCursorChanger() // destructor { Screen->Cursor = FCursor; } }; void __fastcall TForm1::Button2Click(TObject *Sender) { TScreenCursorChanger Obj(crHourGlass); //Local stack-based object //Do stuff that might cause an exception //Who cares if it does? The object above will still get destroyed //So the cursor will be changed back Sleep(1000); StrToInt("Hello"); //This will generate an exception }
if Sender is TMenuItem then with TMenuItem(Sender) do Checked = not Checked; try with (Sender as TMenuItem) do Checked := not Checked except on EInvalidCast do { nothing } end;There are several problems in translating these into C++.
Some possible ways of expressing the same sentiment in C++ appear below. You can see that compile-time typecasting can be done with brackets, much like in Delphi, or with static_cast.
if (dynamic_cast<TMenuItem *>(Sender)) ((TMenuItem *)Sender)->Checked = !((TMenuItem *)Sender)->Checked; if (dynamic_cast<TMenuItem *>(Sender)) { TMenuItem *Tmp = ((TMenuItem *)Sender); Tmp->Checked = !Tmp->Checked; } if (dynamic_cast<TMenuItem *>(Sender)) { TMenuItem *Tmp = static_cast<TMenuItem *>(Sender); Tmp->Checked = !Tmp->Checked; } if (InheritsFrom(Sender->ClassType(), __classid(TMenuItem))) { TMenuItem *Tmp = ((TMenuItem *)Sender); Tmp->Checked = !Tmp->Checked; }
Variant MSWord; MSWord = CreateOleObject("Word.Basic"); MSWord.OleProcedure("AppShow"); MSWord.OleProcedure("FileNew"); MSWord.OleProcedure("Insert", Edit1->Text + "\n"); MSWord.OleProcedure("EditSelectAll"); Variant CurValues = MSWord.OlePropertyGet("CurValues"); Variant SumInfo = CurValues.OlePropertyGet("FileSummaryInfo"); //Work out how many characters we just typed in MSWord.OleProcedure("Insert", " I Just inserted "); MSWord.OleProcedure("Insert", SumInfo.OlePropertyGet("NumChars")); MSWord.OleProcedure("Insert", " characters"); ShowMessage("Press Enter to terminate link to MS Word"); MSWord = Unassigned;
The first is that Delphi will always be a few steps ahead. Delphi 3 was released at about the same time as C++Builder 1. Delphi 3 brings along packages and ActiveForms to name but two features. C++Builder is based around the Delphi 2 VCL, with one or two items from Delphi 3 (for example the Input Method Editor support for international applications).
Secondly, despite C++Builder being able to compile Pascal units, it is inherently a C++ product. As a result, C++Builder project files are always C++, and C++Builder refuses to manufacture Pascal event handlers (see below). There are those Delphi hacks that will want to write a completely Delphi project every now and again.
Thirdly, being a C++ product it is rather slower at compiling a project than Delphi. It is also rather more disk-hungry whilst doing so.
Lastly, there is the language itself. C++ is not an easy language for a beginner to pick up. Pascal is generally considered to be easier to adopt and helps avoid many problems that C++ is happy to let you get away with
It is most cunning how Borland have migrated almost everything from the Delphi world into a direct C++ equivalent, using combinations of classes, templates, macros and other pre-processor directives.
In fact, I heard someone mention that during the latter stages of Delphi's original development, C++Builder was already being planned. The code name for Delphi was Delphi - they never managed to drop it. The code name for this new C++ version of Delphi was originally Sci-Fi, suggesting its ahead-of-its-time and out-of-this-world nature.
So now we can see Delphi and Sci-Fi side by side (note for the English: remember that the people who wrote these products pronounce the second syllable of Delphi the same as the second syllable of Sci-Fi).
However I would not recommend anyone to move exclusively to this product if they are a Delphi developer with no C++ experience, hoping that C++Builder will give them everything that Delphi does, plus more. Although this is very nearly true, since C++Builder compiles C++ and Delphi Pascal source files, the length of C++Builder's initial compile times might make them regret their decision.
Developers who have used C++ compilers appreciate Delphi's blinding compilation cycles. For existing Delphi developers however, C++Builder may well prove to a useful adjunct with the side benefit of allowing you to gain C++ experience in what is currently the least painful way.
For more information on Borland C++Builder, check out the Borland home page and the Borland C++Builder home page.
This paper is based on an article by Brian Long that appeared in The Delphi Magazine, Issue 19, March 1997.
Brian Long used to work at Borland UK, performing a number of duties including Technical Support on all the programming tools. Since leaving in 1995, Brian has spent the intervening years as a trainer, trouble-shooter and mentor focusing on the use of the C#, Delphi and C++ languages, and of the Win32 and .NET platforms. In his spare time Brian actively researches and employs strategies for the convenient identification, isolation and removal of malware. If you need training in these areas or need solutions to problems you have with them, please get in touch or visit Brian's Web site.
Brian authored a Borland Pascal problem-solving book in 1994 and occasionally acts as a Technical Editor for Wiley (previously Sybex); he was the Technical Editor for Mastering Delphi 7 and Mastering Delphi 2005 and also contributed a chapter to Delphi for .NET Developer Guide. Brian is a regular columnist in The Delphi Magazine and has had numerous articles published in Developer's Review, Computing, Delphi Developer's Journal and EXE Magazine. He was nominated for the Spirit of Delphi award in 2000.