Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2003-11

Nested function as argument for TList.Sort

11 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Pedro Lopez-Cabanillas

Hi,

The attached program compiles and works fine under Kylix 3, and also compiles under fpc 1.9, but bombs with this message:

An unhandled exception occurred at 0x0807F0B5 :
EAccessViolation : Access violation
0x0807F0B5

It happens on TObjectList.Sort when the argument is a local nested function. It works well when the argument is a global function.

Regards,
Pedro

ALSA Library Bindings for Pascal http://alsapas.alturl.com

Reply by email

Florian Klaempfl
to Pedro Lopez-Cabanillas
Pedro Lopez-Cabanillas wrote 86 lines
Hi, The attached program compiles and works fine under Kylix 3, and also compiles under fpc 1.9, but bombs with this message: An unhandled exception occurred at 0x0807F0B5 : EAccessViolation : Access violation 0x0807F0B5 It happens on TObjectList.Sort when the argument is a local nested function. It works well when the argument is a global function. Regards, Pedro ------------------------------------------------------------------------ program test6; {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} uses Contnrs; type TestClass = class private key: Integer; public constructor Create(keyValue: Integer); function getKey: Integer; end; constructor TestClass.Create(keyValue: Integer); begin key := keyValue; end; function TestClass.getKey: Integer; begin Result := key; end; var list: TObjectList; procedure Populate; begin list := TObjectList.Create; list.add(TestClass.Create(10)); list.add(TestClass.Create(5)); list.add(TestClass.Create(25)); list.add(TestClass.Create(15)); end; procedure SortAndPrint; function TestCompare(ptr1, ptr2: pointer): Integer; var int1, int2: Integer; begin int1 := TestClass(ptr1).getKey; int2 := TestClass(ptr2).getKey; if int1 = int2 then Result := 0 else if int1 < int2 then Result := -1 else Result := 1; end; var i: Integer; begin list.Sort(@TestCompare); for i := 0 to list.Count-1 do begin WriteLn('Item ', i, ', key = ', TestClass(list.Items[i]).getKey); end; end; begin Populate; SortAndPrint; end.

This is bad coding imho and works in Delphi only "by accident" because Delphi uses register calling conventions. Or are I'am wrong and sort expects a local procedure?

Reply by email

Marc Weustink
to Florian Klaempfl
Florian Klaempfl wrote 76 lines
Pedro Lopez-Cabanillas wrote: >Hi, >The attached program compiles and works fine under Kylix 3, and also >compiles under fpc 1.9, but bombs with this message: >An unhandled exception occurred at 0x0807F0B5 : >EAccessViolation : Access violation > 0x0807F0B5 >It happens on TObjectList.Sort when the argument is a local nested >function. It works well when the argument is a global function. >Regards, >Pedro > >------------------------------------------------------------------------ >program test6; >{$IFDEF FPC} > {$MODE DELPHI} >{$ENDIF} >uses > Contnrs; >type > TestClass = class > private > key: Integer; > public > constructor Create(keyValue: Integer); > function getKey: Integer; > end; > constructor TestClass.Create(keyValue: Integer); > begin > key := keyValue; > end; > function TestClass.getKey: Integer; > begin > Result := key; > end; >var > list: TObjectList; > procedure Populate; > begin > list := TObjectList.Create; > list.add(TestClass.Create(10)); > list.add(TestClass.Create(5)); > list.add(TestClass.Create(25)); > list.add(TestClass.Create(15)); > end; > procedure SortAndPrint; > function TestCompare(ptr1, ptr2: pointer): Integer; > var > int1, int2: Integer; > begin > int1 := TestClass(ptr1).getKey; > int2 := TestClass(ptr2).getKey; > if int1 = int2 > then Result := 0 > else if int1 < int2 > then Result := -1 > else Result := 1; > end; > var > i: Integer; > begin > list.Sort(@TestCompare); > for i := 0 to list.Count-1 do > begin > WriteLn('Item ', i, ', key = ', TestClass(list.Items[i]).getKey); > end; > end; >begin > Populate; > SortAndPrint; >end. This is bad coding imho and works in Delphi only "by accident" because Delphi uses register calling conventions. Or are I'am wrong and sort expects a local procedure?

AFAIK it is illegal in D5 to use a local procedure for TList.Sort. Don't know if it is still the case in later versions (I haven't tried it and don't expect it) In D1 (or earlier ?) it was legal to do this

