Lazarus

Free Pascal => General => Topic started by: jamie on September 04, 2024, 05:24:23 pm

Title: operators overloads working only one way?
Post by: jamie on September 04, 2024, 05:24:23 pm
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     procedure Button1Click(Sender: TObject);
  17.   private
  18.  
  19.   public
  20.  
  21.   end;
  22.  
  23. var
  24.   Form1: TForm1;
  25. operator := (A:TStream):Byte;
  26. operator := (A:Byte):TStream;
  27. implementation
  28. operator :=(A:TStream):Byte;
  29. Begin
  30.  A.Read(Result,1);
  31. End;
  32. operator :=(A:byte):Tstream;
  33. Begin
  34.    Result.Write(A,1);
  35. End;
  36. {$R *.lfm}
  37.  
  38. { TForm1 }
  39.  
  40. procedure TForm1.Button1Click(Sender: TObject);
  41. var
  42.   B:Byte;
  43.   S:TMemoryStream;
  44. begin
  45.   S:= TMemoryStream.Create;
  46.   S :=B; //<<<< nope
  47.   B:= S; //ok
  48.   S.Free;
  49. end;
  50.  
  51. end.
  52.  
  53.  

What em I doing wrong here?

 It works well only one way, the other way the compiler chokes.
Title: Re: operators overloads working only one way?
Post by: Martin_fr on September 04, 2024, 05:39:11 pm
I haven't run your code, but...

Code: Pascal  [Select][+][-]
  1. operator :=(A:byte):Tstream;
  2. Begin
  3.    Result.Write(A,1);
  4. End;

Result is not initialized. So it will contain trash, and you get an access violation, or other random error.

You are assuming that you will always get the actual memory of the target variable. But that is not the case. (afaik by design)
Title: Re: operators overloads working only one way?
Post by: jamie on September 04, 2024, 05:44:59 pm
the problem is the compiler won't pass it.

it's not a runtime error, it's a compile time error.

So how do I adjust that so the compiler will accept it?

If i get runtime errors due to uncreated object then that is my fault?


Title: Re: operators overloads working only one way?
Post by: Thaddy on September 04, 2024, 05:48:01 pm
The compiler won't allow this, because the assignment operator the other way around is already taken and you can not override that.
Tip: in such cases I tend to override one of the less used set operators, when deemed safe, like ><

 The result is not created... how can it return a valid value...?
Title: Re: operators overloads working only one way?
Post by: Martin_fr on September 04, 2024, 05:50:32 pm
Because it is a different class. It has to be exact. (No idea if by design /  I found it by trial and error)

So either make an operator on TMemoryStream, or make/cast the variable TStream.

But, test it with diff settings (optimizations). And maybe ask the fpc team. I am not sure it is safe or will work as expected.
Title: Re: operators overloads working only one way?
Post by: jamie on September 04, 2024, 05:53:34 pm
Because it is a different class. It has to be exact. (No idea if by design /  I found it by trial and error)

So either make an operator on TMemoryStream, or make/cast the variable TStream.

But, test it with diff settings (optimizations). And maybe ask the fpc team. I am not sure it is safe or will work as expected.

Ok, casting worked but that makes no sense to me.

if that was the case, I should also need to cast the other way but I don't?

Title: Re: operators overloads working only one way?
Post by: Thaddy on September 04, 2024, 05:56:32 pm
Casting does NOT work. There is simply no stream instance.
Title: Re: operators overloads working only one way?
Post by: TRon on September 04, 2024, 06:23:31 pm
fwiw: compiles just fine for me with fixes.

The (source) stream isn't passed, you have to create it manually in the operator. It does work, just not as expected.

Use a class operator instead.
Title: Re: operators overloads working only one way?
Post by: jamie on September 04, 2024, 06:29:33 pm
Last time I checked class operators didn't work in classes only records. This a class
Title: Re: operators overloads working only one way?
Post by: jamie on September 04, 2024, 06:40:05 pm
When I run it casted it works.
Title: Re: operators overloads working only one way?
Post by: Thaddy on September 04, 2024, 06:56:43 pm
I haven't run your code, but...

