Sign in Join

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

Mailing lists / fpc-devel / 2019-04

class operator overloads

16 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

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

Ryan Joseph

Nagging question I had that I would like answered. I was told that we can’t have operator overloads on classes (only records) because there is a potential temporary class that could be created and thus some kind of automatic memory management would be needed. How is this any different than function operator overloads? If you allocated a new class in "operator +” then you would get a memory leak right? If that’s true then I don’t understand why we can’t have them in the actual class structure.

// these both do leak
operator + (left: TClass; right: T): TClass;
begin
  result := TClass.Create(right);
end;
class operator + (left: TClass; right: T): TClass;
begin
    result := TClass.Create(right);
end;
// these both don’t leak
operator + (left: TClass; right: T): TClass;
begin
  result := left;
end;
class operator + (left: TClass; right: T): TClass;
begin
    result := left;
end;

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph
Ryan Joseph wrote
Nagging question I had that I would like answered. I was told that we can’t have operator overloads on classes (only records) because there is a potential temporary class that could be created and thus some kind of automatic memory management would be needed. How is this any different than function operator overloads? If you allocated a new class in "operator +” then you would get a memory leak right? If that’s true then I don’t understand why we can’t have them in the actual class structure.

It's the same problem there.
The thing however is that FPC's operator overloading support is quite old (compared to Delphi's) and it's possible that it is even older than the support for Delphi's class type. So the global operators simply gained support for classes by "accident" while the ones nested in records came much later by choice. Adding support for operators inside classes would be a choice as well.

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
On Apr 16, 2019, at 4:51 PM, Sven Barth via fpc-devel wrote: It's the same problem there. The thing however is that FPC's operator overloading support is quite old (compared to Delphi's) and it's possible that it is even older than the support for Delphi's class type. So the global operators simply gained support for classes by "accident" while the ones nested in records came much later by choice. Adding support for operators inside classes would be a choice as well.

Ok now I finally understand. So why is this really a problem? It just means the operator needs to mutate the actual class instead of making a copy like for records, but those are the rules anyways. Given that I still don’t understand why "class operator” isn’t included in classes.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph
Ryan Joseph wrote 6 lines
> On Apr 16, 2019, at 4:51 PM, Sven Barth via fpc-devel wrote: > > It's the same problem there. > The thing however is that FPC's operator overloading support is quite old (compared to Delphi's) and it's possible that it is even older than the support for Delphi's class type. So the global operators simply gained support for classes by "accident" while the ones nested in records came much later by choice. Adding support for operators inside classes would be a choice as well. Ok now I finally understand. So why is this really a problem? It just means the operator needs to mutate the actual class instead of making a copy like for records, but those are the rules anyways. Given that I still don’t understand why "class operator” isn’t included in classes.

Mutating is also not the solution. Consider this:

=== code begin ===

operator + (aLeft, aRight: TSomeClass): TSomeClass;
begin
   aLeft.DoSomething(aRight);
   Result := aLeft;
end;
var
   a, b, c: TSomeClass;
begin
   a := TSomeClass.Create;
   b := TSomeClass.Create;
   c := a + b;
   a.Free;
   b.Free;
   c.Free; (* boom! *)
end.

=== code end ===

A user does not necessarily know what an operator overload does.

For records and TP style objects this is not a problem, because they are copied, but for classes only their pointer is copied (and this either leads to the above problem or memory leaks if temps are involved).

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
Show quoted text 31 lines
On Apr 16, 2019, at 6:10 PM, Sven Barth via fpc-devel wrote: Mutating is also not the solution. Consider this: === code begin === operator + (aLeft, aRight: TSomeClass): TSomeClass; begin aLeft.DoSomething(aRight); Result := aLeft; end; var a, b, c: TSomeClass; begin a := TSomeClass.Create; b := TSomeClass.Create; c := a + b; a.Free; b.Free; c.Free; (* boom! *) end. === code end === A user does not necessarily know what an operator overload does. For records and TP style objects this is not a problem, because they are copied, but for classes only their pointer is copied (and this either leads to the above problem or memory leaks if temps are involved). Regards, Sven

I see why that could be a problem but aren’t users reasonable for not doing dangerous things in a language like Pascal with low-level memory access? They can already do this anyways via operator functions so it’s already too late. :) If you want a language that prioritizes safety then Pascal is already a bad choice so I think most people know how to be safe.

What this means is that’s it harder for more advanced programmers to do things like add + and := operator to collection classes like arrays and that’s pretty unfortunate. At this point it’s already been smuggled in via operator functions so I think the team should reconsider allowing them for classes.

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
Show quoted text 6 lines
On Apr 20, 2019, at 9:56 AM, Ryan Joseph wrote: > On Apr 16, 2019, at 8:43 PM, Ryan Joseph wrote: > > I see why that could be a problem but aren’t users reasonable for not doing dangerous things in a language like Pascal with low-level memory access? They can already do this anyways via operator functions so it’s already too late. :) If you want a language that prioritizes safety then Pascal is already a bad choice so I think most people know how to be safe.

