Passing open array into an anonymous function

853 views Asked by At

What is the least wasteful way (i.e. avoiding copying if at all possible) to pass the content of an open string array into an anonymous function and from there into another function that expects an open array?

The problem is that open arrays cannot be captured in anonymous functions in Delphi XE2.

This illustrates the problem:

procedure TMyClass.DoSomething(const aStrings: array of string);
begin
  EnumItems(
    function (aItem: string) : Boolean
    begin
      Result := IndexText(aItem, aStrings) >= 0;
    end);
end;

The compiler complains: "Cannot capture symbol 'aStrings'".

An obvious solution is to make a copy of aStrings in a dynamic array and capture that. But I don't want to make a copy. (While my specific problem involves a string array and making a copy would only copy the pointers not the string data itself due to reference counting, it would also be instructive to learn how to solve the problem for an arbitrarily large array of a non-reference counted type.)

So I tried capturing instead a PString pointer to the first string in aStrings and an Integer value of the length. But then I couldn't figure out a way to pass these to InsertText.

One other constraint: I want it to be possible to call DoSomething([a, b, c]).

Is there a way to do this without making a copy of the array, and without writing my own version of IndexText, and without being hideously ugly? If so, how?

For the sake of this question I've used IndexText, but it would be instructive to find a solution for a function that could not be trivially rewritten to accept a pointer and length parameter instead of an open array.

An acceptable answer to this question would be: No, you can't do that (at least not without making a copy or rewriting IndexText) though if so I'd also like to know the fundamental reason why not.

2

There are 2 answers

2
Stefan Glienke On BEST ANSWER

If you don't want to copy the array then you should change the signature of DoSomething to take a TArray<string> instead. You of course have to change the caller side if you are passing the elements directly (only since XE7 you can pass dynamic arrays in the same way) - like DoSomething([a, b, c]) i mean.

My advice is not to mess around with some internal pointers and stuff, especially not for an open array.

7
David Heffernan On

There's no way to do this without making a copy. Open arrays cannot be captured as you have found, and you cannot get the information into the anonymous method without capture. You must capture, in general, because you need to extend the life of the variable.

So, you cannot do this with an open array and avoid a copy. You could instead:

  1. Switch from open array to a dynamic array, TArray<string>.
  2. Make a copy of the array. You would not be copying the string data, just the array of references to the strings.