Code: Pascal  [Select][+][-]
  1. operator :=(A:byte):Tstream;
  2. Begin
  3.    Result.Write(A,1);
  4. End;

Result is not initialized. So it will contain trash, and you get an access violation, or other random error.

You are assuming that you will always get the actual memory of the target variable. But that is not the case. (afaik by design)
Is the correct answer. Why does Jamie does not see that? TStream is a class. IF that works it is by accident. The result needs an initialized TStream class and that is nowhere to be found.
Title: Re: operators overloads working only one way?
Post by: TRon on September 04, 2024, 06:57:46 pm
Last time I checked class operators didn't work in classes only records. This a class
Drats, you're right. I have mixed up my languages here. Apologies.

With all the posts you've been making lately, I wonder if that what you're trying to accomplish could perhaps be solved with using old-style objects?
Title: Re: operators overloads working only one way?
Post by: Martin_fr on September 04, 2024, 06:59:21 pm
Actually about the cast.

Code: Pascal  [Select][+][-]
  1.  S :=B;

With
- the operator returning TStream
- S being TStringstream
that translates to
Code: Pascal  [Select][+][-]
  1. s := TStream(something);
But S can't hold all types of stream, only TStringStreams and subclasses there of.

So, there is a point for that to be rejected.
Title: Re: operators overloads working only one way?
Post by: Thaddy on September 04, 2024, 07:00:20 pm
Does not matter TRon, Jamie is still wrong.
Title: Re: operators overloads working only one way?
Post by: Martin_fr on September 04, 2024, 07:39:34 pm
When I run it casted it works.
Have you compiled with -gt ?

Because that will likely cause it to crash at runtime.

And while you can just compile without -gt, the same effect can happen by the optimizer. Not in all cases, maybe just with very specific code, and maybe only with very specific versions of the compiler....
Title: Re: operators overloads working only one way?
Post by: PascalDragon on September 04, 2024, 08:07:49 pm
What em I doing wrong here?

There is no operator in your code that converts a TByte to a TMemoryStream. The assignment operator you expect to work only applies to assignments to TStream.

Also as others said your implementation of the operator overload itself is wrong. It might work by pure chance depending on the involved platform and optimizations settings, but it's definitely not guaranteed, because it's simply wrong code.
Title: Re: operators overloads working only one way?
Post by: jamie on September 04, 2024, 08:18:44 pm
I have the default settings lazarus sets.

Anyways,

 I found that if I use a
Code: Pascal  [Select][+][-]
  1. Var
  2.  S:STream;
  3.  B:Byte;
  4. Begin
  5.  S:= TMemoryStream.Create;
  6.  B := Byte(10);
  7.  S.Position := 0;
  8.   B:= S;
  9.   Caption := B.ToString;
  10.   S.Free;
  11.  End;
  12.  

Seems to work just fine! thank you.
Title: Re: operators overloads working only one way?
Post by: PascalDragon on September 04, 2024, 09:27:06 pm
Seems to work just fine! thank you.

You use the right word: it seems to work, simply because the instance value from the TMemoryStream.Create is still located on the no longer used stack when the assignment operator is called. If you instead insert a function that uses that area then your code will fail:

Code: Pascal  [Select][+][-]
  1. program tcnv;
  2.  
  3. {$mode objfpc}
  4.  
  5. uses
  6.   Classes;
  7.  
  8. operator :=(aArg: Byte): TStream;
  9. begin
  10.   Result.Write(aArg, SizeOf(aArg));
  11. end;
  12.  
  13. operator :=(aArg: TStream): Byte;
  14. begin
  15.   aArg.Read(Result, SizeOf(Result));
  16. end;
  17.  
  18. procedure OverwriteStack(aArg1, aArg2: Int64);
  19. var
  20.   a, b: Int64;
  21. begin
  22.   a := aArg1;
  23.   b := aArg2;
  24.   Writeln(a, b);
  25. end;
  26.  
  27. var
  28.   s: TStream;
  29.   b: Byte;
  30. begin
  31.   s := TMemoryStream.Create;
  32.   OverwriteStack(0, 0);
  33.   s := Byte(42);
  34.   s.Position := 0;
  35.   b := s;
  36.   Writeln(b);
  37.   s.Free;
  38. end.
