How do I build an array of const?

11,883

Solution 1

The array of const gives you the freedom to add strings, integers, floats and so on and having these formatted into a string. And there is no limit to how many items you can add.

The way Delphi deals with this issue is that the array of const really is a array of TVarRec's.

A TVarRec is a record of the following type:

TVarRec = record
  case Byte of
    vtInteger:    (VInteger: Integer; VType: Byte);
    vtBoolean:    (VBoolean: Boolean);
    vtChar:       (VChar: Char);
    vtExtended:   (VExtended: PExtended);
    vtString:     (VString: PShortString);
    vtPointer:    (VPointer: Pointer);
    vtPChar:      (VPChar: PChar);
    vtObject:     (VObject: TObject);
    vtClass:      (VClass: TClass);
    vtWideChar:   (VWideChar: WideChar);
    vtPWideChar:  (VPWideChar: PWideChar);
    vtAnsiString: (VAnsiString: Pointer);
    vtCurrency:   (VCurrency: PCurrency);
    vtVariant:    (VVariant: PVariant);

The type of the value inside the TVarRec is determined by the VType value.

This gives you the flexibility to add either type you wish to the array of const, like in the Format() function:

Format( '%s is a string, %d is an integer', ['string',10] );

Using the array of const in your own procedure is no big deal. Take a look at this example:

 procedure VarArraySample( AVarArray : array of const );
  var
    i : integer;
  begin
    for i := 0 to High(AVarArray) do
      do_something;
  end;

The function High() returns the last index of the array.

You can also convert the contents of the TVarRec. This example is taken from the Delphi on-line help and revamped a bit. The function converts a TVarRec to a string:

function VarRecToStr( AVarRec : TVarRec ) : string;
  const
    Bool : array[Boolean] of string = ('False', 'True');
  begin
    case AVarRec.VType of
      vtInteger:    Result := IntToStr(AVarRec.VInteger);
      vtBoolean:    Result := Bool[AVarRec.VBoolean];
      vtChar:       Result := AVarRec.VChar;
      vtExtended:   Result := FloatToStr(AVarRec.VExtended^);
      vtString:     Result := AVarRec.VString^;
      vtPChar:      Result := AVarRec.VPChar;
      vtObject:     Result := AVarRec.VObject.ClassName;
      vtClass:      Result := AVarRec.VClass.ClassName;
      vtAnsiString: Result := string(AVarRec.VAnsiString);
      vtCurrency:   Result := CurrToStr(AVarRec.VCurrency^);
      vtVariant:    Result := string(AVarRec.VVariant^);
    else
      result := '';
    end;
  end;

You can combine the two functions above to one function that converts all elements in the array of const into one string:

function VarArrayToStr( AVarArray : array of const ) : string;
  var
    i : integer;
  begin
    result := '';
    for i := 0 to High(AVarArray) do
      result := result + VarRecToStr( AVarArray[i] );
  end;

you will now be able to create your own Format() function. The Format() function scans for %'s and replaces the %something with the value in the array of const, depending on the format specifiers and precision specifiers.

Solution 2

As you know, array of const is the same as array of TVarRec. To construct one, begin by declaring such as array, and then set the values of each of the elements, just as you would any other array.

TVarRec is a variant record, which means it can hold many different types of values. It has a field, VType, to indicate the type of the value it holds. Of the other fields, only one has a valid value at a time. Set the VType field, and then set the corresponding value field, such as VInteger or VString.

Beware that some of the fields are really pointers, like VVariant and VInt64. When you assign those pointer values, you'll need to make sure that whatever they point at remains accessible and valid for as long as Format needs it.

Other fields are typeless versions of their real value types. These include VAnsiStringand VInterface. When you assign to those fields, beware that they don't maintain the usual reference count that an ordinary AnsiString or IUnknown variable would, so again, watch those variable lifetimes.