Marc

Reply by email

Michael Van Canneyt
to Marc Weustink
Marc Weustink wrote 81 lines
At 08:54 27-11-2003, Florian Klaempfl wrote: >Pedro Lopez-Cabanillas wrote: > >>Hi, >>The attached program compiles and works fine under Kylix 3, and also >>compiles under fpc 1.9, but bombs with this message: >>An unhandled exception occurred at 0x0807F0B5 : >>EAccessViolation : Access violation >> 0x0807F0B5 >>It happens on TObjectList.Sort when the argument is a local nested >>function. It works well when the argument is a global function. >>Regards, >>Pedro >> >>------------------------------------------------------------------------ >>program test6; >>{$IFDEF FPC} >> {$MODE DELPHI} >>{$ENDIF} >>uses >> Contnrs; >>type >> TestClass = class >> private >> key: Integer; >> public >> constructor Create(keyValue: Integer); >> function getKey: Integer; >> end; >> constructor TestClass.Create(keyValue: Integer); >> begin >> key := keyValue; >> end; >> function TestClass.getKey: Integer; >> begin >> Result := key; >> end; >>var >> list: TObjectList; >> procedure Populate; >> begin >> list := TObjectList.Create; >> list.add(TestClass.Create(10)); >> list.add(TestClass.Create(5)); >> list.add(TestClass.Create(25)); >> list.add(TestClass.Create(15)); >> end; >> procedure SortAndPrint; >> function TestCompare(ptr1, ptr2: pointer): Integer; >> var >> int1, int2: Integer; >> begin >> int1 := TestClass(ptr1).getKey; >> int2 := TestClass(ptr2).getKey; >> if int1 = int2 >> then Result := 0 >> else if int1 < int2 >> then Result := -1 >> else Result := 1; >> end; >> var >> i: Integer; >> begin >> list.Sort(@TestCompare); >> for i := 0 to list.Count-1 do >> begin >> WriteLn('Item ', i, ', key = ', TestClass(list.Items[i]).getKey); >> end; >> end; >>begin >> Populate; >> SortAndPrint; >>end. > >This is bad coding imho and works in Delphi only "by accident" because >Delphi uses register calling conventions. Or are I'am wrong and sort >expects a local procedure? AFAIK it is illegal in D5 to use a local procedure for TList.Sort. Don't know if it is still the case in later versions (I haven't tried it and don't expect it) In D1 (or earlier ?) it was legal to do this

It is illegal.

Michael.

Reply by email

Peter Vreman
to Florian Klaempfl: This is bad coding imho and works in Delphi only "by accident" because Delphi uses…
Show quoted text 90 lines
> The attached program compiles and works fine under Kylix 3, and also > compiles > under fpc 1.9, but bombs with this message: > > An unhandled exception occurred at 0x0807F0B5 : > EAccessViolation : Access violation > 0x0807F0B5 > > It happens on TObjectList.Sort when the argument is a local nested > function. > It works well when the argument is a global function. > > Regards, > Pedro > > > > ------------------------------------------------------------------------ > > program test6; > > {$IFDEF FPC} > {$MODE DELPHI} > {$ENDIF} > > uses > Contnrs; > > type > TestClass = class > private > key: Integer; > public > constructor Create(keyValue: Integer); > function getKey: Integer; > end; > > constructor TestClass.Create(keyValue: Integer); > begin > key := keyValue; > end; > > function TestClass.getKey: Integer; > begin > Result := key; > end; > > var > list: TObjectList; > > procedure Populate; > begin > list := TObjectList.Create; > list.add(TestClass.Create(10)); > list.add(TestClass.Create(5)); > list.add(TestClass.Create(25)); > list.add(TestClass.Create(15)); > end; > > procedure SortAndPrint; > function TestCompare(ptr1, ptr2: pointer): Integer; > var > int1, int2: Integer; > begin > int1 := TestClass(ptr1).getKey; > int2 := TestClass(ptr2).getKey; > if int1 = int2 > then Result := 0 > else if int1 < int2 > then Result := -1 > else Result := 1; > end; > var > i: Integer; > begin > list.Sort(@TestCompare); > for i := to list.Count-1 do > begin > WriteLn('Item ', i, ', key = ', TestClass(list.Items[i]).getKey); > end; > end; > > begin > Populate; > SortAndPrint; > end. This is bad coding imho and works in Delphi only "by accident" because Delphi uses register calling conventions. Or are I'am wrong and sort expects a local procedure?

