[fpc-pascal]Linked List/ pointers/ casting/ OOP Question

Anton Tichawa anton.tichawa at chello.at
Sun Jul 11 23:09:04 CEST 2004


Hello!

Ron Weidner wrote:

>>Your design is the right one for the problem, -and-
>>FreePascal supports 
>>this design very well. What you seem to miss is the
>>correspondence 
>>between your design ideas and FreePascal's syntax
>>and semantics.
>>
>>Sorry, but before proceeding I ask you to make a
>>decision (because else 
>>writing any code examples might be wasting our
>>time):
>>
>>FreePascal offers two different approaches to your
>>design: Objects and 
>>Classes. Objects is what you're using now. Classes
>>are essentially 
>>pointers to Objects, but they also simplify the
>>syntax, and they allow 
>>pointer types to inherit from each other. This
>>latter capability 
>>perfectly addresses the problem of choosing the
>>right type for the 
>>parameter X in AddWidget(X). So, before proceeding,
>>please decide if you 
>>want to continue using good old Objects, or if you
>>want to switch to the 
>>newer and more powerful Classes. If unsure, choose
>>Objects. Also, if you 
>>cannot easily change the declaration of a widget
>>because it's legacy 
>>software, choose Objects. I'll then post code
>>examples using the 
>>approach you choose.
>>
>>Anton.
>>
>>    
>>
>
>When you put it like that, it seems silly to use
>Objects over Classes.  I've only written 4 files for
>this project, and they could probably be easily ported
>from Objects to Classes.  So if what you are saying is
>that the Classes way of solving this (and like)
>problem, and since I have no legacy software to
>support, I would love to see a working example of this
>code done with Classes.
>
>Thankyou.
>
>Ron_W
>
>
>  
>
<snip>

program widgets; // free demo of a list of class instances, by Anton 
Tichawa.


// first, the common ancestor class, CWidget, is declared.

type CWidget = class(TObject) // TObject is, despite it's name, a class 
type here
Tag: String;
constructor Create(ATag: String);
// the destructor, Destroy, defaults to TObject.Destroy
end;


// next, the list. It's still a record, i. e. smaller and faster 
compared to classes.

type PWidgetList = ^TWidgetList; TWidgetList = record
Widget: CWidget; // a pointer to an instance of CWidget or a descendant
Next: PWidgetList;
end;


// next, the two descendants of CWidget (inheriting all data and methods 
from CWidget):

type CWidgetEntity = class(CWidget) // maybe unused now?
end;

type CWidgetContainer = class(CWidget)
HeadWidgetPtr: PWidgetList;
CurWidgetPtr: PWidgetList;
constructor Create(ATag:string); // constructors need no 'override'
destructor Destroy; override; // overrides, i. e. replaces TObject.Destroy
procedure AddWidget(AWidget: CWidget); // might be a container, too
procedure Print(ALevel: integer);
end;

// 'the' widget container

var TheWidgetContainer: CWidgetContainer; // a pointer to an instance on 
the heap


// CWidget methods:

constructor CWidget.Create(ATag: String);
begin
inherited Create; // this calls TObject.Create
Tag := ATag;
end;


// CWidgetContainer methods:

constructor CWidgetContainer.Create(ATag: String);
begin
inherited Create(ATag); // = CWidget.Create, assigns ATag to Tag etc.
CurWidgetPtr := nil;
HeadWidgetPtr := nil;
end;

destructor CWidgetContainer.Destroy;
begin

// caution: here, all deallocation / destruction should be done

inherited Destroy; // call to the inherited Destructor, TObject.Destroy
end;

procedure CWidgetContainer.AddWidget(AWidget: CWidget);
var MyNewListPtr: PWidgetList;
begin
new(MyNewListPtr);

// AWidget might be a widget or a container, but we don't care here

MyNewListPtr^.Widget := AWidget;
MyNewListPtr^.Next := HeadWidgetPtr;
HeadWidgetPtr := MyNewListPtr;
end;

procedure CWidgetContainer.Print(ALevel: integer);
var i: integer;
var indent: String;
var MyWidget: CWidget; // might be a CWidget or CWidgetContainer
begin

// for the sake of simplicity, the printout is reversed. But it's indented:

indent := '';
for i := 0 to ALevel - 1 do begin
indent := indent + ' ';
end;
writeln(indent + 'widget container ' + Tag + ':' );
CurWidgetPtr := HeadWidgetPtr;

repeat
if CurWidgetPtr = nil then break;
MyWidget := CurWidgetPtr^.Widget;
if MyWidget = nil then begin // just to be sure

// InheritsFrom is a method of TObject. This is runtime type info (RTTI).
// We have to check for the more specialized CWidgetContainer first:

end else if MyWidget.InheritsFrom(CWidgetContainer) then begin

// InheritsFrom just told us it's a CWidgetContainer (at runtime). The
// compiler does not know this, and would normally reject the Print
// method. So, we need a type cast:

CWidgetContainer(MyWidget).Print(ALevel + 1);

end else if MyWidget.InheritsFrom(CWidget) then begin
writeln(indent + ' ordinary widget ''' + MyWidget.Tag + ':');
end;
CurWidgetPtr := CurWidgetPtr^.Next;
until false;
end;

var MyWidgetContainer: CWidgetContainer; // a temporary buffer for 
sub-containers

begin

// create 'the' widget container

TheWidgetContainer := CWidgetContainer.Create('TheWidgetContainer');

// add some widgets and containers

TheWidgetContainer.AddWidget(CWidget.Create('Widget-A'));

MyWidgetContainer := CWidgetContainer.Create('WidgetContainer-B');
MyWidgetContainer.AddWidget(CWidget.Create('Widget-B1'));
MyWidgetContainer.AddWidget(CWidget.Create('Widget-B2'));

// among other things, a CWidgetContainer is still a CWidget, so the 
compiler
// accepts passing MyWidgetContainer without type cast.

TheWidgetContainer.AddWidget(MyWidgetContainer);
TheWidgetContainer.AddWidget(CWidget.Create('Widget-C'));

// print

TheWidgetContainer.Print(0);

// caution: cleaning up (dispose, destroy) not yet implemented ..

end.

//
// the printout reads (GNU/Linux, fpc 1.0.10, sequence of members is 
reversed):
//
// widget container TheWidgetContainer:
// ordinary widget 'Widget-C:
// widget container WidgetContainer-B:
// ordinary widget 'Widget-B2:
// ordinary widget 'Widget-B1:
// ordinary widget 'Widget-A:
//

hth,

Anton.







More information about the fpc-pascal mailing list