Search code examples
arraysdelphidelphi-xe8omnithreadlibrary

How to pass an array as parameter to a task with SetParameter from OmniThreadLibrary?


In Delphi XE8, I am trying to pass an array to the OTL task in SetParameter from OmniThreadLibrary:

implementation

type
  TCookie = record
    Name:    string;
    Value:   string;
    ExpDate: string;
    ModDate: string;
  end;

  TCookieArray = array of TCookie;

var
  CurCookies: TCookieArray;

procedure TForm1.btnStartTaskClick(Sender: TObject);
begin
  SetLength(CurCookies, 2);
  CurCookies[0].Name  := 'username';
  CurCookies[0].Value := 'Paul';
  CurCookies[1].Name  := 'password';
  CurCookies[1].Value := 'none';

  FGetCookieDetailsTask := CreateTask(GetCookieEntries, 'GetCookieEntries')
    .MonitorWith(OTLMonitor)
    // Compiler complaint:
    .SetParameter('CookiesArray', TOmniValue.FromArray<TCookieArray>(CurCookies))
    .Run;
end;

The compiler complains about the SetParameter line:

[dcc32 Error] Unit1.pas(310): E2010 Incompatible types:
'System.TArray<Unit1.TCookieArray>' and 'TCookieArray'

Unfortunately, there are no examples in the OTL book on how to use FromArray in SetParameter to pass an array to the task.

So how can this be done?

EDIT: ba__friend asked that I show the source code from my solution in the comments of his answer:

  FGetCookieDetailsTask := CreateTask(GetCookieEntries, 'GetCookieEntries')
    .MonitorWith(OTLMonitor)
    // Now no compiler complaint:
    .SetParameter('CookiesArray', CurCookies)
    .Run;

procedure GetCookieEntries(const task: IOmniTask);
var
  TaskCookies, HostCookies: TCookieArray;
begin
  HostCookies := task.Param['CookiesArray'];
  TaskCookies := Copy(HostCookies, 0, Length(HostCookies));

Solution

  • There are two problems in your code.

    1) TCookieArray has to be declared as

    TCookieArray = TArray<TCookie>;
    

    From the compiler's standpoint, array of T and TArray<T> are (sadly) not the same thing.

    2) FromArray<T> expects the type T to be the array item type, not the array type (see OtlCommon), so you have to change it from

    TOmniValue.FromArray<TCookieArray>
    

    to

    TOmniValue.FromArray<TCookie>
    

    To access this array from a task, call:

    var
      cookies: TCookieArray;
    
    cookies := task.Param['CookiesArray'].ToArray<TCookie>;
    

    Side note: This is how things should work. In OTL up to (and including) 3.04b, record type is, however, not handled correctly in ToArray<T> and FromArray<T>. A fix for that has just been committed to the GitHub.

    If you want to fix your copy manually, two modifications are needed.

    In TOmniValue.CastFrom<T>, this block

    if ds = 0 then // complicated stuff
      {$IFDEF OTL_ERTTI}
    

    should be changed to

    if ds = 0 then // complicated stuff
      if ti^.Kind = tkRecord then
        Result.SetAsRecord(CreateAutoDestroyObject(
          TOmniRecordWrapper<T>.Create(value)))
      else
        {$IFDEF OTL_ERTTI}
    

    In TOmniValue.CastTo<T>, following block

    if ds = 0 then // complicated stuff
      {$IFDEF OTL_ERTTI}
    

    should be changed to

    if ds = 0 then // complicated stuff
      if ti.Kind = tkRecord then
        Result := TOmniRecordWrapper<T>(CastToRecord.Value).Value
      else
        {$IFDEF OTL_ERTTI}