How do you call a procedure given the procedure pointer in Pascal? [closed] - pascal

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 days ago.
This post was edited and submitted for review 8 days ago and failed to reopen the post:
Original close reason(s) were not resolved
Improve this question
Given an array of pointers where those pointers are to procedures and where they pass a single pointer passed to a procedure, how do you call the passed pointer?
procedure CallPointerProc(Proc : Pointer);
begin
// ???????????
end;
The old DOS method was fairly simple but inline is not available on Windows port so couldn't even convert to ebx,esp, etc..:
inline(
$89/$E3/ {mov bx,sp}
$36/$FF/$1F/ {call dword ptr ss:[bx]}
$83/$C4/$04); {add sp,4}

In general, you should not pass a blank pointer but a type that describes the exact type of subroutine like parameters with their types and return type in case of a function.
Example on how to declares procedure or function types
type
// A procedure with no parameters
TProc = procedure;
// A procedure that expexcts a string
TStringProc = procedure(str: string);
// A function that expects and returns a string
TStringToStringFunc = function(str: string): string;
A function that expects a pointer to a procedure and calls it:
procedure CallPointerProc(Proc : TProc);
begin
Proc();
end;
A function that expects a blank pointer, casts it to a procedure and then calls that:
procedure CallPointerProc(Proc : Pointer);
var
TypedProc: TProc;
begin
TypedProc := TProc(Proc);
TypedProc();
end;
Demo code that works with both definitions of CallPointerProc above. Note that we use the # symbol to get the address of a defined procedure or function.
procedure Demo;
begin
Writeln('Hello World');
end;
begin
CallPointerProc(#Demo);
Readln;
end.

Related

Is there a way to determine if a reference to an object procedure corresponds to the object instance in 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;

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.

Can not create TStream in Lazarus [duplicate]

This question already has answers here:
Delphi: Access Violation at the end of Create() constructor
(2 answers)
Closed 5 years ago.
It fails in this simple example to:
procedure TForm1.Button1Click(Sender: TObject);
var
ts: TStream;
begin
ts.Create; //<---- fails here
ts.Free;
end;
With error:
Project project1 raised exception class 'External: SIGSEGV'.
At address 10000DB38
You are using the wrong code. It should be
procedure TForm1.Button1Click(Sender: TObject);
var
ts: TStream;
begin
ts := TStream.Create; // If Lazarus supports creation of Stream instances.
ts.Free;
end;
Until it is created, your variable ts simply contains junk from previous use of the stack. You have to call the class's constructor to allocate the actual object on the heap and point your ts variable at it.
If Lazarus complains that it can't create an instance of TStream (it may treat it as an abstract class and I don't have Lazarus on this machine to check), try something like this instead:
var
ts: TMemoryStream;
begin
ts := TMemoryStream.Create;
ts.Free;
end;
Instead of TMemoryStream, you could use any other concrete TStream-descendant class.
Was originaly trying this code:
memStream.Create;
But it should be:
memStream := TMemoryStream.Create;
Blah...

It's some kind of magic... or bug? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I added my program to the SendTo. I send two files to it.
They are:
C:\ThisIsMySuperTestHelloWorld\ThisBookIsRedMyPenIsWhite\test.jpg
C:\ThisIsMySuperTestHelloWorld\ThisBookIsRedMyPenIsWhite\hello.jpg
The code below shows C:\ThisIsMySuperTestHelloWorld\ThisBookIsRedMyPenIsWhite\test.jpg#C:\ThisIsMySuperTestHelloWorld\ThisBookIsRedMyPenIsWhite\test.jpg
procedure TForm1.FormCreate(Sender: TObject);
var Files: array of PAnsiChar;
i: Integer;
begin
SetLength(Files, 2);
for i:=0 to 1 do begin
Files[i] := PAnsiChar(ParamStr(2+i));
end;
ShowMessage( Files[0] +'#' + Files[1] );
end;
I use Delphi 6 on Windows7.
Under Delphi Xe3 (still Win7) I changed (both) PAnsiChar to PWideChar and I have the same effect.
My SendTo link links to:
"C:\<PATH_HERE>\Project1.exe" c
and is placed here:
C:\Users\<USER>\AppData\Roaming\Microsoft\Windows\SendTo
What about using strings? For example:
procedure HandleParams;
var Files: array of string;
i: Integer;
begin
SetLength(Files, ParamCount);
for i := 1 to ParamCount do
Files[i-1] := ParamStr(i);
if ParamCount >= 2 then
ShowMessage( Files[0] +'#' + Files[1] );
end;
Your code does not work, because PAnsiChar is only a Pointer and does not store the actual string data. When you assign the string returned from the ParamStr function only a pointer to the (temporary) function result is stored. The actual data is overwritten with the next function call. This can even crash your program when further used.
By the way, your ParamStr index iterates over 2 and 3, with references to the second and third parameter; maybe that's not intended as the arguments start at index 1 (index 0 being the program call itself)?
To solve the issue one has to store the string data, which makes the pointers kinda useless, but anyway, here's a fixed version of your example:
procedure HandleParamsPAnsi;
var Files: array of PAnsiChar;
FilesData: array of AnsiString;
i: Integer;
begin
SetLength(Files, 2);
SetLength(FilesData, 2);
for i:=0 to 1 do begin
FilesData[i] := AnsiString(ParamStr(1+i));
Files[i] := PAnsiChar(FilesData[i]);
end;
ShowMessage( Files[0] + '#' + Files[1] );
end;

Resources