I just noticed this didn’t get posted (as happens often for some reason) so I’m resending. Sorry for the noise if it gets posted twice.

====================================

I had some time today so I investigated what it would take to add class operators and it’s actually totally trivial.

Given that there are some compromises we could possibly make.

1) Allow class operators but put them behind a mode switch so the user explicitly knows they’re opting in. Users can already add operators to classes via operator functions so this just formalizes the feature and syntax which was introduced for records.
2) Only use a subset of class operators that return booleans (i.e. comparison, in) so there isn’t a potential for allocating temporary memory. Better than nothing but I’d prefer users who understand operator overloads be allowed full access.

Does that sound reasonable?

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Ryan Joseph

Sorry to bring this up again but I had a thought today while working regarding class operator overloads in classes (i.e. operators that are members of classes).

If the operators in classes allowed another parameter for the left sided value could this idea be more viable? Unless I’m mistaken I think this would make it possible to do the memory management required.

============

type
  TMyClass = class
    public
      class operator := (left: TMyClass; right: integer): TMyClass;
  end;
class operator TMyClass.:= (left: TMyClass; right: integer): TMyClass;
begin
  if not asssigned(left) then
    ; // allocate new
  else
    ; // delete old and return new (or mutate)
end;
var
  c: TMyClass;
begin
  c := 100; // c := TMyClass.:=(c, 100);
end.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Mo., 10. Juni 2019, 03:00:

Show quoted text 8 lines
Sorry to bring this up again but I had a thought today while working regarding class operator overloads in classes (i.e. operators that are members of classes). If the operators in classes allowed another parameter for the left sided value could this idea be more viable? Unless I’m mistaken I think this would make it possible to do the memory management required.

A basic principle of operator overloads is that existing, internal operators are not overloadable. This principle won't be abandoned only because you want managed classes.

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
On Jun 10, 2019, at 3:13 AM, Sven Barth via fpc-devel wrote: A basic principle of operator overloads is that existing, internal operators are not overloadable. This principle won't be abandoned only because you want managed classes.
Existing internal operators? Sorry I don’t understand. The := operator is already overloadable but the left side isn’t passed in. Same goes for other binary operators where the destination variable is not passed it even though that could have been useful information.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Mo., 10. Juni 2019, 14:28:

Show quoted text 15 lines
> On Jun 10, 2019, at 3:13 AM, Sven Barth via fpc-devel < > wrote: > > A basic principle of operator overloads is that existing, internal operators are not overloadable. This principle won't be abandoned only because you want managed classes. > Existing internal operators? Sorry I don’t understand. The := operator is already overloadable but the left side isn’t passed in. Same goes for other binary operators where the destination variable is not passed it even though that could have been useful information.

The operator for assigning one class instance to another exists and is internal thus it is not allowed to be overloaded.

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
On Jun 10, 2019, at 9:25 AM, Sven Barth via fpc-devel wrote: The operator for assigning one class instance to another exists and is internal thus it is not allowed to be overloaded.

