Sign in Join

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

Mailing lists / fpc-devel / 2013-08

How FPC handles dynamic arrays with const modifier?

7 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

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

Maciej Izak

Hi, I have some code (very important for next version of Generics.Collections):

------ code ------
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
{$APPTYPE CONSOLE}
uses
  Types;
{$IFDEF FPC}
function DynArraySize(p : pointer): tdynarrayindex; external name
'FPC_DYNARRAY_LENGTH';
{$ENDIF}
procedure Foo(A: Pointer);
begin
  WriteLn(DynArraySize(A));
end;
type
  TFoo = procedure(const A: TIntegerDynArray);
var
  Test: TFoo;
begin
  Test := @Foo;
  Test(TIntegerDynArray.Create(1, 2, 3, 4));
  ReadLn;
end.
------ code ------

In Delphi all is ok (DynArraySize in Foo returns 4). In FPC DynArraySize return 1 (and program crash at the end). Is dynamic array with const modifier in FPC passed by value? With small modifications especially for FPC (by adding constref) all works fine:

------ code ------
procedure Foo(constref A: Pointer);
begin
  WriteLn(DynArraySize(A));
end;
type
  TFoo = procedure(constref A: TIntegerDynArray);
------ code ------

I can live with constref, but I'm much worried by this behavior (in version without constref). Why my code don't work in both compilers same? I would like to understand...

Regards,
HNB

Reply by email

Jonas Maebe
to Maciej Izak
Maciej Izak wrote 35 lines
Hi, I have some code (very important for next version of Generics.Collections): ------ code ------ {$IFDEF FPC}{$MODE DELPHI}{$ENDIF} {$APPTYPE CONSOLE} uses Types; {$IFDEF FPC} function DynArraySize(p : pointer): tdynarrayindex; external name 'FPC_DYNARRAY_LENGTH'; {$ENDIF} procedure Foo(A: Pointer); begin WriteLn(DynArraySize(A)); end; type TFoo = procedure(const A: TIntegerDynArray); var Test: TFoo; begin Test := @Foo; Test(TIntegerDynArray.Create(1, 2, 3, 4)); ReadLn; end. ------ code ------ In Delphi all is ok (DynArraySize in Foo returns 4). In FPC DynArraySize return 1 (and program crash at the end). Is dynamic array with const modifier in FPC passed by value?

It is undefined how it is passed (and how it is passed can change from version to version, although we will never change such details just because we can). Relying on such implementation details is wrong, regardless of what Delphi does. We do not aim to create a code generator that creates exactly the same code as Delphi, we only work towards compatibility at the Pascal language level (that does not include pointer hacks to peek at implementation details) and only to the extent that this Pascal-level behaviour is documented.

Show quoted text 16 lines
With small modifications especially for FPC (by adding constref) all works fine: ------ code ------ procedure Foo(constref A: Pointer); begin WriteLn(DynArraySize(A)); end; type TFoo = procedure(constref A: TIntegerDynArray); ------ code ------ I can live with constref, but I'm much worried by this behavior (in version without constref). Why my code don't work in both compilers same? I would like to understand...

The behaviour of "const" is implementation-defined in FPC. There is no guarantee regarding how it affects parameter passing at the low level for non-cdecl/cppdecl/mwpascal calling conventions.

Jonas

Reply by email

Maciej Izak
to Jonas Maebe: It is undefined how it is passed (and how it is passed can change from version to…
Show quoted text 16 lines
It is undefined how it is passed (and how it is passed can change from version to version, although we will never change such details just because we can). Relying on such implementation details is wrong, regardless of what Delphi does. We do not aim to create a code generator that creates exactly the same code as Delphi, we only work towards compatibility at the Pascal language level (that does not include pointer hacks to peek at implementation details) and only to the extent that this Pascal-level behaviour is documented. The behaviour of "const" is implementation-defined in FPC. There is no guarantee regarding how it affects parameter passing at the low level for non-cdecl/cppdecl/mwpascal calling conventions.

But in this case the behaviour of "const" (for dynamic arrays) is the default for register calling convention (independent of the Pascal language level, this is the low level behaviour forced by the register calling convention)... Probably. ;) Should I report it to Bugtracker?

