0

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.

Michel Hua
  • 1,614
  • 2
  • 23
  • 44
  • 3
    When they are not part of an array, I would just leave it like your first code. – The_Fox Oct 15 '13 at 08:30
  • @John, regarding your bonus question. Mixing different types in one AssignValue procedure call will only cause your code to become unnecessary complex, without no gain at all. – LU RD Oct 15 '13 at 09:59

3 Answers3

7

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.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I have hundreds of lines in one single file that do that type of operation. Basically pointer initialization to nil, number initialization to 0. I was thinking that using the `AssignValue` technique could be more safe and suppress the redundancy. And it considerably reduces the lines of code. – Michel Hua Oct 15 '13 at 11:40
  • 1
    If you put these variables inside a record you could write `Rec := Default(TMyRecord);` – David Heffernan Oct 15 '13 at 11:46
2

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]);
Andrei Galatyn
  • 3,322
  • 2
  • 24
  • 38
0

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;
Arnaud Bouchez
  • 42,305
  • 3
  • 71
  • 159