Is there a way to determine if a reference to an object procedure corresponds to the object instance in Pascal? - pascal

I have a not very good solution for this task, but can someone tell me how to use the standard library or a more elegant approach. The current solution is based on the fact that the reference to the procedure of the object stores two pointers in memory, a pointer to the procedure code and a pointer to the object.
function IsObject( const AEvent: TNotifyEvent; const AObject: TObject ): Boolean;
begin
Result := PointerArray( Pointer( #AEvent )^ )[1] = Pointer( AObject );
end;

I'd use the TMethod record declared in the System unit. The record contains two member fields of type Pointer - Code and Data, that allow you to pick out the two pointers of the method. In your case, you need the Data member, which is the method's subject:
function ObjectIsMethodSubject(AObject: TObject; AEvent: TNotifyEvent): Boolean;
begin
Result := TMethod(AEvent).Data = AObject;
end;

Related

Cannot find what is wrong with the array declaration

It says
fatal: syntax error, OF expected but [ found" for the variable "lo"
But I really can't see whats wrong with it.
I tried to change variable name but seems not working.
procedure reg( index, gen : char;
fname, sname, loginname, passwords, pid : string;
var lo : array [1..26,1..1025] of bucket ;
var main : array[1..1025] of detail);
var
convertedindex, i, j : integer;
found, found2 : boolean;
It's supposed to be without error but it says syntax error.
You can't define an array while you are in the middle of declaring the parameters of a procedure (or function). You need to define the array type beforehand by doing something like this instead:
program arraydecl;
type
Bucket = integer;
Detail = integer;
type
BucketArray = array [1..26,1..1025] of Bucket;
DetailArray = array[1..1025] of Detail;
procedure reg(index, gen : char; fname, sname, loginname, passwords, pid : string ; var lo : BucketArray; var main : DetailArray);
begin
end;
The error message says that the compiler expected the keyword of, but instead it found an opening bracket [.
The reason is (I guess) that in procedure declarations, you cannot define the bounds of an array. For example, you cannot say main: array[1..2] of integer, you can only say main: array of integer.
You can try to define an array type and then use that type as the procedure parameter:
type TwoInts = array[1..2] of integer;
procedure PrintTwoInts(ti: TwoInts)
begin
WriteLn(ti[1], ti[2])
end;
I haven't programmed in Pascal for a long time, so the above may or may not work. I don't remember whether ti would be passed by value or by reference, and whether the array indices inside the procedure would always start at 0. That's some things you would need to find out.
Parameter lists
In some Pascal versions, like FreePascal or Delphi, parameter lists of functions or procedures cannot contain type declarations, only type specifications.
So, to specify such an array as parameter, you must declare its type first, before the function/procedure declaration:
type
// Type declarations
Bucket = ...
Detail = ...
TBuckets = array[1..26, 1..1025] of Bucket;
TDetails = array[1..1025] of Detail;
procedure Reg(Index, Gen: Char; FName, SName, LoginName, Passwords, PID: string;
var Lo: TBuckets; var Main: TDetails);
Note that other Pascals (including ISO Pascal, if I remember correctly) do allow these ad hoc (on the spot) declarations, even in parameter lists. But obviously your dialect of Pascal doesn't.
Open array parameters
Now if you see a parameter specifications like x: array of Integer or similar, then you are dealing with open array parameters. This is not a declaration and it doesn't specify one single type, it accepts all kinds of one-dimensional arrays of that base type. More on that in my article Open array parameters and array of const.
This explains the error message: only of can follow array in a parameter list, to specify an open array parameter.
For what it's worth: if you are using FreePascal or Delphi, then you should get in the habit of passing strings as const, if possible: const FName, SName, etc...: string.

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.

Assigning procedures to variables and calling them in Pascal

I have a small terminal program which displays a menu. I want to have a function which takes the user's input and an array of procedure names and be able to call the procedure the user chose. I know that I could do that using if or case statements in the main program, but I want to make a unit with the above procedure and a couple of other menu functions, and if this was possible, I could make it call arbitrary procedures. Here's more or less how I would like it to work (I know it's wrong, but so you get a general idea).
program menu;
uses crt;
type procs = array [0..1] of procedure;
procedure call_procs(inp: int; procsy: procs);
begin
writeln(procsy[ord(inp)]); {And call it here.}
end;
var procsx : procs;
begin
procsx[0] := readkey; {I would like to somehow store the procedure here.}
procsx[1] := clrscr;
call_procs(0, procsx);
end.
Is there any way to do something like this? Thank you in advance.
There are a few things wrong with your original code which are not cited in your answer.
You have an array of procedure but you are calling writeln with these procedure calls as arguments as if they were function, which they are not.
readkey is a function, not a procedure, so its type doesn't match the element type of your array
Your assignment of the procedures to the array need to use # to reference the procedure pointer and not actually call the procedure
Not sure what compiler or options you're using, but int isn't the standard Pascal integer type, rather integer is.
As a niggle, since you're already using the integer index of the array, you don't need to use ord.
So the minimal changes to your code to make it basically work would be:
program menu;
uses crt;
type procs = array [0..1] of procedure;
procedure call_procs(inp: integer; procsy: procs);
begin
procsy[inp]; { call the procedure here - removed 'ord' since it's superfluous }
end;
var procsx : procs;
begin
{ procsx[0] := readkey; {- 'readkey' is a function and won't work here }
procsx[1] := #clrscr;
call_procs(1, procsx);
end.
You can create an array of functions that return char which matches the type for readkey:
program menu;
uses crt;
type procs = array [0..1] of function: char;
procedure call_procs(inp: integer; procsy: procs);
begin
writeln(procsy[inp]); { call the function; write out the returned char }
end;
function foo: char;
begin
foo := 'X';
end;
var procsx : procs;
begin
procsx[0] := #readkey;
procsx[1] := #foo;
call_procs(0, procsx);
call_procs(1, procsx);
end.
I figured out how to do this. One can use pointers to a procedure, then create an array of those pointers, and pass them to the procedure I wanted to use. Also, for some reason, it doesn't seem to work with the functions that come with Pascal (such as readkey or clrscr). For this example, one could do this:
program menu;
type
Tprocptr = procedure; {This creates a pointer to procedures.}
Tprocarray = array of Tprocptr;
procedure writeHi;
begin
writeln('Hi!');
end;
procedure writeHello;
begin
writeln('Hello!');
end;
procedure call_proc(inp: integer; procsy: Tprocarray);
{This now calls functions like one would expect.}
begin
procsy[ord(inp)];
end;
var
proclist : Tprocarray;
begin
setlength(proclist, 2);
proclist[0] := #writeHi; {The '#' creates a pointer to those procedures.}
proclist[1] := #writeHello;
call_proc(0, proclist);
end.
This works as expected, calling (in this case) the procedure writeHi, so if you run this program it will output Hi! to the terminal. If you change call_proc(0,proclist) to call_proc(1, proclist), it will call writeHello instead.

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.

Procedures on Free Pascal

Im relatively new to Pascal and, though i have a fair understanding of the language, there's still some stuff i cant figure out how to implement. I've ran into this problem and, after trying for like hours on my own and looking for similar cases on the internet, i have not found anything. I hope this question is a fair one because, honestly, i dont know how to figure this out.
Here's the thing.
I have an application which dynamically creates TextBoxes (TextEdits in this case) and adds them to a panel for displaying. Thing is, i need to execute some procedures on the newly created elements. I added a new procedure in my app (this is for explaining purposes only):
procedure Demo_Procedure(i: integer, a: String);
Then i proceeded to "develop" my procedure underneath the "implementation" part of the Form.
procedure Demo_Procedure(i: integer, a: String);
begin
ShowMessage(a, ' ' ,i);
end;
Now, for my dynamically created elements im trying to set the "OnKeyDow" event to run my new procedure (this is what i dont A- know if its possible to do or B- how to do it)
NewlyButton.OnClick:= Demo_Procedure(5, 'Hi');
Im getting different errors depending on how i call up my procedure. For example:
If i do it like this: Demo_Procedures(5, 'Hi'), it says:
Error: Incompatible types: got "untyped" expected "procedure variable type of procedure(TObject,var Word,TShiftState) of object;Register>"
Now, researching around i found out that some people that put an '#' before calling the method, the only difference is that this time instead of saying "untyped" it says that it got "procedure variable type of procedure(AnsiString,LongInt) of object" and that it was expecting the same as before (procedure(TObject,var> Word,Tshift...etc)
Can anyone help me out here? I really am lost so any help would be greatly appreciated. Thanks in advance :)
There are errors in your code:
procedure Demo_Procedure(i: integer, a: String); // Wrong
procedure Demo_Procedure(i: integer; a: String); // Right, use semicolon as parameters delimiter
ShowMessage(a, ' ' ,i); // Wrong, ShowMessage takes only one string parameter
ShowMessage(Format('%s %d', [a, i])); // Right, %s means string value and %d means decimal value, see help about Format function
Events is a procedural variables so they have its own types. For example, OnKeyDown event have a type
TKeyEvent = procedure(Sender: TObject; var Key: Word; Shift: TShiftState) of Object;
where of Object means that your event handler must be a class method.
So, you can not assign to the event any procedure but only class method with parameters provided in the type declaration.
Here is the simple code:
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
e: TEdit;
begin
e := TEdit.Create(Self); // Create new TEdit control
e.Parent := Self; // Place control onto the form
e.Left := 10; // Set control coordinates
e.Top := 10;
e.OnKeyDown := #EditKeyDown; // Assign event handler
end;
procedure TForm1.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
ShowMessage(Format('Key code is %d', [Key]));
end;
end.

Resources