Pascal - Class inheritance between two different files? - pascal

Say I have two files, characters.pas and ogre.pas. An ogre is a character, but I'm trying to separate the two files for cleanliness sake. In characters.pas I have
unit Characters;
{$mode objfpc}{$H+}
interface
type
TCharacter = class(TOBject)
private
// ...
public
// ...
published
// ...
end;
implementation
// Method bodies
end.
In ogre.pas I have
unit Ogre;
{$mode objfpc}{$H+}
interface
type
TOgre = class(TCharacter)
public
constructor create; override;
end;
implementation
constructor TOgre.create();
begin
// Banana banana banana
end;
end.
Adding a uses block anywhere in either of the .pas files throws an error, which leads me to believe that all classes that rely on inheritance must be in the same file as their parents. Am I missing something?

Yes you miss something: the use section. You have to declare that unit Ogre uses unit Characters:
unit Ogre;
{$mode objfpc}{$H+}
interface
uses
Characters;
type
TOgre = class(TCharacter)
public
constructor create; override;
end;
implementation
constructor TOgre.create();
begin
// Banana banana banana
end;
end.
read more:
unit example #FPC
the use of a second form #FPC wiki
Also note that if you want some fields to be visible from a TCharacter to a TOgre but still not accessible from the main program then you'll have to set their visibility to protected

Related

Changing a TMemo font size # runtime

