Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Ryan Joseph via fpc-pascal



> On Feb 17, 2021, at 9:59 AM, Adriaan van Os via fpc-pascal 
>  wrote:
> 
> 1. multiple inheritance is nice to have, but it has the big issue that the 
> inheritance tree becomes an inheritance graph and that makes overrules 
> ambiguent.
> 2. interfaces don't have this issue with multiple inheritance, because they 
> just declare, not implement
> 3. but that is also the weakness of interfaces,  as we don't want to 
> reimplement the same code each time
> 4. so, we really want an multiple-inheritance graph at the declaration level 
> with clear tree-like unambigous inheritance paths at the implementation level
> 5. thus, the idea is to "push-in" implementation code into an interface that 
> integrates fully at the declaration level but is independent at the 
> implementation level.

I would say that's right.

The problem is that, we like OOP inheritance  but when we extend classes we are 
forced into a single hierarchy. We could use existing delegation patterns and 
dot-notation  like obj.helper.DoSomething or by breaking out entirely and using 
plain functions  but then we lose some of what makes OOP nice, which is, simply 
saying "something.DoFunction". It may be trivial in terms of typing but it's 
elegant and in my opinion clean code which is not tedious to write makes happy 
and more productive.


Here's a more practical example but there are other possibilities for 
composition patterns (I'l think of examples later). I copied this from the RTL 
and made some changes. Assume you have this hierarchy and you want to add some 
methods/data to JPEG and GIF images but not TIFF and PNG (they have some 
special compression needs or something like that). What we would do now is 
probably just dump them into TCustomBitmap (and bloat TIFF/PNG)  or make 
another subclass just to store the extra methods (which is useless besides 
being a method store for 2 specific other classes). 

What I want with traits is that I can extend those 2 classes with specific 
functionality, still retain the object-orientedness of the syntax and not get 
trapped trying to inject stuff into a hierarchy where it doesn't really belong 
anyways.


TTIFFBitmap   TPNGBitmap   TJPEGBitmap   TGIFBitmap
| 
TCustomBitmap
| 
TRasterImage
| 
TGraphic
| 
TPersistent
| 
TObject


Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] interfaces and smartpointers [was Traits Proposal]

2021-02-17 Thread Ryan Joseph via fpc-pascal


> On Feb 17, 2021, at 4:51 PM, Benito van der Zander via fpc-pascal 
>  wrote:
> 
> I benchmarked it years ago, I do not remember the details.
> 
> But InitInterfacePointers was bad. Just look at it:
> 

That's not great for sure. Fillchar could be a big culprit also actually. I for 
one would like I way to disable that for special circumstances.

I don't Lazarus usually and I can't get the debugger to work on my Mac so tried 
Compiler Explorer. Not seeing the method calls here though.

https://godbolt.org/z/cjY3Te

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] interfaces and smartpointers [was Traits Proposal]

2021-02-17 Thread Benito van der Zander via fpc-pascal


On 17.02.21 21:43, Ryan Joseph via fpc-pascal wrote:

So where is your 10% performance hit coming from then?

on init:

InitInterfacePointers or TInterfacedObject.AfterConstruction?



I benchmarked it years ago, I do not remember the details.

But InitInterfacePointers was bad. Just look at it:

  procedure InitInterfacePointers(objclass: tclass;instance : pointer);

    var
  ovmt: PVmt;
  i: longint;
  intftable: pinterfacetable;
  Res: pinterfaceentry;
    begin
  ovmt := PVmt(objclass);
  while assigned(ovmt) and {$ifdef VER3_0}(ovmt^.vIntfTable <> 
@emptyintf){$else}assigned(ovmt^.vIntfTable){$endif} do

    begin
  intftable:=ovmt^.vIntfTable;
  {$ifdef VER3_0}
  if assigned(intftable) then
  {$endif VER3_0}
  begin
    i:=intftable^.EntryCount;
Res:=@intftable^.Entries[0];
    while i>0 do begin
  if Res^.IType = etStandard then
    ppointer(@(pbyte(instance)[Res^.IOffset]))^:=
  pointer(Res^.VTable);
  inc(Res);
  dec(i);
    end;
  end;
  ovmt:=ovmt^.vParent;
    end;
    end;


But reusing objects avoids calling all the functions from create. (but 
reusing is also expensive, besides the memory increase, it needs a free 
list and thread safe handling of the head of the list)




