Sign in Join

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

Mailing lists / fpc-pascal / 2017-07

Subclassing generic records?

7 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

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

Ryan Joseph

I’m trying to find a way to extend generic records since (as far as I know) there is no subclassing of records. I could use variant records here but these don’t work with generics so I tried record helpers and properties to basically rename the x/y variables in TVec2 but I get errors on the width/height properties. Is there a workaround for this?

type
	generic TVec2<T> = record
		public
			x, y: T;
	end;
type
	TSize = specialize TVec2<TFloat>;
type
	TVec2Size = record helper for TSize
		property Width: TFloat read x write x;
		property Height: TFloat read y write y;
	end;

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph
"Ryan Joseph" wrote
I’m trying to find a way to extend generic records since (as far as I

know) there is no subclassing of records. I could use variant records here but these don’t work with generics so I tried record helpers and properties to basically rename the x/y variables in TVec2 but I get errors on the width/height properties. Is there a workaround for this?

Show quoted text 13 lines
type generic TVec2<T> = record public x, y: T; end; type TSize = specialize TVec2<TFloat>; type TVec2Size = record helper for TSize property Width: TFloat read x write x; property Height: TFloat read y write y; end;

I'll need to check whether Delphi allows that for helpers (doesn't matter whether the extended type is a specialization or not).

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
Show quoted text 5 lines
On Jul 17, 2017, at 10:58 AM, Sven Barth via fpc-pascal wrote: I'll need to check whether Delphi allows that for helpers (doesn't matter whether the extended type is a specialization or not).

Thanks Sven. Records/objects/classes in Pascal feel very confused right now. Records are moving in the direction of legacy style “objects” which are allocated on the stack so I wonder why objects just don’t replace records already? Having said that why don’t classes just replace records/objects except they allocate on the stack? These 3 distinct constructs don’t really make sense more imo.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph
Ryan Joseph wrote 8 lines
> On Jul 17, 2017, at 10:58 AM, Sven Barth via fpc-pascal wrote: > > I'll need to check whether Delphi allows that for helpers (doesn't matter whether the extended type is a specialization or not). > > Thanks Sven. Records/objects/classes in Pascal feel very confused right now. Records are moving in the direction of legacy style “objects” which are allocated on the stack so I wonder why objects just don’t replace records already? Having said that why don’t classes just replace records/objects except they allocate on the stack? These 3 distinct constructs don’t really make sense more imo.

First of records can't replace objects, because objects have inheritance and a VMT for virtual methods not to mention destructors. Secondly this would lead to all kind of backwards compatibility problems. Classes are not designed to be allocated on the stack. They are only on the heap, thus they are a different kind of beast to records and objects. And records - unlike objects and classes - can have operator overloads *inside* of them which allows this operators to be used inside generic specializations.

I have also another solution for your original problem. Take this code:

=== code begin ===

program trechelpfld;
{$mode objfpc}
{$modeswitch advancedrecords}
type
  TTest = record
    x, y: Double;
  end;
  TTestHelper = record helper for TTest
  private
    function GetWidth: Double; inline;
    function GetHeight: Double; inline;
    procedure SetWidth(aValue: Double); inline;
    procedure SetHeight(aValue: Double); inline;
  public
    property Width: Double read GetWidth write SetWidth;
    property Height: Double read GetHeight write SetHeight;
  end;
function TTestHelper.GetWidth: Double;
begin
  Result := x;
end;
function TTestHelper.GetHeight: Double;
begin
  Result := y;
end;
procedure TTestHelper.SetWidth(aValue: Double);
begin
  x := aValue;
end;
procedure TTestHelper.SetHeight(aValue: Double);
begin
  y := aValue;
end;
var
  t: TTest;
  x, y: Double;
begin
  t.x := 42.0;
  t.y := 21.0;
  x := t.Width;
  y := t.Height;
  t.Width := y;
  t.Height := x;
end.

=== code end ===