The compiler should give an error. It is bad coding and only a "undocumented feature" of delphi register calling that makes it working. Because there is no full description of the register calling in delphi, like how does it insert the self,parameter return or parent framepointer parameters.

Reply by email

Pedro Lopez-Cabanillas
to Peter Vreman

Peter Vreman wrote:
[...]

Show quoted text 9 lines
> This is bad coding imho and works in Delphi only "by accident" because > Delphi uses register calling conventions. Or are I'am wrong and sort > expects a local procedure? The compiler should give an error. It is bad coding and only a "undocumented feature" of delphi register calling that makes it working. Because there is no full description of the register calling in delphi, like how does it insert the self,parameter return or parent framepointer parameters.

I don't fully agree with both comments. Why is it bad code? Is this code violating some rule about scope, or any other rule or Pascal language convention? If not, the compiler is right here, eating this code without a single warning.

OTOH, if the construct is not allowed and the program crashes under both FPC and old releases of Delphi compilers (this is not true anymore, because at least under Kylix3 this is perfectly legal and runs fine), If this construct is not supported by FPC, it should be stated at some point in the documentation. Perhaps a line about it on the FCL documentation, inside the TList.Sort description, and the Reference guide, section 3.5 - Procedural types.

BTW, the same issue about TList.Sort was discussed some time ago in Borland's forums, see:
http://groups.google.com/groups?&selm=6c4ter%24j1c4%40forums.borland.com

It is a comment from a Borland's developer, justifying why this code does not work under Delphi 3, and giving some details about internals. I guess that it is not an accident if it works under newer Delphi releases. The main point is that he apologizes about that ;-)

I hope that you don't get angry with me because my comments, I really like FPC and want it being my main developing tool. Thank you very much to all the FPC team.

Regards,
Pedro

ALSA Library Bindings for Pascal http://alsapas.alturl.com

Reply by email

Peter Vreman
to Pedro Lopez-Cabanillas
Show quoted text 16 lines
Peter Vreman wrote: [...] > > This is bad coding imho and works in Delphi only "by accident" because > > Delphi uses register calling conventions. Or are I'am wrong and sort > > expects a local procedure? > > The compiler should give an error. It is bad coding and only a > "undocumented feature" of delphi register calling that makes it working. > Because there is no full description of the register calling in delphi, > like how does it insert the self,parameter return or parent framepointer > parameters. I don't fully agree with both comments. Why is it bad code? Is this code violating some rule about scope, or any other rule or Pascal language convention? If not, the compiler is right here, eating this code without a single warning.

The the nested procedure is only valid when called from the parent procedure. TList.Sort() is a different procedure that does not know if a giving procedure is a nested procedure or not. The correct fix for the compiler is to give an error. Relying on the behaviour of calling conventions is very bad. Also the code won't work on sparc/ppc which have a different calling convention.

Reply by email

Pedro Lopez-Cabanillas
to Peter Vreman
Peter Vreman wrote 24 lines
> Peter Vreman wrote: > [...] > >> > This is bad coding imho and works in Delphi only "by accident" because >> > Delphi uses register calling conventions. Or are I'am wrong and sort >> > expects a local procedure? >> >> The compiler should give an error. It is bad coding and only a >> "undocumented feature" of delphi register calling that makes it working. >> Because there is no full description of the register calling in delphi, >> like how does it insert the self,parameter return or parent framepointer >> parameters. > > I don't fully agree with both comments. Why is it bad code? Is this code > violating some rule about scope, or any other rule or Pascal language > convention? If not, the compiler is right here, eating this code without > a single warning. The the nested procedure is only valid when called from the parent procedure. TList.Sort() is a different procedure that does not know if a giving procedure is a nested procedure or not. The correct fix for the compiler is to give an error. Relying on the behaviour of calling conventions is very bad. Also the code won't work on sparc/ppc which have a different calling convention.

Of course, if the compiler doesn't support this feature, because the internal handling of function calls, a compiler error message is better than a program crash.

