1

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));
user1580348
  • 5,721
  • 4
  • 43
  • 105

1 Answers1

3

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}
gabr
  • 26,580
  • 9
  • 75
  • 141
ba__friend
  • 5,783
  • 2
  • 27
  • 20
  • Now I have `SetParameter('CookiesArray', TOmniValue.FromArray(CurCookies))`. But the compiler still complains: `[dcc32 Error] Unit1.pas(310): E2010 Incompatible types: 'System.TArray' and 'TCookieArray'` – user1580348 Aug 29 '15 at 16:14
  • Have you tried using the generic array type in the first place? Or aliasing TCookieArray to TArray? – ba__friend Aug 29 '15 at 16:19
  • I am not sure what you mean. Could you provide a code example? – user1580348 Aug 29 '15 at 17:02
  • Something like: `type TCookieArray = TArray;` – ba__friend Aug 29 '15 at 17:40
  • Now the compiler complains: `[dcc32 Error] Unit1.pas(310): E2010 Incompatible types: 'System.TArray>' and 'System.TArray'` – user1580348 Aug 29 '15 at 18:05
  • Look at the code in my question -- that's how it is defined and declared. – user1580348 Aug 29 '15 at 18:23
  • Now I have tried using simply `SetParameter('CookiesArray', CurCookies)`. It works flawlessly. But I don't know whether it is threadsafe. – user1580348 Aug 29 '15 at 18:25
  • When using `SetParameter('CookiesArray', CurCookies)`, both the host program and the task SHARE the same array object - even when I use it with a task variable: `TaskCookies := task.Param['CookiesArray'];`. This means that when I write to `TaskCookies` inside the task then also `CurCookies` in the host program is affected! I am not sure whether this is threadsafe... – user1580348 Aug 29 '15 at 18:43
  • ...which is not the case when I COPY the array inside the task: `var TaskCookies, HostCookies: TCookieArray; begin HostCookies := task.Param['CookiesArray']; TaskCookies := Copy(HostCookies, 0, Length(HostCookies));` – user1580348 Aug 29 '15 at 19:04
  • If you look at the implementation of FromArray and I am not mistaken your records of type TCookie are being copied. – ba__friend Aug 29 '15 at 19:06
  • What exactly? FromArray? Could you please update your question with the most recent version of your source code? Thanks – ba__friend Aug 29 '15 at 19:14
  • I have edited the answer to include fix for both mistakes. – gabr Aug 29 '15 at 19:21
  • @gabr Thanks, and in the task? With `TaskCookies := task.Param['CookiesArray'].ToArray;` I get an exception at run-time: `TValue of type tkRecord cannot be converted to TOmniValue` --- Edit: Aaaah, the exception comes not from the task but from `SetParameter`: `.SetParameter('CookiesArray', TOmniValue.FromArray(CurCookies))` – user1580348 Aug 29 '15 at 19:47
  • Strange, is it the newest version of the library? Edit: Looks like it comes from [here](https://github.com/gabr42/OmniThreadLibrary/blob/6cab1ec994138ef72a189d39de7cbb7a9d4d7142/OtlCommon.pas#L2449) – ba__friend Aug 29 '15 at 21:09
  • @gabr Thank you very much for the quick fix! I have downloaded the new `OtlCommon.pas` from GitHub and now it works! – user1580348 Aug 30 '15 at 17:51
  • It's a mistake to encourage the use of anything other than `TArray`. Aliases serve no purpose at all. – David Heffernan Aug 30 '15 at 19:05