2

This might seem like a strange request, but there is a good reason (code generation application). I pass a variant array to a procedure, contained within an array of variants, as follows:

TVarArray = array of variant;

procedure TMainForm.Button1Click(Sender: TObject);
var
  params: TVarArray;
  numRows: integer;
  numCols: integer;
  i: integer;
  j: integer;
begin
  SetLength(params, 2);
  numRows := 2;
  numCols := 2;

  params[0] := 5;
  params[1] := VarArrayCreate([1, numRows, 1, numCols], varVariant);
  for i := 1 to numRows do
    for j := 1 to numCols do
      params[1][i, j] := i + j;

  TestProc(params);
end;

procedure TMainForm.TestProc(params: TVarArray);
var
  arr: variant;
  p: PVariant;
  v: variant;
begin
  arr := params[1];    // -- Copies the array to arr.
  arr[2, 2] := 99;

  p := @(params[1]);
  p^[2, 2] := 88;      // -- Directly reference the passed-in array.

  v := p^;             // -- Copies the array to v -> How to prevent?
  v[2, 2] := 77;       // -- This should change the value in the original array.

  edit1.Text := VarToStr(arr[2, 2]);             // -- 99
  edit2.Text := VarToStr(params[1][2, 2]);       // -- 88  - should be 77
  edit3.Text := VarToStr(v[2, 2]);               // -- 77
end;

I don't want to create a copy of the array, so could use p^[] to directly access the passed-in array. However, I don't want to use the p^ syntax in TestProc but would prefer to use a variable name without the ^. Of course, if I try v := p^ I just get a copy. Is there any way around this? Thanks!

6
  • 1
    So, as I understand it you already have a solution, namely to use p^. Commented Jan 5, 2015 at 17:29
  • @DavidHeffernan Yes I can use p^, but the TestProc equivalents come out of my code generator and from an end-user viewpoint if they see the "^" in the generated code they're bound to get spooked (this is aimed at non-expert coders). I suspect it's not possible but thought I'd ask. Commented Jan 5, 2015 at 18:43
  • Why are you using variants in the first place? Commented Jan 5, 2015 at 19:12
  • Typically, generated code should never need to be looked at by humans. It's written by the generator and read by the compiler, and that's all. In that light, there's rarely any need for the generated code to look pretty. Commented Jan 5, 2015 at 19:29
  • @RobKennedy In my case it's further complicated by the fact that TestProc will include a mixture of user and generated code. Commented Jan 5, 2015 at 19:31

1 Answer 1

3

What you're looking for is a local variable that can act as a reference for something else (in particular, an element in an array of Variant). However, Delphi provides no way of creating a "local reference" variable. References only exist in the context of parameters passed as var, out, or sometimes const.

Maybe you could introduce a subroutine and pass param[1] as a var parameter. Inside the subroutine, you could refer to that parameter, and it would alias the array element form the caller. For example:

procedure ModifyVariant(var p: Variant);
begin
  p[2, 2] := 77;
end;

procedure TMainForm.TestProc(params: TVarArray);
var
  p: PVariant;
begin
  p := @params[1];

  ModifyVariant(params[1]);

  Assert(params[1][2, 2] = p^[2, 2]);
end;

ModifyVariant could even be an anonymous procedure so you can implement within the same scope as the caller:

procedure TMainForm.TestProc(params: TVarArray);
var
  ModifyVariant: reference to procedure(var x: Variant);
  p: PVariant;
begin
  p := @params[1];

  ModifyVariant := procedure(var v: Variant)
  begin
    v[2, 2] := 77;
  end;

  ModifyVariant(params[1]);

  Assert(params[1][2, 2] = p^[2, 2]);
end;

Neither of those looks particularly appealing, though, especially if you're afraid that mere pointer access will "spook" your code's consumers.

You've mentioned that you expect your users to incorporate their own code into the code you're generating. I wouldn't advise that. After all, what are they expected to do after they re-run your code generator? Surely they'll lose whatever customizations they've made. It's better to keep generated code separate, ideally in a separate file. For user customization, you can provide hooks in the form of callback functions that users can implement. That way, for example, a user could provide something analogous to ModifyVariant, and then your generated code can simply call it. You'll have your "Variant references," and you'll have generated code cleanly separated from user code.

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

1 Comment

Thanks interesting suggestions. I already keep generated code separate from the user's code and merge them at compile time, as you pointed out.

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.