otherwise, during the wrapper function? As Sven pointed out the ref counting 
happens with records operators also. ARC is general may just be slow if you 
implement it everywhere.



The wrapper function was not included in the 10%


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] interfaces and smartpointers [was Traits Proposal]

2021-02-17 Thread Ryan Joseph via fpc-pascal


> On Feb 17, 2021, at 11:11 AM, Benito van der Zander via fpc-pascal 
>  wrote:
> 
> just open the disassembler window in Lazarus and single step through all the 
> instructions. Then you see everything
> 
> create alone calls a bunch of methods:
> 
> TInterfacedObject.NewInstance 
> TObject.NewInstance 
> getmem
> TObject.InitInstance
> fillchar
> InitInterfacePointers
> fpc_pushexceptaddr (on linux only?)
> fpc_setjmp
> TInterfacedObject.AfterConstruction
> 

So where is your 10% performance hit coming from then? 

on init:

InitInterfacePointers or TInterfacedObject.AfterConstruction? 

otherwise, during the wrapper function? As Sven pointed out the ref counting 
happens with records operators also. ARC is general may just be slow if you 
implement it everywhere.


Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] interfaces and smartpointers [was Traits Proposal]

2021-02-17 Thread Benito van der Zander via fpc-pascal

Hi,


1) Implicit cast to ITest which does a runtime lookup (but there's interface 
name so no string comparison like Supports?)


just open the disassembler window in Lazarus and single step through all 
the instructions. Then you see everything


create alone calls a bunch of methods:

TInterfacedObject.NewInstance
TObject.NewInstance
getmem
TObject.InitInstance
fillchar
InitInterfacePointers
fpc_pushexceptaddr (on linux only?)
fpc_setjmp
TInterfacedObject.AfterConstruction


(interfaces with TInterfacedObject are even worse than implementing the 
interface yourself )


The implicit casting is very fast, a single assembly instruction:

add    $0x20,%rsi

But then it also calls fpc_intf_assign for reference counting



2) calling "println" there is a call to a wrapper function (called "thunks")?


Yes. Basically the wrapper function casts the interface back to the 
class (sub    $0x20,%rdi) before calling the actual method. Because the 
class method assumes self is the class and not an interface





Bye,
Benito
On 17.02.21 17:37, Ryan Joseph via fpc-pascal wrote:



On Feb 17, 2021, at 8:27 AM, Benito van der Zander via fpc-pascal 
 wrote:

var c: ITest;
begin
   c := TTest.Create(123);
   c.println;

So this is where you're getting your performance penalties? Correct me if I'm 
wrong but two things happen here:

1) Implicit cast to ITest which does a runtime lookup (but there's interface 
name so no string comparison like Supports?)
2) calling "println" there is a call to a wrapper function (called "thunks")?

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] self corrupted after jump to overridden method

2021-02-17 Thread Mattias Gaertner via fpc-pascal
On Wed, 17 Feb 2021 15:27:40 +0100
Mattias Gaertner via fpc-pascal  wrote:

> Hi,
> 
> I have a strange bug, that suddenly Self becomes a random value when
> calling an overridden virtual method. It happens when activating a
> TCustomSQLQuery.
> Below is a gdb stacktrace with fpc 3.3.1, where Self became 0. On
> other runs I saw various other values.

My patch turns the crash into a normal Exception:
https://bugs.freepascal.org/view.php?id=38503

 
> Where to start looking?
> It happens in Lazarus at designtime, so maybe some vmt issue?

Turns out it was an evil combination of optimization and misleading gdb
output.

Mattias
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Adriaan van Os via fpc-pascal

To streamline the discussion, let me recapitulate:

1. multiple inheritance is nice to have, but it has the big issue that the inheritance tree becomes 
an inheritance graph and that makes overrules ambiguent.

2. interfaces don't have this issue with multiple inheritance, because they 
just declare, not implement
3. but that is also the weakness of interfaces,  as we don't want to reimplement the same code each 
time
4. so, we really want an multiple-inheritance graph at the declaration level with clear tree-like 
unambigous inheritance paths at the implementation level
5. thus, the idea is to "push-in" implementation code into an interface that integrates fully at 
the declaration level but is independent at the implementation level.


So, the idea (and the purpose of the discussion) I think is to implement (5) in 
an elegant way.

Regards,