Through the use of "inline" this results in code without calls as the assembly output of the main procedure without any optimizations shows:

=== code begin ===

.section .text.n_main
.balign 16,0x90
.globl PASCALMAIN
.type PASCALMAIN,@function
PASCALMAIN:
.globl main
.type main,@function
main:
.Lc21:
# Temps allocated between rbp-16 and rbp+0
# [45] begin
pushq %rbp
.Lc23:
.Lc24:
movq %rsp,%rbp
.Lc25:
leaq -16(%rsp),%rsp
call FPC_INITIALIZEUNITS
# [46] t.x := 42.0;
movq _$TRECHELPFLD$_Ld1,%rax
movq %rax,U_$P$TRECHELPFLD_$$_T
# [47] t.y := 21.0;
movq _$TRECHELPFLD$_Ld2,%rax
movq %rax,U_$P$TRECHELPFLD_$$_T+8
# [49] x := t.Width;
movsd U_$P$TRECHELPFLD_$$_T,%xmm0
movsd %xmm0,U_$P$TRECHELPFLD_$$_X
# [50] y := t.Height;
movsd U_$P$TRECHELPFLD_$$_T+8,%xmm0
movsd %xmm0,U_$P$TRECHELPFLD_$$_Y
# [52] t.Width := y;
movq U_$P$TRECHELPFLD_$$_Y,%rax
movq %rax,U_$P$TRECHELPFLD_$$_T
# [53] t.Height := x;
movq U_$P$TRECHELPFLD_$$_X,%rax
movq %rax,U_$P$TRECHELPFLD_$$_T+8
# [54] end.
call FPC_DO_EXIT
leave
ret
.Lc22:
.Le4:
.size main, .Le4 - main

=== code end ===

It might be more to write, but it works...

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth

Thanks Sven, that’s one way around it.

So is it by design that records don’t have inheritance or is this planned?

and is it also by design that classes don’t allow operator overloads inside them? That seems like an omission since the way they’re implemented in records is much nicer imo.

On Jul 20, 2017, at 4:11 AM, Sven Barth via fpc-pascal wrote: I have also another solution for your original problem. Take this code:

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph
"Ryan Joseph" wrote
Thanks Sven, that’s one way around it. So is it by design that records don’t have inheritance or is this planned?

Yes, that's by design and there are no plans to change this.

and is it also by design that classes don’t allow operator overloads

inside them? That seems like an omission since the way they’re implemented in records is much nicer imo.

There are two points you need to be aware of to understand why thinks are the way they are:
First of global operator overloads were the first that were implemented in FPC. Then Delphi came along and added record operator overloads, because they needed them for generics. FPC followed, cause the global overloads are useless in that case.
Secondly even for global operator overloads classes are not really that suitable, cause in complex expressions (let's say "a * (b + c)") you'll have the generation of temporary variables (in the example the result of "b
+ c"), but the compiler can't know whether it's really a temporary value that it can free after the whole expression is handled or whether merely the instance that had been passed in (let's say "b") had been modified and passed as a result. Thus operator overloads for classes can easily lead to memory leaks or other hard to debug problems. Yes, that's already possible with global operators as well, but with operators that are part of the class that would be much more inviting.

Regards,
Sven

Reply by email

Martin Schreiber
to Ryan Joseph: Thanks Sven, that’s one way around it. So is it by design that records don’t have…
Ryan Joseph wrote
Thanks Sven, that’s one way around it. So is it by design that records don’t have inheritance or is this planned?

MSElang has a concept of unified record, object and class constructs. There a record is a simplified object which never has a VMT and has no methods. An object has a VMT only if the attribute [virtual] is given. An object can be instantiated in global or stack memory or on heap if it has a constructor() and destructor().
A class is an object which always is instantiated on heap and which provides implicit dereferencing. Classes can inherit from objects. https://gitlab.com/mseide-msegui/mselang/wikis/home/mselang_objects https://gitlab.com/mseide-msegui/mselang/wikis/home (under construction).

Martin

Reply by email