Search code examples
delphidelphi-10.2-tokyo

Delphi TThread descendant return result


SITUATION. I have created an unit with some classes to solve algebra stuff (congruences and systems), I am showing you the code:

type
 TCongrError = class(Exception)
 end;

type
 TCongruence = class(TComponent)
  //code stuff
  constructor Create(a, b, n: integer); virtual;
 end;

type
 TCongrSystem = array of TCongruence;

type
 TCongruenceSystem = class(TThread)
  private
   resInner: integer;
   FData: TCongrSystem;
   function modinv(u, v: integer): integer; //not relevant
  protected
   procedure Execute; override;
  public
   constructor Create(data: TCongrSystem; var result: integer; hasClass: boolean);
 end;

I have decided to use TThread because this class has an Execute method that could take some time to finish due to the length of the parameters passed to the constructor. Here's the implementation:

constructor TCongruenceSystem.Create(data: TCongrSystem; var result: integer; hasClass: boolean);
begin

 inherited Create(True);
 FreeOnTerminate := true;

 FData := data;
 setClass := hasClass;
 resInner := result;

end;

procedure TCongruenceSystem.Execute;
var sysResult, i, n, t: integer;
begin

 sysResult := 0;
 n := 1;

 //computation

 Queue( procedure
        begin
         ShowMessage('r = ' + sysResult.ToString);
         resInner := sysResult;
        end );

end;

PROBLEM

If you look at the Queue you see that I am using (just as test) the ShowMessage and it is showing the correct value of sysResult. The second line by the way has some problems that I cannot understand.

The constructor has var result: integer so I can have side-effect from the passed variable and then I can assign resInner := result;. At the end (in the Queue) I am giving resInner the value of sysResult and I expect result to be updated too due to the side effect of var. Why doesn't this happen?

I have made another test changing the constructor like this:

constructor TCongruenceSystem.Create(data: TCongrSystem; result: TMemo; hasClass: boolean);
//now of course I have resInner: TMemo

And changing the Queue to this:

Queue( procedure
        begin
         ShowMessage('r = ' + sysResult.ToString);
         resInner.Lines.Add(sysResult.ToString);
        end ); //this code now works properly in both cases! (showmessage and memo)

In the constructor I am passing TMemo which is a reference and ok, but isn't the original var result: integer passed as reference too? Why then it doesn't work?

I want to do this because I'd like to do something like this:

 //I put var a: integer; inside the public part of the TForm
 test := TCongruenceSystem.Create(..., a, true);
 test.OnTerminate := giveMeSolution;
 test.Start;
 test := nil;

Where giveMeSolution is just a simple procedure that uses the variable a containing the result of the system. If this is not possible what could I do? Basically the result at the end of Execute is just an integer number that has to be passed to the main thread.

I have read about ReturnValue but I am not sure how to use it.


Solution

  • Basically the result at the end of Execute is just an integer number that has to be passed to the main thread.

    I have read about ReturnValue but I am not sure how to use it.

    Using the ReturnValue property is very easy:

    type
      TCongruenceSystem = class(TThread)
        ... 
      protected
        procedure Execute; override;
      public
        property ReturnValue; // protected by default
      end;
    
    procedure TCongruenceSystem.Execute;
    var
     ...
    begin
      // computation
      ReturnValue := ...;
    end;
    

    test := TCongruenceSystem.Create(...);
    test.OnTerminate := giveMeSolution;
    test.Start;
    
    ....
    
    procedure TMyForm.giveMeSolution(Sender: TObject);
    var
      Result: Integer;
    begin
      Result := TCongruenceSystem(Sender).ReturnValue;
      ...
    end;