An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171023/4eb087cb/attachment.html>
Mailing lists / lazarus / 2017-10
Weird object variables access - fpc issue?
45 messages · 9 people · –
- GGiuliano Colla 14
- MMattias Gaertner 8
- SSven Barth 7
- Eel es 5
- MMichael Van Canneyt 5
- MMichael Schnell 2
- +3 more
Reply by email Subscribe to this list Your subscription
Read-only archive. Use j and k to move between messages.
Am 23.10.2017 21:00 schrieb "Giuliano Colla via Lazarus" <
:
I'm doing something wrong, I'm missing something, or this is just a compiler bug?
How do you pass the SendMessage() method to your thread? And how does the code look that calls it?
Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171023/8119af3a/attachment.html>
Il 23/10/2017 21:33, Sven Barth via Lazarus ha scritto:
How do you pass the SendMessage() method to your thread? And how does the code look that calls it?
The SendMessage() method belongs to the main thread only, and is supposed to execute only in the main thread context.
It fills up a buffer (TBuffer), and sets a SendLen value and an OkToSend boolean to true.
The buffer and SendLen are written by the main thread and read from the serial thread. OkToSend is set by the main thread and reset by the serial thread.
TBuffer, SendLen and OkToSend are public variables not belonging to any object.
The code looks like that:
......
TBuffer[J] := Etx;
inc(J);
SendLen := J;
OKToSend := True;
if not SendCreated then begin
SendCreate;
end;
if Send.suspended then
Send.Resume;
Application.ProcessMessages;
end;The serial thread checks for OkToSend, sets it to false, and takes care of sending the message and getting an answer. Once it's done it posts a message for the main thread and suspends itself.
SendMessage() isn't invoked any more until the serial thread has managed to send the message, and to receive an answer, or the communication has timed out.
Giuliano
Show quoted text 6 lines
Check if 'Self' inside SendMessage is the right instance.
Mattias
An HTML attachment was scrubbed... URL: <http://lists.lazarus-ide.org/pipermail/lazarus/at…
On 23/10/17 20:00, Giuliano Colla via Lazarus wrote: [snip]
I don't remember if I was hitting AV using Now(), likely I wasn't - but due to 'normal' Now() being not thread safe, I was getting inconsistent timing results, when I used Now() both in main thread and in the thread;
So with my application I ended up re-implementing Now() into ThreadNow :
var ThreadNowInUse :boolean = false; // unit global 'just' above the function
function TMyThread.ThreadNow: TDateTime;
var ASystemTime : TSYSTEMTIME; // struct.inc, I'm under Windows
begin
repeat
until not ThreadNowInUse; //
try
ThreadNowInUse :=true;
GetSystemTime(ASystemTime);
Result := SystemTimeToDateTime(ASystemTime);
finally
ThreadNowInUse := false;
end;
end;Hope this helps
el es
Show quoted text 7 lines
This is not thread safe.
Mattias
Mattias Gaertner wrote 15 lines
Because separated by 'try' ?
I see; Will it be enough to put it like
try
repeat
until not ThreadNowInUse;
ThreadNowInUse := true;...
?
I haven't ran into problems with my routine yet,
but I wasn't really calling it in a very 'tight loop'
(as it is used for timing/'profiling' the calls to hardware 'master node' routines and for deciding whether the thread should go to sleep; so there is always ample time between calls to ThreadNow() and it's only called in context of the thread; so the 'poor mans semaphore' isn't really that required - it's just a safeguard)
(probably if I need timing measurements, every other type of thread I will ever define, will have their own ThreadNow call)
I realize I could just use 'naked' GetSystemTime + SystemTimeToDateTime pair with a local variable;
or define the above pair as a nested function in the thread, that'll serialize it enough...Anyone ever thought of a thread-safe global Now() call ?
el es
Am 24.10.2017 10:49 schrieb "el es via Lazarus" <
:
Mattias Gaertner wrote 15 lines
Because separated by 'try' ?
I see; Will it be enough to put it like
try
repeat
until not ThreadNowInUse;
ThreadNowInUse := true;...
?
You should look at InterlockedCompareExchange(), everything else is an accident waiting to happen (except for a critical section, but that would be overkill on the other hand).
Regards,
Sven
I haven't ran into problems with my routine yet,
but I wasn't really calling it in a very 'tight loop'
(as it is used for timing/'profiling' the calls to hardware 'master node' routines
and for deciding whether the thread should go to sleep; so there is always ample time
between calls to ThreadNow() and it's only called in context of the thread; so the
'poor mans semaphore' isn't really that required - it's just a safeguard)
(probably if I need timing measurements, every other type of thread I will ever define, will have their own ThreadNow call)
I realize I could just use 'naked' GetSystemTime + SystemTimeToDateTime pair with a local variable;
or define the above pair as a nested function in the thread, that'll serialize it enough...
Anyone ever thought of a thread-safe global Now() call ?
el es
[snip] I don't remember if I was hitting AV using Now(), likely I wasn't - but due to…
Show quoted text 6 lines
AFAIK Now is thread safe. On what platform and how did you test?
Mattias
Mattias Gaertner wrote 13 lines
The first call to Now is not necessarily thread-safe, because of the initialization of TZ info ?
Michael.
Because separated by 'try' ? I see; Will it be enough to put it like try repeat until not…
el es wrote 8 lines
Busy wait actions are usually a very bad idea
Better use a TTimer for such.
-Michael
Show quoted text 11 lines
Are you kidding?
Mattias
Mattias Gaertner wroteAre you kidding?
If this is not appropriate, I suppose the general design of that functionality should be reconsidered, doing anything that needs a fast reaction in the worker thread.
-Michael
The first call to Now is not necessarily thread-safe, because of the initialization of TZ…
[...] From memory: The first call to Now is not necessarily thread-safe, because of the initialization of TZ info ?
Please add this to the docs.
Mattias
Mattias Gaertner wrote 9 lines
After I've verified that my memory in this correct in this matter :)
I know that there is some late TZ initialization, but I'm not 100% sure whether Now() invokes it.
Michael.
Busy wait actions are usually a very bad idea Better use a TTimer for such. -Michael
Michael Schnell wrote 14 lines
I suppose you'd need to look at the parent/my post, where it's in full code what this function does ;)
tl/dr: it's a function that tries to get a time stamp in (maybe less) thread-safe / (I'd rather call it) reentrant way
for use in thread timing and profiling.
So no, TTimer not appropriate here.
:)
el es
On Tue, 24 Oct 2017 08:59:36 +0100 AFAIK Now is thread safe. On what platform and how did…
Mattias Gaertner wrote 14 lines
Now() is literally
function Now : TDateTime;
var ASystemTime : TSYSTEMTIME;
begin
GetSystemTime(ASystemTime);
Result := SystemTimeToDateTime(ASystemTime);
end;on my installation (lazarus 1.6.4 #54278, FPC 3.0.2)
I'd say it's questionable to say it's thread-safe that way; It may be safe to use in
main thread (where no 2 different contexts can mangle each others ASystemTime);But I had problems using Now() in main thread context (graphic) and in additional thread(s) _simultaneously_.
I do not recall getting AV's, but I was getting mangled results when calls to Now() overlapped from 2 different thread contexts.
(try it by calling Now() from a TTimer.OnTimer handler, simultaneously with a background thread also calling Now() in a loop.)
I guess, simpler code for ThreadNow would be 'just' thread-local/nested to Execute() function containing just a pair of
GetSystemTime(ASystemTime);
Result := SystemTimeToDateTime(ASystemTime);(or literally a copy of Now(); )
because each thread instance would have its own local variables, right?
so no thread would step on each others' toes;Hopefully this makes sense now ;)
el es
On Mon, 23 Oct 2017 22:50:49 +0200 Check if 'Self' inside SendMessage is the right…
Il 23/10/2017 22:57, Mattias Gaertner via Lazarus ha scritto:
Check if 'Self' inside SendMessage is the right instance.
Yes, you're right, that's what clarifies the issue. When it works, Self is a TFORM1 object, when it fails, Self is just Nil.
But now it's also clear why this happens.
The issue has nothing to do with thread code, it has to do with Delphi mode and Delphi syntax! It is either an fpc Delphi mode bug, or a Delphi bug, kept in fpc for "compatibility".
The full story is that this application has been originally written more than 20 years ago, using Delphi 3. Then it was updated to Delphi 5, and subsequently ported to Lazarus, but keeping the Delphi mode, to minimize the changes.
SendMessage can be called in two ways: either directly from another main thread method, or somehow indirectly (always from another main thread method), through a NextProc value fetched from a queue.
The application is used to upload a program to an embedded processor. The main thread reads by chunks from the disk the program to be uploaded and pushes into a queue what should be done next (send the first header, send an intermediate chunk, send the last chunk, send the program version and date, etc). At the good time, the appropriate method is fetched from the queue and called; in turn it calls SendMessage to do the actual job.
When SendMessage is called directly from another method, the Self instance is correct, when it is called indirectly by a fetching from the queue the method to call, Self is just Nil.
Now the catch is that the queue used is just a queue of pointers, while here we're dealing with procedures of object. This is legal with Delphi syntax, while it is forbidden in ObjFpc. When I tried to see if the problem was originated by Delphi mode, and changed to objfpc mode, it turned out that there was no way to make it compile. But just changing my queue from a queue of pointers to a queue of TProc (TProc = procedure of object) fixes the issue: it compiles, it works, Self is correct, there's no need to qualify the identifier, and all is well.
The moral of the story is: in Delphi mode you can assign a procedure of object to a pointer, and the compiler doesn't complain, but it is wrong, because when you call your method through this pointer, the method is executed, but it's unaware of the data space of the object it belongs to, even if it's called from another method of the same object! If the method doesn't access variables of the object, everything appears to work, but if it does the program simply crashes.
I will try to see if the same occurs also in Delphi, to understand if it's a Delphi bug or an fpc bug.
Giuliano
Now() is literally function Now : TDateTime; var ASystemTime : TSYSTEMTIME; begin…
Show quoted text 14 lines
ASystemTime is a stack variable. That's thread-safe.
In fpc trunk it calls GetLocalTime on Unix, which fetches timezone every time. No global var. So it should be thread safe.
because each thread instance would have its own local variables, right? so no thread would step on each others' toes;
You have to check called code too.
Mattias
Now() is literally function Now : TDateTime; var ASystemTime : TSYSTEMTIME; begin…
Am 24.10.2017 13:41 schrieb "el es via Lazarus" <
:
Mattias Gaertner wrote 8 lines
inconsistent
Show quoted text 6 lines
Now() is literally
function Now : TDateTime;
var ASystemTime : TSYSTEMTIME;
begin
GetSystemTime(ASystemTime);
Result := SystemTimeToDateTime(ASystemTime);
end;on my installation (lazarus 1.6.4 #54278, FPC 3.0.2)
I'd say it's questionable to say it's thread-safe that way; It may be safe to use in
main thread (where no 2 different contexts can mangle each others ASystemTime);
ASystemTime is a local variable, so two threads won't influence each other there thus at least on Windows Now() is threadsafe.
Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171024/faed2c93/attachment.html>
Il 23/10/2017 22:57, Mattias Gaertner via Lazarus ha scritto: Yes, you're right, that's…
Giuliano Colla wrote 13 lines
A valid method can never be stored in a pointer, since the former is actually 2 pointers (method/data) and the latter is just 1 pointer.
That the assignment is valid is Delphi compatible: only the procedure address is copied.
Michael.
Il 23/10/2017 22:57, Mattias Gaertner via Lazarus ha scritto: Yes, you're right, that's…
Show quoted text 5 lines
I'm glad you figured it out.
[...] Now the catch is that the queue used is just a queue of pointers, while here we're dealing with procedures of object. This is legal with Delphi syntax, while it is forbidden in ObjFpc.
It's only legal with a typecast.
When I tried to see if the problem was originated by Delphi mode, and changed to objfpc mode, it turned out that there was no way to make it compile.
The compiler tried to protect you from shooting yourself in the foot.
Show quoted text 7 lines
I just tried with $mode delphi:
p:=o.DoIt;
test1.pas(32,4) Error: Invalid assignment, procedures return no
valueMattias
On Tue, 24 Oct 2017 12:41:11 +0100 ASystemTime is a stack variable. That's thread-safe.…
Mattias Gaertner wrote 12 lines
Sven Barth wrote 7 lines
MMMmmm...
i can't exactly now but I will try to reproduce what I was seeing ...
for now sorry for the noise...
el es
A valid method can never be stored in a pointer, since the former is actually 2 pointers…
Il 24/10/2017 14:10, Michael Van Canneyt via Lazarus ha scritto:
Show quoted text 5 lines
I wonder if Delphi does such a stupid thing, or rather it stores in the pointer the address of a pointer pair (method/data). However here the issue is that a pointer can be assigned to a "procedure of object" without flagging an error:
type
TProc = Procedure of object;
......
NextProc: TProc;
....
@NextProc := Coda.Remove; // Remove returns a pointer
Sending := True;
NextProc;This compiles (in Delphi mode) without generating an error, and it is wrong, because NextProc is a Method, not a simple procedure.
Giuliano
On Tue, 24 Oct 2017 13:56:43 +0200 I'm glad you figured it out. It's only legal with a…
Il 24/10/2017 14:15, Mattias Gaertner via Lazarus ha scritto:
Show quoted text 9 lines
You can typecast a procedure of object to a pointer, but not the opposite. And that is good.
Show quoted text 6 lines
Yes, but not in Delphi mode.
Show quoted text 13 lines
But in Delphi mode
@NextProc := Coda.Remove;
Sending := True;
NextProc;with NextProc defined as a procedure of object does compile but it's wrong.
Mattias
Am 24.10.2017 14:57 schrieb "Giuliano Colla via Lazarus" <
:
Il 24/10/2017 14:15, Mattias Gaertner via Lazarus ha scritto:
Show quoted text 12 lines
You can typecast a procedure of object to a pointer, but not the opposite. And that is good.
Show quoted text 7 lines
But just changing
Show quoted text 14 lines
But in Delphi mode
@NextProc := Coda.Remove;
Sending := True;
NextProc;with NextProc defined as a procedure of object does compile but it's wrong.
Why are you using an @ when you assign the NextProc variable? O.o
Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171024/b6654505/attachment.html>
Il 24/10/2017 17:04, Sven Barth via Lazarus ha scritto:
Show quoted text 11 lines
Because it's required by Delphi syntax. An unqualified procedural type means a call to the procedure.
Without the @ it would rise an "invalid left hand assignment" error.
Giuliano
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171024/8f8de2eb/attachment.html>
A valid method can never be stored in a pointer, since the former is actually 2 pointers…
Il 24/10/2017 14:10, Michael Van Canneyt via Lazarus ha scritto:
Show quoted text 5 lines
I can confirm it as an fpc bug.
Delphi does apparently more than just copying the procedure address and dropping the data address. It doesn't raise an error, but it handles the assignment of a procedural type to a pointer in a sensible way.
The quickest way for me to test was using Kylix, so I created a minimal application to test the feature.
I created it in Kylix, then converted it to Lazarus.
Just two buttons and a memo.
Button1 displays the value of "Self" in the memo, and it is correct in both cases, Kylix and Lazarus.
Button2 makes an indirect call to a Method via a Pointer, always to display the "Self" value. It works in Kylix, it segfaults in Lazarus TForm1.Memo1Update, when executing Memo1.Lines.add(aString). But in Lazarus the debugger tells that the string it fails to display is "Self value is Nil".
Please find here enclosed the two sources of the test, the Delphi/Kylix one and the Lazarus one.
Giuliano
-------------- next part --------------
A non-text attachment was scrubbed...
Name: lazarus.zip
Type: application/zip
Size: 2506 bytes
Desc: not available
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171024/dd8bc869/attachment-0002.zip> -------------- next part --------------
A non-text attachment was scrubbed...
Name: kylix.zip
Type: application/zip
Size: 1488 bytes
Desc: not available
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171024/dd8bc869/attachment-0003.zip>
Il 24/10/2017 17:04, Sven Barth via Lazarus ha scritto: Because it's required by Delphi…
Giuliano Colla wrote 16 lines
You're completely defeating the type system here and at least in Delphi XE the value of Self is Nil if I do it like you did above. If it indeed worked in older versions then it probably worked completely by accident.
Regards,
Sven
Dear specialists,
Because making Lazarus accessible for visually impaired future developers is so easy, that it is rather not realistic, that somebody will make it free of charge, since project development is very heavy even without making it accessible for screen readers users, I have other constructive suggestion.
Who of us would initiate crout founding campaign by using Indigo or other servers, so Lazarus would become fully accessible. Please take in mind, that even latest Embarcade products are not fully accessible. The goal of The project would make Lazarus IDE fully accessible including form designer and also all libraries or units, which this IDE uses to make accessible also other packages, such Lamw, Lazarus android module wizard.
Any opinions will be very welcomed. It would be great, since it would allow visually impaired programmers to develop professionally looked apps without need to use sighted assistance.
May be, that The manager of The whole project would try to think about it. Since finance from such campaign would be divided between engaged developers. I Am afraid, that The best results can only be achieved, if advanced Pascal developers, who are intensively developing Lazarus for several years can achieve this goal.
I think it well. I know, that making such thinks, thinks, which have not been made even by big commercial companies can not be made by yours team free of charge thanks its complexity.
I believe, that somebody will atleast think about my suggestion.
Il 24/10/2017 14:10, Michael Van Canneyt via Lazarus ha scritto: I can confirm it as an…
Giuliano Colla wrote 8 lines
No, I'm sorry to say that IMO you did not. I looked at your code.
It simply works by accident in Delphi, because you are calling a method of the same object as the caller. Try calling a method of another object, and displaying the classname and make sure you trash the registers between the assignment of the method pointer and actually calling it.
Michael.
On 25 October 2017 at 03:48, Mgr. Janusz Chmiel via Lazarus <
wrote: Who of us would initiate crout founding campaign by using Indigo or other servers, so Lazarus would become fully accessible.
While not putting my hand up to be involved (too busy I'm afraid), I would happily contribute financially to this or similar endeavor.
Right - back to lurking...
Mike
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171025/2038c966/attachment.html>
No, I'm sorry to say that IMO you did not. I looked at your code. It simply works by…
Michael Van Canneyt wrote 15 lines
Yes, the code is wrong. But the reason is that myMethod is assigned in FormCreate. The later "@myMethod := pMethod;" assignment overwrites only half of the myMethod:TProc variable and so correct Self stays in the second half of myMethod.
Remove FormCreate and you get an AV.
Furthermore, "Format('0x%P',)" doesn't make sense - it should be "Format('0x%P',[Pointer(Myself)])".
No bug at all - just wrong code.
Ondrej
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171025/514a45a3/attachment.html>
Ondrej Pokorny wrote 21 lines
That is what I wanted to indicate :)
Michael.
Michael Van Canneyt wrote 7 lines
Sorry - I am often to dumb to understand indications - my wife can confirm that :)
Ondrej
No, I'm sorry to say that IMO you did not. I looked at your code. It simply works by…
Il 24/10/2017 23:18, Michael Van Canneyt via Lazarus ha scritto:
Show quoted text 5 lines
I'm forced to admit that you're (almost) right.
Changing the initial assignment of myMethod from a valid procedural type to Nil, makes the program crash in Kylix.
I found it hard to swallow that Delphi of the good old times allowed such an unsafe assignment without even a warning, and I had assumed that they were using some compiler magic to work around the issue, but apparently this is the sad truth.
However some issues remain, about fpc Delphi mode:
With older Delphi's up to, I believe, Delphi 7 you have that:
1) the syntax for the assignment of a procedural type is @ProcIdentifier
:= whatever;2) If whatever is a pointer, and ProcIdentifier is a Method, the data field is left untouched
This made it possible, with some dirty tricks to build a proper method procedural type.
In recent Delphi's (someone tested with X7, I did with X10)
1) The syntax @ProcIdentifier := whatever is no more valid. It's no more
possible to assign a pointer to a method type without typecasting.2) Assigning (with typecasting) a pointer to a method type will set the data field to Nil.
This makes it impossible to use dirty tricks.
Currently fpc in Delphi mode accepts the old syntax, but sets the data field to Nil, thus creating a misleading inconsistency.
Giuliano
Yes, the code is wrong. But the reason is that myMethod is assigned in FormCreate. The…
Il 25/10/2017 10:49, Ondrej Pokorny via Lazarus ha scritto:
Furthermore, "Format('0x%P',)" doesn't make sense - it should be "Format('0x%P',[Pointer(Myself)])".
With @Myself I get the address of the object, with Pointer(Myself) I get the address of the pointer to the object.
Giuliano
Il 24/10/2017 23:18, Michael Van Canneyt via Lazarus ha scritto: I'm forced to admit that…
Show quoted text 5 lines
No. The Delphi syntax is "ProcIdentifier:=Proc" or
"".
The "@Some:=" means assign something to address of Some. Which is bogus.
I guess Delphi's auto dereference actually translates this to
@Some^:=. Still bogus, but valid bogus.Show quoted text 10 lines
Good.
2) Assigning (with typecasting) a pointer to a method type will set the data field to Nil.
True.
This makes it impossible to use dirty tricks.
Huh? Delphi allows to typecast almost anything. At least it warns.
Currently fpc in Delphi mode accepts the old syntax, but sets the data field to Nil, thus creating a misleading inconsistency.
Mattias
Il 25/10/2017 13:41, Mattias Gaertner via Lazarus ha scritto:
Show quoted text 6 lines
From embarcadero doc wiki
(http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Procedural_Types_(Delphi)):
Show quoted text 10 lines
[snip]
Show quoted text 6 lines
This is no more true (as least as of X10 where I tested and it gave rise to a compiler error), but it has been for a long time. That's why I assumed that assigning an untyped pointer to a procedural variable was both legal and also properly supported.
Giuliano
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171025/78756cb9/attachment.html>
Il 24/10/2017 23:18, Michael Van Canneyt via Lazarus ha scritto: I'm forced to admit that…
Am 25.10.2017 13:06 schrieb "Giuliano Colla via Lazarus" <
:
With older Delphi's up to, I believe, Delphi 7 you have that:
1) the syntax for the assignment of a procedural type is @ProcIdentifier :=
whatever;2) If whatever is a pointer, and ProcIdentifier is a Method, the data field is left untouched
This made it possible, with some dirty tricks to build a proper method procedural type.
You're relying on implementation details which now often than not is an accident waiting to happen.
If you want to play around with the data pointer of a method pointer then cast it to TMethod which a record with a code and data pointer and is the documented way for this.
In recent Delphi's (someone tested with X7, I did with X10)
1) The syntax @ProcIdentifier := whatever is no more valid. It's no more
possible to assign a pointer to a method type without typecasting.Probably for good reason.
Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171025/8782d4a4/attachment.html>
Il 25/10/2017 13:41, Mattias Gaertner via Lazarus ha scritto: From embarcadero doc wiki…
Am 25.10.2017 14:12 schrieb "Giuliano Colla via Lazarus" <
:
Il 25/10/2017 13:41, Mattias Gaertner via Lazarus ha scritto:
No. The Delphi syntax is "ProcIdentifier:=Proc" or
"".
The "@Some:=" means assign something to address of Some. Which is bogus.
I guess Delphi's auto dereference actually translates this to
@Some^:=. Still bogus, but valid bogus.From embarcadero doc wiki (http://docwiki.embarcadero.
com/RADStudio/Tokyo/en/Procedural_Types_(Delphi)):
Procedural Types in Statements and Expressions
When a procedural variable is on the left side of an assignment statement, the compiler expects a procedural value on the right. The assignment makes the variable on the left a pointer to the function or procedure indicated on the right. In other contexts, however, using a procedural variable results in a call to the referenced procedure or function. You can even use a procedural variable to pass parameters:
[snip]
The operator can also be used to assign an untyped pointer value to a procedural variable. For example:
var StrComp: function(Str1, Str2: PChar): Integer;
...
@StrComp := GetProcAddress(KernelHandle, 'lstrcmpi');This is no more true (as least as of X10 where I tested and it gave rise to a compiler error), but it has been for a long time. That's why I assumed that assigning an untyped pointer to a procedural variable was both legal and also properly supported.
Did you test with a procedure pointer type or a method pointer type? Cause this might be the important difference here as the example uses a procedure pointer type.
Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171025/0229ce57/attachment-0001.html>
Am 25.10.2017 13:06 schrieb "Giuliano Colla via Lazarus" < With older Delphi's up to, I…
Il 25/10/2017 14:17, Sven Barth via Lazarus ha scritto:
Show quoted text 8 lines
You're right. It was misleading. I was misled.
Giuliano
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171025/8e418b5e/attachment.html>
Am 25.10.2017 14:12 schrieb "Giuliano Colla via Lazarus" < Il 25/10/2017 13:41, Mattias…
Il 25/10/2017 14:20, Sven Barth via Lazarus ha scritto:
Did you test with a procedure pointer type or a method pointer type? Cause this might be the important difference here as the example uses a procedure pointer type.
I tested assigning an untyped pointer to a method pointer type, because that's what I was interested to. It used to be accepted, it isn't anymore. A procedure pointer type shouldn't give rise to problems as far as I can gather, as it doesn't require a data field.
Giuliano
Il 25/10/2017 17:15, Sven Barth ha scritto:
That's not what I asked. The documentation used a procedure pointer type, thus the documentation would still be correct if it works as the allowed usage of a method pointer type in past versions would then indeed have been a bug in Delphi.
In Delphi 7 with just a procedure pointer it accepts both forms. If P is a generic pointer and PTS is defined as a Procedure Pointer type, you may write either
P := @myProcedure;
PTS := P;
PTS;or
P := @myProcedure;
@PTS := P;
PTS;In both cases no error is given and myProcedure is properly called.
As soon as I can put my hands on the right platform, I'll verify what happens with Rad Studio X10, and will let you know.
However I'm planning to pass this topic to the fpc list, where I think now it belongs.
Giuliano
Il 25/10/2017 13:41, Mattias Gaertner via Lazarus ha scritto: From embarcadero doc wiki…
Il 25/10/2017 14:12, Giuliano Colla via Lazarus ha scritto:
I raised some noise about procedural variables in Delphi mode, but it turns out that I had failed to notice some discrepancies while porting code back and forth from Delphi to Lazarus.
1.
I had failed to notice that RAD Studio X10 when importing the Delphi 7 project had silently dropped a declaration from my source, and this is what gave rise to a compiler error.
2.
I had failed to detect that the OnCreate event, which should have properly initialized the procedural variable in my test program was never triggered.
When I realized it I did refine and repeat all my tests in Kylix, Delphi 7, X10 and fpc, and the final result is the following:
1) Delphi accepts the assignment of an untyped pointer to a procedural variable even if it is a method pointer. This is true from Delphi 3 up to Kylix, Delphi 7 and Rad Studio X10.
2) The syntax for this particular assignment (again from Delphi 3 to X10) is:
@Procvar := aPointer;3) What Delphi does in that case is:
copy the pointer to the procedure in the "code" field of the procedural variable, leaving the "data" field untouched.
4) fpc in Delphi mode does exactly the same.
This makes it possible in Delphi, or in Delphi mode fpc, to initialize the procedural variable to point to a method of an object, and subsequently to make it point to any other compatible method of the same object, just by providing an untyped pointer, which may be handled with the many tools which deal only with pointers, for jump tables, queues of actions etc.
Of course this is not the best thing to do. An appropriate typecast of the procedural variable to a TMethod, and then explicitly setting the fields would make the code much more readable. That is what common sense and good programming practice would suggest. ObjFpc mode only provides this second option, for the same purpose, and this is certainly better.
But, as the purpose of Delphi mode is not to provide what common sense or good programming practice would suggest, but to mimic Delphi behaviour, I raised the question when I detected an apparent fpc bug.
It turns out that it was my fault, so, sorry for the noise.
Giuliano
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus-ide.org/pipermail/lazarus/attachments/20171103/737b9934/attachment.html>
