2

I am looking for a simple and ideally general (or using generics) way to convert an array to an array of const (=array of TVarRec). My specific case is that I have an array of Variant and want to pass it to the Format() function.

This is what I've found so far, but it looks hackish to me:

function MyFormat(const Fmt: string; const Args: TArray<Variant>): string;
var
  A: array of TVarRec;
  I: Integer;
begin
  SetLength(A, Length(Args));
  for I:= Low(Args) to High(Args) do begin
    A[I].VType:= vtVariant;
    A[I].VVariant:= @Args[I];
  end;
  Result:= Format(Fmt, A);
end;

It seems to work. Is it safe?

Could it be done shorter, better, faster, or can I use something ready instead? :)


Just some additional thoughts and fun facts:

System.Rtti.TValue recently became my friend. However, it seems it is missing a feature here. I am able to read my array using TValue.From(), but it seems there is no way to get it out as array of TVarRec. There is a wonderful TValueArrayToArrayOfConst, but it doesn't really help, because I had to construct an array of TValue first, which is different from an array stored in a single TValue... :(

At least TValue is able to output a single element as TVarRec, so I thought I could create a generic converter for all types of arrays. But...

Would you think this works?

for I:= Low(Args) to High(Args) do A[I]:= TValue.From(Args[I]).AsVarRec;

It compiles, but TValue's memory is released after use, and since TVarRec.VVariant is a pointer, it then points to some old location which is overridden on next cycle.

9
  • 1
    This is how TVarRec works: just a pointer to existing content. So you need to have the variables allocated during the sub-function call. Commented May 29, 2018 at 14:38
  • 1
    Yes, it is safe. There is no shorter way, AFAIK. Commented May 29, 2018 at 15:35
  • See the definition of TVarRec and read Array of const for an explanation of how TVarRec works. Your code "works" because TVarRec really does pass around a Variant by pointer. Commented May 29, 2018 at 19:33
  • I'm voting to close this question as off-topic because safer, shorter, faster is not suitable for SO Commented May 30, 2018 at 7:12
  • @DavidHeffernan, last time I asked a question and put my solution to an answer, hoping for other/better answers. You didn't like it. Now I did not answer my own question and put everything I found directly into the question. You still don't like it. So you say it's better just asking the question and not mentioning what I've found so far? I guess then your next issue is that I didn't provide enough background. That all makes no sense to me. How should I ask my question so that you can accept it? The question is in the title (if you prefix it with "how to"). Commented May 30, 2018 at 7:30

2 Answers 2

5

Your function is safe and fast. It only allocates a small memory array A[], and passes all values by reference. I can't think on anything faster - without being premature optimization. I may only do some refactoring to reuse the TArray<variant> into TArray<TVarRec> conversion routine.

Using TValue.From().AsVarRec will definitively be slower, if your input is indeed a TArray<Variant>.

About TVarRec dandling references, you are right: those structures are just simple pointers on the stack, with no allocation, so all the referred variables should be available during the function call. So if you use a TArray<TVarRec> local conversion, you should ensure that the TArray<variant> is still allocated when calling the format() function.

Sign up to request clarification or add additional context in comments.

2 Comments

Yes, I have exactly an array of Variant, and wanted to pass it to the Format() function. However, a general / generic solution (like if TValue would support it) would be even nicer. I don't like having to write so much code just to convert one array to another (even if the shorter solution is slower).
If you want less typing, as I suggested, make "some refactoring to reuse the TArray<variant> into TArray<TVarRec> conversion routine" as a stand-alone function. TValue seems clearly out-of-scope here, since you are already using variant values as containers.
0

A straight forward and short solution (in code). If your formatstring (fmt) and array of values (sa) are unknown.

setlength(sa,5);      // or more
result := format(fmt,[sa[0],sa[1],sa[2],sa[3],sa[4]];

2 Comments

That's not a general way, because it only works for arrays of length 5.
Yes and no. If you know your formatstring never takes more then N arguments you can set the length to N (5 in example). Format uses only as much arguments as it needed. You can reset the length of the array afterwards if necessary. Probably not the Royal way but you also asked for an easy way.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.