Adriaan van Os

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] interfaces and smartpointers [was Traits Proposal]

2021-02-17 Thread Ryan Joseph via fpc-pascal


> On Feb 17, 2021, at 8:27 AM, Benito van der Zander via fpc-pascal 
>  wrote:
> 
> var c: ITest;
> begin
>   c := TTest.Create(123);
>   c.println;

So this is where you're getting your performance penalties? Correct me if I'm 
wrong but two things happen here:

1) Implicit cast to ITest which does a runtime lookup (but there's interface 
name so no string comparison like Supports?)
2) calling "println" there is a call to a wrapper function (called "thunks")?

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] interfaces and smartpointers [was Traits Proposal]

2021-02-17 Thread Benito van der Zander via fpc-pascal

Hi,



(1) A record is not a pointer. So that would require some implicit 
referencing in the property


(2) If it was managed, it would be an allocation, so I don't 
understand this.



I am talking about replacing interfaces with an record.

For example this

/

type ITest = interface
  procedure println;
end;
TTest = class(TInterfacedObject, ITest)
  v: integer;
  constructor Create(av: integer);
  procedure println;
end;

constructor TTest.Create(av: integer);
begin
  v := av;
end;

procedure TTest.println;
begin
  writeln(v);
end;

var c: ITest;
begin
  c := TTest.Create(123);
  c.println;
/


would become:


/

type

TTestRec = class
  rc: Integer;
  v: integer;
  constructor Create(av: integer);
  procedure println;
end;
RTest = record
  ptr: TTestRec;
  procedure println; inline;
  class operator :=(c: TTestRec): RTest;
  class operator Initialize(var aRec: RTest);
  class operator finalize(var aRec: RTest);
  class operator AddRef(var aRec: RTest);
end;

constructor TTestRec.Create(av: integer);
begin
  v := av;
end;

procedure TTestRec.println;
begin
  writeln(v);
end;

procedure RTest.println;
begin
  ptr.println; //the wrapper function is inlined
end;

class operator RTest.:=(c: TTestRec): RTest;
begin
  result := default(RTest);
  result.ptr := c;
  if c <> nil then
    InterlockedIncrement(c.rc);
end;

class operator RTest.Initialize(var aRec: RTest);
begin
  aRec.ptr := nil;
end;

class operator RTest.finalize(var aRec: RTest);
begin
  if aRec.ptr <> nil then
    if InterlockedDecrement(aRec.ptr.rc) = 0 then
  aRec.ptr.Free;
end;

class operator RTest.AddRef(var aRec: RTest);
begin
  if aRec.ptr <> nil then
    InterlockedIncrement(aRec.ptr.rc);
end;

var
    r: RTest;
begin
  r := TTestRec.Create(123);
  r.println;
/

Or even replace the class with a record, too:


/

type
PTestRec2 = ^TTestRec2;
TTestRec2 = record
  rc: Integer;
  v: integer;
  class function Create(av: integer): PTestRec2; static;
  procedure println;
end;
RTest2 = record
  ptr: ^TTestRec2;
  procedure println; inline;
  class operator :=(c: PTestRec2): RTest2;
  class operator Initialize(var aRec: RTest2);
  class operator finalize(var aRec: RTest2);
  class operator AddRef(var aRec: RTest2);
end;

class function TTestRec2.Create(av: integer): PTestRec2;
begin
  new(result);
  result^.rc := 0;
  result^.v := av;
end;

procedure TTestRec2.println;
begin
  writeln(v);
end;

procedure RTest2.println;
begin
  ptr^.println;
end;

class operator RTest2.:=(c: PTestRec2): RTest2;
begin
  result := default(RTest2);
  result.ptr := c;
  if c <> nil then
    InterlockedIncrement(c^.rc);
end;

class operator RTest2.Initialize(var aRec: RTest2);
begin
  aRec.ptr := nil;
end;

class operator RTest2.finalize(var aRec: RTest2);
begin
  if aRec.ptr <> nil then
    if InterlockedDecrement(aRec.ptr^.rc) = 0 then
  dispose(aRec.ptr);
end;

class operator RTest2.AddRef(var aRec: RTest2);
begin
  if aRec.ptr <> nil then
    InterlockedIncrement(aRec.ptr^.rc);
end;
var
    r2: RTest2;
begin
  r2 := TTestRec2.Create(123);
  r2.println;
/