Is that true for binary operators also? The same principle could apply to them also and perhaps solve the problem you posted below.

operator + (aLeft, aRight: TSomeClass): TSomeClass;
begin
  aLeft.DoSomething(aRight);
  Result := aLeft;
end;
var
  a, b, c: TSomeClass;
begin
  a := TSomeClass.Create;
  b := TSomeClass.Create;
  c := a + b;
  a.Free;
  b.Free;
  c.Free; (* boom! *)
end.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Mo., 10. Juni 2019, 15:28:

Show quoted text 12 lines
> On Jun 10, 2019, at 9:25 AM, Sven Barth via fpc-devel < > wrote: > > The operator for assigning one class instance to another exists and is internal thus it is not allowed to be overloaded. > Is that true for binary operators also? The same principle could apply to them also and perhaps solve the problem you posted below.

The +-operator is not defined internally thus it is allowed to be overloaded. (is it really that hard to understand? If you can use an operator in code without declaring an operator overload then this operator is internal and not allowed to be overloaded).

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
On Jun 10, 2019, at 9:31 AM, Sven Barth via fpc-devel wrote: The +-operator is not defined internally thus it is allowed to be overloaded. (is it really that hard to understand? If you can use an operator in code without declaring an operator overload then this operator is internal and not allowed to be overloaded).
I get what you mean but I had no idea := with the destination variable was an operator.

I just tried this right now and I didn’t an error but it was indeed not overloaded and never got called. A bug?

operator := (left, right: TObject): TObject;
begin
  writeln('custom :=');
end;
var
  c: TObject;
begin
  c := TObject.Create;
end.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Mo., 10. Juni 2019, 16:26:

Show quoted text 13 lines
> On Jun 10, 2019, at 9:31 AM, Sven Barth via fpc-devel < > wrote: > > The +-operator is not defined internally thus it is allowed to be overloaded. (is it really that hard to understand? If you can use an operator in code without declaring an operator overload then this operator is internal and not allowed to be overloaded). > I get what you mean but I had no idea := with the destination variable was an operator.

There exist only two assignment operators, namely "Implicit" (aka := in mode ObjFPC) and "Explicit" both taking a single argument. Introducing a binary operator does not change that the operator is forbidden for classes.

Show quoted text 14 lines
I just tried this right now and I didn’t an error but it was indeed not overloaded and never got called. A bug? operator := (left, right: TObject): TObject; begin writeln('custom :='); end; var c: TObject; begin c := TObject.Create; end.

Did you try with trunk? Cause I fixed something related to that a few weeks ago.

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
Show quoted text 20 lines
On Jun 10, 2019, at 2:21 PM, Sven Barth via fpc-devel wrote: There exist only two assignment operators, namely "Implicit" (aka := in mode ObjFPC) and "Explicit" both taking a single argument. Introducing a binary operator does not change that the operator is forbidden for classes. I just tried this right now and I didn’t an error but it was indeed not overloaded and never got called. A bug? operator := (left, right: TObject): TObject; begin writeln('custom :='); end; var c: TObject; begin c := TObject.Create; end. Did you try with trunk? Cause I fixed something related to that a few weeks ago.

Yeah in trunk it says impossible overload for equal types.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Mo., 10. Juni 2019, 20:35:

Show quoted text 30 lines
> On Jun 10, 2019, at 2:21 PM, Sven Barth via fpc-devel < > wrote: > > There exist only two assignment operators, namely "Implicit" (aka := in mode ObjFPC) and "Explicit" both taking a single argument. Introducing a binary operator does not change that the operator is forbidden for classes. > > > I just tried this right now and I didn’t an error but it was indeed not overloaded and never got called. A bug? > > operator := (left, right: TObject): TObject; > begin > writeln('custom :='); > end; > > var > c: TObject; > begin > c := TObject.Create; > end. > > Did you try with trunk? Cause I fixed something related to that a few weeks ago. > Yeah in trunk it says impossible overload for equal types.

As it should.

Regards,
Sven

Reply by email