Regards,
HNB

Reply by email

Jonas Maebe
to Maciej Izak
Maciej Izak wrote 5 lines
But in this case the behaviour of "const" (for dynamic arrays) is the default for register calling convention (independent of the Pascal language level, this is the low level behaviour forced by the register calling convention)... Probably. ;) Should I report it to Bugtracker?

There is already an overarching bug report for that (http://bugs.freepascal.org/view.php?id=15691 ). But unless you want your code only to work on i386, that won't help you much. The "register" calling convention is not supported on any other architecture.

Jonas

Reply by email

Maciej Izak
to Sven Barth: Generics.Collections): I don't see a reason to use hacks for such code. ... I wonder why…
I don't see a reason to use hacks for such code.

It is hard to explain. You will see it soon in all its glory ;). It's very important construction (I use this construction very often - generics, serialization, script languages binding...). A short introduction:

===begin code===
// I need universal non generic method to compare arrays without knowledge
of its TypeInfo...
class function TEquals.DynArray(constref ALeft, ARight: Pointer): Boolean;
var
  _self: TAbstractHashCode.PSpoofInterfacedTypeSizeObject absolute Self; //
few more tricks...
  LLength: Integer;
begin
  LLength := DynArraySize(ALeft);
  if LLength  DynArraySize(ARight) then
    Exit(False);
  Result := CompareMem(ALeft, ARight, LLength * _self.Size);
end;
===end code===

you wrote earlier in my other topic:

"You can't do things as equality tests in a general way anyway (best example: case sensitive vs. case insensitive comparison of strings)."

So... I just finished writing something theoretically impossible to write O_o. Now I do equality tests in a general way (partially because the construction like in Foo).

To get:

===begin code===
    IEqualityComparer<T> = interface
    function Equals(constref ALeft, ARight: T): Boolean;
    function GetHashCode(constref AValue: T): UInt32; overload;
    function GetHashCode(constref AValue: T; out APrimaryHash: UInt32):
UInt32; overload; // for double hashing
  end;
===end code===

for any type (seriously any type supported by FPC except file type - Error: File types must be var parameters), just call TEqualityComparer<T>.Default:

===begin code===
var
  ec_array: IEqualityComparer<TIntegerDynArray>;
  ec_string: IEqualityComparer<string>;
begin
  ec_array := TEqualityComparer<TIntegerDynArray>.Default;
  ec_array :=
TEqualityComparer<TIntegerDynArray>.Default(TOptionalNonDefaultHashFactory);
  // case insensitive comparer
  ec_string :=
TEqualityComparer<string>.Construct(MyNonCaseSensitiveComparerMethod);
  // or soon with anonymous methods
  ec_string := TEqualityComparer<string>.Construct(
      function(constref ALeft, ARight: string): Integer);
      begin
        Result := SameText(ALeft, ARight);
      end, TOptionalNonDefaultHashFactory);
===end code===

At this moment you can use dictionary (with any custom IEqualityComparer) in FPC with any type (used type don't need to implement any additional operators):

===begin code===
var
  dr: TDictionary<TMyRecord, string>;
  dp: TDictionary<PAnyPointer, Variant>;
  dcr: TDictionary<TClass, TArrayOfObjects>;
  da: TDictionary<TIntegerDynArray, Single>;
  dCaseInsensitive: TDictionary<string, TObject>;
  dCaseSensitive: TDictionary<string, TObject>;
  {... and so on...}
  dCaseInsensitive := TDictionary<string, TObject>(StringC
aseInsensitiveEqualityComparer);
  dCaseSensitive := TDictionary<string, TObject>.Create; // default case
sensitive
===end code===

If you need to use default freepascal operators for comparisons there is also TFastDictionary - for some optimizations.

Btw. I proposed the creation of the new system functions: GetReferenceToValue and GetValueSize (I think it's still a good idea). This would simplify some things...

I wonder why this combination compiles at all O.o

comfortable for my tricks ^_^

Regards,
HNB

Reply by email