Title: Re: operators overloads working only one way?
Post by: TRon on September 04, 2024, 09:33:38 pm
As PascalDragon wrote,

Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$mode objfpc}{$h+}
  4. {.$define cast}
  5.  
  6. uses
  7.   classes;
  8.  
  9. operator :=(S: TMemoryStream) B :Byte;
  10. begin
  11.   writeln('S->B, address of S = $', HexStr(S));
  12.   S.Read(B,1);
  13. end;
  14.  
  15. operator :=(B:byte) S: TMemoryStream;
  16. begin
  17.    writeln('B->S, address of S = $', HexStr(S));
  18.    S.Position := 0;
  19.    S.Write(B,1);
  20. end;
  21.  
  22. var
  23.   B:Byte;
  24.   {$ifdef cast}
  25.   S:TStream;
  26.   {$else}
  27.   MS:TMemoryStream;
  28.   {$endif}
  29. begin
  30.   {$ifndef cast}
  31.   MS:= TMemoryStream.Create;
  32.   writeln('address of S = $', HexStr(MS));
  33.   MS := B; // compiles if operator is defined using exact class-name. There isn't an operator for B->TStream unless it is created, or cast see below
  34.   B := MS;
  35.   MS.Free;
  36.   {$else}
  37.   S:= TMemoryStream.Create;
  38.   writeln('address of S = $', HexStr(S));
  39.   TMemoryStream(S) := B; // compiles, if cast correctly.
  40.   B := TMemoryStream(S);
  41.   S.Free;
  42.   {$endif}
  43. end.

linux x86_64:
Code: Bash  [Select][+][-]
  1. $ ./test
  2. ./test
  3. address of S = $00007F283D695E00
  4. B->S, address of S = $00000000004D83F8
  5. An unhandled exception occurred at $00000000004011B3:
  6. EAccessViolation: Access violation
  7.   $00000000004011B3  assign,  line 18 of test.pas
  8.   $0000000000401296  main,  line 33 of test.pas
  9.  
  10. Heap dump by heaptrc unit of /media/ramdisk-8gb/fpc/test
  11. 222 memory blocks allocated : 48050/48072
  12. 218 memory blocks freed     : 47818/47840
  13. 4 unfreed memory blocks : 232
  14. True heap size : 196608
  15. True free heap : 195488
  16. Should be : 195608
  17. Call trace for block $00007F283D67D100 size 128
  18.   $0000000000414241
  19.   $0000000000401296  main,  line 33 of test.pas
  20. Call trace for block $00007F283D696000 size 40
  21.   $0000000000414241
  22.   $0000000000401296  main,  line 33 of test.pas
  23. Call trace for block $00007F283D695F00 size 24
  24.   $0000000000401296  main,  line 33 of test.pas
  25. Call trace for block $00007F283D695E00 size 40
  26.  

edit: line-numbers were off, re-posted source and output so that they (hopefully) match.
Title: Re: operators overloads working only one way?
Post by: jamie on September 04, 2024, 11:02:09 pm
Ok, I'll go a different direction with this.

The problem here, and I have asked for this before, is we need a REFENCE option for the return, which C++ has

For larger than items that can't fit in registers, the compiler generates a reference to it. with the reference you have a direct route to the return item so you can directly interact on it.

For example.

Code: Pascal  [Select][+][-]
  1. operator := (A:Byte) Var :TStream;
  2. Begin
  3.  Result.xxxxx;// now is guaranteed to work.
  4. End;
  5.  

Something to think about if you want to get ahead of the other guys!  :o
TinyPortal © 2005-2018