How does one change a FMX.TMemo font size # runtime? In the following app the Spinbox1.Change method does not change the Memo.Font.Size. I have tried the BeginUpdate() and EndUpdate() methods of the TMemo. I have also tried Memo1.Repaint() and nothing seems to work. I have looked at every property, function and procedure for TMemo, but, I can't find what I need. Later versions of Delphi have TTextSettings for TMemo, but, XE5 does not. I also tried a class helper for TMemo to add a TTextSettings property, but, to no avail.
unit Unit13;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls,
FMX.Edit, FMX.Layouts, FMX.Memo;
type
TForm13 = class(TForm)
Memo1: TMemo;
ToolBar1: TToolBar;
SpinBox1: TSpinBox;
procedure FormCreate( Sender : TObject );
procedure SpinBox1Change(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form13: TForm13;
implementation
{$R *.fmx}
procedure TForm13.FormCreate( Sender : TObject );
begin
Memo1.Font.Size := SpinBox1.Value;
end;
procedure TForm13.SpinBox1Change(Sender: TObject);
begin
Memo1.Font.Size := SpinBox1.Value;
end;
end.
working with FMX is certainly not like the VCL.
The TMemo as many other controls has a property StyledSettings which has a few subfields, Family, Size, Style, FontColor and Other.
All except Other are True by default, so the control would automatically follow any used style setting. Set StyledSettings.Size = False in the Object Inspector and the TMemo will follow your own font size settings.
As I don't have any older XE-version than XE7, the above is tested with XE7. If XE5 doesn't have the corresponding settings, I will remove this answer as it doesn't answer your question.
Alternatively, in code you can write:
with Memo1 do
StyledSettings := StyledSettings - [TStyledSetting.ssSize];
This should work in XE5.

Creating a class in Pascal

I'm attempting to create a class in Pascal, I am a bit confused about the declaration and syntax. the main thing is an error I'm getting "Forward declaration not solved Tetromino.Rotate(LongInt)", I read that I need to declare my procedure in the implementation section but I'm not sure where I'm meant to be putting that. also if you notice anything else wrong with my class declaration please tell me.
program Tetris;
{$MODE OBJFPC}
uses crt, sysutils;
type
Tetromino = class
private
TempFace : array [0..15] of char;
public
Face : array[0..15] of char;
//constructor create(); (idk what this is but read somewhere that you need it)
procedure Rotate(rotation : integer);
end;
var
a,b,c,d,e,f,g : tetromino;
begin
ReadKey();
end.
In a program module there is no need for division into interface and implementation. Therefore the error description (to implement the procedure in the implementation section) is a little bit misleading. Still, it indicates that the implementation of the Rotate() procedure is missing.
So, the error is that you have declared a procedure in the Tetromino class, but the implementation of that procedure is missing. You need to implement it somewhere between the class declaration and the begin .. end block of the program.
In a unit module, which has named sections: interface and implementation, you declare classes in the interface section (if those classes are to be accessible from other modules) and implement them in the implementation section.
In the following I outline what you need to do in your program, including the constructor for Tetromino
program Tetris;
{$MODE OBJFPC}
uses crt, sysutils;
type
Tetromino = class
private
TempFace : array [0..15] of char;
public
Face : array[0..15] of char;
constructor create(); (idk what this is but read somewhere that you need it)
procedure Rotate(rotation : integer);
end;
var
a,b,c,d,e,f,g : tetromino;
constructor Tetromino.create;
begin
// constructor (automatically) aquires a block of memory
// to hold members of the class
// to do: initialize member fields of the instance
end;
procedure Tetromino.Rotate(rotation: Integer);
begin
// implementation of the Rotate() method
end;
begin
ReadKey();
end.

How do use destruct and create correctly

I have a Form. When the user clicks the TESTBUTON an array is generated (here with a loop) and an array is filled. (that works).
Now the user will be able to change some parameters an hit the button again.
Than I want to clear / free / destroy the old array an create it new.
I found a lot of examlpes for that but they not work (because I do not know where exatly to place the different procedures).
So I made this samplescript with all the sections.
Can someone move my procedures to the rigth place or send me an example that shows the correct implementation.
unit frmmywindow;
interface
uses
type
TArrayA = record
Field1:integer;
Field2:integer;
Field3:integer;
Field4:integer;
String5:string;
//other fields, strings, integers..
end;
private
{ private declarations }
public
{ public declarations }
destructor Destroy; override;
end;
var
var ArrayA : array of TarrayA;
implementation
destructor TArrayA.Destroy;
begin
ArrayA.Free;
inherited;
end;
procedure TArrayA.Free;
begin
if Assigned(self) then Destroy;
end;
procedure TForm1.btnTest(Sender: TObject);
var
x: integer;
reccount: integer;
begin
ArrayA.free:
ArrayA.create;
reccount := 1000;
for x := 1 to reccount do
begin
setLength(ArrayA,x+1);
ArrayA[x].field1 := 2000 - x;
ArrayA[x].field2 := x;
ArrayA[x].field3 := x;
ArrayA[x].field4 := x;
ArrayA[x].string5 := 'str' + inttostr(x);
end;
end;
Your code has a number of issues.
The main issue is that TArrayA is a record (normally I would link to the DocWiki documentation for structured types, but it seems to be down right now). Records are not classes, they don't have a destructor and you should not call Free on them. Records are so called value types. They don't even have a proper constructor, even if the syntax suggests they do. Record "constructors" are mere initializers.
Another issue is that you should never code Free yourself, not even for classes. Free is inherited from the root for all class instances, TObject. For classes, if you want to give it a destructor, override the inherited destructor:
destructor Destroy; override;
So the answer is: you don't use nor define them at all, for records.
How you should declare, define and use them for classes is described in the documentation.
As I already commented, you should get better acquainted with the language. I suggest you read the Delphi or Object Pascal Language Guide (name differs, depending on version), which is part of the documentation that is installed with Delphi.

Understanding the Pascal equivalent to C/C++ forward declaration of types

I know forward declarations from C/C++, and I know why and how they are used. When changing to Pascal, I'm missing a true equivalent of this feature. If I try to declare a type that I'm using (via pointer) in another type, like so,
type
TBRec = record
FA: ^TARec;
end;
TARec = record
FB: ^TBRec;
end;
Pascal (in fact, I tried with Free Pascal) implicitly deduces incomplete types from the ^ preceding an identifier (that is treated as an incomplete type). But this technique is limited to the scope of the same type definition section.
Is it possible to place related type definitions into separate units, and how to do this?
Edit: I falsely wrote class for TARec, it should be record in both cases.
The Pascalish way to do such things is:
type
// Forward declarations of pointers
PARec = ^TARec;
PBRec = ^TBRec;
TBRec = record
FA: PARec;
end;
TARec = record
FB: PBRec;
end;
Read more here
It works in case if all declarations are in the same unit and same type section.
In case if declarations are splitted in different units it can be solved using untyped pointers, explicit types casting and cyclic units references:
unit a;
interface
PARec = ^TARec;
TARec = record
FB: Pointer; // Untyped pointer
end;
implementation
uses
b; // Cyclic unit reference in the implementation section, see unit b below
procedure foo;
var
ra: PARec;
begin
ra := New(PARec);
ra^.FB := New(PBRec);
PBRec(ra^.FB)^... // PBRec is declared in the b unit
// or
TBRec(ra^.FB^)...
end;
end.
unit b;
interface
uses
a;
type
PBRec = ^TBRec;
TBRec = record
FA: PARec; // We can use PARec type because it already known here
end;
You can define a forward declaration by define an empty class or interface. You are able to specify the type later in the same type declaration section but you can already use it between the forwarded and the specified declaration.
type
TARec = class; // <-- Forward declaration
TBRec = record
FA: ^TARec;
end;
TARec = class
public
FB: ^TBRec;
end;
So it is not possible to declare a forwarded type in file A.Pas and specify it in file B.pas

Why doesn't calling a function with identical signatures in different units result in compiler error?

Why doesn't this code result in a compiler error? I would have expected error for example 'ambiguous call to "CallMe"'. Is this a bug in the compiler or in the language? This can worked around by using the unit name and a dot in front of the function call but this not shield user code and library code against name collisions. You think that your code did something but it did something else and that's bad.
uses
Unit2, Unit3;
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(CallMe(5)));
end;
unit Unit2;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
function CallMe(A: Integer) : Integer;
implementation
function CallMe(A: Integer) : Integer;
begin
Result := A * 2;
end;
end.
unit Unit3;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
function CallMe(A: Integer) : Integer;
implementation
function CallMe(A: Integer) : Integer;
begin
Result := A * -1;
end;
end.
From documentation:
If two units declare a variable, constant, type, procedure, or function with the same name, the compiler uses the one from the unit listed last in the uses clause. (To access the identifier from the other unit, you would have to add a qualifier: UnitName.Identifier.)
As said it is by design, the compiler loads symbols from units using a stack based approach, and parses through the stack from last loaded to first loaded to search for a symbol. Preprocessor state is directly merged into the global state though.
Cross unit overloading is an exception though. If you mark both functions with overload; directive, you get an error (bla was the name of the function in the test)
[dcc32 Error] test.dpr: E2251 Ambiguous overloaded call to 'bla'
Unit1.pas(8): Related method: procedure bla;
Unit2.pas(8): Related method: procedure bla;
if you have two different signatures, it will select the best matching one.
cross overloading is a newer feature, but I don't remember exactly when. My guess is D2006.

Resources