But I was talking about Pascal language conventions. Instead of my code snippet, you can refer to the document "Extended Pascal", ISO-10206, chapter 6.11.6, examples 6-9. It's available here (among other places): http://www.pascal-central.com/standards.html

And GPC supports this feature, see:
http://www.gnu-pascal.de/demos/iteratordemo.pas

Regards,
Pedro

ALSA Library Bindings for Pascal http://alsapas.alturl.com

Reply by email

Jonas Maebe
to Pedro Lopez-Cabanillas
Pedro Lopez-Cabanillas wrote 5 lines
Of course, if the compiler doesn't support this feature, because the internal handling of function calls, a compiler error message is better than a program crash.

The problem is that some iterator-implementations do (and only) support local functions/procedures. The collections in TP's (and as such also FPC's) objects.pas are an example. They include special code for that support, however. So always giving an error is going to give a lot of false errors for people coming from TP/BP.

Show quoted text 8 lines
But I was talking about Pascal language conventions. Instead of my code snippet, you can refer to the document "Extended Pascal", ISO-10206, chapter 6.11.6, examples 6-9. It's available here (among other places): http://www.pascal-central.com/standards.html And GPC supports this feature, see: http://www.gnu-pascal.de/demos/iteratordemo.pas

That's because GPC supports standard/extended Pascal, we don't. To implement this, I think a procedure variable must always be a pointer+it's frame pointer (nil if it's not a nested procedure), a bit like procedure of object.

Jonas

Reply by email

Olle Raab
to Jonas Maebe

03-11-30 10.36, skrev Jonas Maebe följande:

Show quoted text 28 lines
On 30-nov-03, at 02:29, Pedro Lopez-Cabanillas wrote: > Of course, if the compiler doesn't support this feature, because the > internal > handling of function calls, a compiler error message is better than a > program > crash. The problem is that some iterator-implementations do (and only) support local functions/procedures. The collections in TP's (and as such also FPC's) objects.pas are an example. They include special code for that support, however. So always giving an error is going to give a lot of false errors for people coming from TP/BP. > But I was talking about Pascal language conventions. Instead of my code > snippet, you can refer to the document "Extended Pascal", ISO-10206, > chapter > 6.11.6, examples 6-9. It's available here (among other places): > http://www.pascal-central.com/standards.html > > And GPC supports this feature, see: > http://www.gnu-pascal.de/demos/iteratordemo.pas That's because GPC supports standard/extended Pascal, we don't. To implement this, I think a procedure variable must always be a pointer+it's frame pointer (nil if it's not a nested procedure), a bit like procedure of object.

This is correct, in some Apple docs this pointer was called the "static link".

It's also supported in mac pascal dialects. It would be nice if we could support it, it is one of the strength with pascal to be able to do such things, e g supporting a local compare funciton to a generic sort procedure, IMHO. Its a nice feature to beat C fans with.

Olle

Reply by email

Florian Klaempfl
to Olle Raab
Olle Raab wrote 42 lines
03-11-30 10.36, skrev Jonas Maebe följande: >On 30-nov-03, at 02:29, Pedro Lopez-Cabanillas wrote: > > >>Of course, if the compiler doesn't support this feature, because the >>internal >>handling of function calls, a compiler error message is better than a >>program >>crash. > >The problem is that some iterator-implementations do (and only) support >local functions/procedures. The collections in TP's (and as such also >FPC's) objects.pas are an example. They include special code for that >support, however. So always giving an error is going to give a lot of >false errors for people coming from TP/BP. > > >>But I was talking about Pascal language conventions. Instead of my code >>snippet, you can refer to the document "Extended Pascal", ISO-10206, >>chapter >>6.11.6, examples 6-9. It's available here (among other places): >>http://www.pascal-central.com/standards.html >> >>And GPC supports this feature, see: >>http://www.gnu-pascal.de/demos/iteratordemo.pas > >That's because GPC supports standard/extended Pascal, we don't. To >implement this, I think a procedure variable must always be a >pointer+it's frame pointer (nil if it's not a nested procedure), a bit >like procedure of object. This is correct, in some Apple docs this pointer was called the "static link". It's also supported in mac pascal dialects. It would be nice if we could support it, it is one of the strength with pascal to be able to do such things, e g supporting a local compare funciton to a generic sort procedure, IMHO. Its a nice feature to beat C fans with.

OMG, I thought the proc. var stuff is fixed finished now :)

Reply by email