The compiler is usually the only thing that generate such arrays, so there's little reference code available to see how they're built. Instead, you can look at other code that consumes arrays of const. For example, I implemented a Unicode-aware Format function for the JCL a number of years ago. It parses the format string one character at a time using a finite-state machine. Each time it finishes parsing an argument string, it fetches the corresponding argument from the input array and formats it according to the string and the argument type.

It used a minimum of assembler, and only for some minor efficiency, not because it was ever truly necessary. For reference, all the assembler is accompanied by the equivalent Delphi code in comments.

Solution 3

Found this code at https://groups.google.com/forum/#!topic/borland.public.delphi.objectpascal/-xb6O0qX2zc

procedure test(numArgs:integer; MyFormattingString:string);    
var
  v:array of tvarrec;
  i:integer;
begin
  setlength(v, numArgs);
  for i:=1 to numArgs do
  begin
    v[i-1].vtype:=vtpchar;
    v[i-1].vtpchar:=strnew(pchar(myDataSet.FieldByName(inttostr(i)).asstring));
  end;
  memo1.lines.add(Format(MyFormattingString,v);
  for i:=1 to numArgs do strdispose(v[i-1].vtpchar);
end;

Doesn't answer everything I have to deal with, but I think I know how to construct the array of TVarRec now.

Share:
11,883

Related videos on Youtube

Sam
Author by

Sam

I am a human being not a human resource

Updated on June 04, 2022

Comments

  • Sam
    Sam almost 2 years

    I am implementing an interpreter, and one of the functions my interpreter will support is like Delphi's Format. In fact, I'm implementing my function using SysUtils.Format. However, I'm having trouble building the second parameter to the function, the array of TVarRec.

    Suppose I have the following code. For now, I just assume which Delphi variables the interpreted code will need access to (iVar1 and iVar2), but I still don't know how to put them into the structure that Format requires (arFormatArgs).

    type TFormatArgs = array of TVarRec;
    
    procedure RunInterpretedFormatFunction;
    var
      iMyAge: integer;
      iMyIQ: integer;
      sCode: string;
      sText: string;
    begin
      iMyAge := 5;
      iMyIQ := -5;
      sCode := 'Format(''My age is %d and my IQ is %d'', [iMyAge, iMyIQ])';
      sText := FormatThis(sCode, iMyAge, iMyIQ);
    end;
    
    function FormatThis(sFormatCode: string; iVar1: integer; iVar2: integer): string;
    var
      sFormatString: string;
      arFormatArgs: TFormatArgs;
    begin
      sFormatString := GetFormatString(sFormatCode); // I can implement this function
      arFormatArgs := ConstructFormatArgs(iVar1, iVar2); // NEED HELP HERE!
      result := SysUtils.Format(sFormatString, arFormatArgs);
    end;
    

    How can I implement my ConstructFormatArgs function in Delphi (not Assembly)?

    • Glen Morse
      Glen Morse over 10 years
      did you see docwiki.embarcadero.com/CodeExamples/XE2/en/… example? I am not sure what consructformatargs is but did you try using ivar1 and ivar2 in place of arformatargs in the result?
    • Glen Morse
      Glen Morse over 10 years
      Well i only suggested it cause it shows the rules for sysutils.format not knowing what constructformatargs is doing. i was thinking you could look at it and see if arFormatARgs is following these rules.
    • Glen Morse
      Glen Morse over 10 years
      @Sam Sorry then i dont think i can help. As how you know it follows the rules if you dont know what ConstructFormatArgs is yet? and if it is following the rules, then whats the issue? anyhow good luck i am sure someone better at this then me can help.
    • Glen Morse
      Glen Morse over 10 years
      are you trying to Making a function with indefinite number of parameters?
    • Glen Morse
      Glen Morse over 10 years
      updated answer.. maybe its what your looking for..or at least what i am taking it as?
  • Glen Morse
    Glen Morse over 10 years
    thats same as my answer but mine shows the how.. anyhow glad you found the answer
  • Atys
    Atys about 7 years
    @GlenMorse no, it has nothing to do with your answer. It's the opposit.