Kylix Newsletter #3
INDEX
1. A FEW WORDS FROM THE EDITOR
2. INTRODUCTION TO BORLAND OBJECT PASCAL (II)
3. PORTING ISSUES: FILE SYSTEM
4. WHAT'S NEXT?
________________________________________________________________________
1. A FEW WORDS FROM THE EDITOR
Since there was no opposition, this will be the last issue of the Kylix
Newsletter. All subscribers to this newsletter and to the Delphi
Newsletter will automatically be subscribed to the new Pascal Newsletter
that will replace these two publications.
Your contributions, comments, critics and questions are welcome. Please
stay in touch!
Yours,
Ernesto De Spirito
eds2004 @ latiumsoftware.com
________________________________________________________________________
JfControls Library. Multi-language. Multi-appearance. Skins. Privileges.
More than 40 integrated and customizable components. Impressive GUI.
Centralized resources administration. Multiple programming problems
solved. For Delphi 3-6 and C++ Builder 3-5. http://www.jfactivesoft.com/
________________________________________________________________________
2. INTRODUCTION TO BORLAND OBJECT PASCAL (II)
This article is an introduction to OOP in Delphi. It will not teach you
OOP --we assume you are already familiar with it--, but it will show the
basics of working with classes in Borland Object Pascal.
Classes are declared in a type declaration of a program or a unit's
interface section. A class is made up of members, that can be data
fields, methods and properties.
Let's see it with an example of a class that we will call TRectangle and
that represents a rectangle:
type
TRectangle = class
X1, Y1, X2, Y2: single;
function getWidth: single;
function getHeight: single;
procedure setWidth(Width: single);
procedure setHeight(Height: single);
end;
This class has four fields (X1, Y1, X2 and Y2) that represent the
coordinates of the corners of the rectangle, and four methods to get/set
the size of the rectangle. In the implementation section this methods
could be defined as follows:
function TRectangle.getHeight: single;
begin Result := Y2 - Y1; end;
function TRectangle.getWidth: single;
begin Result := X2 - X1; end;
procedure TRectangle.setHeight(Height: single);
begin Y2 := Y1 + Height; end;
procedure TRectangle.setWidth(Width: single);
begin X2 := X1 + Width; end;
To declare objects of a class is the same as declaring variables of any
type. For example:
var
r1, r2: TRectangle;
Here we have declared two objects of TRectangle class. In C++ you are
limited to declaring objects inside functions. Memory is allocated for
those objects in the stack and their default constructors are called
upon entering the function. Also, destructors are automatically called
when returning from the function. This is not the case in Object Pascal.
Object variables are references (like pointers, but the dereference
operator ^ is not needed) so they use minimum allocation on the data
segment or the stack, and you have to explicitly create and free the
object. For example:
r1 := TRectangle.Create; // Creates the object
r1.X1 := 10;
r1.Y1 := 35;
r1.X2 := 50;
r1.Y2 := 60;
ShowMessage('Width = ' + FloatToStr(r1.getWidth) + #13
+ 'Height = ' + FloatToStr(r1.getHeight));
r1.Free; // Releases the object
When an object is created, its memory is allocated in the heap and you
get back a reference that you should assign to the object variable.
Later you should free the memory calling the Free method. Now, we didn't
declare Create and Free, so where did they come from? They are methods
of TObject, and every class is a descendant of TObject, so every class
has these methods available to it. So,
TRectangle = class
and
TRectangle = class(TObject)
are the same thing: TRectangle is a direct descendant of TObject. Use
this syntax if you want a class to inherit (derive) from another class:
type
class2 = class(class1)
<member declarations>
end;
Here class2 inherits from class1, or you can say it is a derived class
of class1, or that class1 is the base class of class2, etc.
Now, we said apart from fields and methods, a class might have
properties. What is a property? It is a convenient construct to allow a
further level of hiding. For programmers using the class, properties
look very much like fields, but they can really be fields or method
calls. For example, let's add two properties to our TRectangle class:
type
TRectangle = class
...
property Width: single read getWidth write setWidth;
property Height: single read getHeight write setHeight;
end;
This way, a code like this one:
r1.Width := 20;
r1.Height := 10;
ShowMessage('Width = ' + FloatToStr(r1.Width) + #13
+ 'Height = ' + FloatToStr(r1.Height));
will automatically be translated by the compiler to:
r1.setWidth(20);
r1.setHeight(10);
ShowMessage('Width = ' + FloatToStr(r1.getWidth) + #13
+ 'Height = ' + FloatToStr(r1.getHeight));
Members of a class can be private, protected or public. Private members
are only accessible within the unit where the class is declared, so all
classes within a unit are "friends" in C++ jargon. A protected member is
like a private member, except it can also be accessible to descendants
of a class, even if they are declared in another unit. Public members
are always visible. There can also be published members, which are
public members for which RTTI (RunTime Type Introspection) is generated,
allowing an application to query the fields and properties of an object
and to locate its methods at runtime. Delphi uses RTTI to access the
values of properties when saving and loading form files (.DFM), to
display properties in the Object Inspector, and to associate specific
methods (event handlers) with specific properties (events), so published
members are very common when writing visual components.
Here is an example of class using all these kinds of members:
{$TYPEINFO ON}
TRectangle = class
private
FX1, FY1, FX2, FY2: single;
protected
function getWidth: single;
function getHeight: single;
procedure setWidth(Width: single);
procedure setHeight(Height: single);
public
function getSurface: single;
published
property X1: single read FX1 write FX1;
property Y1: single read FY1 write FY2;
property X2: single read FX2 write FX2;
property Y2: single read FY2 write FY2;
property Width: single read getWidth write setWidth;
property Height: single read getHeight write setHeight;
property Surface: read getSurface;
end;
{$TYPEINFO OFF}
We used a compiler directive to turn RTTI on an off. As you can see, the
Surface property is read-only.
A class can have a constructor and a destructor:
type
TFile = class
private
FFile: File;
public
constructor Create(FileName: string);
destructor Destroy(); override;
end;
implementation
constructor TFile.Create(FileName: string);
begin
Assign(FFile, FileName);
Reset(FFile, 1);
end;
destructor TFile.Destroy;
begin
Close(FFile);
end;
In this example, the constructor Create redefines the default Create
constructor of TObject and takes as a parameter a file to be opened. The
destructor Destroy closes the file. We must use a destructor to free all
the allocated resources by an object.
A class can have many constructors, for example overloading the Create
method or declaring new constructors:
type
TFile = class
private
FFile: File;
public
constructor Create(FileName: string); overload;
constructor Create(FileName: string; Mode: integer); overload;
constructor CreateWithSize(FileName: string; Size: integer);
destructor Destroy(); override;
end;
To allow polymorphism, a method can be declared as virtual or dynamic in
a base class and then overridden if necessary in the derived classes.
For example:
interface
type
TFigure = class(TObject)
procedure Show; virtual;
end;
TCircle = class(TFigure)
procedure Show; override;
end;
TSquare = class(TFigure)
procedure Show; override;
end;
TOval= class(TCircle)
procedure Show; override;
end;
implementation
procedure TFigure.Show; begin ShowMessage('Figure'); end;
procedure TCircle.Show; begin ShowMessage('Circle'); end;
procedure TSquare.Show; begin ShowMessage('Square'); end;
procedure TOval.Show; begin ShowMessage('Oval'); end;
TFigure is the base class of TCircle and TSquare, and it declares one
virtual method (Show) which is overridden in its descendants. The actual
Show method that will be called will the depend on the actual object a
TFigure variable is referencing at the time of the call, so it is
determined at runtime:
var
Figure: TFigure;
Circle: TCircle;
Square: TSquare;
Oval: TOval;
begin
// First create the objects
Circle := TCircle.Create;
Square := TSquare.Create;
Oval := TOval.Create;
Figure := Circle; // Figure now references a TCircle object
Figure.Show; // TCircle.Show is called
Figure := Square; // Figure now references a TSquare object
Figure.Show; // TSquare.Show is called
Figure := Oval; // Figure now references a TOval object
Figure.Show; // TOval.Show is called
// Release the objects
Circle.Free; Square.Free; Oval.Free;
end;
If Show wasn't declared virtual, it would have been a static method and
therefore in the above example TFigure.Show would have been called three
times.
Instead of "virtual", you can use the keyword "dynamic". The only
difference is that virtual is optimized for speed while dynamic is
optimized for code size, so -for performance reasons- virtual is
preferred to implement polymorphism while dynamic is only used for
the rare cases when a base class has virtual methods that are only
occasionally overridden by derived classes.
We will continue with OOP in Object Pascal in the next issue (in the
first issue of the Pascal Newsletter, actually).
________________________________________________________________________
3. PORTING ISSUES: FILE SYSTEM
This is not going to be a big headache, but keep aspirins handy just in
case. :-) Linux and Windows file systems are quite different and you
must take this fact into account if you want to make your applications
portable.
3.a) Filename case sensitivity
------------------------------
Linux file names are case sensitive, so README.TXT, README.txt,
Readme.txt and readme.txt are all different names and therefore they can
coexist in the same directory, while in Windows it wouldn't be possible
since it treats all those names like a single one.
In Windows, you can open a file named Readme.txt using README.TXT
instead, but this won't happen in Linux. You must use the exact case
(Readme.txt) if you want to open that file. To be portable, an
application must preserve the case of filenames.
Another solution could be always converting the filenames to lowercase.
The only problem with this approach is that if you prompt an advanced
Linux user for a filename, he/she will think that HELLO, Hello and hello
are different file names, while your application would be always
converting them to hello, and that could be a bother... although it
could be a blessing for Linux novices used to Windows, so you should
carefully consider the target audience of your application and whether
case distinctions or allowing mixing capital and lowercase letters is
important or not.
3.b) Path separator
-------------------
This is another difference to take into account. In Windows, the path
separator is the backslash ("\") while in Linux it is the forward slash
("/").
Delphi for Linux and Delphi 6 for Windows will have a predefined
constant named PathSeparator that will contain the appropriate path
separator character, so you should translate code like this:
AssignFile(f, 'data\file001.dat');
into code like this:
AssignFile(f, 'data' + PathSeparator + 'file001.dat');
to make your code portable.
If you use a previous version of Delphi, then you should define
PathSeparator yourself. A way to do this is defining a simple unit:
unit LinuxCompatibility;
interface
const PathSeparator: char = '\';
implementation
end.
Then, in every unit you should add LinuxCompatibility to the uses clause
for the Win32 compile:
{$IFDEF Win32}
uses
Windows, ... LinuxCompatibility;
{$ELSE}
uses
Linux, ...;
{$ENDIF}
You will have to use conditional compilation for the uses clause anyway
because the units for Windows and for Linux will be different, so the
only "extra charge" for the path separator is adding the
LinuxCompatibility unit to the uses clause.
3.c) Drive letters
------------------
Windows uses letters followed by a colon (":") to designate drive units.
For example usually "A:" refers to the diskette drive, "C:" refers to
the first partition of the boot hard disk and "D:" can refer to another
partition in the same or another hard disk, a network drive or to the
CD-ROM drive for example.
There are no drive letters in Linux. Devices are mapped as files or
directories under one single root directory ("/"). For example, the
first diskette drive is referenced by /dev/fd0 and is usually mounted as
/mnt/floppy using this command:
mount -t msdos /dev/fd0 /mnt/floppy
For example what in Windows is A:\DIR\FILENAME.EXT in Linux it would be
/mnt/floppy/DIR/FILENAME.EXT if you are sure /dev/fd0 is mounted to
/mnt/floppy because the user can mount it using a different name every
time, so you should always prompt the user for locations. If your
program makes a backup for example, in Linux allow the user to choose a
directory rather than a disk drive, and don't show "A:\" as default! :)
If you use the standard dialogs and Win32 controls to get filenames or
locations then you shouldn't have much problem, but if you somehow
separated drives from paths then you should use conditional compilation
to ignore drives in Linux and that's it!
________________________________________________________________________
4. WHAT'S NEXT?
The first issue of the Pascal Newsletter will appear probably next week.
We will continue the introduction to Object Pascal and we will keep
discussing porting issues, and also there will be articles about Delphi
programming in Windows. See you then!
________________________________________________________________________
YOU CAN HELP US
We need your help to keep this newsletter going and growing. You can
help by referring the newsletter to your colleagues:
http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php
Or you can help by voting for us in some or all of these rankings to
give more visibility to our web site and thus increase the number of
subscriptions to this newsletter:
http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium
http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E
http://www.programmingpages.com/?r=latiumsoftwarecomenpascal
http://www.top219.org/cgi-bin/vote.cgi?delphi&83
http://top100borland.com/in.php?who=20
http://top200.jazarsoft.com/delphi/rank.php3?id=latium
http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80
It's just a few seconds for you that REALLY mean a lot to us.
________________________________________________________________________
This newsletter is provided "AS IS" without warranty of any kind. Its
use implies the acceptance of our licensing terms and disclaimer of
warranty you can read at http://www.latiumsoftware.com/en/legal.php
where you will also find a note about legal trademarks. Articles are
copyright of their respective authors and they are reproduced here with
their permission. You can redistribute this newsletter as long as you do
it in full (including copyright notices), without changes, and gratis.
________________________________________________________________________
Main page: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php
Group home page: http://groups.yahoo.com/group/pascal-newsletter/
Subscribe/join: pascal-newsletter-subscribe@yahoogroups.com
Unsubscribe/leave: pascal-newsletter-unsubscribe@yahoogroups.com
Problems with your subscription? eds2004 @ latiumsoftware.com
________________________________________________________________________
Latium Software http://www.latiumsoftware.com/en/index.php
Copyright (c) 2000 by Ernesto De Spirito. All rights reserved.
________________________________________________________________________
|