Not sure if it is actually faster. That needs to be investigated.

But it definitely helps with the memory usage:

  writeln(ttest.InstanceSize);
  writeln(ttestrec.InstanceSize);
  writeln(sizeof(ttestrec2));

40
16
8

With many small objects it should be faster just because it fits better 
in the cache.


Cheers,
Benito
On 17.02.21 14:31, Marco van de Voort via fpc-pascal wrote:


Op 2021-02-17 om 00:02 schreef Benito van der Zander via fpc-pascal:


And there often is a lot of unintentional deep copying. This is also 
why a property returning a record is fairly useless except for 
extremely small records like TPoint (and even that is not optimal no 


But a managed record to replace an interface, would only contain a 
single pointer/class ref. That can be copied fast


(1) A record is not a pointer. So that would require some implicit 
referencing in the property


(2) If it was managed, it would be an allocation, so I don't 
understand this.



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Ryan Joseph via fpc-pascal


> On Feb 17, 2021, at 6:10 AM, Sven Barth  wrote:
> 
> Simply because no one has come around to implement it yet. The class type 
> case is more complicated than the interface case. (Same would be true for 
> record and objects)
> So, yes, we'll need to implement this first which would finally bring FPC up 
> to Delphi compatibility regarding interface delegation. 

So "class type method resolution" is what's missing? I never used the interface 
method resolution so I don't really understand this feature. What needs to 
happen as far as the compiler is concerned?

What if you want to implement virtual methods in a base class? This is kind of 
the core of "composition over inheritance" so it needs to be handled via method 
resolution I assume.




(*
 * Shape
 *)

type
  TShape = class
protected
  procedure Draw; virtual; abstract;
  end;

(*
 * Circle
 *)

type
  ICircle = interface
procedure Draw;
  end;

type
  TCircle = class(ICircle)
procedure Draw;
  end;

(*
 * My Shape
 *)

type
  TMyShape = class(TShape, ICircle)
private
  m_circle: TCircle;
protected
  procedure ICircle.Draw = Draw;
public
  property circle: TCircle read m_circle implements ICircle;
  end;

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Ryan Joseph via fpc-pascal


> On Feb 17, 2021, at 6:17 AM, Sven Barth  wrote:
> 
> No. With wrapper function he means the thunks that are responsible for 
> adjusting the instance pointer of the interface to the instance pointer of 
> the class before jumping to the method of the class. 

Oh, so this is some extra code that runs every time you call a method of an 
interface? I understand interfaces and use them sometimes, but not all the 
internal workings in the compiler and how they're implemented.

> I have the feeling that you don't really know how interfaces work.
> ARC would have the exact same penalty as the reference counting of 
> interfaces, because that is essentially what the compiler would use: call 
> some function that increases/decreases the reference count of the instance. 
> That is *exactly* what is done for interfaces. 
> 
> Also the interface is not created. It's part of the class instance. You 
> simply have a pointer sized value that points to that. In the ideal situation 
> of *only* working with the interface you can simply forget the class 
> instance, cause the class will be freed once the reference count reached 0. 
> Thus there will be *no* additional storage involved.

I'm talking about the decoupling of the interface and class and how that's a 
strange way to implement ARC. These wrapper functions and runtime lookups for 
casting are another side of it. I would hope someday we can implement ref 
counting in classes the same way as dynamic arrays and ansistrings, i.e. you 
just pass the reference and forget about it.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


[fpc-pascal] self corrupted after jump to overridden method

2021-02-17 Thread Mattias Gaertner via fpc-pascal
Hi,

I have a strange bug, that suddenly Self becomes a random value when
calling an overridden virtual method. It happens when activating a
TCustomSQLQuery.
Below is a gdb stacktrace with fpc 3.3.1, where Self became 0. On other
runs I saw various other values.

Where to start looking?
It happens in Lazarus at designtime, so maybe some vmt issue?


#0  0x013cba40 in INTERNALOPEN (this=0x0) at
fcl-db/src/sqldb/sqldb.pp:3140 

#1  0x01250f1d in DOINTERNALOPEN
(this=0x7fffc5c0) at fcl-db/src/base/dataset.inc:410 

#2
0x012520f1 in OPENCURSOR (this=0x752aab50, INFOQUERY=184)
at fcl-db/src/base/dataset.inc:958 

#3  0x013cac53 in OPENCURSOR
(this=0x752aab50, INFOQUERY=false) at
fcl-db/src/sqldb/sqldb.pp:2824 

#4  0x01252518 in SETACTIVE
(this=0x752aab50, VALUE=true) at fcl-db/src/base/dataset.inc:1093

#5  0x0057a662 in SETORDPROP (INSTANCE=0x7fffd001,
PROPINFO=0x752aab50, VALUE=1) at ../objpas/typinfo.pp:1898 

