Michel Hua
Michel Hua

Reputation: 1777

Procedure with an arbitrary number of double parameters

I have 10 double variables I would like to initialize with the value 0. They are unstructured and not part of an array by design.

procedure Initialize;
var
  a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
begin
  a1 := 0; 
  a2 := 0;
  a3 := 0;
  a4 := 0;
  a5 := 0;

  b1 := 0; 
  b2 := 0;
  b3 := 0;
  b4 := 0;
  b5 := 0;
end;

To refactor that piece of code, I'm introducing a helper method AssignValue.

procedure Initialize;
var
  a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
begin
  AssignValue(0,a1); 
  AssignValue(0,a2);
  ...
end;

procedure AssignValue(value: Double; var target: Double);
begin
  target:= value;
end;

How do I write a more general AssignValue procedure that takes an arbitrary number of arguments and make the call AssignValue(0,a1,a2,a3,a4,a5,b1,b2,b3,b4,b5) possible?

Bonus question: How do you write that procedure so that it takes into account double or int reference in any order, assuming value: Int as first parameter.

Upvotes: 0

Views: 223

Answers (3)

Arnaud Bouchez
Arnaud Bouchez

Reputation: 43023

First of all, you can use a record and call fillchar(myrecord,sizeof(myrecord),0) but it may be error prone if you have some internal reference-counted values (like string).

But in your case, since it is only double values, it may be very easy to write:

procedure Initialize;
var localdata: record
    a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
    obj: TObject;
    i1, i2, i3, i4: integer;
  end;
begin
  fillchar(localdata,sizeof(localdata),0);
  with localdata do
  begin
    a1 := 10;
    a2 := a1+10; 
    assert(obj=nil);
    inc(i1,20);
    i2 := i1+10;
    assert(i2=30);
  end;
end;

As you can see, you can even mix types within the record. The trick is that you define your record type inline, without any type definition, which is not needed.

I admit this is not the direct answer, but I humbly suggest that you change your design to switch to something more "OOP-compatible".

Just use a dynamic array, or a class to embed the values. They will be all set to 0 by default.

For a dynamic array:

var a,b: array of double;

SetLength(a,5); // then use a[0] instead of a1, a[2] instead of a2...
SetLength(b,5); // then use b[0] instead of b1, b[2] instead of b2...

For a class - which is my preferred, since you can embedd your code within your data, as good objects:

type
  TMyClass = class
  public
    a1, a2, a3, a4, a5, b1, b2, b3, b4, b5: Double;
    procedure OneMethodHere;
    function OneTestHere(aValue: double): boolean;
  end;

var C: TMyClass;

  C := TMyClass.Create; // every C member will be set to 0
  try
    if C.OneTestHere(10) then
      C.OneMethodHere;
    // you can use C.a1 or C.b5
  finally
    C.Free;
  end;

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 612794

You could do it like this:

procedure AssignValue(const Value: Double; const Addresses: array of PDouble);
var
  i: Integer;
begin
  for i := low(Addresses) to high(Addresses) do
    Addresses[i]^ := Value;
end;

Call it like this:

AssignValue(0.0, [@a1, @a2, @a3, ...]);

Here we are passing an open array containing the addresses of your variables.

To support multiple types you would use overloads declared like this:

procedure AssignValue(const Value: Double; const Addresses: array of PDouble); 
  overload;
procedure AssignValue(const Value: Integer; const Addresses: array of PInteger); 
  overload;
// and so on, implementation of these functions is obvious

It's up to you to judge whether or not this is any better than your current solution. Personally, I'd stick with the plain old assignment operator. Another option would be to put the variables inside a record and assign Default(TMyRecord) to your record variable.

Upvotes: 7

Andrei Galatyn
Andrei Galatyn

Reputation: 3432

You can use open array parameters for this:

procedure AssignValue(value: double; const arr: array of PDouble);
var
  i: Integer;
begin
  for i := 0 to length(arr)-1 do
    PDouble(arr[i])^ := value;
end;

use it like this (i don't see the way to avoid of "@" for such task):

AssignValue(1, [@a1,@a2,@a3]);

Upvotes: 2

Related Questions