From: hallvard@falcon.no (Hallvard Vassbotn)
Subject: Delphi: Example of variable number of parameters
Date: Wed, 29 Mar 95 11:37:23 GMT

program VarPar;

{ A simple program to demonstrate use of type-safe variable number of
  parameters in Delphi.

  Written Mars 1995 by Hallvard Vassbotn
  hallvard@falcon.no
}

uses WinCrt, SysUtils;

{ These are predefined in System:
const
    vtInteger  = 0;
    vtBoolean  = 1;
    vtChar     = 2;
    vtExtended = 3;
    vtString   = 4;
    vtPointer  = 5;
    vtPChar    = 6;
    vtObject   = 7;
    vtClass    = 8;

  type
    TVarRec = record
      case Integer of
        vtInteger:  (VInteger: Longint; VType: Byte);
        vtBoolean:  (VBoolean: Boolean);
        vtChar:     (VChar: Char);
        vtExtended: (VExtended: PExtended);
        vtString:   (VString: PString);
        vtPointer:  (VPointer: Pointer);
        vtPChar:    (VPChar: PChar);
        vtObject:   (VObject: TObject);
        vtClass:    (VClass: TClass);
    end;
}

const
  TypeNames : array [vtInteger..vtClass] of PChar  =
   ('Integer', 'Boolean', 'Char', 'Extended', 'String',
    'Pointer', 'PChar', 'Object', 'Class');

{
According to the on-line docs (search for TVarRec), array of const
parameters are treated like array of TVarRec by the compiler.
This example will work just as well if you change the declaration of
TestMultiPar to:

  procedure TestMultiPar(const Args: array of TVarRec);

This would make the implementation of the routine cleaner (no absolute
variable declaration), but the interface would be less understandable
to the user of the routine.

The compiler looks at the parameters and builds the array directly on the
stack. For each item in the array it also sets the VType field to one
of the pre-defined constants vtXXXX. The actual value is always sent as
four bytes of information. For the Boolean and Char types, only the first
byte contains useful information.

So, go ahead, now you can write all those neat routines with variable
number of parameters - and still keep the type safety!
}

function PtrToHex(P: pointer): string;
begin
  Result := IntToHex(Seg(P^), 4) + ':' + IntToHex(Ofs(P^), 4);
end;

procedure TestMultiPar(const Args: array of const);
var
  ArgsTyped : array [0..$fff0 div sizeof(TVarRec)] of TVarRec absolute Args;
  i         : integer;
begin
  for i := Low(Args) to High(Args) do
    with ArgsTyped[i] do
    begin
      Write('Args[', i, '] : ', TypeNames[VType], ' = ');
      case VType of
        vtInteger:  writeln(VInteger);
        vtBoolean:  writeln(VBoolean);
        vtChar:     writeln(VChar);
        vtExtended: writeln(VExtended^:0:4);
        vtString:   writeln(VString^);
        vtPointer:  writeln(PtrToHex(VPointer));
        vtPChar:    writeln(VPChar);
        vtObject:   writeln(PtrToHex(Pointer(VObject)));
        vtClass:    writeln(PtrToHex(Pointer(VClass)));
      end;
    end;
end;

var
  MyObj : TObject;
begin
  Writeln('Test of type-safe variable number of parameters in Delphi:');
  MyObj := TObject.Create;
  TestMultiPar([123, 45.67, PChar('ASCIIZ'), 'Hello, world!', true, 'X',
                @ShortDayNames, TObject, MyObj]);
  MyObj.Free;

  { To verify that the type-safety is used in the supplied formatting routines,
    try this: }
  writeln(Format('%d', ['hi']));
  { The supplied parameter is not of the type expected. The '%d' format string
    signals that the parameter should be an integer value, but instead we
    send a string. At run-time this will generate a exception, and if you
    have enabled IDE-trapping of exceptions, Delphi will show you the offending
    line. Using c-type sprintf funtions like this will result in undefined
    behaviour (read: system crash, GP or whatever) }
end.


-- 
 Hallvard Vassbotn  | Falcon AS (a Reuters company) | Without programmers,
 hallvard@falcon.no | Stranden 1, 0250 OSLO, Norway | the world would stop!