#6
0x00a7a239 in SETORDVALUE (this=0x7fffc47155e0, NEWVALUE=1) at
propedits.pp:3315


Mattias
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Marco van de Voort via fpc-pascal


Op 2021-02-17 om 00:02 schreef Benito van der Zander via fpc-pascal:


And there often is a lot of unintentional deep copying. This is also 
why a property returning a record is fairly useless except for 
extremely small records like TPoint (and even that is not optimal no 


But a managed record to replace an interface, would only contain a 
single pointer/class ref. That can be copied fast


(1) A record is not a pointer. So that would require some implicit 
referencing in the property


(2) If it was managed, it would be an allocation, so I don't understand 
this.



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Mi., 17. Feb. 2021, 02:21:

>
>
> > On Feb 16, 2021, at 3:35 PM, Benito van der Zander via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
> >
> > But it is not calling the method, it is calling the wrapper function
>
> what you say wrapper function do you mean the ref counting functions when
> you pass around the interface?
>

No. With wrapper function he means the thunks that are responsible for
adjusting the instance pointer of the interface to the instance pointer of
the class before jumping to the method of the class.

I guess for ref counting you're supposed to cast the class to the interface
> (which is an expensive runtime lookup) and then pass that thing around?
> That seems like a crazy way to get ref counting plus you need to cast the
> interface back to the class if you want to call other methods? I still
> don't get this pattern. ARC should be handled by the compiler and be on the
> class itself, not some additional object you need to create and then store.
>

I have the feeling that you don't really know how interfaces work.
ARC would have the exact same penalty as the reference counting of
interfaces, because that is essentially what the compiler would use: call
some function that increases/decreases the reference count of the instance.
That is *exactly* what is done for interfaces.

Also the interface is not created. It's part of the class instance. You
simply have a pointer sized value that points to that. In the ideal
situation of *only* working with the interface you can simply forget the
class instance, cause the class will be freed once the reference count
reached 0. Thus there will be *no* additional storage involved.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Mi., 17. Feb. 2021, 02:15:

>
>
> > On Feb 16, 2021, at 2:43 PM, Sven Barth via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
> >
> > FPC currently does not yet support class types not to mention records
> and objects which is what I had mentioned earlier already.
>
> Yes I see this on https://freepascal.org/docs-html/ref/refse47.html. Why
> isn't this supported? I guess as part of this proposal we need to implement
> this feature as well?  I'm not familiar with this area of the language so
> I'm not sure what this entails.
>

Simply because no one has come around to implement it yet. The class type
case is more complicated than the interface case. (Same would be true for
record and objects)
So, yes, we'll need to implement this first which would finally bring FPC
up to Delphi compatibility regarding interface delegation.


> >
> >> It's a strange syntax compared to anything else we allow in classes and
> the boiler plate is really adding up now. "procedure Draw" is going to be
> in 4 lines now!
> >>
> >> It would be nicer if they made this into one line so we don't have yet
> another set of duplicate method signatures, i.e.:
> >>
> >>   procedure Draw as ICircle.Draw;
> >
> > I can't shake the feeling that all you complain about is writing even a
> little bit too much. Pascal is *not* about using as less code as possible,
> but to be as correct as possible and the syntax of interface delegation
> allows exactly that.
>
> Being too verbose is clunky in my opinion. That method resolution syntax
> is just kind of an odd addition for classes to my eyes. "procedure
> Type.Name = Name;" doesn't have any precedence inside classes/records so it
> stands out.
>

Again, the syntax is much more powerful than what you suggested. Also it's
the existing syntax and not something new. Not to mention that it only
needs to be used if you need to implement a method yourself.
Iff(!) we find out down the line once we have the basic functionality
working, that people are struggling with this, we can still think about
improving something, but for now I'd prefer to add as less new syntaxes as
possible (especially if a syntax that does provide this exact functionality
*does* exist already, no matter if it's too verbose for some).

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal